@@ -52,6 +52,17 @@ pub struct MasternodeSyncManager<S: StorageManager, N: NetworkManager> {
5252
5353 // Checkpoint base height (0 when syncing from genesis)
5454 sync_base_height : u32 ,
55+
56+ // Track start-list backfills required before QRInfo can be processed
57+ pending_start_list_backfills : HashMap < BlockHash , u32 > ,
58+
59+ // Deferred QRInfo that will be retried once missing start lists are backfilled
60+ pending_qrinfo_retry : Option < QRInfo > ,
61+ }
62+
63+ enum QrInfoProcessResult {
64+ Completed ,
65+ WaitingForStartList ( BlockHash ) ,
5566}
5667
5768impl < S : StorageManager + Send + Sync + ' static , N : NetworkManager + Send + Sync + ' static >
@@ -122,6 +133,8 @@ impl<S: StorageManager + Send + Sync + 'static, N: NetworkManager + Send + Sync
122133 mnlistdiff_wait_start : None ,
123134 mnlistdiff_retry_count : 0 ,
124135 sync_base_height : 0 ,
136+ pending_start_list_backfills : HashMap :: new ( ) ,
137+ pending_qrinfo_retry : None ,
125138 _phantom_s : std:: marker:: PhantomData ,
126139 _phantom_n : std:: marker:: PhantomData ,
127140 }
@@ -132,6 +145,103 @@ impl<S: StorageManager + Send + Sync + 'static, N: NetworkManager + Send + Sync
132145 self . sync_base_height = height;
133146 }
134147
148+ async fn schedule_start_list_backfill (
149+ & mut self ,
150+ missing_block_hash : BlockHash ,
151+ storage : & mut S ,
152+ network : & mut dyn NetworkManager ,
153+ ) -> Result < ( ) , String > {
154+ if self . pending_start_list_backfills . contains_key ( & missing_block_hash) {
155+ tracing:: info!(
156+ "Already waiting for start masternode list at block {}" ,
157+ missing_block_hash
158+ ) ;
159+ return Ok ( ( ) ) ;
160+ }
161+
162+ let engine =
163+ self . engine . as_mut ( ) . ok_or_else ( || "Masternode engine not initialized" . to_string ( ) ) ?;
164+ let ( latest_height, latest_block_hash) = engine
165+ . latest_masternode_list ( )
166+ . map ( |list| ( list. known_height , list. block_hash ) )
167+ . ok_or_else ( || "Masternode engine has no known masternode list" . to_string ( ) ) ?;
168+
169+ let target_height = storage
170+ . get_header_height_by_hash ( & missing_block_hash)
171+ . await
172+ . map_err ( |e| {
173+ format ! (
174+ "Failed to look up height for missing start list block {}: {}" ,
175+ missing_block_hash, e
176+ )
177+ } ) ?
178+ . ok_or_else ( || {
179+ format ! (
180+ "Height not found in storage for missing start list block {}" ,
181+ missing_block_hash
182+ )
183+ } ) ?;
184+
185+ if latest_height >= target_height {
186+ tracing:: debug!(
187+ "Engine already has masternode list at or beyond height {}" ,
188+ target_height
189+ ) ;
190+ return Ok ( ( ) ) ;
191+ }
192+
193+ engine. feed_block_height ( target_height, missing_block_hash) ;
194+
195+ let request = dashcore:: network:: message_sml:: GetMnListDiff {
196+ base_block_hash : latest_block_hash,
197+ block_hash : missing_block_hash,
198+ } ;
199+
200+ network
201+ . send_message ( NetworkMessage :: GetMnListD ( request) )
202+ . await
203+ . map_err ( |e| format ! ( "Failed to send MnListDiff backfill request: {}" , e) ) ?;
204+
205+ tracing:: info!(
206+ "Requested MnListDiff backfill from height {} to {} ({} -> {})" ,
207+ latest_height,
208+ target_height,
209+ latest_block_hash,
210+ missing_block_hash
211+ ) ;
212+
213+ self . pending_start_list_backfills . insert ( missing_block_hash, target_height) ;
214+
215+ Ok ( ( ) )
216+ }
217+
218+ async fn apply_start_list_backfill (
219+ & mut self ,
220+ diff : MnListDiff ,
221+ target_height : u32 ,
222+ ) -> Result < ( ) , String > {
223+ let engine =
224+ self . engine . as_mut ( ) . ok_or_else ( || "Masternode engine not initialized" . to_string ( ) ) ?;
225+ let block_hash = diff. block_hash ;
226+ engine. feed_block_height ( target_height, block_hash) ;
227+ engine
228+ . apply_diff ( diff, Some ( target_height) , false , None )
229+ . map_err ( |e| format ! ( "Failed to apply MnListDiff backfill: {}" , e) ) ?;
230+ tracing:: info!(
231+ "Applied MnListDiff backfill up to height {} (block {})" ,
232+ target_height,
233+ block_hash
234+ ) ;
235+ Ok ( ( ) )
236+ }
237+
238+ async fn retry_pending_qrinfo ( & mut self , storage : & mut S , network : & mut dyn NetworkManager ) {
239+ if let Some ( pending) = self . pending_qrinfo_retry . take ( ) {
240+ tracing:: info!( "Retrying deferred QRInfo after start list backfill" ) ;
241+ self . handle_qrinfo_message ( pending, storage, network) . await ;
242+ }
243+ }
244+
135245 /// Request QRInfo - simplified non-blocking implementation
136246 pub async fn request_qrinfo (
137247 & mut self ,
@@ -483,10 +593,27 @@ impl<S: StorageManager + Send + Sync + 'static, N: NetworkManager + Send + Sync
483593 & mut self ,
484594 diff : MnListDiff ,
485595 storage : & mut S ,
486- _network : & mut dyn NetworkManager ,
596+ network : & mut dyn NetworkManager ,
487597 ) -> SyncResult < bool > {
488598 self . insert_mn_list_diff ( & diff, storage) . await ;
489599
600+ let mut backfill_applied = false ;
601+ if let Some ( target_height) = self . pending_start_list_backfills . remove ( & diff. block_hash ) {
602+ match self . apply_start_list_backfill ( diff. clone ( ) , target_height) . await {
603+ Ok ( ( ) ) => {
604+ backfill_applied = true ;
605+ }
606+ Err ( e) => {
607+ tracing:: error!(
608+ "❌ Failed to apply start list backfill for block {}: {}" ,
609+ diff. block_hash,
610+ e
611+ ) ;
612+ self . error = Some ( e) ;
613+ }
614+ }
615+ }
616+
490617 // Decrement pending request counter if we were expecting this response
491618 if self . pending_mnlistdiff_requests > 0 {
492619 self . pending_mnlistdiff_requests -= 1 ;
@@ -534,6 +661,10 @@ impl<S: StorageManager + Send + Sync + 'static, N: NetworkManager + Send + Sync
534661 }
535662 }
536663
664+ if backfill_applied {
665+ self . retry_pending_qrinfo ( storage, network) . await ;
666+ }
667+
537668 Ok ( false ) // Not used for sync completion in simple approach
538669 }
539670
@@ -689,11 +820,21 @@ impl<S: StorageManager + Send + Sync + 'static, N: NetworkManager + Send + Sync
689820
690821 // Feed QRInfo to engine and get additional MnListDiffs needed for quorum validation
691822 // This is the critical step that dash-evo-tool performs after initial QRInfo processing
692- if let Err ( e) = self . feed_qrinfo_and_get_additional_diffs ( & qr_info, storage, network) . await
693- {
694- tracing:: error!( "❌ Failed to process QRInfo follow-up diffs: {}" , e) ;
695- self . error = Some ( e) ;
696- return ;
823+ match self . feed_qrinfo_and_get_additional_diffs ( & qr_info, storage, network) . await {
824+ Ok ( QrInfoProcessResult :: Completed ) => { }
825+ Ok ( QrInfoProcessResult :: WaitingForStartList ( block_hash) ) => {
826+ tracing:: info!(
827+ "⏳ Waiting for missing start masternode list at block {} before processing QRInfo" ,
828+ block_hash
829+ ) ;
830+ self . pending_qrinfo_retry = Some ( qr_info) ;
831+ return ;
832+ }
833+ Err ( e) => {
834+ tracing:: error!( "❌ Failed to process QRInfo follow-up diffs: {}" , e) ;
835+ self . error = Some ( e) ;
836+ return ;
837+ }
697838 }
698839
699840 // Cache the QRInfo using the requested block hash as key
@@ -758,7 +899,7 @@ impl<S: StorageManager + Send + Sync + 'static, N: NetworkManager + Send + Sync
758899 qr_info : & QRInfo ,
759900 storage : & mut S ,
760901 network : & mut dyn NetworkManager ,
761- ) -> Result < ( ) , String > {
902+ ) -> Result < QrInfoProcessResult , String > {
762903 tracing:: info!(
763904 "🔗 Feeding QRInfo to engine and getting additional diffs for quorum validation"
764905 ) ;
@@ -785,6 +926,12 @@ impl<S: StorageManager + Send + Sync + 'static, N: NetworkManager + Send + Sync
785926 Ok ( ( ) ) => {
786927 tracing:: info!( "✅ Successfully fed QRInfo to masternode list engine" ) ;
787928 }
929+ Err ( dashcore:: sml:: quorum_validation_error:: QuorumValidationError :: SMLError (
930+ dashcore:: sml:: error:: SmlError :: MissingStartMasternodeList ( block_hash) ,
931+ ) ) => {
932+ self . schedule_start_list_backfill ( block_hash, storage, network) . await ?;
933+ return Ok ( QrInfoProcessResult :: WaitingForStartList ( block_hash) ) ;
934+ }
788935 Err ( e) => {
789936 let error_msg = format ! ( "Failed to feed QRInfo to engine: {}" , e) ;
790937 tracing:: error!( "❌ {}" , error_msg) ;
@@ -829,7 +976,7 @@ impl<S: StorageManager + Send + Sync + 'static, N: NetworkManager + Send + Sync
829976 }
830977 }
831978
832- Ok ( ( ) )
979+ Ok ( QrInfoProcessResult :: Completed )
833980 }
834981
835982 /// Fetch additional MnListDiffs for quorum validation (dash-evo-tool pattern)
0 commit comments