From b81cd4c29fd73b83eb27df8522e086313fce830d Mon Sep 17 00:00:00 2001 From: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz> Date: Tue, 3 Jan 2023 13:19:19 +0100 Subject: [PATCH] test: Run mempool_packages.py with MiniWallet Summary: ``` This allows to run the test even when no wallet is compiled in. Also, it is a lot nicer to read now. ``` Backport of [core#26625](https://github.com/bitcoin/bitcoin/pull/26625). Depends on D16431. Test Plan: ./test/functional/test_runner.py mempool_packages Reviewers: #bitcoin_abc, roqqit Reviewed By: roqqit Differential Revision: https://reviews.bitcoinabc.org/D16432 --- test/functional/abc_mempool_chainedtx.py | 9 +++-- test/functional/mempool_packages.py | 48 +++++------------------- test/functional/rpc_packages.py | 8 ++-- test/functional/test_framework/wallet.py | 46 +++++++++++------------ 4 files changed, 42 insertions(+), 69 deletions(-) diff --git a/test/functional/abc_mempool_chainedtx.py b/test/functional/abc_mempool_chainedtx.py index 97f88757e2..6ac638718c 100644 --- a/test/functional/abc_mempool_chainedtx.py +++ b/test/functional/abc_mempool_chainedtx.py @@ -22,9 +22,12 @@ def run_test(self): wallet = MiniWallet(node) self.generate(wallet, COINBASE_MATURITY + 2) - chain_hex = wallet.create_self_transfer_chain( - chain_length=LEGACY_MAX_CHAINED_TX * 2 - )["chain_hex"] + chain_hex = [ + t["hex"] + for t in wallet.create_self_transfer_chain( + chain_length=LEGACY_MAX_CHAINED_TX * 2 + ) + ] for i, tx_hex in enumerate(chain_hex): txid = wallet.sendrawtransaction(from_node=node, tx_hex=tx_hex) diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index 4d2ad8a5cd..20bdf90e17 100644 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -3,12 +3,10 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test descendant package tracking code.""" -from decimal import Decimal - -from test_framework.blocktools import COINBASE_MATURITY from test_framework.p2p import P2PTxInvStore from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, satoshi_round +from test_framework.wallet import MiniWallet MAX_ANCESTORS = 50 @@ -18,49 +16,22 @@ def set_test_params(self): self.num_nodes = 2 self.extra_args = [["-maxorphantx=1000"]] * self.num_nodes - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() - - # Build a transaction that spends parent_txid:vout - # Return amount sent - def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs): - send_value = satoshi_round((value - fee) / num_outputs) - inputs = [{"txid": parent_txid, "vout": vout}] - outputs = {} - for _ in range(num_outputs): - outputs[node.getnewaddress()] = send_value - rawtx = node.createrawtransaction(inputs, outputs) - signedtx = node.signrawtransactionwithwallet(rawtx) - txid = node.sendrawtransaction(signedtx["hex"]) - fulltx = node.getrawtransaction(txid, 1) - # make sure we didn't generate a change output - assert len(fulltx["vout"]) == num_outputs - return (txid, send_value) - def run_test(self): - # Mine some blocks and have them mature. - # keep track of invs + self.wallet = MiniWallet(self.nodes[0]) + self.wallet.rescan_utxos() + peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) - self.generate(self.nodes[0], COINBASE_MATURITY + 1) - utxo = self.nodes[0].listunspent(10) - txid = utxo[0]["txid"] - value = utxo[0]["amount"] - fee = Decimal("100") # MAX_ANCESTORS transactions off a confirmed tx should be fine - chain = [] - for i in range(MAX_ANCESTORS): - (txid, sent_value) = self.chain_transaction( - self.nodes[0], txid, 0, value, fee, 1 - ) - value = sent_value - chain.append(txid) + chain = self.wallet.send_self_transfer_chain( + from_node=self.nodes[0], chain_length=MAX_ANCESTORS + ) # Wait until mempool transactions have passed initial broadcast # (sent inv and received getdata) # Otherwise, getrawmempool may be inconsistent with getmempoolentry if # unbroadcast changes in between - peer_inv_store.wait_for_broadcast(chain) + peer_inv_store.wait_for_broadcast([t["txid"] for t in chain]) # Check mempool has MAX_ANCESTORS transactions in it mempool = self.nodes[0].getrawmempool(True) @@ -74,7 +45,8 @@ def run_test(self): ancestor_fees = sum([mempool[tx]["fees"]["base"] for tx in mempool]) descendants = [] - ancestors = list(chain) + ancestors = [t["txid"] for t in chain] + chain = [t["txid"] for t in chain] for x in reversed(chain): # Check that getmempoolentry is consistent with getrawmempool entry = self.nodes[0].getmempoolentry(x) diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py index 976647f09f..2fd40d6ac8 100644 --- a/test/functional/rpc_packages.py +++ b/test/functional/rpc_packages.py @@ -178,8 +178,8 @@ def test_chain(self): node = self.nodes[0] chain = self.wallet.create_self_transfer_chain(chain_length=25) - chain_hex = chain["chain_hex"] - chain_txns = chain["chain_txns"] + chain_hex = [t["hex"] for t in chain] + chain_txns = [t["tx"] for t in chain] self.log.info( "Check that testmempoolaccept requires packages to be sorted by dependency" @@ -471,7 +471,9 @@ def test_submitpackage(self): self.log.info("Submitpackage only allows packages of 1 child with its parents") # Chain of 3 transactions has too many generations - chain_hex = self.wallet.create_self_transfer_chain(chain_length=25)["chain_hex"] + chain_hex = [ + t["hex"] for t in self.wallet.create_self_transfer_chain(chain_length=25) + ] assert_raises_rpc_error( -25, "not-child-with-parents", node.submitpackage, chain_hex ) diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index 0486fc91f4..c8425ec080 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -254,6 +254,9 @@ def create_self_transfer_multi( inputs_value_total = sum([int(XEC * utxo["value"]) for utxo in utxos_to_spend]) outputs_value_total = inputs_value_total - fee_per_output * num_outputs amount_per_output = amount_per_output or (outputs_value_total // num_outputs) + assert amount_per_output > 0 + outputs_value_total = amount_per_output * num_outputs + fee = Decimal(inputs_value_total - outputs_value_total) / XEC # create tx tx = CTransaction() @@ -302,6 +305,7 @@ def create_self_transfer_multi( ) for i in range(len(tx.vout)) ], + "fee": fee, "txid": txid, "hex": tx.serialize().hex(), "tx": tx, @@ -334,7 +338,6 @@ def create_self_transfer( send_value = satoshi_round( utxo_to_spend["value"] - (fee or (fee_rate * (Decimal(size) / 1000))) ) - assert send_value > 0 # create tx tx = self.create_self_transfer_multi( @@ -347,49 +350,42 @@ def create_self_transfer( if not target_size: assert_equal(len(tx["tx"].serialize()), size) - return { - "txid": tx["txid"], - "hex": tx["hex"], - "tx": tx["tx"], - "new_utxo": tx["new_utxos"][0], - } + tx["new_utxo"] = tx.pop("new_utxos")[0] + + return tx def sendrawtransaction(self, *, from_node, tx_hex): txid = from_node.sendrawtransaction(tx_hex) self.scan_tx(from_node.decoderawtransaction(tx_hex)) return txid - def create_self_transfer_chain(self, *, chain_length): + def create_self_transfer_chain(self, *, chain_length, utxo_to_spend=None): """ Create a "chain" of chain_length transactions. The nth transaction in the chain is a child of the n-1th transaction and parent of the n+1th transaction. - Returns a dic {"chain_hex": chain_hex, "chain_txns" : chain_txns} - "chain_hex" is a list representing the chain's transactions in hexadecimal. - "chain_txns" is a list representing the chain's transactions in the CTransaction object. """ - chaintip_utxo = self.get_utxo() - chain_hex = [] - chain_txns = [] + chaintip_utxo = utxo_to_spend or self.get_utxo() + chain = [] for _ in range(chain_length): tx = self.create_self_transfer(utxo_to_spend=chaintip_utxo) chaintip_utxo = tx["new_utxo"] - chain_hex.append(tx["hex"]) - chain_txns.append(tx["tx"]) + chain.append(tx) - return {"chain_hex": chain_hex, "chain_txns": chain_txns} + return chain - def send_self_transfer_chain(self, *, from_node, chain_length, utxo_to_spend=None): + def send_self_transfer_chain(self, *, from_node, **kwargs): """Create and send a "chain" of chain_length transactions. The nth transaction in the chain is a child of the n-1th transaction and parent - of the n+1th transaction. Returns the chaintip (nth) utxo. + of the n+1th transaction. + + Returns a list of objects for each tx (see create_self_transfer_multi). """ - chaintip_utxo = utxo_to_spend or self.get_utxo() - for _ in range(chain_length): - chaintip_utxo = self.send_self_transfer( - utxo_to_spend=chaintip_utxo, from_node=from_node - )["new_utxo"] - return chaintip_utxo + chain = self.create_self_transfer_chain(**kwargs) + for t in chain: + self.sendrawtransaction(from_node=from_node, tx_hex=t["hex"]) + + return chain def getnewdestination():