Merge bitcoin/bitcoin#21762: test: Speed up mempool_spend_coinbase.py

fa40eb5b6bfd151912c58d61771f6a6528f44e67 test: Speed up mempool_spend_coinbase.py (MarcoFalke)
fa29382ab23d52b86bfda8a267195b6c51b713c2 test: Fix test cache issue (MarcoFalke)
fa085b470a9647f3b261f506b46f4e7ca2baf0b0 test: Create MiniWallet.create_self_transfer (MarcoFalke)
fa1bedb4944b513a3c9184ad549f58bfbe69e20e test: Add MiniWallet.sendrawtransaction (MarcoFalke)

Pull request description:

  Locally the test will run 4 seconds faster with `--valgrind` (18s vs 14s)

ACKs for top commit:
  mjdietzx:
    crACK fa40eb5b6b

Tree-SHA512: ecfb60dda5ca5d7e6367bb9c6210390d95ebf6396ce657728901d118b75bb90c98f9351df3b01004d00682234448d6c6a13338d12097f7dced2cf7f1bd84d924
This commit is contained in:
MarcoFalke 2021-04-29 07:13:42 +02:00
commit fb66dbe786
No known key found for this signature in database
GPG Key ID: CE2B75697E69A548
4 changed files with 43 additions and 27 deletions

View File

@ -33,7 +33,7 @@ class FeatureBlockfilterindexPruneTest(BitcoinTestFramework):
self.log.info("prune some blocks") self.log.info("prune some blocks")
pruneheight = self.nodes[0].pruneblockchain(400) pruneheight = self.nodes[0].pruneblockchain(400)
assert_equal(pruneheight, 250) assert_equal(pruneheight, 248)
self.log.info("check if we can access the tips blockfilter when we have pruned some blocks") self.log.info("check if we can access the tips blockfilter when we have pruned some blocks")
assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getbestblockhash())['filter']), 0) assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getbestblockhash())['filter']), 0)

View File

@ -20,40 +20,41 @@ from test_framework.wallet import MiniWallet
class MempoolSpendCoinbaseTest(BitcoinTestFramework): class MempoolSpendCoinbaseTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
self.num_nodes = 1 self.num_nodes = 1
self.setup_clean_chain = True
def run_test(self): def run_test(self):
wallet = MiniWallet(self.nodes[0]) wallet = MiniWallet(self.nodes[0])
wallet.generate(200) # Invalidate two blocks, so that miniwallet has access to a coin that will mature in the next block
chain_height = self.nodes[0].getblockcount() chain_height = 198
assert_equal(chain_height, 200) self.nodes[0].invalidateblock(self.nodes[0].getblockhash(chain_height + 1))
assert_equal(chain_height, self.nodes[0].getblockcount())
# Coinbase at height chain_height-100+1 ok in mempool, should # Coinbase at height chain_height-100+1 ok in mempool, should
# get mined. Coinbase at height chain_height-100+2 is # get mined. Coinbase at height chain_height-100+2 is
# too immature to spend. # too immature to spend.
b = [self.nodes[0].getblockhash(n) for n in range(101, 103)] wallet.scan_blocks(start=chain_height - 100 + 1, num=1)
coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] utxo_mature = wallet.get_utxo()
utxo_101 = wallet.get_utxo(txid=coinbase_txids[0]) wallet.scan_blocks(start=chain_height - 100 + 2, num=1)
utxo_102 = wallet.get_utxo(txid=coinbase_txids[1]) utxo_immature = wallet.get_utxo()
spend_101_id = wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_101)["txid"] spend_mature_id = wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_mature)["txid"]
# coinbase at height 102 should be too immature to spend # 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)
assert_raises_rpc_error(-26, assert_raises_rpc_error(-26,
"bad-txns-premature-spend-of-coinbase", "bad-txns-premature-spend-of-coinbase",
lambda: wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_102)) lambda: self.nodes[0].sendrawtransaction(immature_tx['hex']))
# mempool should have just spend_101: # mempool should have just the mature one
assert_equal(self.nodes[0].getrawmempool(), [spend_101_id]) assert_equal(self.nodes[0].getrawmempool(), [spend_mature_id])
# mine a block, spend_101 should get confirmed # mine a block, mature one should get confirmed
self.nodes[0].generate(1) self.nodes[0].generate(1)
assert_equal(set(self.nodes[0].getrawmempool()), set()) assert_equal(set(self.nodes[0].getrawmempool()), set())
# ... and now height 102 can be spent: # ... and now previously immature can be spent:
spend_102_id = wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_102)["txid"] spend_new_id = self.nodes[0].sendrawtransaction(immature_tx['hex'])
assert_equal(self.nodes[0].getrawmempool(), [spend_102_id]) assert_equal(self.nodes[0].getrawmempool(), [spend_new_id])
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -739,11 +739,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# block in the cache does not age too much (have an old tip age). # block in the cache does not age too much (have an old tip age).
# This is needed so that we are out of IBD when the test starts, # This is needed so that we are out of IBD when the test starts,
# see the tip age check in IsInitialBlockDownload(). # see the tip age check in IsInitialBlockDownload().
gen_addresses = [k.address for k in TestNode.PRIV_KEYS] + [ADDRESS_BCRT1_P2WSH_OP_TRUE] gen_addresses = [k.address for k in TestNode.PRIV_KEYS][:3] + [ADDRESS_BCRT1_P2WSH_OP_TRUE]
assert_equal(len(gen_addresses), 4)
for i in range(8): for i in range(8):
cache_node.generatetoaddress( cache_node.generatetoaddress(
nblocks=25 if i != 7 else 24, nblocks=25 if i != 7 else 24,
address=gen_addresses[i % 4], address=gen_addresses[i % len(gen_addresses)],
) )
assert_equal(cache_node.getblockchaininfo()["blocks"], 199) assert_equal(cache_node.getblockchaininfo()["blocks"], 199)

View File

@ -37,9 +37,13 @@ class MiniWallet:
for i in range(start, start + num): for i in range(start, start + num):
block = self._test_node.getblock(blockhash=self._test_node.getblockhash(i), verbosity=2) block = self._test_node.getblock(blockhash=self._test_node.getblockhash(i), verbosity=2)
for tx in block['tx']: for tx in block['tx']:
for out in tx['vout']: self.scan_tx(tx)
if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value']}) def scan_tx(self, tx):
"""Scan the tx for self._scriptPubKey outputs and add them to self._utxos"""
for out in tx['vout']:
if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value']})
def generate(self, num_blocks): def generate(self, num_blocks):
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list""" """Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
@ -69,6 +73,12 @@ class MiniWallet:
def send_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None): def send_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None):
"""Create and send a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed.""" """Create and send a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
tx = self.create_self_transfer(fee_rate=fee_rate, from_node=from_node, utxo_to_spend=utxo_to_spend)
self.sendrawtransaction(from_node=from_node, tx_hex=tx['hex'])
return tx
def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, mempool_valid=True):
"""Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
self._utxos = sorted(self._utxos, key=lambda k: k['value']) self._utxos = sorted(self._utxos, key=lambda k: k['value'])
utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee
vsize = Decimal(96) vsize = Decimal(96)
@ -84,8 +94,12 @@ class MiniWallet:
tx_hex = tx.serialize().hex() tx_hex = tx.serialize().hex()
tx_info = from_node.testmempoolaccept([tx_hex])[0] tx_info = from_node.testmempoolaccept([tx_hex])[0]
self._utxos.append({'txid': tx_info['txid'], 'vout': 0, 'value': send_value}) assert_equal(mempool_valid, tx_info['allowed'])
from_node.sendrawtransaction(tx_hex) if mempool_valid:
assert_equal(tx_info['vsize'], vsize) assert_equal(tx_info['vsize'], vsize)
assert_equal(tx_info['fees']['base'], fee) assert_equal(tx_info['fees']['base'], fee)
return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex} return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex}
def sendrawtransaction(self, *, from_node, tx_hex):
from_node.sendrawtransaction(tx_hex)
self.scan_tx(from_node.decoderawtransaction(tx_hex))