diff options
author | Pieter Wuille <pieter.wuille@gmail.com> | 2017-11-03 15:32:02 -0700 |
---|---|---|
committer | Pieter Wuille <pieter.wuille@gmail.com> | 2017-11-03 15:42:50 -0700 |
commit | f518d9ae6aa525c4cd360a16f07a9272cbef558d (patch) | |
tree | 335ee53ff19753f10dfd40b42570e0419701e183 /src/validation.cpp | |
parent | f224cbc3d8f4478d30e121126658c2ab22c6fb90 (diff) | |
parent | 8195cb0d7fc4f8699b35aff7a43ed2fb3013608c (diff) |
Merge #11592: 0.15: Backportsv0.15.1rc1
8195cb0d7 rpc: further constrain the libevent workaround (Cory Fields)
34153a7e4 rpc: work-around an upstream libevent bug (Cory Fields)
fc308a6cd Add unit test for stale tip checking (Suhas Daftuar)
2ed0647ac Add CConnmanTest to mutate g_connman in tests (João Barbosa)
a607a95d8 Connect to an extra outbound peer if our tip is stale (Suhas Daftuar)
459f2db42 Track tip update time and last new block announcement from each peer (Suhas Daftuar)
49bf09018 net: Allow connecting to extra outbound peers (Suhas Daftuar)
bb83fe190 Add release notes describing blockmaxweight deprecation (Matt Corallo)
4c82cea99 Use a sensible default for blockmaxweight (Matt Corallo)
7871a7d3b Deprecate confusing blockmaxsize, fix getmininginfo output (Matt Corallo)
6baa317b5 Fix minchainwork test for 0.15 backport (Suhas Daftuar)
55b7abfa8 Make p2p-acceptablock not an extended test (Matt Corallo)
5bec7744d [qa] test that invalid blocks on an invalid chain get a disconnect (Matt Corallo)
92d6105c4 Reject headers building on invalid chains by tracking invalidity (Matt Corallo)
51001d684 Accept unrequested blocks with work equal to our tip (Matt Corallo)
c6e4d0ce8 Stop always storing blocks from whitelisted peers (Matt Corallo)
e976c36dd Rewrite p2p-acceptblock in preparation for slight behavior changes (Matt Corallo)
ec8dedff4 net: Add missing lock in ProcessHeadersMessage(...) (practicalswift)
59b210d9a Disconnect outbound peers relaying invalid headers (Suhas Daftuar)
fc966bbd2 moveonly: factor out headers processing into separate function (Suhas Daftuar)
e3272242e Add unit test for outbound peer eviction (Suhas Daftuar)
9961abf9e Permit disconnection of outbound peers on bad/slow chains (Suhas Daftuar)
bf191a718 Disconnecting from bad outbound peers in IBD (Suhas Daftuar)
d570aa429 Fix uninitialized g_connman crash in Shutdown() (MeshCollider)
0a5477c7e net: stop both net/net_processing before destroying them (Cory Fields)
b4136f21c net: drop unused connman param (Cory Fields)
dc897e53d net: use an interface class rather than signals for message processing (Cory Fields)
8aee55af3 net: pass CConnman via pointer rather than reference (Cory Fields)
6f279652b Rename fAddnode to a more-descriptive "manual_connection" (Matt Corallo)
ffb6ea4e5 Add comment explaining forced processing of compact blocks (Suhas Daftuar)
2df65eeb9 qa: add test for minchainwork use in acceptblock (Suhas Daftuar)
3acec3878 Don't process unrequested, low-work blocks (Suhas Daftuar)
0e9d04bf0 [qa] Test nMinimumChainWork (Suhas Daftuar)
da4908c3a Allow setting nMinimumChainWork on command line (Suhas Daftuar)
41088795d qa: Remove never used return value of sync_with_ping (MarcoFalke)
f3457d0e8 qa: Make tmpdir option an absolute path (MarcoFalke)
9c8006dc3 Avoid opening copied wallet databases simultaneously (Russell Yanofsky)
de7053f11 [wallet] Fix leak in CDB constructor (João Barbosa)
fd79ed6b2 Ensure backupwallet fails when attempting to backup to source file (Tomas van der Wansem)
d94fc336c scripted-diff: rename assert_raises_jsonrpc to assert_raises_rpc error (John Newbery)
623de0acb [tests] do not allow assert_raises_message to be called with JSONRPCException (John Newbery)
5b728c8e9 [tests] remove direct testing on JSONRPCException from individual test cases (John Newbery)
Pull request description:
Tree-SHA512: 9fdb5c47844a899271023d8d445f7fc728e3ad71916490cd9783464684967594b07cda05dd644b722bfcea9fade74d06cfc501e1a68abf118d6d03fbbf7d7707
Diffstat (limited to 'src/validation.cpp')
-rw-r--r-- | src/validation.cpp | 99 |
1 files changed, 82 insertions, 17 deletions
diff --git a/src/validation.cpp b/src/validation.cpp index 8bd23a0f1d..8a92992dba 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -80,6 +80,7 @@ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; uint256 hashAssumeValid; +arith_uint256 nMinimumChainWork; CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; @@ -152,6 +153,26 @@ namespace { /** chainwork for the last block that preciousblock has been applied to. */ arith_uint256 nLastPreciousChainwork = 0; + /** In order to efficiently track invalidity of headers, we keep the set of + * blocks which we tried to connect and found to be invalid here (ie which + * were set to BLOCK_FAILED_VALID since the last restart). We can then + * walk this set and check if a new header is a descendant of something in + * this set, preventing us from having to walk mapBlockIndex when we try + * to connect a bad block and fail. + * + * While this is more complicated than marking everything which descends + * from an invalid block as invalid at the time we discover it to be + * invalid, doing so would require walking all of mapBlockIndex to find all + * descendants. Since this case should be very rare, keeping track of all + * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as + * well. + * + * Because we alreardy walk mapBlockIndex in height-order at startup, we go + * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time, + * instead of putting things in this set. + */ + std::set<CBlockIndex*> g_failed_blocks; + /** Dirty block index entries. */ std::set<CBlockIndex*> setDirtyBlockIndex; @@ -1032,8 +1053,6 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) bool IsInitialBlockDownload() { - const CChainParams& chainParams = Params(); - // Once this function has returned false, it must remain false. static std::atomic<bool> latchToFalse{false}; // Optimization: pre-test latch before taking the lock. @@ -1047,7 +1066,7 @@ bool IsInitialBlockDownload() return true; if (chainActive.Tip() == nullptr) return true; - if (chainActive.Tip()->nChainWork < UintToArith256(chainParams.GetConsensus().nMinimumChainWork)) + if (chainActive.Tip()->nChainWork < nMinimumChainWork) return true; if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge)) return true; @@ -1169,6 +1188,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { if (!state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; + g_failed_blocks.insert(pindex); setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); InvalidChainFound(pindex); @@ -1664,7 +1684,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (it != mapBlockIndex.end()) { if (it->second->GetAncestor(pindex->nHeight) == pindex && pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && - pindexBestHeader->nChainWork >= UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) { + pindexBestHeader->nChainWork >= nMinimumChainWork) { // This block is a member of the assumed verified chain and an ancestor of the best header. // The equivalent time check discourages hash power from extorting the network via DOS attack // into accepting an invalid block through telling users they must manually set assumevalid. @@ -2518,17 +2538,18 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C { AssertLockHeld(cs_main); - // Mark the block itself as invalid. - pindex->nStatus |= BLOCK_FAILED_VALID; - setDirtyBlockIndex.insert(pindex); - setBlockIndexCandidates.erase(pindex); + // We first disconnect backwards and then mark the blocks as invalid. + // This prevents a case where pruned nodes may fail to invalidateblock + // and be left unable to start as they have no tip candidates (as there + // are no blocks that meet the "have data and are not invalid per + // nStatus" criteria for inclusion in setBlockIndexCandidates). + + bool pindex_was_in_chain = false; + CBlockIndex *invalid_walk_tip = chainActive.Tip(); DisconnectedBlockTransactions disconnectpool; while (chainActive.Contains(pindex)) { - CBlockIndex *pindexWalk = chainActive.Tip(); - pindexWalk->nStatus |= BLOCK_FAILED_CHILD; - setDirtyBlockIndex.insert(pindexWalk); - setBlockIndexCandidates.erase(pindexWalk); + pindex_was_in_chain = true; // ActivateBestChain considers blocks already in chainActive // unconditionally valid already, so force disconnect away from it. if (!DisconnectTip(state, chainparams, &disconnectpool)) { @@ -2539,6 +2560,21 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C } } + // Now mark the blocks we just disconnected as descendants invalid + // (note this may not be all descendants). + while (pindex_was_in_chain && invalid_walk_tip != pindex) { + invalid_walk_tip->nStatus |= BLOCK_FAILED_CHILD; + setDirtyBlockIndex.insert(invalid_walk_tip); + setBlockIndexCandidates.erase(invalid_walk_tip); + invalid_walk_tip = invalid_walk_tip->pprev; + } + + // Mark the block itself as invalid. + pindex->nStatus |= BLOCK_FAILED_VALID; + setDirtyBlockIndex.insert(pindex); + setBlockIndexCandidates.erase(pindex); + g_failed_blocks.insert(pindex); + // DisconnectTip will add transactions to disconnectpool; try to add these // back to the mempool. UpdateMempoolForReorg(disconnectpool, true); @@ -2576,6 +2612,7 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) { // Reset invalid block marker if it was pointing to one of those. pindexBestInvalid = nullptr; } + g_failed_blocks.erase(it->second); } it++; } @@ -3052,6 +3089,21 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); + + if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) { + for (const CBlockIndex* failedit : g_failed_blocks) { + if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) { + assert(failedit->nStatus & BLOCK_FAILED_VALID); + CBlockIndex* invalid_walk = pindexPrev; + while (invalid_walk != failedit) { + invalid_walk->nStatus |= BLOCK_FAILED_CHILD; + setDirtyBlockIndex.insert(invalid_walk); + invalid_walk = invalid_walk->pprev; + } + return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); + } + } + } } if (pindex == nullptr) pindex = AddToBlockIndex(block); @@ -3065,13 +3117,15 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state } // Exposed wrapper for AcceptBlockHeader -bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex) +bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex, CBlockHeader *first_invalid) { + if (first_invalid != nullptr) first_invalid->SetNull(); { LOCK(cs_main); for (const CBlockHeader& header : headers) { CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast if (!AcceptBlockHeader(header, state, chainparams, &pindex)) { + if (first_invalid) *first_invalid = header; return false; } if (ppindex) { @@ -3101,7 +3155,7 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation // process an unrequested block if it's new and has enough work to // advance our tip, and isn't too many blocks ahead. bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA; - bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true); + bool fHasMoreOrSameWork = (chainActive.Tip() ? pindex->nChainWork >= chainActive.Tip()->nChainWork : true); // Blocks that are too out-of-order needlessly limit the effectiveness of // pruning, because pruning will not delete block files that contain any // blocks which are too close in height to the tip. Apply this test @@ -3118,9 +3172,15 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation // and unrequested blocks. if (fAlreadyHave) return true; if (!fRequested) { // If we didn't ask for it: - if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned - if (!fHasMoreWork) return true; // Don't process less-work chains - if (fTooFarAhead) return true; // Block height is too high + if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned + if (!fHasMoreOrSameWork) return true; // Don't process less-work chains + if (fTooFarAhead) return true; // Block height is too high + + // Protect against DoS attacks from low-work chains. + // If our tip is behind, a peer could try to send us + // low-work blocks on a fake chain that we would never + // request; don't process these. + if (pindex->nChainWork < nMinimumChainWork) return true; } if (fNewBlock) *fNewBlock = true; @@ -3470,6 +3530,10 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) pindex->nChainTx = pindex->nTx; } } + if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) { + pindex->nStatus |= BLOCK_FAILED_CHILD; + setDirtyBlockIndex.insert(pindex); + } if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr)) setBlockIndexCandidates.insert(pindex); if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) @@ -3860,6 +3924,7 @@ void UnloadBlockIndex() nLastBlockFile = 0; nBlockSequenceId = 1; setDirtyBlockIndex.clear(); + g_failed_blocks.clear(); setDirtyFileInfo.clear(); versionbitscache.Clear(); for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) { |