| 118 | } |
| 119 | |
| 120 | bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { |
| 121 | CDBBatch batch(*m_db); |
| 122 | size_t count = 0; |
| 123 | size_t changed = 0; |
| 124 | size_t batch_size = (size_t)gArgs.GetIntArg("-dbbatchsize", nDefaultDbBatchSize); |
| 125 | int crash_simulate = gArgs.GetIntArg("-dbcrashratio", 0); |
| 126 | assert(!hashBlock.IsNull()); |
| 127 | |
| 128 | uint256 old_tip = GetBestBlock(); |
| 129 | if (old_tip.IsNull()) { |
| 130 | // We may be in the middle of replaying. |
| 131 | std::vector<uint256> old_heads = GetHeadBlocks(); |
| 132 | if (old_heads.size() == 2) { |
| 133 | assert(old_heads[0] == hashBlock); |
| 134 | old_tip = old_heads[1]; |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | // In the first batch, mark the database as being in the middle of a |
| 139 | // transition from old_tip to hashBlock. |
| 140 | // A vector is used for future extensibility, as we may want to support |
| 141 | // interrupting after partial writes from multiple independent reorgs. |
| 142 | batch.Erase(DB_BEST_BLOCK); |
| 143 | batch.Write(DB_HEAD_BLOCKS, Vector(hashBlock, old_tip)); |
| 144 | |
| 145 | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { |
| 146 | if (it->second.flags & CCoinsCacheEntry::DIRTY) { |
| 147 | // ELEMENTS: |
| 148 | if (it->second.flags & CCoinsCacheEntry::PEGIN) { |
| 149 | if (!it->second.peginSpent) { |
| 150 | batch.Erase(std::make_pair(DB_PEGIN_FLAG, it->first)); |
| 151 | } else { |
| 152 | // Once spent, we don't care about the entry data, so we store |
| 153 | // a static byte to indicate spentness. |
| 154 | batch.Write(std::make_pair(DB_PEGIN_FLAG, it->first), 1); |
| 155 | } |
| 156 | } else { |
| 157 | // Non-pegin entries are stored the same way as in Core. |
| 158 | CoinEntry entry(&it->first.second); |
| 159 | if (it->second.coin.IsSpent()) { |
| 160 | batch.Erase(entry); |
| 161 | } else { |
| 162 | batch.Write(entry, it->second.coin); |
| 163 | } |
| 164 | } |
| 165 | changed++; |
| 166 | } |
| 167 | count++; |
| 168 | CCoinsMap::iterator itOld = it++; |
| 169 | mapCoins.erase(itOld); |
| 170 | if (batch.SizeEstimate() > batch_size) { |
| 171 | LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0)); |
| 172 | m_db->WriteBatch(batch); |
| 173 | batch.Clear(); |
| 174 | if (crash_simulate) { |
| 175 | static FastRandomContext rng; |
| 176 | if (rng.randrange(crash_simulate) == 0) { |
| 177 | LogPrintf("Simulating a crash. Goodbye.\n"); |
nothing calls this directly
no test coverage detected