Skip to content

Commit 591dded

Browse files
committed
Add caching for block hashes to CBlock
A new block's hash is recomputed multiple times as the block moves through the acceptance pipeline. This enables caching of the block hashes to improve performance.
1 parent c402c26 commit 591dded

File tree

2 files changed

+62
-33
lines changed

2 files changed

+62
-33
lines changed

src/main.cpp

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,7 +1155,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
11551155
}
11561156

11571157
// Update the tx's hashBlock
1158-
hashBlock = pblock->GetHash();
1158+
hashBlock = pblock->GetHash(true);
11591159

11601160
// Locate the transaction
11611161
for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++)
@@ -1577,7 +1577,7 @@ int CTxIndex::GetDepthInMainChain() const
15771577
if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false))
15781578
return 0;
15791579
// Find the block in the index
1580-
BlockMap::iterator mi = mapBlockIndex.find(block.GetHash());
1580+
BlockMap::iterator mi = mapBlockIndex.find(block.GetHash(true));
15811581
if (mi == mapBlockIndex.end())
15821582
return 0;
15831583
CBlockIndex* pindex = (*mi).second;
@@ -1603,7 +1603,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock)
16031603
{
16041604
CBlock block;
16051605
if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
1606-
hashBlock = block.GetHash();
1606+
hashBlock = block.GetHash(true);
16071607
return true;
16081608
}
16091609
}
@@ -1629,7 +1629,7 @@ bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions)
16291629
}
16301630
if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions))
16311631
return false;
1632-
if (GetHash() != pindex->GetBlockHash())
1632+
if (GetHash(true) != pindex->GetBlockHash())
16331633
return error("CBlock::ReadFromDisk() : GetHash() doesn't match index");
16341634
return true;
16351635
}
@@ -2949,7 +2949,7 @@ bool ReorganizeChain(CTxDB& txdb, unsigned &cnt_dis, unsigned &cnt_con, CBlock &
29492949
//assert(pindexBest || hashBestChain == pindexBest->GetBlockHash());
29502950
//assert(nBestHeight = pindexBest->nHeight && nBestChainTrust == pindexBest->nChainTrust);
29512951
//assert(!pindexBest->pnext);
2952-
assert(pindexNew->GetBlockHash()==blockNew.GetHash());
2952+
assert(pindexNew->GetBlockHash()==blockNew.GetHash(true));
29532953
/* note: it was already determined that this chain is better than current best */
29542954
/* assert(pindexNew->nChainTrust > nBestChainTrust); but may be overriden by command */
29552955
assert( !pindexGenesisBlock == !pindexBest );
@@ -3025,16 +3025,16 @@ bool ReorganizeChain(CTxDB& txdb, unsigned &cnt_dis, unsigned &cnt_con, CBlock &
30253025
{
30263026
if (!block.ReadFromDisk(pindex))
30273027
return error("ReorganizeChain: ReadFromDisk for connect failed");
3028-
assert(pindex->GetBlockHash()==block.GetHash());
3028+
assert(pindex->GetBlockHash()==block.GetHash(true));
30293029
}
30303030
else
30313031
{
30323032
assert(pindex==pindexNew);
3033-
assert(pindexNew->GetBlockHash()==block.GetHash());
3034-
assert(pindexNew->GetBlockHash()==blockNew.GetHash());
3033+
assert(pindexNew->GetBlockHash()==block.GetHash(true));
3034+
assert(pindexNew->GetBlockHash()==blockNew.GetHash(true));
30353035
}
30363036

3037-
uint256 hash = block.GetHash();
3037+
uint256 hash = block.GetHash(true);
30383038
arith_uint256 nBestBlockTrust;
30393039

30403040
if (fDebug) LogPrintf("ReorganizeChain: connect %s",hash.ToString());
@@ -3053,7 +3053,7 @@ bool ReorganizeChain(CTxDB& txdb, unsigned &cnt_dis, unsigned &cnt_con, CBlock &
30533053
}
30543054
else
30553055
{
3056-
assert(pindex->GetBlockHash()==block.GetHash());
3056+
assert(pindex->GetBlockHash()==block.GetHash(true));
30573057
assert(pindex->pprev == pindexBest);
30583058
if (!block.ConnectBlock(txdb, pindex, false, false))
30593059
{
@@ -3218,7 +3218,7 @@ bool CTransaction::GetCoinAge(CTxDB& txdb, uint64_t& nCoinAge) const
32183218
bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const uint256& hashProof)
32193219
{
32203220
// Check for duplicate
3221-
uint256 hash = GetHash();
3221+
uint256 hash = GetHash(true);
32223222
if (mapBlockIndex.count(hash))
32233223
return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str());
32243224

@@ -3291,7 +3291,7 @@ bool CBlock::CheckBlock(std::string sCaller, int height1, int64_t Mint, bool fCh
32913291
{
32923292
// Allow the genesis block to pass.
32933293
if(hashPrevBlock.IsNull() &&
3294-
GetHash() == (fTestNet ? hashGenesisBlockTestNet : hashGenesisBlock))
3294+
GetHash(true) == (fTestNet ? hashGenesisBlockTestNet : hashGenesisBlock))
32953295
return true;
32963296

32973297
// These are checks that are independent of context
@@ -3307,7 +3307,7 @@ bool CBlock::CheckBlock(std::string sCaller, int height1, int64_t Mint, bool fCh
33073307
}
33083308

33093309
// Check proof of work matches claimed amount
3310-
if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetPoWHash(), nBits))
3310+
if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(true), nBits))
33113311
return DoS(50, error("CheckBlock[] : proof of work failed"));
33123312

33133313
//Reject blocks with diff that has grown to an extrordinary level (should never happen)
@@ -3441,7 +3441,7 @@ bool CBlock::AcceptBlock(bool generated_by_me)
34413441
return DoS(100, error("AcceptBlock() : reject unknown block version %d", nVersion));
34423442

34433443
// Check for duplicate
3444-
uint256 hash = GetHash();
3444+
uint256 hash = GetHash(true);
34453445
if (mapBlockIndex.count(hash))
34463446
return error("AcceptBlock() : block already in mapBlockIndex");
34473447

@@ -3544,7 +3544,7 @@ bool CBlock::AcceptBlock(bool generated_by_me)
35443544
// PoW is checked in CheckBlock[]
35453545
if (IsProofOfWork())
35463546
{
3547-
hashProof = GetPoWHash();
3547+
hashProof = GetHash(true);
35483548
}
35493549

35503550
//Grandfather
@@ -3717,7 +3717,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, bool generated_by_me)
37173717
AssertLockHeld(cs_main);
37183718

37193719
// Check for duplicate
3720-
uint256 hash = pblock->GetHash();
3720+
uint256 hash = pblock->GetHash(true);
37213721
if (mapBlockIndex.count(hash))
37223722
return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().c_str());
37233723
if (mapOrphanBlocks.count(hash))
@@ -3865,7 +3865,7 @@ bool CBlock::CheckBlockSignature() const
38653865
return false;
38663866
if (vchBlockSig.empty())
38673867
return false;
3868-
return key.Verify(GetHash(), vchBlockSig);
3868+
return key.Verify(GetHash(true), vchBlockSig);
38693869
}
38703870

38713871
return false;
@@ -4012,7 +4012,7 @@ bool LoadBlockIndex(bool fAllowNew)
40124012
block.nNonce = !fTestNet ? 130208 : 22436;
40134013
LogPrintf("starting Genesis Check...");
40144014
// If genesis block hash does not match, then generate new genesis hash.
4015-
if (block.GetHash() != (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
4015+
if (block.GetHash(true) != (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
40164016
{
40174017
LogPrintf("Searching for genesis block...");
40184018
// This will figure out a valid hash and Nonce if you're
@@ -4021,7 +4021,7 @@ bool LoadBlockIndex(bool fAllowNew)
40214021
arith_uint256 thash;
40224022
while (true)
40234023
{
4024-
thash = UintToArith256(block.GetHash());
4024+
thash = UintToArith256(block.GetHash(true));
40254025
if (thash <= hashTarget)
40264026
break;
40274027
if ((block.nNonce & 0xFFF) == 0)
@@ -4037,7 +4037,7 @@ bool LoadBlockIndex(bool fAllowNew)
40374037
}
40384038
LogPrintf("block.nTime = %u ", block.nTime);
40394039
LogPrintf("block.nNonce = %u ", block.nNonce);
4040-
LogPrintf("block.GetHash = %s", block.GetHash().ToString());
4040+
LogPrintf("block.GetHash = %s", block.GetHash(true).ToString());
40414041
}
40424042

40434043

@@ -4048,7 +4048,7 @@ bool LoadBlockIndex(bool fAllowNew)
40484048
//GENESIS3: Official Merkle Root
40494049
uint256 merkle_root = uint256S("0x5109d5782a26e6a5a5eb76c7867f3e8ddae2bff026632c36afec5dc32ed8ce9f");
40504050
assert(block.hashMerkleRoot == merkle_root);
4051-
assert(block.GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet));
4051+
assert(block.GetHash(true) == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet));
40524052
assert(block.CheckBlock("LoadBlockIndex",1,10*COIN));
40534053

40544054
// Start new block file
@@ -4210,7 +4210,7 @@ void PrintBlockTree()
42104210
pindex->nHeight,
42114211
pindex->nFile,
42124212
pindex->nBlockPos,
4213-
block.GetHash().ToString().c_str(),
4213+
block.GetHash(true).ToString().c_str(),
42144214
block.nBits,
42154215
DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(),
42164216
FormatMoney(pindex->nMint).c_str(),
@@ -5025,7 +5025,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
50255025
CBlock block;
50265026
std::string acid = "";
50275027
vRecv >> block >> acid;
5028-
uint256 hashBlock = block.GetHash();
5028+
uint256 hashBlock = block.GetHash(true);
50295029

50305030
LogPrintf(" Received block %s; ", hashBlock.ToString());
50315031
if (fDebug10) block.print();

src/main.h

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,21 +1049,38 @@ class CBlockHeader
10491049
nTime = 0;
10501050
nBits = 0;
10511051
nNonce = 0;
1052+
m_hash_cache.SetNull();
10521053
}
10531054

10541055
bool IsNull() const
10551056
{
10561057
return (nBits == 0);
10571058
}
10581059

1059-
uint256 GetHash() const
1060+
uint256 GetHash(const bool use_cache = false) const
10601061
{
1061-
if (nVersion >= 11)
1062-
return Hash(BEGIN(nVersion), END(nBits));
1063-
else if (nVersion >= 7)
1064-
return Hash(BEGIN(nVersion), END(nNonce));
1065-
else
1066-
return GetPoWHash();
1062+
// The block hash cache field prevents repeated computations of the
1063+
// block's hash in the block acceptance pipeline. It's particularly
1064+
// effective for early blocks with expensive scrypt hashes. Dynamic
1065+
// caching isn't the prettiest solution, but it provides an interim
1066+
// performance advantage as we refactor legacy code.
1067+
//
1068+
// use_cache defaults to false to discourage use of the cache except
1069+
// in carefully chosen single-threaded scenarios. Avoid hash caching
1070+
// for block objects except where thread-safety is obvious and where
1071+
// performance improves significantly.
1072+
//
1073+
if (use_cache) {
1074+
if (!m_hash_cache.IsNull()) {
1075+
return m_hash_cache;
1076+
}
1077+
1078+
m_hash_cache = ComputeHash();
1079+
1080+
return m_hash_cache;
1081+
}
1082+
1083+
return ComputeHash();
10671084
}
10681085

10691086
uint256 GetPoWHash() const
@@ -1075,6 +1092,18 @@ class CBlockHeader
10751092
{
10761093
return (int64_t)nTime;
10771094
}
1095+
1096+
private:
1097+
mutable uint256 m_hash_cache;
1098+
1099+
uint256 ComputeHash() const
1100+
{
1101+
if (nVersion >= 7) {
1102+
return SerializeHash(*this);
1103+
}
1104+
1105+
return GetPoWHash();
1106+
}
10781107
};
10791108

10801109
class CBlock : public CBlockHeader
@@ -1199,9 +1228,9 @@ class CBlock : public CBlockHeader
11991228
unsigned int GetStakeEntropyBit() const
12001229
{
12011230
// Take last bit of block hash as entropy bit
1202-
unsigned int nEntropyBit = ((GetHash().GetUint64()) & 1llu);
1231+
unsigned int nEntropyBit = ((GetHash(true).GetUint64()) & 1llu);
12031232
if (fDebug && GetBoolArg("-printstakemodifier"))
1204-
LogPrintf("GetStakeEntropyBit: hashBlock=%s nEntropyBit=%u", GetHash().ToString(), nEntropyBit);
1233+
LogPrintf("GetStakeEntropyBit: hashBlock=%s nEntropyBit=%u", GetHash(true).ToString(), nEntropyBit);
12051234
return nEntropyBit;
12061235
}
12071236

@@ -1327,7 +1356,7 @@ class CBlock : public CBlockHeader
13271356
}
13281357

13291358
// Check the header
1330-
if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetPoWHash(), nBits))
1359+
if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetHash(true), nBits))
13311360
return error("CBlock::ReadFromDisk() : errors in block header");
13321361

13331362
return true;

0 commit comments

Comments
 (0)