Skip to content

Commit bb04076

Browse files
committed
fix mnlist sync
1 parent 7300fe2 commit bb04076

File tree

1 file changed

+155
-8
lines changed

1 file changed

+155
-8
lines changed

dash-spv/src/sync/masternodes.rs

Lines changed: 155 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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

5768
impl<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

Comments
 (0)