Skip to content

Commit 57865eb

Browse files
committed
CDiskBlockIndex: rename GetBlockHash() to ConstructBlockHash()
and mark the inherited CBlockIndex#GetBlockHash public interface member as deleted, to disallow calling it in the derived CDiskBlockIndex class. Here is a failing test on master demonstrating the inconsistent behavior of the current design: calling the same inherited public interface functions on the same CDiskBlockIndex object should yield identical behavior. ```diff diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 6dc522b..dac3840f32 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -240,6 +240,15 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup) const CBlockIndex* tip = chainman.ActiveTip(); BOOST_CHECK_EQUAL(tip->nChainTx, au_data.nChainTx); + // CDiskBlockIndex "is a" CBlockIndex, as it publicly inherits from it. + // Test that calling the same inherited interface functions on the same + // object yields identical behavior. + CDiskBlockIndex index{tip}; + CBlockIndex *pB = &index; + CDiskBlockIndex *pD = &index; + BOOST_CHECK_EQUAL(pB->GetBlockHash(), pD->GetBlockHash()); + BOOST_CHECK_EQUAL(pB->ToString(), pD->ToString()); + ``` The GetBlockHash() test assertion only passes on master because the different methods invoked by the current design happen to return the same result. If one of the two is changed, it fails like the ToString() assertion does. Redefining inherited non-virtual functions is well-documented as incorrect design to avoid inconsistent behavior (see Scott Meyers, "Effective C++", Item 36). Class usage is confusing when the behavior depends on the pointer definition instead of the object definition (static binding happening where dynamic binding was expected). This can lead to unsuspected or hard-to-track bugs. Outside of critical hot spots, correctness usually comes before optimisation, but the current design dates back to main.cpp and it may possibly have been chosen to avoid the overhead of dynamic dispatch. This solution does the same: the class sizes are unchanged and no vptr or vtbl is added. There are better designs for doing this that use composition instead of inheritance or that separate the public interface from the private implementations. One example of the latter would be a non-virtual public interface that calls private virtual implementation methods, i.e. the Template pattern via the Non-Virtual Interface (NVI) idiom.
1 parent 99e8ec8 commit 57865eb

File tree

3 files changed

+4
-3
lines changed

3 files changed

+4
-3
lines changed

src/chain.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ class CDiskBlockIndex : public CBlockIndex
403403
READWRITE(obj.nNonce);
404404
}
405405

406-
uint256 GetBlockHash() const
406+
uint256 ConstructBlockHash() const
407407
{
408408
CBlockHeader block;
409409
block.nVersion = nVersion;
@@ -415,6 +415,7 @@ class CDiskBlockIndex : public CBlockIndex
415415
return block.GetHash();
416416
}
417417

418+
uint256 GetBlockHash() = delete;
418419
std::string ToString() = delete;
419420
};
420421

src/test/fuzz/chain.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ FUZZ_TARGET(chain)
2323
disk_block_index->phashBlock = &zero;
2424
{
2525
LOCK(::cs_main);
26-
(void)disk_block_index->GetBlockHash();
26+
(void)disk_block_index->ConstructBlockHash();
2727
(void)disk_block_index->GetBlockPos();
2828
(void)disk_block_index->GetBlockTime();
2929
(void)disk_block_index->GetBlockTimeMax();

src/txdb.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
310310
CDiskBlockIndex diskindex;
311311
if (pcursor->GetValue(diskindex)) {
312312
// Construct block index object
313-
CBlockIndex* pindexNew = insertBlockIndex(diskindex.GetBlockHash());
313+
CBlockIndex* pindexNew = insertBlockIndex(diskindex.ConstructBlockHash());
314314
pindexNew->pprev = insertBlockIndex(diskindex.hashPrev);
315315
pindexNew->nHeight = diskindex.nHeight;
316316
pindexNew->nFile = diskindex.nFile;

0 commit comments

Comments
 (0)