1
- use std:: { collections:: HashMap , io, path:: Path } ;
1
+ use std:: { collections:: HashMap , io, path:: Path , sync :: Arc } ;
2
2
3
3
use alloy_consensus:: BlockHeader ;
4
4
use alloy_eips:: BlockHashOrNumber ;
5
5
use alloy_primitives:: { BlockHash , BlockNumber , Sealable , B256 } ;
6
6
use futures:: Future ;
7
7
use itertools:: Either ;
8
+ use reth_consensus:: { ConsensusError , HeaderValidator } ;
8
9
use reth_network_p2p:: {
9
10
bodies:: client:: { BodiesClient , BodiesFut } ,
10
11
download:: DownloadClient ,
@@ -56,6 +57,10 @@ pub struct FileClient<B: Block = reth_primitives::Block> {
56
57
/// An error that can occur when constructing and using a [`FileClient`].
57
58
#[ derive( Debug , Error ) ]
58
59
pub enum FileClientError {
60
+ /// An error occurred when validating a header from file.
61
+ #[ error( transparent) ]
62
+ Consensus ( #[ from] ConsensusError ) ,
63
+
59
64
/// An error occurred when opening or reading the file.
60
65
#[ error( transparent) ]
61
66
Io ( #[ from] std:: io:: Error ) ,
@@ -77,21 +82,30 @@ impl From<&'static str> for FileClientError {
77
82
78
83
impl < B : FullBlock > FileClient < B > {
79
84
/// Create a new file client from a file path.
80
- pub async fn new < P : AsRef < Path > > ( path : P ) -> Result < Self , FileClientError > {
85
+ pub async fn new < P : AsRef < Path > > (
86
+ path : P ,
87
+ consensus : Arc < dyn HeaderValidator < B :: Header > > ,
88
+ ) -> Result < Self , FileClientError > {
81
89
let file = File :: open ( path) . await ?;
82
- Self :: from_file ( file) . await
90
+ Self :: from_file ( file, consensus ) . await
83
91
}
84
92
85
93
/// Initialize the [`FileClient`] with a file directly.
86
- pub ( crate ) async fn from_file ( mut file : File ) -> Result < Self , FileClientError > {
94
+ pub ( crate ) async fn from_file (
95
+ mut file : File ,
96
+ consensus : Arc < dyn HeaderValidator < B :: Header > > ,
97
+ ) -> Result < Self , FileClientError > {
87
98
// get file len from metadata before reading
88
99
let metadata = file. metadata ( ) . await ?;
89
100
let file_len = metadata. len ( ) ;
90
101
91
102
let mut reader = vec ! [ ] ;
92
103
file. read_to_end ( & mut reader) . await ?;
93
104
94
- Ok ( Self :: from_reader ( & reader[ ..] , file_len) . await ?. file_client )
105
+ Ok ( FileClientBuilder { consensus, parent_header : None }
106
+ . build ( & reader[ ..] , file_len)
107
+ . await ?
108
+ . file_client )
95
109
}
96
110
97
111
/// Get the tip hash of the chain.
@@ -183,14 +197,23 @@ impl<B: FullBlock> FileClient<B> {
183
197
}
184
198
}
185
199
186
- impl < B : FullBlock > FromReader for FileClient < B > {
200
+ struct FileClientBuilder < B : Block = reth_primitives:: Block > {
201
+ pub consensus : Arc < dyn HeaderValidator < B :: Header > > ,
202
+ pub parent_header : Option < SealedHeader < B :: Header > > ,
203
+ }
204
+
205
+ impl < B : FullBlock < Header : reth_primitives_traits:: BlockHeader > > FromReader
206
+ for FileClientBuilder < B >
207
+ {
187
208
type Error = FileClientError ;
209
+ type Output = FileClient < B > ;
188
210
189
211
/// Initialize the [`FileClient`] from bytes that have been read from file.
190
- fn from_reader < R > (
212
+ fn build < R > (
213
+ & self ,
191
214
reader : R ,
192
215
num_bytes : u64 ,
193
- ) -> impl Future < Output = Result < DecodedFileChunk < Self > , Self :: Error > >
216
+ ) -> impl Future < Output = Result < DecodedFileChunk < Self :: Output > , Self :: Error > >
194
217
where
195
218
R : AsyncReadExt + Unpin ,
196
219
{
@@ -213,6 +236,8 @@ impl<B: FullBlock> FromReader for FileClient<B> {
213
236
let mut log_interval = 0 ;
214
237
let mut log_interval_start_block = 0 ;
215
238
239
+ let mut parent_header = self . parent_header . clone ( ) ;
240
+
216
241
async move {
217
242
while let Some ( block_res) = stream. next ( ) . await {
218
243
let block = match block_res {
@@ -231,6 +256,14 @@ impl<B: FullBlock> FromReader for FileClient<B> {
231
256
let block_number = block. header ( ) . number ( ) ;
232
257
let block_hash = block. header ( ) . hash_slow ( ) ;
233
258
259
+ // Validate incoming header
260
+ let sealed = SealedHeader :: new ( block. header ( ) . clone ( ) , block_hash) ;
261
+ self . consensus . validate_header ( & sealed) ?;
262
+ if let Some ( parent) = & parent_header {
263
+ self . consensus . validate_header_against_parent ( & sealed, parent) ?;
264
+ parent_header = Some ( sealed) ;
265
+ }
266
+
234
267
// add to the internal maps
235
268
headers. insert ( block. header ( ) . number ( ) , block. header ( ) . clone ( ) ) ;
236
269
hash_to_number. insert ( block_hash, block. header ( ) . number ( ) ) ;
@@ -255,7 +288,7 @@ impl<B: FullBlock> FromReader for FileClient<B> {
255
288
trace ! ( target: "downloaders::file" , blocks = headers. len( ) , "Initialized file client" ) ;
256
289
257
290
Ok ( DecodedFileChunk {
258
- file_client : Self { headers, hash_to_number, bodies } ,
291
+ file_client : FileClient { headers, hash_to_number, bodies } ,
259
292
remaining_bytes,
260
293
highest_block : None ,
261
294
} )
@@ -452,15 +485,18 @@ impl ChunkedFileReader {
452
485
}
453
486
454
487
/// Read next chunk from file. Returns [`FileClient`] containing decoded chunk.
455
- pub async fn next_chunk < T > ( & mut self ) -> Result < Option < T > , T :: Error >
456
- where
457
- T : FromReader ,
458
- {
488
+ pub async fn next_chunk < B : FullBlock > (
489
+ & mut self ,
490
+ consensus : Arc < dyn HeaderValidator < B :: Header > > ,
491
+ parent_header : Option < SealedHeader < B :: Header > > ,
492
+ ) -> Result < Option < FileClient < B > > , FileClientError > {
459
493
let Some ( next_chunk_byte_len) = self . read_next_chunk ( ) . await ? else { return Ok ( None ) } ;
460
494
461
495
// make new file client from chunk
462
496
let DecodedFileChunk { file_client, remaining_bytes, .. } =
463
- T :: from_reader ( & self . chunk [ ..] , next_chunk_byte_len) . await ?;
497
+ FileClientBuilder { consensus, parent_header }
498
+ . build ( & self . chunk [ ..] , next_chunk_byte_len)
499
+ . await ?;
464
500
465
501
// save left over bytes
466
502
self . chunk = remaining_bytes;
@@ -494,14 +530,18 @@ pub trait FromReader {
494
530
/// Error returned by file client type.
495
531
type Error : From < io:: Error > ;
496
532
533
+ /// Output returned by file client type.
534
+ type Output ;
535
+
497
536
/// Returns a file client
498
- fn from_reader < B > (
499
- reader : B ,
537
+ fn build < R > (
538
+ & self ,
539
+ reader : R ,
500
540
num_bytes : u64 ,
501
- ) -> impl Future < Output = Result < DecodedFileChunk < Self > , Self :: Error > >
541
+ ) -> impl Future < Output = Result < DecodedFileChunk < Self :: Output > , Self :: Error > >
502
542
where
503
543
Self : Sized ,
504
- B : AsyncReadExt + Unpin ;
544
+ R : AsyncReadExt + Unpin ;
505
545
}
506
546
507
547
/// Output from decoding a file chunk with [`FromReader::from_reader`].
@@ -530,11 +570,12 @@ mod tests {
530
570
use assert_matches:: assert_matches;
531
571
use futures_util:: stream:: StreamExt ;
532
572
use rand:: Rng ;
533
- use reth_consensus:: test_utils:: TestConsensus ;
573
+ use reth_consensus:: { noop :: NoopConsensus , test_utils:: TestConsensus } ;
534
574
use reth_network_p2p:: {
535
575
bodies:: downloader:: BodyDownloader ,
536
576
headers:: downloader:: { HeaderDownloader , SyncTarget } ,
537
577
} ;
578
+ use reth_primitives:: Block ;
538
579
use reth_provider:: test_utils:: create_test_provider_factory;
539
580
use std:: sync:: Arc ;
540
581
@@ -549,8 +590,12 @@ mod tests {
549
590
// create an empty file
550
591
let file = tempfile:: tempfile ( ) . unwrap ( ) ;
551
592
552
- let client: Arc < FileClient > =
553
- Arc :: new ( FileClient :: from_file ( file. into ( ) ) . await . unwrap ( ) . with_bodies ( bodies. clone ( ) ) ) ;
593
+ let client: Arc < FileClient > = Arc :: new (
594
+ FileClient :: from_file ( file. into ( ) , NoopConsensus :: arc ( ) )
595
+ . await
596
+ . unwrap ( )
597
+ . with_bodies ( bodies. clone ( ) ) ,
598
+ ) ;
554
599
let mut downloader = BodiesDownloaderBuilder :: default ( )
555
600
. build :: < reth_primitives:: Block , _ , _ > (
556
601
client. clone ( ) ,
@@ -576,12 +621,14 @@ mod tests {
576
621
577
622
let file = tempfile:: tempfile ( ) . unwrap ( ) ;
578
623
let client: Arc < FileClient > = Arc :: new (
579
- FileClient :: from_file ( file. into ( ) ) . await . unwrap ( ) . with_headers ( HashMap :: from ( [
580
- ( 0u64 , p0. clone_header ( ) ) ,
581
- ( 1 , p1. clone_header ( ) ) ,
582
- ( 2 , p2. clone_header ( ) ) ,
583
- ( 3 , p3. clone_header ( ) ) ,
584
- ] ) ) ,
624
+ FileClient :: from_file ( file. into ( ) , NoopConsensus :: arc ( ) ) . await . unwrap ( ) . with_headers (
625
+ HashMap :: from ( [
626
+ ( 0u64 , p0. clone_header ( ) ) ,
627
+ ( 1 , p1. clone_header ( ) ) ,
628
+ ( 2 , p2. clone_header ( ) ) ,
629
+ ( 3 , p3. clone_header ( ) ) ,
630
+ ] ) ,
631
+ ) ,
585
632
) ;
586
633
587
634
let mut downloader = ReverseHeadersDownloaderBuilder :: default ( )
@@ -604,7 +651,8 @@ mod tests {
604
651
// Generate some random blocks
605
652
let ( file, headers, _) = generate_bodies_file ( 0 ..=19 ) . await ;
606
653
// now try to read them back
607
- let client: Arc < FileClient > = Arc :: new ( FileClient :: from_file ( file) . await . unwrap ( ) ) ;
654
+ let client: Arc < FileClient > =
655
+ Arc :: new ( FileClient :: from_file ( file, NoopConsensus :: arc ( ) ) . await . unwrap ( ) ) ;
608
656
609
657
// construct headers downloader and use first header
610
658
let mut header_downloader = ReverseHeadersDownloaderBuilder :: default ( )
@@ -629,7 +677,8 @@ mod tests {
629
677
let ( file, headers, mut bodies) = generate_bodies_file ( 0 ..=19 ) . await ;
630
678
631
679
// now try to read them back
632
- let client: Arc < FileClient > = Arc :: new ( FileClient :: from_file ( file) . await . unwrap ( ) ) ;
680
+ let client: Arc < FileClient > =
681
+ Arc :: new ( FileClient :: from_file ( file, NoopConsensus :: arc ( ) ) . await . unwrap ( ) ) ;
633
682
634
683
// insert headers in db for the bodies downloader
635
684
insert_headers ( factory. db_ref ( ) . db ( ) , & headers) ;
@@ -668,7 +717,9 @@ mod tests {
668
717
let mut local_header = headers. first ( ) . unwrap ( ) . clone ( ) ;
669
718
670
719
// test
671
- while let Some ( client) = reader. next_chunk :: < FileClient > ( ) . await . unwrap ( ) {
720
+ while let Some ( client) =
721
+ reader. next_chunk :: < Block > ( NoopConsensus :: arc ( ) , None ) . await . unwrap ( )
722
+ {
672
723
let sync_target = client. tip_header ( ) . unwrap ( ) ;
673
724
674
725
let sync_target_hash = sync_target. hash ( ) ;
0 commit comments