p2p: log mutated blocks when unknown & valid PoW#29549
p2p: log mutated blocks when unknown & valid PoW#295490xB10C wants to merge 1 commit intobitcoin:masterfrom
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. 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. |
|
I'm interested in conceptual review on:
|
34e1777 to
5fce7ef
Compare
|
🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the Possibly this is due to a silent merge conflict (the changes in this pull request being Leave a comment here, if you need help tracking down a confusing failure. |
net_processing.cpp:4733:53: error: cannot call function 'MaybeCheckNotHeld' while mutex 'cs_main' is held [-Werror,-Wthread-safety-analysis]
const CBlockIndex* mutated_block{WITH_LOCK(m_chainman.GetMutex(), return m_chainman.m_blockman.LookupBlockIndex(pblock->GetHash()))};
^
./sync.h:301:30: note: expanded from macro 'WITH_LOCK'
#define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }())
^
1 error generated. |
Old blocks can cheaply be mutated and send to us. We don't log when receiving these. However, blocks with a valid PoW (above our DoS threshold) that we didn't know about could indicate a problem, for example, on the miner side. By logging about these mutated blocks, we make it easier to notice and debug these cases.
5fce7ef to
ffd22c8
Compare
|
Dropped the |
|
Somewhat related: #27277 There I'm trying to declutter |
| { | ||
| LOCK(cs_main); | ||
| RemoveBlockRequest(pblock->GetHash(), peer->m_id); | ||
| if (prev_block && prev_block->nChainWork + CalculateHeadersWork({pblock->GetBlockHeader()}) >= GetAntiDoSWorkThreshold()) { |
There was a problem hiding this comment.
| if (prev_block && prev_block->nChainWork + CalculateHeadersWork({pblock->GetBlockHeader()}) >= GetAntiDoSWorkThreshold()) { | |
| if (prev_block->nChainWork + CalculateHeadersWork({pblock->GetBlockHeader()}) >= GetAntiDoSWorkThreshold()) { |
| expected_msgs=["bad-witness-nonce-size, CheckWitnessMalleation", LOGMESSAGE_REJECTED_MUTATED_BLOCK]): | ||
| attacker.send_message(msg_block(mutated_block)) | ||
| attacker.wait_for_disconnect(timeout=5) | ||
|
|
There was a problem hiding this comment.
Logic seems wrong, and here's a test which catches it. We'll make an unconditional log for every mutated block, over and over. To mitigate this we would need to accept the header of the mutated block and use that to rate-limit the log.
| # But won't log a second time | |
| attacker = self.nodes[0].add_p2p_connection(P2PInterface()) | |
| with self.nodes[0].assert_debug_log( | |
| expected_msgs=["Received mutated block from peer"], # have it wait for this to make sure unexpected has time | |
| unexpected_msgs=["bad-witness-nonce-size, CheckWitnessMalleation", LOGMESSAGE_REJECTED_MUTATED_BLOCK]): | |
| attacker.send_message(msg_block(mutated_block)) | |
| attacker.wait_for_disconnect(timeout=5) | |
There was a problem hiding this comment.
This was mentioned in #29412 (comment) too. The current implementation in this PR is the same behavior as before #29412. The logging is somewhat rate-limited by needing to open a new connection each time.
I agree that re-introducing this isn't ideal. As this would partially revert #29412 again, I don't think it's worth having this on-by-default logging: #29549 (comment)
src/net_processing.cpp
Outdated
There was a problem hiding this comment.
This is not an actual proof-of-work check. CalculateHeadersWork only returns the work claimed by the header but doesn't check whether the header actually has valid work. So the log is trivial to trigger by anyone.
There was a problem hiding this comment.
that's a very unfortunate name for the function
There was a problem hiding this comment.
And it's bitten people before, see e.g. #27278 (comment)
|
I came to the conclusion that checking for mutations early (before we even look at the header), isn't trivially compatible with doing anything that's potentially DoSable (e.g. logging), even if we disconnect after each mutated block. As mentioned in #29412 (comment) and #29549 (comment), we would want to check (and accept) the header before logging, which would partially revert #29412. I don't think logging mutated blocks is worth doing this. Additionally, this is only a problem if the mutated block is submitted over a custom/broken P2P client that doesn't itself check the block. Any miner using If still wanted, it's probably better to log mutated blocks in |
eb7cc9f Rename CalculateHeadersWork to CalculateClaimedHeadersWork (Greg Sanders) Pull request description: And clean up some comments. Confusion about what this is doing seems to be a running theme: #29549 (comment) #27278 (comment) ACKs for top commit: achow101: ACK eb7cc9f pablomartin4btc: ACK eb7cc9f 0xB10C: ACK eb7cc9f dergoegge: ACK eb7cc9f BrandonOdiwuor: ACK eb7cc9f Tree-SHA512: 6ccbc5e417155516487bb220753d189b5341dec05366db88a3fa5b1932eace21fbfaf23408c639bb54b36169a8d0a7536a1ee5e63b4ce5a3b70f2ff8407b6e07
Follow-up to #29412. Before 29412, we were logging an error when receiving an unknown mutated block with a valid PoW. With 29412, we only log about mutated blocks to the
netdebug logging category. As the mutated block logging is and has been useful for detecting and debugging problem of some mining pools, it's re-added here. See the conversation starting here: #29412 (comment).Old blocks can cheaply be mutated and send to us. We don't log when receiving these. However, blocks with a valid PoW (above our DoS threshold) that we didn't know about could indicate a problem, for example, on the miner side. By logging about these mutated blocks, we make it easier to notice and debug these cases.