| 2999 | } |
| 3000 | |
| 3001 | UniValue CreateUTXOSnapshot( |
| 3002 | NodeContext& node, |
| 3003 | CChainState& chainstate, |
| 3004 | CAutoFile& afile, |
| 3005 | const fs::path& path, |
| 3006 | const fs::path& temppath) |
| 3007 | { |
| 3008 | std::unique_ptr<CCoinsViewCursor> pcursor; |
| 3009 | CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED}; |
| 3010 | CBlockIndex* tip; |
| 3011 | |
| 3012 | { |
| 3013 | // We need to lock cs_main to ensure that the coinsdb isn't written to |
| 3014 | // between (i) flushing coins cache to disk (coinsdb), (ii) getting stats |
| 3015 | // based upon the coinsdb, and (iii) constructing a cursor to the |
| 3016 | // coinsdb for use below this block. |
| 3017 | // |
| 3018 | // Cursors returned by leveldb iterate over snapshots, so the contents |
| 3019 | // of the pcursor will not be affected by simultaneous writes during |
| 3020 | // use below this block. |
| 3021 | // |
| 3022 | // See discussion here: |
| 3023 | // https://github.com/bitcoin/bitcoin/pull/15606#discussion_r274479369 |
| 3024 | // |
| 3025 | LOCK(::cs_main); |
| 3026 | |
| 3027 | chainstate.ForceFlushStateToDisk(); |
| 3028 | |
| 3029 | if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, node.rpc_interruption_point)) { |
| 3030 | throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); |
| 3031 | } |
| 3032 | |
| 3033 | pcursor = chainstate.CoinsDB().Cursor(); |
| 3034 | tip = chainstate.m_blockman.LookupBlockIndex(stats.hashBlock); |
| 3035 | CHECK_NONFATAL(tip); |
| 3036 | } |
| 3037 | |
| 3038 | LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)", |
| 3039 | tip->nHeight, tip->GetBlockHash().ToString(), |
| 3040 | fs::PathToString(path), fs::PathToString(temppath))); |
| 3041 | |
| 3042 | SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, tip->nChainTx}; |
| 3043 | |
| 3044 | afile << metadata; |
| 3045 | |
| 3046 | COutPoint key; |
| 3047 | Coin coin; |
| 3048 | unsigned int iter{0}; |
| 3049 | |
| 3050 | while (pcursor->Valid()) { |
| 3051 | if (iter % 5000 == 0) node.rpc_interruption_point(); |
| 3052 | ++iter; |
| 3053 | if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { |
| 3054 | afile << key; |
| 3055 | afile << coin; |
| 3056 | } |
| 3057 | |
| 3058 | pcursor->Next(); |