/ test / functional / mempool_accept_wtxid.py
mempool_accept_wtxid.py
 1  #!/usr/bin/env python3
 2  # Copyright (c) 2021 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.messages import (
11      COIN,
12  )
13  from test_framework.p2p import P2PTxInvStore
14  from test_framework.script_util import ValidWitnessMalleatedTx
15  from test_framework.test_framework import BitcoinTestFramework
16  from test_framework.util import (
17      assert_not_equal,
18      assert_equal,
19  )
20  
21  
22  class MempoolWtxidTest(BitcoinTestFramework):
23      def set_test_params(self):
24          self.num_nodes = 1
25          self.setup_clean_chain = True
26  
27      def run_test(self):
28          node = self.nodes[0]
29  
30          self.log.info('Start with empty mempool and 101 blocks')
31          # The last 100 coinbase transactions are premature
32          blockhash = self.generate(node, 101)[0]
33          txid = node.getblock(blockhash=blockhash, verbosity=2)["tx"][0]["txid"]
34          assert_equal(node.getmempoolinfo()['size'], 0)
35  
36          self.log.info("Submit parent with multiple script branches to mempool")
37          txgen = ValidWitnessMalleatedTx()
38          parent = txgen.build_parent_tx(txid, 9.99998 * COIN)
39  
40          privkeys = [node.get_deterministic_priv_key().key]
41          raw_parent = node.signrawtransactionwithkey(hexstring=parent.serialize().hex(), privkeys=privkeys)['hex']
42          signed_parent_txid = node.sendrawtransaction(hexstring=raw_parent, maxfeerate=0)
43          self.generate(node, 1)
44  
45          peer_wtxid_relay = node.add_p2p_connection(P2PTxInvStore())
46  
47          child_one, child_two = txgen.build_malleated_children(signed_parent_txid, 9.99996 * COIN)
48          child_one_wtxid = child_one.wtxid_hex
49          child_one_txid = child_one.txid_hex
50          child_two_wtxid = child_two.wtxid_hex
51          child_two_txid = child_two.txid_hex
52  
53          assert_equal(child_one_txid, child_two_txid)
54          assert_not_equal(child_one_wtxid, child_two_wtxid)
55  
56          self.log.info("Submit child_one to the mempool")
57          txid_submitted = node.sendrawtransaction(child_one.serialize().hex())
58          assert_equal(node.getmempoolentry(txid_submitted)['wtxid'], child_one_wtxid)
59  
60          peer_wtxid_relay.wait_for_broadcast([child_one_wtxid])
61          assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)
62  
63          # testmempoolaccept reports the "already in mempool" error
64          assert_equal(node.testmempoolaccept([child_one.serialize().hex()]), [{
65              "txid": child_one_txid,
66              "wtxid": child_one_wtxid,
67              "allowed": False,
68              "reject-reason": "txn-already-in-mempool",
69              "reject-details": "txn-already-in-mempool"
70          }])
71          assert_equal(node.testmempoolaccept([child_two.serialize().hex()])[0], {
72              "txid": child_two_txid,
73              "wtxid": child_two_wtxid,
74              "allowed": False,
75              "reject-reason": "txn-same-nonwitness-data-in-mempool",
76              "reject-details": "txn-same-nonwitness-data-in-mempool"
77          })
78  
79          # sendrawtransaction will not throw but quits early when the exact same transaction is already in mempool
80          node.sendrawtransaction(child_one.serialize().hex())
81  
82          self.log.info("Connect another peer that hasn't seen child_one before")
83          peer_wtxid_relay_2 = node.add_p2p_connection(P2PTxInvStore())
84  
85          self.log.info("Submit child_two to the mempool")
86          # sendrawtransaction will not throw but quits early when a transaction with the same non-witness data is already in mempool
87          node.sendrawtransaction(child_two.serialize().hex())
88  
89          # The node should rebroadcast the transaction using the wtxid of the correct transaction
90          # (child_one, which is in its mempool).
91          peer_wtxid_relay_2.wait_for_broadcast([child_one_wtxid])
92          assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)
93  
94  if __name__ == '__main__':
95      MempoolWtxidTest(__file__).main()