@@ -205,12 +205,10 @@ async fn fetch_block<S: Sleeper>(
205
205
206
206
// We avoid fetching blocks higher than previously fetched `latest_blocks` as the local chain
207
207
// tip is used to signal for the last-synced-up-to-height.
208
- let & tip_height = latest_blocks
209
- . keys ( )
210
- . last ( )
211
- . expect ( "must have atleast one entry" ) ;
212
- if height > tip_height {
213
- return Ok ( None ) ;
208
+ if let Some ( tip_height) = latest_blocks. keys ( ) . last ( ) . copied ( ) {
209
+ if height > tip_height {
210
+ return Ok ( None ) ;
211
+ }
214
212
}
215
213
216
214
Ok ( Some ( client. get_block_hash ( height) . await ?) )
@@ -227,27 +225,36 @@ async fn chain_update<S: Sleeper>(
227
225
anchors : & BTreeSet < ( ConfirmationBlockTime , Txid ) > ,
228
226
) -> Result < CheckPoint , Error > {
229
227
let mut point_of_agreement = None ;
228
+ let mut local_cp_hash = local_tip. hash ( ) ;
230
229
let mut conflicts = vec ! [ ] ;
230
+
231
231
for local_cp in local_tip. iter ( ) {
232
232
let remote_hash = match fetch_block ( client, latest_blocks, local_cp. height ( ) ) . await ? {
233
233
Some ( hash) => hash,
234
234
None => continue ,
235
235
} ;
236
236
if remote_hash == local_cp. hash ( ) {
237
- point_of_agreement = Some ( local_cp. clone ( ) ) ;
237
+ point_of_agreement = Some ( local_cp) ;
238
238
break ;
239
- } else {
240
- // it is not strictly necessary to include all the conflicted heights (we do need the
241
- // first one) but it seems prudent to make sure the updated chain's heights are a
242
- // superset of the existing chain after update.
243
- conflicts. push ( BlockId {
244
- height : local_cp. height ( ) ,
245
- hash : remote_hash,
246
- } ) ;
247
239
}
240
+ local_cp_hash = local_cp. hash ( ) ;
241
+ // It is not strictly necessary to include all the conflicted heights (we do need the
242
+ // first one) but it seems prudent to make sure the updated chain's heights are a
243
+ // superset of the existing chain after update.
244
+ conflicts. push ( BlockId {
245
+ height : local_cp. height ( ) ,
246
+ hash : remote_hash,
247
+ } ) ;
248
248
}
249
249
250
- let mut tip = point_of_agreement. expect ( "remote esplora should have same genesis block" ) ;
250
+ let mut tip = match point_of_agreement {
251
+ Some ( tip) => tip,
252
+ None => {
253
+ return Err ( Box :: new ( esplora_client:: Error :: HeaderHashNotFound (
254
+ local_cp_hash,
255
+ ) ) ) ;
256
+ }
257
+ } ;
251
258
252
259
tip = tip
253
260
. extend ( conflicts. into_iter ( ) . rev ( ) )
@@ -545,7 +552,7 @@ mod test {
545
552
local_chain:: LocalChain ,
546
553
BlockId ,
547
554
} ;
548
- use bdk_core:: ConfirmationBlockTime ;
555
+ use bdk_core:: { bitcoin , ConfirmationBlockTime } ;
549
556
use bdk_testenv:: { anyhow, bitcoincore_rpc:: RpcApi , TestEnv } ;
550
557
use esplora_client:: Builder ;
551
558
@@ -557,6 +564,41 @@ mod test {
557
564
} } ;
558
565
}
559
566
567
+ // Test that `chain_update` fails due to wrong network.
568
+ #[ tokio:: test]
569
+ async fn test_chain_update_wrong_network_error ( ) -> anyhow:: Result < ( ) > {
570
+ let env = TestEnv :: new ( ) ?;
571
+ let base_url = format ! ( "http://{}" , & env. electrsd. esplora_url. clone( ) . unwrap( ) ) ;
572
+ let client = Builder :: new ( base_url. as_str ( ) ) . build_async ( ) ?;
573
+ let initial_height = env. rpc_client ( ) . get_block_count ( ) ? as u32 ;
574
+
575
+ let mine_to = 16 ;
576
+ let _ = env. mine_blocks ( ( mine_to - initial_height) as usize , None ) ?;
577
+ while client. get_height ( ) . await ? < mine_to {
578
+ std:: thread:: sleep ( Duration :: from_millis ( 64 ) ) ;
579
+ }
580
+ let latest_blocks = fetch_latest_blocks ( & client) . await ?;
581
+ assert ! ( !latest_blocks. is_empty( ) ) ;
582
+ assert_eq ! ( latest_blocks. keys( ) . last( ) , Some ( & mine_to) ) ;
583
+
584
+ let genesis_hash =
585
+ bitcoin:: constants:: genesis_block ( bitcoin:: Network :: Testnet4 ) . block_hash ( ) ;
586
+ let cp = bdk_chain:: CheckPoint :: new ( BlockId {
587
+ height : 0 ,
588
+ hash : genesis_hash,
589
+ } ) ;
590
+
591
+ let anchors = BTreeSet :: new ( ) ;
592
+ let res = chain_update ( & client, & latest_blocks, & cp, & anchors) . await ;
593
+ use esplora_client:: Error ;
594
+ assert ! (
595
+ matches!( * res. unwrap_err( ) , Error :: HeaderHashNotFound ( hash) if hash == genesis_hash) ,
596
+ "`chain_update` should error if it can't connect to the local CP" ,
597
+ ) ;
598
+
599
+ Ok ( ( ) )
600
+ }
601
+
560
602
/// Ensure that update does not remove heights (from original), and all anchor heights are
561
603
/// included.
562
604
#[ tokio:: test]
0 commit comments