From b5a962564eb15075e4e2a7bc0c235a56fa998ac3 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 26 Sep 2023 17:45:39 -0400 Subject: [PATCH] tests: Use manual bumps instead of bumpfee for resendwallettransactions Bumpfee will try to increase the entire package to the target feerate, which causes repeated bumpfees to quickly shoot up in fees, causing intermittent failures when the fee is too large. We don't care about this property, just that the child is continuously replaced until we observe it's position in mapWallet is before its parent. Instead of using bumpfee, we can create raw transactions which have only pay the additional incremental relay fee, thus avoiding this problem. --- .../wallet_resendwallettransactions.py | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py index f36d8efda70..f61e1edc1de 100755 --- a/test/functional/wallet_resendwallettransactions.py +++ b/test/functional/wallet_resendwallettransactions.py @@ -5,6 +5,8 @@ """Test that the wallet resends transactions periodically.""" import time +from decimal import Decimal + from test_framework.blocktools import ( create_block, create_coinbase, @@ -15,6 +17,8 @@ from test_framework.util import ( assert_equal, assert_raises_rpc_error, + get_fee, + try_rpc, ) class ResendWalletTransactionsTest(BitcoinTestFramework): @@ -86,18 +90,34 @@ def run_test(self): # ordering of mapWallet is, if the child is not before the parent, we will create a new # child (via bumpfee) and remove the old child (via removeprunedfunds) until we get the # ordering of child before parent. - child_txid = node.send(outputs=[{addr: 0.5}], inputs=[{"txid":txid, "vout":0}])["txid"] + child_inputs = [{"txid": txid, "vout": 0}] + child_txid = node.sendall(recipients=[addr], inputs=child_inputs)["txid"] + # Get the child tx's info for manual bumping + child_tx_info = node.gettransaction(txid=child_txid, verbose=True) + child_output_value = child_tx_info["decoded"]["vout"][0]["value"] + # Include an additional 1 vbyte buffer to handle when we have a smaller signature + additional_child_fee = get_fee(child_tx_info["decoded"]["vsize"] + 1, Decimal(0.00001100)) while True: txids = node.listreceivedbyaddress(minconf=0, address_filter=addr)[0]["txids"] if txids == [child_txid, txid]: break - bumped = node.bumpfee(child_txid) + # Manually bump the tx + # The inputs and the output address stay the same, just changing the amount for the new fee + child_output_value -= additional_child_fee + bumped_raw = node.createrawtransaction(inputs=child_inputs, outputs=[{addr: child_output_value}]) + bumped = node.signrawtransactionwithwallet(bumped_raw) + bumped_txid = node.decoderawtransaction(bumped["hex"])["txid"] + # Sometimes we will get a signature that is a little bit shorter than we expect which causes the + # feerate to be a bit higher, then the followup to be a bit lower. This results in a replacement + # that can't be broadcast. We can just skip that and keep grinding. + if try_rpc(-26, "insufficient fee, rejecting replacement", node.sendrawtransaction, bumped["hex"]): + continue # The scheduler queue creates a copy of the added tx after # send/bumpfee and re-adds it to the wallet (undoing the next # removeprunedfunds). So empty the scheduler queue: node.syncwithvalidationinterfacequeue() node.removeprunedfunds(child_txid) - child_txid = bumped["txid"] + child_txid = bumped_txid entry_time = node.getmempoolentry(child_txid)["time"] block_time = entry_time + 6 * 60