diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index e1b7a71b4..07789a03a 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -45,6 +45,7 @@ testScripts=( 'nodehandling.py' 'reindex.py' 'decodescript.py' + 'blockchain.py' 'disablewallet.py' 'zcjoinsplit.py' 'zcjoinsplitdoublespend.py' diff --git a/qa/rpc-tests/blockchain.py b/qa/rpc-tests/blockchain.py new file mode 100755 index 000000000..85b061317 --- /dev/null +++ b/qa/rpc-tests/blockchain.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python2 +# Copyright (c) 2014 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test RPC calls related to blockchain state. +# + +import decimal + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + initialize_chain, + assert_equal, + start_nodes, + connect_nodes_bi, +) + +class BlockchainTest(BitcoinTestFramework): + """ + Test blockchain-related RPC calls: + + - gettxoutsetinfo + + """ + + def setup_chain(self): + print("Initializing test directory " + self.options.tmpdir) + initialize_chain(self.options.tmpdir) + + def setup_network(self, split=False): + self.nodes = start_nodes(2, self.options.tmpdir) + connect_nodes_bi(self.nodes, 0, 1) + self.is_network_split = False + self.sync_all() + + def run_test(self): + node = self.nodes[0] + res = node.gettxoutsetinfo() + + assert_equal(res[u'total_amount'], decimal.Decimal('2181.25000000')) # 150*12.5 + 49*6.25 + assert_equal(res[u'transactions'], 200) + assert_equal(res[u'height'], 200) + assert_equal(res[u'txouts'], 349) # 150*2 + 49 + assert_equal(res[u'bytes_serialized'], 14951), # 32*199 + 48*90 + 49*60 + 27*49 + assert_equal(len(res[u'bestblock']), 64) + assert_equal(len(res[u'hash_serialized']), 64) + + +if __name__ == '__main__': + BlockchainTest().main() diff --git a/src/Makefile.am b/src/Makefile.am index 266d77151..f323e56e5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -152,7 +152,7 @@ BITCOIN_CORE_H = \ init.h \ key.h \ keystore.h \ - leveldbwrapper.h \ + dbwrapper.h \ limitedmap.h \ main.h \ memusage.h \ @@ -244,7 +244,7 @@ libbitcoin_server_a_SOURCES = \ httprpc.cpp \ httpserver.cpp \ init.cpp \ - leveldbwrapper.cpp \ + dbwrapper.cpp \ main.cpp \ merkleblock.cpp \ metrics.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 2ba7eb9ce..0d33c3434 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -60,6 +60,7 @@ BITCOIN_TESTS =\ test/getarg_tests.cpp \ test/hash_tests.cpp \ test/key_tests.cpp \ + test/dbwrapper_tests.cpp \ test/main_tests.cpp \ test/mempool_tests.cpp \ test/miner_tests.cpp \ diff --git a/src/leveldbwrapper.cpp b/src/dbwrapper.cpp similarity index 71% rename from src/leveldbwrapper.cpp rename to src/dbwrapper.cpp index b5d024abb..b34be5af7 100644 --- a/src/leveldbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "leveldbwrapper.h" +#include "dbwrapper.h" #include "util.h" @@ -12,20 +12,7 @@ #include #include #include - -void HandleError(const leveldb::Status& status) -{ - if (status.ok()) - return; - LogPrintf("%s\n", status.ToString()); - if (status.IsCorruption()) - throw leveldb_error("Database corrupted"); - if (status.IsIOError()) - throw leveldb_error("Database I/O error"); - if (status.IsNotFound()) - throw leveldb_error("Database entry missing"); - throw leveldb_error("Unknown database error"); -} +#include static leveldb::Options GetOptions(size_t nCacheSize) { @@ -43,7 +30,7 @@ static leveldb::Options GetOptions(size_t nCacheSize) return options; } -CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe) +CDBWrapper::CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe) { penv = NULL; readoptions.verify_checksums = true; @@ -59,17 +46,17 @@ CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path& path, size_t nCa if (fWipe) { LogPrintf("Wiping LevelDB in %s\n", path.string()); leveldb::Status result = leveldb::DestroyDB(path.string(), options); - HandleError(result); + dbwrapper_private::HandleError(result); } TryCreateDirectory(path); LogPrintf("Opening LevelDB in %s\n", path.string()); } leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); - HandleError(status); + dbwrapper_private::HandleError(status); LogPrintf("Opened LevelDB successfully\n"); } -CLevelDBWrapper::~CLevelDBWrapper() +CDBWrapper::~CDBWrapper() { delete pdb; pdb = NULL; @@ -81,9 +68,39 @@ CLevelDBWrapper::~CLevelDBWrapper() options.env = NULL; } -bool CLevelDBWrapper::WriteBatch(CLevelDBBatch& batch, bool fSync) +bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync) { leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); - HandleError(status); + dbwrapper_private::HandleError(status); return true; } + +bool CDBWrapper::IsEmpty() +{ + boost::scoped_ptr it(NewIterator()); + it->SeekToFirst(); + return !(it->Valid()); +} + +CDBIterator::~CDBIterator() { delete piter; } +bool CDBIterator::Valid() { return piter->Valid(); } +void CDBIterator::SeekToFirst() { piter->SeekToFirst(); } +void CDBIterator::Next() { piter->Next(); } + +namespace dbwrapper_private { + +void HandleError(const leveldb::Status& status) +{ + if (status.ok()) + return; + LogPrintf("%s\n", status.ToString()); + if (status.IsCorruption()) + throw dbwrapper_error("Database corrupted"); + if (status.IsIOError()) + throw dbwrapper_error("Database I/O error"); + if (status.IsNotFound()) + throw dbwrapper_error("Database entry missing"); + throw dbwrapper_error("Unknown database error"); +} + +}; diff --git a/src/leveldbwrapper.h b/src/dbwrapper.h similarity index 54% rename from src/leveldbwrapper.h rename to src/dbwrapper.h index 639f736a5..1f3b4fc16 100644 --- a/src/leveldbwrapper.h +++ b/src/dbwrapper.h @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_LEVELDBWRAPPER_H -#define BITCOIN_LEVELDBWRAPPER_H +#ifndef BITCOIN_DBWRAPPER_H +#define BITCOIN_DBWRAPPER_H #include "clientversion.h" #include "serialize.h" @@ -16,23 +16,39 @@ #include #include -class leveldb_error : public std::runtime_error +class dbwrapper_error : public std::runtime_error { public: - leveldb_error(const std::string& msg) : std::runtime_error(msg) {} + dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {} }; +class CDBWrapper; + +/** These should be considered an implementation detail of the specific database. + */ +namespace dbwrapper_private { + +/** Handle database error by throwing dbwrapper_error exception. + */ void HandleError(const leveldb::Status& status); -/** Batch of changes queued to be written to a CLevelDBWrapper */ -class CLevelDBBatch +}; + +/** Batch of changes queued to be written to a CDBWrapper */ +class CDBBatch { - friend class CLevelDBWrapper; + friend class CDBWrapper; private: + const CDBWrapper &parent; leveldb::WriteBatch batch; public: + /** + * @param[in] _parent CDBWrapper that this batch is to be submitted to + */ + CDBBatch(const CDBWrapper &_parent) : parent(_parent) { }; + template void Write(const K& key, const V& value) { @@ -61,7 +77,69 @@ public: } }; -class CLevelDBWrapper +class CDBIterator +{ +private: + const CDBWrapper &parent; + leveldb::Iterator *piter; + +public: + + /** + * @param[in] _parent Parent CDBWrapper instance. + * @param[in] _piter The original leveldb iterator. + */ + CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter) : + parent(_parent), piter(_piter) { }; + ~CDBIterator(); + + bool Valid(); + + void SeekToFirst(); + + template void Seek(const K& key) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + piter->Seek(slKey); + } + + void Next(); + + template bool GetKey(K& key) { + leveldb::Slice slKey = piter->key(); + try { + CDataStream ssKey(slKey.data(), slKey.data() + slKey.size(), SER_DISK, CLIENT_VERSION); + ssKey >> key; + } catch(std::exception &e) { + return false; + } + return true; + } + + unsigned int GetKeySize() { + return piter->key().size(); + } + + template bool GetValue(V& value) { + leveldb::Slice slValue = piter->value(); + try { + CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION); + ssValue >> value; + } catch(std::exception &e) { + return false; + } + return true; + } + + unsigned int GetValueSize() { + return piter->value().size(); + } + +}; + +class CDBWrapper { private: //! custom environment this database is using (may be NULL in case of default environment) @@ -86,8 +164,14 @@ private: leveldb::DB* pdb; public: - CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); - ~CLevelDBWrapper(); + /** + * @param[in] path Location in the filesystem where leveldb data will be stored. + * @param[in] nCacheSize Configures various leveldb cache settings. + * @param[in] fMemory If true, use leveldb's memory environment. + * @param[in] fWipe If true, remove all existing data. + */ + CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); + ~CDBWrapper(); template bool Read(const K& key, V& value) const @@ -103,7 +187,7 @@ public: if (status.IsNotFound()) return false; LogPrintf("LevelDB read failure: %s\n", status.ToString()); - HandleError(status); + dbwrapper_private::HandleError(status); } try { CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); @@ -117,7 +201,7 @@ public: template bool Write(const K& key, const V& value, bool fSync = false) { - CLevelDBBatch batch; + CDBBatch batch(*this); batch.Write(key, value); return WriteBatch(batch, fSync); } @@ -136,7 +220,7 @@ public: if (status.IsNotFound()) return false; LogPrintf("LevelDB read failure: %s\n", status.ToString()); - HandleError(status); + dbwrapper_private::HandleError(status); } return true; } @@ -144,12 +228,12 @@ public: template bool Erase(const K& key, bool fSync = false) { - CLevelDBBatch batch; + CDBBatch batch(*this); batch.Erase(key); return WriteBatch(batch, fSync); } - bool WriteBatch(CLevelDBBatch& batch, bool fSync = false); + bool WriteBatch(CDBBatch& batch, bool fSync = false); // not available for LevelDB; provide for compatibility with BDB bool Flush() @@ -159,15 +243,20 @@ public: bool Sync() { - CLevelDBBatch batch; + CDBBatch batch(*this); return WriteBatch(batch, true); } - // not exactly clean encapsulation, but it's easiest for now - leveldb::Iterator* NewIterator() + CDBIterator *NewIterator() { - return pdb->NewIterator(iteroptions); + return new CDBIterator(*this, pdb->NewIterator(iteroptions)); } + + /** + * Return true if the database managed by this class contains no entries. + */ + bool IsEmpty(); }; -#endif // BITCOIN_LEVELDBWRAPPER_H +#endif // BITCOIN_DBWRAPPER_H + diff --git a/src/paymentdisclosuredb.cpp b/src/paymentdisclosuredb.cpp index ef32f2845..1851e77ec 100644 --- a/src/paymentdisclosuredb.cpp +++ b/src/paymentdisclosuredb.cpp @@ -5,7 +5,7 @@ #include "paymentdisclosuredb.h" #include "util.h" -#include "leveldbwrapper.h" +#include "dbwrapper.h" #include @@ -38,7 +38,7 @@ PaymentDisclosureDB::PaymentDisclosureDB(const boost::filesystem::path& dbPath) TryCreateDirectory(path); options.create_if_missing = true; leveldb::Status status = leveldb::DB::Open(options, path.string(), &db); - HandleError(status); // throws exception + dbwrapper_private::HandleError(status); // throws exception LogPrintf("PaymentDisclosure: Opened LevelDB successfully\n"); } @@ -62,7 +62,7 @@ bool PaymentDisclosureDB::Put(const PaymentDisclosureKey& key, const PaymentDisc leveldb::Slice slice(&ssValue[0], ssValue.size()); leveldb::Status status = db->Put(writeOptions, key.ToString(), slice); - HandleError(status); + dbwrapper_private::HandleError(status); return true; } @@ -80,7 +80,7 @@ bool PaymentDisclosureDB::Get(const PaymentDisclosureKey& key, PaymentDisclosure if (status.IsNotFound()) return false; LogPrintf("PaymentDisclosure: LevelDB read failure: %s\n", status.ToString()); - HandleError(status); + dbwrapper_private::HandleError(status); } try { diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp new file mode 100644 index 000000000..496ae4be5 --- /dev/null +++ b/src/test/dbwrapper_tests.cpp @@ -0,0 +1,120 @@ +// Copyright (c) 2012-2013 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "dbwrapper.h" +#include "uint256.h" +#include "random.h" +#include "test/test_bitcoin.h" + +#include // for 'operator+=()' +#include +#include + +using namespace std; +using namespace boost::assign; // bring 'operator+=()' into scope +using namespace boost::filesystem; + +// Test if a string consists entirely of null characters +bool is_null_key(const vector& key) { + bool isnull = true; + + for (unsigned int i = 0; i < key.size(); i++) + isnull &= (key[i] == '\x00'); + + return isnull; +} + +BOOST_FIXTURE_TEST_SUITE(dbwrapper_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(dbwrapper) +{ + { + path ph = temp_directory_path() / unique_path(); + CDBWrapper dbw(ph, (1 << 20), true, false); + char key = 'k'; + uint256 in = GetRandHash(); + uint256 res; + + BOOST_CHECK(dbw.Write(key, in)); + BOOST_CHECK(dbw.Read(key, res)); + BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); + } +} + +// Test batch operations +BOOST_AUTO_TEST_CASE(dbwrapper_batch) +{ + { + path ph = temp_directory_path() / unique_path(); + CDBWrapper dbw(ph, (1 << 20), true, false); + + char key = 'i'; + uint256 in = GetRandHash(); + char key2 = 'j'; + uint256 in2 = GetRandHash(); + char key3 = 'k'; + uint256 in3 = GetRandHash(); + + uint256 res; + CDBBatch batch(dbw); + + batch.Write(key, in); + batch.Write(key2, in2); + batch.Write(key3, in3); + + // Remove key3 before it's even been written + batch.Erase(key3); + + dbw.WriteBatch(batch); + + BOOST_CHECK(dbw.Read(key, res)); + BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); + BOOST_CHECK(dbw.Read(key2, res)); + BOOST_CHECK_EQUAL(res.ToString(), in2.ToString()); + + // key3 never should've been written + BOOST_CHECK(dbw.Read(key3, res) == false); + } +} + +BOOST_AUTO_TEST_CASE(dbwrapper_iterator) +{ + { + path ph = temp_directory_path() / unique_path(); + CDBWrapper dbw(ph, (1 << 20), true, false); + + // The two keys are intentionally chosen for ordering + char key = 'j'; + uint256 in = GetRandHash(); + BOOST_CHECK(dbw.Write(key, in)); + char key2 = 'k'; + uint256 in2 = GetRandHash(); + BOOST_CHECK(dbw.Write(key2, in2)); + + boost::scoped_ptr it(const_cast(&dbw)->NewIterator()); + + // Be sure to seek past any earlier key (if it exists) + it->Seek(key); + + char key_res; + uint256 val_res; + + it->GetKey(key_res); + it->GetValue(val_res); + BOOST_CHECK_EQUAL(key_res, key); + BOOST_CHECK_EQUAL(val_res.ToString(), in.ToString()); + + it->Next(); + + it->GetKey(key_res); + it->GetValue(val_res); + BOOST_CHECK_EQUAL(key_res, key2); + BOOST_CHECK_EQUAL(val_res.ToString(), in2.ToString()); + + it->Next(); + BOOST_CHECK_EQUAL(it->Valid(), false); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/txdb.cpp b/src/txdb.cpp index a40df59aa..24ab0b4b5 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -31,44 +31,11 @@ static const char DB_REINDEX_FLAG = 'R'; static const char DB_LAST_BLOCK = 'l'; -void static BatchWriteAnchor(CLevelDBBatch &batch, - const uint256 &croot, - const ZCIncrementalMerkleTree &tree, - const bool &entered) -{ - if (!entered) - batch.Erase(make_pair(DB_ANCHOR, croot)); - else { - batch.Write(make_pair(DB_ANCHOR, croot), tree); - } -} - -void static BatchWriteNullifier(CLevelDBBatch &batch, const uint256 &nf, const bool &entered) { - if (!entered) - batch.Erase(make_pair(DB_NULLIFIER, nf)); - else - batch.Write(make_pair(DB_NULLIFIER, nf), true); -} - -void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) { - if (coins.IsPruned()) - batch.Erase(make_pair(DB_COINS, hash)); - else - batch.Write(make_pair(DB_COINS, hash), coins); -} - -void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { - batch.Write(DB_BEST_BLOCK, hash); -} - -void static BatchWriteHashBestAnchor(CLevelDBBatch &batch, const uint256 &hash) { - batch.Write(DB_BEST_ANCHOR, hash); -} - CCoinsViewDB::CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / dbName, nCacheSize, fMemory, fWipe) { } -CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { +CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) +{ } @@ -118,12 +85,15 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashAnchor, CAnchorsMap &mapAnchors, CNullifiersMap &mapNullifiers) { - CLevelDBBatch batch; + CDBBatch batch(db); size_t count = 0; size_t changed = 0; for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { if (it->second.flags & CCoinsCacheEntry::DIRTY) { - BatchWriteCoins(batch, it->first, it->second.coins); + if (it->second.coins.IsPruned()) + batch.Erase(make_pair(DB_COINS, it->first)); + else + batch.Write(make_pair(DB_COINS, it->first), it->second.coins); changed++; } count++; @@ -133,7 +103,11 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, for (CAnchorsMap::iterator it = mapAnchors.begin(); it != mapAnchors.end();) { if (it->second.flags & CAnchorsCacheEntry::DIRTY) { - BatchWriteAnchor(batch, it->first, it->second.tree, it->second.entered); + if (!it->second.entered) + batch.Erase(make_pair(DB_ANCHOR, it->first)); + else { + batch.Write(make_pair(DB_ANCHOR, it->first), it->second.tree); + } // TODO: changed++? } CAnchorsMap::iterator itOld = it++; @@ -142,7 +116,10 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, for (CNullifiersMap::iterator it = mapNullifiers.begin(); it != mapNullifiers.end();) { if (it->second.flags & CNullifiersCacheEntry::DIRTY) { - BatchWriteNullifier(batch, it->first, it->second.entered); + if (!it->second.entered) + batch.Erase(make_pair(DB_NULLIFIER, it->first)); + else + batch.Write(make_pair(DB_NULLIFIER, it->first), true); // TODO: changed++? } CNullifiersMap::iterator itOld = it++; @@ -150,15 +127,15 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, } if (!hashBlock.IsNull()) - BatchWriteHashBestChain(batch, hashBlock); + batch.Write(DB_BEST_BLOCK, hashBlock); if (!hashAnchor.IsNull()) - BatchWriteHashBestAnchor(batch, hashAnchor); + batch.Write(DB_BEST_ANCHOR, hashAnchor); LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); return db.WriteBatch(batch); } -CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { +CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { } bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { @@ -185,8 +162,8 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { /* It seems that there are no "const iterators" for LevelDB. Since we only need read operations on it, use a const-cast to get around that restriction. */ - boost::scoped_ptr pcursor(const_cast(&db)->NewIterator()); - pcursor->SeekToFirst(); + boost::scoped_ptr pcursor(const_cast(&db)->NewIterator()); + pcursor->Seek(DB_COINS); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); stats.hashBlock = GetBestBlock(); @@ -194,22 +171,10 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { CAmount 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 == DB_COINS) { - 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.fCoinBase ? 'c' : 'n'); - ss << VARINT(coins.nHeight); + std::pair key; + CCoins coins; + if (pcursor->GetKey(key) && key.first == DB_COINS) { + if (pcursor->GetValue(coins)) { stats.nTransactions++; for (unsigned int i=0; iGetValueSize(); ss << VARINT(0); + } else { + return error("CCoinsViewDB::GetStats() : unable to read value"); } - pcursor->Next(); - } catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } else { + break; } + pcursor->Next(); } { LOCK(cs_main); @@ -238,7 +205,7 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { } bool CBlockTreeDB::WriteBatchSync(const std::vector >& fileInfo, int nLastFile, const std::vector& blockinfo) { - CLevelDBBatch batch; + CDBBatch batch(*this); for (std::vector >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) { batch.Write(make_pair(DB_BLOCK_FILES, it->first), *it->second); } @@ -250,7 +217,7 @@ bool CBlockTreeDB::WriteBatchSync(const std::vector& blockinfo) { - CLevelDBBatch batch; + CDBBatch batch(*this); for (std::vector::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) { batch.Erase(make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash())); } @@ -262,7 +229,7 @@ bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { } bool CBlockTreeDB::WriteTxIndex(const std::vector >&vect) { - CLevelDBBatch batch; + CDBBatch batch(*this); for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) batch.Write(make_pair(DB_TXINDEX, it->first), it->second); return WriteBatch(batch); @@ -282,26 +249,17 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { bool CBlockTreeDB::LoadBlockIndexGuts() { - boost::scoped_ptr pcursor(NewIterator()); + boost::scoped_ptr pcursor(NewIterator()); - CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); - ssKeySet << make_pair(DB_BLOCK_INDEX, uint256()); - pcursor->Seek(ssKeySet.str()); + pcursor->Seek(make_pair(DB_BLOCK_INDEX, uint256())); // 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 == DB_BLOCK_INDEX) { - leveldb::Slice slValue = pcursor->value(); - CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); - CDiskBlockIndex diskindex; - ssValue >> diskindex; - + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) { + CDiskBlockIndex diskindex; + if (pcursor->GetValue(diskindex)) { // Construct block index object CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); @@ -332,10 +290,10 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pcursor->Next(); } else { - break; // if shutdown requested or finished loading block index + return error("LoadBlockIndex() : failed to read value"); } - } catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } else { + break; } } diff --git a/src/txdb.h b/src/txdb.h index ddc32fe0c..f96b07676 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -7,7 +7,7 @@ #define BITCOIN_TXDB_H #include "coins.h" -#include "leveldbwrapper.h" +#include "dbwrapper.h" #include #include @@ -26,11 +26,11 @@ static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024; //! min. -dbcache in (MiB) static const int64_t nMinDbCache = 4; -/** CCoinsView backed by the LevelDB coin database (chainstate/) */ +/** CCoinsView backed by the coin database (chainstate/) */ class CCoinsViewDB : public CCoinsView { protected: - CLevelDBWrapper db; + CDBWrapper db; CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory = false, bool fWipe = false); public: CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); @@ -50,7 +50,7 @@ public: }; /** Access to the block database (blocks/index/) */ -class CBlockTreeDB : public CLevelDBWrapper +class CBlockTreeDB : public CDBWrapper { public: CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);