// 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" #include "hash.h" #include "chainparams.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(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { } bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) { return db.Read(make_pair('c', txid), coins); } bool CCoinsViewDB::SetCoins(const uint256 &txid, const CCoins &coins) { CLevelDBBatch batch; BatchWriteCoins(batch, txid, coins); return db.WriteBatch(batch); } bool CCoinsViewDB::HaveCoins(const 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); if (pindex) BatchWriteHashBestChain(batch, pindex->GetBlockHash()); return db.WriteBatch(batch); } CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDB(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { } 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::WriteReindexing(bool fReindexing) { if (fReindexing) return Write('R', '1'); else return Erase('R'); } bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { fReindexing = Exists('R'); return true; } bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { return Read('l', nFile); } bool CCoinsViewDB::GetStats(CCoinsStats &stats) { leveldb::Iterator *pcursor = db.NewIterator(); pcursor->SeekToFirst(); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); stats.hashBlock = GetBestBlock()->GetBlockHash(); ss << stats.hashBlock; int64 nTotalAmount = 0; while (pcursor->Valid()) { boost::this_thread::interruption_point(); 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') { 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; ss << txhash; ss << VARINT(coins.nVersion); ss << coins.userName; ss << coins.pubKey; ss << VARINT(coins.nHeight); stats.nTransactions++; stats.nSerializedSize += 32 + slValue.size(); ss << VARINT(0); } pcursor->Next(); } catch (std::exception &e) { return error("%s() : deserialize error", __PRETTY_FUNCTION__); } } delete pcursor; stats.nHeight = GetBestBlock()->nHeight; stats.hashSerialized = ss.GetHash(); stats.nTotalAmount = nTotalAmount; return true; } bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { return Read(make_pair('t', txid), pos); } bool CBlockTreeDB::WriteTxIndex(const std::vector >&vect) { CLevelDBBatch batch; for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) batch.Write(make_pair('t', it->first), it->second); return WriteBatch(batch); } bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { return Write(std::make_pair('F', name), fValue ? '1' : '0'); } bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { char ch; if (!Read(std::make_pair('F', name), ch)) return false; fValue = ch == '1'; 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()) { boost::this_thread::interruption_point(); 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') { 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() == Params().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; }