Skip to content
Bitcoin Roots

v30.3-roots.1 · Commit 23: [PATCH 23/68] 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 a20affafee7400fc9f0fed4f56690eefafb60a3c..b6c5e442293b508c4245b07d47d3a2f2499f4f76 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
@@ -2165,8 +2165,7 @@ bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>&
2165
  COutPoint key;
2166
  Coin coin;
2167
  if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
2168
- if (++count % 8192 == 0) {
2169
- interruption_point();
2170
  if (should_abort) {
2171
  // allow to abort the scan via the abort reference
2172
  return false;
@@ -2188,33 +2187,40 @@ bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>&
2188
  } // namespace
2189
 
2190
  /** RAII object to prevent concurrency issue when scanning the txout set */
2191
- static std::atomic<int> g_scan_progress;
2192
- static std::atomic<bool> g_scan_in_progress;
2193
- static std::atomic<bool> g_should_abort_scan;
2194
- class CoinsViewScanReserver
2195
- {
2196
- private:
2197
- bool m_could_reserve{false};
2198
- public:
2199
- explicit CoinsViewScanReserver() = default;
 
 
 
 
2200
 
2201
- bool reserve() {
2202
- CHECK_NONFATAL(!m_could_reserve);
2203
- if (g_scan_in_progress.exchange(true)) {
2204
- return false;
2205
- }
2206
- CHECK_NONFATAL(g_scan_progress == 0);
2207
- m_could_reserve = true;
2208
- return true;
 
 
 
2209
  }
2210
-
2211
- ~CoinsViewScanReserver() {
2212
- if (m_could_reserve) {
2213
- g_scan_in_progress = false;
2214
- g_scan_progress = 0;
2215
- }
2216
  }
2217
- };
 
2218
 
2219
  static const auto scan_action_arg_desc = RPCArg{
2220
  "action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
@@ -2321,24 +2327,24 @@ static RPCHelpMan scantxoutset()
2321
  UniValue result(UniValue::VOBJ);
2322
  const auto action{self.Arg<std::string>("action")};
2323
  if (action == "status") {
2324
- CoinsViewScanReserver reserver;
2325
  if (reserver.reserve()) {
2326
  // no scan in progress
2327
  return UniValue::VNULL;
2328
  }
2329
- result.pushKV("progress", g_scan_progress.load());
2330
  return result;
2331
  } else if (action == "abort") {
2332
- CoinsViewScanReserver reserver;
2333
  if (reserver.reserve()) {
2334
  // reserve was possible which means no scan was running
2335
  return false;
2336
  }
2337
  // set the abort flag
2338
- g_should_abort_scan = true;
2339
  return true;
2340
  } else if (action == "start") {
2341
- CoinsViewScanReserver reserver;
2342
  if (!reserver.reserve()) {
2343
  throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
2344
  }
@@ -2366,7 +2372,7 @@ static RPCHelpMan scantxoutset()
2366
  UniValue unspents(UniValue::VARR);
2367
  std::vector<CTxOut> input_txos;
2368
  std::map<COutPoint, Coin> coins;
2369
- g_should_abort_scan = false;
2370
  int64_t count = 0;
2371
  std::unique_ptr<CCoinsViewCursor> pcursor;
2372
  const CBlockIndex* tip;
@@ -2379,7 +2385,7 @@ static RPCHelpMan scantxoutset()
2379
  pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor());
2380
  tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
2381
  }
2382
- bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins, node.rpc_interruption_point);
2383
  result.pushKV("success", res);
2384
  result.pushKV("txouts", count);
2385
  result.pushKV("height", tip->nHeight);
 
2165
  COutPoint key;
2166
  Coin coin;
2167
  if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
2168
+ if (rpc::scan::CheckInterruption(++count, interruption_point)) {
 
2169
  if (should_abort) {
2170
  // allow to abort the scan via the abort reference
2171
  return false;
 
2187
  } // namespace
2188
 
2189
  /** RAII object to prevent concurrency issue when scanning the txout set */
2190
+ std::atomic<int> rpc::scan::g_progress;
2191
+ std::atomic<bool> rpc::scan::g_in_progress;
2192
+ std::atomic<bool> rpc::scan::g_should_abort;
2193
+ bool rpc::scan::Reserver::reserve() {
2194
+ CHECK_NONFATAL(!m_reserved);
2195
+ if (g_in_progress.exchange(true)) return false;
2196
+ CHECK_NONFATAL(g_progress == 0);
2197
+ m_reserved = true;
2198
+ return true;
2199
+ }
2200
+ rpc::scan::Reserver::~Reserver() { if (m_reserved) { g_in_progress = false; g_progress = 0; } }
2201
+ bool rpc::scan::CheckInterruption(int64_t count, const std::function<void()>& interruption) { if (count % 8192 != 0) return false; interruption(); return true; }
2202
+ using rpc::scan::Reserver;
2203
 
2204
+ interfaces::ScanResult ScanUTXOSet(NodeContext& node, const std::set<CScript>& needles, std::map<COutPoint, Coin>& coins)
2205
+ {
2206
+ Reserver reserver;
2207
+ if (!reserver.reserve()) return interfaces::ScanResult::BUSY;
2208
+ std::unique_ptr<CCoinsViewCursor> cursor;
2209
+ {
2210
+ ChainstateManager& chainman = EnsureChainman(node);
2211
+ LOCK(cs_main);
2212
+ Chainstate& chainstate = chainman.ActiveChainstate();
2213
+ chainstate.ForceFlushStateToDisk();
2214
+ cursor = CHECK_NONFATAL(chainstate.CoinsDB().Cursor());
2215
  }
2216
+ rpc::scan::g_should_abort = false;
2217
+ std::atomic<int> progress;
2218
+ int64_t count;
2219
+ if (FindScriptPubKey(progress, rpc::scan::g_should_abort, count, cursor.get(), needles, coins, node.rpc_interruption_point)) {
2220
+ return interfaces::ScanResult::SUCCESS;
 
2221
  }
2222
+ return rpc::scan::g_should_abort ? interfaces::ScanResult::ABORTED : interfaces::ScanResult::UNAVAILABLE;
2223
+ }
2224
 
2225
  static const auto scan_action_arg_desc = RPCArg{
2226
  "action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
 
2327
  UniValue result(UniValue::VOBJ);
2328
  const auto action{self.Arg<std::string>("action")};
2329
  if (action == "status") {
2330
+ Reserver reserver;
2331
  if (reserver.reserve()) {
2332
  // no scan in progress
2333
  return UniValue::VNULL;
2334
  }
2335
+ result.pushKV("progress", rpc::scan::g_progress.load());
2336
  return result;
2337
  } else if (action == "abort") {
2338
+ Reserver reserver;
2339
  if (reserver.reserve()) {
2340
  // reserve was possible which means no scan was running
2341
  return false;
2342
  }
2343
  // set the abort flag
2344
+ rpc::scan::g_should_abort = true;
2345
  return true;
2346
  } else if (action == "start") {
2347
+ Reserver reserver;
2348
  if (!reserver.reserve()) {
2349
  throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
2350
  }
 
2372
  UniValue unspents(UniValue::VARR);
2373
  std::vector<CTxOut> input_txos;
2374
  std::map<COutPoint, Coin> coins;
2375
+ rpc::scan::g_should_abort = false;
2376
  int64_t count = 0;
2377
  std::unique_ptr<CCoinsViewCursor> pcursor;
2378
  const CBlockIndex* tip;
 
2385
  pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor());
2386
  tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
2387
  }
2388
+ bool res = FindScriptPubKey(rpc::scan::g_progress, rpc::scan::g_should_abort, count, pcursor.get(), needles, coins, node.rpc_interruption_point);
2389
  result.pushKV("success", res);
2390
  result.pushKV("txouts", count);
2391
  result.pushKV("height", tip->nHeight);