mempool_accept_wtxid.py
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()