perf(net): one balance scan per poll, filtered opstatus, a guarded merge scope

The Core poll ran z_gettotalbalance twice (minconf 0 and 1), each a whole-
wallet scan under dragonxd's cs_main. The spendable (minconf=1) call now
runs only after the display call succeeded, and only when something is
unconfirmed or pending or the exact total moved: about 95% of polls make
one scan instead of two. A forced post-send balance can no longer be lost
to a refused enqueue, and pending-send debits aren't re-applied each tick.

z_getoperationstatus is filtered to our opids with its real argument form
([[id,...]]; the old ["id"] was rejected), falling back to the unfiltered
call if a daemon refuses the filter. The Shield/Merge dialog's own polls
are filtered and session-guarded too, and finally reach Done; the dead
RPCClient wrappers are deleted.

The Merge/Consolidate scope scan becomes a tested state machine: one scan
at a time, failures latch behind a Retry button, only "node not ready"
retries by itself (10/30/60 s, at most 3), Lite never scans. Its 120 s
calls are maintenance calls: every main-thread disconnect aborts one in
flight first, so a lifecycle action no longer freezes the UI behind it.
An aborted merge that was already sent says it may still go through.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 11:06:34 -05:00
co-authored by Claude Opus 5.5
parent 4eed4283d6
commit 8b9a498eee
23 changed files with 1044 additions and 128 deletions
+5
View File
@@ -2493,6 +2493,7 @@ void I18n::loadBuiltinEnglish()
strings_["shield_unknown_error"] = "Unknown error";
strings_["shield_send_failed"] = "Shield failed: ";
strings_["merge_send_failed"] = "Merge failed: ";
strings_["merge_submit_uncertain"] = "The node didn't confirm the merge before the connection was interrupted. It may still go through \xE2\x80\x94 check History after reconnecting before merging again.";
strings_["merge_description"] = "Merge multiple UTXOs into a single shielded address. This can help reduce wallet size and improve privacy.";
strings_["merge_funds"] = "Merge Funds";
strings_["merge_started"] = "Merge operation started";
@@ -2503,6 +2504,9 @@ void I18n::loadBuiltinEnglish()
strings_["consolidate_funds_btn"] = "Consolidate";
strings_["merge_scope_loading"] = "Checking your inputs\xE2\x80\xA6";
strings_["merge_scope_fmt"] = "%d transparent + %d shielded inputs \xC2\xB7 ~%s DRGX spendable";
strings_["merge_waiting_node"] = "Waiting for the node\xE2\x80\xA6";
strings_["merge_wait_locked"] = "The wallet is locked. Unlock it to continue.";
strings_["merge_scope_failed_fmt"] = "Couldn't read your inputs: %s";
strings_["merge_source"] = "Consolidate";
strings_["merge_src_transparent"] = "Transparent";
strings_["merge_src_shielded"] = "Shielded";
@@ -2535,6 +2539,7 @@ void I18n::loadBuiltinEnglish()
strings_["merge_keep_each_status_failed"] = "%s — failed: %s";
strings_["merge_keep_each_status_done"] = "%s — done: %d notes merged in %d batch(es)";
strings_["merge_keep_each_status_running"] = "%s — merging %d notes (%d of %d batches done)…";
strings_["merge_keep_each_status_unconfirmed"] = "%s \xE2\x80\x94 status could not be confirmed";
strings_["merge_keep_each_run_again"] = "Addresses merged in several batches now hold one note per batch. Run this again after those confirm to merge them into one.";
strings_["done"] = "Done";