From 2195adfdbdfb7dda49f3852f5381b5cfb93a9533 Mon Sep 17 00:00:00 2001 From: okaybro <66475772+Chaitanya-Keyal@users.noreply.github.com> Date: Sun, 20 Sep 2026 16:13:13 +0530 Subject: [PATCH] Settle is_high_fee once in parse() Both PSBTOverviewView and PSBTMathView asked the parser whether the fee was high, and the overview asked twice (once for the screen, once for routing). Each call re-summed the outputs through psbt.tx, which rebuilds the whole transaction on every access. The totals it depends on are all fixed by the end of parse(), so compute it there once and store it as is_high_fee, the same way the other totals are exposed. has_high_fee() stays as the computation itself. --- src/seedsigner/models/psbt_parser.py | 8 ++++++++ src/seedsigner/views/psbt_views.py | 8 +++----- tests/test_psbt_parser.py | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/seedsigner/models/psbt_parser.py b/src/seedsigner/models/psbt_parser.py index f7300eff..73293dc3 100644 --- a/src/seedsigner/models/psbt_parser.py +++ b/src/seedsigner/models/psbt_parser.py @@ -177,6 +177,11 @@ class PSBTParser(): self.destination_amounts = [] self.op_return_data: bytes = None + # Whether the fee is high relative to what is being sent; see has_high_fee(). + # Computed once at the end of parse() so the views can read it without each + # re-walking the outputs. + self.is_high_fee: bool = False + # Contains one entry per input in psbt.inputs and per output in psbt.outputs. Each # entry is either the derivation path the seed genuinely owns there, or it is set # to `None`. @@ -303,6 +308,9 @@ class PSBTParser(): if rt == False: return False + # Every total is known now, so settle this once rather than per view. + self.is_high_fee = self.has_high_fee() + return True diff --git a/src/seedsigner/views/psbt_views.py b/src/seedsigner/views/psbt_views.py index 213b8ae6..7ac58062 100644 --- a/src/seedsigner/views/psbt_views.py +++ b/src/seedsigner/views/psbt_views.py @@ -163,8 +163,6 @@ class PSBTOverviewView(View): else: num_self_transfer_outputs += 1 - is_high_fee_tx = psbt_parser.has_high_fee() - # Run the overview screen selected_menu_num = self.run_screen( PSBTOverviewScreen, @@ -176,7 +174,7 @@ class PSBTOverviewView(View): num_change_outputs=num_change_outputs, destination_addresses=psbt_parser.destination_addresses, has_op_return=psbt_parser.op_return_data is not None, - is_high_fee_tx=is_high_fee_tx, + is_high_fee_tx=psbt_parser.is_high_fee, ) if selected_menu_num == RET_CODE__BACK_BUTTON: @@ -188,7 +186,7 @@ class PSBTOverviewView(View): if psbt_parser.policy == None: return Destination(PSBTUnsupportedScriptTypeWarningView) - elif is_high_fee_tx: + elif psbt_parser.is_high_fee: return Destination(PSBTHighFeeWarningView, view_args={"warning_threshold_percent": psbt_parser.HIGH_FEES_WARNING_THRESHOLD}) elif psbt_parser.change_amount == 0: @@ -298,7 +296,7 @@ class PSBTMathView(View): num_recipients=psbt_parser.num_destinations, fee_amount=psbt_parser.fee_amount, change_amount=psbt_parser.change_amount, - is_high_fee_tx=psbt_parser.has_high_fee(), + is_high_fee_tx=psbt_parser.is_high_fee, ) if selected_menu_num == RET_CODE__BACK_BUTTON: diff --git a/tests/test_psbt_parser.py b/tests/test_psbt_parser.py index c055b921..ee8506fe 100644 --- a/tests/test_psbt_parser.py +++ b/tests/test_psbt_parser.py @@ -445,6 +445,27 @@ class TestPSBTParser: assert parser.has_high_fee() is expected + def test_parse_sets_is_high_fee(self): + """ + parse() should settle is_high_fee once, from the real totals, so the views + can read it without recomputing. Checked in both directions: a realistic fee + leaves it False, a fee dwarfing the spend sets it True. + """ + # 272 sat fee on a 2 BTC spend: nowhere near the threshold + psbt = PSBT.parse(a2b_base64(PSBTTestData.SINGLE_SIG_NATIVE_SEGWIT_2_INPUTS)) + psbt_parser = PSBTParser(p=psbt, seed=PSBTTestData.two_input_seed, network=SettingsConstants.REGTEST) + assert psbt_parser.is_high_fee is False + assert psbt_parser.is_high_fee == psbt_parser.has_high_fee() + + # 1 BTC input paying a 50,000 sat recipient and 10,000 sats change: almost all fee + psbt = PSBT.parse(a2b_base64(PSBTTestData.SINGLE_SIG_NATIVE_SEGWIT_1_INPUT)) + psbt.outputs.append(create_output(PSBTTestData.SINGLE_SIG_NATIVE_SEGWIT_RECEIVE, 50_000)) + psbt.outputs.append(create_output(PSBTTestData.SINGLE_SIG_NATIVE_SEGWIT_CHANGE, 10_000)) + psbt_parser = PSBTParser(p=psbt, seed=PSBTTestData.seed, network=SettingsConstants.REGTEST) + assert psbt_parser.is_high_fee is True + assert psbt_parser.is_high_fee == psbt_parser.has_high_fee() + + # TODO: Refactor all tests to be in the TestPSBTParser class(?) def test_p2tr_change_detection():