Skip to content

Conversation

@jnewbery
Copy link
Contributor

This PR corrects a few places were incorrect error codes are send in reject messages, or where reject messages are sent when they shouldn't be:

  • if a block failed to validate due to CheckQueue returning false, we should send a reject message with REJECT_INVALID
  • if we reject a block because it's a fork from the chain before a checkpoint block that we've already processed, we should send a reject message with REJECT_CHECKPOINT
  • if we reject a block because we don't have it's parent block, we shouldn't send a reject message (because the block is not necessarily invalid).

@sdaftuar we discussed this. Let me know if you have any questions.

@jnewbery jnewbery changed the title Send the correct error code in reject messages [p2p] Send the correct error code in reject messages Mar 31, 2017
@jnewbery
Copy link
Contributor Author

Extended test run succeeds for me locally.

Copy link
Member

@sdaftuar sdaftuar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One nit you can take or leave, and one point of information. utACK 5d08c9c

int nDoS = 0;
if (state.IsInvalid(nDoS)) {
if (it != mapBlockSource.end() && State(it->second.first)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I don't feel strongly but we could leave this assert in, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The if statement has been modified to check that state.GetRejectCode() < REJECT_INTERNAL (so this assert can now never fail)

BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk");
return state.DoS(10, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I didn't think of this before, but perhaps we were sending a reject message here specifically because we're assigning DoS points. Suppressing the reject message will just cause the peer to have less information about why they're being disconnected.

I'm not sure I think BIP 61 is a great idea (in particular, the lack of versioning makes things difficult) but the motivation of it was in part to let peers know why they're being banned. So maybe it makes more sense to use a REJECT_INVALID message here, rather than omit the reject message altogether, even though the block might not actually be invalid if its missing parent was provided?

Really, though, I don't care what we do here, just raising the point in case others have an opinion. Your change here is fine with me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather extend BIP61 than deliberately send a reject message that's incorrect (the block here isn't INVALID, it just doesn't connect with our chain).

If you prefer that we send a reject message, I think we should at least change the error code to a 0x4* value and change the message to 'prev-blk-not-found'.

@fanquake fanquake added the P2P label Mar 31, 2017
@gmaxwell
Copy link
Contributor

gmaxwell commented Apr 1, 2017

I don't really have any objection to making the behavior more correct, but I would rather more or less stop sending reject messages.

There is only one or two places where I'm aware of any implementation doing anything remotely useful with them (some bitcoinj users use transaction rejection to figure out that their txn hasn't relayed at all, which isn't reliable but is arguably better than nothing). They've been a source of serious bugs several times, add to code complexity, and like other error handling are fairly hard to get precise behavior from without more or less setting in stone algorithms that don't need to be set in stone. Plus they waste bandwidth...

I've personally never found them useful in troubleshooting an issue.

I can't help but think that any productive use for them could be better accomplished by some other means.

@jnewbery
Copy link
Contributor Author

jnewbery commented Apr 1, 2017

@gmaxwell - I've heard that argument from several people, and I'm mostly on board with it. Thanks for documenting it here.

reject messages are somewhat useful in black box testing. We can send in invalid messages and assert that bitcoin rejected them for the right reason. The old style 'comparison' test cases use this a lot. That's not a justification for spending a heap of time on improving them, but I think there's no harm in small fixes like this. And if no-one's using them in live deployments, then it should be an uncontentious change, right? :)

Definitely agree with improving error-handling in general. I don't know exactly what that would look like, but it's beyond the scope of this PR.

For my own education, do you have references to the serious bugs that reject messages have caused?

@gmaxwell
Copy link
Contributor

gmaxwell commented Apr 2, 2017

For my own education, do you have references to the serious bugs that reject messages have caused?

PR #4903 was mostly what I was thinking of there, also the original implementation of reject messages themselves printed unsanitized bytes to the logs and IIRC also didn't limit the size of strings, though I believe both of these were fixed before merge. We have also had many PRs for where we broke sending reject messages (or sent the 'wrong' one)-- (PR #7179 comes to mind.)

For tests, we should eventually consider something more targeted. (I have thoughts there but it's too far a tangent for this discussion.-- My comment here was in vague support of your changes but also advising that I think we should be thinking about alternatives going forward.)

@laanwj
Copy link
Member

laanwj commented Apr 2, 2017

utACK 5d08c9c

reject messages are somewhat useful in black box testing. We can send in invalid messages and assert that bitcoin rejected them for the right reason.

Also for things that do custom transaction broadcasting (for example bitcoin-submittx ) it's marginally useful to see if transactions have been accepted or not, and why. I wouldn't care deeply if they were removed, though I don't see any urgency in doing so.

There were indeed some bugs in initial deployment of this feature (including, as I remember, one that could cause a reject message to be sent for a reject message), but I'd say those have been ironed out by now. Attack surface is mostly introduced by parsing/accepting messages not by emitting them, and we don't do anything with them besides optional logging (which could be in a separate logging category from NET).

@laanwj laanwj merged commit 5d08c9c into bitcoin:master Apr 10, 2017
laanwj added a commit that referenced this pull request Apr 10, 2017
5d08c9c Send the correct error code in reject messages (John Newbery)

Tree-SHA512: 0cd3ef3ae202584b138cc0bbfba4125635822e0c5a755fb9276a604b39286959ab22dabc3104aa5d7e71358cd69d965de2a333ff04bf3e8ed43cf0296ac01264
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request May 20, 2019
…ages

5d08c9c Send the correct error code in reject messages (John Newbery)

Tree-SHA512: 0cd3ef3ae202584b138cc0bbfba4125635822e0c5a755fb9276a604b39286959ab22dabc3104aa5d7e71358cd69d965de2a333ff04bf3e8ed43cf0296ac01264
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Sep 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants