1
- use std:: collections:: { HashMap , HashSet } ;
1
+ use std:: collections:: HashMap ;
2
2
use std:: num:: NonZeroU8 ;
3
3
use std:: time:: Duration ;
4
4
5
5
use ahash:: AHashMap ;
6
6
use anyhow:: { anyhow, Context , Result } ;
7
7
use async_channel:: { bounded as channel, Receiver } ;
8
8
use cid:: Cid ;
9
- use futures:: channel:: oneshot:: { self , Sender as OneShotSender } ;
9
+ use futures:: channel:: oneshot:: Sender as OneShotSender ;
10
10
use futures_util:: stream:: StreamExt ;
11
11
use iroh_rpc_client:: Client as RpcClient ;
12
12
use libp2p:: core:: muxing:: StreamMuxerBox ;
@@ -16,7 +16,8 @@ pub use libp2p::gossipsub::{IdentTopic, Topic};
16
16
use libp2p:: identify:: { IdentifyEvent , IdentifyInfo } ;
17
17
use libp2p:: identity:: Keypair ;
18
18
use libp2p:: kad:: {
19
- self , record:: Key , GetProvidersError , GetProvidersOk , KademliaEvent , QueryResult ,
19
+ self , record:: Key , GetProvidersError , GetProvidersOk , GetProvidersProgress , KademliaEvent ,
20
+ QueryProgress , QueryResult ,
20
21
} ;
21
22
use libp2p:: metrics:: { Metrics , Recorder } ;
22
23
use libp2p:: multiaddr:: Protocol ;
@@ -28,7 +29,7 @@ use libp2p::swarm::{
28
29
use libp2p:: yamux:: WindowUpdateMode ;
29
30
use libp2p:: { core, mplex, noise, yamux, PeerId , Swarm , Transport } ;
30
31
use prometheus_client:: registry:: Registry ;
31
- use tokio:: { select, time} ;
32
+ use tokio:: { select, sync :: mpsc , time} ;
32
33
use tracing:: { debug, info, trace, warn} ;
33
34
34
35
use iroh_bitswap:: {
@@ -62,14 +63,16 @@ pub struct Libp2pService {
62
63
}
63
64
64
65
enum QueryChannel {
65
- GetProviders ( Vec < oneshot :: Sender < Result < HashSet < PeerId > , String > > > ) ,
66
+ GetProviders ( Vec < mpsc :: Sender < Result < PeerId , String > > > ) ,
66
67
}
67
68
68
69
#[ derive( Debug , Hash , PartialEq , Eq ) ]
69
70
enum QueryKey {
70
71
ProviderKey ( Key ) ,
71
72
}
72
73
74
+ const PROVIDER_LIMIT : usize = 20 ;
75
+
73
76
impl Libp2pService {
74
77
pub async fn new (
75
78
config : Libp2pConfig ,
@@ -86,7 +89,7 @@ impl Libp2pService {
86
89
. with_max_pending_outgoing ( Some ( 30 ) ) // TODO: configurable
87
90
. with_max_established_incoming ( Some ( config. target_peer_count ) )
88
91
. with_max_established_outgoing ( Some ( config. target_peer_count ) )
89
- . with_max_established_per_peer ( Some ( 5 ) ) ; // TODO: configurable
92
+ . with_max_established_per_peer ( Some ( 60 ) ) ; // TODO: configurable
90
93
91
94
let node = NodeBehaviour :: new ( & net_keypair, & config, registry) . await ?;
92
95
let mut swarm = SwarmBuilder :: new ( transport, node, peer_id)
@@ -221,42 +224,56 @@ impl Libp2pService {
221
224
Event :: Kademlia ( e) => {
222
225
self . metrics . record ( & e) ;
223
226
if let KademliaEvent :: OutboundQueryCompleted { result, .. } = e {
224
- debug ! ( "kad: {:?}" , result) ;
227
+ debug ! ( "kad completed : {:?}" , result) ;
225
228
match result {
226
- QueryResult :: GetProviders ( Ok ( GetProvidersOk {
227
- providers, key, ..
228
- } ) ) => {
229
- if let Some ( QueryChannel :: GetProviders ( chans) ) =
230
- self . kad_queries . remove ( & QueryKey :: ProviderKey ( key. clone ( ) ) )
231
- {
232
- for chan in chans. into_iter ( ) {
233
- debug ! ( "Sending providers for {:?}" , key) ;
234
- chan. send ( Ok ( providers. clone ( ) ) ) . ok ( ) ;
235
- }
236
- } else {
237
- debug ! ( "No listeners" ) ;
238
- }
229
+ QueryResult :: GetProviders ( Ok ( GetProvidersOk { key, .. } ) ) => {
230
+ let _ = self . kad_queries . remove ( & QueryKey :: ProviderKey ( key) ) ;
239
231
}
232
+
240
233
QueryResult :: GetProviders ( Err ( err) ) => {
241
- let ( key, providers) = match err {
242
- GetProvidersError :: Timeout { key, providers, .. } => {
243
- ( key, providers)
244
- }
234
+ let key = match err {
235
+ GetProvidersError :: Timeout { key, .. } => key,
245
236
} ;
246
237
debug ! ( "GetProviders timeout {:?}" , key) ;
247
238
if let Some ( QueryChannel :: GetProviders ( chans) ) =
248
- self . kad_queries . remove ( & QueryKey :: ProviderKey ( key. clone ( ) ) )
239
+ self . kad_queries . remove ( & QueryKey :: ProviderKey ( key) )
249
240
{
250
241
for chan in chans. into_iter ( ) {
251
- debug ! ( "Sending providers for {:?}" , key) ;
252
- chan. send ( Ok ( providers. clone ( ) ) ) . ok ( ) ;
242
+ chan. send ( Err ( "Timeout" . into ( ) ) ) . await . ok ( ) ;
253
243
}
254
244
}
255
245
}
256
246
other => {
257
247
debug ! ( "Libp2p => Unhandled Kademlia query result: {:?}" , other)
258
248
}
259
249
}
250
+ } else if let KademliaEvent :: OutboundQueryProgressed {
251
+ id, result, count, ..
252
+ } = e
253
+ {
254
+ debug ! ( "kad progressed: {:?}" , result) ;
255
+ match result {
256
+ QueryProgress :: GetProviders ( GetProvidersProgress {
257
+ key, provider, ..
258
+ } ) => {
259
+ if count >= PROVIDER_LIMIT {
260
+ debug ! ( "finish provider query {}/{}" , count, PROVIDER_LIMIT ) ;
261
+ // Finish query if we have enough providers.
262
+ self . swarm . behaviour_mut ( ) . finish_query ( & id) ;
263
+ }
264
+
265
+ if let Some ( QueryChannel :: GetProviders ( chans) ) = self
266
+ . kad_queries
267
+ . get_mut ( & QueryKey :: ProviderKey ( key. clone ( ) ) )
268
+ {
269
+ for chan in chans. iter_mut ( ) {
270
+ chan. send ( Ok ( provider) ) . await . ok ( ) ;
271
+ }
272
+ } else {
273
+ debug ! ( "No listeners" ) ;
274
+ }
275
+ }
276
+ }
260
277
}
261
278
}
262
279
Event :: Identify ( e) => {
@@ -342,7 +359,10 @@ impl Libp2pService {
342
359
) ;
343
360
}
344
361
} else {
345
- response_channel. send ( Ok ( Default :: default ( ) ) ) . ok ( ) ;
362
+ response_channel
363
+ . send ( Err ( "kademlia is not available" . into ( ) ) )
364
+ . await
365
+ . ok ( ) ;
346
366
}
347
367
}
348
368
RpcMessage :: NetListeningAddrs ( response_channel) => {
@@ -434,3 +454,59 @@ pub async fn build_transport(local_key: Keypair) -> Boxed<(PeerId, StreamMuxerBo
434
454
. timeout ( Duration :: from_secs ( 20 ) ) // TODO: configurable
435
455
. boxed ( )
436
456
}
457
+
458
+ #[ cfg( test) ]
459
+ mod tests {
460
+ use crate :: metrics;
461
+
462
+ use super :: * ;
463
+ use anyhow:: Result ;
464
+ use libp2p:: identity:: ed25519;
465
+
466
+ #[ tokio:: test]
467
+ async fn test_fetch_providers ( ) -> Result < ( ) > {
468
+ let mut prom_registry = Registry :: default ( ) ;
469
+ let libp2p_metrics = Metrics :: new ( & mut prom_registry) ;
470
+ let net_keypair = {
471
+ let gen_keypair = ed25519:: Keypair :: generate ( ) ;
472
+ Keypair :: Ed25519 ( gen_keypair)
473
+ } ;
474
+
475
+ let mut network_config = Libp2pConfig :: default ( ) ;
476
+ network_config. metrics . debug = true ;
477
+ let metrics_config = network_config. metrics . clone ( ) ;
478
+
479
+ let mut p2p_service = Libp2pService :: new (
480
+ network_config,
481
+ net_keypair,
482
+ & mut prom_registry,
483
+ libp2p_metrics,
484
+ )
485
+ . await ?;
486
+
487
+ let metrics_handle = iroh_metrics:: init_with_registry (
488
+ metrics:: metrics_config_with_compile_time_info ( metrics_config) ,
489
+ prom_registry,
490
+ )
491
+ . await
492
+ . expect ( "failed to initialize metrics" ) ;
493
+
494
+ let cfg = iroh_rpc_client:: Config :: default ( ) ;
495
+ let p2p_task = tokio:: task:: spawn ( async move {
496
+ p2p_service. run ( ) . await . unwrap ( ) ;
497
+ } ) ;
498
+
499
+ {
500
+ let client = RpcClient :: new ( & cfg) . await ?;
501
+ let c = "QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"
502
+ . parse ( )
503
+ . unwrap ( ) ;
504
+ let providers = client. p2p . fetch_providers ( & c) . await ?;
505
+ assert ! ( providers. len( ) >= PROVIDER_LIMIT ) ;
506
+ }
507
+
508
+ p2p_task. abort ( ) ;
509
+ metrics_handle. shutdown ( ) ;
510
+ Ok ( ( ) )
511
+ }
512
+ }
0 commit comments