Skip to content

Commit 86e9da7

Browse files
committed
f Validate merkle inclusion proofs
1 parent 5d6e258 commit 86e9da7

File tree

1 file changed

+39
-2
lines changed

1 file changed

+39
-2
lines changed

lightning-transaction-sync/src/electrum.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ use crate::error::{TxSyncError, InternalError};
33

44
use electrum_client::Client as ElectrumClient;
55
use electrum_client::ElectrumApi;
6+
use electrum_client::GetMerkleRes;
67

78
use lightning::util::logger::Logger;
89
use lightning::{log_error, log_debug, log_trace};
910
use lightning::chain::WatchedOutput;
1011
use lightning::chain::{Confirm, Filter};
1112

1213
use bitcoin::{BlockHash, BlockHeader, Script, Txid};
14+
use bitcoin::hashes::Hash;
15+
use bitcoin::hashes::sha256d::Hash as Sha256d;
1316

1417
use std::ops::Deref;
1518
use std::sync::Mutex;
@@ -263,7 +266,10 @@ where
263266
debug_assert_eq!(prob_conf_height, merkle_res.block_height as u32);
264267
match self.client.block_header(prob_conf_height as usize) {
265268
Ok(block_header) => {
266-
// TODO can we check the merkle proof here to be sure?
269+
if !validate_merkle_proof(**txid, block_header.merkle_root.as_hash(), &merkle_res) {
270+
log_error!(self.logger, "Retrieved Merkle block for txid {} doesn't match expectations. This should not happen. Please verify server integrity.", txid);
271+
return Err(InternalError::Failed);
272+
}
267273
confirmed_txs.push(ConfirmedTx { tx: tx.clone(), block_header, block_height: prob_conf_height, pos: merkle_res.pos });
268274
}
269275
Err(e) => {
@@ -301,7 +307,10 @@ where
301307
debug_assert_eq!(prob_conf_height, merkle_res.block_height as u32);
302308
match self.client.block_header(prob_conf_height as usize) {
303309
Ok(block_header) => {
304-
// TODO can we check the merkle proof here to be sure?
310+
if !validate_merkle_proof(txid, block_header.merkle_root.as_hash(), &merkle_res) {
311+
log_error!(self.logger, "Retrieved Merkle block for txid {} doesn't match expectations. This should not happen. Please verify server integrity.", txid);
312+
return Err(InternalError::Failed);
313+
}
305314
confirmed_txs.push(ConfirmedTx { tx: tx.clone(), block_header, block_height: prob_conf_height, pos: merkle_res.pos });
306315
}
307316
Err(e) => {
@@ -386,6 +395,34 @@ where
386395

387396
}
388397

398+
fn validate_merkle_proof(txid: Txid, merkle_root: Sha256d, merkle_res: &GetMerkleRes) -> bool {
399+
let mut hashes = Vec::new();
400+
for m in &merkle_res.merkle {
401+
let mut reversed = Vec::with_capacity(32);
402+
reversed.truncate(0);
403+
reversed.extend(m.iter().rev());
404+
let h = Sha256d::from_slice(&reversed).unwrap();
405+
hashes.push(h);
406+
}
407+
408+
let mut index = merkle_res.pos;
409+
let mut cur = txid.as_hash();
410+
hashes.reverse();
411+
412+
while let Some(next_hash) = hashes.pop() {
413+
let (left, right) = if index % 2 == 0 {
414+
(cur, next_hash)
415+
} else {
416+
(next_hash, cur)
417+
};
418+
419+
let data = [&left[..], &right[..]].concat();
420+
cur = Sha256d::hash(&data);
421+
index /= 2;
422+
}
423+
cur == merkle_root
424+
}
425+
389426
impl<L: Deref> Filter for ElectrumSyncClient<L>
390427
where
391428
L::Target: Logger,

0 commit comments

Comments
 (0)