Skip to content

Commit

Permalink
merge bitcoin#24035: use MiniWallet for mempool_accept.py
Browse files Browse the repository at this point in the history
  • Loading branch information
kwvg committed Jan 4, 2025
1 parent 7c313d0 commit 4da044b
Show file tree
Hide file tree
Showing 14 changed files with 75 additions and 76 deletions.
7 changes: 4 additions & 3 deletions test/functional/data/invalid_txs.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
CTxIn,
CTxOut,
MAX_MONEY,
SEQUENCE_FINAL,
)
from test_framework.blocktools import create_tx_with_script, MAX_BLOCK_SIGOPS
from test_framework.script import (
Expand Down Expand Up @@ -62,7 +63,7 @@ class BadTxTemplate:
def __init__(self, *, spend_tx=None, spend_block=None):
self.spend_tx = spend_block.vtx[0] if spend_block else spend_tx
self.spend_avail = sum(o.nValue for o in self.spend_tx.vout)
self.valid_txin = CTxIn(COutPoint(self.spend_tx.sha256, 0), b"", 0xffffffff)
self.valid_txin = CTxIn(COutPoint(self.spend_tx.sha256, 0), b"", SEQUENCE_FINAL)

@abc.abstractmethod
def get_tx(self, *args, **kwargs):
Expand Down Expand Up @@ -118,7 +119,7 @@ def get_tx(self):
bad_idx = num_indices + 100

tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256, bad_idx), b"", 0xffffffff))
tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256, bad_idx), b"", SEQUENCE_FINAL))
tx.vout.append(CTxOut(0, basic_p2sh))
tx.calc_sha256()
return tx
Expand Down Expand Up @@ -156,7 +157,7 @@ class NonexistentInput(BadTxTemplate):

def get_tx(self):
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256 + 1, 0), b"", 0xffffffff))
tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256 + 1, 0), b"", SEQUENCE_FINAL))
tx.vin.append(self.valid_txin)
tx.vout.append(CTxOut(1, basic_p2sh))
tx.calc_sha256()
Expand Down
12 changes: 8 additions & 4 deletions test/functional/feature_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
CTransaction,
CTxIn,
CTxOut,
SEQUENCE_FINAL,
uint256_from_compact,
uint256_from_str,
)
Expand Down Expand Up @@ -48,7 +49,10 @@
script_to_p2sh_script,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
from test_framework.util import (
assert_equal,
assert_greater_than,
)
from data import invalid_txs

# This functional test assumes DIP0001 is disabled
Expand Down Expand Up @@ -812,7 +816,7 @@ def run_test(self):
b58 = self.next_block(58, spend=out[17])
tx = CTransaction()
assert len(out[17].vout) < 42
tx.vin.append(CTxIn(COutPoint(out[17].sha256, 42), CScript([OP_TRUE]), 0xffffffff))
tx.vin.append(CTxIn(COutPoint(out[17].sha256, 42), CScript([OP_TRUE]), SEQUENCE_FINAL))
tx.vout.append(CTxOut(0, b""))
tx.calc_sha256()
b58 = self.update_block(58, [tx])
Expand Down Expand Up @@ -887,7 +891,7 @@ def run_test(self):
tx.nLockTime = 0xffffffff # this locktime is non-final
tx.vin.append(CTxIn(COutPoint(out[18].sha256, 0))) # don't set nSequence
tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
assert tx.vin[0].nSequence < 0xffffffff
assert_greater_than(SEQUENCE_FINAL, tx.vin[0].nSequence)
tx.calc_sha256()
b62 = self.update_block(62, [tx])
self.send_blocks([b62], success=False, reject_reason='bad-txns-nonfinal', reconnect=True)
Expand Down Expand Up @@ -1035,7 +1039,7 @@ def run_test(self):
bogus_tx = CTransaction()
bogus_tx.sha256 = uint256_from_str(b"23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c")
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff))
tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", SEQUENCE_FINAL))
tx.vout.append(CTxOut(1, b""))
b70 = self.update_block(70, [tx])
self.send_blocks([b70], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)
Expand Down
9 changes: 5 additions & 4 deletions test/functional/feature_cltv.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
)
from test_framework.messages import (
CTransaction,
SEQUENCE_FINAL,
msg_block,
)
from test_framework.p2p import P2PInterface
Expand Down Expand Up @@ -56,7 +57,7 @@ def cltv_invalidate(tx, failure_reason):
# 3) the lock-time type (height vs. timestamp) of the top stack item and the
# nLockTime field are not the same
# 4) the top stack item is greater than the transaction's nLockTime field
# 5) the nSequence field of the txin is 0xffffffff
# 5) the nSequence field of the txin is 0xffffffff (SEQUENCE_FINAL)
assert failure_reason in range(5)
scheme = [
# | Script to prepend to scriptSig | nSequence | nLockTime |
Expand All @@ -65,7 +66,7 @@ def cltv_invalidate(tx, failure_reason):
[[OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP], None, None],
[[CScriptNum(100), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 1296688602], # timestamp of genesis block
[[CScriptNum(100), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 50],
[[CScriptNum(50), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0xffffffff, 50],
[[CScriptNum(50), OP_CHECKLOCKTIMEVERIFY, OP_DROP], SEQUENCE_FINAL, 50],
][failure_reason]

cltv_modify_tx(tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
Expand Down Expand Up @@ -119,7 +120,7 @@ def run_test(self):
# create one invalid tx per CLTV failure reason (5 in total) and collect them
invalid_cltv_txs = []
for i in range(5):
spendtx = wallet.create_self_transfer(from_node=self.nodes[0])['tx']
spendtx = wallet.create_self_transfer()['tx']
cltv_invalidate(spendtx, i)
invalid_cltv_txs.append(spendtx)

Expand Down Expand Up @@ -154,7 +155,7 @@ def run_test(self):

# create and test one invalid tx per CLTV failure reason (5 in total)
for i in range(5):
spendtx = wallet.create_self_transfer(from_node=self.nodes[0])['tx']
spendtx = wallet.create_self_transfer()['tx']
cltv_invalidate(spendtx, i)

expected_cltv_reject_reason = [
Expand Down
2 changes: 1 addition & 1 deletion test/functional/feature_csv_activation.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def setup_network(self):

def create_self_transfer_from_utxo(self, input_tx):
utxo = self.miniwallet.get_utxo(txid=input_tx.rehash(), mark_as_spent=False)
tx = self.miniwallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo)['tx']
tx = self.miniwallet.create_self_transfer(utxo_to_spend=utxo)['tx']
return tx

def create_bip112special(self, input, txversion):
Expand Down
2 changes: 1 addition & 1 deletion test/functional/feature_dersig.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def set_test_params(self):

def create_tx(self, input_txid):
utxo_to_spend = self.miniwallet.get_utxo(txid=input_txid, mark_as_spent=False)
return self.miniwallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_to_spend)['tx']
return self.miniwallet.create_self_transfer(utxo_to_spend=utxo_to_spend)['tx']

def test_dersig_info(self, *, is_active):
assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip66'],
Expand Down
4 changes: 2 additions & 2 deletions test/functional/feature_utxo_set_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ def test_muhash_implementation(self):
assert_equal(finalized[::-1].hex(), node_muhash)

self.log.info("Test deterministic UTXO set hash results")
assert_equal(node.gettxoutsetinfo()['hash_serialized_2'], "4eb23b9673b7472e33ebf6e6aefee849fe5bc5e598fd387c2f9222070cc2f711")
assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "dd74d7f1fb047577915d490e82d063e843f7853ae7543c9980a28c67326e1a2c")
assert_equal(node.gettxoutsetinfo()['hash_serialized_2'], "91ceba6561281d14db95afb48894fa6f764daa9f2190d05cb397e759634e10bf")
assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "c75ac7fcf057ef30cb5880fc800a99d3908fd0eb8593647512823ada6e359ef5")

def run_test(self):
self.test_muhash_implementation()
Expand Down
81 changes: 36 additions & 45 deletions test/functional/mempool_accept.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test mempool acceptance of raw transactions."""


from copy import deepcopy
from decimal import Decimal
import math

Expand All @@ -18,6 +18,7 @@
CTxOut,
MAX_BLOCK_SIZE,
MAX_MONEY,
SEQUENCE_FINAL,
tx_from_hex,
)
from test_framework.script import (
Expand All @@ -36,6 +37,7 @@
assert_equal,
assert_raises_rpc_error,
)
from test_framework.wallet import MiniWallet


class MempoolAcceptanceTest(BitcoinTestFramework):
Expand All @@ -46,9 +48,6 @@ def set_test_params(self):
]] * self.num_nodes
self.supports_cli = False

def skip_test_if_missing_module(self):
self.skip_if_no_wallet()

def check_mempool_result(self, result_expected, *args, **kwargs):
"""Wrapper to check result of testmempoolaccept on node_0's mempool"""
result_test = self.nodes[0].testmempoolaccept(*args, **kwargs)
Expand All @@ -57,12 +56,13 @@ def check_mempool_result(self, result_expected, *args, **kwargs):

def run_test(self):
node = self.nodes[0]
self.wallet = MiniWallet(node)
self.wallet.rescan_utxos()

self.log.info('Start with empty mempool, and 200 blocks')
self.mempool_size = 0
assert_equal(node.getblockcount(), 200)
assert_equal(node.getmempoolinfo()['size'], self.mempool_size)
coins = node.listunspent()

self.log.info('Should not accept garbage to testmempoolaccept')
assert_raises_rpc_error(-3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar'))
Expand All @@ -71,12 +71,12 @@ def run_test(self):
assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar']))

self.log.info('A transaction already in the blockchain')
coin = coins.pop() # Pick a random coin(base) to spend
raw_tx_in_block = node.signrawtransactionwithwallet(node.createrawtransaction(
inputs=[{'txid': coin['txid'], 'vout': coin['vout']}],
outputs=[{node.getnewaddress(): 0.3}, {node.getnewaddress(): 49}],
))['hex']
txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, maxfeerate=0)
tx = self.wallet.create_self_transfer()['tx'] # Pick a random coin(base) to spend
tx.vout.append(deepcopy(tx.vout[0]))
tx.vout[0].nValue = int(0.3 * COIN)
tx.vout[1].nValue = int(49 * COIN)
raw_tx_in_block = tx.serialize().hex()
txid_in_block = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_in_block, maxfeerate=0)
self.generate(node, 1)
self.mempool_size = 0
self.check_mempool_result(
Expand All @@ -86,27 +86,26 @@ def run_test(self):

self.log.info('A transaction not in the mempool')
fee = Decimal('0.000007')
raw_tx_0 = node.signrawtransactionwithwallet(node.createrawtransaction(
inputs=[{"txid": txid_in_block, "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER}], # RBF is used later
outputs=[{node.getnewaddress(): Decimal('0.3') - fee}],
))['hex']
tx = tx_from_hex(raw_tx_0)
utxo_to_spend = self.wallet.get_utxo(txid=txid_in_block) # use 0.3 BTC UTXO
tx = self.wallet.create_self_transfer(utxo_to_spend=utxo_to_spend, sequence=BIP125_SEQUENCE_NUMBER)['tx']
tx.vout[0].nValue = int((Decimal('0.3') - fee) * COIN)
raw_tx_0 = tx.serialize().hex()
txid_0 = tx.rehash()
self.check_mempool_result(
result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': fee}}],
rawtxs=[raw_tx_0],
)

self.log.info('A final transaction not in the mempool')
coin = coins.pop() # Pick a random coin(base) to spend
output_amount = Decimal('0.025')
raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction(
inputs=[{'txid': coin['txid'], 'vout': coin['vout'], "sequence": 0xffffffff}], # SEQUENCE_FINAL
outputs=[{node.getnewaddress(): output_amount}],
tx = self.wallet.create_self_transfer(
sequence=SEQUENCE_FINAL,
locktime=node.getblockcount() + 2000, # Can be anything
))['hex']
fee_expected = coin['amount'] - output_amount
)['tx']
tx.vout[0].nValue = int(output_amount * COIN)
raw_tx_final = tx.serialize().hex()
tx = tx_from_hex(raw_tx_final)
fee_expected = Decimal('500.0') - output_amount
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': fee_expected}}],
rawtxs=[tx.serialize().hex()],
Expand All @@ -127,8 +126,7 @@ def run_test(self):
tx = tx_from_hex(raw_tx_0)
tx.vout[0].nValue -= int(fee * COIN) # Double the fee
tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER + 1 # Now, opt out of RBF
raw_tx_0_reject = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
tx = tx_from_hex(raw_tx_0_reject)
raw_tx_0_reject = tx.serialize().hex()
txid_0_reject = tx.rehash()
self.check_mempool_result(
# No RBF in DASH
Expand All @@ -142,7 +140,6 @@ def run_test(self):
# take original raw_tx_0
tx = tx_from_hex(raw_tx_0)
tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee
# skip re-signing the tx
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'txn-mempool-conflict'}],
rawtxs=[tx.serialize().hex()],
Expand All @@ -152,7 +149,6 @@ def run_test(self):
self.log.info('A transaction with missing inputs, that never existed')
tx = tx_from_hex(raw_tx_0)
tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14)
# skip re-signing the tx
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'missing-inputs'}],
rawtxs=[tx.serialize().hex()],
Expand All @@ -161,17 +157,16 @@ def run_test(self):
self.log.info('A transaction with missing inputs, that existed once in the past')
tx = tx_from_hex(raw_tx_0)
tx.vin[0].prevout.n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend
raw_tx_1 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
raw_tx_1 = tx.serialize().hex()
txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, maxfeerate=0)
# Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them
raw_tx_spend_both = node.signrawtransactionwithwallet(node.createrawtransaction(
inputs=[
{'txid': txid_0, 'vout': 0},
{'txid': txid_1, 'vout': 0},
],
outputs=[{node.getnewaddress(): 0.1}]
))['hex']
txid_spend_both = node.sendrawtransaction(hexstring=raw_tx_spend_both, maxfeerate=0)
tx = self.wallet.create_self_transfer()['tx']
tx.vin.append(deepcopy(tx.vin[0]))
tx.vin[0].prevout = COutPoint(hash=int(txid_0, 16), n=0)
tx.vin[1].prevout = COutPoint(hash=int(txid_1, 16), n=0)
tx.vout[0].nValue = int(0.1 * COIN)
raw_tx_spend_both = tx.serialize().hex()
txid_spend_both = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_spend_both, maxfeerate=0)
self.generate(node, 1)
self.mempool_size = 0
# Now see if we can add the coins back to the utxo set by sending the exact txs again
Expand All @@ -184,12 +179,11 @@ def run_test(self):
rawtxs=[raw_tx_1],
)

self.log.info('Create a signed "reference" tx for later use')
raw_tx_reference = node.signrawtransactionwithwallet(node.createrawtransaction(
inputs=[{'txid': txid_spend_both, 'vout': 0}],
outputs=[{node.getnewaddress(): 0.05}],
))['hex']
tx = tx_from_hex(raw_tx_reference)
self.log.info('Create a "reference" tx for later use')
utxo_to_spend = self.wallet.get_utxo(txid=txid_spend_both)
tx = self.wallet.create_self_transfer(utxo_to_spend=utxo_to_spend, sequence=SEQUENCE_FINAL)['tx']
tx.vout[0].nValue = int(0.05 * COIN)
raw_tx_reference = tx.serialize().hex()
# Reference tx should be valid on itself
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': Decimal('0.1') - Decimal('0.05')}}],
Expand All @@ -200,8 +194,6 @@ def run_test(self):
self.log.info('A transaction with no outputs')
tx = tx_from_hex(raw_tx_reference)
tx.vout = []
# Skip re-signing the transaction for context independent checks from now on
# tx = tx_from_hex(node.signrawtransactionwithwallet(tx.serialize().hex())['hex'])
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-vout-empty'}],
rawtxs=[tx.serialize().hex()],
Expand Down Expand Up @@ -258,7 +250,7 @@ def run_test(self):
)

self.log.info('A coinbase transaction')
# Pick the input of the first tx we signed, so it has to be a coinbase tx
# Pick the input of the first tx we created, so it has to be a coinbase tx
raw_tx_coinbase_spent = node.getrawtransaction(txid=node.decoderawtransaction(hexstring=raw_tx_in_block)['vin'][0]['txid'])
tx = tx_from_hex(raw_tx_coinbase_spent)
self.check_mempool_result(
Expand Down Expand Up @@ -341,7 +333,6 @@ def run_test(self):
self.log.info('A transaction that is locked by BIP68 sequence logic')
tx = tx_from_hex(raw_tx_reference)
tx.vin[0].nSequence = 2 # We could include it in the second block mined from now, but not the very next one
# Can skip re-signing the tx because of early rejection
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'non-BIP68-final'}],
rawtxs=[tx.serialize().hex()],
Expand Down
11 changes: 5 additions & 6 deletions test/functional/mempool_reorg.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,13 @@ def run_test(self):
utxo_2 = wallet.get_utxo(txid=coinbase_txids[2])
utxo_3 = wallet.get_utxo(txid=coinbase_txids[3])
self.log.info("Create three transactions spending from coinbase utxos: spend_1, spend_2, spend_3")
spend_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_1)
spend_2 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_2)
spend_3 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_3)
spend_1 = wallet.create_self_transfer(utxo_to_spend=utxo_1)
spend_2 = wallet.create_self_transfer(utxo_to_spend=utxo_2)
spend_3 = wallet.create_self_transfer(utxo_to_spend=utxo_3)

self.log.info("Create another transaction which is time-locked to two blocks in the future")
utxo = wallet.get_utxo(txid=coinbase_txids[0])
timelock_tx = wallet.create_self_transfer(
from_node=self.nodes[0],
utxo_to_spend=utxo,
mempool_valid=False,
locktime=self.nodes[0].getblockcount() + 2
Expand All @@ -71,9 +70,9 @@ def run_test(self):

self.log.info("Create spend_2_1 and spend_3_1")
spend_2_utxo = wallet.get_utxo(txid=spend_2['txid'])
spend_2_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=spend_2_utxo)
spend_2_1 = wallet.create_self_transfer(utxo_to_spend=spend_2_utxo)
spend_3_utxo = wallet.get_utxo(txid=spend_3['txid'])
spend_3_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=spend_3_utxo)
spend_3_1 = wallet.create_self_transfer(utxo_to_spend=spend_3_utxo)

self.log.info("Broadcast and mine spend_3_1")
spend_3_1_id = self.nodes[0].sendrawtransaction(spend_3_1['hex'])
Expand Down
2 changes: 1 addition & 1 deletion test/functional/mempool_spend_coinbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def run_test(self):
spend_mature_id = wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_mature)["txid"]

# other coinbase should be too immature to spend
immature_tx = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_immature, mempool_valid=False)
immature_tx = wallet.create_self_transfer(utxo_to_spend=utxo_immature, mempool_valid=False)
assert_raises_rpc_error(-26,
"bad-txns-premature-spend-of-coinbase",
lambda: self.nodes[0].sendrawtransaction(immature_tx['hex']))
Expand Down
Loading

0 comments on commit 4da044b

Please sign in to comment.