1- //! Header validation functionality.
2-
31use rayon:: prelude:: * ;
42use std:: time:: Instant ;
53
64use crate :: error:: { ValidationError , ValidationResult } ;
75use crate :: types:: HashedBlockHeader ;
6+ use crate :: validation:: Validator ;
87
9- /// Validate a chain of headers.
10- pub fn validate_headers ( hashed_headers : & [ HashedBlockHeader ] ) -> ValidationResult < ( ) > {
11- let start = Instant :: now ( ) ;
12-
13- // Check PoW of i and continuity of i-1 to i in parallel
14- hashed_headers. par_iter ( ) . enumerate ( ) . try_for_each ( |( i, header) | {
15- // For the first header, skip chain continuity check since we don't have i-1 here
16- if i > 0 && header. header ( ) . prev_blockhash != * hashed_headers[ i - 1 ] . hash ( ) {
17- return Err ( ValidationError :: InvalidHeaderChain ( format ! (
18- "Header {:?} does not connect to {:?}" ,
19- hashed_headers[ i - 1 ] ,
20- header
21- ) ) ) ;
22- }
23- // Check if PoW target is met
24- if !header. header ( ) . target ( ) . is_met_by ( * header. hash ( ) ) {
25- return Err ( ValidationError :: InvalidProofOfWork ) ;
26- }
27- Ok ( ( ) )
28- } ) ?;
8+ #[ derive( Default ) ]
9+ pub struct BlockHeaderValidator { }
2910
30- tracing:: trace!(
31- "Header chain validation passed for {} headers, duration: {:?}" ,
32- hashed_headers. len( ) ,
33- start. elapsed( ) ,
34- ) ;
11+ impl BlockHeaderValidator {
12+ pub fn new ( ) -> Self {
13+ Self { }
14+ }
15+ }
16+
17+ impl Validator < & [ HashedBlockHeader ] > for BlockHeaderValidator {
18+ fn validate ( & self , hashed_headers : & [ HashedBlockHeader ] ) -> ValidationResult < ( ) > {
19+ let start = Instant :: now ( ) ;
20+
21+ // Check PoW of i and continuity of i-1 to i in parallel
22+ hashed_headers. par_iter ( ) . enumerate ( ) . try_for_each ( |( i, header) | {
23+ // For the first header, skip chain continuity check since we don't have i-1 here
24+ if i > 0 && header. header ( ) . prev_blockhash != * hashed_headers[ i - 1 ] . hash ( ) {
25+ return Err ( ValidationError :: InvalidHeaderChain ( format ! (
26+ "Header {:?} does not connect to {:?}" ,
27+ hashed_headers[ i - 1 ] ,
28+ header
29+ ) ) ) ;
30+ }
31+ // Check if PoW target is met
32+ if !header. header ( ) . target ( ) . is_met_by ( * header. hash ( ) ) {
33+ return Err ( ValidationError :: InvalidProofOfWork ) ;
34+ }
35+ Ok ( ( ) )
36+ } ) ?;
37+
38+ tracing:: trace!(
39+ "Header chain validation passed for {} headers, duration: {:?}" ,
40+ hashed_headers. len( ) ,
41+ start. elapsed( ) ,
42+ ) ;
3543
36- Ok ( ( ) )
44+ Ok ( ( ) )
45+ }
3746}
3847
3948#[ cfg( test) ]
4049mod tests {
41- use super :: validate_headers;
42- use crate :: error:: ValidationError ;
43- use crate :: types:: HashedBlockHeader ;
4450 use dashcore:: {
45- block:: { Header as BlockHeader , Version } ,
46- blockdata:: constants:: genesis_block,
47- CompactTarget , Network ,
51+ block:: Version , constants:: genesis_block, CompactTarget , Header as BlockHeader , Network ,
4852 } ;
4953 use dashcore_hashes:: Hash ;
5054
55+ use super :: * ;
56+
5157 // Very easy target to pass PoW checks for continuity tests
5258 const MAX_TARGET : u32 = 0x2100ffff ;
5359
@@ -64,17 +70,23 @@ mod tests {
6470
6571 #[ test]
6672 fn test_empty_headers ( ) {
67- assert ! ( validate_headers( & [ ] ) . is_ok( ) ) ;
73+ let validator = BlockHeaderValidator :: new ( ) ;
74+
75+ assert ! ( validator. validate( & [ ] ) . is_ok( ) ) ;
6876 }
6977
7078 #[ test]
7179 fn test_single_header ( ) {
80+ let validator = BlockHeaderValidator :: new ( ) ;
81+
7282 let header = create_test_header ( dashcore:: BlockHash :: all_zeros ( ) , 0 ) ;
73- assert ! ( validate_headers ( & [ header] ) . is_ok( ) ) ;
83+ assert ! ( validator . validate ( & [ header] ) . is_ok( ) ) ;
7484 }
7585
7686 #[ test]
7787 fn test_valid_chain ( ) {
88+ let validator = BlockHeaderValidator :: new ( ) ;
89+
7890 let mut headers = vec ! [ ] ;
7991 let mut prev_hash = dashcore:: BlockHash :: all_zeros ( ) ;
8092
@@ -84,22 +96,26 @@ mod tests {
8496 headers. push ( header) ;
8597 }
8698
87- assert ! ( validate_headers ( & headers) . is_ok( ) ) ;
99+ assert ! ( validator . validate ( & headers) . is_ok( ) ) ;
88100 }
89101
90102 #[ test]
91103 fn test_broken_chain ( ) {
104+ let validator = BlockHeaderValidator :: new ( ) ;
105+
92106 let header1 = create_test_header ( dashcore:: BlockHash :: all_zeros ( ) , 0 ) ;
93107 let header2 = create_test_header ( * header1. hash ( ) , 1 ) ;
94108 // header3 doesn't connect to header2
95109 let header3 = create_test_header ( dashcore:: BlockHash :: all_zeros ( ) , 2 ) ;
96110
97- let result = validate_headers ( & [ header1, header2, header3] ) ;
111+ let result = validator . validate ( & [ header1, header2, header3] ) ;
98112 assert ! ( matches!( result, Err ( ValidationError :: InvalidHeaderChain ( _) ) ) ) ;
99113 }
100114
101115 #[ test]
102116 fn test_invalid_pow ( ) {
117+ let validator = BlockHeaderValidator :: new ( ) ;
118+
103119 let header = HashedBlockHeader :: from ( BlockHeader {
104120 version : Version :: from_consensus ( 1 ) ,
105121 prev_blockhash : dashcore:: BlockHash :: all_zeros ( ) ,
@@ -109,16 +125,18 @@ mod tests {
109125 nonce : 0 ,
110126 } ) ;
111127
112- let result = validate_headers ( & [ header] ) ;
128+ let result = validator . validate ( & [ header] ) ;
113129 assert ! ( matches!( result, Err ( ValidationError :: InvalidProofOfWork ) ) ) ;
114130 }
115131
116132 #[ test]
117133 fn test_genesis_blocks ( ) {
134+ let validator = BlockHeaderValidator :: new ( ) ;
135+
118136 for network in [ Network :: Dash , Network :: Testnet , Network :: Regtest ] {
119137 let genesis = HashedBlockHeader :: from ( genesis_block ( network) . header ) ;
120138 assert ! (
121- validate_headers ( & [ genesis] ) . is_ok( ) ,
139+ validator . validate ( & [ genesis] ) . is_ok( ) ,
122140 "Genesis block for {:?} should validate" ,
123141 network
124142 ) ;
@@ -127,6 +145,8 @@ mod tests {
127145
128146 #[ test]
129147 fn test_invalid_pow_mid_chain ( ) {
148+ let validator = BlockHeaderValidator :: new ( ) ;
149+
130150 let header1 = create_test_header ( dashcore:: BlockHash :: all_zeros ( ) , 0 ) ;
131151 let header2 = create_test_header ( * header1. hash ( ) , 1 ) ;
132152
@@ -142,7 +162,7 @@ mod tests {
142162
143163 let header4 = create_test_header ( * header3. hash ( ) , 3 ) ;
144164
145- let result = validate_headers ( & [ header1, header2, header3, header4] ) ;
165+ let result = validator . validate ( & [ header1, header2, header3, header4] ) ;
146166 assert ! ( matches!( result, Err ( ValidationError :: InvalidProofOfWork ) ) ) ;
147167 }
148168}
0 commit comments