Replace CCoinsDB and CBlockTreeDB with LevelDB based implementations.
authoralex <alex@alex-VirtualBox.(none)>
Wed, 22 Jan 2014 23:51:50 +0000 (03:51 +0400)
committeralex <alex@alex-VirtualBox.(none)>
Wed, 22 Jan 2014 23:51:50 +0000 (03:51 +0400)
16 files changed:
novacoin-qt.pro
src/checkpoints.cpp
src/db.cpp
src/db.h
src/init.cpp
src/leveldb.cpp
src/leveldb.h
src/main.cpp
src/main.h
src/makefile.bsd
src/makefile.linux-mingw
src/makefile.mingw
src/makefile.osx
src/makefile.unix
src/txdb.cpp [new file with mode: 0644]
src/txdb.h [new file with mode: 0644]

index 08d8041..d21cfaa 100644 (file)
@@ -183,6 +183,8 @@ HEADERS += src/qt/bitcoingui.h \
     src/net.h \
     src/key.h \
     src/db.h \
+    src/txdb.h \
+    src/leveldb.h \
     src/walletdb.h \
     src/script.h \
     src/init.h \
@@ -258,6 +260,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \
     src/addrman.cpp \
     src/db.cpp \
     src/leveldb.cpp \
+    src/txdb.cpp \
     src/walletdb.cpp \
     src/qt/clientmodel.cpp \
     src/qt/guiutil.cpp \
index 6502ba2..e4b460f 100644 (file)
@@ -7,7 +7,7 @@
 
 #include "checkpoints.h"
 
-#include "db.h"
+#include "txdb.h"
 #include "main.h"
 #include "uint256.h"
 
@@ -135,14 +135,14 @@ namespace Checkpoints
 
     bool WriteSyncCheckpoint(const uint256& hashCheckpoint)
     {
-        pblocktree->TxnBegin();
-        if (!pblocktree->WriteSyncCheckpoint(hashCheckpoint))
         {
-            pblocktree->TxnAbort();
-            return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
+            LOCK(Checkpoints::cs_hashSyncCheckpoint);
+
+            if (!pblocktree->WriteSyncCheckpoint(hashCheckpoint))
+            {
+                return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
+            }
         }
-        if (!pblocktree->TxnCommit())
-            return error("WriteSyncCheckpoint(): failed to commit to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
 
         Checkpoints::hashSyncCheckpoint = hashCheckpoint;
         return true;
index 831cb9e..275bade 100644 (file)
@@ -484,341 +484,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::ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust)
-{
-    return Read('I', bnBestInvalidTrust);
-}
-
-bool CBlockTreeDB::WriteBestInvalidTrust(CBigNum bnBestInvalidTrust)
-{
-    return Write('I', bnBestInvalidTrust);
-}
-
-bool CBlockTreeDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) {
-    return Write(make_pair('f', nFile), info);
-}
-
-bool CBlockTreeDB::ReadSyncCheckpoint(uint256& hashCheckpoint)
-{
-    return Read('H', hashCheckpoint);
-}
-
-bool CBlockTreeDB::WriteSyncCheckpoint(uint256 hashCheckpoint)
-{
-    return Write('H', hashCheckpoint);
-}
-
-bool CBlockTreeDB::ReadCheckpointPubKey(string& strPubKey)
-{
-    return Read('K', strPubKey);
-}
-
-bool CBlockTreeDB::WriteCheckpointPubKey(const string& strPubKey)
-{
-    return Write('K', strPubKey);
-}
-
-
-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 nChainTrust
-    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->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + pindex->GetBlockTrust();
-        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);
-
-        // Calculate stake modifier checksum
-        pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex);
-        if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum))
-            return error("CTxDB::LoadBlockIndex() : Failed stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindex->nHeight, pindex->nStakeModifier);
-    }
-
-    // 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;
-        return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded");
-    }
-    hashBestChain = pindexBest->GetBlockHash();
-    nBestHeight = pindexBest->nHeight;
-    nBestChainTrust = pindexBest->nChainTrust;
-
-    // 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 sync-checkpoint
-    if (!pblocktree->ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint))
-        return error("CTxDB::LoadBlockIndex() : hashSyncCheckpoint not loaded");
-    printf("LoadBlockIndex(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str());
-
-    // Load bnBestInvalidTrust, OK if it doesn't exist
-    CBigNum bnBestInvalidTrust;
-    pblocktree->ReadBestInvalidTrust(bnBestInvalidTrust);
-    nBestInvalidTrust = bnBestInvalidTrust.getuint256();
-
-    // 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;
-    while (true)
-    {
-        // 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;
-
-            uint256 blockHash = diskindex.GetBlockHash();
-
-            // Construct block index object
-            CBlockIndex* pindexNew = InsertBlockIndex(blockHash);
-            pindexNew->pprev          = InsertBlockIndex(diskindex.hashPrev);
-            pindexNew->nHeight        = diskindex.nHeight;
-            pindexNew->nFile          = diskindex.nFile;
-            pindexNew->nDataPos       = diskindex.nDataPos;
-            pindexNew->nUndoPos       = diskindex.nUndoPos;
-            pindexNew->nMint          = diskindex.nMint;
-            pindexNew->nMoneySupply   = diskindex.nMoneySupply;
-            pindexNew->nFlags         = diskindex.nFlags;
-            pindexNew->nStakeModifier = diskindex.nStakeModifier;
-            pindexNew->prevoutStake   = diskindex.prevoutStake;
-            pindexNew->nStakeTime     = diskindex.nStakeTime;
-            pindexNew->hashProofOfStake = diskindex.hashProofOfStake;
-            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 && blockHash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
-                pindexGenesisBlock = pindexNew;
-
-            if (!pindexNew->CheckIndex())
-                return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString().c_str());
-
-            // Build setStakeSeen
-            if (pindexNew->IsProofOfStake())
-                setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
-        }
-        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;
-}
-
-
-
-
-
 //
 // CAddrDB
 //
index 5b805df..5e454bb 100644 (file)
--- a/src/db.h
+++ b/src/db.h
@@ -309,72 +309,6 @@ public:
     bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL);
 };
 
-
-
-
-
-
-
-/** 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 (chain.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 ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust);
-    bool WriteBestInvalidTrust(CBigNum bnBestInvalidTrust);
-    bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo);
-    bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo);
-    bool ReadLastBlockFile(int &nFile);
-    bool WriteLastBlockFile(int nFile);
-    bool ReadSyncCheckpoint(uint256& hashCheckpoint);
-    bool WriteSyncCheckpoint(uint256 hashCheckpoint);
-    bool ReadCheckpointPubKey(std::string& strPubKey);
-    bool WriteCheckpointPubKey(const std::string& strPubKey);
-    bool LoadBlockIndexGuts();
-};
-
-
-bool LoadBlockIndexDB();
-
-
 /** Access to the (IP) address database (peers.dat) */
 class CAddrDB
 {
index d9ee01a..cffc58c 100644 (file)
@@ -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"
@@ -707,7 +707,7 @@ bool AppInit2()
     uiInterface.InitMessage(_("Loading block index..."));
     printf("Loading block index...\n");
     nStart = GetTimeMillis();
-    pblocktree = new CBlockTreeDB("cr+");
+    pblocktree = new CBlockTreeDB();
     pcoinsdbview = new CCoinsViewDB();
     pcoinsTip = new CCoinsViewCache(*pcoinsdbview);
 
index 29e5e6a..e8a0fbe 100644 (file)
@@ -8,6 +8,7 @@
 #include <leveldb/env.h>
 #include <leveldb/cache.h>
 #include <leveldb/filter_policy.h>
+#include <memenv/memenv.h>
 
 #include <boost/filesystem.hpp>
 
@@ -20,7 +21,7 @@ static leveldb::Options GetOptions() {
     return options;
 }
 
-CLevelDB::CLevelDB(const boost::filesystem::path &path) {
+CLevelDB::CLevelDB(const boost::filesystem::path &path, bool fMemory) {
     penv = NULL;
     readoptions.verify_checksums = true;
     iteroptions.verify_checksums = true;
@@ -28,8 +29,13 @@ CLevelDB::CLevelDB(const boost::filesystem::path &path) {
     syncoptions.sync = true;
     options = GetOptions();
     options.create_if_missing = true;
-    boost::filesystem::create_directory(path);
-    printf("Opening LevelDB in %s\n", path.string().c_str());
+    if (fMemory) {
+        penv = leveldb::NewMemEnv(leveldb::Env::Default());
+        options.env = penv;
+    } else {
+        boost::filesystem::create_directory(path);
+        printf("Opening LevelDB in %s\n", path.string().c_str());
+    }
     leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb);
     if (!status.ok())
         throw std::runtime_error(strprintf("CLevelDB(): error opening database environment %s", status.ToString().c_str()));
index 28484da..ee9079c 100644 (file)
@@ -69,7 +69,7 @@ private:
     leveldb::DB *pdb;
 
 public:
-    CLevelDB(const boost::filesystem::path &path);
+    CLevelDB(const boost::filesystem::path &path, bool fMemory = false);
     ~CLevelDB();
 
     template<typename K, typename V> bool Read(const K& key, V& value) {
index 46977b2..11f2f10 100644 (file)
@@ -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"
@@ -196,6 +197,7 @@ bool CCoinsView::HaveCoins(uint256 txid) { return false; }
 CBlockIndex *CCoinsView::GetBestBlock() { return NULL; }
 bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; }
 bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return false; }
+bool CCoinsView::GetStats(CCoinsStats &stats) { return false; }
 
 CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { }
 bool CCoinsViewBacked::GetCoins(uint256 txid, CCoins &coins) { return base->GetCoins(txid, coins); }
@@ -204,6 +206,7 @@ bool CCoinsViewBacked::HaveCoins(uint256 txid) { return base->HaveCoins(txid); }
 CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); }
 bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); }
 void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
+bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); }
 
 bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); }
 
@@ -2792,6 +2795,130 @@ FILE *OpenUndoFile(const CDiskBlockPos &pos, bool 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("InsertBlockIndex() : 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 nChainTrust
+    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->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + pindex->GetBlockTrust();
+        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);
+
+        // Calculate stake modifier checksum
+        pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex);
+        if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum))
+            return error("LoadBlockIndexDB() : Failed stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindex->nHeight, pindex->nStakeModifier);
+    }
+
+    // Load block file info
+    pblocktree->ReadLastBlockFile(nLastBlockFile);
+    printf("LoadBlockIndexDB(): last block file = %i\n", nLastBlockFile);
+    if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+        printf("LoadBlockIndexDB(): 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;
+        return error("LoadBlockIndexDB() : hashBestChain not loaded");
+    }
+    hashBestChain = pindexBest->GetBlockHash();
+    nBestHeight = pindexBest->nHeight;
+    nBestChainTrust = pindexBest->nChainTrust;
+
+    // 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("LoadBlockIndexDB(): 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 sync-checkpoint
+    if (!pblocktree->ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint))
+        return error("LoadBlockIndexDB() : hashSyncCheckpoint not loaded");
+    printf("LoadBlockIndexDB(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str());
+
+    // Load bnBestInvalidTrust, OK if it doesn't exist
+    CBigNum bnBestInvalidTrust;
+    pblocktree->ReadBestInvalidTrust(bnBestInvalidTrust);
+    nBestInvalidTrust = bnBestInvalidTrust.getuint256();
+
+    // 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("LoadBlockIndexDB() : block.ReadFromDisk failed");
+        // check level 1: verify block validity
+        if (nCheckLevel>0 && !block.CheckBlock())
+        {
+            printf("LoadBlockIndexDB() : *** 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("LoadBlockIndexDB(): chain database corrupted");
+    }
+
+    return true;
+}
+
+
 bool LoadBlockIndex(bool fAllowNew)
 {
     CBigNum bnTrustedModulus;
@@ -2893,12 +3020,13 @@ bool LoadBlockIndex(bool fAllowNew)
     // if checkpoint master key changed must reset sync-checkpoint
     if (!pblocktree->ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
     {
-        // write checkpoint master key to db
-        pblocktree->TxnBegin();
-        if (!pblocktree->WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
-            return error("LoadBlockIndex() : failed to write new checkpoint master key to db");
-        if (!pblocktree->TxnCommit())
-            return error("LoadBlockIndex() : failed to commit new checkpoint master key to db");
+        {
+            LOCK(Checkpoints::cs_hashSyncCheckpoint);
+            // write checkpoint master key to db
+            if (!pblocktree->WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
+                return error("LoadBlockIndex() : failed to write new checkpoint master key to db");
+        }
+
         if ((!fTestNet) && !Checkpoints::ResetSyncCheckpoint())
             return error("LoadBlockIndex() : failed to reset sync-checkpoint");
     }
index 12ca01e..853ea0b 100644 (file)
@@ -139,6 +139,7 @@ std::string GetWarnings(std::string strFor);
 bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow);
 bool SetBestChain(CBlockIndex* pindexNew);
 bool ConnectBestBlock();
+CBlockIndex * InsertBlockIndex(uint256 hash);
 uint256 WantedByOrphan(const CBlock* pblockOrphan);
 const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake);
 void StakeMiner(CWallet *pwallet);
@@ -2059,6 +2060,16 @@ public:
 
 extern CTxMemPool mempool;
 
+struct CCoinsStats
+{
+    int nHeight;
+    uint64 nTransactions;
+    uint64 nTransactionOutputs;
+    uint64 nSerializedSize;
+
+    CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0) {}
+};
+
 /** Abstract view on the open txout dataset. */
 class CCoinsView
 {
@@ -2079,6 +2090,7 @@ public:
     // Modify the currently active block index
     virtual bool SetBestBlock(CBlockIndex *pindex);
     virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
+    virtual bool GetStats(CCoinsStats &stats);
 };
 
 /** CCoinsView backed by another CCoinsView */
@@ -2096,6 +2108,7 @@ public:
     bool SetBestBlock(CBlockIndex *pindex);
     void SetBackend(CCoinsView &viewIn);
     bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
+    bool GetStats(CCoinsStats &stats);
 };
 
 /** CCoinsView that adds a memory cache for transactions to another CCoinsView */
index 4baa2f1..1cfe92d 100644 (file)
@@ -107,6 +107,7 @@ OBJS= \
     obj/key.o \
     obj/db.o \
     obj/leveldb.o \
+    obj/txdb.o \
     obj/init.o \
     obj/irc.o \
     obj/keystore.o \
index 836e639..9b20273 100644 (file)
@@ -70,6 +70,7 @@ OBJS= \
     obj/key.o \
     obj/db.o \
     obj/leveldb.o \
+    obj/txdb.o \
     obj/init.o \
     obj/irc.o \
     obj/keystore.o \
index dc438d9..b01bb60 100644 (file)
@@ -61,6 +61,7 @@ OBJS= \
     obj/key.o \
     obj/db.o \
     obj/leveldb.o \
+    obj/txdb.o \
     obj/init.o \
     obj/irc.o \
     obj/keystore.o \
index 3ae4b11..47b64fc 100644 (file)
@@ -72,6 +72,7 @@ OBJS= \
     obj/key.o \
     obj/db.o \
     obj/leveldb.o \
+    obj/txdb.o \
     obj/init.o \
     obj/irc.o \
     obj/keystore.o \
index 184841e..64ecc41 100644 (file)
@@ -113,6 +113,7 @@ OBJS= \
     obj/key.o \
     obj/db.o \
     obj/leveldb.o \
+    obj/txdb.o \
     obj/init.o \
     obj/irc.o \
     obj/keystore.o \
diff --git a/src/txdb.cpp b/src/txdb.cpp
new file mode 100644 (file)
index 0000000..ff40b6c
--- /dev/null
@@ -0,0 +1,222 @@
+// 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.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(bool fMemory) : db(GetDataDir() / "coins", fMemory) {
+}
+
+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);
+}
+
+CBlockTreeDB::CBlockTreeDB(bool fMemory) : CLevelDB(GetDataDir() / "blktree", fMemory) {
+}
+
+bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
+{
+    return Write(make_pair('b', blockindex.GetBlockHash()), blockindex);
+}
+
+bool CBlockTreeDB::ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust)
+{
+    return Read('I', bnBestInvalidTrust);
+}
+
+bool CBlockTreeDB::WriteBestInvalidTrust(CBigNum bnBestInvalidTrust)
+{
+    return Write('I', bnBestInvalidTrust);
+}
+
+bool CBlockTreeDB::ReadSyncCheckpoint(uint256& hashCheckpoint)
+{
+    return Read('H', hashCheckpoint);
+}
+
+bool CBlockTreeDB::WriteSyncCheckpoint(uint256 hashCheckpoint)
+{
+    return Write('H', hashCheckpoint);
+}
+
+bool CBlockTreeDB::ReadCheckpointPubKey(string& strPubKey)
+{
+    return Read('K', strPubKey);
+}
+
+bool CBlockTreeDB::WriteCheckpointPubKey(const string& strPubKey)
+{
+    return Write('K', strPubKey);
+}
+
+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 CCoinsViewDB::GetStats(CCoinsStats &stats) {
+    leveldb::Iterator *pcursor = db.NewIterator();
+    pcursor->SeekToFirst();
+
+    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 == 'c' && !fRequestShutdown) {
+                leveldb::Slice slValue = pcursor->value();
+                CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
+                CCoins coins;
+                ssValue >> coins;
+                uint256 txhash;
+                ssKey >> txhash;
+
+                stats.nTransactions++;
+                BOOST_FOREACH(const CTxOut &out, coins.vout) {
+                    if (!out.IsNull())
+                        stats.nTransactionOutputs++;
+                }
+                stats.nSerializedSize += 32 + slValue.size();
+            }
+            pcursor->Next();
+        } catch (std::exception &e) {
+            return error("%s() : deserialize error", __PRETTY_FUNCTION__);
+        }
+    }
+    delete pcursor;
+    stats.nHeight = GetBestBlock()->nHeight;
+    return true;
+}
+
+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;
+
+                uint256 blockHash = diskindex.GetBlockHash();
+
+                // Construct block index object
+                CBlockIndex* pindexNew = InsertBlockIndex(blockHash);
+                pindexNew->pprev          = InsertBlockIndex(diskindex.hashPrev);
+                pindexNew->nHeight        = diskindex.nHeight;
+                pindexNew->nFile          = diskindex.nFile;
+                pindexNew->nDataPos       = diskindex.nDataPos;
+                pindexNew->nUndoPos       = diskindex.nUndoPos;
+                pindexNew->nMint          = diskindex.nMint;
+                pindexNew->nMoneySupply   = diskindex.nMoneySupply;
+                pindexNew->nFlags         = diskindex.nFlags;
+                pindexNew->nStakeModifier = diskindex.nStakeModifier;
+                pindexNew->prevoutStake   = diskindex.prevoutStake;
+                pindexNew->nStakeTime     = diskindex.nStakeTime;
+                pindexNew->hashProofOfStake = diskindex.hashProofOfStake;
+                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 && blockHash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
+                    pindexGenesisBlock = pindexNew;
+
+                if (!pindexNew->CheckIndex())
+                    return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString().c_str());
+
+                // Build setStakeSeen
+                if (pindexNew->IsProofOfStake())
+                    setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
+
+                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.h b/src/txdb.h
new file mode 100644 (file)
index 0000000..3f07f0e
--- /dev/null
@@ -0,0 +1,51 @@
+// 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 fMemory = false);
+
+    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);
+    bool GetStats(CCoinsStats &stats);
+};
+
+/** Access to the block database (blktree/) */
+class CBlockTreeDB : public CLevelDB
+{
+public:
+    CBlockTreeDB(bool fMemory = false);
+private:
+    CBlockTreeDB(const CBlockTreeDB&);
+    void operator=(const CBlockTreeDB&);
+public:
+    bool WriteBlockIndex(const CDiskBlockIndex& blockindex);
+    bool ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust);
+    bool WriteBestInvalidTrust(CBigNum bnBestInvalidTrust);
+    bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo);
+    bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo);
+    bool ReadLastBlockFile(int &nFile);
+    bool WriteLastBlockFile(int nFile);
+    bool ReadSyncCheckpoint(uint256& hashCheckpoint);
+    bool WriteSyncCheckpoint(uint256 hashCheckpoint);
+    bool ReadCheckpointPubKey(std::string& strPubKey);
+    bool WriteCheckpointPubKey(const std::string& strPubKey);
+    bool LoadBlockIndexGuts();
+};
+
+#endif // BITCOIN_TXDB_LEVELDB_H