Files
ObsidianDragon/src/services/connect_scan_gate.h
T
DanSandClaude Opus 5.5 f86cc4d6b7 perf(net): hold wallet scans at connect, in the background and on tab entry
- Connect gate: after the node starts answering, wallet scans wait until the
  lock state is known and the node knows its tip (or the wallet has fewer
  than 500 transactions), at most 20 s. A node already behind latches the
  sync profile from the connect's getinfo. Meanwhile a list-only pass keeps
  the address list and the cached history up, and the session's first
  balance and per-address balances are read once even while catching up,
  so the wallet shows its last-synced values instead of 0.
- Background: minimized, hidden, or covered without focus (3 s debounce,
  any input cancels) polls only the chain state every 15 s. Wallet scans,
  the mining poll, price, nudges and auto-lock interaction tracking pause;
  auto-lock now fires while minimized. Leaving a held profile marks every
  timer due again.
- Tab entry marks the timers due instead of forcing scans; a stale list or
  a never-loaded history still loads on entry.
- The scan entry points own the hard gates, failed enumerations keep the
  previous lists behind a retry hold, and failed balance calls never
  overwrite known balances with 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:06:34 -05:00

64 lines
2.1 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
// Holds the wallet-wide scans (the balance, the address scan, the history) for a moment after the node
// starts answering: until the lock state is known and either the node knows the chain tip or the wallet is
// small. Right after warmup a node that is behind doesn't know it yet (longestchain stays 0 until its first
// UpdateTip), so the first Core poll would read "synced" and a large wallet's scans would start, holding
// cs_main ahead of the catch-up. Opens after kGraceSeconds whatever happens.
#include <chrono>
namespace dragonx {
namespace services {
class ConnectScanGate {
public:
using Clock = std::chrono::steady_clock;
static constexpr int kGraceSeconds = 20;
static constexpr int kMinWalletTxs = 500; // below this the scans are cheap: don't hold them for the tip
void arm(Clock::time_point now, bool tipKnown)
{
armed_ = true;
edgePending_ = true;
tipKnown_ = tipKnown;
armedAt_ = now;
}
void noteTipKnown() { tipKnown_ = true; }
void disarm()
{
armed_ = false;
edgePending_ = false;
}
bool armed() const { return armed_; }
// walletTxCount: getwalletinfo's txcount, -1 when unknown.
bool open(Clock::time_point now, bool lockStateKnown, int walletTxCount) const
{
if (!armed_) return true;
const bool smallWallet = walletTxCount >= 0 && walletTxCount < kMinWalletTxs;
if (lockStateKnown && (tipKnown_ || smallWallet)) return true;
return now - armedAt_ >= std::chrono::seconds(kGraceSeconds);
}
// True once per arm(), the first time open() is: what the gate held is owed now.
bool consumeOpenEdge(Clock::time_point now, bool lockStateKnown, int walletTxCount)
{
if (!armed_ || !edgePending_ || !open(now, lockStateKnown, walletTxCount)) return false;
edgePending_ = false;
return true;
}
private:
bool armed_ = false;
bool edgePending_ = false;
bool tipKnown_ = false;
Clock::time_point armedAt_{};
};
} // namespace services
} // namespace dragonx