Skip to content

Commit 0990de5

Browse files
committed
Add functional test for Elements witness serialization
1 parent 931b6e9 commit 0990de5

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

test/functional/feature_txwitness.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2015-2018 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""Test the txwitness for elements
6+
7+
1) Make sure python serialization stuff matches whatever the node/wallet creates, signed and unsigned
8+
2) Segwit transactions have witness data still, and is validated correctly
9+
3) Fast merkle root test? (where oh where are we going to find sha2 internals ripped open in python)
10+
4) transaction round-trips through rpc, p2p, inside and outside of blocks
11+
5) make sure non-segwit transactions have no witness data too
12+
6) If we’re not touching all other tests, we’ll need to actually copy our CTransaction python stuff directly into this test, or another adjacent file, otherwise other tests will still break
13+
7) Try to give it some bitcoin serialization transactions, make sure it fails to decode
14+
15+
"""
16+
17+
from test_framework.messages import CTransaction, CBlock, ser_uint256
18+
from test_framework.test_framework import BitcoinTestFramework
19+
from test_framework.util import assert_equal, bytes_to_hex_str, hex_str_to_bytes
20+
from test_framework import util
21+
22+
from io import BytesIO
23+
24+
class TxWitnessTest(BitcoinTestFramework):
25+
def set_test_params(self):
26+
self.setup_clean_chain = True
27+
self.num_nodes = 2
28+
29+
def assert_tx_format_also_signed(self, utxo, segwit):
30+
raw = self.nodes[0].createrawtransaction(
31+
[{"txid": utxo["txid"], "vout": utxo["vout"]}],
32+
[{self.unknown_addr: "49.9"}]
33+
)
34+
35+
unsigned_decoded = self.nodes[0].decoderawtransaction(raw)
36+
assert_equal(len(unsigned_decoded["vin"]), 1)
37+
assert('txinwitness' not in unsigned_decoded["vin"][0])
38+
39+
# Cross-check python serialization
40+
tx = CTransaction()
41+
tx.deserialize(BytesIO(hex_str_to_bytes(raw)))
42+
assert_equal(tx.vin[0].prevout.hash, int("0x"+utxo["txid"], 0))
43+
# assert re-encoding
44+
serialized = bytes_to_hex_str(tx.serialize())
45+
assert_equal(serialized, raw)
46+
47+
# Now sign and repeat tests
48+
signed_raw = self.nodes[0].signrawtransactionwithwallet(raw)["hex"]
49+
signed_decoded = self.nodes[0].decoderawtransaction(signed_raw)
50+
assert_equal(len(signed_decoded["vin"]), 1)
51+
assert(("txinwitness" in signed_decoded["vin"][0]) == segwit)
52+
53+
# Cross-check python serialization
54+
tx = CTransaction()
55+
tx.deserialize(BytesIO(hex_str_to_bytes(signed_raw)))
56+
assert_equal(tx.vin[0].prevout.hash, int("0x"+utxo["txid"], 0))
57+
assert_equal(bytes_to_hex_str(tx.vin[0].scriptSig), signed_decoded["vin"][0]["scriptSig"]["hex"])
58+
# test witness
59+
if segwit:
60+
wit_decoded = signed_decoded["vin"][0]["txinwitness"]
61+
for i in range(len(wit_decoded)):
62+
assert_equal(bytes_to_hex_str(tx.wit.vtxinwit[0].scriptWitness.stack[i]), wit_decoded[i])
63+
# assert re-encoding
64+
serialized = bytes_to_hex_str(tx.serialize())
65+
assert_equal(serialized, signed_raw)
66+
67+
txid = self.nodes[0].sendrawtransaction(serialized)
68+
nodetx = self.nodes[0].getrawtransaction(txid, 1)
69+
assert_equal(nodetx["txid"], tx.rehash())
70+
# cross-check wtxid report from node
71+
wtxid = bytes_to_hex_str(ser_uint256(tx.calc_sha256(True))[::-1])
72+
assert_equal(nodetx["wtxid"], wtxid)
73+
assert_equal(nodetx["hash"], wtxid)
74+
75+
# witness hash stuff
76+
assert_equal(nodetx["withash"], bytes_to_hex_str(ser_uint256(tx.calc_witness_hash())[::-1]))
77+
return (txid, wtxid)
78+
79+
def test_transaction_serialization(self):
80+
legacy_addr = self.nodes[0].getnewaddress("", "legacy")
81+
p2sh_addr = self.nodes[0].getnewaddress("", "p2sh-segwit")
82+
bech32_addr = self.nodes[0].getnewaddress("", "bech32")
83+
self.unknown_addr = self.nodes[1].getnewaddress()
84+
85+
# directly seed types of utxos required
86+
self.nodes[0].generatetoaddress(1, legacy_addr)
87+
self.nodes[0].generatetoaddress(1, p2sh_addr)
88+
self.nodes[0].generatetoaddress(1, bech32_addr)
89+
self.nodes[0].generatetoaddress(101, self.unknown_addr)
90+
91+
# grab utxos filtering by age
92+
legacy_utxo = self.nodes[0].listunspent(104, 104)[0]
93+
p2sh_utxo = self.nodes[0].listunspent(103, 103)[0]
94+
bech32_utxo = self.nodes[0].listunspent(102, 102)[0]
95+
96+
submitted_txids = []
97+
self.log.info("Testing legacy UTXO")
98+
submitted_txids.append(self.assert_tx_format_also_signed(legacy_utxo, segwit=False))
99+
self.log.info("Testing p2sh UTXO")
100+
submitted_txids.append(self.assert_tx_format_also_signed(p2sh_utxo, segwit=True))
101+
self.log.info("Testing bech32 UTXO")
102+
submitted_txids.append(self.assert_tx_format_also_signed(bech32_utxo, segwit=True))
103+
104+
blockhash = self.nodes[0].generate(1)[0]
105+
hexblock = self.nodes[0].getblock(blockhash, 0)
106+
block_details = self.nodes[0].getblock(blockhash, 2)
107+
block = CBlock()
108+
block.deserialize(BytesIO(hex_str_to_bytes(hexblock)))
109+
assert(len(block.vtx) == len(submitted_txids) + 1)
110+
assert_equal(len(block_details["tx"]), len(block.vtx))
111+
for tx1, tx2 in zip(block.vtx[1:], block_details["tx"][1:]):
112+
# no tuple wildcard, just re-used tx2 on first one
113+
assert((tx1.rehash(), tx2["wtxid"]) in submitted_txids)
114+
assert((tx2["txid"], tx2["hash"]) in submitted_txids)
115+
assert((tx2["txid"], tx2["wtxid"]) in submitted_txids)
116+
block.rehash()
117+
assert_equal(block.hash, self.nodes[0].getbestblockhash())
118+
119+
120+
def run_test(self):
121+
util.node_fastmerkle = self.nodes[0]
122+
self.test_transaction_serialization()
123+
124+
125+
126+
127+
128+
129+
130+
if __name__ == '__main__':
131+
TxWitnessTest().main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
# vv First elements tests vv
6161
'feature_fedpeg.py',
6262
'rpc_calcfastmerkleroot.py',
63+
'feature_txwitness.py',
6364
# Longest test should go first, to favor running tests in parallel
6465
'mempool_packages.py',
6566
'feature_maxuploadtarget.py',

0 commit comments

Comments
 (0)