@@ -9,7 +9,7 @@ use bip157::{
99 HashCheckpoint , Header , IndexedBlock , Info , Node as KyotoNode , Requester , TrustedPeer , Warning ,
1010} ;
1111use bitcoin:: { BlockHash , FeeRate , Script , ScriptBuf , Txid } ;
12- use lightning:: chain:: { Listen , WatchedOutput } ;
12+ use lightning:: chain:: { BlockLocator , Listen , WatchedOutput } ;
1313
1414use tokio:: sync:: { mpsc, oneshot} ;
1515
@@ -61,9 +61,20 @@ pub struct CbfChainSource {
6161}
6262
6363enum ChainOp {
64- ConnectFull { block_rx : oneshot:: Receiver < Result < IndexedBlock , FetchBlockError > > } ,
65- ConnectFiltered { header : Header , height : u32 } ,
66- //Reorg { /* accepted / reorganized from BlockHeaderChanges */ },
64+ ConnectFull {
65+ block_rx : oneshot:: Receiver < Result < IndexedBlock , FetchBlockError > > ,
66+ } ,
67+ ConnectFiltered {
68+ header : Header ,
69+ height : u32 ,
70+ } ,
71+ Disconnect {
72+ fork_point : BlockLocator ,
73+ } ,
74+ /// Marks reaching the chain tip.
75+ Synced {
76+ tip_height : u32 ,
77+ } ,
6778}
6879
6980struct BlockApplicator {
@@ -82,9 +93,16 @@ impl BlockApplicator {
8293 Err ( _) => log_error ! ( self . logger, "block oneshot dropped" ) ,
8394 } ,
8495 ChainOp :: ConnectFiltered { header, height } => {
85- self . chain_listener . filtered_block_connected ( & header, & [ ] , height)
96+ self . chain_listener . filtered_block_connected ( & header, & [ ] , height) ;
97+ } ,
98+ ChainOp :: Disconnect { fork_point } => {
99+ self . chain_listener . blocks_disconnected ( fork_point) ;
100+ } ,
101+ ChainOp :: Synced { tip_height } => {
102+ log_info ! ( self . logger, "CBF caught up to tip {}" , tip_height) ;
103+ // TODO: notify sync-completion waiters (start()/sync_wallets()/tests) once
104+ // a notification primitive is plumbed through.
86105 } ,
87- //ChainOp::Reorg { .. } => {},
88106 }
89107 }
90108 }
@@ -239,6 +257,7 @@ impl CbfChainSource {
239257 ) ) ;
240258
241259 let event_handle = tokio:: spawn ( Self :: process_kyoto_events (
260+ Arc :: clone ( & restart_logger) ,
242261 current_event_rx,
243262 Arc :: clone ( & restart_registered_scripts) ,
244263 Arc :: clone ( & restart_cbf_runtime_status) ,
@@ -351,45 +370,98 @@ impl CbfChainSource {
351370 }
352371
353372 async fn process_kyoto_events (
354- mut event_rx : mpsc:: UnboundedReceiver < Event > ,
373+ logger : Arc < Logger > , mut event_rx : mpsc:: UnboundedReceiver < Event > ,
355374 registered_scripts : Arc < Mutex < HashSet < ScriptBuf > > > ,
356375 cbf_runtime_status : Arc < Mutex < CbfRuntimeStatus > > , ops_tx : mpsc:: UnboundedSender < ChainOp > ,
357376 ) {
358377 while let Some ( event) = event_rx. recv ( ) . await {
359378 match event {
360- // match download
361379 Event :: IndexedFilter ( indexed_filter) => {
380+ let requester = match & * cbf_runtime_status. lock ( ) . expect ( "lock" ) {
381+ CbfRuntimeStatus :: Started { requester } => requester. clone ( ) ,
382+ CbfRuntimeStatus :: Stopped => {
383+ //TODO should we panic here? what do we do if we have no requester?
384+ continue ;
385+ } ,
386+ } ;
387+ let block_hash = indexed_filter. block_hash ( ) ;
362388 let matched = indexed_filter
363389 . contains_any ( registered_scripts. lock ( ) . expect ( "lock" ) . iter ( ) ) ;
364- if matched {
365- let rtm = & * cbf_runtime_status. lock ( ) . expect ( "lock" ) ;
366- let requestor = match rtm {
367- CbfRuntimeStatus :: Started { requester } => requester. clone ( ) ,
368- CbfRuntimeStatus :: Stopped => {
369- //panic
370- // todo!();
390+
391+ let chop: ChainOp = if matched {
392+ let block_rx =
393+ requester. request_block ( block_hash) . expect ( "cannot request block" ) ;
394+ ChainOp :: ConnectFull { block_rx }
395+ } else {
396+ let height = indexed_filter. height ( ) ;
397+ //TODO we need to recheck that a particular height has not been
398+ //reorganized, and we retrieve indeed the same block header that we
399+ //received `IndexedFilter` event of. right now this would block
400+ //the further sync, as we cannot apply blocks in order.
401+ //Future solution would use something like `get_header_by_hash`.
402+ match requester. get_header ( height) . await {
403+ Ok ( Some ( indexed_header) ) => {
404+ if indexed_header. block_hash ( ) != block_hash {
405+ log_debug ! (
406+ logger,
407+ "Filter for {} reorged; skipping" ,
408+ block_hash
409+ ) ;
410+ continue ;
411+ }
412+ ChainOp :: ConnectFiltered {
413+ header : indexed_header. header ,
414+ height : indexed_header. height ,
415+ }
416+ } ,
417+ Ok ( None ) => {
418+ log_error ! ( logger, "No header at height {}" , height, ) ;
371419 continue ;
372420 } ,
373- } ;
374- let block_rx = requestor
375- . request_block ( indexed_filter. block_hash ( ) )
376- . expect ( "cannot request block" ) ;
377- let chop = ChainOp :: ConnectFull { block_rx } ;
378- //here we feed evets to the driver
379- ops_tx. send ( chop) ;
421+ Err ( e) => {
422+ log_error ! (
423+ logger,
424+ "Failed to fetch header at height {}: {:?}" ,
425+ height,
426+ e,
427+ ) ;
428+ continue ;
429+ } ,
430+ }
431+ } ;
432+ if let Err ( e) = ops_tx. send ( chop) {
433+ log_debug ! ( logger, "ops_rx gone: {}" , e) ;
380434 }
381435 } ,
382436 Event :: FiltersSynced ( sync_update) => {
383- todo ! ( ) ;
437+ //Because application of blocks is async, the fact that kyoto synced up to the
438+ //tip does NOT mean that we caught everything up, that's why we send a ChainOp,
439+ //only processing of which means we processed all blocks up to the tip.
440+ log_info ! ( logger, "Kyoto synced up to the tip {}" , sync_update. tip( ) . height) ;
441+ let _ = ops_tx. send ( ChainOp :: Synced { tip_height : sync_update. tip ( ) . height } ) ;
384442 } ,
385- Event :: ChainUpdate ( BlockHeaderChanges :: Connected ( connected_blocks) ) => {
386- todo ! ( ) ;
443+ Event :: ChainUpdate ( BlockHeaderChanges :: Connected ( indexed_header) ) => {
444+ log_debug ! (
445+ logger,
446+ "Kyoto connected header at height {}" ,
447+ indexed_header. height
448+ ) ;
387449 } ,
388- Event :: ChainUpdate ( BlockHeaderChanges :: Reorganized { reorganized, accepted } ) => {
389- todo ! ( ) ;
450+ Event :: ChainUpdate ( BlockHeaderChanges :: Reorganized {
451+ reorganized,
452+ accepted : _,
453+ } ) => {
454+ // Rewind to the fork point; kyoto will re-deliver the new chain's filters.
455+ if let Some ( lowest) = reorganized. first ( ) {
456+ let fork_point = BlockLocator :: new (
457+ lowest. prev_blockhash ( ) ,
458+ lowest. height . saturating_sub ( 1 ) ,
459+ ) ;
460+ let _ = ops_tx. send ( ChainOp :: Disconnect { fork_point } ) ;
461+ }
390462 } ,
391463 Event :: ChainUpdate ( BlockHeaderChanges :: ForkAdded ( fork) ) => {
392- todo ! ( ) ;
464+ log_debug ! ( logger , "Kyoto added fork header at height {}" , fork . height ) ;
393465 } ,
394466 }
395467 }
0 commit comments