diff --git a/src/chain.h b/src/chain.h --- a/src/chain.h +++ b/src/chain.h @@ -191,6 +191,15 @@ // Mask used to check if the block failed. static const uint32_t INVALID_MASK = FAILED_FLAG | FAILED_PARENT_FLAG; + // The block is being parked for some reason. It will be reconsidered if its + // chains grows. + static const uint32_t PARKED_FLAG = 0x80; + // One of the block's parent is parked. + static const uint32_t PARKED_PARENT_FLAG = 0x100; + + // Mask used to check for parked blocks. + static const uint32_t PARKED_MASK = PARKED_FLAG | PARKED_PARENT_FLAG; + public: explicit BlockStatus() : status(0) {} @@ -226,6 +235,18 @@ (hasFailedParent ? FAILED_PARENT_FLAG : 0)); } + bool isParked() const { return status & PARKED_FLAG; } + BlockStatus withParked(bool parked = true) const { + return BlockStatus((status & ~PARKED_FLAG) | + (parked ? PARKED_FLAG : 0)); + } + + bool hasParkedParent() const { return status & PARKED_PARENT_FLAG; } + BlockStatus withParkedParent(bool parkedParent = true) const { + return BlockStatus((status & ~PARKED_PARENT_FLAG) | + (parkedParent ? PARKED_PARENT_FLAG : 0)); + } + /** * Check whether this block index entry is valid up to the passed validity * level. @@ -243,6 +264,8 @@ return BlockStatus(status & ~INVALID_MASK); } + bool isOnParkedChain() const { return status & PARKED_MASK; } + ADD_SERIALIZE_METHODS; template diff --git a/src/test/blockstatus_tests.cpp b/src/test/blockstatus_tests.cpp --- a/src/test/blockstatus_tests.cpp +++ b/src/test/blockstatus_tests.cpp @@ -13,18 +13,23 @@ static void CheckBlockStatus(const BlockStatus s, BlockValidity validity, bool hasData, bool hasUndo, bool hasFailed, - bool hasFailedParent) { + bool hasFailedParent, bool isParked, + bool hasParkedParent) { BOOST_CHECK(s.getValidity() == validity); BOOST_CHECK_EQUAL(s.hasData(), hasData); BOOST_CHECK_EQUAL(s.hasUndo(), hasUndo); BOOST_CHECK_EQUAL(s.hasFailed(), hasFailed); BOOST_CHECK_EQUAL(s.hasFailedParent(), hasFailedParent); BOOST_CHECK_EQUAL(s.isInvalid(), hasFailed || hasFailedParent); + BOOST_CHECK_EQUAL(s.isParked(), isParked); + BOOST_CHECK_EQUAL(s.hasParkedParent(), hasParkedParent); + BOOST_CHECK_EQUAL(s.isOnParkedChain(), isParked || hasParkedParent); } static void CheckAllPermutations(const BlockStatus base, bool hasData, bool hasUndo, bool hasFailed, - bool hasFailedParent) { + bool hasFailedParent, bool isParked, + bool hasParkedParent) { // Check all possible permutations. std::set baseValidities{ BlockValidity::UNKNOWN, BlockValidity::HEADER, @@ -34,33 +39,58 @@ for (BlockValidity validity : baseValidities) { const BlockStatus s = base.withValidity(validity); CheckBlockStatus(s, validity, hasData, hasUndo, hasFailed, - hasFailedParent); + hasFailedParent, isParked, hasParkedParent); // Clears failure flags. CheckBlockStatus(s.withClearedFailureFlags(), validity, hasData, - hasUndo, false, false); + hasUndo, false, false, isParked, hasParkedParent); // Also check all possible alterations. CheckBlockStatus(s.withData(true), validity, true, hasUndo, hasFailed, - hasFailedParent); + hasFailedParent, isParked, hasParkedParent); CheckBlockStatus(s.withData(false), validity, false, hasUndo, hasFailed, - hasFailedParent); + hasFailedParent, isParked, hasParkedParent); CheckBlockStatus(s.withUndo(true), validity, hasData, true, hasFailed, - hasFailedParent); + hasFailedParent, isParked, hasParkedParent); CheckBlockStatus(s.withUndo(false), validity, hasData, false, hasFailed, - hasFailedParent); + hasFailedParent, isParked, hasParkedParent); CheckBlockStatus(s.withFailed(true), validity, hasData, hasUndo, true, - hasFailedParent); + hasFailedParent, isParked, hasParkedParent); CheckBlockStatus(s.withFailed(false), validity, hasData, hasUndo, false, - hasFailedParent); + hasFailedParent, isParked, hasParkedParent); CheckBlockStatus(s.withFailedParent(true), validity, hasData, hasUndo, - hasFailed, true); + hasFailed, true, isParked, hasParkedParent); CheckBlockStatus(s.withFailedParent(false), validity, hasData, hasUndo, - hasFailed, false); + hasFailed, false, isParked, hasParkedParent); + + CheckBlockStatus(s.withParked(true), validity, hasData, hasUndo, + hasFailed, hasFailedParent, true, hasParkedParent); + CheckBlockStatus(s.withParked(false), validity, hasData, hasUndo, + hasFailed, hasFailedParent, false, hasParkedParent); + CheckBlockStatus(s.withParkedParent(true), validity, hasData, hasUndo, + hasFailed, hasFailedParent, isParked, true); + CheckBlockStatus(s.withParkedParent(false), validity, hasData, hasUndo, + hasFailed, hasFailedParent, isParked, false); for (BlockValidity newValidity : baseValidities) { CheckBlockStatus(s.withValidity(newValidity), newValidity, hasData, - hasUndo, hasFailed, hasFailedParent); + hasUndo, hasFailed, hasFailedParent, isParked, + hasParkedParent); + } + } +} + +static void CheckParked(const BlockStatus s, bool hasData, bool hasUndo, + bool hasFailed, bool hasFailedParent) { + std::set isParkedValues{false, true}; + std::set hasParkedParentValues{false, true}; + + for (bool isParked : isParkedValues) { + for (bool hasParkedParent : hasParkedParentValues) { + CheckAllPermutations( + s.withParked(isParked).withParkedParent(hasParkedParent), + hasData, hasUndo, hasFailed, hasFailedParent, isParked, + hasParkedParent); } } } @@ -71,7 +101,7 @@ for (bool hasFailed : hasFailedValues) { for (bool hasFailedParent : hasFailedParentValues) { - CheckAllPermutations( + CheckParked( s.withFailed(hasFailed).withFailedParent(hasFailedParent), hasData, hasUndo, hasFailed, hasFailedParent); } @@ -93,7 +123,7 @@ BOOST_AUTO_TEST_CASE(sighash_construction_test) { // Check default values. CheckBlockStatus(BlockStatus(), BlockValidity::UNKNOWN, false, false, false, - false); + false, false, false); CheckHaveDataAndUndo(BlockStatus()); } diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2803,8 +2803,9 @@ // for the most work chain if we come across them; we can't switch // to a chain unless we have all the non-active-chain parent blocks. bool fInvalidChain = pindexTest->nStatus.isInvalid(); + bool fParkedChain = pindexTest->nStatus.isOnParkedChain(); bool fMissingData = !pindexTest->nStatus.hasData(); - if (fInvalidChain || fMissingData) { + if (fInvalidChain || fParkedChain || fMissingData) { // Candidate chain is not usable (either invalid or missing // data) if (fInvalidChain && @@ -2815,9 +2816,11 @@ CBlockIndex *pindexFailed = pindexNew; // Remove the entire chain from the set. while (pindexTest != pindexFailed) { - if (fInvalidChain) { + if (fInvalidChain || fParkedChain) { pindexFailed->nStatus = - pindexFailed->nStatus.withFailedParent(); + pindexFailed->nStatus + .withFailedParent(fInvalidChain) + .withParkedParent(fParkedChain); } else if (fMissingData) { // If we're missing data, then add back to // mapBlocksUnlinked, so that if the block arrives in @@ -5098,6 +5101,8 @@ int nHeight = 0; // Oldest ancestor of pindex which is invalid. CBlockIndex *pindexFirstInvalid = nullptr; + // Oldest ancestor of pindex which is parked. + CBlockIndex *pindexFirstParked = nullptr; // Oldest ancestor of pindex which does not have data available. CBlockIndex *pindexFirstMissing = nullptr; // Oldest ancestor of pindex for which nTx == 0. @@ -5119,6 +5124,9 @@ if (pindexFirstInvalid == nullptr && pindex->nStatus.hasFailed()) { pindexFirstInvalid = pindex; } + if (pindexFirstParked == nullptr && pindex->nStatus.isParked()) { + pindexFirstParked = pindex; + } if (pindexFirstMissing == nullptr && !pindex->nStatus.hasData()) { pindexFirstMissing = pindex; } @@ -5212,6 +5220,11 @@ // The failed mask cannot be set for blocks without invalid parents. assert(!pindex->nStatus.isInvalid()); } + if (pindexFirstParked == nullptr) { + // Checks for not-invalid blocks. + // The failed mask cannot be set for blocks without invalid parents. + assert(!pindex->nStatus.isOnParkedChain()); + } if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstNeverProcessed == nullptr) { if (pindexFirstInvalid == nullptr) { @@ -5310,6 +5323,9 @@ if (pindex == pindexFirstInvalid) { pindexFirstInvalid = nullptr; } + if (pindex == pindexFirstParked) { + pindexFirstParked = nullptr; + } if (pindex == pindexFirstMissing) { pindexFirstMissing = nullptr; }