From 2d8a48292b0da96cda8d7b45a24a22adfb4667b2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 3 Sep 2012 21:14:03 +0200 Subject: [PATCH] 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. --- bitcoin-qt.pro | 6 +- src/db.cpp | 279 --------------------------------------- src/db.h | 55 -------- src/init.cpp | 2 +- src/main.cpp | 112 ++++++++++++++++ src/main.h | 1 + src/makefile.linux-mingw | 4 +- src/makefile.mingw | 4 +- src/makefile.osx | 4 +- src/makefile.unix | 4 +- src/txdb-bdb.cpp | 171 ++++++++++++++++++++++++ src/txdb-bdb.h | 61 +++++++++ src/txdb-leveldb.cpp | 151 +++++++++++++++++++++ src/txdb-leveldb.h | 46 +++++++ src/txdb.h | 14 ++ 15 files changed, 574 insertions(+), 340 deletions(-) create mode 100644 src/txdb-bdb.cpp create mode 100644 src/txdb-bdb.h create mode 100644 src/txdb-leveldb.cpp create mode 100644 src/txdb-leveldb.h create mode 100644 src/txdb.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index c92971661..d0f535347 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -92,12 +92,15 @@ contains(BITCOIN_NEED_QT_PLUGINS, 1) { contains(USE_LEVELDB, -) { message(Building without LevelDB) + SOURCES += src/txdb-bdb.cpp + HEADERS += src/txdb-bdb.h } else { message(Building with LevelDB) DEFINES += USE_LEVELDB INCLUDEPATH += src/leveldb/include src/leveldb/helpers 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 { genleveldb.commands = cd $$PWD/src/leveldb ; $(MAKE) libleveldb.a libmemenv.a } else { @@ -153,6 +156,7 @@ HEADERS += src/qt/bitcoingui.h \ src/net.h \ src/key.h \ src/db.h \ + src/txdb.h \ src/walletdb.h \ src/script.h \ src/init.h \ diff --git a/src/db.cpp b/src/db.cpp index 5fe7e0585..8738a0af8 100644 --- a/src/db.cpp +++ b/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::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 &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::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::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 > 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; -} diff --git a/src/db.h b/src/db.h index d0696f3d8..9a5f1ca9e 100644 --- a/src/db.h +++ b/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 &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) */ class CAddrDB diff --git a/src/init.cpp b/src/init.cpp index 07e5d13d6..30f0d0049 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2,7 +2,7 @@ // 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 "db.h" +#include "txdb.h" #include "walletdb.h" #include "bitcoinrpc.h" #include "net.h" diff --git a/src/main.cpp b/src/main.cpp index 44e690b95..94f1a9315 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include "alert.h" #include "checkpoints.h" #include "db.h" +#include "txdb.h" #include "net.h" #include "init.h" #include "ui_interface.h" @@ -1719,6 +1720,7 @@ bool SetBestChain(CBlockIndex* pindexNew) bool fIsInitialDownload = IsInitialBlockDownload(); if (!fIsInitialDownload || view.GetCacheSize()>5000) { FlushBlockFile(); + pblocktree->Sync(); if (!view.Flush()) return false; } @@ -2203,6 +2205,116 @@ FILE *OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) { return OpenDiskFile(pos, "rev", fReadOnly); } +CBlockIndex * InsertBlockIndex(uint256 hash) +{ + if (hash == 0) + return NULL; + + // Return existing + map::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 > 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) { if (fTestNet) diff --git a/src/main.h b/src/main.h index 2cab48c91..1126feb09 100644 --- a/src/main.h +++ b/src/main.h @@ -119,6 +119,7 @@ std::string GetWarnings(std::string strFor); bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); bool SetBestChain(CBlockIndex* pindexNew); bool ConnectBestBlock(); +CBlockIndex * InsertBlockIndex(uint256 hash); diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 9bad54cb6..eee325d62 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -91,10 +91,12 @@ ifdef USE_LEVELDB LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += -I"$(CURDIR)/leveldb/include" -DUSE_LEVELDB DEFS += -I"$(CURDIR)/leveldb/helpers" -OBJS += obj/leveldb.o +OBJS += obj/leveldb.o obj/txdb-leveldb.o 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 .. obj/leveldb.o: leveldb/libleveldb.a +else +OBJS += obj/txdb-bdb.o endif obj/build.h: FORCE diff --git a/src/makefile.mingw b/src/makefile.mingw index 3953af3b0..4d95d1c03 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -94,10 +94,12 @@ ifdef USE_LEVELDB LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) -OBJS += obj/leveldb.o +OBJS += obj/leveldb.o obj/txdb-leveldb.o leveldb/libleveldb.a: cd leveldb; make libleveldb.a libmemenv.a; cd .. obj/leveldb.o: leveldb/libleveldb.lib +else +OBJS += obj/txdb-bdb.o endif obj/%.o: %.cpp $(HEADERS) diff --git a/src/makefile.osx b/src/makefile.osx index 618236521..9a9296f41 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -127,10 +127,12 @@ ifdef USE_LEVELDB LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) -OBJS += obj/leveldb.o +OBJS += obj/leveldb.o obj/txdb-leveldb.o leveldb/libleveldb.a: @echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd .. obj/leveldb.o: leveldb/libleveldb.a +else +OBJS += obj/txdb-bdb.o endif # auto-generated dependencies: diff --git a/src/makefile.unix b/src/makefile.unix index 3fcefa1ea..95234bae2 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -143,10 +143,12 @@ ifdef USE_LEVELDB LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a DEFS += $(addprefix -I,$(CURDIR)/leveldb/include) -DUSE_LEVELDB DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers) -OBJS += obj/leveldb.o +OBJS += obj/leveldb.o obj/txdb-leveldb.o leveldb/libleveldb.a: @echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..; obj/leveldb.o: leveldb/libleveldb.a +else +OBJS += obj/txdb-bdb.o endif # auto-generated dependencies: diff --git a/src/txdb-bdb.cpp b/src/txdb-bdb.cpp new file mode 100644 index 000000000..5e4609664 --- /dev/null +++ b/src/txdb-bdb.cpp @@ -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::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 &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::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; +} diff --git a/src/txdb-bdb.h b/src/txdb-bdb.h new file mode 100644 index 000000000..d61e95bac --- /dev/null +++ b/src/txdb-bdb.h @@ -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 &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 diff --git a/src/txdb-leveldb.cpp b/src/txdb-leveldb.cpp new file mode 100644 index 000000000..83a71c97d --- /dev/null +++ b/src/txdb-leveldb.cpp @@ -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::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 &mapCoins, CBlockIndex *pindex) { + printf("Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); + + CLevelDBBatch batch; + for (std::map::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; +} diff --git a/src/txdb-leveldb.h b/src/txdb-leveldb.h new file mode 100644 index 000000000..e66ba8f8a --- /dev/null +++ b/src/txdb-leveldb.h @@ -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 &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 diff --git a/src/txdb.h b/src/txdb.h new file mode 100644 index 000000000..e21a1e677 --- /dev/null +++ b/src/txdb.h @@ -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