From 25dabf31058a3e0067c75af2a246539625cd1d83 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Mon, 19 May 2025 12:05:31 +0200 Subject: [PATCH] txbatcher: if we raise NotEnoughFunds, remove the largest output from the current tx and retry In the unit test, this results in waiting until the current tx is mined. --- electrum/txbatcher.py | 12 +++++++++--- tests/test_txbatcher.py | 14 +++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/electrum/txbatcher.py b/electrum/txbatcher.py index 0d49aea5d..a6227e30c 100644 --- a/electrum/txbatcher.py +++ b/electrum/txbatcher.py @@ -439,10 +439,16 @@ class TxBatch(Logger): to_sweep_now[k] = v else: self.wallet.add_future_tx(v, wanted_height) - if not to_pay and not to_sweep_now and not self._should_bump_fee(base_tx): - return while True: - tx = self._create_batch_tx(base_tx, to_sweep_now, to_pay) + if not to_pay and not to_sweep_now and not self._should_bump_fee(base_tx): + return + try: + tx = self._create_batch_tx(base_tx, to_sweep_now, to_pay) + except NotEnoughFunds: + k = max(to_pay, key=lambda x: x.value) + self.logger.info(f'Not enough funds, removing output {k}') + to_pay.remove(k) + continue # 100 kb max standardness rule if tx.estimated_size() < 100_000: break diff --git a/tests/test_txbatcher.py b/tests/test_txbatcher.py index ee92ebe24..8733b10de 100644 --- a/tests/test_txbatcher.py +++ b/tests/test_txbatcher.py @@ -153,13 +153,21 @@ class TestTxBatcher(ElectrumTestCase): # to_self_payment tx1 output1 = PartialTxOutput.from_address_and_value("tb1qyfnv3y866ufedugxxxfksyratv4pz3h78g9dad", 20_000) wallet.txbatcher.add_payment_output('default', output1, self.fee_policy_descriptor) - toself_tx = await self.network.next_tx() - assert len(toself_tx.outputs()) == 2 - assert output1 in toself_tx.outputs() + tx1 = await self.network.next_tx() + assert len(tx1.outputs()) == 2 + assert output1 in tx1.outputs() # outgoing payment tx2 output2 = PartialTxOutput.from_address_and_value("tb1qkfn0fude7z789uys2u7sf80kd4805zpvs3na0h", 90_000) wallet.txbatcher.add_payment_output('default', output2, self.fee_policy_descriptor) + # before tx1 gets confirmed, txbatch.create_transaction will raise notenoughfunds + await asyncio.sleep(wallet.txbatcher.SLEEP_INTERVAL) + + # tx1 gets confirmed + wallet.adb.receive_tx_callback(tx1, tx_height=1) + tx_mined_status = wallet.adb.get_tx_height(tx1.txid()) + wallet.adb.add_verified_tx(tx1.txid(), tx_mined_status._replace(conf=1)) + tx2 = await self.network.next_tx() assert len(tx2.outputs()) == 2 assert output2 in tx2.outputs()