33
44use crate :: args:: RemoteBlockSourceArgs ;
55use alloy_primitives:: Signature ;
6- use alloy_provider:: { Provider , ProviderBuilder } ;
6+ use alloy_provider:: { Provider , ProviderBuilder , RootProvider } ;
77use alloy_rpc_client:: RpcClient ;
88use alloy_transport:: layers:: RetryBackoffLayer ;
99use futures:: StreamExt ;
1010use reth_network_api:: { FullNetwork , PeerId } ;
11+ use reth_provider:: BlockReader ;
1112use reth_scroll_node:: ScrollNetworkPrimitives ;
1213use reth_tasks:: shutdown:: Shutdown ;
1314use reth_tokio_util:: EventStream ;
2627 /// Configuration for the remote block source.
2728 config : RemoteBlockSourceArgs ,
2829 /// Handle to the chain orchestrator for sending commands.
29- handle : ChainOrchestratorHandle < N > ,
30+ orchestrator_handle : ChainOrchestratorHandle < N > ,
31+ /// An event stream for listening to chain orchestrator events, used to wait for block build
32+ /// completion.
33+ events : EventStream < ChainOrchestratorEvent > ,
34+ /// A provider for the remote node, used to fetch blocks and block information.
35+ remote : RootProvider < Scroll > ,
3036 /// Tracks the last block number we imported from remote.
3137 /// This is different from local head because we build blocks on top of imports.
3238 last_imported_block : u64 ,
@@ -40,40 +46,72 @@ where
4046 pub async fn new (
4147 config : RemoteBlockSourceArgs ,
4248 handle : ChainOrchestratorHandle < N > ,
49+ provider : impl BlockReader ,
4350 ) -> eyre:: Result < Self > {
44- let last_imported_block = handle. status ( ) . await ?. l2 . fcs . head_block_info ( ) . number ;
45- Ok ( Self { config, handle, last_imported_block } )
46- }
47-
48- /// Runs the remote block source until shutdown.
49- pub async fn run_until_shutdown ( mut self , mut shutdown : Shutdown ) -> eyre:: Result < ( ) > {
50- let Some ( url) = self . config . url . clone ( ) else {
51+ // Build remote provider with retry layer.
52+ let Some ( url) = config. url . clone ( ) else {
5153 tracing:: error!( target: "scroll::remote_source" , "URL required when remote-source is enabled" ) ;
5254 return Err ( eyre:: eyre!( "URL required when remote-source is enabled" ) ) ;
5355 } ;
54-
55- // Build remote provider with retry layer
5656 let retry_layer = RetryBackoffLayer :: new ( 10 , 100 , 330 ) ;
5757 let client = RpcClient :: builder ( ) . layer ( retry_layer) . http ( url) ;
5858 let remote = ProviderBuilder :: < _ , _ , Scroll > :: default ( ) . connect_client ( client) ;
5959
6060 // Get event listener for waiting on block completion
61- let mut event_stream = match self . handle . get_event_listener ( ) . await {
61+ let events = match handle. get_event_listener ( ) . await {
6262 Ok ( stream) => stream,
6363 Err ( e) => {
6464 tracing:: error!( target: "scroll::remote_source" , ?e, "Failed to get event listener" ) ;
6565 return Err ( eyre:: eyre!( e) ) ;
6666 }
6767 } ;
6868
69+ // Determine the last imported block by finding the highest common block
70+ // between the local chain and the remote node.
71+ let local_head = provider. best_block_number ( ) ?;
72+ let remote_head = remote. get_block_number ( ) . await ?;
73+
74+ let last_imported_block;
75+ let mut search = local_head. min ( remote_head) ;
76+ loop {
77+ if search == 0 {
78+ // Genesis is always a common block (same chain spec assumed).
79+ last_imported_block = 0 ;
80+ break ;
81+ }
82+ let local_hash = provider. block_hash ( search) ?;
83+ let remote_block = remote. get_block_by_number ( search. into ( ) ) . await ?;
84+ match ( local_hash, remote_block) {
85+ ( Some ( lh) , Some ( rb) ) if lh == rb. header . hash => {
86+ last_imported_block = search;
87+ break ;
88+ }
89+ _ => {
90+ search = search. saturating_sub ( 1 ) ;
91+ }
92+ }
93+ }
94+ tracing:: info!(
95+ target: "scroll::remote_source" ,
96+ last_imported_block,
97+ local_head,
98+ remote_head,
99+ "Determined highest common block with remote"
100+ ) ;
101+
102+ Ok ( Self { config, orchestrator_handle : handle, events, remote, last_imported_block } )
103+ }
104+
105+ /// Runs the remote block source until shutdown.
106+ pub async fn run_until_shutdown ( mut self , mut shutdown : Shutdown ) -> eyre:: Result < ( ) > {
69107 let mut poll_interval = interval ( Duration :: from_millis ( self . config . poll_interval_ms ) ) ;
70108
71109 loop {
72110 tokio:: select! {
73111 biased;
74112 _guard = & mut shutdown => break ,
75113 _ = poll_interval. tick( ) => {
76- if let Err ( e) = self . follow_and_build( & remote , & mut event_stream ) . await {
114+ if let Err ( e) = self . follow_and_build( ) . await {
77115 tracing:: error!( target: "scroll::remote_source" , ?e, "Sync error" ) ;
78116 }
79117 }
@@ -84,14 +122,11 @@ where
84122 }
85123
86124 /// Follows the remote node and builds blocks on top of imported blocks.
87- async fn follow_and_build < P : Provider < Scroll > > (
88- & mut self ,
89- remote : & P ,
90- event_stream : & mut EventStream < ChainOrchestratorEvent > ,
91- ) -> eyre:: Result < ( ) > {
125+ async fn follow_and_build ( & mut self ) -> eyre:: Result < ( ) > {
92126 loop {
93127 // Get remote head
94- let remote_block = remote
128+ let remote_block = self
129+ . remote
95130 . get_block_by_number ( alloy_eips:: BlockNumberOrTag :: Latest )
96131 . full ( )
97132 . await ?
@@ -117,7 +152,8 @@ where
117152
118153 // Fetch and import the next block from remote
119154 let next_block_num = self . last_imported_block + 1 ;
120- let block = remote
155+ let block = self
156+ . remote
121157 . get_block_by_number ( next_block_num. into ( ) )
122158 . full ( )
123159 . await ?
@@ -134,7 +170,7 @@ where
134170
135171 // Import the block (this will cause a reorg if we had a locally built block at this
136172 // height)
137- let chain_import = match self . handle . import_block ( block_with_peer) . await {
173+ let chain_import = match self . orchestrator_handle . import_block ( block_with_peer) . await {
138174 Ok ( Ok ( chain_import) ) => {
139175 self . last_imported_block = next_block_num;
140176 chain_import
@@ -155,12 +191,12 @@ where
155191 }
156192
157193 // Trigger block building on top of the imported block
158- self . handle . build_block ( ) ;
194+ self . orchestrator_handle . build_block ( ) ;
159195
160196 // Wait for BlockSequenced event
161197 tracing:: debug!( target: "scroll::remote_source" , "Waiting for block to be built..." ) ;
162198 loop {
163- match event_stream . next ( ) . await {
199+ match self . events . next ( ) . await {
164200 Some ( ChainOrchestratorEvent :: BlockSequenced ( block) ) => {
165201 tracing:: info!( target: "scroll::remote_source" ,
166202 block_number = block. header. number,
0 commit comments