mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-19 06:43:45 +01:00
LevelDB block and coin databases
Split off CBlockTreeDB and CCoinsViewDB into txdb-*.{cpp,h} files,
implemented by either LevelDB or BDB.
Based on code from earlier commits by Mike Hearn in his leveldb
branch.
This commit is contained in:
279
src/db.cpp
279
src/db.cpp
@@ -482,285 +482,6 @@ void CDBEnv::Flush(bool fShutdown)
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CBlockTreeDB and CCoinsDB
|
||||
//
|
||||
|
||||
bool CCoinsDB::HaveCoins(uint256 hash) {
|
||||
assert(!fClient);
|
||||
return Exists(make_pair('c', hash));
|
||||
}
|
||||
|
||||
bool CCoinsDB::ReadCoins(uint256 hash, CCoins &coins) {
|
||||
assert(!fClient);
|
||||
return Read(make_pair('c', hash), coins);
|
||||
}
|
||||
|
||||
bool CCoinsDB::WriteCoins(uint256 hash, const CCoins &coins) {
|
||||
assert(!fClient);
|
||||
if (coins.IsPruned())
|
||||
return Erase(make_pair('c', hash));
|
||||
else
|
||||
return Write(make_pair('c', hash), coins);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
|
||||
{
|
||||
return Write(make_pair('b', blockindex.GetBlockHash()), blockindex);
|
||||
}
|
||||
|
||||
bool CCoinsDB::ReadHashBestChain(uint256& hashBestChain)
|
||||
{
|
||||
return Read('B', hashBestChain);
|
||||
}
|
||||
|
||||
bool CCoinsDB::WriteHashBestChain(uint256 hashBestChain)
|
||||
{
|
||||
return Write('B', hashBestChain);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)
|
||||
{
|
||||
return Read('I', bnBestInvalidWork);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)
|
||||
{
|
||||
return Write('I', bnBestInvalidWork);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) {
|
||||
return Write(make_pair('f', nFile), info);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
|
||||
return Read(make_pair('f', nFile), info);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::WriteLastBlockFile(int nFile) {
|
||||
return Write('l', nFile);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadLastBlockFile(int &nFile) {
|
||||
return Read('l', nFile);
|
||||
}
|
||||
|
||||
CCoinsViewDB::CCoinsViewDB() : db("cr+") {}
|
||||
bool CCoinsViewDB::GetCoins(uint256 txid, CCoins &coins) { return db.ReadCoins(txid, coins); }
|
||||
bool CCoinsViewDB::SetCoins(uint256 txid, const CCoins &coins) { return db.WriteCoins(txid, coins); }
|
||||
bool CCoinsViewDB::HaveCoins(uint256 txid) { return db.HaveCoins(txid); }
|
||||
CBlockIndex *CCoinsViewDB::GetBestBlock() {
|
||||
uint256 hashBestChain;
|
||||
if (!db.ReadHashBestChain(hashBestChain))
|
||||
return NULL;
|
||||
std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hashBestChain);
|
||||
if (it == mapBlockIndex.end())
|
||||
return NULL;
|
||||
return it->second;
|
||||
}
|
||||
bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { return db.WriteHashBestChain(pindex->GetBlockHash()); }
|
||||
bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) {
|
||||
printf("Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size());
|
||||
|
||||
if (!db.TxnBegin())
|
||||
return false;
|
||||
bool fOk = true;
|
||||
for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) {
|
||||
fOk = db.WriteCoins(it->first, it->second);
|
||||
if (!fOk)
|
||||
break;
|
||||
}
|
||||
if (fOk)
|
||||
fOk = db.WriteHashBestChain(pindex->GetBlockHash());
|
||||
|
||||
if (!fOk)
|
||||
db.TxnAbort();
|
||||
else
|
||||
fOk = db.TxnCommit();
|
||||
|
||||
return fOk;
|
||||
}
|
||||
|
||||
CBlockIndex static * InsertBlockIndex(uint256 hash)
|
||||
{
|
||||
if (hash == 0)
|
||||
return NULL;
|
||||
|
||||
// Return existing
|
||||
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
|
||||
if (mi != mapBlockIndex.end())
|
||||
return (*mi).second;
|
||||
|
||||
// Create new
|
||||
CBlockIndex* pindexNew = new CBlockIndex();
|
||||
if (!pindexNew)
|
||||
throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");
|
||||
mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
|
||||
pindexNew->phashBlock = &((*mi).first);
|
||||
|
||||
return pindexNew;
|
||||
}
|
||||
|
||||
bool LoadBlockIndexDB()
|
||||
{
|
||||
if (!pblocktree->LoadBlockIndexGuts())
|
||||
return false;
|
||||
|
||||
if (fRequestShutdown)
|
||||
return true;
|
||||
|
||||
// Calculate bnChainWork
|
||||
vector<pair<int, CBlockIndex*> > vSortedByHeight;
|
||||
vSortedByHeight.reserve(mapBlockIndex.size());
|
||||
BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
|
||||
{
|
||||
CBlockIndex* pindex = item.second;
|
||||
vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
|
||||
}
|
||||
sort(vSortedByHeight.begin(), vSortedByHeight.end());
|
||||
BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
|
||||
{
|
||||
CBlockIndex* pindex = item.second;
|
||||
pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork();
|
||||
pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx;
|
||||
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK))
|
||||
setBlockIndexValid.insert(pindex);
|
||||
}
|
||||
|
||||
// Load block file info
|
||||
pblocktree->ReadLastBlockFile(nLastBlockFile);
|
||||
printf("LoadBlockIndex(): last block file = %i\n", nLastBlockFile);
|
||||
if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile))
|
||||
printf("LoadBlockIndex(): last block file: %s\n", infoLastBlockFile.ToString().c_str());
|
||||
|
||||
// Load hashBestChain pointer to end of best chain
|
||||
pindexBest = pcoinsTip->GetBestBlock();
|
||||
if (pindexBest == NULL)
|
||||
{
|
||||
if (pindexGenesisBlock == NULL)
|
||||
return true;
|
||||
}
|
||||
hashBestChain = pindexBest->GetBlockHash();
|
||||
nBestHeight = pindexBest->nHeight;
|
||||
bnBestChainWork = pindexBest->bnChainWork;
|
||||
|
||||
// set 'next' pointers in best chain
|
||||
CBlockIndex *pindex = pindexBest;
|
||||
while(pindex != NULL && pindex->pprev != NULL) {
|
||||
CBlockIndex *pindexPrev = pindex->pprev;
|
||||
pindexPrev->pnext = pindex;
|
||||
pindex = pindexPrev;
|
||||
}
|
||||
printf("LoadBlockIndex(): hashBestChain=%s height=%d date=%s\n",
|
||||
hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
|
||||
DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
|
||||
|
||||
// Load bnBestInvalidWork, OK if it doesn't exist
|
||||
pblocktree->ReadBestInvalidWork(bnBestInvalidWork);
|
||||
|
||||
// Verify blocks in the best chain
|
||||
int nCheckLevel = GetArg("-checklevel", 1);
|
||||
int nCheckDepth = GetArg( "-checkblocks", 2500);
|
||||
if (nCheckDepth == 0)
|
||||
nCheckDepth = 1000000000; // suffices until the year 19000
|
||||
if (nCheckDepth > nBestHeight)
|
||||
nCheckDepth = nBestHeight;
|
||||
printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
|
||||
CBlockIndex* pindexFork = NULL;
|
||||
for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
|
||||
{
|
||||
if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
|
||||
break;
|
||||
CBlock block;
|
||||
if (!block.ReadFromDisk(pindex))
|
||||
return error("LoadBlockIndex() : block.ReadFromDisk failed");
|
||||
// check level 1: verify block validity
|
||||
if (nCheckLevel>0 && !block.CheckBlock())
|
||||
{
|
||||
printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
|
||||
pindexFork = pindex->pprev;
|
||||
}
|
||||
// TODO: stronger verifications
|
||||
}
|
||||
if (pindexFork && !fRequestShutdown)
|
||||
{
|
||||
// TODO: reorg back
|
||||
return error("LoadBlockIndex(): chain database corrupted");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CBlockTreeDB::LoadBlockIndexGuts()
|
||||
{
|
||||
// Get database cursor
|
||||
Dbc* pcursor = GetCursor();
|
||||
if (!pcursor)
|
||||
return false;
|
||||
|
||||
// Load mapBlockIndex
|
||||
unsigned int fFlags = DB_SET_RANGE;
|
||||
loop
|
||||
{
|
||||
// Read next record
|
||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||
if (fFlags == DB_SET_RANGE)
|
||||
ssKey << make_pair('b', uint256(0));
|
||||
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
||||
int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
|
||||
fFlags = DB_NEXT;
|
||||
if (ret == DB_NOTFOUND)
|
||||
break;
|
||||
else if (ret != 0)
|
||||
return false;
|
||||
|
||||
// Unserialize
|
||||
|
||||
try {
|
||||
char chType;
|
||||
ssKey >> chType;
|
||||
if (chType == 'b' && !fRequestShutdown)
|
||||
{
|
||||
CDiskBlockIndex diskindex;
|
||||
ssValue >> diskindex;
|
||||
|
||||
// Construct block index object
|
||||
CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
|
||||
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
|
||||
pindexNew->nHeight = diskindex.nHeight;
|
||||
pindexNew->nFile = diskindex.nFile;
|
||||
pindexNew->nDataPos = diskindex.nDataPos;
|
||||
pindexNew->nUndoPos = diskindex.nUndoPos;
|
||||
pindexNew->nVersion = diskindex.nVersion;
|
||||
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
|
||||
pindexNew->nTime = diskindex.nTime;
|
||||
pindexNew->nBits = diskindex.nBits;
|
||||
pindexNew->nNonce = diskindex.nNonce;
|
||||
pindexNew->nStatus = diskindex.nStatus;
|
||||
pindexNew->nTx = diskindex.nTx;
|
||||
|
||||
// Watch for genesis block
|
||||
if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock)
|
||||
pindexGenesisBlock = pindexNew;
|
||||
|
||||
if (!pindexNew->CheckIndex())
|
||||
return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
break; // if shutdown requested or finished loading block index
|
||||
}
|
||||
} // try
|
||||
catch (std::exception &e) {
|
||||
return error("%s() : deserialize error", __PRETTY_FUNCTION__);
|
||||
}
|
||||
}
|
||||
pcursor->close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user