11use crate :: NodeClient ;
22use async_trait:: async_trait;
3- use backoff:: future:: retry;
4- use backoff:: ExponentialBackoff ;
5- use futures:: stream:: FuturesUnordered ;
6- use futures:: StreamExt ;
73use lru:: LruCache ;
84use parking_lot:: Mutex ;
9- use std:: error:: Error ;
10- use std:: future:: Future ;
11- use std:: pin:: Pin ;
12- use std:: sync:: atomic:: { AtomicBool , Ordering } ;
13- use std:: time:: Duration ;
145use subspace_archiving:: archiver:: is_piece_valid;
156use subspace_core_primitives:: crypto:: kzg:: Kzg ;
167use subspace_core_primitives:: {
17- Piece , PieceIndex , PieceIndexHash , RecordsRoot , SegmentIndex , PIECES_IN_SEGMENT , RECORD_SIZE ,
8+ Piece , PieceIndex , RecordsRoot , SegmentIndex , PIECES_IN_SEGMENT , RECORD_SIZE ,
189} ;
19- use subspace_farmer_components:: plotting:: PieceReceiver ;
20- use subspace_networking:: libp2p:: multihash:: Multihash ;
2110use subspace_networking:: libp2p:: PeerId ;
22- use subspace_networking:: utils:: multihash:: MultihashCode ;
23- use subspace_networking:: { Node , PieceByHashRequest , PieceByHashResponse , PieceKey , ToMultihash } ;
24- use tokio:: time:: { sleep, timeout} ;
25- use tracing:: { debug, error, trace, warn} ;
11+ use subspace_networking:: { Node , PieceValidator } ;
12+ use tracing:: error;
2613
27- /// Defines initial duration between get_piece calls.
28- const GET_PIECE_INITIAL_INTERVAL : Duration = Duration :: from_secs ( 1 ) ;
29- /// Defines max duration between get_piece calls.
30- const GET_PIECE_MAX_INTERVAL : Duration = Duration :: from_secs ( 5 ) ;
31- /// Delay for getting piece from cache before resorting to archival storage
32- const GET_PIECE_ARCHIVAL_STORAGE_DELAY : Duration = Duration :: from_secs ( 2 ) ;
33- /// Max time allocated for getting piece from DSN before attempt is considered to fail
34- const GET_PIECE_TIMEOUT : Duration = Duration :: from_secs ( 5 ) ;
35-
36- // Defines target storage type for requets.
37- #[ derive( Debug , Copy , Clone ) ]
38- enum StorageType {
39- // L2 piece cache
40- Cache ,
41- // L1 archival storage for pieces
42- ArchivalStorage ,
43- }
44-
45- impl From < StorageType > for MultihashCode {
46- fn from ( storage_type : StorageType ) -> Self {
47- match storage_type {
48- StorageType :: Cache => MultihashCode :: PieceIndex ,
49- StorageType :: ArchivalStorage => MultihashCode :: Sector ,
50- }
51- }
52- }
53-
54- // Temporary struct serving pieces from different providers using configuration arguments.
55- pub ( crate ) struct MultiChannelPieceReceiver < ' a , NC > {
14+ pub ( crate ) struct RecordsRootPieceValidator < ' a , NC > {
5615 dsn_node : & ' a Node ,
5716 node_client : & ' a NC ,
5817 kzg : & ' a Kzg ,
5918 records_root_cache : & ' a Mutex < LruCache < SegmentIndex , RecordsRoot > > ,
60- cancelled : & ' a AtomicBool ,
6119}
6220
63- impl < ' a , NC > MultiChannelPieceReceiver < ' a , NC >
64- where
65- NC : NodeClient ,
66- {
21+ impl < ' a , NC > RecordsRootPieceValidator < ' a , NC > {
6722 pub ( crate ) fn new (
6823 dsn_node : & ' a Node ,
6924 node_client : & ' a NC ,
7025 kzg : & ' a Kzg ,
7126 records_root_cache : & ' a Mutex < LruCache < SegmentIndex , RecordsRoot > > ,
72- cancelled : & ' a AtomicBool ,
7327 ) -> Self {
7428 Self {
7529 dsn_node,
7630 node_client,
7731 kzg,
7832 records_root_cache,
79- cancelled,
80- }
81- }
82-
83- fn check_cancellation ( & self ) -> Result < ( ) , Box < dyn Error + Send + Sync + ' static > > {
84- if self . cancelled . load ( Ordering :: Acquire ) {
85- debug ! ( "Getting a piece was cancelled." ) ;
86-
87- return Err ( "Getting a piece was cancelled." . into ( ) ) ;
88- }
89-
90- Ok ( ( ) )
91- }
92-
93- // Get from piece cache (L2) or archival storage (L1)
94- async fn get_piece_from_storage (
95- & self ,
96- piece_index : PieceIndex ,
97- storage_type : StorageType ,
98- ) -> Option < Piece > {
99- let piece_index_hash = PieceIndexHash :: from_index ( piece_index) ;
100- let key = piece_index_hash. to_multihash_by_code ( storage_type. into ( ) ) ;
101- let piece_key = match storage_type {
102- StorageType :: Cache => PieceKey :: Cache ( piece_index_hash) ,
103- StorageType :: ArchivalStorage => PieceKey :: ArchivalStorage ( piece_index_hash) ,
104- } ;
105-
106- let get_providers_result = self . dsn_node . get_providers ( key) . await ;
107-
108- match get_providers_result {
109- Ok ( mut get_providers_stream) => {
110- while let Some ( provider_id) = get_providers_stream. next ( ) . await {
111- trace ! ( %piece_index, %provider_id, "get_providers returned an item" ) ;
112-
113- let request_result = self
114- . dsn_node
115- . send_generic_request ( provider_id, PieceByHashRequest { key : piece_key } )
116- . await ;
117-
118- match request_result {
119- Ok ( PieceByHashResponse { piece : Some ( piece) } ) => {
120- trace ! ( %provider_id, %piece_index, ?key, "Piece request succeeded." ) ;
121- return self
122- . validate_piece ( provider_id, piece_index, key, piece)
123- . await ;
124- }
125- Ok ( PieceByHashResponse { piece : None } ) => {
126- debug ! ( %provider_id, %piece_index, ?key, "Piece request returned empty piece." ) ;
127- }
128- Err ( error) => {
129- warn ! ( %provider_id, %piece_index, ?key, ?error, "Piece request failed." ) ;
130- }
131- }
132- }
133- }
134- Err ( err) => {
135- warn ! ( %piece_index, ?key, ?err, "get_providers returned an error" ) ;
136- }
13733 }
138-
139- None
14034 }
35+ }
14136
37+ #[ async_trait]
38+ impl < ' a , NC > PieceValidator for RecordsRootPieceValidator < ' a , NC >
39+ where
40+ NC : NodeClient ,
41+ {
14242 async fn validate_piece (
14343 & self ,
14444 source_peer_id : PeerId ,
14545 piece_index : PieceIndex ,
146- key : Multihash ,
14746 piece : Piece ,
14847 ) -> Option < Piece > {
14948 if source_peer_id != self . dsn_node . id ( ) {
15958 Err ( error) => {
16059 error ! (
16160 %piece_index,
162- ?key,
16361 ?error,
16462 "Failed tor retrieve records root from node"
16563 ) ;
17270 None => {
17371 error ! (
17472 %piece_index,
175- ?key,
17673 %segment_index,
17774 "Records root for segment index wasn't found on node"
17875 ) ;
19996 ) {
20097 error ! (
20198 %piece_index,
202- ?key,
20399 %source_peer_id,
204100 "Received invalid piece from peer"
205101 ) ;
@@ -213,64 +109,3 @@ where
213109 Some ( piece)
214110 }
215111}
216-
217- #[ async_trait]
218- impl < ' a , NC > PieceReceiver for MultiChannelPieceReceiver < ' a , NC >
219- where
220- NC : NodeClient ,
221- {
222- async fn get_piece (
223- & self ,
224- piece_index : PieceIndex ,
225- ) -> Result < Option < Piece > , Box < dyn Error + Send + Sync + ' static > > {
226- trace ! ( %piece_index, "Piece request." ) ;
227-
228- let backoff = ExponentialBackoff {
229- initial_interval : GET_PIECE_INITIAL_INTERVAL ,
230- max_interval : GET_PIECE_MAX_INTERVAL ,
231- // Try until we get a valid piece
232- max_elapsed_time : None ,
233- ..ExponentialBackoff :: default ( )
234- } ;
235-
236- retry ( backoff, || async {
237- self . check_cancellation ( )
238- . map_err ( backoff:: Error :: Permanent ) ?;
239-
240- // Try to pull pieces in two ways, whichever is faster
241- let mut piece_attempts = [
242- timeout (
243- GET_PIECE_TIMEOUT ,
244- Box :: pin ( self . get_piece_from_storage ( piece_index, StorageType :: Cache ) )
245- as Pin < Box < dyn Future < Output = _ > + Send > > ,
246- ) ,
247- //TODO: verify "broken pipe" error cause
248- timeout (
249- GET_PIECE_TIMEOUT ,
250- Box :: pin ( async {
251- // Prefer cache if it can return quickly, otherwise fall back to archival storage
252- sleep ( GET_PIECE_ARCHIVAL_STORAGE_DELAY ) . await ;
253- self . get_piece_from_storage ( piece_index, StorageType :: ArchivalStorage )
254- . await
255- } ) as Pin < Box < dyn Future < Output = _ > + Send > > ,
256- ) ,
257- ]
258- . into_iter ( )
259- . collect :: < FuturesUnordered < _ > > ( ) ;
260-
261- while let Some ( maybe_piece) = piece_attempts. next ( ) . await {
262- if let Ok ( Some ( piece) ) = maybe_piece {
263- trace ! ( %piece_index, "Got piece" ) ;
264- return Ok ( Some ( piece) ) ;
265- }
266- }
267-
268- warn ! ( %piece_index, "Couldn't get a piece from DSN. Retrying..." ) ;
269-
270- Err ( backoff:: Error :: transient (
271- "Couldn't get piece from DSN" . into ( ) ,
272- ) )
273- } )
274- . await
275- }
276- }
0 commit comments