Skip to content

Commit 072ada5

Browse files
committed
Stop loading headers twice into ChainState::headers
We currently load the headers twice into the header vector in `HeaderSyncManager::load_headers_from_storage`: - First1. within `storage.load_chain_state` - And then within `load_headers_from_storage` itself further down ``` 2025-12-04T16:05:36.717779Z INFO 🚀 Starting sync from checkpoint at height 1900000 instead of genesis (requested start height: 2200000) 2025-12-04T16:05:36.727966Z INFO ✅ Loaded 2 headers into HeaderSyncManager in 0.00s (1129 headers/sec) .... 2025-12-05T04:31:15.980982Z ERROR Sequential sync manager error handling message: Validation error: Block at height 2300000 does not match checkpoint ``` ``` 2025-12-04T16:09:37.959349Z INFO 🚀 Starting sync from checkpoint at height 1900000 instead of genesis (requested start height: 2200000) 2025-12-04T16:09:37.966703Z INFO ✅ Loaded 1 headers into HeaderSyncManager in 0.00s (862 headers/sec) ´´´
1 parent 8e94e50 commit 072ada5

File tree

1 file changed

+9
-88
lines changed

1 file changed

+9
-88
lines changed

dash-spv/src/sync/headers/manager.rs

Lines changed: 9 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -102,116 +102,37 @@ impl<S: StorageManager + Send + Sync + 'static, N: NetworkManager + Send + Sync
102102

103103
/// Load headers from storage into the chain state
104104
pub async fn load_headers_from_storage(&mut self, storage: &S) -> SyncResult<u32> {
105+
let start_time = std::time::Instant::now();
106+
let mut loaded_count = 0;
107+
let mut tip_height = 0;
105108
// First, try to load the persisted chain state which may contain sync_base_height
106109
if let Ok(Some(stored_chain_state)) = storage.load_chain_state().await {
107110
tracing::info!(
108111
"Loaded chain state from storage with sync_base_height: {}",
109112
stored_chain_state.sync_base_height,
110113
);
111-
// Update our chain state with the loaded one to preserve sync_base_height
114+
// Update our chain state with the loaded one
112115
{
116+
loaded_count = stored_chain_state.headers.len();
117+
tip_height = stored_chain_state.tip_height();
113118
self.cached_sync_base_height = stored_chain_state.sync_base_height;
114119
let mut cs = self.chain_state.write().await;
115120
*cs = stored_chain_state;
116121
}
117122
}
118123

119-
// Get the current tip height from storage
120-
let tip_height = storage
121-
.get_tip_height()
122-
.await
123-
.map_err(|e| SyncError::Storage(format!("Failed to get tip height: {}", e)))?;
124-
125-
let Some(tip_height) = tip_height else {
126-
tracing::debug!("No headers found in storage");
127-
// If we're syncing from a checkpoint, this is expected
128-
if self.is_synced_from_checkpoint() {
129-
tracing::info!("No headers in storage for checkpoint sync - this is expected");
130-
return Ok(0);
131-
}
132-
return Ok(0);
133-
};
134-
135-
if tip_height == 0 && !self.is_synced_from_checkpoint() {
136-
tracing::debug!("Only genesis block in storage");
137-
return Ok(0);
138-
}
139-
140-
tracing::info!("Loading {} headers from storage into HeaderSyncManager", tip_height);
141-
let start_time = std::time::Instant::now();
142-
143-
// Load headers in batches
144-
const BATCH_SIZE: u32 = 10_000;
145-
let mut loaded_count = 0u32;
146-
147-
// For checkpoint syncs we start at the checkpoint base; otherwise we skip genesis (already present).
148-
let mut current_height = self.get_sync_base_height().max(1);
149-
150-
while current_height <= tip_height {
151-
let end_height = (current_height + BATCH_SIZE - 1).min(tip_height);
152-
153-
// Load batch from storage
154-
let headers_result = storage.load_headers(current_height..end_height + 1).await;
155-
156-
match headers_result {
157-
Ok(headers) if !headers.is_empty() => {
158-
// Add headers to chain state
159-
{
160-
let mut cs = self.chain_state.write().await;
161-
for header in headers {
162-
cs.add_header(header);
163-
loaded_count += 1;
164-
}
165-
}
166-
}
167-
Ok(_) => {
168-
// Empty headers - this can happen for checkpoint sync with minimal headers
169-
tracing::debug!(
170-
"No headers found for range {}..{} - continuing",
171-
current_height,
172-
end_height + 1
173-
);
174-
// Break out of the loop since we've reached the end of available headers
175-
break;
176-
}
177-
Err(e) => {
178-
// For checkpoint sync with only 1 header stored, this is expected
179-
if self.is_synced_from_checkpoint() && loaded_count == 0 && tip_height == 0 {
180-
tracing::info!(
181-
"No additional headers to load for checkpoint sync - this is expected"
182-
);
183-
return Ok(0);
184-
}
185-
return Err(SyncError::Storage(format!("Failed to load headers: {}", e)));
186-
}
187-
}
188-
189-
// Progress logging
190-
if loaded_count.is_multiple_of(50_000) || loaded_count == tip_height {
191-
let elapsed = start_time.elapsed();
192-
let headers_per_sec = loaded_count as f64 / elapsed.as_secs_f64();
193-
tracing::info!(
194-
"Loaded {}/{} headers ({:.0} headers/sec)",
195-
loaded_count,
196-
tip_height,
197-
headers_per_sec
198-
);
199-
}
200-
201-
current_height = end_height + 1;
202-
}
203-
204124
self.total_headers_synced = tip_height;
205125

206126
let elapsed = start_time.elapsed();
207127
tracing::info!(
208-
"✅ Loaded {} headers into HeaderSyncManager in {:.2}s ({:.0} headers/sec)",
128+
"✅ Loaded {} headers for tip height {} into HeaderSyncManager in {:.2}s ({:.0} headers/sec)",
209129
loaded_count,
130+
tip_height,
210131
elapsed.as_secs_f64(),
211132
loaded_count as f64 / elapsed.as_secs_f64()
212133
);
213134

214-
Ok(loaded_count)
135+
Ok(loaded_count as u32)
215136
}
216137

217138
/// Handle a Headers message

0 commit comments

Comments
 (0)