Skip to content

Commit 9b85df8

Browse files
refactor(spv): share ChainState between client and header sync (#142)
- Eliminate duplicate ChainState initialization by sharing a single Arc<RwLock<ChainState>> across client and header sync. - HeaderSyncManagerWithReorg now holds shared state and no longer constructs its own ChainState. - SequentialSyncManager::new signature updated to accept the shared ChainState and plumb it through. - Client passes its ChainState to sync manager and removes the client-side header copy path. - Read/write updated to use RwLock guards; added lightweight cached checkpoint flags in header sync. This removes the duplicate "Initialized ChainState" logs and unifies state as a single source of truth.
1 parent 5375349 commit 9b85df8

File tree

3 files changed

+165
-209
lines changed

3 files changed

+165
-209
lines changed

dash-spv/src/client/mod.rs

Lines changed: 15 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,13 @@ impl<
190190
// Create sync manager
191191
let received_filter_heights = stats.read().await.received_filter_heights.clone();
192192
tracing::info!("Creating sequential sync manager");
193-
let sync_manager =
194-
SequentialSyncManager::new(&config, received_filter_heights, wallet.clone())
195-
.map_err(SpvError::Sync)?;
193+
let sync_manager = SequentialSyncManager::new(
194+
&config,
195+
received_filter_heights,
196+
wallet.clone(),
197+
state.clone(),
198+
)
199+
.map_err(SpvError::Sync)?;
196200

197201
// Create validation manager
198202
let validation = ValidationManager::new(config.validation_mode);
@@ -343,20 +347,6 @@ impl<
343347
match loaded_count {
344348
Ok(loaded_count) => {
345349
tracing::info!("✅ Sync manager loaded {} headers from storage", loaded_count);
346-
347-
// IMPORTANT: Also load headers into the client's ChainState for normal sync
348-
// This is needed because the status display reads from the client's ChainState
349-
let state = self.state.read().await;
350-
let is_normal_sync = !state.synced_from_checkpoint;
351-
drop(state); // Release the lock before loading headers
352-
353-
if is_normal_sync && loaded_count > 0 {
354-
tracing::info!("Loading headers into client ChainState for normal sync...");
355-
if let Err(e) = self.load_headers_into_client_state(tip_height).await {
356-
tracing::error!("Failed to load headers into client ChainState: {}", e);
357-
// This is not critical for normal sync, continue anyway
358-
}
359-
}
360350
}
361351
Err(e) => {
362352
tracing::error!("Failed to load headers into sync manager: {}", e);
@@ -1940,79 +1930,6 @@ impl<
19401930
Ok(true)
19411931
}
19421932

1943-
/// Load headers from storage into the client's ChainState.
1944-
/// This is used during normal sync to ensure the status display shows correct header count.
1945-
async fn load_headers_into_client_state(&mut self, tip_height: u32) -> Result<()> {
1946-
if tip_height == 0 {
1947-
return Ok(());
1948-
}
1949-
1950-
tracing::debug!("Loading {} headers from storage into client ChainState", tip_height);
1951-
let start_time = Instant::now();
1952-
1953-
// Load headers in batches to avoid memory spikes
1954-
const BATCH_SIZE: u32 = 10_000;
1955-
let mut loaded_count = 0u32;
1956-
1957-
// Start from height 1 (genesis is already in ChainState)
1958-
let mut current_height = 1u32;
1959-
1960-
while current_height <= tip_height {
1961-
let end_height = (current_height + BATCH_SIZE - 1).min(tip_height);
1962-
1963-
// Load batch of headers from storage
1964-
let headers = {
1965-
let storage = self.storage.lock().await;
1966-
storage
1967-
.load_headers(current_height..end_height + 1)
1968-
.await
1969-
.map_err(SpvError::Storage)?
1970-
};
1971-
1972-
if headers.is_empty() {
1973-
tracing::warn!(
1974-
"No headers found for range {}..{} - storage may be incomplete",
1975-
current_height,
1976-
end_height + 1
1977-
);
1978-
break;
1979-
}
1980-
1981-
// Add headers to client's chain state
1982-
{
1983-
let mut state = self.state.write().await;
1984-
for header in headers {
1985-
state.add_header(header);
1986-
loaded_count += 1;
1987-
}
1988-
}
1989-
1990-
// Progress logging for large header counts
1991-
if loaded_count % 50_000 == 0 || loaded_count == tip_height {
1992-
let elapsed = start_time.elapsed();
1993-
let headers_per_sec = loaded_count as f64 / elapsed.as_secs_f64();
1994-
tracing::debug!(
1995-
"Loaded {}/{} headers into client ChainState ({:.0} headers/sec)",
1996-
loaded_count,
1997-
tip_height,
1998-
headers_per_sec
1999-
);
2000-
}
2001-
2002-
current_height = end_height + 1;
2003-
}
2004-
2005-
let elapsed = start_time.elapsed();
2006-
tracing::info!(
2007-
"✅ Loaded {} headers into client ChainState in {:.2}s ({:.0} headers/sec)",
2008-
loaded_count,
2009-
elapsed.as_secs_f64(),
2010-
loaded_count as f64 / elapsed.as_secs_f64()
2011-
);
2012-
2013-
Ok(())
2014-
}
2015-
20161933
/// Rollback chain state to a specific height.
20171934
async fn rollback_to_height(&mut self, target_height: u32) -> Result<()> {
20181935
tracing::info!("Rolling back chain state to height {}", target_height);
@@ -2255,9 +2172,9 @@ impl<
22552172
self.config.network,
22562173
);
22572174

2258-
// Clone the chain state for storage and sync manager
2175+
// Clone the chain state for storage
22592176
let chain_state_for_storage = (*chain_state).clone();
2260-
let checkpoint_chain_state = (*chain_state).clone();
2177+
let headers_len = chain_state_for_storage.headers.len() as u32;
22612178
drop(chain_state);
22622179

22632180
// Update storage with chain state including sync_base_height
@@ -2278,8 +2195,12 @@ impl<
22782195
checkpoint.height
22792196
);
22802197

2281-
// Update the sync manager's chain state with the checkpoint-initialized state
2282-
self.sync_manager.set_chain_state(checkpoint_chain_state);
2198+
// Update the sync manager's cached flags from the checkpoint-initialized state
2199+
self.sync_manager.update_chain_state_cache(
2200+
true,
2201+
checkpoint.height,
2202+
headers_len,
2203+
);
22832204
tracing::info!(
22842205
"Updated sync manager with checkpoint-initialized chain state"
22852206
);

0 commit comments

Comments
 (0)