-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Check that new headers are not a descendant of an invalid block (more effeciently) #11531
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
Check that new headers are not a descendant of an invalid block (more effeciently) #11531
Conversation
552ad25
to
9bbd66a
Compare
src/validation.cpp
Outdated
@@ -3066,6 +3086,19 @@ 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)); | |||
|
|||
for (const CBlockIndex* failedit : g_failed_blocks) { |
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.
What do you think about bypassing this logic in the event that pindexPrev is VALID_SCRIPTS? I'm slightly concerned about some kind of weird scenario where g_failed_blocks grows (hopefully slowly!) over time, slowing down block processing the longer a node has been up.
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.
Done
src/validation.cpp
Outdated
setDirtyBlockIndex.insert(invalid_walk); | ||
invalid_walk = invalid_walk->pprev; | ||
} | ||
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); |
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.
It would be nice to specifically test for bad-prevblk
being thrown, perhaps using the mechanism in #11220. I'm not sure how to do that in the python tests though.
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.
Another place to test this would be to add a test for ProcessNewBlockHeaders. I can take a stab at it, but I'm not sure which file that test should be added too, or which test would be a good example to base it on.
9bbd66a
to
ba5dd28
Compare
I think this needs a 0.15.0.2 tag |
utACK up until ffedf48b218b50b8187cbabf2969e519315c98a6, which has nuances that I'm not comfortable ACKing before taking some rabbit-hole dives. Side-note: This is a really well-done patch set that is (other than the last commit which is the meat of it) very straightforward and easy to review. Though the changes are mostly one-liners, breaking them up functionally is a big help. Nice work :) |
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.
You may want to rebase this to remove the commits from #11458 now that it is merged.
ba5dd28
to
9df01d2
Compare
Rebased on master with no changes. |
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.
utACK 9df01d2f2cb9eed5af395424e702fa5d7e441207. I left a lot of comments, but they are all minor, so feel free to ignore and just respond to whatever you think is useful.
@@ -2533,17 +2542,14 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C | |||
{ | |||
AssertLockHeld(cs_main); |
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.
In commit "Reject headers building on invalid chains by tracking invalidity"
Maybe assert chainActive.Contains(pindex)
here. Otherwise it isn't clear that the while (invalid_walk_tip != pindex)
loop below will work.
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.
Nah, thats just a bug, fixing.
src/validation.cpp
Outdated
@@ -2554,6 +2560,19 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C | |||
} | |||
} | |||
|
|||
while (invalid_walk_tip != pindex) { |
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.
In commit "Reject headers building on invalid chains by tracking invalidity"
Maybe add // Mark descendants of the block invalid
comment for consistency and easier skimming
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 |
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.
In commit "Reject headers building on invalid chains by tracking invalidity"
Maybe move this comment above the setBlockIndexCandidates.erase() call so it is clearer what this is referring to.
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.
Its actually not related to setBlockIndexCandidates (which is not persistent) - its related to the nStatus flags, I'll clarify comment.
CBlockIndex* invalid_walk = pindexPrev; | ||
while (invalid_walk != failedit) { | ||
invalid_walk->nStatus |= BLOCK_FAILED_CHILD; | ||
setDirtyBlockIndex.insert(invalid_walk); |
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.
In commit "Reject headers building on invalid chains by tracking invalidity"
Why isn't setBlockIndexCandidates.erase() needed here?
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.
None of those objects can be in our setBlockIndexCandidates - if they were we sould have tried to connect towards them, found them to be invalid, and then marked them as such (at least enough to get them out of our setBlockIndexCandidates).
@@ -3492,6 +3527,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)) { |
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.
In commit "Reject headers building on invalid chains by tracking invalidity"
Should the first condition be pindex->nStatus & BLOCK_FAILED_CHILD
instead of FAILED_MASK?
Otherwise if it is intentional to avoid setting FAILED_CHILD here when FAILED_VALID is set, it might be good to have a comment, because the |= FAILED_CHILD code in AcceptBlockHeader and InvalidateBlock isn't checking this.
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.
It shouldn't matter - really this is an indication of corruption - you can't be both FAILED_CHILD and FAILED_VALID, you also cannot be FAILED_VALID if you have a FAILED_MASK parent. At the risk of overcomplicating things for a (currently) harmless error, I'll skip adding a check, unless you object.
@@ -3115,7 +3115,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); |
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.
In commit "Accept unrequested blocks with work equal to our tip"
Same question again: Is this change related to the "new headers are not a descendant of an invalid block" check or is it only part of this PR because it simplifies the p2p-acceptblock test? Again it seems fine, just asking to understand.
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.
The second. Mostly because it started as a "cleanup p2p-acceptblock test" pr and then I didn't want to rewrite the p2p-acceptblock test again to not care about this.
test/functional/p2p-acceptblock.py
Outdated
# At this point we've sent an obviously-bogus block, wait for full processing | ||
# without assuming whether we will be disconnected or not | ||
try: | ||
test_node.sync_with_ping(timeout=1) |
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.
In commit "[qa] test that invalid blocks on an invalid chain get a disconnect"
Could you add a comment to explain why the timeout is required? Is this assuming that if disconnect happens, it will happen within 1 second? Why not just do a normal sync_with_ping with the default timeout?
test/functional/p2p-acceptblock.py
Outdated
# without assuming whether we will be disconnected or not | ||
try: | ||
test_node.sync_with_ping(timeout=1) | ||
except AssertionError: |
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.
In commit "[qa] test that invalid blocks on an invalid chain get a disconnect"
Could you add a comment explaining AssertionError? Is this catching the disconnect or the timeout or both?
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.
Honestly, I have no idea...someone who knows more about the test framework should come along and make the catch here much more specific.
test/functional/p2p-acceptblock.py
Outdated
try: | ||
test_node.sync_with_ping(timeout=1) | ||
except AssertionError: | ||
test_node.wait_for_disconnect() |
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.
In commit "[qa] test that invalid blocks on an invalid chain get a disconnect"
This seems to waiting 1 second for a ping response, and if that fails, then wait 60 seconds for disconnection before reconnecting? Does sync_with_ping not raise an error on disconnect?
test_node.send_message(msg_block(block_291)) | ||
|
||
# At this point we've sent an obviously-bogus block, wait for full processing | ||
# without assuming whether we will be disconnected or not |
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.
In commit "[qa] test that invalid blocks on an invalid chain get a disconnect"
Can you expand comment to say why it's not desirable to make an assumption. Is there some kind of nondeterminism or complexity in knowing what the node is going to do?
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.
Standard testing practice - we've sent something for which a reasonable node may very well decide to DoS us, disconnect us or whathave you, dont test that the node not do a thing that its perfectly reasonable for it to do. I'd say thats implied here, but happy to clarify more (or put something in some dev-notes doc).
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.
In commit "test that invalid blocks on an invalid chain get a disconnect"
Test seems like it will hang and fail in the case where a node takes more than one second to the respond to the ping, but does not disconnect because sync_with_ping and wait_for_disconnect will both timeout.
Addressed (or commented on) @ryanofsky's feedback. |
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.
utACK 9429cef
Only briefly looked at tests, couple of very minor nits inlline
src/net_processing.cpp
Outdated
@@ -2484,7 +2484,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr | |||
// unless we're still syncing with the network. | |||
// Such an unrequested block may still be processed, subject to the | |||
// conditions in AcceptBlock(). | |||
bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload(); | |||
bool forceProcessing = false; |
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.
Seems like the comment above needs modification/removing because of this?
src/validation.cpp
Outdated
@@ -156,6 +156,15 @@ namespace { | |||
/** chainwork for the last block that preciousblock has been applied to. */ | |||
arith_uint256 nLastPreciousChainwork = 0; | |||
|
|||
/** In order to effeciently track invalidity of headers, we keep the set of |
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.
Nit: typo effeciently
-> efficiently
Addressed @meshcollider's code comment issues. |
utACK 35e7b4e2bb7f749a4c8426dc3ea71b77f3457f30 |
re-utACK 35e7b4e |
utACK 35e7b4e |
35e7b4e
to
eae0559
Compare
Squashed. |
eae0559
to
9a5013c
Compare
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.
utACK eae05599f4f577667441530109a338000a48bdd2. Has updated comments, new block not found checks in the test, and the pindex_was_in_chain
fix.
if x['hash'] == block_292.hash: | ||
assert_equal(x['status'], "headers-only") | ||
tip_entry_found = True | ||
assert(tip_entry_found) |
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.
In commit "[qa] test that invalid blocks on an invalid chain get a disconnect"
This chunk of code is repeated several times in the test. I think you could simplify it as:
status = {block['hash']: block['status'] for block in self.nodes[0].getchaintips()}
assert_equal(status[block_292.hash], "headers-only")
|
||
DisconnectedBlockTransactions disconnectpool; | ||
while (chainActive.Contains(pindex)) { | ||
CBlockIndex *pindexWalk = chainActive.Tip(); | ||
pindexWalk->nStatus |= BLOCK_FAILED_CHILD; |
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.
Moving this has the disadvantage that if Bitcoin Core is shut down while invalidating, even after part of it is was flushed already, the invalidating will be reverted at startup.
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.
i'd say thats better than, at least in the common-pruning-case, failing to start at all and requiring re-sync by assert(false)ing each time you try to start up?
p2p-acceptblock.py is broken, it's apparently an extended test and not run by travis:
|
Gah, Russ scared me into adding too many checks. I moved p2p-acceptblock into regular tests so that travis runs it and fixed the test. |
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.
Few nits.
These changes seem conceptually fine, though I would have preferred that this PR had a narrower scope since we're targeting this for 0.15.0.2, rather than master. The changes to the AcceptBlock unrequested block logic seem independent of the improvements to invalid block handling (and even the behavior improvement in InvalidateBlock
seems not directly related, though maybe I'm missing something with that?).
But no objection to including this whole PR in 0.15 if others are fine with these changes; aside from my nits I think this is fine.
src/validation.cpp
Outdated
* 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. | ||
*/ |
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.
Yeah I think this comment is pretty cryptic for anyone not aware of the two approaches we've discussed about how to detect that a header builds on an invalid chain:
a) when you detect an invalid block, mark all descendants as BLOCK_FAILED_CHILD
b) (what we implement in this pr)
Perhaps:
/** 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
* determine if a new header is extending an invalid chain by checking if
* its parent is invalid, or if it's a descendant of an entry in g_failed_blocks.
* On startup, g_failed_blocks is empty, and all headers that are invalid
* (whether directly or because a parent is invalid) are marked as such
* in LoadBlockIndexDB().
*/
test/functional/p2p-acceptblock.py
Outdated
@@ -144,7 +144,7 @@ def run_test(self): | |||
assert_equal(x['status'], "headers-only") | |||
tip_entry_found = True | |||
assert(tip_entry_found) | |||
assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_h2f.hash) | |||
self.nodes[0].getblock(block_h2f.hash) |
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.
If you're going to squash this in to Rewrite p2p-acceptblock in preparation for slight behavior changes
, then you'll need to have this line commented out (and re-uncomment it in Accept unrequested blocks with work equal to our tip
), in order to make the test succeed after that first commit, I think.
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.
Oh ffs, I'm just gonna remove that line.
|
||
4b.Send 288 more blocks on the longer chain. | ||
4c.Send 288 more blocks on the longer chain (the number of blocks ahead |
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.
4c?
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.
Yea, 4 was split into 4 a and b, I updated the comment to note that.
I think it's worth mentioning why I think his change, which adds a way to ban all peers giving us a block header that builds on an invalid previous block, is okay, when we have been discussing changing our ban-behavior to exclude inbound peers from these checks, and to generally favor disconnects over ban+disconnect even for outbound peers. While I'd like to see the ban-behavior vastly improved, in this case we're starting from a baseline of already banning a peer for relaying a block header that builds on an invalid block (regardless of inbound/outbound). This PR addresses the inconsistency in behavior between a situation where you get one header and block at a time (and discover that one such block is invalid), versus if you download a bunch of headers, and then process the first block in the chain and discover it is invalid. So given that there's already a situation where you'd ban all your peers that relay a block header building on an invalid block, I think it's okay to extend that to the case where you receive descendant headers before you process the first block. I think it would also be okay to improve this logic so that we only apply it to (eg) outbound peers, but I don't think that should be a merge blocker, and I think this is a valuable enough improvement that it's worth shipping as-is for 0.15.0.2, as it addresses a shortcoming of our headers processing and disconnect logic that should be buttoned up. |
b291009
to
f3d4adf
Compare
Addressed @sdaftuar's suggestions and squashed. Indeed, I dont think the way things are with header disconnection here, and as mentioned already have a branch working towards redoing it rather significantly, but that certainly isn't gonna make 0.15.0.2. |
utACK: only changes between b291009 and f3d4adf: diff -dur bitcoin-b29100958b37fc7d499e9932e38ae88e3596fa1e/src/validation.cpp bitcoin-f3d4adfa6ff5db180ec09d93f78cdc8bfda26f96/src/validation.cpp
--- bitcoin-b29100958b37fc7d499e9932e38ae88e3596fa1e/src/validation.cpp 2017-10-31 02:30:32.000000000 +0100
+++ bitcoin-f3d4adfa6ff5db180ec09d93f78cdc8bfda26f96/src/validation.cpp 2017-10-31 18:51:34.000000000 +0100
@@ -162,6 +162,17 @@
* 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;
diff -dur bitcoin-b29100958b37fc7d499e9932e38ae88e3596fa1e/test/functional/p2p-acceptblock.py bitcoin-f3d4adfa6ff5db180ec09d93f78cdc8bfda26f96/test/functional/p2p-acceptblock.py
--- bitcoin-b29100958b37fc7d499e9932e38ae88e3596fa1e/test/functional/p2p-acceptblock.py 2017-10-31 02:30:32.000000000 +0100
+++ bitcoin-f3d4adfa6ff5db180ec09d93f78cdc8bfda26f96/test/functional/p2p-acceptblock.py 2017-10-31 18:51:34.000000000 +0100
@@ -23,7 +23,7 @@
Node0 should not process this block (just accept the header), because it
is unrequested and doesn't have more or equal work to the tip.
-4. Send another two blocks that builds on the forking block.
+4a,b. Send another two blocks that build on the forking block.
Node0 should process the second block but be stuck on the shorter chain,
because it's missing an intermediate block.
@@ -144,7 +144,6 @@
assert_equal(x['status'], "headers-only")
tip_entry_found = True
assert(tip_entry_found)
- self.nodes[0].getblock(block_h2f.hash)
# But this block should be accepted by node since it has equal work.
self.nodes[0].getblock(block_h2f.hash) |
…id block (more effeciently) f3d4adf Make p2p-acceptablock not an extended test (Matt Corallo) 00dcda6 [qa] test that invalid blocks on an invalid chain get a disconnect (Matt Corallo) 015a525 Reject headers building on invalid chains by tracking invalidity (Matt Corallo) 932f118 Accept unrequested blocks with work equal to our tip (Matt Corallo) 3d9c70c Stop always storing blocks from whitelisted peers (Matt Corallo) 3b4ac43 Rewrite p2p-acceptblock in preparation for slight behavior changes (Matt Corallo) Pull request description: @sdaftuar pointed out that the version in #11487 was somewhat DoS-able as someone could feed you a valid chain that forked off the the last checkpoint block and force you to do lots of work just walking backwards across blocks for each new block they gave you. We came up with a few proposals but settled on the one implemented here as likely the simplest without obvious DoS issues. It uses our existing on-load mapBlockIndex walk to make sure everything that descends from an invalid block is marked as such, and then simply caches blocks which we attempted to connect but which were found to be invalid. To avoid DoS issues during IBD, this will need to depend on #11458. Includes tests from #11487. Tree-SHA512: 46aff8332908e122dae72ceb5fe8cd241902c2281a87f58a5fb486bf69d46458d84a096fdcb5f3e8e07fbcf7466232b10c429f4d67855425f11b38ac0bf612e1
posthumous utACK f3d4adf; didn't review the test changes |
Removes checking whitelisted behavior (which will be removed, the difference in behavior here makes little sense) and no longer requires that blocks at the same work as our tip be dropped if not requested (in part because we *do* request those blocks). Github-Pull: bitcoin#11531 Rebased-From: 3b4ac43
There is no reason to wish to store blocks on disk always just because a peer is whitelisted. This appears to be a historical quirk to avoid breaking things when the accept limits were added. Github-Pull: bitcoin#11531 Rebased-From: 3d9c70c
This is a simple change that makes our accept requirements the same as our request requirements, (ever so slightly) further decoupling our consensus logic from our FindNextBlocksToDownload logic in net_processing. Github-Pull: bitcoin#11531 Rebased-From: 932f118
This tracks the set of all known invalid-themselves blocks (ie blocks which we attempted to connect but which were found to be invalid). This is used to cheaply check if new headers build on an invalid chain. While we're at it we also resolve an edge-case in invalidateblock on pruned nodes which results in them needing a reindex if they fail to reorg. Github-Pull: bitcoin#11531 Rebased-From: 015a525
Github-Pull: bitcoin#11531 Rebased-From: 00dcda6
Github-Pull: bitcoin#11531 Rebased-From: f3d4adf
…id block (more effeciently) bitcoin/bitcoin#11531
…n invalid block (more effeciently) f3d4adf Make p2p-acceptablock not an extended test (Matt Corallo) 00dcda6 [qa] test that invalid blocks on an invalid chain get a disconnect (Matt Corallo) 015a525 Reject headers building on invalid chains by tracking invalidity (Matt Corallo) 932f118 Accept unrequested blocks with work equal to our tip (Matt Corallo) 3d9c70c Stop always storing blocks from whitelisted peers (Matt Corallo) 3b4ac43 Rewrite p2p-acceptblock in preparation for slight behavior changes (Matt Corallo) Pull request description: @sdaftuar pointed out that the version in bitcoin#11487 was somewhat DoS-able as someone could feed you a valid chain that forked off the the last checkpoint block and force you to do lots of work just walking backwards across blocks for each new block they gave you. We came up with a few proposals but settled on the one implemented here as likely the simplest without obvious DoS issues. It uses our existing on-load mapBlockIndex walk to make sure everything that descends from an invalid block is marked as such, and then simply caches blocks which we attempted to connect but which were found to be invalid. To avoid DoS issues during IBD, this will need to depend on bitcoin#11458. Includes tests from bitcoin#11487. Tree-SHA512: 46aff8332908e122dae72ceb5fe8cd241902c2281a87f58a5fb486bf69d46458d84a096fdcb5f3e8e07fbcf7466232b10c429f4d67855425f11b38ac0bf612e1
…n invalid block (more effeciently) f3d4adf Make p2p-acceptablock not an extended test (Matt Corallo) 00dcda6 [qa] test that invalid blocks on an invalid chain get a disconnect (Matt Corallo) 015a525 Reject headers building on invalid chains by tracking invalidity (Matt Corallo) 932f118 Accept unrequested blocks with work equal to our tip (Matt Corallo) 3d9c70c Stop always storing blocks from whitelisted peers (Matt Corallo) 3b4ac43 Rewrite p2p-acceptblock in preparation for slight behavior changes (Matt Corallo) Pull request description: @sdaftuar pointed out that the version in bitcoin#11487 was somewhat DoS-able as someone could feed you a valid chain that forked off the the last checkpoint block and force you to do lots of work just walking backwards across blocks for each new block they gave you. We came up with a few proposals but settled on the one implemented here as likely the simplest without obvious DoS issues. It uses our existing on-load mapBlockIndex walk to make sure everything that descends from an invalid block is marked as such, and then simply caches blocks which we attempted to connect but which were found to be invalid. To avoid DoS issues during IBD, this will need to depend on bitcoin#11458. Includes tests from bitcoin#11487. Tree-SHA512: 46aff8332908e122dae72ceb5fe8cd241902c2281a87f58a5fb486bf69d46458d84a096fdcb5f3e8e07fbcf7466232b10c429f4d67855425f11b38ac0bf612e1
…n invalid block (more effeciently) f3d4adf Make p2p-acceptablock not an extended test (Matt Corallo) 00dcda6 [qa] test that invalid blocks on an invalid chain get a disconnect (Matt Corallo) 015a525 Reject headers building on invalid chains by tracking invalidity (Matt Corallo) 932f118 Accept unrequested blocks with work equal to our tip (Matt Corallo) 3d9c70c Stop always storing blocks from whitelisted peers (Matt Corallo) 3b4ac43 Rewrite p2p-acceptblock in preparation for slight behavior changes (Matt Corallo) Pull request description: @sdaftuar pointed out that the version in bitcoin#11487 was somewhat DoS-able as someone could feed you a valid chain that forked off the the last checkpoint block and force you to do lots of work just walking backwards across blocks for each new block they gave you. We came up with a few proposals but settled on the one implemented here as likely the simplest without obvious DoS issues. It uses our existing on-load mapBlockIndex walk to make sure everything that descends from an invalid block is marked as such, and then simply caches blocks which we attempted to connect but which were found to be invalid. To avoid DoS issues during IBD, this will need to depend on bitcoin#11458. Includes tests from bitcoin#11487. Tree-SHA512: 46aff8332908e122dae72ceb5fe8cd241902c2281a87f58a5fb486bf69d46458d84a096fdcb5f3e8e07fbcf7466232b10c429f4d67855425f11b38ac0bf612e1
…id block (more effeciently) (#218) bitcoin/bitcoin#11531
@sdaftuar pointed out that the version in #11487 was somewhat DoS-able as someone could feed you a valid chain that forked off the the last checkpoint block and force you to do lots of work just walking backwards across blocks for each new block they gave you. We came up with a few proposals but settled on the one implemented here as likely the simplest without obvious DoS issues. It uses our existing on-load mapBlockIndex walk to make sure everything that descends from an invalid block is marked as such, and then simply caches blocks which we attempted to connect but which were found to be invalid. To avoid DoS issues during IBD, this will need to depend on #11458.
Includes tests from #11487.