def createrawtransaction(self, addscript=True, withmark=True): """ :addscript bool: For witness input, you can choose dont add input script(i.e. 0014dc72fd7bf4ef5bfd8dfe021f41f1ad7d37166dca) """ if not (self.check_inputs() and self.check_outputs()): # check input and output, if failed, over. return hex_input = "" witness_blank = "" vin_count = tolittle_endian(len(self.inputs), 2) hex_input += vin_count for _, input_ in enumerate(self.inputs): prev_txid = input_.get("prev_txid") prev_vout = input_.get("prev_vout") # script_length, signature, sighash_type, pubkey script = self.createscript(input_) if script and addscript: for _ in range(2): # double length for witness input script script = _hex(len(script) / 2) + script else: script = "00" sequence = self.seq if input_.get("address_type") in [P2SH, P2PKH]: script_blank = "{%s_%s}" % (input_.get("address"), prev_txid) hex_input += prev_txid + prev_vout + script_blank + sequence witness_blank += "00" else: witness_blank += "{%s_%s}" % (input_.get("address"), prev_txid) hex_input += prev_txid + prev_vout + script + sequence hex_output = "" vout_count = tolittle_endian(len(self.outputs), 2) hex_output += vout_count for output_ in self.outputs: amount = tolittle_endian(output_.get("amount"), 16) scriptpubkey = output_.get("scriptpubkey") hex_output += amount + _hex(len(scriptpubkey) / 2) + scriptpubkey txhex = self.ver + self.maker + self.flag + hex_input + hex_output + witness_blank + self.locktime if withmark: return txhex return re.sub(r"{.*?}", "{}", txhex).format(*(["00"] * int(vin_count)))
def scriptlength(self, value): value = int(len(value) / 2) if 0 < value <= int("fc", 16): return _hex(value) else: return "fd" + tolittle_endian( _hex(value + 1)) + "00" # 1bytes for OP_0(00)
def createrawtransaction(self, withmark=True): # raise RuntimeError("can not handle multisig yet") if not (self.check_inputs() and self.check_outputs()): # check input and output, if failed, over. return hex_input = "" vin_count = tolittle_endian(len(self.inputs), 2) hex_input += vin_count for num, input_ in enumerate(self.inputs): prev_txid = input_.get("prev_txid") prev_vout = input_.get("prev_vout") # script_length, signature, sighash_type, pubkey script_blank = "{%s_%s}" % (input_.get("address"), prev_txid) sequence = self.seq hex_input += prev_txid + prev_vout + script_blank + sequence hex_output = "" vout_count = tolittle_endian(len(self.outputs), 2) hex_output += vout_count for output_ in self.outputs: amount = tolittle_endian(output_.get("amount"), 16) scriptpubkey = output_.get("scriptpubkey") hex_output += amount + _hex(len(scriptpubkey) / 2) + scriptpubkey txhex = self.ver + hex_input + hex_output + self.locktime if withmark: return txhex return re.sub(r"{.*?}", "{}", txhex).format(*(["00"] * int(vin_count)))
def txid(txhex): """ return txid, wtxid """ # https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-id if txhex.find("0001") == 8: # witness wtxid = hexlify(dsha256(txhex)) _dict = loads(witness_tx.decoderawtransaction(txhex), object_pairs_hook=OrderedDict) _dict["maker"] = "" _dict["flag"] = "" for v in _dict.get("vin"): if v.get("txwitness"): v["txwitness"] = "" txid = hexlify(dsha256(tohex(_dict))) else: txid = wtxid = hexlify(dsha256(txhex)) return tolittle_endian(txid), tolittle_endian(wtxid)
def decodewtx(txhex): if not txhex[8:12] == "0001": raise RuntimeError("This is not witness transaction") version = txhex[:8] maker = txhex[8:10] flag = txhex[10:12] locktime = txhex[-8:] txhex = txhex[12:-8] vin_count = txhex[:2] txhex = txhex[2:] inputs = [] for _ in range(int(vin_count)): __input = OrderedDict() __input["prev_txid"] = tolittle_endian(txhex[:64]) __input["prev_vout"] = tolittle_endian(txhex[64:72]) txhex = txhex[72:] # fd + 2bytes length if length > fc. fdfd00 -> length = fd # sl -> script length if re.findall(r"^fd\w+", txhex): __input["script_length"] = txhex[:6] sl = int(tolittle_endian(__input["script_length"][2:]), 16) * 2 else: __input["script_length"] = txhex[:2] sl = int(__input["script_length"], 16) * 2 # the length of script_length sll = len(__input["script_length"]) if sl >= 20000: print(__input["script_length"]) raise RuntimeError("script length is too long") __input["script"] = txhex[sll:sll + sl] if 0 <= sl < 134: # just scriptpubkey or empty, only witness can do that __input["txwitness"] = 1 l = sll + sl __input["sequence"] = txhex[l:l + 8] inputs.append(__input) txhex = txhex[l + 8:] vout_count = txhex[:2] txhex = txhex[2:] outputs = [] for _ in range(int(vout_count, 16)): __output = OrderedDict() __output["amount"] = txhex[:16] __output["script_length"] = txhex[16:18] l = 18 + int(__output["script_length"], 16) * 2 __output["scriptpubkey"] = txhex[18:l] outputs.append(__output) txhex = txhex[l:] txwitness = [] for _ in range(int(vin_count)): witness_list = [] witness_count = txhex[:2] if not txhex: # vin count <= witness count break witness_count_int = int(witness_count, 16) if witness_count_int > 2: # multisig OP_0 = txhex[2:4] # necessary txhex = txhex[4:] witness_list.append(OP_0) witness_count_int -= 1 elif witness_count_int == 0: # Placeholder, watch dfb40fbf72c8fa9b11c67ba24f12bc48ba2a26f08bd709a3a11ca9ae30323a3f.test_tx txhex = txhex[2:] continue elif witness_count_int == 2: # one sig txhex = txhex[2:] for _ in range(witness_count_int): length_int = int(txhex[:2], 16) * 2 witness_list.append(txhex[2:length_int + 2]) txhex = txhex[length_int + 2:] txwitness.append(witness_list) txwitness = txwitness[::-1] for inp in inputs: if inp.get("txwitness"): inp["txwitness"] = txwitness[-1] txwitness.pop() tx_json = OrderedDict(version=version, maker=maker, flag=flag, vin_count=vin_count, vin=inputs, vout_count=vout_count, vout=outputs, locktime=locktime) return dumps(tx_json, indent=4)
def decodetx(txhex): if txhex[8:12] == "0001": raise RuntimeError("Don't support witness transaction for now.") version = txhex[:8] locktime = txhex[-8:] txhex = txhex[8:-8] vin_count = txhex[:2] txhex = txhex[2:] inputs = [] for _ in range(int(vin_count)): __input = OrderedDict() __input["prev_txid"] = txhex[:64] __input["prev_vout"] = txhex[64:72] txhex = txhex[72:] # fd + 2bytes length if length > fc. fdfd00 -> length = fd # sl -> script length if re.findall(r"^fd\w+", txhex): __input["script_length"] = txhex[:6] sl = int(tolittle_endian(__input["script_length"][2:]), 16) * 2 else: __input["script_length"] = txhex[:2] sl = int(__input["script_length"], 16) * 2 # the length of script_length sll = len(__input["script_length"]) if sl >= 20000: print(__input["script_length"]) raise RuntimeError("script length is too long") __input["script"] = txhex[sll:sll + sl] l = sll + sl __input["sequence"] = txhex[l:l + 8] inputs.append(__input) txhex = txhex[l + 8:] vout_count = txhex[:2] txhex = txhex[2:] outputs = [] for _ in range(int(vout_count, 16)): __output = OrderedDict() __output["amount"] = txhex[:16] __output["script_length"] = txhex[16:18] l = 18 + int(__output["script_length"], 16) * 2 __output["scriptpubkey"] = txhex[18:l] outputs.append(__output) txhex = txhex[l:] tx_json = OrderedDict(version=version, vin_count=vin_count, vin=inputs, vout_count=vout_count, vout=outputs, locktime=locktime) return dumps(tx_json, indent=4)
def __init__(self, inputs, ouputs, witness=None, maker=0, flag=1, **kw): super(witness_tx, self).__init__(inputs, ouputs, **kw) self.maker = tolittle_endian(maker, 2) self.flag = tolittle_endian(flag, 2) self.witness = witness
def check_inputs(self): """ address address_type(option) P2SH P2SH(P2WSH) P2SH(P2WPKH) P2PKH P2WSH P2WPKH redeemscript(option, multisig, redeemscript and mon must have one) mon(option, for multisig, tuple) :pubkey list: :prikey(option): locktime(option) sequence(option) prev_txid prev_vout """ for _input in self.inputs: locktime = _input.get("locktime") self.locktime = tolittle_endian( locktime) if locktime and locktime > 0 and isinstance( locktime, int) else self.locktime sequence = _input.get("sequence") self.seq = tolittle_endian( sequence) if sequence and sequence > 0 and isinstance( sequence, int) else self.seq prev_txid, prev_vout = _input.get("prev_txid"), _input.get( "prev_vout") if prev_txid != None and prev_vout != None and isinstance( prev_vout, int) and len(prev_txid) == 64: _input["prev_txid"] = tolittle_endian(prev_txid) _input["prev_vout"] = tolittle_endian(prev_vout, 8) else: print(prev_txid, prev_vout, len(prev_txid) == 64) raise RuntimeError( ":prev_txid string 32bytes: :prev_vout int:") pubkey = _input.get("pubkey") redeemscript = _input.get("redeemscript") mon = _input.get("mon") if not pubkey or not isinstance(pubkey, list): raise RuntimeError( "pubkey is necessary. And it must be in a list") elif len(pubkey) > 1 and not redeemscript: if not isinstance(mon, tuple): raise RuntimeError( "redeemscript is necessary when using multisig") elif mon[0] <= mon[1] and mon[1] > 2: _input["redeemscript"] = self.MoNscript( mon[0], mon[1], pubkey) elif len(pubkey) == 1 and redeemscript: warnings.warn( "redeemscript should not exit when using single signature", RuntimeError) elif len(pubkey) == 1 and mon: warnings.warn( "mon should not exit when using single signature", RuntimeError) address = _input.get("address") if not address or not isinstance(address, str): raise RuntimeError( "address is necessary. And it must be a string") elif not _input.get("address_type"): # analysis what kind of address it is. if re.findall(r"^[3,2]", address) and len(pubkey) > 1: # P2SH or P2SH(P2WSH) if not redeemscript: check_redeemscript_again = _input.get("redeemscript") if check_redeemscript_again and P2WSHoP2SHAddress( bytes.fromhex(check_redeemscript_again) ) == _input["address"]: _input["address_type"] = P2WSHoP2SH elif check_redeemscript_again: _input["address_type"] = P2SH else: _input["address_type"] = P2WSHoP2SH warnings.warn( "P2SH or P2SH(P2WSH), not sure, but has set to P2SH(P2WSH)[default]" ) elif re.findall(r"^(0020)", redeemscript): _input["address_type"] = P2WSHoP2SH else: _input["address_type"] = P2SH elif re.findall(r"^[3,2]", address) and len(pubkey) == 1: # P2SH(P2WPKH) _input["address_type"] = P2WPKHoP2SH elif re.findall(r"^[1,m]", address): # P2PKH _input["address_type"] = P2PKH elif re.findall(r"^(bc|tb)", address) and len(pubkey) > 1: # P2WSH _input["address_type"] = P2WSH elif re.findall(r"^(bc|tb)", address) and len(pubkey) == 1: # P2WPKH _input["address_type"] = P2WPKH return True
def __init__(self, inputs, ouputs, locktime=0, seq=4294967295, version=2): self.ver = tolittle_endian(version) self.inputs = inputs self.outputs = ouputs self.locktime = tolittle_endian(locktime) self.seq = tolittle_endian(seq)
def deserialize(self, txhex, amounts, sighashtype="ALL"): json = self.decodetransaction(txhex) nVersion = json.get("version") vin = json.get("vin") Preimages = [] count = 0 for pos, v in enumerate(vin): st = self.check_scripttype(v) the_txwitness = v.get("txwitness") if the_txwitness and len(the_txwitness) == 2 and st in [3, 5]: # P2WPKH / P2SH(P2WPKH) outpoint = v.get("prev_txid") + v.get("prev_vout") nSequence = v.get("sequence") hashPrevouts = hexlify( dsha256("".join([ vx.get("prev_txid") + vx.get("prev_vout") for vx in vin ]))) hashSequence = hexlify( dsha256("".join([vx.get("sequence") for vx in vin]))) scriptCode = P2PKH(the_txwitness[-1]) # used P2PKH scriptCode = hex(int(len(scriptCode) / 2))[2:] + scriptCode amount = amounts[count] count += 1 output = "".join([ vout.get("amount") + vout.get("script_length") + vout.get("scriptpubkey") for vout in json.get("vout") ]) hashOutputs = hexlify(dsha256(output)) nLockTime = json.get("locktime") nHashType = tolittle_endian(SIGHASH.get(sighashtype), 8) if sighashtype == "SINGLE": hashSequence = "0" * 64 Preimages.append(nVersion + hashPrevouts + hashSequence + outpoint + scriptCode + amount + nSequence + hashOutputs + nLockTime + nHashType) if the_txwitness and len(the_txwitness) >= 2 and st in [4, 6]: # P2WSH / P2SH(P2WSH) outpoint = v.get("prev_txid") + v.get("prev_vout") nSequence = v.get("sequence") hashPrevouts = hexlify( dsha256("".join([ vx.get("prev_txid") + vx.get("prev_vout") for vx in vin ]))) hashSequence = hexlify( dsha256("".join([vx.get("sequence") for vx in vin]))) if sighashtype.find("SINGLE") >= 0: scriptCode = self.getlastpart( the_txwitness[-1]) # get last part if not scriptCode: scriptCode = the_txwitness[-1] elif sighashtype.find("ALL") >= 0 or sighashtype.find( "None") >= 0: scriptCode = the_txwitness[-1] scriptCode = hex(int(len(scriptCode) / 2))[2:] + scriptCode amount = amounts[count] count += 1 if sighashtype.find("SINGLE") >= 0: output = "".join( [ vout.get("amount") + vout.get("script_length") + vout.get("scriptpubkey") for _, vout in enumerate(json.get("vout")) if _ == pos ] ) # _ == pos means second inputs to second ouput/ 1-input to 1-output elif sighashtype.find("ALL") >= 0: output = "".join([ vout.get("amount") + vout.get("script_length") + vout.get("scriptpubkey") for _, vout in enumerate(json.get("vout")) ]) elif sighashtype.find("None") >= 0: output = "" hashOutputs = hexlify(dsha256(output)) if output else "0" * 64 nLockTime = json.get("locktime") nHashType = tolittle_endian(SIGHASH.get(sighashtype), 8) if sighashtype == "SINGLE" or sighashtype == "None": hashSequence = "0" * 64 elif sighashtype.find("ANYONECANPAY") > 0: hashSequence = hashPrevouts = "0" * 64 Preimages.append(nVersion + hashPrevouts + hashSequence + outpoint + scriptCode + amount + nSequence + hashOutputs + nLockTime + nHashType) return [hexlify(dsha256(p)) for p in Preimages]
print("tx2:", state2) # My transaction 4dc48bc2dff60410c77bf3674cdc22954db99e4b2841c0ef8cfd5ff4a0df34fa testnet txhex3 = "0200000000010341e241eb1e14125ac93a43bd1561a839de3aad1fce8b49ab7a44e31b903a5e100100000000fdffffff6b626907ad9ebe7dab31e18337aa42408757312fa239a98fa0afc68fe8dcce9e0000000000fdffffffb87c4c430ad2d1d3575e55824bfbbb394016760d6a0b987d10759ccbeef5ab4a0000000000fdffffff01f2a2210600000000160014dc72fd7bf4ef5bfd8dfe021f41f1ad7d37166dca0247304402200ed9eb7189da61da05c9ed35ffdb589e5b98a9135b9d74db1edfb8e00177e2b90220229bd12925be2aff620b6ae2f72d832eb27468c0dac6165ffab590eacdad273d012102b497bfc891cc3b85df8b1ab4cbe4d6dce76d037c3d3a13712073960d54d6dcf7024730440220716345f34607a9a8b4acd434f8318937ad7164bd92dbe602fae458afe7b83a9102201217c1357df8fc3be1bf28283ef3fd16f5269daef9a00f45368b0770090b3e45012102b497bfc891cc3b85df8b1ab4cbe4d6dce76d037c3d3a13712073960d54d6dcf70247304402205300b916b6ce48a945098585e61197783ef98ddb61af385d432c2ea0ad1fedef0220279e3afc47e77f8503880efaeebc3a72f6f65dd0989933c38a256144b2ec6637012102b0573d73a59544b9f5e6f50f992738970a5521d74b94c4dd2d7ecf393fb1da7a2ffa1700" signature3 = "304402200ed9eb7189da61da05c9ed35ffdb589e5b98a9135b9d74db1edfb8e00177e2b90220229bd12925be2aff620b6ae2f72d832eb27468c0dac6165ffab590eacdad273d" pubkeyhash3 = "02b497bfc891cc3b85df8b1ab4cbe4d6dce76d037c3d3a13712073960d54d6dcf7" signature4 = "30440220716345f34607a9a8b4acd434f8318937ad7164bd92dbe602fae458afe7b83a9102201217c1357df8fc3be1bf28283ef3fd16f5269daef9a00f45368b0770090b3e45" pubkeyhash4 = "02b497bfc891cc3b85df8b1ab4cbe4d6dce76d037c3d3a13712073960d54d6dcf7" signature5 = "304402205300b916b6ce48a945098585e61197783ef98ddb61af385d432c2ea0ad1fedef0220279e3afc47e77f8503880efaeebc3a72f6f65dd0989933c38a256144b2ec6637" pubkeyhash5 = "02b0573d73a59544b9f5e6f50f992738970a5521d74b94c4dd2d7ecf393fb1da7a" ffff3 = inspector.deserialize( txhex3, amounts=[tolittle_endian(i, 16) for i in [4178886, 96115369, 2573688]]) ffff3, ffff4, ffff5 = ffff3 state3 = inspector.verify(signature=signature3, pubkeyhash=pubkeyhash3, msg=ffff3) state4 = inspector.verify(signature=signature4, pubkeyhash=pubkeyhash4, msg=ffff4) state5 = inspector.verify(signature=signature5, pubkeyhash=pubkeyhash5, msg=ffff5) print("tx3:", state3) print("tx4:", state4) print("tx5:", state5)