@@ -50,6 +50,7 @@ use rand::seq::IteratorRandom;
50
50
use ssz:: { Decode , Encode } ;
51
51
use ssz_types:: VariableList ;
52
52
use tokio:: sync:: mpsc:: UnboundedSender ;
53
+ use tokio:: sync:: Mutex ;
53
54
use tracing:: { debug, warn} ;
54
55
55
56
pub use super :: overlay_service:: { OverlayRequestError , RequestDirection } ;
@@ -116,12 +117,13 @@ pub struct OverlayProtocol<TContentKey, TMetric, TValidator> {
116
117
phantom_content_key : PhantomData < TContentKey > ,
117
118
/// Associate a metric with the overlay network.
118
119
phantom_metric : PhantomData < TMetric > ,
119
- /// Declare the Validator type for a given overlay network.
120
- phantom_validator : PhantomData < TValidator > ,
120
+ /// Non-blocking validator that makes requests to this/other overlay networks (or infura) to
121
+ /// validate accepted content.
122
+ validator : Arc < Mutex < TValidator > > ,
121
123
}
122
124
123
125
impl <
124
- TContentKey : OverlayContentKey + Send + Sync ,
126
+ TContentKey : ' static + OverlayContentKey + Send + Sync ,
125
127
TMetric : Metric + Send + Sync ,
126
128
TValidator : ' static + Validator < TContentKey > + Send + Sync ,
127
129
> OverlayProtocol < TContentKey , TMetric , TValidator >
@@ -135,7 +137,7 @@ where
135
137
storage : Arc < RwLock < PortalStorage > > ,
136
138
data_radius : U256 ,
137
139
protocol : ProtocolId ,
138
- validator : TValidator ,
140
+ validator : Arc < Mutex < TValidator > > ,
139
141
) -> Self {
140
142
let kbuckets = Arc :: new ( RwLock :: new ( KBucketsTable :: new (
141
143
discovery. local_enr ( ) . node_id ( ) . into ( ) ,
@@ -156,7 +158,7 @@ where
156
158
protocol. clone ( ) ,
157
159
utp_listener_tx. clone ( ) ,
158
160
config. enable_metrics ,
159
- validator,
161
+ Arc :: clone ( & validator) ,
160
162
config. query_timeout ,
161
163
config. query_peer_timeout ,
162
164
config. query_parallelism ,
@@ -176,7 +178,7 @@ where
176
178
utp_listener_tx,
177
179
phantom_content_key : PhantomData ,
178
180
phantom_metric : PhantomData ,
179
- phantom_validator : PhantomData ,
181
+ validator ,
180
182
}
181
183
}
182
184
@@ -253,35 +255,60 @@ where
253
255
) ) ;
254
256
}
255
257
256
- // TODO: Verify overlay content data with an Oracle
257
-
258
- // Temporarily store content key/value pairs to propagate here
259
- let mut content_keys_values: Vec < ( TContentKey , ByteList ) > = Vec :: new ( ) ;
260
-
261
- // Try to store the content into the database and propagate gossip the content
258
+ // Validate, and try to store the content into the database and propagate gossip the content
262
259
for ( content_key, content_value) in content_keys. into_iter ( ) . zip ( content_values. to_vec ( ) ) {
263
260
match TContentKey :: try_from ( content_key) {
264
261
Ok ( key) => {
265
- // Store accepted content in DB
266
- self . store_overlay_content ( & key, content_value. clone ( ) ) ;
267
- content_keys_values. push ( ( key, content_value) )
262
+ // Spawn a task that...
263
+ // - Validates accepted content (this step requires a dedicated task since it
264
+ // might require non-blocking requests to this/other overlay networks)
265
+ // - Checks if content should be stored, and stores it if true
266
+ // - Propagate all validated content
267
+ let validator = Arc :: clone ( & self . validator ) ;
268
+ let storage = Arc :: clone ( & self . storage ) ;
269
+ let kbuckets = Arc :: clone ( & self . kbuckets ) ;
270
+ let request_tx = self . request_tx . clone ( ) ;
271
+
272
+ tokio:: spawn ( async move {
273
+ // Validated received content
274
+ let mut lock = validator. lock ( ) . await ;
275
+ if let Err ( err) = lock. validate_content ( & key, & content_value. to_vec ( ) ) . await
276
+ {
277
+ // Skip storing & propagating content if it's not valid
278
+ error ! ( "Unable to validate received content: {err:?}" ) ;
279
+ return ;
280
+ } ;
281
+
282
+ // Check if data should be stored, and store if true.
283
+ let _ = storage
284
+ . write ( )
285
+ . store_if_should ( & key, & content_value. to_vec ( ) ) ;
286
+
287
+ // Propagate all validated content, whether or not it was stored.
288
+ OverlayProtocol :: < TContentKey , TMetric , TValidator > :: propagate_gossip (
289
+ ( key, content_value) ,
290
+ kbuckets,
291
+ request_tx,
292
+ ) ;
293
+ } ) ;
268
294
}
269
295
Err ( err) => {
270
- return Err ( anyhow ! (
271
- "Unexpected error while decoding overlay content key: {err}"
272
- ) ) ;
296
+ warn ! ( "Unexpected error while decoding overlay content key: {err}" ) ;
297
+ continue ;
273
298
}
274
299
}
275
300
}
276
- // Propagate gossip accepted content
277
- self . propagate_gossip ( content_keys_values) ?;
278
301
Ok ( ( ) )
279
302
}
280
303
281
304
/// Propagate gossip accepted content via OFFER/ACCEPT:
282
- fn propagate_gossip ( & self , content : Vec < ( TContentKey , ByteList ) > ) -> anyhow:: Result < ( ) > {
305
+ fn propagate_gossip (
306
+ content_key_value : ( TContentKey , ByteList ) ,
307
+ kbuckets : Arc < RwLock < KBucketsTable < NodeId , Node > > > ,
308
+ request_tx : UnboundedSender < OverlayRequest > ,
309
+ ) {
283
310
// Get all nodes from overlay routing table
284
- let kbuckets = self . kbuckets . read ( ) ;
311
+ let kbuckets = kbuckets. read ( ) ;
285
312
let all_nodes: Vec < & kbucket:: Node < NodeId , Node > > = kbuckets
286
313
. buckets_iter ( )
287
314
. map ( |kbucket| {
@@ -297,37 +324,44 @@ where
297
324
let mut enrs_and_content: HashMap < String , Vec < RawContentKey > > = HashMap :: new ( ) ;
298
325
299
326
// Filter all nodes from overlay routing table where XOR_distance(content_id, nodeId) < node radius
300
- for content_key_value in content {
301
- let interested_enrs: Vec < Enr > = all_nodes
302
- . clone ( )
303
- . into_iter ( )
304
- . filter ( |node| {
305
- XorMetric :: distance (
306
- & content_key_value. 0 . content_id ( ) ,
307
- & node. key . preimage ( ) . raw ( ) ,
308
- ) < node. value . data_radius ( )
309
- } )
310
- . map ( |node| node. value . enr ( ) )
311
- . collect ( ) ;
312
-
313
- // Continue if no nodes are interested in the content
314
- if interested_enrs. is_empty ( ) {
315
- debug ! ( "No nodes interested in neighborhood gossip: content_key={} num_nodes_checked={}" ,
316
- hex_encode( content_key_value. 0 . into( ) ) , all_nodes. len( ) ) ;
317
- continue ;
318
- }
327
+ let interested_enrs: Vec < Enr > = all_nodes
328
+ . clone ( )
329
+ . into_iter ( )
330
+ . filter ( |node| {
331
+ XorMetric :: distance (
332
+ & content_key_value. 0 . content_id ( ) ,
333
+ & node. key . preimage ( ) . raw ( ) ,
334
+ ) < node. value . data_radius ( )
335
+ } )
336
+ . map ( |node| node. value . enr ( ) )
337
+ . collect ( ) ;
319
338
320
- // Get log2 random ENRs to gossip
321
- let random_enrs = log2_random_enrs ( interested_enrs) ?;
339
+ // Continue if no nodes are interested in the content
340
+ if interested_enrs. is_empty ( ) {
341
+ debug ! (
342
+ "No nodes interested in neighborhood gossip: content_key={} num_nodes_checked={}" ,
343
+ hex_encode( content_key_value. 0 . into( ) ) ,
344
+ all_nodes. len( )
345
+ ) ;
346
+ return ;
347
+ }
322
348
323
- // Temporarily store all randomly selected nodes with the content of interest.
324
- // We want this so we can offer all the content to interested node in one request.
325
- for enr in random_enrs {
326
- enrs_and_content
327
- . entry ( enr. to_base64 ( ) )
328
- . or_default ( )
329
- . push ( content_key_value. clone ( ) . 0 . into ( ) ) ;
349
+ // Get log2 random ENRs to gossip
350
+ let random_enrs = match log2_random_enrs ( interested_enrs) {
351
+ Ok ( val) => val,
352
+ Err ( msg) => {
353
+ debug ! ( "No available enrs for gossip: {msg}" ) ;
354
+ return ;
330
355
}
356
+ } ;
357
+
358
+ // Temporarily store all randomly selected nodes with the content of interest.
359
+ // We want this so we can offer all the content to interested node in one request.
360
+ for enr in random_enrs {
361
+ enrs_and_content
362
+ . entry ( enr. to_base64 ( ) )
363
+ . or_default ( )
364
+ . push ( content_key_value. clone ( ) . 0 . into ( ) ) ;
331
365
}
332
366
333
367
// Create and send OFFER overlay request to the interested nodes
@@ -351,24 +385,10 @@ where
351
385
None ,
352
386
) ;
353
387
354
- if let Err ( err) = self . request_tx . send ( overlay_request) {
388
+ if let Err ( err) = request_tx. send ( overlay_request) {
355
389
error ! ( "Unable to send OFFER request to overlay: {err}." )
356
390
}
357
391
}
358
- Ok ( ( ) )
359
- }
360
-
361
- /// Try to store overlay content into database
362
- fn store_overlay_content ( & self , content_key : & TContentKey , value : ByteList ) {
363
- let should_store = self . storage . read ( ) . should_store ( content_key) ;
364
- match should_store {
365
- Ok ( _) => {
366
- if let Err ( err) = self . storage . write ( ) . store ( content_key, & value. into ( ) ) {
367
- warn ! ( "Unable to store accepted content: {err}" ) ;
368
- }
369
- }
370
- Err ( err) => error ! ( "Unable to determine whether to store accepted content: {err}" ) ,
371
- }
372
392
}
373
393
374
394
/// Returns a vector of all ENR node IDs of nodes currently contained in the routing table.
0 commit comments