diff --git a/src/init.cpp b/src/init.cpp index 863cdaf69..dc1383387 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1122,7 +1122,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greated than nMaxDbcache int64_t nBlockTreeDBCache = nTotalCache / 8; - if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false)) + if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false) && !GetBoolArg("-addrindex", false)) nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB nTotalCache -= nBlockTreeDBCache; int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache @@ -1190,6 +1190,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) break; } + // Check for changed -addrindex state + if (fAddrIndex != GetBoolArg("-addrindex", false)) { + strLoadError = _("You need to rebuild the database using -reindex to change -addrindex"); + break; + } + uiInterface.InitMessage(_("Verifying blocks...")); if (fHavePruned && GetArg("-checkblocks", 288) > MIN_BLOCKS_TO_KEEP) { LogPrintf("Prune: pruned datadir may not have more than %d blocks; -checkblocks=%d may fail\n", diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp index c353dfa6d..f149b6ef8 100644 --- a/src/leveldbwrapper.cpp +++ b/src/leveldbwrapper.cpp @@ -43,6 +43,13 @@ static leveldb::Options GetOptions(size_t nCacheSize) return options; } +CLevelDBIterator::~CLevelDBIterator() { delete piter; } +bool CLevelDBIterator::Valid() { return piter->Valid(); } +void CLevelDBIterator::SeekToFirst() { piter->SeekToFirst(); } +void CLevelDBIterator::SeekToLast() { piter->SeekToLast(); } +void CLevelDBIterator::Next() { piter->Next(); } +void CLevelDBIterator::Prev() { piter->Prev(); } + CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe) { penv = NULL; diff --git a/src/leveldbwrapper.h b/src/leveldbwrapper.h index c65e84270..523fcaa1c 100644 --- a/src/leveldbwrapper.h +++ b/src/leveldbwrapper.h @@ -61,6 +61,63 @@ public: } }; +class CLevelDBIterator +{ +private: + leveldb::Iterator *piter; + +public: + CLevelDBIterator(leveldb::Iterator *piterIn) : piter(piterIn) {} + ~CLevelDBIterator(); + + bool Valid(); + + void SeekToFirst(); + void SeekToLast(); + + 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(); + void Prev(); + + 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 CLevelDBWrapper { private: @@ -163,10 +220,8 @@ public: return WriteBatch(batch, true); } - // not exactly clean encapsulation, but it's easiest for now - leveldb::Iterator* NewIterator() - { - return pdb->NewIterator(iteroptions); + CLevelDBIterator *NewIterator() { + return new CLevelDBIterator(pdb->NewIterator(iteroptions)); } }; diff --git a/src/main.cpp b/src/main.cpp index 9325a2fdb..dc2cce04e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,7 @@ #include "merkleblock.h" #include "net.h" #include "pow.h" +#include "pubkey.h" #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" @@ -58,6 +59,7 @@ int nScriptCheckThreads = 0; bool fImporting = false; bool fReindex = false; bool fTxIndex = false; +bool fAddrIndex = false; bool fHavePruned = false; bool fPruneMode = false; bool fIsBareMultisigStd = true; @@ -1070,6 +1072,45 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return true; } +bool ReadTransaction(CTransaction& txOut, const CDiskTxPos &pos, uint256 &hashBlock) { + CAutoFile file(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); + if (file.IsNull()) + return error("%s: OpenBlockFile failed", __func__); + CBlockHeader header; + try { + file >> header; + fseek(file.Get(), pos.nTxOffset, SEEK_CUR); + file >> txOut; + } catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + hashBlock = header.GetHash(); + return true; +} + +bool FindTransactionsByDestination(const CTxDestination &dest, std::set &setpos) { + uint160 addrid; + const CKeyID *pkeyid = boost::get(&dest); + if (pkeyid) { + addrid = static_cast(*pkeyid); + } else { + const CScriptID *pscriptid = boost::get(&dest); + if (pscriptid) + addrid = static_cast(*pscriptid); + else + return false; + } + + LOCK(cs_main); + if (!fAddrIndex) + return false; + std::vector vPos; + if (!pblocktree->ReadAddrIndex(addrid, vPos)) + return false; + setpos.insert(vPos.begin(), vPos.end()); + return true; +} + /** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) { @@ -1086,18 +1127,8 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock if (fTxIndex) { CDiskTxPos postx; if (pblocktree->ReadTxIndex(hash, postx)) { - CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); - if (file.IsNull()) - return error("%s: OpenBlockFile failed", __func__); - CBlockHeader header; - try { - file >> header; - fseek(file.Get(), postx.nTxOffset, SEEK_CUR); - file >> txOut; - } catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - hashBlock = header.GetHash(); + if (!ReadTransaction(txOut, postx, hashBlock)) + return false; if (txOut.GetHash() != hash) return error("%s: txid mismatch", __func__); return true; @@ -1804,6 +1835,32 @@ static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 0; static int64_t nTimeTotal = 0; +// Index either: a) every data push >=8 bytes, b) if no such pushes, the entire script +void static BuildAddrIndex(const CScript &script, const CExtDiskTxPos &pos, std::vector > &out) { + CScript::const_iterator pc = script.begin(); + CScript::const_iterator pend = script.end(); + std::vector data; + opcodetype opcode; + bool fHaveData = false; + while (pc < pend) { + script.GetOp(pc, opcode, data); + if (0 <= opcode && opcode <= OP_PUSHDATA4 && data.size() >= 8) { // data element + uint160 addrid; + if (data.size() <= 20) { + memcpy(&addrid, &data[0], data.size()); + } else { + addrid = Hash160(data); + } + out.push_back(std::make_pair(addrid, pos)); + fHaveData = true; + } + } + if (!fHaveData) { + uint160 addrid = Hash160(script); + out.push_back(std::make_pair(addrid, pos)); + } +} + bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) { const CChainParams& chainparams = Params(); @@ -1868,11 +1925,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CAmount nFees = 0; int nInputs = 0; unsigned int nSigOps = 0; - CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); - std::vector > vPos; - vPos.reserve(block.vtx.size()); - blockundo.vtxundo.reserve(block.vtx.size() - 1); - for (unsigned int i = 0; i < block.vtx.size(); i++) + CExtDiskTxPos pos(CDiskTxPos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())), pindex->nHeight); + std::vector > vPosTxid; + std::vector > vPosAddrid; + if (fTxIndex) { + vPosTxid.reserve(block.vtx.size()); + blockundo.vtxundo.reserve(block.vtx.size() - 1); + } + if (fAddrIndex) { + vPosAddrid.reserve(4*block.vtx.size()); + // We don't keep undo information, as we never remove address indexes + } + for (unsigned int i=0; i 0) { blockundo.vtxundo.push_back(CTxUndo()); } UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); - vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart; @@ -1953,8 +2030,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } if (fTxIndex) - if (!pblocktree->WriteTxIndex(vPos)) + if (!pblocktree->WriteTxIndex(vPosTxid)) return AbortNode(state, "Failed to write transaction index"); + if (fAddrIndex) + if (!pblocktree->AddAddrIndex(vPosAddrid)) + return AbortNode(state, "Failed to write address index"); // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); @@ -3277,6 +3357,9 @@ bool static LoadBlockIndexDB() pblocktree->ReadFlag("txindex", fTxIndex); LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); + pblocktree->ReadFlag("addrindex", fAddrIndex); + LogPrintf("%s: address index %s\n", __func__, fAddrIndex ? "enabled" : "disabled"); + // Load pointer to end of best chain BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); if (it == mapBlockIndex.end()) @@ -3430,6 +3513,8 @@ bool InitBlockIndex() { // Use the provided setting for -txindex in the new database fTxIndex = GetBoolArg("-txindex", false); pblocktree->WriteFlag("txindex", fTxIndex); + fAddrIndex = GetBoolArg("-addrindex", false); + pblocktree->WriteFlag("addrindex", fAddrIndex); LogPrintf("Initializing databases...\n"); // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) diff --git a/src/main.h b/src/main.h index 4a35ddfc1..2cf31df28 100644 --- a/src/main.h +++ b/src/main.h @@ -110,6 +110,7 @@ extern bool fImporting; extern bool fReindex; extern int nScriptCheckThreads; extern bool fTxIndex; +extern bool fAddrIndex; extern bool fIsBareMultisigStd; extern bool fCheckBlockIndex; extern bool fCheckpointsEnabled; @@ -268,8 +269,58 @@ struct CDiskTxPos : public CDiskBlockPos CDiskBlockPos::SetNull(); nTxOffset = 0; } + + friend bool operator==(const CDiskTxPos &a, const CDiskTxPos &b) { + return (a.nFile == b.nFile && a.nPos == b.nPos && a.nTxOffset == b.nTxOffset); + } + + friend bool operator!=(const CDiskTxPos &a, const CDiskTxPos &b) { + return !(a == b); + } + + friend bool operator<(const CDiskTxPos &a, const CDiskTxPos &b) { + return (a.nFile < b.nFile || ( + (a.nFile == b.nFile) && (a.nPos < b.nPos || ( + (a.nPos == b.nPos) && (a.nTxOffset < b.nTxOffset))))); + } }; +struct CExtDiskTxPos : public CDiskTxPos +{ + unsigned int nHeight; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(*(CDiskTxPos*)this); + READWRITE(VARINT(nHeight)); + } + + CExtDiskTxPos(const CDiskTxPos &pos, int nHeightIn) : CDiskTxPos(pos), nHeight(nHeightIn) { + } + + CExtDiskTxPos() { + SetNull(); + } + + void SetNull() { + CDiskTxPos::SetNull(); + nHeight = 0; + } + + friend bool operator==(const CExtDiskTxPos &a, const CExtDiskTxPos &b) { + return (a.nHeight == b.nHeight && a.nFile == b.nFile && a.nPos == b.nPos && a.nTxOffset == b.nTxOffset); + } + + friend bool operator!=(const CExtDiskTxPos &a, const CExtDiskTxPos &b) { + return !(a == b); + } + + friend bool operator<(const CExtDiskTxPos &a, const CExtDiskTxPos &b) { + if (a.nHeight < b.nHeight) return true; + if (a.nHeight > b.nHeight) return false; + return ((const CDiskTxPos)a < (const CDiskTxPos)b); + } +}; CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree); @@ -378,8 +429,10 @@ public: /** Functions for disk access for blocks */ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); +bool ReadTransaction(CTransaction& tx, const CDiskTxPos &pos, uint256 &hashBlock); bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex); +bool FindTransactionsByDestination(const CTxDestination &dest, std::set &setpos); /** Functions for validating blocks and updating the block tree */ diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 4b576b370..cea7db872 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -71,6 +71,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getblock", 1 }, { "gettransaction", 1 }, { "getrawtransaction", 1 }, + { "searchrawtransactions", 1 }, + { "searchrawtransactions", 2 }, + { "searchrawtransactions", 3 }, { "createrawtransaction", 0 }, { "createrawtransaction", 1 }, { "signrawtransaction", 1 }, diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 211fc0160..424efa3f4 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -106,6 +106,66 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) } } +Value searchrawtransactions(const Array ¶ms, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 4) + throw runtime_error( + "searchrawtransactions
[verbose=1] [skip=0] [count=100]\n"); + + if (!fAddrIndex) + throw JSONRPCError(RPC_MISC_ERROR, "Address index not enabled"); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + CTxDestination dest = address.Get(); + + std::set setpos; + if (!FindTransactionsByDestination(dest, setpos)) + throw JSONRPCError(RPC_DATABASE_ERROR, "Cannot search for address"); + + int nSkip = 0; + int nCount = 100; + bool fVerbose = true; + if (params.size() > 1) + fVerbose = (params[1].get_int() != 0); + if (params.size() > 2) + nSkip = params[2].get_int(); + if (params.size() > 3) + nCount = params[3].get_int(); + + if (nSkip < 0) + nSkip += setpos.size(); + if (nSkip < 0) + nSkip = 0; + if (nCount < 0) + nCount = 0; + + std::set::const_iterator it = setpos.begin(); + while (it != setpos.end() && nSkip--) it++; + + Array result; + while (it != setpos.end() && nCount--) { + CTransaction tx; + uint256 hashBlock; + if (!ReadTransaction(tx, *it, hashBlock)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Cannot read transaction from disk"); + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << tx; + string strHex = HexStr(ssTx.begin(), ssTx.end()); + if (fVerbose) { + Object object; + TxToJSON(tx, hashBlock, object); + object.push_back(Pair("hex", strHex)); + result.push_back(object); + } else { + result.push_back(strHex); + } + it++; + } + return result; +} + Value getrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 048e221d5..25ebaf903 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -328,6 +328,7 @@ static const CRPCCommand vRPCCommands[] = { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true }, { "rawtransactions", "decodescript", &decodescript, true }, { "rawtransactions", "getrawtransaction", &getrawtransaction, true }, + { "rawtransactions", "searchrawtransactions", &searchrawtransactions, true }, { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false }, { "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */ diff --git a/src/rpcserver.h b/src/rpcserver.h index d1051c4b0..0e1656568 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -213,6 +213,7 @@ extern json_spirit::Value setmocktime(const json_spirit::Array& params, bool fHe extern json_spirit::Value resendwallettransactions(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp +extern json_spirit::Value searchrawtransactions(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value lockunspent(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listlockunspent(const json_spirit::Array& params, bool fHelp); diff --git a/src/txdb.cpp b/src/txdb.cpp index ff87655c3..3a595f9e5 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -80,6 +80,10 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { } CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { + if (!Read('S', salt)) { + salt = GetRandHash(); + Write('S', salt); + } } bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { @@ -106,8 +110,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('c'); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); stats.hashBlock = GetBestBlock(); @@ -115,22 +119,10 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { arith_uint256 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; iGetKeySize(); 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(); } stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight; stats.hashSerialized = ss.GetHash(); @@ -178,6 +172,42 @@ bool CBlockTreeDB::WriteTxIndex(const std::vector return WriteBatch(batch); } +bool CBlockTreeDB::ReadAddrIndex(const uint160 &addrid, std::vector &list) { + CLevelDBIterator *iter = NewIterator(); + uint64_t lookupid; + { + CHashWriter ss(SER_GETHASH, 0); + ss << salt; + ss << addrid; + lookupid = UintToArith256(ss.GetHash()).GetLow64(); + } + iter->Seek(make_pair('a', lookupid)); + while (iter->Valid()) { + std::pair, CExtDiskTxPos> key; + if (iter->GetKey(key) && key.first.first == 'a' && key.first.second == lookupid) { + list.push_back(key.second); + } else { + break; + } + iter->Next(); + } + + delete iter; + return true; +} + +bool CBlockTreeDB::AddAddrIndex(const std::vector > &list) { + unsigned char foo[0]; + CLevelDBBatch batch; + for (std::vector >::const_iterator it=list.begin(); it!=list.end(); it++) { + CHashWriter ss(SER_GETHASH, 0); + ss << salt; + ss << it->first; + batch.Write(make_pair(make_pair('a', UintToArith256(ss.GetHash()).GetLow64()), it->second), FLATDATA(foo)); + } + return WriteBatch(batch); +} + bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0'); } @@ -192,26 +222,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); @@ -240,10 +261,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 bef5dc9fd..62209702e 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -17,6 +17,7 @@ class CBlockFileInfo; class CBlockIndex; struct CDiskTxPos; +struct CExtDiskTxPos; class uint256; //! -dbcache default (MiB) @@ -47,6 +48,7 @@ class CBlockTreeDB : public CLevelDBWrapper public: CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); private: + uint256 salt; CBlockTreeDB(const CBlockTreeDB&); void operator=(const CBlockTreeDB&); public: @@ -57,6 +59,8 @@ public: bool ReadReindexing(bool &fReindex); bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos); bool WriteTxIndex(const std::vector > &list); + bool ReadAddrIndex(const uint160 &addrid, std::vector &list); + bool AddAddrIndex(const std::vector > &list); bool WriteFlag(const std::string &name, bool fValue); bool ReadFlag(const std::string &name, bool &fValue); bool LoadBlockIndexGuts();