// 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 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