diff --git a/dash-spv/Cargo.toml b/dash-spv/Cargo.toml index 5d6851de3..36064d816 100644 --- a/dash-spv/Cargo.toml +++ b/dash-spv/Cargo.toml @@ -81,8 +81,5 @@ name = "dash_spv" path = "src/lib.rs" [features] -# Gate incomplete mock-dependent tests to avoid "unexpected cfg" warnings. -skip_mock_implementation_incomplete = [] - # Terminal UI feature (off by default, for use by binary only) terminal-ui = ["dep:crossterm"] diff --git a/dash-spv/src/chain/chainlock_test.rs b/dash-spv/src/chain/chainlock_test.rs index 2e840d743..a8870472c 100644 --- a/dash-spv/src/chain/chainlock_test.rs +++ b/dash-spv/src/chain/chainlock_test.rs @@ -1,10 +1,22 @@ #[cfg(test)] mod tests { use super::super::*; - use crate::{storage::DiskStorageManager, types::ChainState}; - use dashcore::{ChainLock, Network}; + use crate::{ + storage::{BlockHeaderStorage, DiskStorageManager}, + types::ChainState, + }; + use dashcore::{constants::genesis_block, ChainLock, Network}; use dashcore_test_utils::fixtures::test_block_hash; + /// Create a test ChainLock with minimal valid data + fn create_test_chainlock(height: u32, block_hash: BlockHash) -> ChainLock { + ChainLock { + block_height: height, + block_hash, + signature: dashcore::bls_sig_utils::BLSSignature::from([0u8; 96]), // BLS signature placeholder + } + } + #[tokio::test] async fn test_chainlock_processing() { // Create storage and ChainLock manager @@ -47,11 +59,8 @@ mod tests { let chain_state = ChainState::new_for_network(Network::Testnet); // Process first ChainLock at height 1000 - let chainlock1 = ChainLock { - block_height: 1000, - block_hash: test_block_hash(1), - signature: dashcore::bls_sig_utils::BLSSignature::from([0; 96]), - }; + let chainlock1 = create_test_chainlock(1000, test_block_hash(1)); + chainlock_manager .process_chain_lock(chainlock1.clone(), &chain_state, &mut storage) .await @@ -104,4 +113,57 @@ mod tests { assert!(chainlock_manager.would_violate_chain_lock(1500, 2500)); // Would reorg ChainLock at 2000 assert!(!chainlock_manager.would_violate_chain_lock(3001, 4000)); // After ChainLocks - OK } + + #[tokio::test] + async fn test_chainlock_queue_and_process_flow() { + let chainlock_manager = ChainLockManager::new(true); + + // Queue multiple ChainLocks + let chain_lock1 = create_test_chainlock(100, BlockHash::from([1u8; 32])); + let chain_lock2 = create_test_chainlock(200, BlockHash::from([2u8; 32])); + let chain_lock3 = create_test_chainlock(300, BlockHash::from([3u8; 32])); + + chainlock_manager.queue_pending_chainlock(chain_lock1).unwrap(); + chainlock_manager.queue_pending_chainlock(chain_lock2).unwrap(); + chainlock_manager.queue_pending_chainlock(chain_lock3).unwrap(); + + // Verify all are queued + { + // Note: pending_chainlocks is private, can't access directly + let pending = chainlock_manager.pending_chainlocks.read().unwrap(); + assert_eq!(pending.len(), 3); + assert_eq!(pending[0].block_height, 100); + assert_eq!(pending[1].block_height, 200); + assert_eq!(pending[2].block_height, 300); + } + } + + #[tokio::test] + async fn test_chainlock_manager_cache_operations() { + let mut storage = DiskStorageManager::with_temp_dir().await.unwrap(); + + let chainlock_manager = ChainLockManager::new(true); + + // Add test headers + let genesis = genesis_block(Network::Dash).header; + storage.store_headers_at_height(&[genesis], 0).await.unwrap(); + + // Create and process a ChainLock + let chain_lock = create_test_chainlock(0, genesis.block_hash()); + let chain_state = ChainState::new(); + let _ = chainlock_manager + .process_chain_lock(chain_lock.clone(), &chain_state, &mut storage) + .await; + + // Test cache operations + assert!(chainlock_manager.has_chain_lock_at_height(0)); + + let entry = chainlock_manager.get_chain_lock_by_height(0); + assert!(entry.is_some()); + assert_eq!(entry.unwrap().chain_lock.block_height, 0); + + let entry_by_hash = chainlock_manager.get_chain_lock_by_hash(&genesis.block_hash()); + assert!(entry_by_hash.is_some()); + assert_eq!(entry_by_hash.unwrap().chain_lock.block_height, 0); + } } diff --git a/dash-spv/tests/block_download_test.rs b/dash-spv/tests/block_download_test.rs index 86b81f0c9..e5014621b 100644 --- a/dash-spv/tests/block_download_test.rs +++ b/dash-spv/tests/block_download_test.rs @@ -1,10 +1,3 @@ -//! Tests for block downloading on filter match functionality. -//! -//! NOTE: This test file is currently disabled due to incomplete mock NetworkManager implementation. -//! TODO: Re-enable once NetworkManager trait methods are fully implemented. - -#![cfg(feature = "skip_mock_implementation_incomplete")] - //! Tests for block downloading on filter match functionality. use std::collections::HashSet; @@ -159,7 +152,6 @@ fn create_test_filter_match(block_hash: BlockHash, height: u32) -> FilterMatch { } } -#[ignore = "mock implementation incomplete"] #[tokio::test] async fn test_filter_sync_manager_creation() { let config = create_test_config(); @@ -171,7 +163,6 @@ async fn test_filter_sync_manager_creation() { assert_eq!(filter_sync.pending_download_count(), 0); } -#[ignore = "mock implementation incomplete"] #[tokio::test] async fn test_request_block_download() { let config = create_test_config(); @@ -209,7 +200,6 @@ async fn test_request_block_download() { assert_eq!(filter_sync.pending_download_count(), 1); } -#[ignore = "mock implementation incomplete"] #[tokio::test] async fn test_duplicate_block_request_prevention() { let config = create_test_config(); @@ -233,7 +223,6 @@ async fn test_duplicate_block_request_prevention() { assert_eq!(filter_sync.pending_download_count(), 1); } -#[ignore = "mock implementation incomplete"] #[tokio::test] async fn test_handle_downloaded_block() { let config = create_test_config(); @@ -264,7 +253,6 @@ async fn test_handle_downloaded_block() { assert_eq!(filter_sync.pending_download_count(), 0); } -#[ignore = "mock implementation incomplete"] #[tokio::test] async fn test_handle_unexpected_block() { let config = create_test_config(); @@ -281,7 +269,6 @@ async fn test_handle_unexpected_block() { assert!(result.is_none()); } -#[ignore = "mock implementation incomplete"] #[tokio::test] async fn test_process_multiple_filter_matches() { let config = create_test_config(); @@ -332,11 +319,9 @@ async fn test_process_multiple_filter_matches() { assert_eq!(filter_sync.pending_download_count(), 3); } -#[ignore = "mock implementation incomplete"] #[tokio::test] async fn test_sync_manager_integration() {} -#[ignore = "mock implementation incomplete"] #[tokio::test] async fn test_filter_match_and_download_workflow() { let config = create_test_config(); @@ -369,7 +354,6 @@ async fn test_filter_match_and_download_workflow() { assert!(filter_sync.has_pending_downloads()); } -#[ignore = "mock implementation incomplete"] #[tokio::test] async fn test_reset_clears_download_state() { let config = create_test_config(); diff --git a/dash-spv/tests/chainlock_validation_test.rs b/dash-spv/tests/chainlock_validation_test.rs deleted file mode 100644 index d3f5a8810..000000000 --- a/dash-spv/tests/chainlock_validation_test.rs +++ /dev/null @@ -1,372 +0,0 @@ -//! Integration tests for ChainLock validation flow with masternode engine -//! -//! NOTE: This test file is currently disabled due to incomplete mock NetworkManager implementation. -//! TODO: Re-enable once NetworkManager trait methods are fully implemented. - -#![cfg(feature = "skip_mock_implementation_incomplete")] - -//! Integration tests for ChainLock validation flow with masternode engine - -use dash_spv::client::{ClientConfig, DashSpvClient}; -use dash_spv::network::NetworkManager; -use dash_spv::storage::DiskStorageManager; -use dash_spv::types::ValidationMode; -use dashcore::blockdata::constants::genesis_block; -// use dashcore::sml::masternode_list_engine::MasternodeListEngine; -use dashcore::Network; -use dashcore::{BlockHash, ChainLock}; -use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; -use key_wallet_manager::wallet_manager::WalletManager; -use std::sync::Arc; -use tempfile::TempDir; -use tokio::sync::RwLock; -use tracing::{info, Level}; - -/// Mock network manager that simulates ChainLock messages -struct MockNetworkManager { - chain_locks: Vec, - chain_locks_sent: Arc>, -} - -impl MockNetworkManager { - fn new() -> Self { - Self { - chain_locks: Vec::new(), - chain_locks_sent: Arc::new(RwLock::new(0)), - } - } - - fn add_chain_lock(&mut self, chain_lock: ChainLock) { - self.chain_locks.push(chain_lock); - } -} - -#[async_trait::async_trait] -impl NetworkManager for MockNetworkManager { - fn as_any(&self) -> &dyn std::any::Any { - self - } - - async fn connect(&mut self) -> dash_spv::error::NetworkResult<()> { - Ok(()) - } - - async fn disconnect(&mut self) -> dash_spv::error::NetworkResult<()> { - Ok(()) - } - - async fn send_message( - &mut self, - _message: dashcore::network::message::NetworkMessage, - ) -> dash_spv::error::NetworkResult<()> { - Ok(()) - } - - async fn receive_message( - &mut self, - ) -> dash_spv::error::NetworkResult> { - // Simulate receiving ChainLock messages - let mut sent = self.chain_locks_sent.write().await; - if *sent < self.chain_locks.len() { - let chain_lock = self.chain_locks[*sent].clone(); - *sent += 1; - Ok(Some(dashcore::network::message::NetworkMessage::CLSig(chain_lock))) - } else { - // No more messages - Ok(None) - } - } - - fn is_connected(&self) -> bool { - true - } - - fn peer_count(&self) -> usize { - 1 - } - - fn peer_info(&self) -> Vec { - vec![dash_spv::types::PeerInfo { - address: "127.0.0.1:9999".parse().unwrap(), - connected: true, - last_seen: std::time::SystemTime::now(), - version: Some(70232), - services: Some(0), // ServiceFlags::NONE as u64 - user_agent: Some("/MockNode/".to_string()), - best_height: Some(0), - wants_dsq_messages: Some(false), - has_sent_headers2: false, - }] - } - - async fn get_peer_best_height(&self) -> dash_spv::error::NetworkResult> { - Ok(Some(0)) // Return dummy height - } - - async fn has_peer_with_service( - &self, - _service_flags: dashcore::network::constants::ServiceFlags, - ) -> bool { - true // Mock always has service - } - - async fn update_peer_dsq_preference( - &mut self, - _wants_dsq: bool, - ) -> dash_spv::error::NetworkResult<()> { - Ok(()) // No-op for mock - } -} - -fn init_logging() { - let _ = tracing_subscriber::fmt() - .with_max_level(Level::DEBUG) - .with_target(false) - .with_thread_ids(true) - .with_line_number(true) - .try_init(); -} - -/// Create a test ChainLock with minimal valid data -fn create_test_chainlock(height: u32, block_hash: BlockHash) -> ChainLock { - ChainLock { - block_height: height, - block_hash, - signature: dashcore::bls_sig_utils::BLSSignature::from([0u8; 96]), // BLS signature placeholder - } -} - -#[ignore = "mock implementation incomplete"] -#[tokio::test] -async fn test_chainlock_validation_without_masternode_engine() { - init_logging(); - - // Placeholder: test requires API updates; skip for now - return; - - // Verify it was queued - // Note: pending_chainlocks is private, can't access directly - // let pending = chainlock_manager.pending_chainlocks.read().unwrap(); - // assert_eq!(pending.len(), 1); - // assert_eq!(pending[0].block_height, 0); -} - -#[ignore = "mock implementation incomplete"] -#[tokio::test] -async fn test_chainlock_validation_with_masternode_engine() { - init_logging(); - - // Create temp directory for storage - let temp_dir = TempDir::new().unwrap(); - let storage_path = temp_dir.path().to_path_buf(); - - // Create storage and network managers - let storage = DiskStorageManager::new(storage_path).await.unwrap(); - let mut network = MockNetworkManager::new(); - - // Add a test ChainLock to be received - let genesis = genesis_block(Network::Dash).header; - let chain_lock = create_test_chainlock(0, genesis.block_hash()); - network.add_chain_lock(chain_lock.clone()); - - // Create client config with masternodes enabled - let config = ClientConfig { - network: Network::Dash, - enable_filters: false, - enable_masternodes: true, - validation_mode: ValidationMode::Basic, - ..Default::default() - }; - - // Create wallet manager - let wallet = Arc::new(RwLock::new(WalletManager::::new(config.network))); - - // Create the SPV client - let client = DashSpvClient::new(config, network, storage, wallet).await.unwrap(); - - // Add genesis header - // Note: storage_mut() is not available in current API - // let storage = client.storage_mut(); - // storage.store_header(&genesis, 0).await.unwrap(); - - // Simulate masternode sync completion by creating a mock engine - // In a real scenario, this would be populated by the masternode sync - // let mock_engine = MasternodeListEngine::default_for_network(Network::Dash); - - // Update the ChainLock manager with the engine - let updated = client.update_chainlock_validation().unwrap(); - assert!(!updated); // Should be false since we don't have a real engine - - // For testing, directly set a mock engine - // let engine_arc = Arc::new(mock_engine); - // client.chainlock_manager().set_masternode_engine(engine_arc); - - // Process pending ChainLocks (skipped for now due to API changes) - // let chain_state = ChainState::new(); - // Note: storage_mut() is not available in current API - // let storage = client.storage_mut(); - // Skip this test section as it needs to be rewritten for the new client API - return; -} - -#[ignore = "mock implementation incomplete"] -#[tokio::test] -async fn test_chainlock_queue_and_process_flow() { - init_logging(); - - // Create temp directory for storage - let temp_dir = TempDir::new().unwrap(); - let storage_path = temp_dir.path().to_path_buf(); - - // Create storage - let storage = DiskStorageManager::new(storage_path).await.unwrap(); - let network = MockNetworkManager::new(); - - // Create client config - let config = ClientConfig { - network: Network::Dash, - enable_filters: false, - enable_masternodes: false, - validation_mode: ValidationMode::Basic, - ..Default::default() - }; - - // Create wallet manager - let wallet = Arc::new(RwLock::new(WalletManager::::new(config.network))); - - // Create the SPV client - let client = DashSpvClient::new(config, network, storage, wallet).await.unwrap(); - let chainlock_manager = client.chainlock_manager(); - - // Queue multiple ChainLocks - let chain_lock1 = create_test_chainlock(100, BlockHash::from([1u8; 32])); - let chain_lock2 = create_test_chainlock(200, BlockHash::from([2u8; 32])); - let chain_lock3 = create_test_chainlock(300, BlockHash::from([3u8; 32])); - - chainlock_manager.queue_pending_chainlock(chain_lock1).unwrap(); - chainlock_manager.queue_pending_chainlock(chain_lock2).unwrap(); - chainlock_manager.queue_pending_chainlock(chain_lock3).unwrap(); - - // Verify all are queued - { - // Note: pending_chainlocks is private, can't access directly - // let pending = chainlock_manager.pending_chainlocks.read().unwrap(); - // assert_eq!(pending.len(), 3); - // assert_eq!(pending[0].block_height, 100); - // assert_eq!(pending[1].block_height, 200); - // assert_eq!(pending[2].block_height, 300); - } - - // Process pending (skipped for now due to API changes) - // Skip this test as it needs to be rewritten for the new client API - return; -} - -#[ignore = "mock implementation incomplete"] -#[tokio::test] -async fn test_chainlock_manager_cache_operations() { - init_logging(); - - // Create temp directory for storage - let temp_dir = TempDir::new().unwrap(); - let storage_path = temp_dir.path().to_path_buf(); - - // Create storage - let storage = DiskStorageManager::new(storage_path).await.unwrap(); - let network = MockNetworkManager::new(); - - // Create client config - let config = ClientConfig { - network: Network::Dash, - enable_filters: false, - enable_masternodes: false, - validation_mode: ValidationMode::Basic, - ..Default::default() - }; - - // Create wallet manager - let wallet = Arc::new(RwLock::new(WalletManager::::new(config.network))); - - // Create the SPV client - let client = DashSpvClient::new(config, network, storage, wallet).await.unwrap(); - let chainlock_manager = client.chainlock_manager(); - - // Add test headers - let genesis = genesis_block(Network::Dash).header; - // let storage = client.storage(); - // storage.store_header(&genesis, 0).await.unwrap(); - - // Create and process a ChainLock - skip for now as storage access pattern changed - // let chain_lock = create_test_chainlock(0, genesis.block_hash()); - // let chain_state = ChainState::new(); - // Note: storage access pattern has changed in the new client API - // let _ = chainlock_manager.process_chain_lock(chain_lock.clone(), &chain_state, storage).await; - - // Test cache operations - assert!(chainlock_manager.has_chain_lock_at_height(0)); - - let entry = chainlock_manager.get_chain_lock_by_height(0); - assert!(entry.is_some()); - assert_eq!(entry.unwrap().chain_lock.block_height, 0); - - let entry_by_hash = chainlock_manager.get_chain_lock_by_hash(&genesis.block_hash()); - assert!(entry_by_hash.is_some()); - assert_eq!(entry_by_hash.unwrap().chain_lock.block_height, 0); -} - -#[ignore = "mock implementation incomplete"] -#[tokio::test] -async fn test_client_chainlock_update_flow() { - init_logging(); - - // Create temp directory for storage - let temp_dir = TempDir::new().unwrap(); - let storage_path = temp_dir.path().to_path_buf(); - - // Create storage and network - let storage = DiskStorageManager::new(storage_path).await.unwrap(); - let network = MockNetworkManager::new(); - - // Create client config with masternodes enabled - let config = ClientConfig { - network: Network::Dash, - enable_filters: false, - enable_masternodes: true, - validation_mode: ValidationMode::Basic, - ..Default::default() - }; - - // Create wallet manager - let wallet = Arc::new(RwLock::new(WalletManager::::new(config.network))); - - // Create the SPV client - let client = DashSpvClient::new(config, network, storage, wallet).await.unwrap(); - - // Initially, update should fail (no masternode engine) - let updated = client.update_chainlock_validation().unwrap(); - assert!(!updated); - - // Simulate masternode sync by manually setting sequential sync state - // In real usage, this would happen automatically during sync - // Note: sync_manager is private, can't access directly - // client.sync_manager.set_phase(dash_spv::sync::SyncPhase::FullySynced { - // sync_completed_at: std::time::Instant::now(), - // total_sync_time: Duration::from_secs(10), - // headers_synced: 1000, - // filters_synced: 0, - // blocks_downloaded: 0, - // }); - - // Create a mock masternode list engine - // let mock_engine = MasternodeListEngine::default_for_network(Network::Dash); - - // Manually inject the engine (in real usage, this would come from masternode sync) - // Note: sync_manager is private, can't access directly - // client.sync_manager.masternode_sync_mut().set_engine(Some(mock_engine)); - - // Now update should succeed - let updated = client.update_chainlock_validation().unwrap(); - assert!(updated); - - info!("ChainLock validation update flow test completed"); -} diff --git a/dash-spv/tests/edge_case_filter_sync_test.rs b/dash-spv/tests/edge_case_filter_sync_test.rs index f79b4052c..f1fe6a85c 100644 --- a/dash-spv/tests/edge_case_filter_sync_test.rs +++ b/dash-spv/tests/edge_case_filter_sync_test.rs @@ -1,10 +1,3 @@ -//! Tests for edge case handling in filter header sync, particularly at the tip. -//! -//! NOTE: This test file is currently disabled due to incomplete mock NetworkManager implementation. -//! TODO: Re-enable once NetworkManager trait methods are fully implemented. - -#![cfg(feature = "skip_mock_implementation_incomplete")] - //! Tests for edge case handling in filter header sync, particularly at the tip. use std::collections::HashSet; @@ -114,7 +107,6 @@ impl NetworkManager for MockNetworkManager { } } -#[ignore = "mock implementation incomplete"] #[tokio::test] async fn test_filter_sync_at_tip_edge_case() { let config = ClientConfig::new(Network::Dash); @@ -159,7 +151,6 @@ async fn test_filter_sync_at_tip_edge_case() { assert_eq!(sent_messages.len(), 0, "Should not send any messages when at tip"); } -#[ignore = "mock implementation incomplete"] #[tokio::test] async fn test_no_invalid_getcfheaders_at_tip() { let config = ClientConfig::new(Network::Dash); diff --git a/dash-spv/tests/filter_header_verification_test.rs b/dash-spv/tests/filter_header_verification_test.rs index 0d4b6de4a..60b748294 100644 --- a/dash-spv/tests/filter_header_verification_test.rs +++ b/dash-spv/tests/filter_header_verification_test.rs @@ -1,10 +1,3 @@ -//! Test to replicate the filter header chain verification failure observed in production. -//! -//! NOTE: This test file is currently disabled due to incomplete mock NetworkManager implementation. -//! TODO: Re-enable once NetworkManager trait methods are fully implemented. - -#![cfg(feature = "skip_mock_implementation_incomplete")] - //! Test to replicate the filter header chain verification failure observed in production. //! //! This test reproduces the exact scenario from the logs where: diff --git a/dash-spv/tests/rollback_test.rs b/dash-spv/tests/rollback_test.rs deleted file mode 100644 index 5a985e830..000000000 --- a/dash-spv/tests/rollback_test.rs +++ /dev/null @@ -1,113 +0,0 @@ -//! Tests for rollback functionality. -//! -//! NOTE: This test file is currently disabled due to incomplete mock StorageManager implementation. -//! TODO: Re-enable once StorageManager trait methods are fully implemented. - -#![cfg(feature = "skip_mock_implementation_incomplete")] - -use dash_spv::storage::{BlockHeaderStorage, DiskStorageManager, FilterHeaderStorage}; -use dashcore::{ - block::{Header as BlockHeader, Version}, - pow::CompactTarget, - BlockHash, -}; -use dashcore_hashes::Hash; -use tempfile::TempDir; - -#[tokio::test] -#[ignore = "mock implementation incomplete"] -async fn test_disk_storage_rollback() -> Result<(), Box> { - // Create a temporary directory for testing - let temp_dir = TempDir::new()?; - let mut storage = DiskStorageManager::new(temp_dir.path().to_path_buf()).await?; - - // Create test headers - let headers: Vec = (0..10) - .map(|i| BlockHeader { - version: Version::from_consensus(1), - prev_blockhash: if i == 0 { - BlockHash::all_zeros() - } else { - BlockHash::from_byte_array([i as u8 - 1; 32]) - }, - merkle_root: dashcore::hashes::sha256d::Hash::from_byte_array([(i + 100) as u8; 32]) - .into(), - time: 1000000 + i, - bits: CompactTarget::from_consensus(0x1d00ffff), - nonce: 12345 + i, - }) - .collect(); - - // Store headers - storage.store_headers(&headers).await?; - - // Verify we have 10 headers - let tip_height = storage.get_tip_height().await; - assert_eq!(tip_height, Some(9)); - - // Load all headers to verify - let loaded_headers = storage.load_headers(0..10).await?; - assert_eq!(loaded_headers.len(), 10); - - // Test rollback to height 5 - // storage.rollback_to_height(5).await?; - - // TODO: Test assertions commented out because rollback_to_height is not implemented - // Verify tip height is now 5 - let _ = storage.get_tip_height().await; - // assert_eq!(tip_height_after_rollback, Some(5)); - - // Verify we can only load headers up to height 5 - let _ = storage.load_headers(0..10).await?; - // assert_eq!(headers_after_rollback.len(), 6); // heights 0-5 - - // Verify header at height 6 is not accessible - let _ = storage.get_header(6).await?; - // assert!(header_at_6.is_none()); - - // Verify header hash index doesn't contain removed headers - let hash_of_removed_header = headers[7].block_hash(); - let _ = storage.get_header_height_by_hash(&hash_of_removed_header).await?; - // assert!(height_of_removed.is_none()); - - Ok(()) -} - -#[tokio::test] -#[ignore = "mock implementation incomplete"] -async fn test_disk_storage_rollback_filter_headers() -> Result<(), Box> { - use dashcore::hash_types::FilterHeader; - - // Create a temporary directory for testing - let temp_dir = TempDir::new()?; - let mut storage = DiskStorageManager::new(temp_dir.path().to_path_buf()).await?; - - // Create test filter headers - let filter_headers: Vec = - (0..10).map(|i| FilterHeader::from_byte_array([i as u8; 32])).collect(); - - // Store filter headers - storage.store_filter_headers(&filter_headers).await?; - - // Verify we have 10 filter headers - let filter_tip_height = storage.get_filter_tip_height().await?; - assert_eq!(filter_tip_height, Some(9)); - - // Test rollback to height 3 - // storage.rollback_to_height(3).await?; - - // TODO: Test assertions commented out because rollback_to_height is not implemented - // Verify filter tip height is now 3 - let _ = storage.get_filter_tip_height().await?; - // assert_eq!(filter_tip_after_rollback, Some(3)); - - // Verify we can only load filter headers up to height 3 - let _ = storage.load_filter_headers(0..10).await?; - // assert_eq!(filter_headers_after_rollback.len(), 4); // heights 0-3 - - // Verify filter header at height 4 is not accessible - let _ = storage.get_filter_header(4).await?; - // assert!(filter_header_at_4.is_none()); - - Ok(()) -}