def test_native_p2wsh_2() -> None: tx_bytes = "0100000002e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac00000000" tx = Tx.parse(tx_bytes) tx.vin[0].script_witness = Witness([ "0063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac" ]) tx.vin[1].script_witness = Witness([ "5163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac" ]) previous_txout_1 = TxOut( 16777215, "0020ba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d") previous_txout_2 = TxOut( 16777215, "0020d9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537") hash_ = sig_hash.from_tx( [previous_txout_1, previous_txout_2], tx, 0, sig_hash.ANYONECANPAY | sig_hash.SINGLE, ) assert hash_ == bytes.fromhex( "e9071e75e25b8a1e298a72f0d2e9f4f95a0f5cdf86a533cda597eb402ed13b3a") script_ = sig_hash.witness_v0_script(tx.vin[1].script_witness.stack[-1])[1] hash_ = sig_hash.segwit_v0( script_, tx, 1, sig_hash.ANYONECANPAY | sig_hash.SINGLE, previous_txout_2.value, ) assert hash_ == bytes.fromhex( "cd72f1f1a433ee9df816857fad88d8ebd97e09a75cd481583eb841c330275e54")
def __init__( self, non_witness_utxo: Optional[Tx] = None, witness_utxo: Optional[TxOut] = None, partial_sigs: Optional[Mapping[Octets, Octets]] = None, sig_hash_type: Optional[int] = None, redeem_script: Octets = b"", witness_script: Octets = b"", hd_key_paths: Optional[Mapping[Octets, BIP32KeyOrigin]] = None, final_script_sig: Octets = b"", final_script_witness: Witness = Witness(), unknown: Optional[Mapping[Octets, Octets]] = None, check_validity: bool = True, ) -> None: self.non_witness_utxo = non_witness_utxo self.witness_utxo = witness_utxo # https://docs.python.org/3/tutorial/controlflow.html#default-argument-values self.partial_sigs = (decode_dict_bytes_bytes(partial_sigs) if partial_sigs else {}) self.sig_hash_type = sig_hash_type self.redeem_script = bytes_from_octets(redeem_script) self.witness_script = bytes_from_octets(witness_script) self.hd_key_paths = decode_hd_key_paths(hd_key_paths) self.final_script_sig = bytes_from_octets(final_script_sig) self.final_script_witness = final_script_witness self.unknown = dict(sorted(decode_dict_bytes_bytes(unknown).items())) if check_validity: self.assert_valid()
def test_valid_script_path() -> None: fname = "tapscript_test_vector.json" filename = path.join(path.dirname(__file__), "_data", fname) with open(filename, "r", encoding="ascii") as file_: data = json.load(file_) for x in data: prevouts = [TxOut.parse(prevout) for prevout in x["prevouts"]] index = x["index"] if not is_p2tr(prevouts[index].script_pub_key.script): continue script_sig = x["success"]["scriptSig"] assert not script_sig witness = Witness(x["success"]["witness"]) if len(witness.stack) >= 2 and witness.stack[-1][0] == 0x50: witness.stack = witness.stack[:-1] # check script paths if len(witness.stack) < 2: continue Q = type_and_payload(prevouts[index].script_pub_key.script)[1] script = witness.stack[-2] control = witness.stack[-1] assert check_output_pubkey(Q, script, control)
def test_single_witness() -> None: # 4e52f7848dab7dd89ef7ba477939574198a170bfcb2fb34355c69f5e0169f63c tx_bytes = "010000000001019bdea7abb2fa14dead47dd14d03cf82212a25b6096a8da6b14feec3658dbcf9d0100000000ffffffff02a02526000000000017a914f987c321394968be164053d352fc49763b2be55c874361610000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220421fbbedf2ee096d6289b99973509809d5e09589040d5e0d453133dd11b2f78a02205686dbdb57e0c44e49421e9400dd4e931f1655332e8d078260c9295ba959e05d014730440220398f141917e4525d3e9e0d1c6482cb19ca3188dc5516a3a5ac29a0f4017212d902204ea405fae3a58b1fc30c5ad8ac70a76ab4f4d876e8af706a6a7b4cd6fa100f44016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000" tx = Tx.parse(tx_bytes) assert tx.serialize(include_witness=True).hex() == tx_bytes assert tx == Tx.from_dict(tx.to_dict()) assert tx.version == 1 assert tx.lock_time == 0 assert len(tx.vin) == 1 assert len(tx.vout) == 2 stack = [ "", "30440220421fbbedf2ee096d6289b99973509809d5e09589040d5e0d453133dd11b2f78a02205686dbdb57e0c44e49421e9400dd4e931f1655332e8d078260c9295ba959e05d01", "30440220398f141917e4525d3e9e0d1c6482cb19ca3188dc5516a3a5ac29a0f4017212d902204ea405fae3a58b1fc30c5ad8ac70a76ab4f4d876e8af706a6a7b4cd6fa100f4401", "52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae", ] witness = Witness(stack) assert tx.vin[0].script_witness == witness tx_id = "4e52f7848dab7dd89ef7ba477939574198a170bfcb2fb34355c69f5e0169f63c" assert tx.id.hex() == tx_id hash_ = "d39eb3e3954be4bdc0b3be2d980124b1e1e11fb414b886b52939b07d95a58a8f" assert tx.hash.hex() == hash_ assert tx.size == 380 assert tx.vsize == 190 assert tx.weight == 758 assert tx.is_segwit() assert any(bool(w) for w in tx.vwitness) assert not tx.is_coinbase()
def test_wrapped_p2wsh() -> None: tx_bytes = "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000" tx = Tx.parse(tx_bytes) stack = [ "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae" ] tx.vin[0].script_witness = Witness(stack) utxo = TxOut( 987654321, "0020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54", ) hash_ = sig_hash.from_tx([utxo], tx, 0, sig_hash.ALL) assert hash_ == bytes.fromhex( "185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c") hash_ = sig_hash.from_tx([utxo], tx, 0, sig_hash.NONE) assert hash_ == bytes.fromhex( "e9733bc60ea13c95c6527066bb975a2ff29a925e80aa14c213f686cbae5d2f36") hash_ = sig_hash.from_tx([utxo], tx, 0, sig_hash.SINGLE) assert hash_ == bytes.fromhex( "1e1f1c303dc025bd664acb72e583e933fae4cff9148bf78c157d1e8f78530aea") hash_ = sig_hash.from_tx([utxo], tx, 0, sig_hash.ANYONECANPAY | sig_hash.ALL) assert hash_ == bytes.fromhex( "2a67f03e63a6a422125878b40b82da593be8d4efaafe88ee528af6e5a9955c6e") hash_ = sig_hash.from_tx([utxo], tx, 0, sig_hash.ANYONECANPAY | sig_hash.NONE) assert hash_ == bytes.fromhex( "781ba15f3779d5542ce8ecb5c18716733a5ee42a6f51488ec96154934e2c890a") hash_ = sig_hash.from_tx([utxo], tx, 0, sig_hash.ANYONECANPAY | sig_hash.SINGLE) assert hash_ == bytes.fromhex( "511e8e52ed574121fc1b654970395502128263f62662e076dc6baf05c2e6a99b")
def test_valid_taproot_script_path() -> None: tx_data = "26dc279d02d8b1a203b653fc4e0f27f408432f3f540136d33f8f930eaeba655910095142980402000000fd697cd4eb5278f1e34545cd57b6670df806fa3a0a064fd8e385a19f1a53d9ce8d8971a30f02000000378d5fb502335dbe02000000001976a9140053a23441c8478caac4c6b769c51f8476cd4b4b88ac58020000000000001976a914f2aae94a43e0d173354201d7832b46c5269c8a2488ac4a08671e" prevouts_data = [ "91ca4c010000000017a9145658b58602cdf7b7e962cfe44e024cb0e366f27087", "cb127401000000002251201ebe8b90363bd097aa9f352c8b21914e1886bc09fe9e70c09f33ef2d2abdf4bc", ] witness_data = [ "9675a9982c6398ea9d441cb7a943bcd6ff033cc3a2e01a0178a7d3be4575be863871c6bf3eef5ecd34721c784259385ca9101c3a313e010ac942c99de05aaaa602", "5799cf4b193b730fb99580b186f7477c2cca4d28957326f6f1a5d14116438530e7ec0ce1cd465ad96968ae8a6a09d4d37a060a115919f56fcfebe7b2277cc2df5cc08fb6cda9105ee2512b2e22635aba", "7520c7b5db9562078049719228db2ac80cb9643ec96c8055aa3b29c2c03d4d99edb0ac", "c1a7957acbaaf7b444c53d9e0c9436e8a8a3247fd515095d66ddf6201918b40a3668f9a4ccdffcf778da624dca2dda0b08e763ec52fd4ad403ec7563a3504d0cc168b9a77a410029e01dac89567c9b2e6cd726e840351df3f2f58fefe976200a19244150d04153909f660184d656ee95fa7bf8e1d4ec83da1fca34f64bc279b76d257ec623e08baba2cfa4ea9e99646e88f1eb1668c00c0f15b7443c8ab83481611cc3ae85eb89a7bfc40067eb1d2e6354a32426d0ce710e88bc4cc0718b99c325509c9d02a6a980d675a8969be10ee9bef82cafee2fc913475667ccda37b1bc7f13f64e56c449c532658ba8481631c02ead979754c809584a875951619cec8fb040c33f06468ae0266cd8693d6a64cea5912be32d8de95a6da6300b0c50fdcd6001ea41126e7b7e5280d455054a816560028f5ca53c9a50ee52f10e15c5337315bad1f5277acb109a1418649dc6ead2fe14699742fee7182f2f15e54279c7d932ed2799d01d73c97e68bbc94d6f7f56ee0a80efd7c76e3169e10d1a1ba3b5f1eb02369dc43af687461c7a2a3344d13eb5485dca29a67f16b4cb988923060fd3b65d0f0352bb634bcc44f2fe668836dcd0f604150049835135dc4b4fbf90fb334b3938a1f137eb32f047c65b85e6c1173b890b6d0162b48b186d1f1af8521945924ac8ac8efec321bf34f1d4b3d4a304a10313052c652d53f6ecb8a55586614e8950cde9ab6fe8e22802e93b3b9139112250b80ebc589aba231af535bb20f7eeec2e412f698c17f3fdc0a2e20924a5e38b21a628a9e3b2a61e35958e60c7f5087c", ] tx = Tx.parse(tx_data) prevouts = [TxOut.parse(prevout) for prevout in prevouts_data] index = 1 witness = Witness(witness_data) tx.vin[index].script_witness = witness sighash_type = 0 # all signature = witness.stack[0][:64] if len(witness.stack[0]) == 65: sighash_type = witness.stack[0][-1] assert sighash_type != 0 msg_hash = sig_hash.from_tx(prevouts, tx, index, sighash_type) tapscript = parse(witness.stack[-2]) pub_key = bytes.fromhex(str(tapscript[1])) ssa.assert_as_valid_(msg_hash, pub_key, signature)
def parse(cls: Type["TxIn"], data: BinaryData, check_validity: bool = True) -> "TxIn": stream = bytesio_from_binarydata(data) prev_out = OutPoint.parse(stream) script_sig = var_bytes.parse(stream) sequence = int.from_bytes(stream.read(4), byteorder="little", signed=False) return cls(prev_out, script_sig, sequence, Witness(), check_validity)
def test_witness() -> None: witness = Witness() assert not witness.stack assert len(witness) == 0 witness.assert_valid() assert witness == Witness.parse(witness.serialize()) assert witness == Witness.from_dict(witness.to_dict()) stack = [ "", "3044022077ecafa04bc23f87057bd54b572a473cf5cc6a945c167fcefe561618b86d097002200eb7c62a855295c8c288ff972c58905861a98260dafb6f8b7587cfda3091e00d01", "3045022100f20ba32865985e66985ba2d7ad11950309e253788b2edb27ccf5899e806f43ef02202899d98360f6476fcefbbf1dc1813595f4d0807d13237218de5ffaaeaa85640101", "304402206f7f5b0723d61f5d9f2ecbae448646d4cf0cf3ecade5ab50867fdfe8e131ce8f02207f0c260620e41dbdecd6ce574bf2773de248178d33f24aee71627843560137cd01", "01", "635321024713c6e66da107644c64ab84189840e78310b247cc7fa563d6f98f2a46900a0d21026cb4cc5bbde0e59806657b1780a9a3b333a8acb6fcac48ade7d52e2b34aa30042102b6615b55426b7362cd82897db26b1423e3732f98eaea5cd2c150c49a46003c6521033557edc1a6aec5a28648f6e22deb542e9ee8c9219d5bb5e81d0fe23c8f955ad221039d7f91444b2d4c4e89a1f550fa7d32c5d9b75a49b14c54f10109d95f637bb7de2103d9cdf5c6da8b2fd66fa918916cc93d831f16781c01ca759c9cf60acf94268bbd56ae67029000b275522102d02570ed9db9ee6abd13a6c269758debcfaa1aa6d0857553e5b6a5cf764ffe0a21030968209ccaaae1c0f8ee7a4a3594b3504fd3f89db1c259aedbdce3aba29f219321036069299a8a990474eb34786bf446e724088896a54bf848650c9543f18af602dc53ae68", ] witness = Witness(stack) assert len(witness) == 6 witness.assert_valid() assert witness == Witness.parse(witness.serialize()) assert witness == Witness.from_dict(witness.to_dict())
def test_double_witness() -> None: tx_bytes = "01000000000102322d4f05c3a4f78e97deda01bd8fc5ff96777b62c8f2daa72b02b70fa1e3e1051600000017160014e123a5263695be634abf3ad3456b4bf15f09cc6afffffffffdfee6e881f12d80cbcd6dc54c3fe390670678ebd26c3ae2dd129f41882e3efc25000000171600145946c8c3def6c79859f01b34ad537e7053cf8e73ffffffff02c763ac050000000017a9145ffd6df9bd06dedb43e7b72675388cbfc883d2098727eb180a000000001976a9145f9e96f739198f65d249ea2a0336e9aa5aa0c7ed88ac024830450221009b364c1074c602b2c5a411f4034573a486847da9c9c2467596efba8db338d33402204ccf4ac0eb7793f93a1b96b599e011fe83b3e91afdc4c7ab82d765ce1da25ace01210334d50996c36638265ad8e3cd127506994100dd7f24a5828155d531ebaf736e160247304402200c6dd55e636a2e4d7e684bf429b7800a091986479d834a8d462fbda28cf6f8010220669d1f6d963079516172f5061f923ef90099136647b38cc4b3be2a80b820bdf90121030aa2a1c2344bc8f38b7a726134501a2a45db28df8b4bee2df4428544c62d731400000000" tx = Tx.parse(tx_bytes) assert tx.serialize(include_witness=True).hex() == tx_bytes assert tx == Tx.from_dict(tx.to_dict()) assert tx.version == 1 assert tx.lock_time == 0 assert len(tx.vin) == 2 assert len(tx.vout) == 2 stack1 = [ "30450221009b364c1074c602b2c5a411f4034573a486847da9c9c2467596efba8db338d33402204ccf4ac0eb7793f93a1b96b599e011fe83b3e91afdc4c7ab82d765ce1da25ace01", "0334d50996c36638265ad8e3cd127506994100dd7f24a5828155d531ebaf736e16", ] witness1 = Witness(stack1) assert tx.vin[0].script_witness == witness1 stack2 = [ "304402200c6dd55e636a2e4d7e684bf429b7800a091986479d834a8d462fbda28cf6f8010220669d1f6d963079516172f5061f923ef90099136647b38cc4b3be2a80b820bdf901", "030aa2a1c2344bc8f38b7a726134501a2a45db28df8b4bee2df4428544c62d7314", ] witness2 = Witness(stack2) assert tx.vin[1].script_witness == witness2 tx_id = "a4b76807519aba5740f7865396bc4c5ca0eb8aa7c3744ca2db88fcc9e345424c" assert tx.id.hex() == tx_id hash_ = "0936cb8dba90e11345b9c05f457f139ddce4a5329701af4708b2cf4a02d75adb" assert tx.hash.hex() == hash_ assert tx.size == 421 assert tx.vsize == 259 assert tx.weight == 1033 assert tx.is_segwit() assert any(bool(w) for w in tx.vwitness) assert any(bool(tx_in.script_witness) for tx_in in tx.vin) assert not tx.is_coinbase()
def test_empty_stack() -> None: utxo = TxOut( 100000000, serialize([ "OP_1", "cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf" ]), ) tx_in = TxIn(OutPoint(), "", 1, Witness([])) tx = Tx(vin=[tx_in], vout=[TxOut(100000000, "")]) err_msg = "Empty stack" with pytest.raises(BTClibValueError, match=err_msg): sig_hash.from_tx([utxo], tx, 0, 0)
def test_wrapped_p2tr() -> None: script = [ "OP_1", "cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaf", ] utxo = TxOut( 100000000, serialize(["OP_HASH160", hash160(serialize(script)), "OP_EQUAL"])) tx_in = TxIn(OutPoint(), serialize(script), 1, Witness(["0A" * 32])) tx = Tx(vin=[tx_in], vout=[TxOut(100000000, "")]) err_msg = "Taproot scripts cannot be wrapped in p2sh" with pytest.raises(BTClibValueError, match=err_msg): sig_hash.from_tx([utxo], tx, 0, 0)
def __init__( self, prev_out: OutPoint = OutPoint(), script_sig: Octets = b"", sequence: int = 0, script_witness: Witness = Witness(), check_validity: bool = True, ) -> None: self.prev_out = prev_out self.script_sig = bytes_from_octets(script_sig) self.sequence = sequence self.script_witness = script_witness if check_validity: self.assert_valid()
def test_invalid_taproot_key_path() -> None: fname = "tapscript_test_vector.json" filename = path.join(path.dirname(__file__), "_data", fname) with open(filename, "r", encoding="ascii") as file_: data = json.load(file_) for x in filter(lambda x: "failure" in x.keys(), data): tx = Tx.parse(x["tx"]) prevouts = [TxOut.parse(prevout) for prevout in x["prevouts"]] index = x["index"] if not is_p2tr(prevouts[index].script_pub_key.script): continue witness = Witness(x["failure"]["witness"]) tx.vin[index].script_witness = witness # check only key paths if (len(witness.stack) == 1 or len(witness.stack) == 2 and witness.stack[-1][0] == 0x50): with pytest.raises( (BTClibRuntimeError, BTClibValueError, AssertionError)): assert not x["failure"]["scriptSig"] sighash_type = 0 # all signature = witness.stack[0][:64] if len(witness.stack[0]) == 65: sighash_type = witness.stack[0][-1] if sighash_type == 0: raise BTClibValueError( "invalid sighash 0 in 65 bytes signature") msg_hash = sig_hash.from_tx(prevouts, tx, index, sighash_type) pub_key = type_and_payload( prevouts[index].script_pub_key.script)[1] ssa.assert_as_valid_(msg_hash, pub_key, signature)
def test_native_p2wsh() -> None: tx_bytes = "0100000002fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e0000000000ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac00000000" tx = Tx.parse(tx_bytes) tx.vin[1].script_witness = Witness([ "21026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac" ]) utxo = TxOut( 4900000000, "00205d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0", ) hash_ = sign_hash.from_utxo(utxo, tx, 1, sign_hash.SINGLE) assert hash_ == bytes.fromhex( "82dde6e4f1e94d02c2b7ad03d2115d691f48d064e9d52f58194a6637e4194391") script_ = sign_hash.witness_v0_script( tx.vin[1].script_witness.stack[-1])[1] hash_ = sign_hash.segwit_v0(script_, tx, 1, sign_hash.SINGLE, utxo.value) assert hash_ == bytes.fromhex( "fef7bd749cce710c5c052bd796df1af0d935e59cea63736268bcbe2d2134fc47")
def test_valid_taproot_key_path() -> None: fname = "tapscript_test_vector.json" filename = path.join(path.dirname(__file__), "_data", fname) with open(filename, "r", encoding="ascii") as file_: data = json.load(file_) for x in filter(lambda x: "TAPROOT" in x["flags"], data): tx = Tx.parse(x["tx"]) prevouts = [TxOut.parse(prevout) for prevout in x["prevouts"]] index = x["index"] if not is_p2tr(prevouts[index].script_pub_key.script): continue assert not x["success"]["scriptSig"] witness = Witness(x["success"]["witness"]) tx.vin[index].script_witness = witness if (len(witness.stack) == 1 or len(witness.stack) == 2 and witness.stack[-1][0] == 0x50): sighash_type = 0 # all signature = witness.stack[0][:64] if len(witness.stack[0]) == 65: sighash_type = witness.stack[0][-1] assert sighash_type != 0 msg_hash = sig_hash.from_tx(prevouts, tx, index, sighash_type) pub_key = type_and_payload( prevouts[index].script_pub_key.script)[1] ssa.assert_as_valid_(msg_hash, pub_key, signature)
def parse( cls: Type["PsbtIn"], input_map: Mapping[bytes, bytes], check_validity: bool = True, ) -> "PsbtIn": "Return a PsbtIn by parsing binary data." # FIX parse must use BinaryData non_witness_utxo = None witness_utxo = None partial_sigs: Dict[Octets, Octets] = {} sig_hash_type = None redeem_script = b"" witness_script = b"" hd_key_paths: Dict[Octets, BIP32KeyOrigin] = {} final_script_sig = b"" final_script_witness = Witness() ripemd160_preimages: Dict[Octets, Octets] = {} sha256_preimages: Dict[Octets, Octets] = {} hash160_preimages: Dict[Octets, Octets] = {} hash256_preimages: Dict[Octets, Octets] = {} unknown: Dict[Octets, Octets] = {} for k, v in input_map.items(): if k[:1] == PSBT_IN_NON_WITNESS_UTXO: non_witness_utxo = deserialize_tx(k, v, "non-witness utxo") elif k[:1] == PSBT_IN_WITNESS_UTXO: witness_utxo = _deserialize_witness_utxo(k, v) elif k[:1] == PSBT_IN_PARTIAL_SIG: partial_sigs[k[1:]] = v elif k[:1] == PSBT_IN_SIG_HASH_TYPE: sig_hash_type = deserialize_int(k, v, "sig_hash type") elif k[:1] == PSBT_IN_REDEEM_SCRIPT: redeem_script = deserialize_bytes(k, v, "redeem script") elif k[:1] == PSBT_IN_WITNESS_SCRIPT: witness_script = deserialize_bytes(k, v, "witness script") elif k[:1] == PSBT_IN_BIP32_DERIVATION: hd_key_paths[k[1:]] = BIP32KeyOrigin.parse(v) elif k[:1] == PSBT_IN_FINAL_SCRIPTSIG: final_script_sig = deserialize_bytes(k, v, "final script_sig") elif k[:1] == PSBT_IN_RIPEMD160: ripemd160_preimages[k[1:]] = v elif k[:1] == PSBT_IN_SHA256: sha256_preimages[k[1:]] = v elif k[:1] == PSBT_IN_HASH160: hash160_preimages[k[1:]] = v elif k[:1] == PSBT_IN_HASH256: hash256_preimages[k[1:]] = v elif k[:1] == PSBT_IN_FINAL_SCRIPTWITNESS: final_script_witness = _deserialize_final_script_witness(k, v) else: # unknown unknown[k] = v return cls( non_witness_utxo, witness_utxo, partial_sigs, sig_hash_type, redeem_script, witness_script, hd_key_paths, final_script_sig, final_script_witness, ripemd160_preimages, sha256_preimages, hash160_preimages, hash256_preimages, unknown, check_validity, )
def test_tx() -> None: # default constructor tx = Tx() assert not tx.is_segwit() assert not any(bool(w) for w in tx.vwitness) assert not any(bool(tx_in.script_witness) for tx_in in tx.vin) assert not tx.is_coinbase() assert tx.version == 1 assert tx.lock_time == 0 assert not tx.vin assert not tx.vout assert tx.nVersion == tx.version assert tx.nLockTime == tx.lock_time tx_id = "d21633ba23f70118185227be58a63527675641ad37967e2aa461559f577aec43" assert tx.id.hex() == tx_id assert tx.hash == tx.id assert tx.size == 10 assert tx.vsize == tx.size assert tx.weight == tx.size * 4 tx_2 = Tx.from_dict(tx.to_dict()) assert tx_2.is_segwit() == tx.is_segwit() assert tx_2 == tx tx_2 = Tx.parse(tx.serialize(include_witness=True)) assert tx_2.is_segwit() == tx.is_segwit() assert tx_2 == tx tx_2 = Tx.parse(tx.serialize(include_witness=False)) assert not tx_2.is_segwit() assert tx_2 == tx # non-default constructor, no segwit prev_out = OutPoint( "9dcfdb5836ecfe146bdaa896605ba21222f83cd014dd47adde14fab2aba7de9b", 1) script_sig = b"" sequence = 0xFFFFFFFF tx_in = TxIn(prev_out, script_sig, sequence) tx_out1 = TxOut(2500000, "a914f987c321394968be164053d352fc49763b2be55c87") tx_out2 = TxOut( 6381891, "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d") version = 1 lock_time = 0 tx = Tx(version, lock_time, [tx_in], [tx_out1, tx_out2]) assert not tx.is_segwit() assert not any(bool(w) for w in tx.vwitness) assert not any(bool(tx_in.script_witness) for tx_in in tx.vin) assert not tx.is_coinbase() assert tx.version == 1 assert tx.lock_time == 0 assert len(tx.vin) == 1 assert len(tx.vout) == 2 assert tx.nVersion == tx.version assert tx.nLockTime == tx.lock_time tx_id = "4e52f7848dab7dd89ef7ba477939574198a170bfcb2fb34355c69f5e0169f63c" assert tx.id.hex() == tx_id assert tx.hash == tx.id assert tx.size == 126 assert tx.vsize == tx.size assert tx.weight == tx.size * 4 tx_2 = Tx.from_dict(tx.to_dict()) assert tx_2.is_segwit() == tx.is_segwit() assert tx_2 == tx tx_2 = Tx.parse(tx.serialize(include_witness=True)) assert tx_2.is_segwit() == tx.is_segwit() assert tx_2 == tx tx_2 = Tx.parse(tx.serialize(include_witness=False)) assert not tx_2.is_segwit() assert tx_2 == tx # non-default constructor, with segwit version = 1 lock_time = 0 tx = Tx(version, lock_time, [tx_in], [tx_out1, tx_out2]) stack = [ "", "30440220421fbbedf2ee096d6289b99973509809d5e09589040d5e0d453133dd11b2f78a02205686dbdb57e0c44e49421e9400dd4e931f1655332e8d078260c9295ba959e05d01", "30440220398f141917e4525d3e9e0d1c6482cb19ca3188dc5516a3a5ac29a0f4017212d902204ea405fae3a58b1fc30c5ad8ac70a76ab4f4d876e8af706a6a7b4cd6fa100f4401", "52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae", ] tx.vin[0].script_witness = Witness(stack) assert tx.is_segwit() assert any(bool(w) for w in tx.vwitness) assert any(bool(tx_in.script_witness) for tx_in in tx.vin) assert not tx.is_coinbase() assert tx.version == 1 assert tx.lock_time == 0 assert len(tx.vin) == 1 assert len(tx.vout) == 2 assert tx.nVersion == tx.version assert tx.nLockTime == tx.lock_time tx_id = "4e52f7848dab7dd89ef7ba477939574198a170bfcb2fb34355c69f5e0169f63c" assert tx.id.hex() == tx_id hash_ = "d39eb3e3954be4bdc0b3be2d980124b1e1e11fb414b886b52939b07d95a58a8f" assert tx.hash.hex() == hash_ assert tx.size == 380 assert tx.vsize == 190 assert tx.weight == 758 tx_2 = Tx.from_dict(tx.to_dict()) assert tx_2.is_segwit() == tx.is_segwit() assert tx_2 == tx tx_2 = Tx.parse(tx.serialize(include_witness=True)) assert tx_2.is_segwit() == tx.is_segwit() assert tx_2 == tx tx_2 = Tx.parse(tx.serialize(include_witness=False)) assert not tx_2.is_segwit() assert tx_2 != tx
def test_tx_in() -> None: tx_in = TxIn() assert tx_in.prev_out == OutPoint() assert tx_in.script_sig == b"" assert tx_in.sequence == 0 assert tx_in.outpoint == tx_in.prev_out assert tx_in.scriptSig == tx_in.script_sig assert tx_in.nSequence == tx_in.nSequence assert tx_in.is_coinbase() assert not tx_in.is_segwit() tx_in2 = TxIn.parse(tx_in.serialize()) assert not tx_in2.is_segwit() assert tx_in == tx_in2 tx_in2 = TxIn.from_dict(tx_in.to_dict()) assert not tx_in2.is_segwit() assert tx_in == tx_in2 tx_id = "d5b5982254eebca64e4b42a3092a10bfb76ab430455b2bf0cf7c4f7f32db1c2e" vout = 0 prev_out = OutPoint(tx_id, vout) script_sig = b"" sequence = 0 tx_in = TxIn(prev_out, script_sig, sequence) assert tx_in.prev_out == prev_out assert tx_in.script_sig == script_sig assert tx_in.sequence == sequence assert tx_in.outpoint == tx_in.prev_out assert tx_in.scriptSig == tx_in.script_sig assert tx_in.nSequence == tx_in.nSequence assert not tx_in.is_coinbase() assert not tx_in.is_segwit() tx_in2 = TxIn.parse(tx_in.serialize()) assert not tx_in2.is_segwit() assert tx_in == tx_in2 tx_in2 = TxIn.from_dict(tx_in.to_dict()) assert not tx_in2.is_segwit() assert tx_in == tx_in2 prev_out = OutPoint( "9dcfdb5836ecfe146bdaa896605ba21222f83cd014dd47adde14fab2aba7de9b", 1 ) script_sig = b"" sequence = 0xFFFFFFFF tx_in = TxIn(prev_out, script_sig, sequence) stack = [ "", "30440220421fbbedf2ee096d6289b99973509809d5e09589040d5e0d453133dd11b2f78a02205686dbdb57e0c44e49421e9400dd4e931f1655332e8d078260c9295ba959e05d01", "30440220398f141917e4525d3e9e0d1c6482cb19ca3188dc5516a3a5ac29a0f4017212d902204ea405fae3a58b1fc30c5ad8ac70a76ab4f4d876e8af706a6a7b4cd6fa100f4401", "52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae", ] tx_in.script_witness = Witness(stack) assert tx_in.prev_out == prev_out assert tx_in.script_sig == script_sig assert tx_in.sequence == sequence assert tx_in.outpoint == tx_in.prev_out assert tx_in.scriptSig == tx_in.script_sig assert tx_in.nSequence == tx_in.nSequence assert not tx_in.is_coinbase() assert tx_in.is_segwit() tx_in2 = TxIn.parse(tx_in.serialize()) assert not tx_in2.is_segwit() assert tx_in == tx_in2 or TX_IN_COMPARES_WITNESS tx_in2 = TxIn.from_dict(tx_in.to_dict()) assert tx_in2.is_segwit() assert tx_in == tx_in2 assert tx_in != OutPoint() tx_in.sequence = 0xFFFFFFFF + 1 with pytest.raises(BTClibValueError, match="invalid sequence: "): tx_in.assert_valid()
def test_exceptions() -> None: # from creator example psbt_str = "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=" psbt = Psbt.b64decode(psbt_str) psbt.outputs[0].redeem_script = "bad script" # type: ignore with pytest.raises(TypeError): psbt.serialize() psbt = Psbt.b64decode(psbt_str) psbt.inputs[0].witness_script = "bad script" # type: ignore with pytest.raises(TypeError): psbt.serialize() psbt = Psbt.b64decode(psbt_str) psbt.outputs[0].unknown = {"bad key": b""} # type: ignore with pytest.raises(TypeError): psbt.serialize() psbt = Psbt.b64decode(psbt_str) psbt.outputs[0].unknown = {b"deadbeef": "bad value"} # type: ignore with pytest.raises(TypeError): psbt.serialize() psbt = Psbt.b64decode(psbt_str) psbt.inputs[0].sig_hash_type = 101 with pytest.raises(BTClibValueError, match="invalid sign_hash type: "): psbt.serialize() psbt = Psbt.b64decode(psbt_str) psbt.inputs[0].final_script_sig = "bad script" # type: ignore with pytest.raises(TypeError): psbt.serialize() psbt = Psbt.b64decode(psbt_str) _, Q = dsa.gen_keys() pub_key = sec_point.bytes_from_point(Q) r = s = int.from_bytes(bytes.fromhex("FF" * 32), byteorder="big", signed=False) sig_bytes = der.Sig(r, s, check_validity=False).serialize(check_validity=False) psbt.inputs[0].partial_sigs = {pub_key: sig_bytes} with pytest.raises(BTClibValueError, match="invalid partial signature: "): psbt.serialize() pub_key = bytes.fromhex("02" + 31 * "00" + "07") psbt.inputs[0].partial_sigs = {pub_key: sig_bytes} with pytest.raises(BTClibValueError, match="invalid partial signature pub_key: "): psbt.serialize() psbt = Psbt.b64decode(psbt_str) err_msg = "invalid version: " psbt.version = -1 with pytest.raises(BTClibValueError, match=err_msg): psbt.serialize() psbt.version = 0xFFFFFFFF + 1 with pytest.raises(BTClibValueError, match=err_msg): psbt.serialize() psbt.version = 1 # TODO: add to test vectors with pytest.raises(BTClibValueError, match="invalid non-zero version: "): psbt.serialize() psbt = Psbt.b64decode(psbt_str) psbt_bin = psbt.serialize() psbt_bin = psbt_bin.replace(PSBT_SEPARATOR, PSBT_DELIMITER) # TODO: add to test vectors with pytest.raises(BTClibValueError, match="malformed psbt: missing separator"): Psbt.parse(psbt_bin) psbt = Psbt.b64decode(psbt_str) psbt.inputs.pop() err_msg = "mismatched number of psb.tx.vin and psb.inputs: " # TODO: add to test vectors with pytest.raises(BTClibValueError, match=err_msg): psbt.serialize() psbt = Psbt.b64decode(psbt_str) psbt.tx.vin[0].script_witness = Witness([b""]) err_msg = "non empty script_sig or witness" # TODO: add to test vectors with pytest.raises(BTClibValueError, match=err_msg): psbt.serialize() psbt = Psbt.b64decode(psbt_str) psbt.outputs.pop() err_msg = "mismatched number of psb.tx.vout and psbt.outputs: " # TODO: add to test vectors with pytest.raises(BTClibValueError, match=err_msg): psbt.serialize() psbt_str = "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA" psbt = Psbt.b64decode(psbt_str) psbt.tx.vin[0].prev_out.tx_id, psbt.tx.vin[1].prev_out.tx_id = ( psbt.tx.vin[1].prev_out.tx_id, psbt.tx.vin[0].prev_out.tx_id, ) err_msg = "mismatched non-witness utxo / outpoint tx_id" with pytest.raises(BTClibValueError, match=err_msg): psbt.assert_valid()
def parse( cls: Type[_PsbtIn], input_map: Mapping[bytes, bytes], check_validity: bool = True, ) -> _PsbtIn: "Return a PsbtIn by parsing binary data." # FIX parse must use BinaryData non_witness_utxo = None witness_utxo = None partial_sigs: Dict[Octets, Octets] = {} sig_hash_type = None redeem_script = b"" witness_script = b"" hd_key_paths: Dict[Octets, BIP32KeyOrigin] = {} final_script_sig = b"" final_script_witness = Witness() unknown: Dict[Octets, Octets] = {} for k, v in input_map.items(): if k[:1] == PSBT_IN_NON_WITNESS_UTXO: if non_witness_utxo: raise BTClibValueError("duplicate PsbtIn non_witness_utxo") non_witness_utxo = deserialize_tx(k, v, "non-witness utxo") elif k[:1] == PSBT_IN_WITNESS_UTXO: if witness_utxo: raise BTClibValueError("duplicate PsbtIn witness_utxo") witness_utxo = _deserialize_witness_utxo(k, v) elif k[:1] == PSBT_IN_PARTIAL_SIG: if k[1:] in partial_sigs: raise BTClibValueError("duplicate PsbtIn partial_sigs") partial_sigs[k[1:]] = v elif k[:1] == PSBT_IN_SIG_HASH_TYPE: if sig_hash_type: raise BTClibValueError("duplicate PsbtIn sig_hash_type") sig_hash_type = deserialize_int(k, v, "sign_hash type") elif k[:1] == PSBT_IN_REDEEM_SCRIPT: if redeem_script: raise BTClibValueError("duplicate PsbtIn redeem_script") redeem_script = deserialize_bytes(k, v, "redeem script") elif k[:1] == PSBT_IN_WITNESS_SCRIPT: if witness_script: raise BTClibValueError("duplicate PsbtIn witness_script") witness_script = deserialize_bytes(k, v, "witness script") elif k[:1] == PSBT_IN_BIP32_DERIVATION: if k[1:] in hd_key_paths: raise BTClibValueError( "duplicate pub_key in PsbtIn hd_key_path") hd_key_paths[k[1:]] = BIP32KeyOrigin.parse(v) elif k[:1] == PSBT_IN_FINAL_SCRIPTSIG: if final_script_sig: raise BTClibValueError("duplicate PsbtIn final_script_sig") final_script_sig = deserialize_bytes(k, v, "final script_sig") elif k[:1] == PSBT_IN_FINAL_SCRIPTWITNESS: if final_script_witness: raise BTClibValueError( "duplicate PsbtIn final_script_witness") final_script_witness = _deserialize_final_script_witness(k, v) else: # unknown if k in unknown: raise BTClibValueError("duplicate PsbtIn unknown") unknown[k] = v return cls( non_witness_utxo, witness_utxo, partial_sigs, sig_hash_type, redeem_script, witness_script, hd_key_paths, final_script_sig, final_script_witness, unknown, check_validity, )