mempool_accept_wtxid.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2021-present 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 """
6 Test mempool acceptance in case of an already known transaction
7 with identical non-witness data but different witness.
8 """
9
10 from test_framework.p2p import P2PTxInvStore
11 from test_framework.script_util import build_malleated_tx_package
12 from test_framework.test_framework import BitcoinTestFramework
13 from test_framework.util import (
14 assert_not_equal,
15 assert_equal,
16 )
17 from test_framework.wallet import (
18 MiniWallet,
19 )
20
21 class MempoolWtxidTest(BitcoinTestFramework):
22 def set_test_params(self):
23 self.num_nodes = 1
24
25 def run_test(self):
26 node = self.nodes[0]
27 mini_wallet = MiniWallet(node)
28 self.log.info('Start with pre-generated blocks')
29
30 assert_equal(node.getmempoolinfo()['size'], 0)
31
32 self.log.info("Submit parent with multiple script branches to mempool")
33
34 parent = mini_wallet.create_self_transfer()["tx"]
35 parent_amount = parent.vout[0].nValue - 10000
36 child_amount = parent_amount - 10000
37 parent, child_one, child_two = build_malleated_tx_package(
38 parent=parent,
39 rebalance_parent_output_amount=parent_amount,
40 child_amount=child_amount
41 )
42
43 mini_wallet.sendrawtransaction(from_node=node, tx_hex=parent.serialize().hex())
44
45 self.generate(node, 1)
46
47 peer_wtxid_relay = node.add_p2p_connection(P2PTxInvStore())
48
49 assert_equal(child_one.txid_hex, child_two.txid_hex)
50 assert_not_equal(child_one.wtxid_hex, child_two.wtxid_hex)
51
52 self.log.info("Submit child_one to the mempool")
53 txid_submitted = node.sendrawtransaction(child_one.serialize().hex())
54 assert_equal(node.getmempoolentry(txid_submitted)['wtxid'], child_one.wtxid_hex)
55 peer_wtxid_relay.wait_for_broadcast([child_one.wtxid_hex])
56 assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)
57
58 # testmempoolaccept reports the "already in mempool" error
59 assert_equal(node.testmempoolaccept([child_one.serialize().hex()]), [{
60 "txid": child_one.txid_hex,
61 "wtxid": child_one.wtxid_hex,
62 "allowed": False,
63 "reject-reason": "txn-already-in-mempool",
64 "reject-details": "txn-already-in-mempool"
65 }])
66 assert_equal(node.testmempoolaccept([child_two.serialize().hex()])[0], {
67 "txid": child_two.txid_hex,
68 "wtxid": child_two.wtxid_hex,
69 "allowed": False,
70 "reject-reason": "txn-same-nonwitness-data-in-mempool",
71 "reject-details": "txn-same-nonwitness-data-in-mempool"
72 })
73
74 # sendrawtransaction will not throw but quits early when the exact same transaction is already in mempool
75 node.sendrawtransaction(child_one.serialize().hex())
76
77 self.log.info("Connect another peer that hasn't seen child_one before")
78 peer_wtxid_relay_2 = node.add_p2p_connection(P2PTxInvStore())
79
80 self.log.info("Submit child_two to the mempool")
81 # sendrawtransaction will not throw but quits early when a transaction with the same non-witness data is already in mempool
82 node.sendrawtransaction(child_two.serialize().hex())
83
84 # The node should rebroadcast the transaction using the wtxid of the correct transaction
85 # (child_one, which is in its mempool).
86 peer_wtxid_relay_2.wait_for_broadcast([child_one.wtxid_hex])
87 assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)
88
89 if __name__ == '__main__':
90 MempoolWtxidTest(__file__).main()
91