1- //! Edge case tests for header validation.
1+ //! Header validation functionality.
2+
3+ use dashcore:: { block:: Header as BlockHeader , error:: Error as DashError } ;
4+ use std:: time:: Instant ;
5+
6+ use crate :: error:: { ValidationError , ValidationResult } ;
7+ use crate :: types:: ValidationMode ;
8+
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 ( ( ) ) ;
14+ }
15+
16+ if headers. is_empty ( ) {
17+ tracing:: debug!( "Skipping header validation: empty headers" ) ;
18+ return Ok ( ( ) ) ;
19+ }
20+
21+ let start = Instant :: now ( ) ;
22+
23+ let mut prev_header_hash = None ;
24+ for header in headers {
25+ // Check chain continuity if we have previous header
26+ if let Some ( prev) = prev_header_hash {
27+ if header. prev_blockhash != prev {
28+ return Err ( ValidationError :: InvalidHeaderChain (
29+ "Header does not connect to previous header" . to_string ( ) ,
30+ ) ) ;
31+ }
32+ }
33+
34+ if mode == ValidationMode :: Full {
35+ // Validate proof of work with X11 hashing
36+ let target = header. target ( ) ;
37+ if let Err ( e) = header. validate_pow ( target) {
38+ return match e {
39+ DashError :: BlockBadProofOfWork => Err ( ValidationError :: InvalidProofOfWork ) ,
40+ DashError :: BlockBadTarget => {
41+ Err ( ValidationError :: InvalidHeaderChain ( "Invalid target" . to_string ( ) ) )
42+ }
43+ _ => Err ( ValidationError :: InvalidHeaderChain ( format ! (
44+ "PoW validation error: {:?}" ,
45+ e
46+ ) ) ) ,
47+ } ;
48+ }
49+ }
50+
51+ prev_header_hash = Some ( header. block_hash ( ) ) ;
52+ }
53+
54+ tracing:: debug!(
55+ "Header chain validation passed for {} headers in mode: {:?}, duration: {:?}" ,
56+ headers. len( ) ,
57+ mode,
58+ start. elapsed( ) ,
59+ ) ;
60+ Ok ( ( ) )
61+ }
262
363#[ cfg( test) ]
464mod tests {
5- use super :: super :: validate_headers;
65+ use super :: validate_headers;
666 use crate :: error:: ValidationError ;
767 use crate :: types:: ValidationMode ;
868 use dashcore:: {
@@ -12,6 +72,23 @@ mod tests {
1272 } ;
1373 use dashcore_hashes:: Hash ;
1474
75+ /// Create a test header with given parameters
76+ fn create_test_header (
77+ prev_hash : dashcore:: BlockHash ,
78+ nonce : u32 ,
79+ bits : u32 ,
80+ time : u32 ,
81+ ) -> BlockHeader {
82+ BlockHeader {
83+ version : Version :: from_consensus ( 0x20000000 ) ,
84+ prev_blockhash : prev_hash,
85+ merkle_root : dashcore:: TxMerkleNode :: from_byte_array ( [ 0 ; 32 ] ) ,
86+ time,
87+ bits : dashcore:: CompactTarget :: from_consensus ( bits) ,
88+ nonce,
89+ }
90+ }
91+
1592 /// Create a test header with specific parameters
1693 fn create_test_header_with_params (
1794 version : u32 ,
@@ -31,6 +108,185 @@ mod tests {
31108 }
32109 }
33110
111+ // ==================== Basic Tests ====================
112+
113+ #[ test]
114+ fn test_validation_mode_none_always_passes ( ) {
115+ let header = create_test_header (
116+ dashcore:: BlockHash :: from_raw_hash ( dashcore_hashes:: hash_x11:: Hash :: from_byte_array (
117+ [ 0 ; 32 ] ,
118+ ) ) ,
119+ 0 ,
120+ 0x1e0fffff ,
121+ 1234567890 ,
122+ ) ;
123+
124+ // Should pass with no previous header
125+ assert ! ( validate_headers( & [ header] , ValidationMode :: None ) . is_ok( ) ) ;
126+
127+ // Should pass even with invalid chain continuity
128+ let prev_header = create_test_header (
129+ dashcore:: BlockHash :: from_raw_hash ( dashcore_hashes:: hash_x11:: Hash :: from_byte_array (
130+ [ 1 ; 32 ] ,
131+ ) ) ,
132+ 1 ,
133+ 0x1e0fffff ,
134+ 1234567890 ,
135+ ) ;
136+ assert ! ( validate_headers( & [ prev_header, header] , ValidationMode :: None ) . is_ok( ) ) ;
137+ }
138+
139+ #[ test]
140+ fn test_basic_validation_chain_continuity ( ) {
141+ // Create two headers that connect properly
142+ let header1 = create_test_header (
143+ dashcore:: BlockHash :: from_raw_hash ( dashcore_hashes:: hash_x11:: Hash :: from_byte_array (
144+ [ 0 ; 32 ] ,
145+ ) ) ,
146+ 1 ,
147+ 0x1e0fffff ,
148+ 1234567890 ,
149+ ) ;
150+ let header2 = create_test_header ( header1. block_hash ( ) , 2 , 0x1e0fffff , 1234567900 ) ;
151+
152+ // Should pass when headers connect
153+ assert ! ( validate_headers( & [ header1, header2] , ValidationMode :: Basic ) . is_ok( ) ) ;
154+
155+ // Should fail when headers don't connect
156+ let disconnected_header = create_test_header (
157+ dashcore:: BlockHash :: from_raw_hash ( dashcore_hashes:: hash_x11:: Hash :: from_byte_array (
158+ [ 99 ; 32 ] ,
159+ ) ) ,
160+ 3 ,
161+ 0x1e0fffff ,
162+ 1234567910 ,
163+ ) ;
164+ let result = validate_headers ( & [ header1, disconnected_header] , ValidationMode :: Basic ) ;
165+ assert ! ( matches!( result, Err ( ValidationError :: InvalidHeaderChain ( _) ) ) ) ;
166+ }
167+
168+ #[ test]
169+ fn test_basic_validation_no_pow_check ( ) {
170+ // Create header with invalid PoW (would fail full validation)
171+ let header = create_test_header (
172+ dashcore:: BlockHash :: from_raw_hash ( dashcore_hashes:: hash_x11:: Hash :: from_byte_array (
173+ [ 0 ; 32 ] ,
174+ ) ) ,
175+ 0 , // Invalid nonce that won't produce valid PoW
176+ 0x1e0fffff ,
177+ 1234567890 ,
178+ ) ;
179+
180+ // Should pass basic validation (no PoW check)
181+ assert ! ( validate_headers( & [ header] , ValidationMode :: Basic ) . is_ok( ) ) ;
182+ }
183+
184+ #[ test]
185+ fn test_full_validation_includes_pow ( ) {
186+ // Create header with invalid PoW
187+ let header = create_test_header (
188+ dashcore:: BlockHash :: from_raw_hash ( dashcore_hashes:: hash_x11:: Hash :: from_byte_array (
189+ [ 0 ; 32 ] ,
190+ ) ) ,
191+ 0 , // Invalid nonce
192+ 0x1d00ffff , // Difficulty that requires real PoW
193+ 1234567890 ,
194+ ) ;
195+
196+ // Should fail full validation due to invalid PoW
197+ let result = validate_headers ( & [ header] , ValidationMode :: Full ) ;
198+ assert ! ( matches!( result, Err ( ValidationError :: InvalidProofOfWork ) ) ) ;
199+ }
200+
201+ #[ test]
202+ fn test_validate_headers_empty ( ) {
203+ for mode in [ ValidationMode :: None , ValidationMode :: Basic , ValidationMode :: Full ] {
204+ let headers: Vec < BlockHeader > = vec ! [ ] ;
205+ // Empty chain should pass
206+ assert ! ( validate_headers( & headers, mode) . is_ok( ) ) ;
207+ }
208+ }
209+
210+ #[ test]
211+ fn test_validate_headers_basic_single_header ( ) {
212+ let header = create_test_header (
213+ dashcore:: BlockHash :: from_raw_hash ( dashcore_hashes:: hash_x11:: Hash :: from_byte_array (
214+ [ 0 ; 32 ] ,
215+ ) ) ,
216+ 1 ,
217+ 0x1e0fffff ,
218+ 1234567890 ,
219+ ) ;
220+
221+ // Single header should pass (no chain validation needed)
222+ assert ! ( validate_headers( & [ header] , ValidationMode :: Basic ) . is_ok( ) ) ;
223+ }
224+
225+ #[ test]
226+ fn test_validate_headers_basic_valid_chain ( ) {
227+ // Create a valid chain of headers
228+ let mut headers = vec ! [ ] ;
229+ let mut prev_hash = dashcore:: BlockHash :: from_raw_hash (
230+ dashcore_hashes:: hash_x11:: Hash :: from_byte_array ( [ 0 ; 32 ] ) ,
231+ ) ;
232+
233+ for i in 0 ..5 {
234+ let header = create_test_header ( prev_hash, i, 0x1e0fffff , 1234567890 + i * 600 ) ;
235+ prev_hash = header. block_hash ( ) ;
236+ headers. push ( header) ;
237+ }
238+
239+ // Valid chain should pass
240+ assert ! ( validate_headers( & headers, ValidationMode :: Basic ) . is_ok( ) ) ;
241+ }
242+
243+ #[ test]
244+ fn test_validate_headers_basic_broken_chain ( ) {
245+ // Create a chain with a break in the middle
246+ let header1 = create_test_header (
247+ dashcore:: BlockHash :: from_raw_hash ( dashcore_hashes:: hash_x11:: Hash :: from_byte_array (
248+ [ 0 ; 32 ] ,
249+ ) ) ,
250+ 1 ,
251+ 0x1e0fffff ,
252+ 1234567890 ,
253+ ) ;
254+ let header2 = create_test_header ( header1. block_hash ( ) , 2 , 0x1e0fffff , 1234567900 ) ;
255+ let header3 = create_test_header (
256+ dashcore:: BlockHash :: from_raw_hash ( dashcore_hashes:: hash_x11:: Hash :: from_byte_array (
257+ [ 99 ; 32 ] ,
258+ ) ) , // Broken link
259+ 3 ,
260+ 0x1e0fffff ,
261+ 1234567910 ,
262+ ) ;
263+
264+ let headers = vec ! [ header1, header2, header3] ;
265+
266+ // Should fail due to broken chain
267+ let result = validate_headers ( & headers, ValidationMode :: Basic ) ;
268+ assert ! ( matches!( result, Err ( ValidationError :: InvalidHeaderChain ( _) ) ) ) ;
269+ }
270+
271+ #[ test]
272+ fn test_validate_headers_full_with_pow ( ) {
273+ // Create headers with invalid PoW
274+ let header1 = create_test_header (
275+ dashcore:: BlockHash :: from_raw_hash ( dashcore_hashes:: hash_x11:: Hash :: from_byte_array (
276+ [ 0 ; 32 ] ,
277+ ) ) ,
278+ 0 , // Invalid nonce
279+ 0x1d00ffff , // Difficulty that requires real PoW
280+ 1234567890 ,
281+ ) ;
282+
283+ // Should fail when PoW validation is enabled
284+ let result = validate_headers ( & [ header1] , ValidationMode :: Full ) ;
285+ assert ! ( matches!( result, Err ( ValidationError :: InvalidProofOfWork ) ) ) ;
286+ }
287+
288+ // ==================== Edge Case Tests ====================
289+
34290 #[ test]
35291 fn test_genesis_block_validation ( ) {
36292 for network in [ Network :: Dash , Network :: Testnet , Network :: Regtest ] {
0 commit comments