From 2ee172fe1506ffa8bdb7b48d3580bd504bf001af Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Thu, 13 Oct 2022 20:13:19 +0100 Subject: [PATCH 1/5] Error reporting improvements. Signed-off-by: Daira Hopwood --- qa/rpc-tests/test_framework/util.py | 4 ++-- src/chain.h | 3 ++- src/txdb.cpp | 5 ++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 20c732349..4aac4e21f 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -214,7 +214,7 @@ def wait_for_bitcoind_start(process, url, i): ''' while True: if process.poll() is not None: - raise Exception('%s exited with status %i during initialization' % (zcashd_binary(), process.returncode)) + raise Exception('%s node %d exited with status %i during initialization' % (zcashd_binary(), i, process.returncode)) try: rpc = get_rpc_proxy(url, i) rpc.getblockcount() @@ -433,7 +433,7 @@ def assert_start_raises_init_error(i, dirname, extra_args=None, expected_msg=Non node = start_node(i, dirname, extra_args, stderr=log_stderr) stop_node(node, i) except Exception as e: - assert ("%s exited" % (zcashd_binary(),)) in str(e) #node must have shutdown + assert ("%s node %d exited" % (zcashd_binary(), i)) in str(e) # node must have shutdown if expected_msg is not None: log_stderr.seek(0) stderr = log_stderr.read().decode('utf-8') diff --git a/src/chain.h b/src/chain.h index 65faca6c6..855310b9b 100644 --- a/src/chain.h +++ b/src/chain.h @@ -403,6 +403,7 @@ public: uint256 GetBlockHash() const { + assert(phashBlock); return *phashBlock; } @@ -432,7 +433,7 @@ public: return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", pprev, nHeight, hashMerkleRoot.ToString(), - GetBlockHash().ToString()); + phashBlock ? GetBlockHash().ToString() : "(nil)"); } //! Check whether this block index entry is valid up to the passed validity level. diff --git a/src/txdb.cpp b/src/txdb.cpp index c95c96846..ddc025fda 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -613,9 +613,12 @@ bool CBlockTreeDB::LoadBlockIndexGuts( // Consistency checks auto header = pindexNew->GetBlockHeader(); + if (header.GetHash() != diskindex.GetBlockHash()) + return error("LoadBlockIndex(): inconsistent header vs diskindex hash: header hash = %s, diskindex hash = %s", + header.GetHash().ToString(), diskindex.GetBlockHash().ToString()); if (header.GetHash() != pindexNew->GetBlockHash()) return error("LoadBlockIndex(): block header inconsistency detected: on-disk = %s, in-memory = %s", - diskindex.ToString(), pindexNew->ToString()); + diskindex.ToString(), pindexNew->ToString()); if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus())) return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); From aee8d5c0eca8ff1a6f4dfcc832107b6d1caf0811 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Fri, 7 Oct 2022 14:40:07 +0100 Subject: [PATCH 2/5] Avoid storing the Equihash solution in a CBlockIndex object once it has been written to the leveldb database. Co-authored-by: Jack Grigg Signed-off-by: Daira Hopwood --- doc/release-notes.md | 6 ++++ src/chain.cpp | 43 ++++++++++++++++++++++ src/chain.h | 81 +++++++++++++++++++++++++++--------------- src/main.cpp | 8 ++++- src/rest.cpp | 10 +++--- src/rpc/blockchain.cpp | 2 +- src/txdb.cpp | 31 +++++++++++++--- src/txdb.h | 3 +- 8 files changed, 145 insertions(+), 39 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index 1a3db57d8..bd76b7521 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -9,3 +9,9 @@ Fixed This release fixes an error "Assertion `uResultHeight == rewindHeight` failed" (#5958) that could sometimes happen when restarting a node. + +Memory Usage Improvement +------------------------ + +The memory usage of zcashd has been reduced by not keeping Equihash solutions for all +block headers in memory. diff --git a/src/chain.cpp b/src/chain.cpp index 9ab62b35e..3f602b4f2 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -6,6 +6,9 @@ #include "chain.h" +#include "main.h" +#include "txdb.h" + /** * CChain implementation */ @@ -58,6 +61,46 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const { return pindex; } +void CBlockIndex::TrimSolution() +{ + AssertLockHeld(cs_main); + + // We can correctly trim a solution as soon as the block index entry has been added + // to leveldb. Updates to the block index entry (to update validity status) will be + // handled by re-reading the solution from the existing db entry. It does not help to + // try to avoid these reads by gating trimming on the validity status: the re-reads are + // efficient anyway because of caching in leveldb, and most of them are unavoidable. + if (HasSolution()) { + MetricsIncrementCounter("zcashd.trimmed_equihash_solutions"); + std::vector empty; + nSolution.swap(empty); + } +} + +CBlockHeader CBlockIndex::GetBlockHeader() const +{ + AssertLockHeld(cs_main); + + CBlockHeader header; + header.nVersion = nVersion; + if (pprev) { + header.hashPrevBlock = pprev->GetBlockHash(); + } + header.hashMerkleRoot = hashMerkleRoot; + header.hashBlockCommitments = hashBlockCommitments; + header.nTime = nTime; + header.nBits = nBits; + header.nNonce = nNonce; + if (HasSolution()) { + header.nSolution = nSolution; + } else { + CDiskBlockIndex dbindex; + assert(pblocktree->ReadDiskBlockIndex(GetBlockHash(), dbindex)); + header.nSolution = dbindex.GetSolution(); + } + return header; +} + /** Turn the lowest '1' bit in the binary representation of a number into a '0'. */ int static inline InvertLowestOne(int n) { return n & (n - 1); } diff --git a/src/chain.h b/src/chain.h index 855310b9b..21d9812ad 100644 --- a/src/chain.h +++ b/src/chain.h @@ -12,6 +12,7 @@ #include "pow.h" #include "tinyformat.h" #include "uint256.h" +#include "util/strencodings.h" #include #include @@ -308,8 +309,13 @@ public: unsigned int nTime; unsigned int nBits; uint256 nNonce; +protected: + // The Equihash solution, if it is stored. Once we know that the block index + // entry is present in leveldb, this field can be cleared via the TrimSolution + // method to save memory. std::vector nSolution; +public: //! (memory only) Sequential id assigned to distinguish order in which blocks are received. uint32_t nSequenceId; @@ -386,20 +392,11 @@ public: return ret; } - CBlockHeader GetBlockHeader() const - { - CBlockHeader block; - block.nVersion = nVersion; - if (pprev) - block.hashPrevBlock = pprev->GetBlockHash(); - block.hashMerkleRoot = hashMerkleRoot; - block.hashBlockCommitments = hashBlockCommitments; - block.nTime = nTime; - block.nBits = nBits; - block.nNonce = nNonce; - block.nSolution = nSolution; - return block; - } + //! Get the block header for this block index. Requires cs_main. + CBlockHeader GetBlockHeader() const; + + //! Clear the Equihash solution to save memory. Requires cs_main. + void TrimSolution(); uint256 GetBlockHash() const { @@ -430,10 +427,11 @@ public: std::string ToString() const { - return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", + return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s, HasSolution=%s)", pprev, nHeight, hashMerkleRoot.ToString(), - phashBlock ? GetBlockHash().ToString() : "(nil)"); + phashBlock ? GetBlockHash().ToString() : "(nil)", + HasSolution()); } //! Check whether this block index entry is valid up to the passed validity level. @@ -445,6 +443,12 @@ public: return ((nStatus & BLOCK_VALID_MASK) >= nUpTo); } + //! Is the Equihash solution stored? + bool HasSolution() const + { + return !nSolution.empty(); + } + //! Raise the validity level of this block index entry. //! Returns true if the validity was changed. bool RaiseValidity(enum BlockStatus nUpTo) @@ -483,8 +487,11 @@ public: hashPrev = uint256(); } - explicit CDiskBlockIndex(const CBlockIndex* pindex) : CBlockIndex(*pindex) { + explicit CDiskBlockIndex(const CBlockIndex* pindex, std::function()> getSolution) : CBlockIndex(*pindex) { hashPrev = (pprev ? pprev->GetBlockHash() : uint256()); + if (!HasSolution()) { + nSolution = getSolution(); + } } ADD_SERIALIZE_METHODS; @@ -565,20 +572,31 @@ public: // them to CBlockTreeDB::LoadBlockIndexGuts() in txdb.cpp :) } - uint256 GetBlockHash() const + //! Get the block header for this block index. + CBlockHeader GetBlockHeader() const { - CBlockHeader block; - block.nVersion = nVersion; - block.hashPrevBlock = hashPrev; - block.hashMerkleRoot = hashMerkleRoot; - block.hashBlockCommitments = hashBlockCommitments; - block.nTime = nTime; - block.nBits = nBits; - block.nNonce = nNonce; - block.nSolution = nSolution; - return block.GetHash(); + CBlockHeader header; + header.nVersion = nVersion; + header.hashPrevBlock = hashPrev; + header.hashMerkleRoot = hashMerkleRoot; + header.hashBlockCommitments = hashBlockCommitments; + header.nTime = nTime; + header.nBits = nBits; + header.nNonce = nNonce; + header.nSolution = nSolution; + return header; } + uint256 GetBlockHash() const + { + return GetBlockHeader().GetHash(); + } + + std::vector GetSolution() const + { + assert(HasSolution()); + return nSolution; + } std::string ToString() const { @@ -589,6 +607,13 @@ public: hashPrev.ToString()); return str; } + +private: + //! This method should not be called on a CDiskBlockIndex. + void TrimSolution() + { + assert(!"called CDiskBlockIndex::TrimSolution"); + } }; /** An in-memory indexed chain of blocks. */ diff --git a/src/main.cpp b/src/main.cpp index b3ba4d89c..97e8b4fca 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3715,7 +3715,7 @@ bool static FlushStateToDisk( vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); it = setDirtyFileInfo.erase(it); } - std::vector vBlocks; + std::vector vBlocks; vBlocks.reserve(setDirtyBlockIndex.size()); for (set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { vBlocks.push_back(*it); @@ -3724,6 +3724,12 @@ bool static FlushStateToDisk( if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { return AbortNode(state, "Files to write to block index database"); } + // Now that we have written the block indices to the database, we do not + // need to store solutions for these CBlockIndex objects in memory. + // cs_main must be held here. + for (CBlockIndex *pblockindex : vBlocks) { + pblockindex->TrimSolution(); + } } // Finally remove any pruned files if (fFlushForPrune) diff --git a/src/rest.cpp b/src/rest.cpp index 29db4a6ba..412709bec 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -141,6 +141,7 @@ static bool rest_headers(HTTPRequest* req, std::vector headers; headers.reserve(count); + CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); { LOCK(cs_main); BlockMap::const_iterator it = mapBlockIndex.find(hash); @@ -151,11 +152,12 @@ static bool rest_headers(HTTPRequest* req, break; pindex = chainActive.Next(pindex); } - } - CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); - for (const CBlockIndex *pindex : headers) { - ssHeader << pindex->GetBlockHeader(); + if (rf == RF_BINARY || rf == RF_HEX) { + for (const CBlockIndex *pindex : headers) { + ssHeader << pindex->GetBlockHeader(); + } + } } switch (rf) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index a929d1c19..772ab17d1 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -118,7 +118,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) result.pushKV("finalsaplingroot", blockindex->hashFinalSaplingRoot.GetHex()); result.pushKV("time", (int64_t)blockindex->nTime); result.pushKV("nonce", blockindex->nNonce.GetHex()); - result.pushKV("solution", HexStr(blockindex->nSolution)); + result.pushKV("solution", HexStr(blockindex->GetBlockHeader().nSolution)); result.pushKV("bits", strprintf("%08x", blockindex->nBits)); result.pushKV("difficulty", GetDifficulty(blockindex)); result.pushKV("chainwork", blockindex->nChainWork.GetHex()); diff --git a/src/txdb.cpp b/src/txdb.cpp index ddc025fda..df2c3c137 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -382,14 +382,29 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { return true; } -bool CBlockTreeDB::WriteBatchSync(const std::vector >& fileInfo, int nLastFile, const std::vector& blockinfo) { +bool CBlockTreeDB::WriteBatchSync(const std::vector >& fileInfo, int nLastFile, const std::vector& blockinfo) { 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); } batch.Write(DB_LAST_BLOCK, nLastFile); for (std::vector::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) { - batch.Write(make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it)); + std::pair key = make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()); + try { + CDiskBlockIndex dbindex {*it, [this, &key]() { + // It can happen that the index entry is written, then the Equihash solution is cleared from memory, + // then the index entry is rewritten. In that case we must read the solution from the old entry. + CDiskBlockIndex dbindex_old; + if (!Read(key, dbindex_old)) { + LogPrintf("%s: Failed to read index entry", __func__); + throw runtime_error("Failed to read index entry"); + } + return dbindex_old.GetSolution(); + }}; + batch.Write(key, dbindex); + } catch (const runtime_error&) { + return false; + } } return WriteBatch(batch, true); } @@ -402,6 +417,10 @@ bool CBlockTreeDB::EraseBatchSync(const std::vector& blockin return WriteBatch(batch, true); } +bool CBlockTreeDB::ReadDiskBlockIndex(const uint256 &blockhash, CDiskBlockIndex &dbindex) { + return Read(make_pair(DB_BLOCK_INDEX, blockhash), dbindex); +} + bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { return Read(make_pair(DB_TXINDEX, txid), pos); } @@ -599,7 +618,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts( pindexNew->nTime = diskindex.nTime; pindexNew->nBits = diskindex.nBits; pindexNew->nNonce = diskindex.nNonce; - pindexNew->nSolution = diskindex.nSolution; + // the Equihash solution will be loaded lazily from the dbindex entry pindexNew->nStatus = diskindex.nStatus; pindexNew->nCachedBranchId = diskindex.nCachedBranchId; pindexNew->nTx = diskindex.nTx; @@ -612,7 +631,11 @@ bool CBlockTreeDB::LoadBlockIndexGuts( pindexNew->hashAuthDataRoot = diskindex.hashAuthDataRoot; // Consistency checks - auto header = pindexNew->GetBlockHeader(); + CBlockHeader header; + { + LOCK(cs_main); + header = pindexNew->GetBlockHeader(); + } if (header.GetHash() != diskindex.GetBlockHash()) return error("LoadBlockIndex(): inconsistent header vs diskindex hash: header hash = %s, diskindex hash = %s", header.GetHash().ToString(), diskindex.GetBlockHash().ToString()); diff --git a/src/txdb.h b/src/txdb.h index 07971805e..96285a265 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -117,12 +117,13 @@ private: CBlockTreeDB(const CBlockTreeDB&); void operator=(const CBlockTreeDB&); public: - bool WriteBatchSync(const std::vector >& fileInfo, int nLastFile, const std::vector& blockinfo); + bool WriteBatchSync(const std::vector >& fileInfo, int nLastFile, const std::vector& blockinfo); bool EraseBatchSync(const std::vector& blockinfo); bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info); bool ReadLastBlockFile(int &nFile); bool WriteReindexing(bool fReindexing); bool ReadReindexing(bool &fReindexing); + bool ReadDiskBlockIndex(const uint256 &blockhash, CDiskBlockIndex &dbindex); bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos); bool WriteTxIndex(const std::vector > &vect); From 8c958e13a5ab17d4e7d41f543f4b1acd65baadf8 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Tue, 8 Nov 2022 23:34:14 +0000 Subject: [PATCH 3/5] Improve handling of database read errors. Signed-off-by: Daira Hopwood --- src/chain.cpp | 5 ++++- src/main.cpp | 6 +++++- src/rest.cpp | 8 ++++++-- src/rpc/blockchain.cpp | 19 +++++++++++-------- src/txdb.cpp | 7 ++++++- 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/chain.cpp b/src/chain.cpp index 3f602b4f2..816a300f8 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -95,7 +95,10 @@ CBlockHeader CBlockIndex::GetBlockHeader() const header.nSolution = nSolution; } else { CDiskBlockIndex dbindex; - assert(pblocktree->ReadDiskBlockIndex(GetBlockHash(), dbindex)); + if (!pblocktree->ReadDiskBlockIndex(GetBlockHash(), dbindex)) { + LogPrintf("%s: Failed to read index entry", __func__); + throw std::runtime_error("Failed to read index entry"); + } header.nSolution = dbindex.GetSolution(); } return header; diff --git a/src/main.cpp b/src/main.cpp index 97e8b4fca..a4b8ffe81 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6134,7 +6134,11 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams) } } } - // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow + // try { + // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow + // } catch (const runtime_error&) { + // assert(!"Failed to read index entry"); + // } // End: actual consistency checks. // Try descending into the first subnode. diff --git a/src/rest.cpp b/src/rest.cpp index 412709bec..83d0313ee 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -154,8 +154,12 @@ static bool rest_headers(HTTPRequest* req, } if (rf == RF_BINARY || rf == RF_HEX) { - for (const CBlockIndex *pindex : headers) { - ssHeader << pindex->GetBlockHeader(); + try { + for (const CBlockIndex *pindex : headers) { + ssHeader << pindex->GetBlockHeader(); + } + } catch (const std::runtime_error&) { + return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Failed to read index entry"); } } } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 772ab17d1..ae35e09c4 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -684,15 +684,18 @@ UniValue getblockheader(const UniValue& params, bool fHelp) CBlockIndex* pblockindex = mapBlockIndex[hash]; - if (!fVerbose) - { - CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); - ssBlock << pblockindex->GetBlockHeader(); - std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); - return strHex; + try { + if (!fVerbose) { + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << pblockindex->GetBlockHeader(); + std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); + return strHex; + } else { + return blockheaderToJSON(pblockindex); + } + } catch (const runtime_error&) { + throw JSONRPCError(RPC_DATABASE_ERROR, "Failed to read index entry"); } - - return blockheaderToJSON(pblockindex); } UniValue getblock(const UniValue& params, bool fHelp) diff --git a/src/txdb.cpp b/src/txdb.cpp index df2c3c137..fab8987a5 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -634,7 +634,12 @@ bool CBlockTreeDB::LoadBlockIndexGuts( CBlockHeader header; { LOCK(cs_main); - header = pindexNew->GetBlockHeader(); + try { + header = pindexNew->GetBlockHeader(); + } catch (const runtime_error&) { + return error("LoadBlockIndex(): failed to read index entry: diskindex hash = %s", + diskindex.GetBlockHash().ToString()); + } } if (header.GetHash() != diskindex.GetBlockHash()) return error("LoadBlockIndex(): inconsistent header vs diskindex hash: header hash = %s, diskindex hash = %s", From 089acd75f7fff7318efc86ba08d2205fc1c9f856 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Sun, 13 Nov 2022 19:50:56 +0000 Subject: [PATCH 4/5] Add Prometheus metrics so we have more visibility into what is going on with the Equihash solution trimming: * (counter) `zcashd.debug.memory.allocated_equihash_solutions` - the number of Equihash solutions that have ever been allocated; * (counter) `zcashd.debug.memory.trimmed_equihash_solutions` - the number of Equihash solutions that were trimmed; * (counter) `zcashd.debug.blocktree.trimmed_equihash_read_dbindex` - the number of times a block index entry needed to be reread to construct a block header because a solution had been trimmed; * (counter) `zcashd.debug.blocktree.write_batch` - the number of block tree batch write operations (calls to `CBlockTreeDB::WriteBatchSync`); * (counter) `zcashd.debug.blocktree.write_batch_read_dbindex` - the number of times a block index entry needed to be reread before being written during a block tree batch write operation. These metrics are specific to `zcashd`; they are primarily intended for performance debugging of this feature and should not be considered stable. Co-authored-by: Jack Grigg Signed-off-by: Daira Hopwood --- src/chain.cpp | 5 ++++- src/chain.h | 3 +++ src/txdb.cpp | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/chain.cpp b/src/chain.cpp index 816a300f8..d49d2d733 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -9,6 +9,8 @@ #include "main.h" #include "txdb.h" +#include + /** * CChain implementation */ @@ -71,7 +73,7 @@ void CBlockIndex::TrimSolution() // try to avoid these reads by gating trimming on the validity status: the re-reads are // efficient anyway because of caching in leveldb, and most of them are unavoidable. if (HasSolution()) { - MetricsIncrementCounter("zcashd.trimmed_equihash_solutions"); + MetricsIncrementCounter("zcashd.debug.memory.trimmed_equihash_solutions"); std::vector empty; nSolution.swap(empty); } @@ -94,6 +96,7 @@ CBlockHeader CBlockIndex::GetBlockHeader() const if (HasSolution()) { header.nSolution = nSolution; } else { + MetricsIncrementCounter("zcashd.debug.blocktree.trimmed_equihash_read_dbindex"); CDiskBlockIndex dbindex; if (!pblocktree->ReadDiskBlockIndex(GetBlockHash(), dbindex)) { LogPrintf("%s: Failed to read index entry", __func__); diff --git a/src/chain.h b/src/chain.h index 21d9812ad..22de203d0 100644 --- a/src/chain.h +++ b/src/chain.h @@ -17,6 +17,8 @@ #include #include +#include + static const int SPROUT_VALUE_VERSION = 1001400; static const int SAPLING_VALUE_VERSION = 1010100; static const int CHAIN_HISTORY_ROOT_VERSION = 2010200; @@ -372,6 +374,7 @@ public: nBits = block.nBits; nNonce = block.nNonce; nSolution = block.nSolution; + MetricsIncrementCounter("zcashd.debug.memory.allocated_equihash_solutions"); } CDiskBlockPos GetBlockPos() const { diff --git a/src/txdb.cpp b/src/txdb.cpp index fab8987a5..65dc3e5da 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -17,6 +17,8 @@ #include +#include + using namespace std; // NOTE: Per issue #3277, do not use the prefix 'X' or 'x' as they were @@ -383,6 +385,7 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { } bool CBlockTreeDB::WriteBatchSync(const std::vector >& fileInfo, int nLastFile, const std::vector& blockinfo) { + MetricsIncrementCounter("zcashd.debug.blocktree.write_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); @@ -392,6 +395,7 @@ bool CBlockTreeDB::WriteBatchSync(const std::vector key = make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()); try { CDiskBlockIndex dbindex {*it, [this, &key]() { + MetricsIncrementCounter("zcashd.debug.blocktree.write_batch_read_dbindex"); // It can happen that the index entry is written, then the Equihash solution is cleared from memory, // then the index entry is rewritten. In that case we must read the solution from the old entry. CDiskBlockIndex dbindex_old; From fb66589022df4a0eff6ff244aa6b713c3a2bb2dd Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Thu, 17 Nov 2022 21:06:05 +0000 Subject: [PATCH 5/5] Declare `CBlockTreeDB::Read*` methods as `const` when they are trivially so. Signed-off-by: Daira Hopwood --- src/txdb.cpp | 16 ++++++++-------- src/txdb.h | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/txdb.cpp b/src/txdb.cpp index 65dc3e5da..e22533d0e 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -318,7 +318,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { } -bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { +bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) const { return Read(make_pair(DB_BLOCK_FILES, nFile), info); } @@ -329,12 +329,12 @@ bool CBlockTreeDB::WriteReindexing(bool fReindexing) { return Erase(DB_REINDEX_FLAG); } -bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { +bool CBlockTreeDB::ReadReindexing(bool &fReindexing) const { fReindexing = Exists(DB_REINDEX_FLAG); return true; } -bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { +bool CBlockTreeDB::ReadLastBlockFile(int &nFile) const { return Read(DB_LAST_BLOCK, nFile); } @@ -421,11 +421,11 @@ bool CBlockTreeDB::EraseBatchSync(const std::vector& blockin return WriteBatch(batch, true); } -bool CBlockTreeDB::ReadDiskBlockIndex(const uint256 &blockhash, CDiskBlockIndex &dbindex) { +bool CBlockTreeDB::ReadDiskBlockIndex(const uint256 &blockhash, CDiskBlockIndex &dbindex) const { return Read(make_pair(DB_BLOCK_INDEX, blockhash), dbindex); } -bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { +bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) const { return Read(make_pair(DB_TXINDEX, txid), pos); } @@ -514,7 +514,7 @@ bool CBlockTreeDB::ReadAddressIndex( return true; } -bool CBlockTreeDB::ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) { +bool CBlockTreeDB::ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) const { return Read(make_pair(DB_SPENTINDEX, key), value); } @@ -570,7 +570,7 @@ bool CBlockTreeDB::WriteTimestampBlockIndex(const CTimestampBlockIndexKey &block return WriteBatch(batch); } -bool CBlockTreeDB::ReadTimestampBlockIndex(const uint256 &hash, unsigned int <imestamp) +bool CBlockTreeDB::ReadTimestampBlockIndex(const uint256 &hash, unsigned int <imestamp) const { CTimestampBlockIndexValue(lts); if (!Read(std::make_pair(DB_BLOCKHASHINDEX, hash), lts)) @@ -585,7 +585,7 @@ bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0'); } -bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { +bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) const { char ch; if (!Read(std::make_pair(DB_FLAG, name), ch)) return false; diff --git a/src/txdb.h b/src/txdb.h index 96285a265..46bc694f8 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -119,12 +119,12 @@ private: public: bool WriteBatchSync(const std::vector >& fileInfo, int nLastFile, const std::vector& blockinfo); bool EraseBatchSync(const std::vector& blockinfo); - bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info); - bool ReadLastBlockFile(int &nFile); + bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info) const; + bool ReadLastBlockFile(int &nFile) const; bool WriteReindexing(bool fReindexing); - bool ReadReindexing(bool &fReindexing); - bool ReadDiskBlockIndex(const uint256 &blockhash, CDiskBlockIndex &dbindex); - bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos); + bool ReadReindexing(bool &fReindexing) const; + bool ReadDiskBlockIndex(const uint256 &blockhash, CDiskBlockIndex &dbindex) const; + bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) const; bool WriteTxIndex(const std::vector > &vect); // START insightexplorer @@ -133,18 +133,18 @@ public: bool WriteAddressIndex(const std::vector &vect); bool EraseAddressIndex(const std::vector &vect); bool ReadAddressIndex(uint160 addressHash, int type, std::vector &addressIndex, int start = 0, int end = 0); - bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); + bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) const; bool UpdateSpentIndex(const std::vector &vect); bool WriteTimestampIndex(const CTimestampIndexKey ×tampIndex); bool ReadTimestampIndex(unsigned int high, unsigned int low, const bool fActiveOnly, std::vector > &vect); bool WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, const CTimestampBlockIndexValue &logicalts); - bool ReadTimestampBlockIndex(const uint256 &hash, unsigned int &logicalTS); + bool ReadTimestampBlockIndex(const uint256 &hash, unsigned int &logicalTS) const; // END insightexplorer bool WriteFlag(const std::string &name, bool fValue); - bool ReadFlag(const std::string &name, bool &fValue); + bool ReadFlag(const std::string &name, bool &fValue) const; bool LoadBlockIndexGuts( std::function insertBlockIndex, const CChainParams& chainParams);