Changeset View
Changeset View
Standalone View
Standalone View
src/txdb.cpp
Show All 25 Lines | |||||
static const char DB_BLOCK_INDEX = 'b'; | static const char DB_BLOCK_INDEX = 'b'; | ||||
static const char DB_BEST_BLOCK = 'B'; | static const char DB_BEST_BLOCK = 'B'; | ||||
static const char DB_HEAD_BLOCKS = 'H'; | static const char DB_HEAD_BLOCKS = 'H'; | ||||
static const char DB_FLAG = 'F'; | static const char DB_FLAG = 'F'; | ||||
static const char DB_REINDEX_FLAG = 'R'; | static const char DB_REINDEX_FLAG = 'R'; | ||||
static const char DB_LAST_BLOCK = 'l'; | static const char DB_LAST_BLOCK = 'l'; | ||||
static const char DB_COMMITMENT = 'U'; | |||||
namespace { | namespace { | ||||
struct CoinEntry { | struct CoinEntry { | ||||
COutPoint *outpoint; | COutPoint *outpoint; | ||||
char key; | char key; | ||||
CoinEntry(const COutPoint *ptr) | CoinEntry(const COutPoint *ptr) | ||||
: outpoint(const_cast<COutPoint *>(ptr)), key(DB_COIN) {} | : outpoint(const_cast<COutPoint *>(ptr)), key(DB_COIN) {} | ||||
Show All 31 Lines | |||||
std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const { | std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const { | ||||
std::vector<uint256> vhashHeadBlocks; | std::vector<uint256> vhashHeadBlocks; | ||||
if (!db.Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) { | if (!db.Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) { | ||||
return std::vector<uint256>(); | return std::vector<uint256>(); | ||||
} | } | ||||
return vhashHeadBlocks; | return vhashHeadBlocks; | ||||
} | } | ||||
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { | CUtxoCommit *CCoinsViewDB::GetCommitment() const { | ||||
CUtxoCommit *result = new CUtxoCommit(); | |||||
if (!db.Read(DB_COMMITMENT, *result)) { | |||||
delete result; | |||||
return nullptr; | |||||
} else { | |||||
return result; | |||||
} | |||||
} | |||||
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, | |||||
CUtxoCommit *commitDelta) { | |||||
CDBBatch batch(db); | CDBBatch batch(db); | ||||
size_t count = 0; | size_t count = 0; | ||||
size_t changed = 0; | size_t changed = 0; | ||||
size_t batch_size = | size_t batch_size = | ||||
(size_t)gArgs.GetArg("-dbbatchsize", nDefaultDbBatchSize); | (size_t)gArgs.GetArg("-dbbatchsize", nDefaultDbBatchSize); | ||||
int crash_simulate = gArgs.GetArg("-dbcrashratio", 0); | int crash_simulate = gArgs.GetArg("-dbcrashratio", 0); | ||||
assert(!hashBlock.IsNull()); | assert(!hashBlock.IsNull()); | ||||
uint256 old_tip = GetBestBlock(); | uint256 old_tip = GetBestBlock(); | ||||
if (old_tip.IsNull()) { | if (old_tip.IsNull()) { | ||||
// We may be in the middle of replaying. | // We may be in the middle of replaying. | ||||
std::vector<uint256> old_heads = GetHeadBlocks(); | std::vector<uint256> old_heads = GetHeadBlocks(); | ||||
if (old_heads.size() == 2) { | if (old_heads.size() == 2) { | ||||
assert(old_heads[0] == hashBlock); | assert(old_heads[0] == hashBlock); | ||||
old_tip = old_heads[1]; | old_tip = old_heads[1]; | ||||
} | } | ||||
} | } | ||||
// In the first batch, mark the database as being in the middle of a | // In the first batch, mark the database as being in the middle of a | ||||
// transition from old_tip to hashBlock. | // transition from old_tip to hashBlock. | ||||
// A vector is used for future extensibility, as we may want to support | // A vector is used for future extensibility, as we may want to support | ||||
// interrupting after partial writes from multiple independent reorgs. | // interrupting after partial writes from multiple independent reorgs. | ||||
batch.Erase(DB_BEST_BLOCK); | batch.Erase(DB_BEST_BLOCK); | ||||
batch.Write(DB_HEAD_BLOCKS, std::vector<uint256>{hashBlock, old_tip}); | batch.Write(DB_HEAD_BLOCKS, std::vector<uint256>{hashBlock, old_tip}); | ||||
batch.Erase(DB_COMMITMENT); | |||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { | ||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) { | if (it->second.flags & CCoinsCacheEntry::DIRTY) { | ||||
CoinEntry entry(&it->first); | CoinEntry entry(&it->first); | ||||
if (it->second.coin.IsSpent()) { | if (it->second.coin.IsSpent()) { | ||||
batch.Erase(entry); | batch.Erase(entry); | ||||
} else { | } else { | ||||
batch.Write(entry, it->second.coin); | batch.Write(entry, it->second.coin); | ||||
Show All 13 Lines | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { | ||||
if (rng.randrange(crash_simulate) == 0) { | if (rng.randrange(crash_simulate) == 0) { | ||||
LogPrintf("Simulating a crash. Goodbye.\n"); | LogPrintf("Simulating a crash. Goodbye.\n"); | ||||
_Exit(0); | _Exit(0); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Merge a commitment is needed | |||||
// If commitDelta == nullptr, we just delete the commitment | |||||
// This can happen if blocks are assumevalid | |||||
if (commitDelta != nullptr) { | |||||
// It may be easiest to start synchronous, blocking a few minutes: | |||||
CUtxoCommit *dbCommitment = GetCommitment(); | |||||
if (dbCommitment != nullptr) { | |||||
// We have a commitment and are maintaining it | |||||
// merge the incoming delta | |||||
LogPrint(BCLog::COINDB, "Merging UTXO commitment\n"); | |||||
dbCommitment->Add(*commitDelta); | |||||
batch.Write(DB_COMMITMENT, *dbCommitment); | |||||
} else { | |||||
// We are not yet maintaining a commitment. This can happen | |||||
// * If the incoming blocks are assumevalid | |||||
// * If we're updating from a previous version | |||||
// * If we crashed during a previous update | |||||
LogPrint(BCLog::COINDB, "Start maintaining UTXO commitment\n"); | |||||
// Create the commitment from the current coinsdb dataset | |||||
// This blocks a few minutes | |||||
// We could do this async, but this would make it rather | |||||
// difficult to check commitments during the procedure | |||||
CUtxoCommit snapshotCommitment; | |||||
std::unique_ptr<CCoinsViewCursor> cursor(this->Cursor()); | |||||
snapshotCommitment.AddCoinView( | |||||
cursor.get()); // blocks a few minutes | |||||
snapshotCommitment.Add(*commitDelta); | |||||
batch.Write(DB_COMMITMENT, snapshotCommitment); | |||||
} | |||||
} | |||||
// In the last batch, mark the database as consistent with hashBlock again. | // In the last batch, mark the database as consistent with hashBlock again. | ||||
batch.Erase(DB_HEAD_BLOCKS); | batch.Erase(DB_HEAD_BLOCKS); | ||||
batch.Write(DB_BEST_BLOCK, hashBlock); | batch.Write(DB_BEST_BLOCK, hashBlock); | ||||
LogPrint(BCLog::COINDB, "Writing final batch of %.2f MiB\n", | LogPrint(BCLog::COINDB, "Writing final batch of %.2f MiB\n", | ||||
batch.SizeEstimate() * (1.0 / 1048576.0)); | batch.SizeEstimate() * (1.0 / 1048576.0)); | ||||
bool ret = db.WriteBatch(batch); | bool ret = db.WriteBatch(batch); | ||||
LogPrint(BCLog::COINDB, "Committed %u changed transaction outputs (out of " | LogPrint(BCLog::COINDB, "Committed %u changed transaction outputs (out of " | ||||
▲ Show 20 Lines • Show All 313 Lines • Show Last 20 Lines |