Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
aa0e1ea
the disk storage manager worker is now a time based check, removed ol…
ZocoLini Dec 16, 2025
a558988
tests updated
ZocoLini Dec 16, 2025
0ade0bf
thanks coderabbit
ZocoLini Dec 16, 2025
1f168b7
merged dev branch
ZocoLini Dec 18, 2025
2e85243
removed chain storage an related code
ZocoLini Dec 18, 2025
3ca55b5
removed headers from ChainState
ZocoLini Dec 18, 2025
8f3d065
tip_height method removed
ZocoLini Dec 18, 2025
53be7f4
removed get_tip_hesh
ZocoLini Dec 18, 2025
eb32b7b
replaced header_at_height
ZocoLini Dec 18, 2025
2bf3a91
removed unused methods
ZocoLini Dec 18, 2025
f758a7f
init_from_checkpoint sync
ZocoLini Dec 18, 2025
f995b89
tip_header removed
ZocoLini Dec 18, 2025
7acccc0
removed two methos that where invovled in the same process
ZocoLini Dec 18, 2025
90957cf
fixed ffi
ZocoLini Dec 18, 2025
c443764
test updated to the changes
ZocoLini Dec 18, 2025
4be4cef
chore: removed fork detector and fork structs (#290)
ZocoLini Dec 19, 2025
0e98a8b
get_header now checks out of bound to return None instead of panics
ZocoLini Dec 19, 2025
06d35e9
start height was not being updated properly
ZocoLini Dec 19, 2025
abb37f8
fixed other test by correctly storing the headers in the storage
ZocoLini Dec 19, 2025
4a26a7f
removed genesis block creation in ChainState creation
ZocoLini Dec 19, 2025
bc544a5
fixed clippy warnings
ZocoLini Dec 19, 2025
0b05253
Merge branch 'remove-chain-storage' into remove-headers-from-chain-state
ZocoLini Dec 19, 2025
4be7e6e
dropped unuseed code
ZocoLini Dec 19, 2025
b492b93
removed filters field from ChainState (ez)
ZocoLini Dec 19, 2025
90f0b07
traits created
ZocoLini Dec 22, 2025
014baff
merged storage worker rework
ZocoLini Dec 22, 2025
7f24a22
merged remove-filters-from-chain-state
ZocoLini Dec 22, 2025
e8ce999
merged remove-headers-from-chain-state
ZocoLini Dec 22, 2025
cac9b29
everything moved where I want it to be
ZocoLini Dec 23, 2025
8759858
general structure made
ZocoLini Dec 26, 2025
58c29ad
persist segments caches now requires the directory where the user wan…
ZocoLini Dec 26, 2025
c809c1f
using rwlock to allow segmentcache mutability behind inmutable ref
ZocoLini Dec 26, 2025
0bf4407
clear method fixed
ZocoLini Dec 26, 2025
3a9e273
default method implementations in storage traits
ZocoLini Dec 26, 2025
f40a2bd
storage manager trait implemented
ZocoLini Dec 29, 2025
8a542f0
fixed code to pass the tests
ZocoLini Dec 29, 2025
2bc0490
storage documentation updated
ZocoLini Dec 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions dash-spv-ffi/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,6 @@ impl From<DetailedSyncProgress> for FFIDetailedSyncProgress {

#[repr(C)]
pub struct FFIChainState {
pub header_height: u32,
pub filter_header_height: u32,
pub masternode_height: u32,
pub last_chainlock_height: u32,
pub last_chainlock_hash: FFIString,
Expand All @@ -192,8 +190,6 @@ pub struct FFIChainState {
impl From<ChainState> for FFIChainState {
fn from(state: ChainState) -> Self {
FFIChainState {
header_height: state.headers.len() as u32,
filter_header_height: state.filter_headers.len() as u32,
masternode_height: state.last_masternode_diff_height.unwrap_or(0),
last_chainlock_height: state.last_chainlock_height.unwrap_or(0),
last_chainlock_hash: FFIString::new(
Expand Down
5 changes: 1 addition & 4 deletions dash-spv-ffi/tests/unit/test_type_conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,6 @@ mod tests {
#[test]
fn test_chain_state_none_values() {
let state = dash_spv::ChainState {
headers: vec![],
filter_headers: vec![],
last_chainlock_height: None,
last_chainlock_hash: None,
current_filter_tip: None,
Expand All @@ -174,8 +172,7 @@ mod tests {
};

let ffi_state = FFIChainState::from(state);
assert_eq!(ffi_state.header_height, 0);
assert_eq!(ffi_state.filter_header_height, 0);

assert_eq!(ffi_state.masternode_height, 0);
assert_eq!(ffi_state.last_chainlock_height, 0);
assert_eq!(ffi_state.current_filter_tip, 0);
Expand Down
3 changes: 1 addition & 2 deletions dash-spv/examples/filter_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let network_manager = PeerNetworkManager::new(&config).await?;

// Create storage manager
let storage_manager =
DiskStorageManager::new("./.tmp/filter-sync-example-storage".into()).await?;
let storage_manager = DiskStorageManager::new("./.tmp/filter-sync-example-storage").await?;

// Create wallet manager
let wallet = Arc::new(RwLock::new(WalletManager::<ManagedWalletInfo>::new()));
Expand Down
3 changes: 1 addition & 2 deletions dash-spv/examples/simple_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let network_manager = PeerNetworkManager::new(&config).await?;

// Create storage manager
let storage_manager =
DiskStorageManager::new("./.tmp/simple-sync-example-storage".into()).await?;
let storage_manager = DiskStorageManager::new("./.tmp/simple-sync-example-storage").await?;

// Create wallet manager
let wallet = Arc::new(RwLock::new(WalletManager::<ManagedWalletInfo>::new()));
Expand Down
3 changes: 1 addition & 2 deletions dash-spv/examples/spv_with_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let network_manager = PeerNetworkManager::new(&config).await?;

// Create storage manager - use disk storage for persistence
let storage_manager =
DiskStorageManager::new("./.tmp/spv-with-wallet-example-storage".into()).await?;
let storage_manager = DiskStorageManager::new("./.tmp/spv-with-wallet-example-storage").await?;

// Create wallet manager
let wallet = Arc::new(RwLock::new(WalletManager::<ManagedWalletInfo>::new()));
Expand Down
6 changes: 5 additions & 1 deletion dash-spv/src/chain/chainlock_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,11 @@ impl ChainLockManager {
}

// Verify the block exists in our chain
if let Some(header) = chain_state.header_at_height(chain_lock.block_height) {
if let Some(header) = storage
.get_header(chain_lock.block_height)
.await
.map_err(ValidationError::StorageError)?
{
let header_hash = header.block_hash();
if header_hash != chain_lock.block_hash {
return Err(ValidationError::InvalidChainLock(format!(
Expand Down
2 changes: 1 addition & 1 deletion dash-spv/src/client/block_processor_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
mod tests {
use crate::client::block_processor::{BlockProcessingTask, BlockProcessor};

use crate::storage::DiskStorageManager;
use crate::storage::{BlockHeaderStorage, DiskStorageManager};
use crate::types::{SpvEvent, SpvStats};
use dashcore::{blockdata::constants::genesis_block, Block, Network, Transaction};

Expand Down
47 changes: 7 additions & 40 deletions dash-spv/src/client/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,17 @@ impl<

/// Returns the current chain tip hash if available.
pub async fn tip_hash(&self) -> Option<dashcore::BlockHash> {
let state = self.state.read().await;
state.tip_hash()
let storage = self.storage.lock().await;

let tip_height = storage.get_tip_height().await?;
let header = storage.get_header(tip_height).await.ok()??;

Some(header.block_hash())
}

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

/// Get current chain state (read-only).
Expand Down Expand Up @@ -271,42 +274,6 @@ impl<
Ok(())
}

/// Clear all stored filter headers and compact filters while keeping other data intact.
pub async fn clear_filters(&mut self) -> Result<()> {
{
let mut storage = self.storage.lock().await;
storage.clear_filters().await.map_err(SpvError::Storage)?;
}

// Reset in-memory chain state for filters
{
let mut state = self.state.write().await;
state.filter_headers.clear();
state.current_filter_tip = None;
}

// Reset filter sync manager tracking
self.sync_manager.filter_sync_mut().clear_filter_state().await;

// Reset filter-related statistics
let received_heights = {
let stats = self.stats.read().await;
stats.received_filter_heights.clone()
};

{
let mut stats = self.stats.write().await;
stats.filter_headers_downloaded = 0;
stats.filter_height = 0;
stats.filters_downloaded = 0;
stats.filters_received = 0;
}

received_heights.lock().await.clear();

Ok(())
}

// ============ Configuration ============

/// Update the client configuration.
Expand Down
37 changes: 9 additions & 28 deletions dash-spv/src/client/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,30 +169,12 @@ impl<
// This ensures the ChainState has headers loaded for both checkpoint and normal sync
let tip_height = {
let storage = self.storage.lock().await;
storage.get_tip_height().await.map_err(SpvError::Storage)?.unwrap_or(0)
storage.get_tip_height().await.unwrap_or(0)
};
if tip_height > 0 {
tracing::info!("Found {} headers in storage, loading into sync manager...", tip_height);
let loaded_count = {
let storage = self.storage.lock().await;
self.sync_manager.load_headers_from_storage(&storage).await
};

match loaded_count {
Ok(loaded_count) => {
tracing::info!("✅ Sync manager loaded {} headers from storage", loaded_count);
}
Err(e) => {
tracing::error!("Failed to load headers into sync manager: {}", e);
// For checkpoint sync, this is critical
let state = self.state.read().await;
if state.synced_from_checkpoint() {
return Err(SpvError::Sync(e));
}
// For normal sync, we can continue as headers will be re-synced
tracing::warn!("Continuing without pre-loaded headers for normal sync");
}
}
let storage = self.storage.lock().await;
self.sync_manager.load_headers_from_storage(&storage).await
}

// Connect to network
Expand All @@ -209,8 +191,7 @@ impl<
// Get initial header count from storage
let (header_height, filter_height) = {
let storage = self.storage.lock().await;
let h_height =
storage.get_tip_height().await.map_err(SpvError::Storage)?.unwrap_or(0);
let h_height = storage.get_tip_height().await.unwrap_or(0);
let f_height =
storage.get_filter_tip_height().await.map_err(SpvError::Storage)?.unwrap_or(0);
(h_height, f_height)
Expand Down Expand Up @@ -244,7 +225,7 @@ impl<
// Shutdown storage to ensure all data is persisted
{
let mut storage = self.storage.lock().await;
storage.shutdown().await.map_err(SpvError::Storage)?;
storage.shutdown().await;
tracing::info!("Storage shutdown completed - all data persisted");
}

Expand All @@ -271,7 +252,7 @@ impl<
// Check if we already have any headers in storage
let current_tip = {
let storage = self.storage.lock().await;
storage.get_tip_height().await.map_err(SpvError::Storage)?
storage.get_tip_height().await
};

if current_tip.is_some() {
Expand Down Expand Up @@ -344,12 +325,12 @@ impl<

// Clone the chain state for storage
let chain_state_for_storage = (*chain_state).clone();
let headers_len = chain_state_for_storage.headers.len() as u32;
drop(chain_state);

// Update storage with chain state including sync_base_height
{
let mut storage = self.storage.lock().await;
storage.store_headers(&[checkpoint_header]).await?;
storage
.store_chain_state(&chain_state_for_storage)
.await
Expand All @@ -366,7 +347,7 @@ impl<
);

// Update the sync manager's cached flags from the checkpoint-initialized state
self.sync_manager.update_chain_state_cache(checkpoint.height, headers_len);
self.sync_manager.update_chain_state_cache(checkpoint.height);
tracing::info!(
"Updated sync manager with checkpoint-initialized chain state"
);
Expand Down Expand Up @@ -414,7 +395,7 @@ impl<
// Verify it was stored correctly
let stored_height = {
let storage = self.storage.lock().await;
storage.get_tip_height().await.map_err(SpvError::Storage)?
storage.get_tip_height().await
};
tracing::info!(
"✅ Genesis block initialized at height 0, storage reports tip height: {:?}",
Expand Down
2 changes: 1 addition & 1 deletion dash-spv/src/client/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl<
// Get current heights from storage
{
let storage = self.storage.lock().await;
if let Ok(Some(header_height)) = storage.get_tip_height().await {
if let Some(header_height) = storage.get_tip_height().await {
stats.header_height = header_height;
}

Expand Down
2 changes: 1 addition & 1 deletion dash-spv/src/client/status_display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl<'a, S: StorageManager + Send + Sync + 'static, W: WalletInterface + Send +
// For genesis sync: sync_base_height = 0, so height = 0 + storage_count
// For checkpoint sync: height = checkpoint_height + storage_count
let storage = self.storage.lock().await;
if let Ok(Some(storage_tip)) = storage.get_tip_height().await {
if let Some(storage_tip) = storage.get_tip_height().await {
let blockchain_height = storage_tip;
if with_logging {
tracing::debug!(
Expand Down
6 changes: 3 additions & 3 deletions dash-spv/src/client/sync_coordinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl<
let result = SyncProgress {
header_height: {
let storage = self.storage.lock().await;
storage.get_tip_height().await.map_err(SpvError::Storage)?.unwrap_or(0)
storage.get_tip_height().await.unwrap_or(0)
},
filter_header_height: {
let storage = self.storage.lock().await;
Expand Down Expand Up @@ -241,7 +241,7 @@ impl<
// Storage tip now represents the absolute blockchain height.
let current_tip_height = {
let storage = self.storage.lock().await;
storage.get_tip_height().await.ok().flatten().unwrap_or(0)
storage.get_tip_height().await.unwrap_or(0)
};
let current_height = current_tip_height;
let peer_best = self
Expand Down Expand Up @@ -315,7 +315,7 @@ impl<
// Emit filter headers progress only when heights change
let (abs_header_height, filter_header_height) = {
let storage = self.storage.lock().await;
let storage_tip = storage.get_tip_height().await.ok().flatten().unwrap_or(0);
let storage_tip = storage.get_tip_height().await.unwrap_or(0);
let filter_tip =
storage.get_filter_tip_height().await.ok().flatten().unwrap_or(0);
(storage_tip, filter_tip)
Expand Down
2 changes: 1 addition & 1 deletion dash-spv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
//!
//! // Create the required components
//! let network = PeerNetworkManager::new(&config).await?;
//! let storage = DiskStorageManager::new("./.tmp/example-storage".into()).await?;
//! let storage = DiskStorageManager::new("./.tmp/example-storage").await?;
//! let wallet = Arc::new(RwLock::new(WalletManager::<ManagedWalletInfo>::new()));
//!
//! // Create and start the client
Expand Down
Loading