Browse Source
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.0.8
Pieter Wuille
12 years ago
15 changed files with 574 additions and 340 deletions
@ -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; |
||||||
|
} |
@ -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
|
@ -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; |
||||||
|
} |
@ -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
|
@ -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