Skip to content

test: refactor mempool_accept_wtxid #33067

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 17 additions & 30 deletions test/functional/mempool_accept_wtxid.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,70 +7,57 @@
with identical non-witness data but different witness.
"""

from test_framework.messages import (
COIN,
)
from test_framework.p2p import P2PTxInvStore
from test_framework.script_util import ValidWitnessMalleatedTx
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_not_equal,
assert_equal,
)
from test_framework.wallet import (
MiniWallet,
)
from test_framework.script_util import valid_witness_malleate_tx


class MempoolWtxidTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
self.setup_clean_chain = False

def run_test(self):
node = self.nodes[0]
wallet = MiniWallet(node)
self.log.info('Start with pre-generated blocks')

self.log.info('Start with empty mempool and 101 blocks')
# The last 100 coinbase transactions are premature
blockhash = self.generate(node, 101)[0]
txid = node.getblock(blockhash=blockhash, verbosity=2)["tx"][0]["txid"]
assert_equal(node.getmempoolinfo()['size'], 0)

self.log.info("Submit parent with multiple script branches to mempool")
txgen = ValidWitnessMalleatedTx()
parent = txgen.build_parent_tx(txid, 9.99998 * COIN)
child_one, child_two = valid_witness_malleate_tx(wallet, node)

privkeys = [node.get_deterministic_priv_key().key]
raw_parent = node.signrawtransactionwithkey(hexstring=parent.serialize().hex(), privkeys=privkeys)['hex']
signed_parent_txid = node.sendrawtransaction(hexstring=raw_parent, maxfeerate=0)
self.generate(node, 1)

peer_wtxid_relay = node.add_p2p_connection(P2PTxInvStore())

child_one, child_two = txgen.build_malleated_children(signed_parent_txid, 9.99996 * COIN)
child_one_wtxid = child_one.wtxid_hex
child_one_txid = child_one.txid_hex
child_two_wtxid = child_two.wtxid_hex
child_two_txid = child_two.txid_hex

assert_equal(child_one_txid, child_two_txid)
assert_not_equal(child_one_wtxid, child_two_wtxid)
assert_equal(child_one.txid_hex, child_two.txid_hex)
assert_not_equal(child_one.wtxid_hex, child_two.wtxid_hex)

self.log.info("Submit child_one to the mempool")
txid_submitted = node.sendrawtransaction(child_one.serialize().hex())
assert_equal(node.getmempoolentry(txid_submitted)['wtxid'], child_one_wtxid)

peer_wtxid_relay.wait_for_broadcast([child_one_wtxid])
assert_equal(node.getmempoolentry(txid_submitted)['wtxid'], child_one.wtxid_hex)
peer_wtxid_relay.wait_for_broadcast([child_one.wtxid_hex])
assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)

# testmempoolaccept reports the "already in mempool" error
assert_equal(node.testmempoolaccept([child_one.serialize().hex()]), [{
"txid": child_one_txid,
"wtxid": child_one_wtxid,
"txid": child_one.txid_hex,
"wtxid": child_one.wtxid_hex,
"allowed": False,
"reject-reason": "txn-already-in-mempool",
"reject-details": "txn-already-in-mempool"
}])
assert_equal(node.testmempoolaccept([child_two.serialize().hex()])[0], {
"txid": child_two_txid,
"wtxid": child_two_wtxid,
"txid": child_two.txid_hex,
"wtxid": child_two.wtxid_hex,
"allowed": False,
"reject-reason": "txn-same-nonwitness-data-in-mempool",
"reject-details": "txn-same-nonwitness-data-in-mempool"
Expand All @@ -88,7 +75,7 @@ def run_test(self):

# The node should rebroadcast the transaction using the wtxid of the correct transaction
# (child_one, which is in its mempool).
peer_wtxid_relay_2.wait_for_broadcast([child_one_wtxid])
peer_wtxid_relay_2.wait_for_broadcast([child_one.wtxid_hex])
assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)

if __name__ == '__main__':
Expand Down
59 changes: 26 additions & 33 deletions test/functional/test_framework/script_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
CTxInWitness,
CTxOut,
sha256,
COIN,
)
from test_framework.script import (
CScript,
Expand Down Expand Up @@ -150,43 +151,35 @@ def check_script(script):
assert False


class ValidWitnessMalleatedTx:
def valid_witness_malleate_tx(wallet, node):
"""
Creates a valid witness malleation transaction test case:
Creates a valid witness malleation transaction:
- Parent transaction with a script supporting 2 branches
- 2 child transactions with the same txid but different wtxids
"""
def __init__(self):
hashlock = hash160(b'Preimage')
self.witness_script = CScript([OP_IF, OP_HASH160, hashlock, OP_EQUAL, OP_ELSE, OP_TRUE, OP_ENDIF])

def build_parent_tx(self, funding_txid, amount):
# Create an unsigned parent transaction paying to the witness script.
witness_program = sha256(self.witness_script)
script_pubkey = CScript([OP_0, witness_program])

parent = CTransaction()
parent.vin.append(CTxIn(COutPoint(int(funding_txid, 16), 0), b""))
parent.vout.append(CTxOut(int(amount), script_pubkey))
return parent

def build_malleated_children(self, signed_parent_txid, amount):
# Create 2 valid children that differ only in witness data.
# 1. Create a new transaction with witness solving first branch
child_witness_script = CScript([OP_TRUE])
child_witness_program = sha256(child_witness_script)
child_script_pubkey = CScript([OP_0, child_witness_program])

child_one = CTransaction()
child_one.vin.append(CTxIn(COutPoint(int(signed_parent_txid, 16), 0), b""))
child_one.vout.append(CTxOut(int(amount), child_script_pubkey))
child_one.wit.vtxinwit.append(CTxInWitness())
child_one.wit.vtxinwit[0].scriptWitness.stack = [b'Preimage', b'\x01', self.witness_script]

# 2. Create another identical transaction with witness solving second branch
child_two = deepcopy(child_one)
child_two.wit.vtxinwit[0].scriptWitness.stack = [b'', self.witness_script]
return child_one, child_two
hashlock = hash160(b'Preimage')
witness_script = CScript([OP_IF, OP_HASH160, hashlock, OP_EQUAL, OP_ELSE, OP_TRUE, OP_ENDIF])
witness_program = sha256(witness_script)
script_pubkey = CScript([OP_0, witness_program])

# Create parent transaction with a script supporting 2 branches
parent = wallet.send_to(from_node=node, scriptPubKey=script_pubkey, amount=int(9.99998 * COIN), fee=1000)

# Create 2 valid children that differ only in witness data.
# 1. Create a new transaction with witness solving first branch
child_witness_script = CScript([OP_TRUE])
child_witness_program = sha256(child_witness_script)
child_script_pubkey = CScript([OP_0, child_witness_program])
child_one = CTransaction()

child_one.vin.append(CTxIn(COutPoint(int(parent['txid'], 16), parent['sent_vout']), b""))
child_one.vout.append(CTxOut(int(9.99996 * COIN), child_script_pubkey))
child_one.wit.vtxinwit.append(CTxInWitness())
child_one.wit.vtxinwit[0].scriptWitness.stack = [b'Preimage', b'\x01', witness_script]
# 2. Create another identical transaction with witness solving second branch
child_two = deepcopy(child_one)
child_two.wit.vtxinwit[0].scriptWitness.stack = [b'', witness_script]
return child_one, child_two


class TestFrameworkScriptUtil(unittest.TestCase):
Expand Down
Loading