mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-10 17:17:14 +01:00
Sanity assert GetAncestor() != nullptr where appropriate
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: Aurèle Oulès <aurele@oules.com> Co-Authored-By: danra <danra@users.noreply.github.com>
This commit is contained in:
parent
b1c5991eeb
commit
308dd2e93e
5 changed files with 39 additions and 28 deletions
|
@ -11,6 +11,7 @@
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <script/interpreter.h>
|
#include <script/interpreter.h>
|
||||||
|
#include <util/check.h>
|
||||||
#include <util/moneystr.h>
|
#include <util/moneystr.h>
|
||||||
|
|
||||||
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
|
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
|
||||||
int nCoinHeight = prevHeights[txinIndex];
|
int nCoinHeight = prevHeights[txinIndex];
|
||||||
|
|
||||||
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) {
|
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) {
|
||||||
int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast();
|
const int64_t nCoinTime{Assert(block.GetAncestor(std::max(nCoinHeight - 1, 0)))->GetMedianTimePast()};
|
||||||
// NOTE: Subtract 1 to maintain nLockTime semantics
|
// NOTE: Subtract 1 to maintain nLockTime semantics
|
||||||
// BIP 68 relative lock times have the semantics of calculating
|
// BIP 68 relative lock times have the semantics of calculating
|
||||||
// the first block or time at which the transaction would be
|
// the first block or time at which the transaction would be
|
||||||
|
|
|
@ -1618,9 +1618,9 @@ static RPCHelpMan getchaintxstats()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount);
|
const CBlockIndex& past_block{*CHECK_NONFATAL(pindex->GetAncestor(pindex->nHeight - blockcount))};
|
||||||
int nTimeDiff = pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast();
|
const int64_t nTimeDiff{pindex->GetMedianTimePast() - past_block.GetMedianTimePast()};
|
||||||
int nTxDiff = pindex->nChainTx - pindexPast->nChainTx;
|
const int nTxDiff = pindex->nChainTx - past_block.nChainTx;
|
||||||
|
|
||||||
UniValue ret(UniValue::VOBJ);
|
UniValue ret(UniValue::VOBJ);
|
||||||
ret.pushKV("time", (int64_t)pindex->nTime);
|
ret.pushKV("time", (int64_t)pindex->nTime);
|
||||||
|
@ -1761,8 +1761,7 @@ static RPCHelpMan getblockstats()
|
||||||
{
|
{
|
||||||
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
const CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
|
const CBlockIndex& pindex{*CHECK_NONFATAL(ParseHashOrHeight(request.params[0], chainman))};
|
||||||
CHECK_NONFATAL(pindex != nullptr);
|
|
||||||
|
|
||||||
std::set<std::string> stats;
|
std::set<std::string> stats;
|
||||||
if (!request.params[1].isNull()) {
|
if (!request.params[1].isNull()) {
|
||||||
|
@ -1773,8 +1772,8 @@ static RPCHelpMan getblockstats()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CBlock block = GetBlockChecked(chainman.m_blockman, pindex);
|
const CBlock& block = GetBlockChecked(chainman.m_blockman, &pindex);
|
||||||
const CBlockUndo blockUndo = GetUndoChecked(chainman.m_blockman, pindex);
|
const CBlockUndo& blockUndo = GetUndoChecked(chainman.m_blockman, &pindex);
|
||||||
|
|
||||||
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
|
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
|
||||||
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
|
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
|
||||||
|
@ -1892,25 +1891,25 @@ static RPCHelpMan getblockstats()
|
||||||
ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0);
|
ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0);
|
||||||
ret_all.pushKV("avgfeerate", total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0); // Unit: sat/vbyte
|
ret_all.pushKV("avgfeerate", total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0); // Unit: sat/vbyte
|
||||||
ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);
|
ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);
|
||||||
ret_all.pushKV("blockhash", pindex->GetBlockHash().GetHex());
|
ret_all.pushKV("blockhash", pindex.GetBlockHash().GetHex());
|
||||||
ret_all.pushKV("feerate_percentiles", feerates_res);
|
ret_all.pushKV("feerate_percentiles", feerates_res);
|
||||||
ret_all.pushKV("height", (int64_t)pindex->nHeight);
|
ret_all.pushKV("height", (int64_t)pindex.nHeight);
|
||||||
ret_all.pushKV("ins", inputs);
|
ret_all.pushKV("ins", inputs);
|
||||||
ret_all.pushKV("maxfee", maxfee);
|
ret_all.pushKV("maxfee", maxfee);
|
||||||
ret_all.pushKV("maxfeerate", maxfeerate);
|
ret_all.pushKV("maxfeerate", maxfeerate);
|
||||||
ret_all.pushKV("maxtxsize", maxtxsize);
|
ret_all.pushKV("maxtxsize", maxtxsize);
|
||||||
ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));
|
ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));
|
||||||
ret_all.pushKV("mediantime", pindex->GetMedianTimePast());
|
ret_all.pushKV("mediantime", pindex.GetMedianTimePast());
|
||||||
ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array));
|
ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array));
|
||||||
ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee);
|
ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee);
|
||||||
ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);
|
ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);
|
||||||
ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize);
|
ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize);
|
||||||
ret_all.pushKV("outs", outputs);
|
ret_all.pushKV("outs", outputs);
|
||||||
ret_all.pushKV("subsidy", GetBlockSubsidy(pindex->nHeight, Params().GetConsensus()));
|
ret_all.pushKV("subsidy", GetBlockSubsidy(pindex.nHeight, Params().GetConsensus()));
|
||||||
ret_all.pushKV("swtotal_size", swtotal_size);
|
ret_all.pushKV("swtotal_size", swtotal_size);
|
||||||
ret_all.pushKV("swtotal_weight", swtotal_weight);
|
ret_all.pushKV("swtotal_weight", swtotal_weight);
|
||||||
ret_all.pushKV("swtxs", swtxs);
|
ret_all.pushKV("swtxs", swtxs);
|
||||||
ret_all.pushKV("time", pindex->GetBlockTime());
|
ret_all.pushKV("time", pindex.GetBlockTime());
|
||||||
ret_all.pushKV("total_out", total_out);
|
ret_all.pushKV("total_out", total_out);
|
||||||
ret_all.pushKV("total_size", total_size);
|
ret_all.pushKV("total_size", total_size);
|
||||||
ret_all.pushKV("total_weight", total_weight);
|
ret_all.pushKV("total_weight", total_weight);
|
||||||
|
|
|
@ -446,16 +446,18 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
BOOST_CHECK(CheckFinalTxAtTip(m_node.chainman->ActiveChain().Tip(), CTransaction{tx})); // Locktime passes
|
BOOST_CHECK(CheckFinalTxAtTip(m_node.chainman->ActiveChain().Tip(), CTransaction{tx})); // Locktime passes
|
||||||
BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail
|
BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail
|
||||||
|
|
||||||
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
|
const int SEQUENCE_LOCK_TIME = 512; // Sequence locks pass 512 seconds later
|
||||||
m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
|
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; ++i)
|
||||||
|
m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += SEQUENCE_LOCK_TIME; // Trick the MedianTimePast
|
||||||
{
|
{
|
||||||
CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
|
CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
|
||||||
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip))); // Sequence locks pass 512 seconds later
|
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
|
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; ++i) {
|
||||||
m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
|
CBlockIndex* ancestor{Assert(m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i))};
|
||||||
|
ancestor->nTime -= SEQUENCE_LOCK_TIME; // undo tricked MTP
|
||||||
|
}
|
||||||
|
|
||||||
// absolute height locked
|
// absolute height locked
|
||||||
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
|
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
|
||||||
|
@ -500,9 +502,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
// but relative locked txs will if inconsistently added to mempool.
|
// but relative locked txs will if inconsistently added to mempool.
|
||||||
// For now these will still generate a valid template until BIP68 soft fork
|
// For now these will still generate a valid template until BIP68 soft fork
|
||||||
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3U);
|
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3U);
|
||||||
// However if we advance height by 1 and time by 512, all of them should be mined
|
// However if we advance height by 1 and time by SEQUENCE_LOCK_TIME, all of them should be mined
|
||||||
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
|
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; ++i) {
|
||||||
m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
|
CBlockIndex* ancestor{Assert(m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i))};
|
||||||
|
ancestor->nTime += SEQUENCE_LOCK_TIME; // Trick the MedianTimePast
|
||||||
|
}
|
||||||
m_node.chainman->ActiveChain().Tip()->nHeight++;
|
m_node.chainman->ActiveChain().Tip()->nHeight++;
|
||||||
SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1);
|
SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1);
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,12 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
|
||||||
maxInputHeight = std::max(maxInputHeight, height);
|
maxInputHeight = std::max(maxInputHeight, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lp->maxInputBlock = tip->GetAncestor(maxInputHeight);
|
// tip->GetAncestor(maxInputHeight) should never return a nullptr
|
||||||
|
// because maxInputHeight is always less than the tip height.
|
||||||
|
// It would, however, be a bad bug to continue execution, since a
|
||||||
|
// LockPoints object with the maxInputBlock member set to nullptr
|
||||||
|
// signifies no relative lock time.
|
||||||
|
lp->maxInputBlock = Assert(tip->GetAncestor(maxInputHeight));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return EvaluateSequenceLocks(index, lockPair);
|
return EvaluateSequenceLocks(index, lockPair);
|
||||||
|
@ -4077,10 +4082,11 @@ bool CChainState::ReplayBlocks()
|
||||||
// Roll forward from the forking point to the new tip.
|
// Roll forward from the forking point to the new tip.
|
||||||
int nForkHeight = pindexFork ? pindexFork->nHeight : 0;
|
int nForkHeight = pindexFork ? pindexFork->nHeight : 0;
|
||||||
for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) {
|
for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) {
|
||||||
const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight);
|
const CBlockIndex& pindex{*Assert(pindexNew->GetAncestor(nHeight))};
|
||||||
LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight);
|
|
||||||
|
LogPrintf("Rolling forward %s (%i)\n", pindex.GetBlockHash().ToString(), nHeight);
|
||||||
uiInterface.ShowProgress(_("Replaying blocks…").translated, (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false);
|
uiInterface.ShowProgress(_("Replaying blocks…").translated, (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false);
|
||||||
if (!RollforwardBlock(pindex, cache)) return false;
|
if (!RollforwardBlock(&pindex, cache)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.SetBestBlock(pindexNew->GetBlockHash());
|
cache.SetBestBlock(pindexNew->GetBlockHash());
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include <versionbits.h>
|
|
||||||
#include <consensus/params.h>
|
#include <consensus/params.h>
|
||||||
|
#include <util/check.h>
|
||||||
|
#include <versionbits.h>
|
||||||
|
|
||||||
ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
|
ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
|
||||||
{
|
{
|
||||||
|
@ -158,7 +159,7 @@ int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex*
|
||||||
// if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and
|
// if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and
|
||||||
// if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period.
|
// if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period.
|
||||||
// The parent of the genesis block is represented by nullptr.
|
// The parent of the genesis block is represented by nullptr.
|
||||||
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
|
pindexPrev = Assert(pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)));
|
||||||
|
|
||||||
const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
|
const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue