Skip to content

Commit 5d702ed

Browse files
authored
refactor: Cleanup SPV validation (#237)
- Some simplifications and name changes - Some unused code - Add header validation timing logs - Dropping `ValidationManager` as its not really used and not needed - Dropping the `HeaderValidator` making `validate_headers` a standalone function now located in `sync::headers::validation` - Making the `ValidationMode::Full` mode checking PoW by default, not based on yet another parameter
1 parent 1284cc9 commit 5d702ed

File tree

8 files changed

+81
-860
lines changed

8 files changed

+81
-860
lines changed

dash-spv/src/client/core.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ use crate::storage::StorageManager;
2222
use crate::sync::filters::FilterNotificationSender;
2323
use crate::sync::SyncManager;
2424
use crate::types::{ChainState, DetailedSyncProgress, MempoolState, SpvEvent, SpvStats};
25-
use crate::validation::ValidationManager;
2625
use key_wallet_manager::wallet_interface::WalletInterface;
2726

2827
use super::{BlockProcessingTask, ClientConfig, StatusDisplay};
@@ -133,7 +132,6 @@ pub struct DashSpvClient<W: WalletInterface, N: NetworkManager, S: StorageManage
133132
///
134133
/// The current design prioritizes simplicity and correctness over concurrent access.
135134
pub(super) sync_manager: SyncManager<S, N, W>,
136-
pub(super) validation: ValidationManager,
137135
pub(super) chainlock_manager: Arc<ChainLockManager>,
138136
pub(super) running: Arc<RwLock<bool>>,
139137
#[cfg(feature = "terminal-ui")]

dash-spv/src/client/lifecycle.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use crate::network::NetworkManager;
1919
use crate::storage::StorageManager;
2020
use crate::sync::SyncManager;
2121
use crate::types::{ChainState, MempoolState, SpvStats};
22-
use crate::validation::ValidationManager;
2322
use dashcore::network::constants::NetworkExt;
2423
use dashcore_hashes::Hash;
2524
use key_wallet_manager::wallet_interface::WalletInterface;
@@ -61,9 +60,6 @@ impl<
6160
)
6261
.map_err(SpvError::Sync)?;
6362

64-
// Create validation manager
65-
let validation = ValidationManager::new(config.validation_mode);
66-
6763
// Create ChainLock manager
6864
let chainlock_manager = Arc::new(ChainLockManager::new(true));
6965

@@ -87,7 +83,6 @@ impl<
8783
storage,
8884
wallet,
8985
sync_manager,
90-
validation,
9186
chainlock_manager,
9287
running: Arc::new(RwLock::new(false)),
9388
#[cfg(feature = "terminal-ui")]

dash-spv/src/client/sync_coordinator.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use crate::network::constants::MESSAGE_RECEIVE_TIMEOUT;
1818
use crate::network::NetworkManager;
1919
use crate::storage::StorageManager;
2020
use crate::types::{DetailedSyncProgress, SyncProgress};
21+
use crate::validation::validate_headers;
22+
use crate::ValidationMode;
2123
use key_wallet_manager::wallet_interface::WalletInterface;
2224
use std::time::{Duration, Instant, SystemTime};
2325
use tokio::sync::mpsc::UnboundedReceiver;
@@ -946,7 +948,9 @@ impl<
946948
// Validate headers before adding to chain state
947949
{
948950
// Validate the batch of headers
949-
if let Err(e) = self.validation.validate_header_chain(&headers, false) {
951+
// Use basic validation only, the headers anyway are already validated since they
952+
// come from storage.
953+
if let Err(e) = validate_headers(&headers, ValidationMode::Basic) {
950954
tracing::error!(
951955
"Header validation failed for range {}..{}: {:?}",
952956
current_height,

dash-spv/src/validation/headers.rs

Lines changed: 32 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,196 +1,58 @@
11
//! Header validation functionality.
22
3-
use dashcore::{
4-
block::Header as BlockHeader, error::Error as DashError, network::constants::NetworkExt,
5-
Network,
6-
};
3+
use dashcore::{block::Header as BlockHeader, error::Error as DashError};
4+
use std::time::Instant;
75

86
use crate::error::{ValidationError, ValidationResult};
97
use crate::types::ValidationMode;
108

11-
/// Validates block headers.
12-
pub struct HeaderValidator {
13-
mode: ValidationMode,
14-
network: Network,
15-
}
16-
17-
impl HeaderValidator {
18-
/// Create a new header validator.
19-
pub fn new(mode: ValidationMode) -> Self {
20-
Self {
21-
mode,
22-
network: Network::Dash, // Default to mainnet
23-
}
9+
/// Validate a chain of headers considering the validation mode.
10+
pub fn validate_headers(headers: &[BlockHeader], mode: ValidationMode) -> ValidationResult<()> {
11+
if mode == ValidationMode::None {
12+
tracing::debug!("Skipping header validation: disabled");
13+
return Ok(());
2414
}
2515

26-
/// Set validation mode.
27-
pub fn set_mode(&mut self, mode: ValidationMode) {
28-
self.mode = mode;
29-
}
30-
31-
/// Set network.
32-
pub fn set_network(&mut self, network: Network) {
33-
self.network = network;
34-
}
16+
let start = Instant::now();
3517

36-
/// Validate a single header.
37-
pub fn validate(
38-
&self,
39-
header: &BlockHeader,
40-
prev_header: Option<&BlockHeader>,
41-
) -> ValidationResult<()> {
42-
match self.mode {
43-
ValidationMode::None => Ok(()),
44-
ValidationMode::Basic => self.validate_basic(header, prev_header),
45-
ValidationMode::Full => self.validate_full(header, prev_header),
46-
}
47-
}
48-
49-
/// Basic header validation (structure and chain continuity).
50-
fn validate_basic(
51-
&self,
52-
header: &BlockHeader,
53-
prev_header: Option<&BlockHeader>,
54-
) -> ValidationResult<()> {
18+
let mut prev_block_hash = None;
19+
for header in headers {
5520
// Check chain continuity if we have previous header
56-
if let Some(prev) = prev_header {
57-
if header.prev_blockhash != prev.block_hash() {
21+
if let Some(prev) = prev_block_hash {
22+
if header.prev_blockhash != prev {
5823
return Err(ValidationError::InvalidHeaderChain(
5924
"Header does not connect to previous header".to_string(),
6025
));
6126
}
6227
}
6328

64-
Ok(())
65-
}
66-
67-
/// Full header validation (includes PoW verification).
68-
fn validate_full(
69-
&self,
70-
header: &BlockHeader,
71-
prev_header: Option<&BlockHeader>,
72-
) -> ValidationResult<()> {
73-
// First do basic validation
74-
self.validate_basic(header, prev_header)?;
75-
76-
// Validate proof of work with X11 hashing (now enabled with core-block-hash-use-x11 feature)
77-
let target = header.target();
78-
if let Err(e) = header.validate_pow(target) {
79-
match e {
80-
DashError::BlockBadProofOfWork => {
81-
return Err(ValidationError::InvalidProofOfWork);
82-
}
83-
DashError::BlockBadTarget => {
84-
return Err(ValidationError::InvalidHeaderChain("Invalid target".to_string()));
85-
}
86-
_ => {
87-
return Err(ValidationError::InvalidHeaderChain(format!(
29+
if mode == ValidationMode::Full {
30+
// Validate proof of work with X11 hashing
31+
let target = header.target();
32+
if let Err(e) = header.validate_pow(target) {
33+
return match e {
34+
DashError::BlockBadProofOfWork => Err(ValidationError::InvalidProofOfWork),
35+
DashError::BlockBadTarget => {
36+
Err(ValidationError::InvalidHeaderChain("Invalid target".to_string()))
37+
}
38+
_ => Err(ValidationError::InvalidHeaderChain(format!(
8839
"PoW validation error: {:?}",
8940
e
90-
)));
91-
}
92-
}
93-
}
94-
95-
Ok(())
96-
}
97-
98-
/// Validate a chain of headers with basic validation.
99-
pub fn validate_chain_basic(&self, headers: &[BlockHeader]) -> ValidationResult<()> {
100-
// Respect ValidationMode::None
101-
if self.mode == ValidationMode::None {
102-
return Ok(());
103-
}
104-
105-
if headers.is_empty() {
106-
return Ok(());
107-
}
108-
109-
// Validate chain continuity
110-
for i in 1..headers.len() {
111-
let header = &headers[i];
112-
let prev_header = &headers[i - 1];
113-
114-
self.validate_basic(header, Some(prev_header))?;
115-
}
116-
117-
tracing::debug!("Basic header chain validation passed for {} headers", headers.len());
118-
Ok(())
119-
}
120-
121-
/// Validate a chain of headers with full validation.
122-
pub fn validate_chain_full(
123-
&self,
124-
headers: &[BlockHeader],
125-
validate_pow: bool,
126-
) -> ValidationResult<()> {
127-
// Respect ValidationMode::None
128-
if self.mode == ValidationMode::None {
129-
return Ok(());
130-
}
131-
132-
if headers.is_empty() {
133-
return Ok(());
134-
}
135-
136-
// For the first header, we might need to check it connects to genesis or our existing chain
137-
// For now, we'll just validate internal chain continuity
138-
139-
// Validate each header in the chain
140-
for i in 0..headers.len() {
141-
let header = &headers[i];
142-
let prev_header = if i > 0 {
143-
Some(&headers[i - 1])
144-
} else {
145-
None
146-
};
147-
148-
if validate_pow {
149-
self.validate_full(header, prev_header)?;
150-
} else {
151-
self.validate_basic(header, prev_header)?;
41+
))),
42+
};
15243
}
15344
}
15445

155-
tracing::debug!("Full header chain validation passed for {} headers", headers.len());
156-
Ok(())
46+
prev_block_hash = Some(header.block_hash());
15747
}
15848

159-
/// Validate headers connect to genesis block.
160-
pub fn validate_connects_to_genesis(&self, headers: &[BlockHeader]) -> ValidationResult<()> {
161-
if headers.is_empty() {
162-
return Ok(());
163-
}
164-
165-
let genesis_hash = self.network.known_genesis_block_hash().ok_or_else(|| {
166-
ValidationError::Consensus("No known genesis hash for network".to_string())
167-
})?;
168-
169-
if headers[0].prev_blockhash != genesis_hash {
170-
return Err(ValidationError::InvalidHeaderChain(
171-
"First header doesn't connect to genesis".to_string(),
172-
));
173-
}
174-
175-
Ok(())
176-
}
177-
178-
/// Validate difficulty adjustment (simplified for SPV).
179-
pub fn validate_difficulty_adjustment(
180-
&self,
181-
header: &BlockHeader,
182-
prev_header: &BlockHeader,
183-
) -> ValidationResult<()> {
184-
// For SPV client, we trust that the network has validated difficulty properly
185-
// We only check basic constraints
186-
187-
// For SPV we trust the network for difficulty validation
188-
// TODO: Implement proper difficulty validation if needed
189-
let _prev_target = prev_header.target();
190-
let _current_target = header.target();
191-
192-
Ok(())
193-
}
49+
tracing::debug!(
50+
"Header chain validation passed for {} headers in mode: {:?}, duration: {:?}",
51+
headers.len(),
52+
mode,
53+
start.elapsed(),
54+
);
55+
Ok(())
19456
}
19557

19658
#[cfg(test)]

0 commit comments

Comments
 (0)