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()