Skip to content

Commit f527cec

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

File tree

1 file changed

+33
-2
lines changed

1 file changed

+33
-2
lines changed

lightning-transaction-sync/src/electrum.rs

+33-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,28 @@ where
386395

387396
}
388397

398+
fn validate_merkle_proof(txid: Txid, merkle_root: Sha256d, merkle_res: &GetMerkleRes) -> bool {
399+
let mut index = merkle_res.pos;
400+
let mut cur = txid.as_hash();
401+
for bytes in &merkle_res.merkle {
402+
let mut reversed = Vec::with_capacity(32);
403+
reversed.truncate(0);
404+
reversed.extend(bytes.iter().rev());
405+
let next_hash = Sha256d::from_slice(&reversed).unwrap();
406+
407+
let (left, right) = if index % 2 == 0 {
408+
(cur, next_hash)
409+
} else {
410+
(next_hash, cur)
411+
};
412+
413+
let data = [&left[..], &right[..]].concat();
414+
cur = Sha256d::hash(&data);
415+
index /= 2;
416+
}
417+
cur == merkle_root
418+
}
419+
389420
impl<L: Deref> Filter for ElectrumSyncClient<L>
390421
where
391422
L::Target: Logger,

0 commit comments

Comments
 (0)