Skip to content

Check correct merkle leaf size and validate txids on Electrum#4675

Open
tnull wants to merge 3 commits into
lightningdevkit:mainfrom
tnull:2026-06-merkle-base-size
Open

Check correct merkle leaf size and validate txids on Electrum#4675
tnull wants to merge 3 commits into
lightningdevkit:mainfrom
tnull:2026-06-merkle-base-size

Conversation

@tnull

@tnull tnull commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Just a few minor Electrum/Esplora fixes.

These findings were discovered by Project Loupe.

tnull added 3 commits June 10, 2026 15:56
Electrum confirmation checks must reject transactions whose non-witness
serialization is 64 bytes, since txids and Merkle leaves are computed
from that serialization. Witness padding can otherwise move total_size
above 64 without removing the inner-node ambiguity.

Co-Authored-By: HAL 9000

This finding was discovered by Project Loupe
Esplora confirmation checks must use the non-witness transaction size
for the 64-byte Merkle leaf guard. Witness padding can otherwise raise
total_size without changing the serialization hashed into the txid and
Merkle tree.

Co-Authored-By: HAL 9000

This finding was discovered by Project Loupe
Electrum confirmations must reject transaction_get responses whose body
does not compute the requested txid. Otherwise a malicious server can
substitute an unrelated transaction and provide matching Merkle data for
the substituted body.

Co-Authored-By: HAL 9000

This finding was discovered by Project Loupe
@ldk-reviews-bot

ldk-reviews-bot commented Jun 10, 2026

Copy link
Copy Markdown

I've assigned @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

if tx.compute_txid() != txid {
log_error!(self.logger, "Retrieved transaction for txid {} doesn't match expectations. This should not happen. Please verify server integrity.", txid);
return Err(InternalError::Failed);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This watched-output spend path adds the txid verification but, unlike the watched-transactions path above (line 292), it never applies the is_potentially_unsafe_merkle_leaf check before accepting the spending tx via get_confirmed_tx. Esplora avoids this asymmetry by placing the base-size check inside its shared get_confirmed_tx, covering both paths. The 64-byte merkle-leaf weakness is exactly an attack that lets a crafted node pass validate_merkle_proof, so a malicious server could claim a watched output is spent by a 64-byte (base size) transaction and have it accepted here. Consider moving the base-size check into Electrum's get_confirmed_tx so both call sites are protected.

@ldk-claude-review-bot

ldk-claude-review-bot commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

I've re-reviewed the same commit (35e8dac) against my prior findings. The code is unchanged from my last pass — the output-spend asymmetry, stale comments, and brittle test are all still present and already flagged. No new issues surfaced on this pass.

Summary

The core fix is correct: base_size() is the right metric for the 64-byte merkle-leaf weakness (txid/merkle hashing uses the non-witness serialization), closing the gap where a witness tx with a 64-byte base could bypass the old total_size() == 64 check. The added Electrum txid verifications correctly mirror Esplora's existing one.

Previously flagged (not re-posted):

  • lightning-transaction-sync/src/electrum.rs:397 — The watched-output spend path verifies the txid but never applies the is_potentially_unsafe_merkle_leaf base-size check before accepting the spend via get_confirmed_tx, unlike the watched-transactions path (line 292). Esplora avoids this asymmetry by putting the check inside its shared get_confirmed_tx. This leaves residual 64-byte-leaf attack surface on the Electrum output-spend path; consider moving the check into Electrum's get_confirmed_tx.
  • Stale comments at electrum.rs:290-291 and esplora.rs:394-395 ("at least 65 bytes in length") no longer match the actual check (base size exactly 64).
  • The transaction_get_responses_are_verified_at_call_sites test (electrum.rs:531) uses include_str! + concat! to assert source text — brittle and only checks string presence, not behavior.

No additional issues found.

@ldk-reviews-bot ldk-reviews-bot requested a review from wpaulino June 10, 2026 14:24
@wpaulino

Copy link
Copy Markdown
Contributor

LGTM once the bot's comment is addressed

@tnull tnull self-assigned this Jun 11, 2026
@tnull tnull force-pushed the 2026-06-merkle-base-size branch from 57524e3 to 35e8dac Compare June 11, 2026 16:42
@tnull

tnull commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Updated to line-wrap commit messages.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @wpaulino! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants