Validate Esplora merkle proof against the block header's merkle root#4596
Conversation
`EsploraSyncClient::get_confirmed_tx` parsed the SPV proof returned by
the Esplora server but threw away the security check: the merkle root
computed by `PartialMerkleTree::extract_matches` was discarded
(`let _ = …`), and only the leaf-equality check (`matches[0] == txid`)
remained. Anyone can construct a single-leaf partial tree advertising
an arbitrary txid via `PartialMerkleTree::from_txids(&[txid], &[true])`,
so this gate was vacuous.
A malicious or compromised Esplora server could therefore convince
`EsploraSyncClient` that any transaction was confirmed in any block by
returning `MerkleBlock { header: real_header, txn: forged_partial_tree }`,
causing LDK to feed a synthesized `ConfirmedTx` into `Confirm`
implementations such as `ChannelManager` / `ChainMonitor`. From there,
the channel-funding / closing / HTLC flows would treat the transaction
as confirmed at an attacker-chosen height, with consequences ranging
from premature state transitions to force-close races.
Capture the merkle root returned by `extract_matches` and require it
to equal `block_header.merkle_root`, matching the validation the
Electrum sibling already performs via `validate_merkle_proof`.
Co-Authored-By: HAL 9000
|
👋 I see @joostjager was un-assigned. |
|
The fix is straightforward and correct. Let me verify the full diff one more time to make sure I haven't missed anything. The change is isolated to 6 lines in
No issues found in the code change itself. The one concern worth raising is the lack of a regression test. No issues found. Cross-cutting concern:
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4596 +/- ##
==========================================
- Coverage 86.84% 86.22% -0.62%
==========================================
Files 161 159 -2
Lines 109260 109170 -90
Branches 109260 109170 -90
==========================================
- Hits 94882 94136 -746
- Misses 11797 12424 +627
- Partials 2581 2610 +29
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
EsploraSyncClient::get_confirmed_txparsed the SPV proof returned by the Esplora server but threw away the security check: the merkle root computed byPartialMerkleTree::extract_matcheswas discarded (let _ = …), and only the leaf-equality check (matches[0] == txid) remained. Anyone can construct a single-leaf partial tree advertising an arbitrary txid viaPartialMerkleTree::from_txids(&[txid], &[true]), so this gate was vacuous.Note: I recall that at implementation time we decided doing this check wasn't necessary/redundant. But, it can surely not hurt to be more strict here.