From 659671ac3db7e5157178efe4d9f8bce7d92ea237 Mon Sep 17 00:00:00 2001 From: Hodlinator <172445034+hodlinator@users.noreply.github.com> Date: Thu, 4 Jun 2026 10:23:41 +0200 Subject: [PATCH 1/3] qa: Avoid cleanup when exception is raised If an exception is raised before we get to cleanup, do not swallow it and attempt to reset the state. Doing so could trigger knock-on exceptions and extra tracebacks. --- test/functional/mempool_cluster.py | 13 ++++---- test/functional/mempool_truc.py | 17 +++++----- test/functional/p2p_opportunistic_1p1c.py | 21 ++++++------ test/functional/p2p_orphan_handling.py | 25 +++++++-------- test/functional/wallet_sendall.py | 11 +++---- test/functional/wallet_v3_txs.py | 39 +++++++++++------------ 6 files changed, 60 insertions(+), 66 deletions(-) diff --git a/test/functional/mempool_cluster.py b/test/functional/mempool_cluster.py index 0dfa6b09ef3..a05377ab760 100755 --- a/test/functional/mempool_cluster.py +++ b/test/functional/mempool_cluster.py @@ -30,13 +30,12 @@ def weight_to_vsize(weight): def cleanup(func): def wrapper(self, *args, **kwargs): - try: - func(self, *args, **kwargs) - finally: - # Mine blocks to clear the mempool and replenish the wallet's confirmed UTXOs. - while (len(self.nodes[0].getrawmempool()) > 0): - self.generate(self.nodes[0], 1) - self.wallet.rescan_utxos(include_mempool=True) + func(self, *args, **kwargs) + + # Mine blocks to clear the mempool and replenish the wallet's confirmed UTXOs. + while (len(self.nodes[0].getrawmempool()) > 0): + self.generate(self.nodes[0], 1) + self.wallet.rescan_utxos(include_mempool=True) return wrapper class MempoolClusterTest(BitcoinTestFramework): diff --git a/test/functional/mempool_truc.py b/test/functional/mempool_truc.py index e3abe039344..96de2ca93cc 100755 --- a/test/functional/mempool_truc.py +++ b/test/functional/mempool_truc.py @@ -29,15 +29,14 @@ TRUC_CHILD_MAX_VSIZE = 1000 def cleanup(extra_args=None): def decorator(func): def wrapper(self): - try: - if extra_args is not None: - self.restart_node(0, extra_args=extra_args) - func(self) - finally: - # Clear mempool again after test - self.generate(self.nodes[0], 1) - if extra_args is not None: - self.restart_node(0) + if extra_args is not None: + self.restart_node(0, extra_args=extra_args) + func(self) + + # Clear mempool again after test + self.generate(self.nodes[0], 1) + if extra_args is not None: + self.restart_node(0) return wrapper return decorator diff --git a/test/functional/p2p_opportunistic_1p1c.py b/test/functional/p2p_opportunistic_1p1c.py index ef4440b9ecb..f1d42d10ed2 100755 --- a/test/functional/p2p_opportunistic_1p1c.py +++ b/test/functional/p2p_opportunistic_1p1c.py @@ -58,18 +58,17 @@ GETDATA_WAIT = 60 def cleanup(func): def wrapper(self, *args, **kwargs): - try: - func(self, *args, **kwargs) - finally: - self.nodes[0].disconnect_p2ps() - # Do not clear the node's mempool, as each test requires mempool min feerate > min - # relay feerate. However, do check that this is the case. - assert self.nodes[0].getmempoolinfo()["mempoolminfee"] > self.nodes[0].getnetworkinfo()["relayfee"] - # Ensure we do not try to spend the same UTXOs in subsequent tests, as they will look like RBF attempts. - self.wallet.rescan_utxos(include_mempool=True) + func(self, *args, **kwargs) - # Resets if mocktime was used - self.nodes[0].setmocktime(0) + self.nodes[0].disconnect_p2ps() + # Do not clear the node's mempool, as each test requires mempool min feerate > min + # relay feerate. However, do check that this is the case. + assert self.nodes[0].getmempoolinfo()["mempoolminfee"] > self.nodes[0].getnetworkinfo()["relayfee"] + # Ensure we do not try to spend the same UTXOs in subsequent tests, as they will look like RBF attempts. + self.wallet.rescan_utxos(include_mempool=True) + + # Resets if mocktime was used + self.nodes[0].setmocktime(0) return wrapper class PackageRelayTest(BitcoinTestFramework): diff --git a/test/functional/p2p_orphan_handling.py b/test/functional/p2p_orphan_handling.py index 4cd59f8ff14..3d94f0338a2 100755 --- a/test/functional/p2p_orphan_handling.py +++ b/test/functional/p2p_orphan_handling.py @@ -50,20 +50,19 @@ TXREQUEST_TIME_SKIP = NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY + OVERLOADED_PEER def cleanup(func): def wrapper(self): - try: - func(self) - finally: - # Clear mempool - self.generate(self.nodes[0], 1) - self.nodes[0].disconnect_p2ps() - # Check that mempool and orphanage have been cleared - self.wait_until(lambda: len(self.nodes[0].getorphantxs()) == 0) - assert_equal(0, len(self.nodes[0].getrawmempool())) + func(self) - self.restart_node(0, extra_args=["-persistmempool=0"]) - # Allow use of bumpmocktime again - self.nodes[0].setmocktime(int(time.time())) - self.wallet.rescan_utxos(include_mempool=True) + # Clear mempool + self.generate(self.nodes[0], 1) + self.nodes[0].disconnect_p2ps() + # Check that mempool and orphanage have been cleared + self.wait_until(lambda: len(self.nodes[0].getorphantxs()) == 0) + assert_equal(0, len(self.nodes[0].getrawmempool())) + + self.restart_node(0, extra_args=["-persistmempool=0"]) + # Allow use of bumpmocktime again + self.nodes[0].setmocktime(int(time.time())) + self.wallet.rescan_utxos(include_mempool=True) return wrapper class PeerTxRelayer(P2PTxInvStore): diff --git a/test/functional/wallet_sendall.py b/test/functional/wallet_sendall.py index 0524b5c82ed..89afae4c786 100755 --- a/test/functional/wallet_sendall.py +++ b/test/functional/wallet_sendall.py @@ -18,12 +18,11 @@ from test_framework.util import ( # Decorator to reset activewallet to zero utxos def cleanup(func): def wrapper(self): - try: - func(self) - finally: - if 0 < self.wallet.getbalances()["mine"]["trusted"]: - self.wallet.sendall([self.remainder_target]) - assert_equal(0, self.wallet.getbalances()["mine"]["trusted"]) # wallet is empty + func(self) + + if 0 < self.wallet.getbalances()["mine"]["trusted"]: + self.wallet.sendall([self.remainder_target]) + assert_equal(0, self.wallet.getbalances()["mine"]["trusted"]) # wallet is empty return wrapper class SendallTest(BitcoinTestFramework): diff --git a/test/functional/wallet_v3_txs.py b/test/functional/wallet_v3_txs.py index 6ba30de4a3f..8ba422f2572 100755 --- a/test/functional/wallet_v3_txs.py +++ b/test/functional/wallet_v3_txs.py @@ -38,28 +38,27 @@ from test_framework.mempool_util import ( # sweep alice and bob's wallets and clear the mempool def cleanup(func): def wrapper(self, *args): - try: - self.generate(self.nodes[0], 1) - func(self, *args) - finally: - self.generate(self.nodes[0], 1) - for wallet in [self.alice, self.bob]: - txs = set(tx["txid"] for tx in wallet.listtransactions("*", 1000) if tx["confirmations"] == 0 and not tx["abandoned"]) - for tx in txs: - wallet.abandontransaction(tx) - try: - wallet.sendall([self.charlie.getnewaddress()]) - except JSONRPCException as e: - assert "Total value of UTXO pool too low to pay for transaction" in e.error['message'] - self.generate(self.nodes[0], 1) + self.generate(self.nodes[0], 1) + func(self, *args) - for wallet in [self.alice, self.bob]: - balance = wallet.getbalances()["mine"] - for balance_type in ["untrusted_pending", "trusted", "immature", "nonmempool"]: - assert_equal(balance[balance_type], 0) + self.generate(self.nodes[0], 1) + for wallet in [self.alice, self.bob]: + txs = set(tx["txid"] for tx in wallet.listtransactions("*", 1000) if tx["confirmations"] == 0 and not tx["abandoned"]) + for tx in txs: + wallet.abandontransaction(tx) + try: + wallet.sendall([self.charlie.getnewaddress()]) + except JSONRPCException as e: + assert "Total value of UTXO pool too low to pay for transaction" in e.error['message'] + self.generate(self.nodes[0], 1) - assert_equal(self.alice.getrawmempool(), []) - assert_equal(self.bob.getrawmempool(), []) + for wallet in [self.alice, self.bob]: + balance = wallet.getbalances()["mine"] + for balance_type in ["untrusted_pending", "trusted", "immature", "nonmempool"]: + assert_equal(balance[balance_type], 0) + + assert_equal(self.alice.getrawmempool(), []) + assert_equal(self.bob.getrawmempool(), []) return wrapper From f42226d526ebb3eb9217e738108e4f8148a1b069 Mon Sep 17 00:00:00 2001 From: Hodlinator <172445034+hodlinator@users.noreply.github.com> Date: Thu, 4 Jun 2026 10:22:22 +0200 Subject: [PATCH 2/3] qa: Silence socket.timeout exception when substituting it for a JSONRPCException This prevents "During handling of the above exception, another exception occurred:" and an extra traceback. --- test/functional/test_framework/authproxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py index 0377414acce..100372a4a45 100644 --- a/test/functional/test_framework/authproxy.py +++ b/test/functional/test_framework/authproxy.py @@ -169,7 +169,7 @@ class AuthServiceProxy(): 'message': '%r RPC took longer than %f seconds. Consider ' 'using larger timeout for calls that take ' 'longer to return.' % (self._service_name, - self.__conn.timeout)}) + self.__conn.timeout)}) from None if http_response is None: raise JSONRPCException({ 'code': -342, 'message': 'missing HTTP response from server'}) From 472b950b7f799a5d336b1be4e4036deeb86ca0be Mon Sep 17 00:00:00 2001 From: Hodlinator <172445034+hodlinator@users.noreply.github.com> Date: Wed, 10 Jun 2026 21:55:51 +0200 Subject: [PATCH 3/3] qa: Use custom assert_greater_than() over naked assert Co-authored-by: DrahtBot <39886733+DrahtBot@users.noreply.github.com> --- test/functional/p2p_opportunistic_1p1c.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/p2p_opportunistic_1p1c.py b/test/functional/p2p_opportunistic_1p1c.py index f1d42d10ed2..77616de13e5 100755 --- a/test/functional/p2p_opportunistic_1p1c.py +++ b/test/functional/p2p_opportunistic_1p1c.py @@ -63,7 +63,7 @@ def cleanup(func): self.nodes[0].disconnect_p2ps() # Do not clear the node's mempool, as each test requires mempool min feerate > min # relay feerate. However, do check that this is the case. - assert self.nodes[0].getmempoolinfo()["mempoolminfee"] > self.nodes[0].getnetworkinfo()["relayfee"] + assert_greater_than(self.nodes[0].getmempoolinfo()["mempoolminfee"], self.nodes[0].getnetworkinfo()["relayfee"]) # Ensure we do not try to spend the same UTXOs in subsequent tests, as they will look like RBF attempts. self.wallet.rescan_utxos(include_mempool=True)