Skip to content

Commit

Permalink
validation: CheckNewBlock()
Browse files Browse the repository at this point in the history
  • Loading branch information
Sjors committed Dec 24, 2024
1 parent ffe77e1 commit aeb532b
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 0 deletions.
92 changes: 92 additions & 0 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4582,6 +4582,98 @@ bool ChainstateManager::AcceptBlock(const std::shared_ptr<const CBlock>& pblock,
return true;
}

void ChainstateManager::CheckNewBlock(const std::shared_ptr<const CBlock>& block, const bool check_pow, const unsigned int multiplier)
{
AssertLockNotHeld(cs_main);

{
// TODO: cleanup the ret mess
bool ret{true};
BlockValidationState state;

// CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race.
// Therefore, the following critical section must include the CheckBlock() call as well.
LOCK(cs_main);

CBlockIndex* tip = ActiveTip();

if (block->hashPrevBlock != *tip->phashBlock) {
state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "inconclusive-not-best-prevblk", "block not on tip");
ret = false;
}

// Check the actual proof-of-work. The nBits value is checked by
// ContextualCheckBlock below later as part of AcceptBlock.
const CChainParams& params{GetParams()};
if (ret && check_pow) {
ret = CheckWeakProofOfWork(block->GetHash(), block->nBits, multiplier, params.GetConsensus());
if (!ret) {
if (multiplier == 1) {
state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "high-hash", "proof of work failed");
} else {
state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "high-weak-hash", "weak proof of work failed");
}
}
}
if (ret) {
ret = CheckBlock(*block, state, GetConsensus(), /*fCheckPow=*/false);
}
if (ret) {
/**
* At this point ProcessNewBlock would call AcceptBlock(), but we
* don't want to store the block or its header. Run individual checks
* instead.
*
* - don't run AcceptBlockHeader(): it doesn't check anything we care about
* - we already ran CheckBlock()
* - do run ContextualCheckBlockHeader()
* - do run ContextualCheckBlock()
*/

if (!ContextualCheckBlockHeader(*block, state, ActiveChainstate().m_blockman, ActiveChainstate().m_chainman, tip)) {
ret = false;
}

if (ret && !ContextualCheckBlock(*block, state, *this, tip)) {
ret = false;
}

if (ret) {
CBlockIndex index{*block};
uint256 block_hash(block->GetHash());
index.pprev = tip;
index.nHeight = tip->nHeight + 1;
index.phashBlock = &block_hash;

// We don't want ConnectBlock to update the actual chainstate, so create
// a cache on top of it.
CCoinsViewCache tipView(&ActiveChainstate().CoinsTip());
CCoinsView blockCoins;
CCoinsViewCache view(&blockCoins);
view.SetBackend(tipView);

// Set fJustCheck to true in order to update, and not clear, validation caches.
ret = ActiveChainstate().ConnectBlock(*block, state, &index, view, /*fJustCheck=*/true);

if (!state.IsValid()) {
ret = false;
}

// TODO: (optionally) loop through the block again and add useful transactions to the mempool?
}

}
if (!ret) {
if (m_options.signals) {
m_options.signals->BlockChecked(*block, state);
}
return;
}
}

return;
}

bool ChainstateManager::ProcessNewBlock(const std::shared_ptr<const CBlock>& block, bool force_processing, bool min_pow_checked, bool* new_block)
{
AssertLockNotHeld(cs_main);
Expand Down
29 changes: 29 additions & 0 deletions src/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,35 @@ class ChainstateManager
FlatFilePos* dbp = nullptr,
std::multimap<uint256, FlatFilePos>* blocks_with_unknown_parent = nullptr);

/**
* Verify a block. Optionally skips proof-of-work check or use nBits multiplier.
*
* TODO: avoid this convoluted mess?
*
* If you want to *possibly* get feedback on whether block is valid, you must
* install a CValidationInterface (see validationinterface.h) - this will have
* its BlockChecked method called whenever *any* block completes validation.
*
* Note that we guarantee that either the proof-of-work is valid on block, or
* (and possibly also) BlockChecked will have been called.
*
* May not be called in a validationinterface callback.
*
* TODO:
* - option to add good transactions to the mempool
* - option to jail non-standard transactions:
* https://delvingbitcoin.org/t/second-look-at-weak-blocks/805
* - optionally skip PoW check
* - optionally provide higher target
*
* @param[in] block The block we want to process.
* @param[in] check_pow Perform proof-of-work check, nbits in the header
* is always checked.
* @param[in] multiplier nBits multiplier, does not apply to the nbits
* header value check.
*/
void CheckNewBlock(const std::shared_ptr<const CBlock>& block, const bool check_pow = true, const unsigned int multiplier = 1) LOCKS_EXCLUDED(cs_main);

/**
* Process an incoming block. This only returns after the best known valid
* block is made active. Note that it does not, however, guarantee that the
Expand Down

0 comments on commit aeb532b

Please sign in to comment.