@@ -3,13 +3,16 @@ use crate::error::{TxSyncError, InternalError};
3
3
4
4
use electrum_client:: Client as ElectrumClient ;
5
5
use electrum_client:: ElectrumApi ;
6
+ use electrum_client:: GetMerkleRes ;
6
7
7
8
use lightning:: util:: logger:: Logger ;
8
9
use lightning:: { log_error, log_debug, log_trace} ;
9
10
use lightning:: chain:: WatchedOutput ;
10
11
use lightning:: chain:: { Confirm , Filter } ;
11
12
12
13
use bitcoin:: { BlockHash , BlockHeader , Script , Txid } ;
14
+ use bitcoin:: hashes:: Hash ;
15
+ use bitcoin:: hashes:: sha256d:: Hash as Sha256d ;
13
16
14
17
use std:: ops:: Deref ;
15
18
use std:: sync:: Mutex ;
@@ -263,7 +266,10 @@ where
263
266
debug_assert_eq ! ( prob_conf_height, merkle_res. block_height as u32 ) ;
264
267
match self . client . block_header ( prob_conf_height as usize ) {
265
268
Ok ( block_header) => {
266
- // TODO can we check the merkle proof here to be sure?
269
+ if !self . validate_merkle_proof ( * * txid, block_header. merkle_root . as_hash ( ) , & merkle_res) ? {
270
+ log_trace ! ( self . logger, "Inconsistency: Block {} was unconfirmed during syncing." , block_header. block_hash( ) ) ;
271
+ return Err ( InternalError :: Inconsistency ) ;
272
+ }
267
273
confirmed_txs. push ( ConfirmedTx { tx : tx. clone ( ) , block_header, block_height : prob_conf_height, pos : merkle_res. pos } ) ;
268
274
}
269
275
Err ( e) => {
@@ -301,7 +307,10 @@ where
301
307
debug_assert_eq ! ( prob_conf_height, merkle_res. block_height as u32 ) ;
302
308
match self . client . block_header ( prob_conf_height as usize ) {
303
309
Ok ( block_header) => {
304
- // TODO can we check the merkle proof here to be sure?
310
+ if !self . validate_merkle_proof ( txid, block_header. merkle_root . as_hash ( ) , & merkle_res) ? {
311
+ log_trace ! ( self . logger, "Inconsistency: Block {} was unconfirmed during syncing." , block_header. block_hash( ) ) ;
312
+ return Err ( InternalError :: Inconsistency ) ;
313
+ }
305
314
confirmed_txs. push ( ConfirmedTx { tx : tx. clone ( ) , block_header, block_height : prob_conf_height, pos : merkle_res. pos } ) ;
306
315
}
307
316
Err ( e) => {
@@ -384,6 +393,31 @@ where
384
393
& self . client
385
394
}
386
395
396
+ fn validate_merkle_proof ( & self , txid : Txid , merkle_root : Sha256d , merkle_res : & GetMerkleRes ) -> Result < bool , InternalError > {
397
+ let mut index = merkle_res. pos ;
398
+ let mut cur = txid. as_hash ( ) ;
399
+ for bytes in & merkle_res. merkle {
400
+ let mut reversed = Vec :: with_capacity ( 32 ) ;
401
+ reversed. truncate ( 0 ) ;
402
+ reversed. extend ( bytes. iter ( ) . rev ( ) ) ;
403
+ let next_hash = Sha256d :: from_slice ( & reversed) . map_err ( |_| {
404
+ log_error ! ( self . logger, "Failed due to the server sending us bogus transaction data. This should not happen. Please verify server integrity." ) ;
405
+ InternalError :: Failed
406
+ } ) ?;
407
+
408
+ let ( left, right) = if index % 2 == 0 {
409
+ ( cur, next_hash)
410
+ } else {
411
+ ( next_hash, cur)
412
+ } ;
413
+
414
+ let data = [ & left[ ..] , & right[ ..] ] . concat ( ) ;
415
+ cur = Sha256d :: hash ( & data) ;
416
+ index /= 2 ;
417
+ }
418
+
419
+ Ok ( cur == merkle_root)
420
+ }
387
421
}
388
422
389
423
impl < L : Deref > Filter for ElectrumSyncClient < L >
0 commit comments