// 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) { 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::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); } 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::ReadModifierUpgradeTime(unsigned int& nUpgradeTime) { return Read('M', nUpgradeTime); } bool CBlockTreeDB::WriteModifierUpgradeTime(const unsigned int& nUpgradeTime) { return Write('M', nUpgradeTime); } 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; if (!coins.IsPruned()) { stats.nTransactions++; BOOST_FOREACH(const CTxOut &out, coins.vout) { if (!out.IsNull()) stats.nTransactionOutputs++; } } else { stats.nPrunedTransactions++; } 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; }