Skip to content

Commit b181584

Browse files
authored
refactor: remove headers from ChainState (#292)
* removed chain storage an related code * removed headers from ChainState * tip_height method removed * removed get_tip_hesh * replaced header_at_height * removed unused methods * init_from_checkpoint sync * tip_header removed * removed two methos that where invovled in the same process * fixed ffi * test updated to the changes * chore: removed fork detector and fork structs (#290) * get_header now checks out of bound to return None instead of panics * start height was not being updated properly * fixed other test by correctly storing the headers in the storage * removed genesis block creation in ChainState creation * fixed clippy warnings * dropped unuseed code * code review suggestions
1 parent cd12793 commit b181584

31 files changed

+263
-529
lines changed

dash-spv-ffi/src/types.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ impl From<DetailedSyncProgress> for FFIDetailedSyncProgress {
181181

182182
#[repr(C)]
183183
pub struct FFIChainState {
184-
pub header_height: u32,
185184
pub masternode_height: u32,
186185
pub last_chainlock_height: u32,
187186
pub last_chainlock_hash: FFIString,
@@ -191,7 +190,6 @@ pub struct FFIChainState {
191190
impl From<ChainState> for FFIChainState {
192191
fn from(state: ChainState) -> Self {
193192
FFIChainState {
194-
header_height: state.headers.len() as u32,
195193
masternode_height: state.last_masternode_diff_height.unwrap_or(0),
196194
last_chainlock_height: state.last_chainlock_height.unwrap_or(0),
197195
last_chainlock_hash: FFIString::new(

dash-spv-ffi/tests/unit/test_type_conversions.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ mod tests {
163163
#[test]
164164
fn test_chain_state_none_values() {
165165
let state = dash_spv::ChainState {
166-
headers: vec![],
167166
last_chainlock_height: None,
168167
last_chainlock_hash: None,
169168
current_filter_tip: None,
@@ -173,7 +172,7 @@ mod tests {
173172
};
174173

175174
let ffi_state = FFIChainState::from(state);
176-
assert_eq!(ffi_state.header_height, 0);
175+
177176
assert_eq!(ffi_state.masternode_height, 0);
178177
assert_eq!(ffi_state.last_chainlock_height, 0);
179178
assert_eq!(ffi_state.current_filter_tip, 0);

dash-spv/src/chain/chainlock_manager.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,11 @@ impl ChainLockManager {
175175
}
176176

177177
// Verify the block exists in our chain
178-
if let Some(header) = chain_state.header_at_height(chain_lock.block_height) {
178+
if let Some(header) = storage
179+
.get_header(chain_lock.block_height)
180+
.await
181+
.map_err(ValidationError::StorageError)?
182+
{
179183
let header_hash = header.block_hash();
180184
if header_hash != chain_lock.block_hash {
181185
return Err(ValidationError::InvalidChainLock(format!(

dash-spv/src/client/core.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,14 +184,17 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
184184

185185
/// Returns the current chain tip hash if available.
186186
pub async fn tip_hash(&self) -> Option<dashcore::BlockHash> {
187-
let state = self.state.read().await;
188-
state.tip_hash()
187+
let storage = self.storage.lock().await;
188+
189+
let tip_height = storage.get_tip_height().await?;
190+
let header = storage.get_header(tip_height).await.ok()??;
191+
192+
Some(header.block_hash())
189193
}
190194

191195
/// Returns the current chain tip height (absolute), accounting for checkpoint base.
192196
pub async fn tip_height(&self) -> u32 {
193-
let state = self.state.read().await;
194-
state.tip_height()
197+
self.storage.lock().await.get_tip_height().await.unwrap_or(0)
195198
}
196199

197200
/// Get current chain state (read-only).

dash-spv/src/client/lifecycle.rs

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -163,30 +163,12 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
163163
// This ensures the ChainState has headers loaded for both checkpoint and normal sync
164164
let tip_height = {
165165
let storage = self.storage.lock().await;
166-
storage.get_tip_height().await.map_err(SpvError::Storage)?.unwrap_or(0)
166+
storage.get_tip_height().await.unwrap_or(0)
167167
};
168168
if tip_height > 0 {
169169
tracing::info!("Found {} headers in storage, loading into sync manager...", tip_height);
170-
let loaded_count = {
171-
let storage = self.storage.lock().await;
172-
self.sync_manager.load_headers_from_storage(&storage).await
173-
};
174-
175-
match loaded_count {
176-
Ok(loaded_count) => {
177-
tracing::info!("✅ Sync manager loaded {} headers from storage", loaded_count);
178-
}
179-
Err(e) => {
180-
tracing::error!("Failed to load headers into sync manager: {}", e);
181-
// For checkpoint sync, this is critical
182-
let state = self.state.read().await;
183-
if state.synced_from_checkpoint() {
184-
return Err(SpvError::Sync(e));
185-
}
186-
// For normal sync, we can continue as headers will be re-synced
187-
tracing::warn!("Continuing without pre-loaded headers for normal sync");
188-
}
189-
}
170+
let storage = self.storage.lock().await;
171+
self.sync_manager.load_headers_from_storage(&storage).await
190172
}
191173

192174
// Connect to network
@@ -203,8 +185,7 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
203185
// Get initial header count from storage
204186
let (header_height, filter_height) = {
205187
let storage = self.storage.lock().await;
206-
let h_height =
207-
storage.get_tip_height().await.map_err(SpvError::Storage)?.unwrap_or(0);
188+
let h_height = storage.get_tip_height().await.unwrap_or(0);
208189
let f_height =
209190
storage.get_filter_tip_height().await.map_err(SpvError::Storage)?.unwrap_or(0);
210191
(h_height, f_height)
@@ -265,7 +246,7 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
265246
// Check if we already have any headers in storage
266247
let current_tip = {
267248
let storage = self.storage.lock().await;
268-
storage.get_tip_height().await.map_err(SpvError::Storage)?
249+
storage.get_tip_height().await
269250
};
270251

271252
if current_tip.is_some() {
@@ -338,12 +319,12 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
338319

339320
// Clone the chain state for storage
340321
let chain_state_for_storage = (*chain_state).clone();
341-
let headers_len = chain_state_for_storage.headers.len() as u32;
342322
drop(chain_state);
343323

344324
// Update storage with chain state including sync_base_height
345325
{
346326
let mut storage = self.storage.lock().await;
327+
storage.store_headers(&[checkpoint_header]).await?;
347328
storage
348329
.store_chain_state(&chain_state_for_storage)
349330
.await
@@ -360,7 +341,7 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
360341
);
361342

362343
// Update the sync manager's cached flags from the checkpoint-initialized state
363-
self.sync_manager.update_chain_state_cache(checkpoint.height, headers_len);
344+
self.sync_manager.update_chain_state_cache(checkpoint.height);
364345
tracing::info!(
365346
"Updated sync manager with checkpoint-initialized chain state"
366347
);
@@ -408,7 +389,7 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
408389
// Verify it was stored correctly
409390
let stored_height = {
410391
let storage = self.storage.lock().await;
411-
storage.get_tip_height().await.map_err(SpvError::Storage)?
392+
storage.get_tip_height().await
412393
};
413394
tracing::info!(
414395
"✅ Genesis block initialized at height 0, storage reports tip height: {:?}",

dash-spv/src/client/progress.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
3333
// Get current heights from storage
3434
{
3535
let storage = self.storage.lock().await;
36-
if let Ok(Some(header_height)) = storage.get_tip_height().await {
36+
if let Some(header_height) = storage.get_tip_height().await {
3737
stats.header_height = header_height;
3838
}
3939

dash-spv/src/client/status_display.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl<'a, S: StorageManager, W: WalletInterface> StatusDisplay<'a, S, W> {
7474
// For genesis sync: sync_base_height = 0, so height = 0 + storage_count
7575
// For checkpoint sync: height = checkpoint_height + storage_count
7676
let storage = self.storage.lock().await;
77-
if let Ok(Some(storage_tip)) = storage.get_tip_height().await {
77+
if let Some(storage_tip) = storage.get_tip_height().await {
7878
let blockchain_height = storage_tip;
7979
if with_logging {
8080
tracing::debug!(

dash-spv/src/client/sync_coordinator.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
3737
let result = SyncProgress {
3838
header_height: {
3939
let storage = self.storage.lock().await;
40-
storage.get_tip_height().await.map_err(SpvError::Storage)?.unwrap_or(0)
40+
storage.get_tip_height().await.unwrap_or(0)
4141
},
4242
filter_header_height: {
4343
let storage = self.storage.lock().await;
@@ -236,7 +236,7 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
236236
// Storage tip now represents the absolute blockchain height.
237237
let current_tip_height = {
238238
let storage = self.storage.lock().await;
239-
storage.get_tip_height().await.ok().flatten().unwrap_or(0)
239+
storage.get_tip_height().await.unwrap_or(0)
240240
};
241241
let current_height = current_tip_height;
242242
let peer_best = self
@@ -310,7 +310,7 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
310310
// Emit filter headers progress only when heights change
311311
let (abs_header_height, filter_header_height) = {
312312
let storage = self.storage.lock().await;
313-
let storage_tip = storage.get_tip_height().await.ok().flatten().unwrap_or(0);
313+
let storage_tip = storage.get_tip_height().await.unwrap_or(0);
314314
let filter_tip =
315315
storage.get_filter_tip_height().await.ok().flatten().unwrap_or(0);
316316
(storage_tip, filter_tip)

dash-spv/src/storage/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ pub trait StorageManager: Send + Sync + 'static {
8181
async fn get_header(&self, height: u32) -> StorageResult<Option<BlockHeader>>;
8282

8383
/// Get the current tip blockchain height.
84-
async fn get_tip_height(&self) -> StorageResult<Option<u32>>;
84+
async fn get_tip_height(&self) -> Option<u32>;
85+
86+
async fn get_start_height(&self) -> Option<u32>;
87+
88+
async fn get_stored_headers_len(&self) -> u32;
8589

8690
/// Store filter headers.
8791
async fn store_filter_headers(&mut self, headers: &[FilterHeader]) -> StorageResult<()>;

dash-spv/src/storage/segments.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ pub struct SegmentCache<I: Persistable> {
7575
segments: HashMap<u32, Segment<I>>,
7676
evicted: HashMap<u32, Segment<I>>,
7777
tip_height: Option<u32>,
78+
start_height: Option<u32>,
7879
base_path: PathBuf,
7980
}
8081

@@ -133,12 +134,14 @@ impl<I: Persistable> SegmentCache<I> {
133134
segments: HashMap::with_capacity(Self::MAX_ACTIVE_SEGMENTS),
134135
evicted: HashMap::new(),
135136
tip_height: None,
137+
start_height: None,
136138
base_path,
137139
};
138140

139141
// Building the metadata
140142
if let Ok(entries) = fs::read_dir(&items_dir) {
141-
let mut max_segment_id = None;
143+
let mut max_seg_id = None;
144+
let mut min_seg_id = None;
142145

143146
for entry in entries.flatten() {
144147
if let Some(name) = entry.file_name().to_str() {
@@ -149,19 +152,27 @@ impl<I: Persistable> SegmentCache<I> {
149152
let segment_id_end = segment_id_start + 4;
150153

151154
if let Ok(id) = name[segment_id_start..segment_id_end].parse::<u32>() {
152-
max_segment_id =
153-
Some(max_segment_id.map_or(id, |max: u32| max.max(id)));
155+
max_seg_id = Some(max_seg_id.map_or(id, |max: u32| max.max(id)));
156+
min_seg_id = Some(min_seg_id.map_or(id, |min: u32| min.min(id)));
154157
}
155158
}
156159
}
157160
}
158161

159-
if let Some(segment_id) = max_segment_id {
162+
if let Some(segment_id) = max_seg_id {
160163
let segment = cache.get_segment(&segment_id).await?;
161164

162165
cache.tip_height = segment
163166
.last_valid_offset()
164-
.map(|offset| segment_id * Segment::<I>::ITEMS_PER_SEGMENT + offset);
167+
.map(|offset| Self::segment_id_to_start_height(segment_id) + offset);
168+
}
169+
170+
if let Some(segment_id) = min_seg_id {
171+
let segment = cache.get_segment(&segment_id).await?;
172+
173+
cache.start_height = segment
174+
.first_valid_offset()
175+
.map(|offset| Self::segment_id_to_start_height(segment_id) + offset);
165176
}
166177
}
167178

@@ -349,6 +360,11 @@ impl<I: Persistable> SegmentCache<I> {
349360
None => Some(height - 1),
350361
};
351362

363+
self.start_height = match self.start_height {
364+
Some(current) => Some(current.min(start_height)),
365+
None => Some(start_height),
366+
};
367+
352368
Ok(())
353369
}
354370

@@ -377,6 +393,11 @@ impl<I: Persistable> SegmentCache<I> {
377393
self.tip_height
378394
}
379395

396+
#[inline]
397+
pub fn start_height(&self) -> Option<u32> {
398+
self.start_height
399+
}
400+
380401
#[inline]
381402
pub fn next_height(&self) -> u32 {
382403
match self.tip_height() {

0 commit comments

Comments
 (0)