@@ -5660,69 +5660,81 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
56605660 return false ;
56615661 }
56625662
5663- COutPoint outpoint;
5664- Coin coin;
56655663 const uint64_t coins_count = metadata.m_coins_count ;
56665664 uint64_t coins_left = metadata.m_coins_count ;
56675665
5668- LogPrintf (" [snapshot] loading coins from snapshot %s\n " , base_blockhash.ToString ());
5666+ LogPrintf (" [snapshot] loading %d coins from snapshot %s\n " , coins_left , base_blockhash.ToString ());
56695667 int64_t coins_processed{0 };
56705668
56715669 while (coins_left > 0 ) {
56725670 try {
5673- coins_file >> outpoint;
5674- coins_file >> coin;
5675- } catch (const std::ios_base::failure&) {
5676- LogPrintf (" [snapshot] bad snapshot format or truncated snapshot after deserializing %d coins\n " ,
5677- coins_count - coins_left);
5678- return false ;
5679- }
5680- if (coin.nHeight > base_height ||
5681- outpoint.n >= std::numeric_limits<decltype (outpoint.n )>::max () // Avoid integer wrap-around in coinstats.cpp:ApplyHash
5682- ) {
5683- LogPrintf (" [snapshot] bad snapshot data after deserializing %d coins\n " ,
5684- coins_count - coins_left);
5685- return false ;
5686- }
5687- if (!MoneyRange (coin.out .nValue )) {
5688- LogPrintf (" [snapshot] bad snapshot data after deserializing %d coins - bad tx out value\n " ,
5689- coins_count - coins_left);
5690- return false ;
5691- }
5671+ Txid txid;
5672+ coins_file >> txid;
5673+ size_t coins_per_txid{0 };
5674+ coins_per_txid = ReadCompactSize (coins_file);
5675+
5676+ if (coins_per_txid > coins_left) {
5677+ LogPrintf (" [snapshot] mismatch in coins count in snapshot metadata and actual snapshot data\n " );
5678+ return false ;
5679+ }
56925680
5693- coins_cache.EmplaceCoinInternalDANGER (std::move (outpoint), std::move (coin));
5681+ for (size_t i = 0 ; i < coins_per_txid; i++) {
5682+ COutPoint outpoint;
5683+ Coin coin;
5684+ outpoint.n = static_cast <uint32_t >(ReadCompactSize (coins_file));
5685+ outpoint.hash = txid;
5686+ coins_file >> coin;
5687+ if (coin.nHeight > base_height ||
5688+ outpoint.n >= std::numeric_limits<decltype (outpoint.n )>::max () // Avoid integer wrap-around in coinstats.cpp:ApplyHash
5689+ ) {
5690+ LogPrintf (" [snapshot] bad snapshot data after deserializing %d coins\n " ,
5691+ coins_count - coins_left);
5692+ return false ;
5693+ }
5694+ if (!MoneyRange (coin.out .nValue )) {
5695+ LogPrintf (" [snapshot] bad snapshot data after deserializing %d coins - bad tx out value\n " ,
5696+ coins_count - coins_left);
5697+ return false ;
5698+ }
5699+ coins_cache.EmplaceCoinInternalDANGER (std::move (outpoint), std::move (coin));
56945700
5695- --coins_left;
5696- ++coins_processed;
5701+ --coins_left;
5702+ ++coins_processed;
56975703
5698- if (coins_processed % 1000000 == 0 ) {
5699- LogPrintf (" [snapshot] %d coins loaded (%.2f%%, %.2f MB)\n " ,
5700- coins_processed,
5701- static_cast <float >(coins_processed) * 100 / static_cast <float >(coins_count),
5702- coins_cache.DynamicMemoryUsage () / (1000 * 1000 ));
5703- }
5704+ if (coins_processed % 1000000 == 0 ) {
5705+ LogPrintf (" [snapshot] %d coins loaded (%.2f%%, %.2f MB)\n " ,
5706+ coins_processed,
5707+ static_cast <float >(coins_processed) * 100 / static_cast <float >(coins_count),
5708+ coins_cache.DynamicMemoryUsage () / (1000 * 1000 ));
5709+ }
57045710
5705- // Batch write and flush (if we need to) every so often.
5706- //
5707- // If our average Coin size is roughly 41 bytes, checking every 120,000 coins
5708- // means <5MB of memory imprecision.
5709- if (coins_processed % 120000 == 0 ) {
5710- if (m_interrupt) {
5711- return false ;
5712- }
5711+ // Batch write and flush (if we need to) every so often.
5712+ //
5713+ // If our average Coin size is roughly 41 bytes, checking every 120,000 coins
5714+ // means <5MB of memory imprecision.
5715+ if (coins_processed % 120000 == 0 ) {
5716+ if (m_interrupt) {
5717+ return false ;
5718+ }
57135719
5714- const auto snapshot_cache_state = WITH_LOCK (::cs_main,
5715- return snapshot_chainstate.GetCoinsCacheSizeState ());
5720+ const auto snapshot_cache_state = WITH_LOCK (::cs_main,
5721+ return snapshot_chainstate.GetCoinsCacheSizeState ());
57165722
5717- if (snapshot_cache_state >= CoinsCacheSizeState::CRITICAL) {
5718- // This is a hack - we don't know what the actual best block is, but that
5719- // doesn't matter for the purposes of flushing the cache here. We'll set this
5720- // to its correct value (`base_blockhash`) below after the coins are loaded.
5721- coins_cache.SetBestBlock (GetRandHash ());
5723+ if (snapshot_cache_state >= CoinsCacheSizeState::CRITICAL) {
5724+ // This is a hack - we don't know what the actual best block is, but that
5725+ // doesn't matter for the purposes of flushing the cache here. We'll set this
5726+ // to its correct value (`base_blockhash`) below after the coins are loaded.
5727+ coins_cache.SetBestBlock (GetRandHash ());
57225728
5723- // No need to acquire cs_main since this chainstate isn't being used yet.
5724- FlushSnapshotToDisk (coins_cache, /* snapshot_loaded=*/ false );
5729+ // No need to acquire cs_main since this chainstate isn't being used yet.
5730+ FlushSnapshotToDisk (coins_cache, /* snapshot_loaded=*/ false );
5731+ }
5732+ }
57255733 }
5734+ } catch (const std::ios_base::failure&) {
5735+ LogPrintf (" [snapshot] bad snapshot format or truncated snapshot after deserializing %d coins\n " ,
5736+ coins_processed);
5737+ return false ;
57265738 }
57275739 }
57285740
@@ -5735,7 +5747,8 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
57355747
57365748 bool out_of_coins{false };
57375749 try {
5738- coins_file >> outpoint;
5750+ Txid txid;
5751+ coins_file >> txid;
57395752 } catch (const std::ios_base::failure&) {
57405753 // We expect an exception since we should be out of coins.
57415754 out_of_coins = true ;
0 commit comments