-
Notifications
You must be signed in to change notification settings - Fork 38.7k
Return BlockValidationState from ProcessNewBlock if CheckBlock/AcceptBlock fails #17479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a9141c4
03668d2
89175a7
935b7c9
9f8a81b
17170a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1385,8 +1385,11 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB | |
| /** | ||
| * Handle invalid block rejection and consequent peer discouragement, maintain which | ||
| * peers announce compact blocks. | ||
| * Called both in case of cursory DoS checks failing (implying the peer is likely | ||
| * sending us bogus data) and after full validation of the block fails (which may | ||
| * be OK if it was sent over compact blocks). | ||
| */ | ||
| void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidationState& state) { | ||
| static void BlockChecked(const CBlock& block, const BlockValidationState& state, CConnman& connman) { | ||
| LOCK(cs_main); | ||
|
|
||
| const uint256 hash(block.GetHash()); | ||
|
|
@@ -1409,13 +1412,17 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidatio | |
| !::ChainstateActive().IsInitialBlockDownload() && | ||
| mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) { | ||
| if (it != mapBlockSource.end()) { | ||
| MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, m_connman); | ||
| MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, connman); | ||
| } | ||
| } | ||
| if (it != mapBlockSource.end()) | ||
| mapBlockSource.erase(it); | ||
| } | ||
|
|
||
| void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidationState& state) { | ||
| ::BlockChecked(block, state, m_connman); | ||
| } | ||
|
|
||
| ////////////////////////////////////////////////////////////////////////////// | ||
| // | ||
| // Messages | ||
|
|
@@ -2086,6 +2093,25 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * A block has been processed. Handle potential peer punishment and housekeeping. | ||
| */ | ||
| void static BlockProcessed(CNode& pfrom, CConnman& connman, CBlock& pblock, BlockValidationState& state, bool new_block) | ||
|
||
| { | ||
| if (!state.IsValid()) { | ||
jnewbery marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // The block failed anti-dos / mutation checks. Call BlockChecked() callback here. | ||
| // This clears the block from mapBlockSource. | ||
| BlockChecked(pblock, state, connman); | ||
| } else if (!new_block) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does this new check behaves with regards to write-failure, first-seen block ? In |
||
| // Block was valid but we've seen it before. Clear it from mapBlockSource. | ||
| LOCK(cs_main); | ||
| ::mapBlockSource.erase(pblock.GetHash()); | ||
| } else { | ||
| // Block is valid and we haven't seen it before. set nLastBlockTime for this peer. | ||
|
||
| pfrom.nLastBlockTime = GetTime(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Validation logic for compact filters request handling. | ||
| * | ||
|
|
@@ -3325,13 +3351,10 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty | |
| // we have a chain with at least nMinimumChainWork), and we ignore | ||
| // compact blocks with less work than our tip, it is safe to treat | ||
| // reconstructed compact blocks as having been requested. | ||
| m_chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); | ||
| if (fNewBlock) { | ||
| pfrom.nLastBlockTime = GetTime(); | ||
| } else { | ||
| LOCK(cs_main); | ||
| mapBlockSource.erase(pblock->GetHash()); | ||
| } | ||
| BlockValidationState dos_state; | ||
| m_chainman.ProcessNewBlock(chainparams, pblock, dos_state, /*fForceProcessing=*/true, &fNewBlock); | ||
| BlockProcessed(pfrom, m_connman, *pblock, dos_state, fNewBlock); | ||
|
|
||
| LOCK(cs_main); // hold cs_main for CBlockIndex::IsValid() | ||
| if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS)) { | ||
| // Clear download state for this block, which is in | ||
|
|
@@ -3415,13 +3438,9 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty | |
| // disk-space attacks), but this should be safe due to the | ||
| // protections in the compact block handler -- see related comment | ||
| // in compact block optimistic reconstruction handling. | ||
| m_chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); | ||
| if (fNewBlock) { | ||
| pfrom.nLastBlockTime = GetTime(); | ||
| } else { | ||
| LOCK(cs_main); | ||
| mapBlockSource.erase(pblock->GetHash()); | ||
| } | ||
| BlockValidationState dos_state; | ||
| m_chainman.ProcessNewBlock(chainparams, pblock, dos_state, /*fForceProcessing=*/true, &fNewBlock); | ||
| BlockProcessed(pfrom, m_connman, *pblock, dos_state, fNewBlock); | ||
| } | ||
| return; | ||
| } | ||
|
|
@@ -3478,13 +3497,9 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty | |
| mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true)); | ||
| } | ||
| bool fNewBlock = false; | ||
| m_chainman.ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock); | ||
| if (fNewBlock) { | ||
| pfrom.nLastBlockTime = GetTime(); | ||
| } else { | ||
| LOCK(cs_main); | ||
| mapBlockSource.erase(pblock->GetHash()); | ||
| } | ||
| BlockValidationState dos_state; | ||
| m_chainman.ProcessNewBlock(chainparams, pblock, dos_state, forceProcessing, &fNewBlock); | ||
| BlockProcessed(pfrom, m_connman, *pblock, dos_state, fNewBlock); | ||
| return; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3828,37 +3828,35 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block | |
| return true; | ||
| } | ||
|
|
||
| bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock) | ||
| bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, BlockValidationState& dos_state, bool fForceProcessing, bool* fNewBlock) | ||
| { | ||
| AssertLockNotHeld(cs_main); | ||
|
|
||
| { | ||
| CBlockIndex *pindex = nullptr; | ||
| if (fNewBlock) *fNewBlock = false; | ||
| 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); | ||
|
|
||
| // Ensure that CheckBlock() passes before calling AcceptBlock, as | ||
| // belt-and-suspenders. | ||
| bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus()); | ||
| bool ret = CheckBlock(*pblock, dos_state, chainparams.GetConsensus()); | ||
|
||
| if (ret) { | ||
| // Store to disk | ||
| ret = ::ChainstateActive().AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock); | ||
| ret = ::ChainstateActive().AcceptBlock(pblock, dos_state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock); | ||
| } | ||
| if (!ret) { | ||
| GetMainSignals().BlockChecked(*pblock, state); | ||
| return error("%s: AcceptBlock FAILED (%s)", __func__, state.ToString()); | ||
| return error("%s: AcceptBlock FAILED (%s)", __func__, dos_state.ToString()); | ||
| } | ||
| } | ||
|
|
||
| NotifyHeaderTip(); | ||
|
|
||
| BlockValidationState state; // Only used to report errors, not invalidity - ignore it | ||
| if (!::ChainstateActive().ActivateBestChain(state, chainparams, pblock)) | ||
| return error("%s: ActivateBestChain failed (%s)", __func__, state.ToString()); | ||
| BlockValidationState dummy_state; // Only used to report errors, not invalidity - ignore it | ||
|
||
| if (!::ChainstateActive().ActivateBestChain(dummy_state, chainparams, pblock)) | ||
| return error("%s: ActivateBestChain failed (%s)", __func__, dummy_state.ToString()); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -862,22 +862,34 @@ class ChainstateManager | |
| * block is made active. Note that it does not, however, guarantee that the | ||
| * specific block passed to it has been checked for validity! | ||
| * | ||
| * If you want to *possibly* get feedback on whether pblock is valid, you must | ||
| * install a CValidationInterface (see validationinterface.h) - this will have | ||
| * its BlockChecked method called whenever *any* block completes validation. | ||
| * Performs initial sanity checks using the provided BlockValidationState before | ||
| * connecting any block(s). If you want to *possibly* get feedback on whether | ||
| * pblock is valid beyond just cursory mutation/DoS checks, you must install | ||
| * a CValidationInterface (see validationinterface.h) - this will have its | ||
| * BlockChecked method called whenever *any* block completes validation (note | ||
| * that any invalidity returned via state will *not* also be provided via | ||
| * BlockChecked). There is, of course, no guarantee that any given block which | ||
| * is not a part of the eventual best chain will ever be checked. | ||
| * | ||
| * Note that we guarantee that either the proof-of-work is valid on pblock, or | ||
| * (and possibly also) BlockChecked will have been called. | ||
| * If the block pblock is built on is in our header tree, and pblock is a | ||
| * candidate for accepting to disk (either because it is a candidate for the | ||
| * best chain soon, or fForceProcessing is set), but pblock has been mutated, | ||
| * state is guaranteed to be some non-IsValid() state. | ||
| * | ||
| * May not be called in a | ||
| * validationinterface callback. | ||
| * If fForceProcessing is set (or fNewBlock returns true), and state.IsValid(), | ||
| * barring pruning and a desire to re-download a pruned block, there should | ||
| * never be any reason to re-ProcessNewBlock any block with the same hash. | ||
| * | ||
| * May not be called in a validationinterface callback. | ||
| * | ||
| * @param[in] pblock The block we want to process. | ||
| * @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources. | ||
| * @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call | ||
| * @returns If the block was processed, independently of block validity | ||
| * @param[in] pblock The block we want to process. | ||
| * @param[out] state Only used for failures in CheckBlock/AcceptBlock. For failure in block connection, | ||
| * a CValidationInterface BlockChecked callback is used to notify clients of validity. | ||
| * @param[in] fForceProcessing This block was requested from the peer or came from a non-network source. | ||
| * @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call | ||
| * @returns bool If the block was processed, independently of block validity | ||
| */ | ||
| bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock) LOCKS_EXCLUDED(cs_main); | ||
| bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, BlockValidationState& state, bool fForceProcessing, bool* fNewBlock) LOCKS_EXCLUDED(cs_main); | ||
|
||
|
|
||
| /** | ||
| * Process incoming block headers. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a85e8b7
In
ConnectTip, we callConnectBlockthenBlockCheckedcallback independently of success or failure of block validation ?