v29.4-roots.4 · Commit 23: [PATCH 23/37] feat(wallet): add transient private-key sweep RPC
src/rpc/blockchain.cpp
Before: src/rpc/blockchain.cpp · After: src/rpc/blockchain.cpp
File metadata
index edda17d3697a56cf3aa6879a036c63b456a09a39..bb3b92d552625562bb4012691070fc1c4753897f 100644
Download original patch · Release notes
Diff rows 1–118 of 118. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.
src/rpc/blockchain.cpp
CHANGED
|
@@ -2116,8 +2116,7 @@ bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>&
|
|
| 2116 |
COutPoint key;
|
| 2117 |
Coin coin;
|
| 2118 |
if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
|
| 2119 |
-
if (++count % 8192 == 0) {
|
| 2120 |
-
interruption_point();
|
| 2121 |
if (should_abort) {
|
| 2122 |
// allow to abort the scan via the abort reference
|
| 2123 |
return false;
|
|
@@ -2139,33 +2138,40 @@ bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>&
|
|
| 2139 |
} // namespace
|
| 2140 |
|
| 2141 |
/** RAII object to prevent concurrency issue when scanning the txout set */
|
| 2142 |
-
static std::atomic<int> g_scan_progress;
|
| 2143 |
-
static std::atomic<bool> g_scan_in_progress;
|
| 2144 |
-
static std::atomic<bool> g_should_abort_scan;
|
| 2145 |
-
class CoinsViewScanReserver
|
| 2146 |
-
{
|
| 2147 |
-
private:
|
| 2148 |
-
bool m_could_reserve{false};
|
| 2149 |
-
public:
|
| 2150 |
-
explicit CoinsViewScanReserver() = default;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2151 |
|
| 2152 |
-
bool reserve() {
|
| 2153 |
-
CHECK_NONFATAL(!m_could_reserve);
|
| 2154 |
-
if (g_scan_in_progress.exchange(true)) {
|
| 2155 |
-
return false;
|
| 2156 |
-
}
|
| 2157 |
-
CHECK_NONFATAL(g_scan_progress == 0);
|
| 2158 |
-
m_could_reserve = true;
|
| 2159 |
-
return true;
|
|
|
|
|
|
|
|
|
|
| 2160 |
}
|
| 2161 |
-
|
| 2162 |
-
~CoinsViewScanReserver() {
|
| 2163 |
-
if (m_could_reserve) {
|
| 2164 |
-
g_scan_in_progress = false;
|
| 2165 |
-
g_scan_progress = 0;
|
| 2166 |
-
}
|
| 2167 |
}
|
| 2168 |
-
};
|
|
|
|
| 2169 |
|
| 2170 |
static const auto scan_action_arg_desc = RPCArg{
|
| 2171 |
"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
|
|
@@ -2267,24 +2273,24 @@ static RPCHelpMan scantxoutset()
|
|
| 2267 |
UniValue result(UniValue::VOBJ);
|
| 2268 |
const auto action{self.Arg<std::string>("action")};
|
| 2269 |
if (action == "status") {
|
| 2270 |
-
CoinsViewScanReserver reserver;
|
| 2271 |
if (reserver.reserve()) {
|
| 2272 |
// no scan in progress
|
| 2273 |
return UniValue::VNULL;
|
| 2274 |
}
|
| 2275 |
-
result.pushKV("progress", g_scan_progress.load());
|
| 2276 |
return result;
|
| 2277 |
} else if (action == "abort") {
|
| 2278 |
-
CoinsViewScanReserver reserver;
|
| 2279 |
if (reserver.reserve()) {
|
| 2280 |
// reserve was possible which means no scan was running
|
| 2281 |
return false;
|
| 2282 |
}
|
| 2283 |
// set the abort flag
|
| 2284 |
-
g_should_abort_scan = true;
|
| 2285 |
return true;
|
| 2286 |
} else if (action == "start") {
|
| 2287 |
-
CoinsViewScanReserver reserver;
|
| 2288 |
if (!reserver.reserve()) {
|
| 2289 |
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
|
| 2290 |
}
|
|
@@ -2312,7 +2318,7 @@ static RPCHelpMan scantxoutset()
|
|
| 2312 |
UniValue unspents(UniValue::VARR);
|
| 2313 |
std::vector<CTxOut> input_txos;
|
| 2314 |
std::map<COutPoint, Coin> coins;
|
| 2315 |
-
g_should_abort_scan = false;
|
| 2316 |
int64_t count = 0;
|
| 2317 |
std::unique_ptr<CCoinsViewCursor> pcursor;
|
| 2318 |
const CBlockIndex* tip;
|
|
@@ -2325,7 +2331,7 @@ static RPCHelpMan scantxoutset()
|
|
| 2325 |
pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor());
|
| 2326 |
tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
|
| 2327 |
}
|
| 2328 |
-
bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins, node.rpc_interruption_point);
|
| 2329 |
result.pushKV("success", res);
|
| 2330 |
result.pushKV("txouts", count);
|
| 2331 |
result.pushKV("height", tip->nHeight);
|
|
|
|
| 2116 |
COutPoint key;
|
| 2117 |
Coin coin;
|
| 2118 |
if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
|
| 2119 |
+
if (rpc::scan::CheckInterruption(++count, interruption_point)) {
|
|
|
|
| 2120 |
if (should_abort) {
|
| 2121 |
// allow to abort the scan via the abort reference
|
| 2122 |
return false;
|
|
|
|
| 2138 |
} // namespace
|
| 2139 |
|
| 2140 |
/** RAII object to prevent concurrency issue when scanning the txout set */
|
| 2141 |
+
std::atomic<int> rpc::scan::g_progress;
|
| 2142 |
+
std::atomic<bool> rpc::scan::g_in_progress;
|
| 2143 |
+
std::atomic<bool> rpc::scan::g_should_abort;
|
| 2144 |
+
bool rpc::scan::Reserver::reserve() {
|
| 2145 |
+
CHECK_NONFATAL(!m_reserved);
|
| 2146 |
+
if (g_in_progress.exchange(true)) return false;
|
| 2147 |
+
CHECK_NONFATAL(g_progress == 0);
|
| 2148 |
+
m_reserved = true;
|
| 2149 |
+
return true;
|
| 2150 |
+
}
|
| 2151 |
+
rpc::scan::Reserver::~Reserver() { if (m_reserved) { g_in_progress = false; g_progress = 0; } }
|
| 2152 |
+
bool rpc::scan::CheckInterruption(int64_t count, const std::function<void()>& interruption) { if (count % 8192 != 0) return false; interruption(); return true; }
|
| 2153 |
+
using rpc::scan::Reserver;
|
| 2154 |
|
| 2155 |
+
interfaces::ScanResult ScanUTXOSet(NodeContext& node, const std::set<CScript>& needles, std::map<COutPoint, Coin>& coins)
|
| 2156 |
+
{
|
| 2157 |
+
Reserver reserver;
|
| 2158 |
+
if (!reserver.reserve()) return interfaces::ScanResult::BUSY;
|
| 2159 |
+
std::unique_ptr<CCoinsViewCursor> cursor;
|
| 2160 |
+
{
|
| 2161 |
+
ChainstateManager& chainman = EnsureChainman(node);
|
| 2162 |
+
LOCK(cs_main);
|
| 2163 |
+
Chainstate& chainstate = chainman.ActiveChainstate();
|
| 2164 |
+
chainstate.ForceFlushStateToDisk();
|
| 2165 |
+
cursor = CHECK_NONFATAL(chainstate.CoinsDB().Cursor());
|
| 2166 |
}
|
| 2167 |
+
rpc::scan::g_should_abort = false;
|
| 2168 |
+
std::atomic<int> progress;
|
| 2169 |
+
int64_t count;
|
| 2170 |
+
if (FindScriptPubKey(progress, rpc::scan::g_should_abort, count, cursor.get(), needles, coins, node.rpc_interruption_point)) {
|
| 2171 |
+
return interfaces::ScanResult::SUCCESS;
|
|
|
|
| 2172 |
}
|
| 2173 |
+
return rpc::scan::g_should_abort ? interfaces::ScanResult::ABORTED : interfaces::ScanResult::UNAVAILABLE;
|
| 2174 |
+
}
|
| 2175 |
|
| 2176 |
static const auto scan_action_arg_desc = RPCArg{
|
| 2177 |
"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
|
|
|
|
| 2273 |
UniValue result(UniValue::VOBJ);
|
| 2274 |
const auto action{self.Arg<std::string>("action")};
|
| 2275 |
if (action == "status") {
|
| 2276 |
+
Reserver reserver;
|
| 2277 |
if (reserver.reserve()) {
|
| 2278 |
// no scan in progress
|
| 2279 |
return UniValue::VNULL;
|
| 2280 |
}
|
| 2281 |
+
result.pushKV("progress", rpc::scan::g_progress.load());
|
| 2282 |
return result;
|
| 2283 |
} else if (action == "abort") {
|
| 2284 |
+
Reserver reserver;
|
| 2285 |
if (reserver.reserve()) {
|
| 2286 |
// reserve was possible which means no scan was running
|
| 2287 |
return false;
|
| 2288 |
}
|
| 2289 |
// set the abort flag
|
| 2290 |
+
rpc::scan::g_should_abort = true;
|
| 2291 |
return true;
|
| 2292 |
} else if (action == "start") {
|
| 2293 |
+
Reserver reserver;
|
| 2294 |
if (!reserver.reserve()) {
|
| 2295 |
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
|
| 2296 |
}
|
|
|
|
| 2318 |
UniValue unspents(UniValue::VARR);
|
| 2319 |
std::vector<CTxOut> input_txos;
|
| 2320 |
std::map<COutPoint, Coin> coins;
|
| 2321 |
+
rpc::scan::g_should_abort = false;
|
| 2322 |
int64_t count = 0;
|
| 2323 |
std::unique_ptr<CCoinsViewCursor> pcursor;
|
| 2324 |
const CBlockIndex* tip;
|
|
|
|
| 2331 |
pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor());
|
| 2332 |
tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
|
| 2333 |
}
|
| 2334 |
+
bool res = FindScriptPubKey(rpc::scan::g_progress, rpc::scan::g_should_abort, count, pcursor.get(), needles, coins, node.rpc_interruption_point);
|
| 2335 |
result.pushKV("success", res);
|
| 2336 |
result.pushKV("txouts", count);
|
| 2337 |
result.pushKV("height", tip->nHeight);
|