/tmp/bitcoin/src/wallet/scan.h
Line | Count | Source |
1 | | // Copyright (c) 2026-present The Bitcoin Core developers |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #ifndef BITCOIN_WALLET_SCAN_H |
6 | | #define BITCOIN_WALLET_SCAN_H |
7 | | |
8 | | #include <uint256.h> |
9 | | #include <util/time.h> |
10 | | |
11 | | #include <atomic> |
12 | | #include <functional> |
13 | | #include <optional> |
14 | | |
15 | | namespace wallet { |
16 | | class CWallet; |
17 | | |
18 | | /** Result of a wallet scan */ |
19 | | struct ScanResult { |
20 | | enum { SUCCESS, FAILURE, USER_ABORT } status = SUCCESS; |
21 | | |
22 | | //! Hash and height of most recent block that was successfully scanned. |
23 | | //! Unset if no blocks were scanned due to read errors or the chain |
24 | | //! being empty. |
25 | | uint256 last_scanned_block; |
26 | | std::optional<int> last_scanned_height; |
27 | | |
28 | | //! Height of the most recent block that could not be scanned due to |
29 | | //! read errors or pruning. Will be set if status is FAILURE, unset if |
30 | | //! status is SUCCESS, and may or may not be set if status is |
31 | | //! USER_ABORT. |
32 | | uint256 last_failed_block; |
33 | | }; |
34 | | |
35 | | /** RAII object to check and reserve a wallet rescan */ |
36 | | class WalletRescanReserver |
37 | | { |
38 | | private: |
39 | | using Clock = std::chrono::steady_clock; |
40 | | using NowFn = std::function<Clock::time_point()>; |
41 | | CWallet& m_wallet; |
42 | | bool m_could_reserve{false}; |
43 | | NowFn m_now; |
44 | | public: |
45 | 1.11k | explicit WalletRescanReserver(CWallet& w) : m_wallet(w) {} |
46 | | |
47 | | bool reserve(bool with_passphrase = false); |
48 | | bool isReserved() const; |
49 | | |
50 | 84.0k | Clock::time_point now() const { return m_now ? m_now() : Clock::now(); }; |
51 | | |
52 | 2 | void setNow(NowFn now) { m_now = std::move(now); } |
53 | | |
54 | | ~WalletRescanReserver(); |
55 | | }; |
56 | | |
57 | | class ChainScanner { |
58 | | private: |
59 | | CWallet& m_wallet; |
60 | | |
61 | | std::atomic<bool> m_abort{false}; |
62 | | std::atomic<bool> m_scanning{false}; |
63 | | std::atomic<bool> m_scanning_with_passphrase{false}; |
64 | | std::atomic<SteadyClock::time_point> m_scanning_start{SteadyClock::time_point{}}; |
65 | | std::atomic<double> m_scanning_progress{0}; |
66 | | |
67 | | //! Progress window and tip tracked across Scan loop iterations. The |
68 | | //! current block's progress is a plain local in Scan; only the window |
69 | | //! bounds are shared with the helpers, and UpdateTipIfChanged is the |
70 | | //! sole mutator. |
71 | | struct LoopState { |
72 | | double progress_begin{0}; |
73 | | double progress_end{0}; |
74 | | uint256 tip_hash; |
75 | | }; |
76 | | |
77 | | //! Locate block_hash in the chain, queueing its active-chain successor |
78 | | //! into next_block if it exists and is within the scan range. Returns |
79 | | //! whether the block itself is still in the active chain. |
80 | | bool QueueNextBlock(const uint256& block_hash, int block_height, std::optional<std::pair<uint256, int>>& next_block, std::optional<int> max_height); |
81 | | bool ScanBlock(const uint256& block_hash, int block_height, bool save_progress); |
82 | | void UpdateProgress(const LoopState& state, double progress_current, int block_height); |
83 | | void UpdateTipIfChanged(LoopState& state); |
84 | | |
85 | | //! Only WalletRescanReserver may reserve and release scans, so that |
86 | | //! reservations are always managed RAII-style. |
87 | | friend class WalletRescanReserver; |
88 | | bool TryReserve(bool with_passphrase = false); |
89 | | void Release(); |
90 | | |
91 | | public: |
92 | 1.17k | explicit ChainScanner(CWallet& wallet) : m_wallet(wallet) {} |
93 | | |
94 | 3 | void Abort() { m_abort = true; } |
95 | 1.07k | bool IsAborting() const { return m_abort; } |
96 | 1.61k | bool IsScanning() const { return m_scanning; } |
97 | 55 | bool IsScanningWithPassphrase() const { return m_scanning_with_passphrase; } |
98 | 1 | SteadyClock::duration ScanningDuration() const { return m_scanning ? SteadyClock::now() - m_scanning_start.load() : SteadyClock::duration{}; } |
99 | 2 | double ScanningProgress() const { return m_scanning ? m_scanning_progress.load() : 0; } |
100 | | |
101 | | /** Scan active chain for relevant transactions after importing keys. Should |
102 | | * be called whenever new keys are added to the wallet, with the oldest key |
103 | | * creation time. |
104 | | * @return Earliest timestamp that could be successfully scanned from. Timestamp |
105 | | * returned will be higher than startTime if relevant blocks could not be read. */ |
106 | | int64_t ScanFromTime(int64_t startTime, const WalletRescanReserver& reserver); |
107 | | |
108 | | /** |
109 | | * Scan the block chain (starting in start_block) for transactions |
110 | | * from or to us. If max_height is not set, the |
111 | | * mempool will be scanned as well. |
112 | | * |
113 | | * @param[in] start_block Scan starting block. If block is not on the active |
114 | | * chain, the scan will return SUCCESS immediately. |
115 | | * @param[in] start_height Height of start_block |
116 | | * @param[in] max_height Optional max scanning height. If unset there is |
117 | | * no maximum and scanning can continue to the tip |
118 | | * |
119 | | * @return ScanResult returning scan information and indicating success or |
120 | | * failure. Return status will be set to SUCCESS if scan was |
121 | | * successful. FAILURE if a complete rescan was not possible (due to |
122 | | * pruning or corruption). USER_ABORT if the rescan was aborted before |
123 | | * it could complete. |
124 | | * |
125 | | * @pre Caller needs to make sure start_block (and the optional stop_block) are on |
126 | | * the main chain after the addition of any new keys you want to detect |
127 | | * transactions for. |
128 | | */ |
129 | | ScanResult Scan(const uint256& start_block, int start_height, std::optional<int> max_height, |
130 | | const WalletRescanReserver& reserver, bool save_progress); |
131 | | |
132 | | }; |
133 | | |
134 | | } // namespace wallet |
135 | | |
136 | | #endif // BITCOIN_WALLET_SCAN_H |