mirror of
https://github.com/twisterarmy/twister-core.git
synced 2025-01-11 07:17:53 +00: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:
parent
44d40f26dc
commit
2d8a48292b
@ -92,12 +92,15 @@ contains(BITCOIN_NEED_QT_PLUGINS, 1) {
|
|||||||
|
|
||||||
contains(USE_LEVELDB, -) {
|
contains(USE_LEVELDB, -) {
|
||||||
message(Building without LevelDB)
|
message(Building without LevelDB)
|
||||||
|
SOURCES += src/txdb-bdb.cpp
|
||||||
|
HEADERS += src/txdb-bdb.h
|
||||||
} else {
|
} else {
|
||||||
message(Building with LevelDB)
|
message(Building with LevelDB)
|
||||||
DEFINES += USE_LEVELDB
|
DEFINES += USE_LEVELDB
|
||||||
INCLUDEPATH += src/leveldb/include src/leveldb/helpers
|
INCLUDEPATH += src/leveldb/include src/leveldb/helpers
|
||||||
LIBS += $$PWD/src/leveldb/libleveldb.a $$PWD/src/leveldb/libmemenv.a
|
LIBS += $$PWD/src/leveldb/libleveldb.a $$PWD/src/leveldb/libmemenv.a
|
||||||
SOURCES += src/leveldb.cpp
|
SOURCES += src/leveldb.cpp src/txdb-leveldb.cpp
|
||||||
|
HEADERS += src/leveldb.h src/txdb-leveldb.h
|
||||||
!windows {
|
!windows {
|
||||||
genleveldb.commands = cd $$PWD/src/leveldb ; $(MAKE) libleveldb.a libmemenv.a
|
genleveldb.commands = cd $$PWD/src/leveldb ; $(MAKE) libleveldb.a libmemenv.a
|
||||||
} else {
|
} else {
|
||||||
@ -153,6 +156,7 @@ HEADERS += src/qt/bitcoingui.h \
|
|||||||
src/net.h \
|
src/net.h \
|
||||||
src/key.h \
|
src/key.h \
|
||||||
src/db.h \
|
src/db.h \
|
||||||
|
src/txdb.h \
|
||||||
src/walletdb.h \
|
src/walletdb.h \
|
||||||
src/script.h \
|
src/script.h \
|
||||||
src/init.h \
|
src/init.h \
|
||||||
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
55
src/db.h
55
src/db.h
@ -315,61 +315,6 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Access to the transaction database (coins.dat) */
|
|
||||||
class CCoinsDB : public CDB
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CCoinsDB(const char* pszMode="r+") : CDB("coins.dat", pszMode) { }
|
|
||||||
private:
|
|
||||||
CCoinsDB(const CCoinsDB&);
|
|
||||||
void operator=(const CCoinsDB&);
|
|
||||||
public:
|
|
||||||
bool ReadCoins(uint256 hash, CCoins &coins);
|
|
||||||
bool WriteCoins(uint256 hash, const CCoins& coins);
|
|
||||||
bool HaveCoins(uint256 hash);
|
|
||||||
bool ReadHashBestChain(uint256& hashBestChain);
|
|
||||||
bool WriteHashBestChain(uint256 hashBestChain);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** CCoinsView backed by a CCoinsDB */
|
|
||||||
class CCoinsViewDB : public CCoinsView
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
CCoinsDB db;
|
|
||||||
public:
|
|
||||||
CCoinsViewDB();
|
|
||||||
bool GetCoins(uint256 txid, CCoins &coins);
|
|
||||||
bool SetCoins(uint256 txid, const CCoins &coins);
|
|
||||||
bool HaveCoins(uint256 txid);
|
|
||||||
CBlockIndex *GetBestBlock();
|
|
||||||
bool SetBestBlock(CBlockIndex *pindex);
|
|
||||||
bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** Access to the block database (blktree.dat) */
|
|
||||||
class CBlockTreeDB : public CDB
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CBlockTreeDB(const char* pszMode="r+") : CDB("blktree.dat", pszMode) { }
|
|
||||||
private:
|
|
||||||
CBlockTreeDB(const CBlockTreeDB&);
|
|
||||||
void operator=(const CBlockTreeDB&);
|
|
||||||
public:
|
|
||||||
bool WriteBlockIndex(const CDiskBlockIndex& blockindex);
|
|
||||||
bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork);
|
|
||||||
bool WriteBestInvalidWork(CBigNum bnBestInvalidWork);
|
|
||||||
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo);
|
|
||||||
bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo);
|
|
||||||
bool ReadLastBlockFile(int &nFile);
|
|
||||||
bool WriteLastBlockFile(int nFile);
|
|
||||||
bool LoadBlockIndexGuts();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
bool LoadBlockIndexDB();
|
|
||||||
|
|
||||||
|
|
||||||
/** Access to the (IP) address database (peers.dat) */
|
/** Access to the (IP) address database (peers.dat) */
|
||||||
class CAddrDB
|
class CAddrDB
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Copyright (c) 2009-2012 The Bitcoin developers
|
// Copyright (c) 2009-2012 The Bitcoin developers
|
||||||
// Distributed under the MIT/X11 software license, see the accompanying
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
#include "db.h"
|
#include "txdb.h"
|
||||||
#include "walletdb.h"
|
#include "walletdb.h"
|
||||||
#include "bitcoinrpc.h"
|
#include "bitcoinrpc.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
112
src/main.cpp
112
src/main.cpp
@ -6,6 +6,7 @@
|
|||||||
#include "alert.h"
|
#include "alert.h"
|
||||||
#include "checkpoints.h"
|
#include "checkpoints.h"
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
|
#include "txdb.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "ui_interface.h"
|
#include "ui_interface.h"
|
||||||
@ -1719,6 +1720,7 @@ bool SetBestChain(CBlockIndex* pindexNew)
|
|||||||
bool fIsInitialDownload = IsInitialBlockDownload();
|
bool fIsInitialDownload = IsInitialBlockDownload();
|
||||||
if (!fIsInitialDownload || view.GetCacheSize()>5000) {
|
if (!fIsInitialDownload || view.GetCacheSize()>5000) {
|
||||||
FlushBlockFile();
|
FlushBlockFile();
|
||||||
|
pblocktree->Sync();
|
||||||
if (!view.Flush())
|
if (!view.Flush())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2203,6 +2205,116 @@ FILE *OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
|
|||||||
return OpenDiskFile(pos, "rev", fReadOnly);
|
return OpenDiskFile(pos, "rev", fReadOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CBlockIndex * 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 static 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 LoadBlockIndex(bool fAllowNew)
|
bool LoadBlockIndex(bool fAllowNew)
|
||||||
{
|
{
|
||||||
if (fTestNet)
|
if (fTestNet)
|
||||||
|
@ -119,6 +119,7 @@ std::string GetWarnings(std::string strFor);
|
|||||||
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false);
|
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false);
|
||||||
bool SetBestChain(CBlockIndex* pindexNew);
|
bool SetBestChain(CBlockIndex* pindexNew);
|
||||||
bool ConnectBestBlock();
|
bool ConnectBestBlock();
|
||||||
|
CBlockIndex * InsertBlockIndex(uint256 hash);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,10 +91,12 @@ ifdef USE_LEVELDB
|
|||||||
LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a
|
LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a
|
||||||
DEFS += -I"$(CURDIR)/leveldb/include" -DUSE_LEVELDB
|
DEFS += -I"$(CURDIR)/leveldb/include" -DUSE_LEVELDB
|
||||||
DEFS += -I"$(CURDIR)/leveldb/helpers"
|
DEFS += -I"$(CURDIR)/leveldb/helpers"
|
||||||
OBJS += obj/leveldb.o
|
OBJS += obj/leveldb.o obj/txdb-leveldb.o
|
||||||
leveldb/libleveldb.a:
|
leveldb/libleveldb.a:
|
||||||
@echo "Building LevelDB ..."; cd leveldb; TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$(INCLUDEPATHS)" LDFLAGS="-L$(LIBPATHS)" make libleveldb.a libmemenv.a; cd ..
|
@echo "Building LevelDB ..."; cd leveldb; TARGET_OS=OS_WINDOWS_CROSSCOMPILE CXXFLAGS="-I$(INCLUDEPATHS)" LDFLAGS="-L$(LIBPATHS)" make libleveldb.a libmemenv.a; cd ..
|
||||||
obj/leveldb.o: leveldb/libleveldb.a
|
obj/leveldb.o: leveldb/libleveldb.a
|
||||||
|
else
|
||||||
|
OBJS += obj/txdb-bdb.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
obj/build.h: FORCE
|
obj/build.h: FORCE
|
||||||
|
@ -94,10 +94,12 @@ ifdef USE_LEVELDB
|
|||||||
LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a
|
LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a
|
||||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB
|
DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB
|
||||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers)
|
DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers)
|
||||||
OBJS += obj/leveldb.o
|
OBJS += obj/leveldb.o obj/txdb-leveldb.o
|
||||||
leveldb/libleveldb.a:
|
leveldb/libleveldb.a:
|
||||||
cd leveldb; make libleveldb.a libmemenv.a; cd ..
|
cd leveldb; make libleveldb.a libmemenv.a; cd ..
|
||||||
obj/leveldb.o: leveldb/libleveldb.lib
|
obj/leveldb.o: leveldb/libleveldb.lib
|
||||||
|
else
|
||||||
|
OBJS += obj/txdb-bdb.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
obj/%.o: %.cpp $(HEADERS)
|
obj/%.o: %.cpp $(HEADERS)
|
||||||
|
@ -127,10 +127,12 @@ ifdef USE_LEVELDB
|
|||||||
LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a
|
LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a
|
||||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB
|
DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB
|
||||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers)
|
DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers)
|
||||||
OBJS += obj/leveldb.o
|
OBJS += obj/leveldb.o obj/txdb-leveldb.o
|
||||||
leveldb/libleveldb.a:
|
leveldb/libleveldb.a:
|
||||||
@echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..
|
@echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..
|
||||||
obj/leveldb.o: leveldb/libleveldb.a
|
obj/leveldb.o: leveldb/libleveldb.a
|
||||||
|
else
|
||||||
|
OBJS += obj/txdb-bdb.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# auto-generated dependencies:
|
# auto-generated dependencies:
|
||||||
|
@ -143,10 +143,12 @@ ifdef USE_LEVELDB
|
|||||||
LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a
|
LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a
|
||||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB
|
DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB
|
||||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers)
|
DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers)
|
||||||
OBJS += obj/leveldb.o
|
OBJS += obj/leveldb.o obj/txdb-leveldb.o
|
||||||
leveldb/libleveldb.a:
|
leveldb/libleveldb.a:
|
||||||
@echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..;
|
@echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..;
|
||||||
obj/leveldb.o: leveldb/libleveldb.a
|
obj/leveldb.o: leveldb/libleveldb.a
|
||||||
|
else
|
||||||
|
OBJS += obj/txdb-bdb.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# auto-generated dependencies:
|
# auto-generated dependencies:
|
||||||
|
171
src/txdb-bdb.cpp
Normal file
171
src/txdb-bdb.cpp
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2012 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "txdb-bdb.h"
|
||||||
|
|
||||||
|
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 CCoinsDB::ReadHashBestChain(uint256& hashBestChain)
|
||||||
|
{
|
||||||
|
return Read('B', hashBestChain);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCoinsDB::WriteHashBestChain(uint256 hashBestChain)
|
||||||
|
{
|
||||||
|
return Write('B', hashBestChain);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
|
||||||
|
{
|
||||||
|
return Write(make_pair('b', blockindex.GetBlockHash()), blockindex);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
61
src/txdb-bdb.h
Normal file
61
src/txdb-bdb.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2012 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
#ifndef BITCOIN_TXDB_BDB_H
|
||||||
|
#define BITCOIN_TXDB_BDB_H
|
||||||
|
|
||||||
|
#include "db.h"
|
||||||
|
|
||||||
|
/** Access to the transaction database (coins.dat) */
|
||||||
|
class CCoinsDB : public CDB
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CCoinsDB(const char* pszMode="r+") : CDB("coins.dat", pszMode) { }
|
||||||
|
private:
|
||||||
|
CCoinsDB(const CCoinsDB&);
|
||||||
|
void operator=(const CCoinsDB&);
|
||||||
|
public:
|
||||||
|
bool ReadCoins(uint256 hash, CCoins &coins);
|
||||||
|
bool WriteCoins(uint256 hash, const CCoins& coins);
|
||||||
|
bool HaveCoins(uint256 hash);
|
||||||
|
bool ReadHashBestChain(uint256& hashBestChain);
|
||||||
|
bool WriteHashBestChain(uint256 hashBestChain);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** CCoinsView backed by a CCoinsDB */
|
||||||
|
class CCoinsViewDB : public CCoinsView
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
CCoinsDB db;
|
||||||
|
public:
|
||||||
|
CCoinsViewDB();
|
||||||
|
|
||||||
|
bool GetCoins(uint256 txid, CCoins &coins);
|
||||||
|
bool SetCoins(uint256 txid, const CCoins &coins);
|
||||||
|
bool HaveCoins(uint256 txid);
|
||||||
|
CBlockIndex *GetBestBlock();
|
||||||
|
bool SetBestBlock(CBlockIndex *pindex);
|
||||||
|
bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Access to the block database (blktree.dat) */
|
||||||
|
class CBlockTreeDB : public CDB
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CBlockTreeDB(const char* pszMode="r+") : CDB("blktree.dat", pszMode) { }
|
||||||
|
private:
|
||||||
|
CBlockTreeDB(const CBlockTreeDB&);
|
||||||
|
void operator=(const CBlockTreeDB&);
|
||||||
|
public:
|
||||||
|
bool WriteBlockIndex(const CDiskBlockIndex& blockindex);
|
||||||
|
bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork);
|
||||||
|
bool WriteBestInvalidWork(CBigNum bnBestInvalidWork);
|
||||||
|
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo);
|
||||||
|
bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo);
|
||||||
|
bool ReadLastBlockFile(int &nFile);
|
||||||
|
bool WriteLastBlockFile(int nFile);
|
||||||
|
bool LoadBlockIndexGuts();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BITCOIN_TXDB_BDB_H
|
151
src/txdb-leveldb.cpp
Normal file
151
src/txdb-leveldb.cpp
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2012 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "txdb-leveldb.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) {
|
||||||
|
if (coins.IsPruned())
|
||||||
|
batch.Erase(make_pair('c', hash));
|
||||||
|
else
|
||||||
|
batch.Write(make_pair('c', hash), coins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) {
|
||||||
|
batch.Write('B', hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
CCoinsViewDB::CCoinsViewDB() : db(GetDataDir() / "coins") {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCoinsViewDB::GetCoins(uint256 txid, CCoins &coins) {
|
||||||
|
return db.Read(make_pair('c', txid), coins);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCoinsViewDB::SetCoins(uint256 txid, const CCoins &coins) {
|
||||||
|
CLevelDBBatch batch;
|
||||||
|
BatchWriteCoins(batch, txid, coins);
|
||||||
|
return db.WriteBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCoinsViewDB::HaveCoins(uint256 txid) {
|
||||||
|
return db.Exists(make_pair('c', txid));
|
||||||
|
}
|
||||||
|
|
||||||
|
CBlockIndex *CCoinsViewDB::GetBestBlock() {
|
||||||
|
uint256 hashBestChain;
|
||||||
|
if (!db.Read('B', 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) {
|
||||||
|
CLevelDBBatch batch;
|
||||||
|
BatchWriteHashBestChain(batch, pindex->GetBlockHash());
|
||||||
|
return db.WriteBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) {
|
||||||
|
printf("Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size());
|
||||||
|
|
||||||
|
CLevelDBBatch batch;
|
||||||
|
for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++)
|
||||||
|
BatchWriteCoins(batch, it->first, it->second);
|
||||||
|
BatchWriteHashBestChain(batch, pindex->GetBlockHash());
|
||||||
|
|
||||||
|
return db.WriteBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
|
||||||
|
{
|
||||||
|
return Write(make_pair('b', blockindex.GetBlockHash()), blockindex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)
|
||||||
|
{
|
||||||
|
return Read('I', bnBestInvalidWork);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::WriteBestInvalidWork(const 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::LoadBlockIndexGuts()
|
||||||
|
{
|
||||||
|
leveldb::Iterator *pcursor = NewIterator();
|
||||||
|
|
||||||
|
CDataStream ssKeySet(SER_DISK, CLIENT_VERSION);
|
||||||
|
ssKeySet << make_pair('b', uint256(0));
|
||||||
|
pcursor->Seek(ssKeySet.str());
|
||||||
|
|
||||||
|
// Load mapBlockIndex
|
||||||
|
while (pcursor->Valid()) {
|
||||||
|
try {
|
||||||
|
leveldb::Slice slKey = pcursor->key();
|
||||||
|
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
|
||||||
|
char chType;
|
||||||
|
ssKey >> chType;
|
||||||
|
if (chType == 'b' && !fRequestShutdown) {
|
||||||
|
leveldb::Slice slValue = pcursor->value();
|
||||||
|
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
|
||||||
|
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());
|
||||||
|
|
||||||
|
pcursor->Next();
|
||||||
|
} else {
|
||||||
|
break; // if shutdown requested or finished loading block index
|
||||||
|
}
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
return error("%s() : deserialize error", __PRETTY_FUNCTION__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete pcursor;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
46
src/txdb-leveldb.h
Normal file
46
src/txdb-leveldb.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2012 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
#ifndef BITCOIN_TXDB_LEVELDB_H
|
||||||
|
#define BITCOIN_TXDB_LEVELDB_H
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
#include "leveldb.h"
|
||||||
|
|
||||||
|
/** CCoinsView backed by the LevelDB coin database (coins/) */
|
||||||
|
class CCoinsViewDB : public CCoinsView
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
CLevelDB db;
|
||||||
|
public:
|
||||||
|
CCoinsViewDB();
|
||||||
|
|
||||||
|
bool GetCoins(uint256 txid, CCoins &coins);
|
||||||
|
bool SetCoins(uint256 txid, const CCoins &coins);
|
||||||
|
bool HaveCoins(uint256 txid);
|
||||||
|
CBlockIndex *GetBestBlock();
|
||||||
|
bool SetBestBlock(CBlockIndex *pindex);
|
||||||
|
bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Access to the block database (blktree/) */
|
||||||
|
class CBlockTreeDB : public CLevelDB
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CBlockTreeDB(const char* pszMode="r+") : CLevelDB(GetDataDir() / "blktree") { }
|
||||||
|
private:
|
||||||
|
CBlockTreeDB(const CBlockTreeDB&);
|
||||||
|
void operator=(const CBlockTreeDB&);
|
||||||
|
public:
|
||||||
|
bool WriteBlockIndex(const CDiskBlockIndex& blockindex);
|
||||||
|
bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork);
|
||||||
|
bool WriteBestInvalidWork(const CBigNum& bnBestInvalidWork);
|
||||||
|
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo);
|
||||||
|
bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo);
|
||||||
|
bool ReadLastBlockFile(int &nFile);
|
||||||
|
bool WriteLastBlockFile(int nFile);
|
||||||
|
bool LoadBlockIndexGuts();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BITCOIN_TXDB_LEVELDB_H
|
14
src/txdb.h
Normal file
14
src/txdb.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2012 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
#ifndef BITCOIN_TXDB_H
|
||||||
|
#define BITCOIN_TXDB_H
|
||||||
|
|
||||||
|
#ifdef USE_LEVELDB
|
||||||
|
#include "txdb-leveldb.h"
|
||||||
|
#else
|
||||||
|
#include "txdb-bdb.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // BITCOIN_TXDB_H
|
Loading…
Reference in New Issue
Block a user