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
validation: log which peer sent us a header #27826
base: master
Are you sure you want to change the base?
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
There's various ways in which It's probably why I saw so many log entries:
Moving to draft while I give it more thought. |
Added a check to ensure the header was valid and newly seen before we log it. That matches the behaviour from before this PR. |
@@ -4268,8 +4295,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, | |||
} | |||
|
|||
if (received_new_header) { |
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.
Since it's possible for ProcessNewBlockHeaders
to return false without us returning, I wonder if we should only log if it returned true
(as we do above).
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 don't think this is possible though?
The call structure is ProcessNewBlockHeaders()
-> AcceptBlockHeader()
-> {CheckBlockHeader()
/ ContextualCheckBlockHeader()
}, and the latter two functions return either true
or state.Invalid(...)
. As a result, ProcessNewBlockHeaders()
would always return the invalid state
of the first header that fails, and net_processing
would abort.
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 think the assert()
however is too strong. Relying on the chain described by @mzumsande above looks to fragile to me. To assert in such a way, IMO ProcessNewBlockHeaders()
should clearly document that it will always set the pointer when it returns true
and if it returns false
, then the state
is invalid. Then the code above should be changed from if (!ProcessNewBlockHeaders...) { if (state.IsInvalid...
to maybe assert that the state is invalid or no checking the state at all.
It looks safer to me to remove assert(pindex);
and change the condition to:
if (received_new_header && pindex != nullptr) {
Edit: now I see that assert(pindex);
existed before this PR and this PR just moves it upwards.
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.
1a5b9d4: I'd like to leave dealing with the assert to a (up for grabs) followup.
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.
Tested ACK b857d92
I like that this makes logging less verbose (by logging one less entry, and by only logging the last header when multiple headers arrive at the same time).
@@ -4268,8 +4295,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, | |||
} | |||
|
|||
if (received_new_header) { |
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 don't think this is possible though?
The call structure is ProcessNewBlockHeaders()
-> AcceptBlockHeader()
-> {CheckBlockHeader()
/ ContextualCheckBlockHeader()
}, and the latter two functions return either true
or state.Invalid(...)
. As a result, ProcessNewBlockHeaders()
would always return the invalid state
of the first header that fails, and net_processing
would abort.
I still have to rebase this, will do soon(tm). |
b857d92
to
deef9d3
Compare
Rebased! (after #26969) |
Concept ACK |
Rebased after #28218 (no longer needs |
deef9d3
to
050c345
Compare
050c345
to
fe62a60
Compare
Added support for |
I get between 0 and 2 of these unsolicited blocks per day:
|
a460dea
to
708600f
Compare
@mzumsande can you take another look? |
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.
ACK 708600f
As with the previous ACK: would be nice if other reviewers assess #27826 (comment).
src/net_processing.cpp
Outdated
via_compact_block ? "cmpctblock " : "", | ||
index.GetBlockHash().ToString(), | ||
index.nHeight, | ||
via_unsolicited_block ? " via unsolicited block" : "", |
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 like the first commit, but the second/third commit are not ideal, because there is a difference between a new block and a new header:
I started up this branch on mainnet while being a few blocks behind (but not that far such that I'd be in IBD so that the log messages would go to BCLog::VALIDATION
).
Then, the log entries I'd get for each downloaded block ("Saw new header via unsolicited block") are wrong in two ways:
The header is not new (only the block), plus the block is not unsolicited (we asked for it with a getdata
message).
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.
That indeed sounds like two bugs, will try to reproduce.
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 can reproduce the issue. When the nodes catches up, for a small fraction of blocks it says "Saw new header via unsolicited block". The header refers to a block that was already connected, sometimes more than 25 blocks ago.
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 solved the second issue by setting /*via_unsolicited_block=*/!is_block_requested
(renamed forceProcessing
). I'll push that in a bit.
I'm still debugging the first issue. E.g an example log during IBD, with -debug=net -debug=validation -loglevel=validation:debug
:
2023-12-04T13:47:10Z Synchronizing blockheaders, height: 819750 (~100.00%)
2023-12-04T13:47:10Z [validation:debug] Saw new header hash=00000000000000000002d311ad8da2e7551130798984e20c87b8156c3d27d261 height=819750 peer=1
2023-12-04T13:47:10Z [net] sending sendheaders (0 bytes) peer=1
2023-12-04T13:47:10Z [net] Requesting block 00000000000000000000cb25399b73ca8d7233d889d6f593185d2d9d8c1c12a4 (819343) peer=1
...
2023-12-04T13:47:10Z [net] Requesting block 00000000000000000000f2a44776f015e8d0fed852974feaa3a4fef29db93863 (819346) peer=1
…
2023-12-04T13:47:11Z [net] received: block (1659646 bytes) peer=1
2023-12-04T13:47:11Z [net] received block 00000000000000000000f2a44776f015e8d0fed852974feaa3a4fef29db93863 peer=1
2023-12-04T13:47:12Z [validation] Pre-allocating up to position 0x900000 in rev03971.dat
2023-12-04T13:47:12Z [validation] BlockChecked: block hash=00000000000000000000f2a44776f015e8d0fed852974feaa3a4fef29db93863 state=Valid
2023-12-04T13:47:12Z UpdateTip: new best=00000000000000000000f2a44776f015e8d0fed852974feaa3a4fef29db93863 height=819346 version=0x32000000 log2_work=94.574638 tx=928820934 date='2023-12-01T22:28:26Z' progress=0.998647 cache=153.7MiB(1297209txo)
2023-12-04T13:47:12Z [validation] Enqueuing BlockConnected: block hash=00000000000000000000f2a44776f015e8d0fed852974feaa3a4fef29db93863 block height=819346
2023-12-04T13:47:12Z [validation] Enqueuing UpdatedBlockTip: new block hash=00000000000000000000f2a44776f015e8d0fed852974feaa3a4fef29db93863 fork block hash=000000000000000000016a22934809aba22dda98fe8a0ee1582b43fe109aebe8 (in IBD=true)
2023-12-04T13:47:12Z [validation:debug] Saw new header hash=00000000000000000000f2a44776f015e8d0fed852974feaa3a4fef29db93863 peer=1
…
2023-12-04T13:47:12Z [validation] BlockConnected: block hash=00000000000000000000f2a44776f015e8d0fed852974feaa3a4fef29db93863 block height=819346
2023-12-04T13:47:12Z [validation] UpdatedBlockTip: new block hash=00000000000000000000f2a44776f015e8d0fed852974feaa3a4fef29db93863 fork block hash=000000000000000000016a22934809aba22dda98fe8a0ee1582b43fe109aebe8 (in IBD=true)
So we already this header from an earlier header sync, yet ProcessBlock
returns true
which it should only do if the header is new.
Well, according to the documentation that I added myself in the second commit 386febd anyway.
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.
Ah, but that documentation is wrong ambiguous. ProcessBlock
only tells us if the block is new, not if the header is new. The latter is checked by AcceptBlockHeader
, but not returned.
708600f
to
9d4e5af
Compare
9d4e5af
to
516b547
Compare
Supports -logips Since 2c3a90f we log received headers. For compact blocks we also log which peer sent it (e5ce857), but not for regular headers. That required an additional refactor, which this commit provides, to move the logging from validation to net_processing. This also reduces the number of log entries (under default configuration) per compact block header from 3 to 2: one for the header and one for the connected tip.
516b547
to
e3f7529
Compare
Rebased to use the new logging functions |
@0xB10C you may also find this interesting. |
Fixes #27744
Since #27278 we log received headers. For compact blocks we also log which peer sent it (e5ce857), but not for regular headers. That required an additional refactor, which this PR provides.
Move the logging from validation to net_processing.
This also reduces the number of log entries (under default configuration) per compact block header from 3 to 2: one for the header and one for the connected tip.
The PR introduces a new helper method
LogBlockHeader
.When receiving a compact block we call
LogBlockHeader
from the exact same place as where we previously logged. So that log message doesn't change. What does change is that we no longer also log fromAcceptBlockHeader
.When receiving a regular header(s) message, we only log the last one. This is a change in behaviour because it was simpler to implement, but it's probably better anyway. It does mean that if a peer sends of a bunch of headers of which any is invalid, we won't log it (here).
Lastly I expanded the code comment explaining why we log this. It initially only covered selfish mining, but we also care about peers sending us headers but not following up (see e.g. #27626).
Example log: