// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 // // Pure decision logic for persisting the tx-history snapshot: what counts as "changed", and when a write is // due. No I/O, no clock, no sodium: unit-tested directly. #pragma once #include "wallet_state.h" #include #include #include #include #include #include namespace dragonx { namespace data { // 64-bit fingerprints of what a snapshot would contain. struct HistorySnapshotSig { std::uint64_t content = 0; // rows (confirmations by UI bucket) + wallet hash + key id std::uint64_t slow = 0; // shielded scan heights + raw confirmations + tip: drift every block std::size_t rows = 0; // rows that would be persisted (pending sends excluded) }; // Order-independent over rows. `content` covers the persisted row fields (txid, type, amount bit pattern, // timestamp, address, from_address, memo) with confirmations bucketed at the thresholds the UI and the refresh // read off a stored row (< 0 conflicted, 0 pending, 1-9 counted, >= 10 confirmed, >= 100 mature), plus the // wallet hash and `keyId` (a passphrase change must rewrite: the old ciphertext is unreadable with the new key). // `slow` covers what changes every block: the per-address scan heights (entries replace() would persist), each // row's raw confirmations, and the tip. HistorySnapshotSig historySnapshotSignature(const std::vector& transactions, const std::unordered_set& skipTxids, const std::string& walletHash, std::uint64_t keyId, const std::unordered_map& shieldedScanHeights, int tipHeight, const std::string& tipHash, // Sent-sweep watermark: W/A/H_A move per sweep (folded into `slow`, // written on the slow cadence and always on disconnect/lock/exit), // the enrichment backlog into `content` (written within ~20 s), so an // invalidation that only changes the watermark still reaches disk. int sentHeight = -1, int sentAnchorHeight = -1, const std::string& sentAnchorHash = std::string(), const std::unordered_set& sentBacklog = {}); enum class HistoryStoreMode { Coalesce, // routine refresh: content changes at most every kHistoryMinStoreIntervalSec, slow drift every // kHistorySlowStoreIntervalSec Now // user action / disconnect / lock / exit: store now if anything differs from the last submit }; enum class HistoryStoreAction { Skip, Defer, Store }; constexpr double kHistoryMinStoreIntervalSec = 20.0; constexpr double kHistorySlowStoreIntervalSec = 600.0; struct HistoryStoreInput { HistoryStoreMode mode = HistoryStoreMode::Coalesce; bool haveStored = false; // a snapshot of this session (submitted, or the loaded one) is on disk HistorySnapshotSig stored; // its signature HistorySnapshotSig current; double sinceLastSubmitSec = 1e9; // 1e9 = never submitted this session }; HistoryStoreAction decideHistoryStore(const HistoryStoreInput& input); } // namespace data } // namespace dragonx