def test_cfilter(self): stop_hash = bytes.fromhex( "000000006f27ddfe1dd680044a34548f41bed47eba9e6f0b310da21423bc5f33" ) getcfilters = GetCFiltersMessage(stop_hash=stop_hash) expected = b"\x00\x01\x00\x00\x00" + stop_hash[::-1] self.assertEqual(getcfilters.serialize(), expected) expected = ( b"\x00" + stop_hash[::-1] + b"\x09" + bytes.fromhex("0385acb4f0fe889ef0") ) cfilter = CFilterMessage.parse(BytesIO(expected)) self.assertEqual(cfilter.filter_type, 0) self.assertEqual(cfilter.block_hash, stop_hash) self.assertEqual(cfilter.cf.hashes, {1341840, 1483084, 570774}) included = Script.parse( BytesIO( bytes.fromhex( "22002027a5000c7917f785d8fc6e5a55adfca8717ecb973ebb7743849ff956d896a7ed" ) ) ) self.assertTrue(included in cfilter) self.assertFalse(Script() in cfilter) with self.assertRaises(RuntimeError): GetCFiltersMessage()
def finalize_p2sh_p2wsh_multisig(self, signatures, witness_script): """Puts together the signatures for a p2sh-p2wsh input so the input verifies.""" # the format for multisig is [b'', each signature, then the WitnessScript (raw-serialization)] items = [b"", *signatures, witness_script.raw_serialize()] # set the witness of the input to be these items self.witness = Witness(items) # the RedeemScript is the p2wsh ScriptPubKey of the WitnessScript redeem_script = witness_script.script_pubkey() # set the ScriptSig of the tx_in to be a new script, which is just the RedeemScript raw-serialized self.script_sig = Script([redeem_script.raw_serialize()])
def finalize_p2wpkh(self, sig, sec, redeem_script=None): """Puts together the ScriptSig and Witness for a p2wpkh input so the input verifies.""" # if the RedeemScript is given, the ScriptSig is a single element Script of its raw serialization if redeem_script: self.script_sig = Script([redeem_script.raw_serialize()]) # else the ScriptSig should be empty else: self.script_sig = Script() # the Witness for p2wpkh is [sig, sec] self.witness = Witness([sig, sec])
def __init__(self, prev_tx, prev_index, script_sig=None, sequence=None): self.prev_tx = prev_tx self.prev_index = prev_index if script_sig is None: self.script_sig = Script() else: self.script_sig = script_sig if sequence is None: self.sequence = Sequence() else: self.sequence = Sequence(sequence) self._value = None self._script_pubkey = None self.witness = Witness() self.tap_script = None
def parse_item(item): if type(item) == dict: tapleaf_version = item["leafVersion"] tap_script = Script.parse( BytesIO(encode_varstr(bytes.fromhex(item["script"]))) ) tap_leaf = TapLeaf(tap_script, tapleaf_version) return tap_leaf else: return TapBranch(parse_item(item[0]), parse_item(item[1]))
def test_parse(self): script_pubkey = BytesIO( bytes.fromhex( "6a47304402207899531a52d59a6de200179928ca900254a36b8dff8bb75f5f5d71b1cdc26125022008b422690b8461cb52c3cc30330b23d574351872b7c361e9aae3649071c1a7160121035d5c93d9ac96881f19ba1f686f15f009ded7c62efe85a872e6a19b43c15a2937" ) ) script = Script.parse(script_pubkey) want = bytes.fromhex( "304402207899531a52d59a6de200179928ca900254a36b8dff8bb75f5f5d71b1cdc26125022008b422690b8461cb52c3cc30330b23d574351872b7c361e9aae3649071c1a71601" ) self.assertEqual(script.commands[0].hex(), want.hex()) want = bytes.fromhex( "035d5c93d9ac96881f19ba1f686f15f009ded7c62efe85a872e6a19b43c15a2937" ) self.assertEqual(script.commands[1], want)
def parse(cls, s): """Takes a byte stream and parses the tx_input at the start return a TxIn object """ # s.read(n) will return n bytes # prev_tx is 32 bytes, little endian prev_tx = s.read(32)[::-1] # prev_index is 4 bytes, little endian, interpret as int prev_index = little_endian_to_int(s.read(4)) # script_sig is a variable field (length followed by the data) # you can use Script.parse to get the actual script script_sig = Script.parse(s) # sequence is 4 bytes, little-endian, interpret as int sequence = Sequence.parse(s) # return an instance of the class (cls(...)) return cls(prev_tx, prev_index, script_sig, sequence)
def test_op_cltv(self): locktime_0 = Locktime(1234) locktime_1 = Locktime(2345) sequence = Sequence() tx_in = TxIn(b"\x00" * 32, 0, sequence=sequence) tx_out = TxOut(1, Script()) tx_obj = Tx(1, [tx_in], [tx_out], locktime_1) stack = [] self.assertFalse(op_checklocktimeverify(stack, tx_obj, 0)) tx_in.sequence = Sequence(0xFFFFFFFE) self.assertFalse(op_checklocktimeverify(stack, tx_obj, 0)) stack = [encode_num(-5)] self.assertFalse(op_checklocktimeverify(stack, tx_obj, 0)) stack = [encode_num(locktime_0)] self.assertTrue(op_checklocktimeverify(stack, tx_obj, 0)) tx_obj.locktime = Locktime(1582820194) self.assertFalse(op_checklocktimeverify(stack, tx_obj, 0)) tx_obj.locktime = Locktime(500) self.assertFalse(op_checklocktimeverify(stack, tx_obj, 0))
def test_cfilter_without_network(self): # Example from Trezor Blog Post (https://blog.trezor.io/bip158-compact-block-filters-9b813b07a878) block_hash_hex = ( "000000000000015d6077a411a8f5cc95caf775ccf11c54e27df75ce58d187313" ) block_hash = bytes.fromhex(block_hash_hex) filter_hex = "09027acea61b6cc3fb33f5d52f7d088a6b2f75d234e89ca800" filter_bytes = bytes.fromhex(filter_hex) cfilter = CFilterMessage( filter_type=BASIC_FILTER_TYPE, block_hash=block_hash, filter_bytes=filter_bytes, ) for raw_script, want in ( ("1976a9143ebc40e411ed3c76f86711507ab952300890397288ac", True), ("1976a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac", True), ("1976a914000000000000000000000000000000000000000088ac", False), # made up ): script = Script.parse(BytesIO(bytes.fromhex(raw_script))) self.assertEqual(script in cfilter, want)
def test_op_csv(self): sequence_0 = Sequence() sequence_1 = Sequence(2345) tx_in = TxIn(b"\x00" * 32, 0, sequence=sequence_0) tx_out = TxOut(1, Script()) tx_obj = Tx(1, [tx_in], [tx_out]) stack = [] self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) tx_in.sequence = sequence_1 self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) stack = [encode_num(-5)] self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) tx_obj.version = 2 self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) stack = [encode_num(1234 | (1 << 22))] self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) stack = [encode_num(9999)] self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) stack = [encode_num(1234)] self.assertTrue(op_checksequenceverify(stack, tx_obj, 0))
def test_p2tr_spending(self): test = { "given": { "rawUnsignedTx": "02000000097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a418420000000000fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0065cd1d", "utxosSpent": [ { "scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343", "amountSats": 420000000, }, { "scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3", "amountSats": 462000000, }, { "scriptPubKey": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac", "amountSats": 294000000, }, { "scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e", "amountSats": 504000000, }, { "scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605", "amountSats": 630000000, }, { "scriptPubKey": "00147dd65592d0ab2fe0d0257d571abf032cd9db93dc", "amountSats": 378000000, }, { "scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831", "amountSats": 672000000, }, { "scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5", "amountSats": 546000000, }, { "scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220", "amountSats": 588000000, }, ], }, "intermediary": { "hashAmounts": "58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde6", "hashOutputs": "a2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc5", "hashPrevouts": "e3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f", "hashScriptPubkeys": "23ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e21", "hashSequences": "18959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e", }, "inputSpending": [ { "given": { "txinIndex": 0, "internalPrivkey": "6b973d88838f27366ed61c9ad6367663045cb456e28335c109e30717ae0c6baa", "merkleRoot": None, "hashType": 3, }, "intermediary": { "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d", "tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70", "tweakedPrivkey": "2405b971772ad26915c8dcdf10f238753a9b837e5f8e6a86fd7c0cce5b7296d9", "sigMsg": "0003020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0000000000d0418f0e9a36245b9a50ec87f8bf5be5bcae434337b87139c3a5b1f56e33cba0", "precomputedUsed": [ "hashAmounts", "hashPrevouts", "hashScriptPubkeys", "hashSequences", ], "sigHash": "2514a6272f85cfa0f45eb907fcb0d121b808ed37c6ea160a5a9046ed5526d555", }, "expected": { "witness": [ "ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c03" ] }, }, { "given": { "txinIndex": 1, "internalPrivkey": "1e4da49f6aaf4e5cd175fe08a32bb5cb4863d963921255f33d3bc31e1343907f", "merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21", "hashType": 131, }, "intermediary": { "internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27", "tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001", "tweakedPrivkey": "ea260c3b10e60f6de018455cd0278f2f5b7e454be1999572789e6a9565d26080", "sigMsg": "0083020000000065cd1d00d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd9900000000808f891b00000000225120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3ffffffffffcef8fb4ca7efc5433f591ecfc57391811ce1e186a3793024def5c884cba51d", "precomputedUsed": [], "sigHash": "325a644af47e8a5a2591cda0ab0723978537318f10e6a63d4eed783b96a71a4d", }, "expected": { "witness": [ "052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83" ] }, }, { "given": { "txinIndex": 3, "internalPrivkey": "d3c7af07da2d54f7a7735d3d0fc4f0a73164db638b2f2f7c43f711f6d4aa7e64", "merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b", "hashType": 1, }, "intermediary": { "internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820", "tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30", "tweakedPrivkey": "97323385e57015b75b0339a549c56a948eb961555973f0951f555ae6039ef00d", "sigMsg": "0001020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50003000000", "precomputedUsed": [ "hashAmounts", "hashOutputs", "hashPrevouts", "hashScriptPubkeys", "hashSequences", ], "sigHash": "bf013ea93474aa67815b1b6cc441d23b64fa310911d991e713cd34c7f5d46669", }, "expected": { "witness": [ "ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a01" ] }, }, { "given": { "txinIndex": 4, "internalPrivkey": "f36bb07a11e469ce941d16b63b11b9b9120a84d9d87cff2c84a8d4affb438f4e", "merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2", "hashType": 0, }, "intermediary": { "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f", "tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4", "tweakedPrivkey": "a8e7aa924f0d58854185a490e6c41f6efb7b675c0f3331b7f14b549400b4d501", "sigMsg": "0000020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50004000000", "precomputedUsed": [ "hashAmounts", "hashOutputs", "hashPrevouts", "hashScriptPubkeys", "hashSequences", ], "sigHash": "4f900a0bae3f1446fd48490c2958b5a023228f01661cda3496a11da502a7f7ef", }, "expected": { "witness": [ "b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f" ] }, }, { "given": { "txinIndex": 6, "internalPrivkey": "415cfe9c15d9cea27d8104d5517c06e9de48e2f986b695e4f5ffebf230e725d8", "merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def", "hashType": 2, }, "intermediary": { "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d", "tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9", "tweakedPrivkey": "241c14f2639d0d7139282aa6abde28dd8a067baa9d633e4e7230287ec2d02901", "sigMsg": "0002020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0006000000", "precomputedUsed": [ "hashAmounts", "hashPrevouts", "hashScriptPubkeys", "hashSequences", ], "sigHash": "15f25c298eb5cdc7eb1d638dd2d45c97c4c59dcaec6679cfc16ad84f30876b85", }, "expected": { "witness": [ "a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee002" ] }, }, { "given": { "txinIndex": 7, "internalPrivkey": "c7b0e81f0a9a0b0499e112279d718cca98e79a12e2f137c72ae5b213aad0d103", "merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef", "hashType": 130, }, "intermediary": { "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592", "tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9", "tweakedPrivkey": "65b6000cd2bfa6b7cf736767a8955760e62b6649058cbc970b7c0871d786346b", "sigMsg": "0082020000000065cd1d00e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf00000000804c8b2000000000225120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5ffffffff", "precomputedUsed": [], "sigHash": "cd292de50313804dabe4685e83f923d2969577191a3e1d2882220dca88cbeb10", }, "expected": { "witness": [ "ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c482" ] }, }, { "given": { "txinIndex": 8, "internalPrivkey": "77863416be0d0665e517e1c375fd6f75839544eca553675ef7fdf4949518ebaa", "merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc", "hashType": 129, }, "intermediary": { "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8", "tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e", "tweakedPrivkey": "ec18ce6af99f43815db543f47b8af5ff5df3b2cb7315c955aa4a86e8143d2bf5", "sigMsg": "0081020000000065cd1da2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc500a778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af101000000002b0c230000000022512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220ffffffff", "precomputedUsed": ["hashOutputs"], "sigHash": "cccb739eca6c13a8a89e6e5cd317ffe55669bbda23f2fd37b0f18755e008edd2", }, "expected": { "witness": [ "bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd981" ] }, }, ], "auxiliary": { "fullySignedTx": "020000000001097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a41842000000006b4830450221008f3b8f8f0537c420654d2283673a761b7ee2ea3c130753103e08ce79201cf32a022079e7ab904a1980ef1c5890b648c8783f4d10103dd62f740d13daa79e298d50c201210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0141ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c030141052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83000141ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a010140b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f0247304402202b795e4de72646d76eab3f0ab27dfa30b810e856ff3a46c9a702df53bb0d8cc302203ccc4d822edab5f35caddb10af1be93583526ccfbade4b4ead350781e2f8adcd012102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f90141a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee0020141ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c4820141bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd9810065cd1d" }, } hex_tx = test["given"]["rawUnsignedTx"] tx_obj = Tx.parse(BytesIO(bytes.fromhex(hex_tx))) self.maxDiff = None self.assertEqual(tx_obj.serialize().hex(), hex_tx) tx_obj.segwit = True for tx_in, utxo in zip(tx_obj.tx_ins, test["given"]["utxosSpent"]): tx_in._value = utxo["amountSats"] tx_in._script_pubkey = Script.parse( BytesIO(encode_varstr(bytes.fromhex(utxo["scriptPubKey"]))) ) shas = test["intermediary"] self.assertEqual(tx_obj.sha_amounts().hex(), shas["hashAmounts"]) self.assertEqual(tx_obj.sha_outputs().hex(), shas["hashOutputs"]) self.assertEqual(tx_obj.sha_prevouts().hex(), shas["hashPrevouts"]) self.assertEqual(tx_obj.sha_script_pubkeys().hex(), shas["hashScriptPubkeys"]) self.assertEqual(tx_obj.sha_sequences().hex(), shas["hashSequences"]) fully_signed = test["auxiliary"]["fullySignedTx"] signed_tx = Tx.parse(BytesIO(bytes.fromhex(fully_signed))) for input_data in test["inputSpending"]: i = input_data["given"]["txinIndex"] secret = big_endian_to_int( bytes.fromhex(input_data["given"]["internalPrivkey"]) ) tx_in = tx_obj.tx_ins[i] private_key = PrivateKey(secret) pubkey = private_key.point hash_type = input_data["given"]["hashType"] self.assertEqual( pubkey.bip340().hex(), input_data["intermediary"]["internalPubkey"] ) mr_hex = input_data["given"]["merkleRoot"] if mr_hex is None: merkle_root = None else: merkle_root = bytes.fromhex(mr_hex) tap_root = TapRoot(pubkey, merkle_root=merkle_root) tweak_want = big_endian_to_int( bytes.fromhex(input_data["intermediary"]["tweak"]) ) self.assertEqual(tap_root.tweak, tweak_want) tweaked_private_key = private_key.tweaked(tap_root.tweak) tweaked_want = big_endian_to_int( bytes.fromhex(input_data["intermediary"]["tweakedPrivkey"]) ) self.assertEqual(tweaked_private_key.secret, tweaked_want) sig_hash_want = input_data["intermediary"]["sigHash"] self.assertEqual( tx_obj.sig_hash_bip341(i, hash_type=hash_type).hex(), sig_hash_want ) tx_obj.sign_input(i, tweaked_private_key, hash_type=hash_type) for j, witness_want in enumerate(input_data["expected"]["witness"]): self.assertEqual(tx_in.witness[j].hex(), witness_want) # the two we can't sign for i in (2, 5): signed_tx.tx_ins[i].script_sig = Script() signed_tx.tx_ins[i].witness = Witness() self.assertEqual(tx_obj.serialize(), signed_tx.serialize())
def finalize_p2sh_multisig(self, signatures, redeem_script): """Puts together the signatures for a p2sh input so the input verifies.""" # the ScriptSig for p2sh multisig is [0, each signature, then the RedeemScript (raw-serialization)] script_sig = Script([0, *signatures, redeem_script.raw_serialize()]) # set the witness of the input to be these items self.script_sig = script_sig
def finalize_p2pkh(self, sig, sec): """Puts together the ScriptSig for a p2pkh input so the input verifies.""" # the ScriptSig for p2pkh is [sig, sec] self.script_sig = Script([sig, sec])
class TxIn: def __init__(self, prev_tx, prev_index, script_sig=None, sequence=None): self.prev_tx = prev_tx self.prev_index = prev_index if script_sig is None: self.script_sig = Script() else: self.script_sig = script_sig if sequence is None: self.sequence = Sequence() else: self.sequence = Sequence(sequence) self._value = None self._script_pubkey = None self.witness = Witness() self.tap_script = None def __repr__(self): return f"{self.prev_tx.hex()}:{self.prev_index}" @classmethod def parse(cls, s): """Takes a byte stream and parses the tx_input at the start return a TxIn object """ # s.read(n) will return n bytes # prev_tx is 32 bytes, little endian prev_tx = s.read(32)[::-1] # prev_index is 4 bytes, little endian, interpret as int prev_index = little_endian_to_int(s.read(4)) # script_sig is a variable field (length followed by the data) # you can use Script.parse to get the actual script script_sig = Script.parse(s) # sequence is 4 bytes, little-endian, interpret as int sequence = Sequence.parse(s) # return an instance of the class (cls(...)) return cls(prev_tx, prev_index, script_sig, sequence) def serialize(self): """Returns the byte serialization of the transaction input""" # serialize prev_tx, little endian result = self.prev_tx[::-1] # serialize prev_index, 4 bytes, little endian result += int_to_little_endian(self.prev_index, 4) # serialize the script_sig result += self.script_sig.serialize() # serialize sequence, 4 bytes, little endian result += self.sequence.serialize() return result def fetch_tx(self, network="mainnet"): return TxFetcher.fetch(self.prev_tx.hex(), network=network) def value(self, network="mainnet"): """Get the outpoint value by looking up the tx hash Returns the amount in satoshi """ if self._value is None: # use self.fetch_tx to get the transaction tx = self.fetch_tx(network=network) # get the output at self.prev_index self._value = tx.tx_outs[self.prev_index].amount return self._value def script_pubkey(self, network="mainnet"): """Get the scriptPubKey by looking up the tx hash Returns a Script object """ if self._script_pubkey is None: # use self.fetch_tx to get the transaction tx = self.fetch_tx(network=network) # get the output at self.prev_index self._script_pubkey = tx.tx_outs[self.prev_index].script_pubkey return self._script_pubkey def finalize_p2pkh(self, sig, sec): """Puts together the ScriptSig for a p2pkh input so the input verifies.""" # the ScriptSig for p2pkh is [sig, sec] self.script_sig = Script([sig, sec]) def finalize_p2wpkh(self, sig, sec, redeem_script=None): """Puts together the ScriptSig and Witness for a p2wpkh input so the input verifies.""" # if the RedeemScript is given, the ScriptSig is a single element Script of its raw serialization if redeem_script: self.script_sig = Script([redeem_script.raw_serialize()]) # else the ScriptSig should be empty else: self.script_sig = Script() # the Witness for p2wpkh is [sig, sec] self.witness = Witness([sig, sec]) def finalize_p2sh_multisig(self, signatures, redeem_script): """Puts together the signatures for a p2sh input so the input verifies.""" # the ScriptSig for p2sh multisig is [0, each signature, then the RedeemScript (raw-serialization)] script_sig = Script([0, *signatures, redeem_script.raw_serialize()]) # set the witness of the input to be these items self.script_sig = script_sig def finalize_p2wsh_multisig(self, signatures, witness_script): """Puts together the signatures for a p2wsh input so the input verifies.""" # the format for multisig is [b'', each signature, then the WitnessScript (raw-serialization)] items = [b"", *signatures, witness_script.raw_serialize()] # set the witness of the input to be these items self.witness = Witness(items) def finalize_p2sh_p2wsh_multisig(self, signatures, witness_script): """Puts together the signatures for a p2sh-p2wsh input so the input verifies.""" # the format for multisig is [b'', each signature, then the WitnessScript (raw-serialization)] items = [b"", *signatures, witness_script.raw_serialize()] # set the witness of the input to be these items self.witness = Witness(items) # the RedeemScript is the p2wsh ScriptPubKey of the WitnessScript redeem_script = witness_script.script_pubkey() # set the ScriptSig of the tx_in to be a new script, which is just the RedeemScript raw-serialized self.script_sig = Script([redeem_script.raw_serialize()]) def finalize_p2tr_keypath(self, sig): self.witness = Witness([sig])
def tap_script(self): if self.has_annex(): raw_tap_script = self.items[-3] else: raw_tap_script = self.items[-2] return Script.parse(BytesIO(encode_varstr(raw_tap_script)))