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.

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