mempool_accept_wtxid.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2021 The Limenka 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 copy import deepcopy
  11  from test_framework.messages import (
  12      COIN,
  13      COutPoint,
  14      CTransaction,
  15      CTxIn,
  16      CTxInWitness,
  17      CTxOut,
  18      sha256,
  19  )
  20  from test_framework.p2p import P2PTxInvStore
  21  from test_framework.script import (
  22      CScript,
  23      OP_0,
  24      OP_ELSE,
  25      OP_ENDIF,
  26      OP_EQUAL,
  27      OP_HASH160,
  28      OP_IF,
  29      OP_TRUE,
  30      hash160,
  31  )
  32  from test_framework.test_framework import LimenkaTestFramework
  33  from test_framework.util import (
  34      assert_equal,
  35  )
  36  
  37  class MempoolWtxidTest(LimenkaTestFramework):
  38      def set_test_params(self):
  39          self.num_nodes = 1
  40          self.setup_clean_chain = True
  41  
  42      def run_test(self):
  43          node = self.nodes[0]
  44  
  45          self.log.info('Start with empty mempool and 101 blocks')
  46          # The last 100 coinbase transactions are premature
  47          blockhash = self.generate(node, 101)[0]
  48          txid = node.getblock(blockhash=blockhash, verbosity=2)["tx"][0]["txid"]
  49          assert_equal(node.getmempoolinfo()['size'], 0)
  50  
  51          self.log.info("Submit parent with multiple script branches to mempool")
  52          hashlock = hash160(b'Preimage')
  53          witness_script = CScript([OP_IF, OP_HASH160, hashlock, OP_EQUAL, OP_ELSE, OP_TRUE, OP_ENDIF])
  54          witness_program = sha256(witness_script)
  55          script_pubkey = CScript([OP_0, witness_program])
  56  
  57          parent = CTransaction()
  58          parent.vin.append(CTxIn(COutPoint(int(txid, 16), 0), b""))
  59          parent.vout.append(CTxOut(int(9.99998 * COIN), script_pubkey))
  60          parent.rehash()
  61  
  62          privkeys = [node.get_deterministic_priv_key().key]
  63          raw_parent = node.signrawtransactionwithkey(hexstring=parent.serialize().hex(), privkeys=privkeys)['hex']
  64          parent_txid = node.sendrawtransaction(hexstring=raw_parent, maxfeerate=0)
  65          self.generate(node, 1)
  66  
  67          peer_wtxid_relay = node.add_p2p_connection(P2PTxInvStore())
  68  
  69          # Create a new transaction with witness solving first branch
  70          child_witness_script = CScript([OP_TRUE])
  71          child_witness_program = sha256(child_witness_script)
  72          child_script_pubkey = CScript([OP_0, child_witness_program])
  73  
  74          child_one = CTransaction()
  75          child_one.vin.append(CTxIn(COutPoint(int(parent_txid, 16), 0), b""))
  76          child_one.vout.append(CTxOut(int(9.99996 * COIN), child_script_pubkey))
  77          child_one.wit.vtxinwit.append(CTxInWitness())
  78          child_one.wit.vtxinwit[0].scriptWitness.stack = [b'Preimage', b'\x01', witness_script]
  79          child_one_wtxid = child_one.getwtxid()
  80          child_one_txid = child_one.rehash()
  81  
  82          # Create another identical transaction with witness solving second branch
  83          child_two = deepcopy(child_one)
  84          child_two.wit.vtxinwit[0].scriptWitness.stack = [b'', witness_script]
  85          child_two_wtxid = child_two.getwtxid()
  86          child_two_txid = child_two.rehash()
  87  
  88          assert_equal(child_one_txid, child_two_txid)
  89          assert child_one_wtxid != child_two_wtxid
  90  
  91          self.log.info("Submit child_one to the mempool")
  92          txid_submitted = node.sendrawtransaction(child_one.serialize().hex())
  93          assert_equal(node.getmempoolentry(txid_submitted)['wtxid'], child_one_wtxid)
  94  
  95          peer_wtxid_relay.wait_for_broadcast([child_one_wtxid])
  96          assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)
  97  
  98          # testmempoolaccept reports the "already in mempool" error
  99          expected = {
 100              "txid": child_one_txid,
 101              "wtxid": child_one_wtxid,
 102              "allowed": False,
 103              "reject-reason": "txn-already-in-mempool",
 104              "reject-details": "txn-already-in-mempool",
 105          }
 106          result = node.testmempoolaccept([child_one.serialize().hex()])[0]
 107          # skip for now
 108          result.pop('usage')
 109          assert_equal(result, expected)
 110  
 111          expected = {
 112              "txid": child_two_txid,
 113              "wtxid": child_two_wtxid,
 114              "allowed": False,
 115              "reject-reason": "txn-same-nonwitness-data-in-mempool",
 116              "reject-details": "txn-same-nonwitness-data-in-mempool",
 117          }
 118          result = node.testmempoolaccept([child_two.serialize().hex()])[0]
 119          # skip for now
 120          result.pop('usage')
 121          assert_equal(result, expected)
 122  
 123          # sendrawtransaction will not throw but quits early when the exact same transaction is already in mempool
 124          node.sendrawtransaction(child_one.serialize().hex())
 125  
 126          self.log.info("Connect another peer that hasn't seen child_one before")
 127          peer_wtxid_relay_2 = node.add_p2p_connection(P2PTxInvStore())
 128  
 129          self.log.info("Submit child_two to the mempool")
 130          # sendrawtransaction will not throw but quits early when a transaction with the same non-witness data is already in mempool
 131          node.sendrawtransaction(child_two.serialize().hex())
 132  
 133          # The node should rebroadcast the transaction using the wtxid of the correct transaction
 134          # (child_one, which is in its mempool).
 135          peer_wtxid_relay_2.wait_for_broadcast([child_one_wtxid])
 136          assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)
 137  
 138  if __name__ == '__main__':
 139      MempoolWtxidTest(__file__).main()
 140