Skip to content

Commit 59ac8ba

Browse files
author
MacroFake
committed
Merge bitcoin/bitcoin#24804: Sanity assert GetAncestor() != nullptr where appropriate
308dd2e Sanity assert GetAncestor() != nullptr where appropriate (Adam Jonas) Pull request description: Re-opening #17232. I have rebased the PR and addressed jonatack's nit suggestions. Add sanity asserts for return value of `CBlockIndex::GetAncestor()` where appropriate. In validation.cpp `CheckSequenceLocks`, check the return value of `tip->GetAncestor(maxInputHeight)` stored into `lp->maxInputBlock`. If it ever returns `nullptr` because the ancestor isn't found, it's going to be a bad bug to keep going, since a `LockPoints` object with the `maxInputBlock` member set to `nullptr` signifies no relative lock time. In the other places, the added asserts would prevent accidental dereferencing of a null pointer which is undefined behavior. Co-Authored-By: Adam Jonas <[email protected]> Co-Authored-By: danra <[email protected]> ACKs for top commit: jonatack: ACK 308dd2e Tree-SHA512: 5bfdaab1499607ae2c3cd3e2e9e8c37850bfd0e327e680f4e36c81f9c6d98a543af78ecfac1ab0e06325d264412615a04d52005875780c7db2a4d81bd2d2259a
2 parents 77a9997 + 308dd2e commit 59ac8ba

File tree

5 files changed

+39
-28
lines changed

5 files changed

+39
-28
lines changed

src/consensus/tx_verify.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <consensus/validation.h>
1212
#include <primitives/transaction.h>
1313
#include <script/interpreter.h>
14+
#include <util/check.h>
1415
#include <util/moneystr.h>
1516

1617
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
@@ -74,7 +75,7 @@ std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags
7475
int nCoinHeight = prevHeights[txinIndex];
7576

7677
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) {
77-
int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast();
78+
const int64_t nCoinTime{Assert(block.GetAncestor(std::max(nCoinHeight - 1, 0)))->GetMedianTimePast()};
7879
// NOTE: Subtract 1 to maintain nLockTime semantics
7980
// BIP 68 relative lock times have the semantics of calculating
8081
// the first block or time at which the transaction would be

src/rpc/blockchain.cpp

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,9 +1613,9 @@ static RPCHelpMan getchaintxstats()
16131613
}
16141614
}
16151615

1616-
const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount);
1617-
int nTimeDiff = pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast();
1618-
int nTxDiff = pindex->nChainTx - pindexPast->nChainTx;
1616+
const CBlockIndex& past_block{*CHECK_NONFATAL(pindex->GetAncestor(pindex->nHeight - blockcount))};
1617+
const int64_t nTimeDiff{pindex->GetMedianTimePast() - past_block.GetMedianTimePast()};
1618+
const int nTxDiff = pindex->nChainTx - past_block.nChainTx;
16191619

16201620
UniValue ret(UniValue::VOBJ);
16211621
ret.pushKV("time", (int64_t)pindex->nTime);
@@ -1756,8 +1756,7 @@ static RPCHelpMan getblockstats()
17561756
{
17571757
ChainstateManager& chainman = EnsureAnyChainman(request.context);
17581758
LOCK(cs_main);
1759-
const CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
1760-
CHECK_NONFATAL(pindex != nullptr);
1759+
const CBlockIndex& pindex{*CHECK_NONFATAL(ParseHashOrHeight(request.params[0], chainman))};
17611760

17621761
std::set<std::string> stats;
17631762
if (!request.params[1].isNull()) {
@@ -1768,8 +1767,8 @@ static RPCHelpMan getblockstats()
17681767
}
17691768
}
17701769

1771-
const CBlock block = GetBlockChecked(chainman.m_blockman, pindex);
1772-
const CBlockUndo blockUndo = GetUndoChecked(chainman.m_blockman, pindex);
1770+
const CBlock& block = GetBlockChecked(chainman.m_blockman, &pindex);
1771+
const CBlockUndo& blockUndo = GetUndoChecked(chainman.m_blockman, &pindex);
17731772

17741773
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
17751774
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
@@ -1887,25 +1886,25 @@ static RPCHelpMan getblockstats()
18871886
ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0);
18881887
ret_all.pushKV("avgfeerate", total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0); // Unit: sat/vbyte
18891888
ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);
1890-
ret_all.pushKV("blockhash", pindex->GetBlockHash().GetHex());
1889+
ret_all.pushKV("blockhash", pindex.GetBlockHash().GetHex());
18911890
ret_all.pushKV("feerate_percentiles", feerates_res);
1892-
ret_all.pushKV("height", (int64_t)pindex->nHeight);
1891+
ret_all.pushKV("height", (int64_t)pindex.nHeight);
18931892
ret_all.pushKV("ins", inputs);
18941893
ret_all.pushKV("maxfee", maxfee);
18951894
ret_all.pushKV("maxfeerate", maxfeerate);
18961895
ret_all.pushKV("maxtxsize", maxtxsize);
18971896
ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));
1898-
ret_all.pushKV("mediantime", pindex->GetMedianTimePast());
1897+
ret_all.pushKV("mediantime", pindex.GetMedianTimePast());
18991898
ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array));
19001899
ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee);
19011900
ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);
19021901
ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize);
19031902
ret_all.pushKV("outs", outputs);
1904-
ret_all.pushKV("subsidy", GetBlockSubsidy(pindex->nHeight, Params().GetConsensus()));
1903+
ret_all.pushKV("subsidy", GetBlockSubsidy(pindex.nHeight, Params().GetConsensus()));
19051904
ret_all.pushKV("swtotal_size", swtotal_size);
19061905
ret_all.pushKV("swtotal_weight", swtotal_weight);
19071906
ret_all.pushKV("swtxs", swtxs);
1908-
ret_all.pushKV("time", pindex->GetBlockTime());
1907+
ret_all.pushKV("time", pindex.GetBlockTime());
19091908
ret_all.pushKV("total_out", total_out);
19101909
ret_all.pushKV("total_size", total_size);
19111910
ret_all.pushKV("total_weight", total_weight);

src/test/miner_tests.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -405,16 +405,18 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
405405
BOOST_CHECK(CheckFinalTxAtTip(m_node.chainman->ActiveChain().Tip(), CTransaction{tx})); // Locktime passes
406406
BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail
407407

408-
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
409-
m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
410-
408+
const int SEQUENCE_LOCK_TIME = 512; // Sequence locks pass 512 seconds later
409+
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; ++i)
410+
m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += SEQUENCE_LOCK_TIME; // Trick the MedianTimePast
411411
{
412412
CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
413-
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip))); // Sequence locks pass 512 seconds later
413+
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip)));
414414
}
415415

416-
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
417-
m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
416+
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; ++i) {
417+
CBlockIndex* ancestor{Assert(m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i))};
418+
ancestor->nTime -= SEQUENCE_LOCK_TIME; // undo tricked MTP
419+
}
418420

419421
// absolute height locked
420422
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
@@ -459,9 +461,11 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
459461
// but relative locked txs will if inconsistently added to mempool.
460462
// For now these will still generate a valid template until BIP68 soft fork
461463
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3U);
462-
// However if we advance height by 1 and time by 512, all of them should be mined
463-
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
464-
m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
464+
// However if we advance height by 1 and time by SEQUENCE_LOCK_TIME, all of them should be mined
465+
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; ++i) {
466+
CBlockIndex* ancestor{Assert(m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i))};
467+
ancestor->nTime += SEQUENCE_LOCK_TIME; // Trick the MedianTimePast
468+
}
465469
m_node.chainman->ActiveChain().Tip()->nHeight++;
466470
SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1);
467471

src/validation.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,12 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
250250
maxInputHeight = std::max(maxInputHeight, height);
251251
}
252252
}
253-
lp->maxInputBlock = tip->GetAncestor(maxInputHeight);
253+
// tip->GetAncestor(maxInputHeight) should never return a nullptr
254+
// because maxInputHeight is always less than the tip height.
255+
// It would, however, be a bad bug to continue execution, since a
256+
// LockPoints object with the maxInputBlock member set to nullptr
257+
// signifies no relative lock time.
258+
lp->maxInputBlock = Assert(tip->GetAncestor(maxInputHeight));
254259
}
255260
}
256261
return EvaluateSequenceLocks(index, lockPair);
@@ -4108,10 +4113,11 @@ bool CChainState::ReplayBlocks()
41084113
// Roll forward from the forking point to the new tip.
41094114
int nForkHeight = pindexFork ? pindexFork->nHeight : 0;
41104115
for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) {
4111-
const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight);
4112-
LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight);
4116+
const CBlockIndex& pindex{*Assert(pindexNew->GetAncestor(nHeight))};
4117+
4118+
LogPrintf("Rolling forward %s (%i)\n", pindex.GetBlockHash().ToString(), nHeight);
41134119
uiInterface.ShowProgress(_("Replaying blocks…").translated, (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false);
4114-
if (!RollforwardBlock(pindex, cache)) return false;
4120+
if (!RollforwardBlock(&pindex, cache)) return false;
41154121
}
41164122

41174123
cache.SetBestBlock(pindexNew->GetBlockHash());

src/versionbits.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5-
#include <versionbits.h>
65
#include <consensus/params.h>
6+
#include <util/check.h>
7+
#include <versionbits.h>
78

89
ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
910
{
@@ -158,7 +159,7 @@ int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex*
158159
// if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and
159160
// if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period.
160161
// The parent of the genesis block is represented by nullptr.
161-
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
162+
pindexPrev = Assert(pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)));
162163

163164
const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
164165

0 commit comments

Comments
 (0)