8000 p2p: Add witness mutation check inside FillBlock by instagibbs · Pull Request #32646 · bitcoin/bitcoin · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

p2p: Add witness mutation check inside FillBlock #32646

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

instagibbs
Copy link
Member
@instagibbs instagibbs commented May 30, 2025

Since #29412, we have not allowed mutated blocks to continue being processed immediately the block is received, but this is only done for the legacy BLOCK message.

Extend these checks as belt-and-suspenders to not allow similar mutation strategies to affect relay by honest peers by applying the check inside PartiallyDownloadedBlock::FillBlock, immediately before returning READ_STATUS_OK.

@DrahtBot
Copy link
Contributor
DrahtBot commented May 30, 2025

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32646.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK Crypt-iQ, achow101, stratospher
Concept ACK dergoegge

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

@DrahtBot DrahtBot added the P2P label May 30, 2025
@instagibbs
Copy link
Member Author

cc @dergoegge @Crypt-iQ

@instagibbs instagibbs force-pushed the 2025-05-fillblock-mutated branch 2 times, most recently from 7224acf to 0d4f232 Compare May 30, 2025 13:54
@DrahtBot
Copy link
Contributor

🚧 At least one of the CI tasks failed.
Task lint: https://github.com/bitcoin/bitcoin/runs/43189542399
LLM reason (✨ experimental): Lint errors caused the CI failure.

Hints

Try to run the tests locally, according to the documentation. However, a CI failure may still
happen due to a number of reasons, for example:

  • Possibly due to a silent merge conflict (the changes in this pull request being
    incompatible with the current code in the target branch). If so, make sure to rebase on the latest
    commit of the target branch.

  • A sanitizer issue, which can only be found by compiling with the sanitizer and running the
    affected test.

  • An intermittent issue.

Leave a comment here, if you need help tracking down a confusing failure.

@instagibbs instagibbs force-pushed the 2025-05-fillblock-mutated branch from 0d4f232 to 2b15ec7 Compare May 30, 2025 14:04
@Crypt-iQ
Copy link
Contributor

Concept ACK

10000
@@ -218,6 +218,13 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
return READ_STATUS_CHECKBLOCK_FAILED;
}

// Belt-and-suspenders check for other mutations now that we have a seemingly good block
if (IsBlockMutated(/*block=*/block,
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this be an opportunity to resolve the TODO comment above after check_block by not calling this function anymore? If I understand the comment correctly it only calls CheckBlock() to allow the caching of the check result and avoid repeated Merkle checks, which IsBlockMutated() also would do today?

Copy link
Member Author

Choose a reason for hiding this comment

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

Would we be able to distinguish shorttxid collision from an intentionally mutated block without the check?

Copy link
Contributor

Choose a reason for hiding this comment

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

For the sake of disconnecting the peer? I think we could use the flags set or not set in the block to learn which mutation check (CheckMerkleRoot() or CheckWitnessMalleation()) has failed.

Copy link
Member Author

Choose a reason for hiding this comment

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

@mzumsande pushed as a follow-up commit. lmk what you think.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, this strategy is unneeded. Both merkle checks can fail due to collisions, and either may need full blocks fetched if they're from the first peer. Pushed a change that is simpler and cleaned up READ_STATUS_CHECKBLOCK_FAILED which is no longer necessary.

Copy link
Contributor

Choose a reason for hiding this comment

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

Both merkle checks can fail due to collisions

Interesting, I did not know this. Since the header merkle check is first... if the witness merkle check fails due to collision, does that mean that the transaction that collided had the same txid but a different wtxid? Or is there another way for short ID collisions to affect the witness merkle check?

Copy link
Member

Choose a reason for hiding this comment

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

if the witness merkle check fails due to collision, does that mean that the transaction that collided had the same txid but a different wtxid?

That would be my understanding as well, i.e. the txs are only different in their witness. Pretty sure that is the only scenario, as otherwise the prior merkle root check would fail.

Copy link
Member
@dergoegge dergoegge left a comment

Choose a reason for hiding this comment

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

Concept ACK

@instagibbs instagibbs force-pushed the 2025-05-fillblock-mutated branch from a585820 to a7cbaca Compare June 3, 2025 14:38
@instagibbs instagibbs changed the title p2p: Add mutation check inside compact block's FillBlock p2p: Add witness mutation check inside FillBlock Jun 3, 2025
@@ -218,6 +218,13 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
return READ_STATUS_CHECKBLOCK_FAILED;
}

// Belt-and-suspenders check for other mutations now that we have a seemingly good block
if (IsBlockMutated(/*block=*/block,
Copy link
Contributor

Choose a reason for hiding this comment

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

Both merkle checks can fail due to collisions

Interesting, I did not know this. Since the header merkle check is first... if the witness merkle check fails due to collision, does that mean that the transaction that collided had the same txid but a different wtxid? Or is there another way for short ID collisions to affect the witness merkle check?

@instagibbs instagibbs force-pushed the 2025-05-fillblock-mutated branch from a7cbaca to 8ce8e93 Compare June 10, 2025 13:59
Since bitcoin#29412, we have not allowed mutated blocks to continue
being processed immediately the block is received, but this
is only done for the legacy BLOCK message.

Extend these checks as belt-and-suspenders to not allow
similar mutation strategies to affect relay by honest peers
by applying the check inside
PartiallyDownloadedBlock::FillBlock, immediately before
returning READ_STATUS_OK.

This also removes the extraneous CheckBlock call.
@instagibbs instagibbs force-pushed the 2025-05-fillblock-mutated branch from 8ce8e93 to 28299ce Compare June 10, 2025 14:08
@Crypt-iQ
Copy link
Contributor

ACK 28299ce

@DrahtBot DrahtBot requested a review from dergoegge June 10, 2025 16:30
@achow101
Copy link
Member

ACK 28299ce

Copy link
Contributor
@stratospher stratospher left a comment

Choose a reason for hiding this comment

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

ACK 28299ce.

makes sense to just do mutation check at this stage, CheckBlock checks are done later in ProcessBlock anyways.

compared the logs in the compact block functional tests on master and PR.

  • on master (when witness mutation happens - CheckBlock passes, later checks in ProcessNewBlock leads to failure)
 node0 2025-06-19T07:17:31.494150Z [msghand] [blockencodings.cpp:260] [FillBlock] ### FillBlock: CheckBlock success 
 node0 2025-06-19T07:17:31.494249Z [msghand] [blockencodings.cpp:263] [FillBlock] ### Successfully reconstructed block 342e2a5c1ffe8bdf9df52480eae1d8f8cb0f6b2fa12839e0e688c4e3c0ac08f7 with 1 txn prefilled, 5 txn from mempool (incl at least 0 from extra pool) and 0 txn (0 bytes) requested 
 node0 2025-06-19T07:17:31.495905Z [msghand] [validation.cpp:4608] [ProcessNewBlock] [error] ProcessNewBlock: AcceptBlock FAILED (unexpected-witness, CheckWitnessMalleation : unexpected witness data found) 
  • in PR (when witness mutation happens - IsBlockMutated fails and we don't need to wait till ProcessNewBlock to detect failure)
node0 2025-06-19T07:09:06.235430Z [msghand] [validation.cpp:4030] [CheckWitnessMalleation] ### CheckWitnessMalleation: BLOCK_MUTATED unexpected-witness 
node0 2025-06-19T07:09:06.235860Z [msghand] [blockencodings.cpp:236] [FillBlock] ### FillBlock: CheckBlock success 
node0 2025-06-19T07:09:06.236283Z [msghand] [net_processing.cpp:3385] [ProcessCompactBlockTxns] [net] Peer 1 sent us a compact block but it failed to reconstruct, waiting on first download to complete 

@stratospher
Copy link
Contributor

could also remove the forward declaration of now unused classes in src/blockencodings.h

class BlockValidationState;
namespace Consensus {
struct Params;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants
0