feat(merge): run per-address consolidation as a paced background job
"Keep in each address" posted one task that called z_mergetoaddress for every batch of every address back to back; each call is a whole-wallet scan under cs_main, and the daemon runs the ops one at a time anyway. It is now an App-owned services::ConsolidationRun: - one RPC at a time; the next merge only after the previous batch left the queue and a gap as long as the last call took (2-120 s); - pauses while the node syncs, the wallet is locked, a send of ours is in progress, or another wallet scan is running; - failures classified: nothing that may have queued is ever resent; a breaker stops after 3 dead addresses or 3 failed ops in a row; - each merge is charged to the scan budget; one re-read and one forced balance at the end; - it keeps running when the dialog closes: a status-bar chip, a run view with progress, the wait reason and a Stop button. The "Into one" merge can no longer be confirmed twice while its call is in flight. fake_dragonxd gains FAKE_SCAN_SECS, FAKE_MERGE_SERIAL and FAKE_MERGE_FAIL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
#include "services/scan_policy.h"
|
||||
#include "services/wallet_scan_governor.h"
|
||||
#include "services/chat_scan_policy.h"
|
||||
#include "services/note_consolidation.h"
|
||||
#include "services/network_refresh_service.h"
|
||||
#include "services/wallet_security_controller.h"
|
||||
#include "services/wallet_security_workflow.h"
|
||||
@@ -492,6 +493,19 @@ public:
|
||||
// full node that is past warmup, not rescanning and not busy.
|
||||
bool walletScanReady() const;
|
||||
|
||||
// C210: the "Keep in each address" consolidation run. Owned here so it keeps going when the Merge dialog
|
||||
// closes; pumped from update(). Full node only (refused in Lite). Main thread.
|
||||
const services::ConsolidationRun& consolidation() const { return consolidation_; }
|
||||
bool startConsolidation(const std::vector<services::NoteGroup>& groups, double fee, int batchLimit);
|
||||
void stopConsolidation() { consolidation_.requestStop(); }
|
||||
void dismissConsolidation() { consolidation_.dismiss(); } // no-op while running
|
||||
// A one-off wallet-wide scan the user asked for (the Merge dialog's scope read, a merge): never held back,
|
||||
// but charged to the scan budget (ScanStream::Adhoc) so the routine scans make room. Main thread.
|
||||
services::ScanTicket beginAdhocWalletScan();
|
||||
void endAdhocWalletScan(const services::ScanTicket& ticket, double costMs, services::ScanOutcome outcome);
|
||||
// A GUI wallet scan is in the daemon or queued for it: a one-off scan waits rather than stack on cs_main.
|
||||
bool walletScanInFlight() const { return scan_governor_.anyInFlight(); }
|
||||
|
||||
// Force refresh
|
||||
void refreshNow();
|
||||
// Called on window restore: drop any daemon-output backlog (update() runs while minimized, but its
|
||||
@@ -1661,6 +1675,13 @@ private:
|
||||
std::atomic<bool> opid_filter_unsupported_{false}; // see opidFilterUnsupported(); set on the worker
|
||||
std::atomic<int> maint_calls_{0}; // maintenance calls in flight (MaintenanceCallScope)
|
||||
std::atomic<bool> maint_abort_{false}; // set by abortMaintenanceCalls(), cleared on connect
|
||||
services::ConsolidationRun consolidation_; // C210 (see consolidation())
|
||||
std::uint64_t rpc_session_ = 0; // ++ in onConnected/onDisconnected: an orphan merge is tracked only within one
|
||||
double run_lock_resync_at_ = 0.0; // steadySeconds() before which the run asks for no lock-state re-read
|
||||
void pumpConsolidation();
|
||||
void postConsolidationCall(int addrIdx);
|
||||
void postConsolidationPoll();
|
||||
void finishConsolidation();
|
||||
// Consecutive Core-refresh cycles where BOTH core RPCs failed → likely a dead
|
||||
// connection. After kCoreFailuresBeforeDisconnect, tear down and reconnect.
|
||||
int consecutive_core_failures_ = 0;
|
||||
|
||||
Reference in New Issue
Block a user