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