@@ -52,7 +52,7 @@ use ip_network::IpNetwork;
52
52
use libp2p:: core:: { connection:: { ConnectionId , ListenerId } , ConnectedPoint , Multiaddr , PeerId , PublicKey } ;
53
53
use libp2p:: swarm:: { NetworkBehaviour , NetworkBehaviourAction , PollParameters , ProtocolsHandler } ;
54
54
use libp2p:: swarm:: protocols_handler:: multi:: MultiHandler ;
55
- use libp2p:: kad:: { Kademlia , KademliaConfig , KademliaEvent , QueryResult , Quorum , Record } ;
55
+ use libp2p:: kad:: { Kademlia , KademliaBucketInserts , KademliaConfig , KademliaEvent , QueryResult , Quorum , Record } ;
56
56
use libp2p:: kad:: GetClosestPeersError ;
57
57
use libp2p:: kad:: handler:: KademliaHandler ;
58
58
use libp2p:: kad:: QueryId ;
@@ -137,17 +137,9 @@ impl DiscoveryConfig {
137
137
}
138
138
139
139
/// Add discovery via Kademlia for the given protocol.
140
- pub fn add_protocol ( & mut self , p : ProtocolId ) -> & mut Self {
141
- // NB: If this protocol name derivation is changed, check if
142
- // `DiscoveryBehaviour::new_handler` is still correct.
143
- let proto_name = {
144
- let mut v = vec ! [ b'/' ] ;
145
- v. extend_from_slice ( p. as_bytes ( ) ) ;
146
- v. extend_from_slice ( b"/kad" ) ;
147
- v
148
- } ;
149
-
150
- self . add_kademlia ( p, proto_name) ;
140
+ pub fn add_protocol ( & mut self , id : ProtocolId ) -> & mut Self {
141
+ let name = protocol_name_from_protocol_id ( & id) ;
142
+ self . add_kademlia ( id, name) ;
151
143
self
152
144
}
153
145
@@ -159,6 +151,10 @@ impl DiscoveryConfig {
159
151
160
152
let mut config = KademliaConfig :: default ( ) ;
161
153
config. set_protocol_name ( proto_name) ;
154
+ // By default Kademlia attempts to insert all peers into its routing table once a dialing
155
+ // attempt succeeds. In order to control which peer is added, disable the auto-insertion and
156
+ // instead add peers manually.
157
+ config. set_kbucket_inserts ( KademliaBucketInserts :: Manual ) ;
162
158
163
159
let store = MemoryStore :: new ( self . local_peer_id . clone ( ) ) ;
164
160
let mut kad = Kademlia :: with_config ( self . local_peer_id . clone ( ) , store, config) ;
@@ -259,17 +255,43 @@ impl DiscoveryBehaviour {
259
255
}
260
256
}
261
257
262
- /// Call this method when a node reports an address for itself.
258
+ /// Add a self-reported address of a remote peer to the k-buckets of the supported
259
+ /// DHTs (`supported_protocols`).
263
260
///
264
- /// **Note**: It is important that you call this method, otherwise the discovery mechanism will
265
- /// not properly work.
266
- pub fn add_self_reported_address ( & mut self , peer_id : & PeerId , addr : Multiaddr ) {
267
- if self . allow_non_globals_in_dht || self . can_add_to_dht ( & addr) {
268
- for k in self . kademlias . values_mut ( ) {
269
- k. add_address ( peer_id, addr. clone ( ) ) ;
261
+ /// **Note**: It is important that you call this method. The discovery mechanism will not
262
+ /// automatically add connecting peers to the Kademlia k-buckets.
263
+ pub fn add_self_reported_address (
264
+ & mut self ,
265
+ peer_id : & PeerId ,
266
+ supported_protocols : impl Iterator < Item = impl AsRef < [ u8 ] > > ,
267
+ addr : Multiaddr
268
+ ) {
269
+ if !self . allow_non_globals_in_dht && !self . can_add_to_dht ( & addr) {
270
+ log:: trace!( target: "sub-libp2p" , "Ignoring self-reported non-global address {} from {}." , addr, peer_id) ;
271
+ return
272
+ }
273
+
274
+ let mut added = false ;
275
+ for protocol in supported_protocols {
276
+ for kademlia in self . kademlias . values_mut ( ) {
277
+ if protocol. as_ref ( ) == kademlia. protocol_name ( ) {
278
+ log:: trace!(
279
+ target: "sub-libp2p" ,
280
+ "Adding self-reported address {} from {} to Kademlia DHT {}." ,
281
+ addr, peer_id, String :: from_utf8_lossy( kademlia. protocol_name( ) ) ,
282
+ ) ;
283
+ kademlia. add_address ( peer_id, addr. clone ( ) ) ;
284
+ added = true ;
285
+ }
270
286
}
271
- } else {
272
- log:: trace!( target: "sub-libp2p" , "Ignoring self-reported address {} from {}" , addr, peer_id) ;
287
+ }
288
+
289
+ if !added {
290
+ log:: trace!(
291
+ target: "sub-libp2p" ,
292
+ "Ignoring self-reported address {} from {} as remote node is not part of any \
293
+ Kademlia DHTs supported by the local node.", addr, peer_id,
294
+ ) ;
273
295
}
274
296
}
275
297
@@ -340,17 +362,21 @@ impl DiscoveryBehaviour {
340
362
}
341
363
342
364
/// Event generated by the `DiscoveryBehaviour`.
365
+ #[ derive( Debug ) ]
343
366
pub enum DiscoveryOut {
344
- /// The address of a peer has been added to the Kademlia routing table.
345
- ///
346
- /// Can be called multiple times with the same identity.
367
+ /// A connection to a peer has been established but the peer has not been
368
+ /// added to the routing table because [`KademliaBucketInserts::Manual`] is
369
+ /// configured. If the peer is to be included in the routing table, it must
370
+ /// be explicitly added via
371
+ /// [`DiscoveryBehaviour::add_self_reported_address`].
347
372
Discovered ( PeerId ) ,
348
373
349
374
/// A peer connected to this node for whom no listen address is known.
350
375
///
351
376
/// In order for the peer to be added to the Kademlia routing table, a known
352
- /// listen address must be added via [`DiscoveryBehaviour::add_self_reported_address`],
353
- /// e.g. obtained through the `identify` protocol.
377
+ /// listen address must be added via
378
+ /// [`DiscoveryBehaviour::add_self_reported_address`], e.g. obtained through
379
+ /// the `identify` protocol.
354
380
UnroutablePeer ( PeerId ) ,
355
381
356
382
/// The DHT yielded results for the record request, grouped in (key, value) pairs.
@@ -569,8 +595,12 @@ impl NetworkBehaviour for DiscoveryBehaviour {
569
595
let ev = DiscoveryOut :: UnroutablePeer ( peer) ;
570
596
return Poll :: Ready ( NetworkBehaviourAction :: GenerateEvent ( ev) ) ;
571
597
}
572
- KademliaEvent :: RoutablePeer { .. } | KademliaEvent :: PendingRoutablePeer { .. } => {
573
- // We are not interested in these events at the moment.
598
+ KademliaEvent :: RoutablePeer { peer, .. } => {
599
+ let ev = DiscoveryOut :: Discovered ( peer) ;
600
+ return Poll :: Ready ( NetworkBehaviourAction :: GenerateEvent ( ev) ) ;
601
+ }
602
+ KademliaEvent :: PendingRoutablePeer { .. } => {
603
+ // We are not interested in this event at the moment.
574
604
}
575
605
KademliaEvent :: QueryResult { result : QueryResult :: GetClosestPeers ( res) , .. } => {
576
606
match res {
@@ -689,25 +719,36 @@ impl NetworkBehaviour for DiscoveryBehaviour {
689
719
}
690
720
}
691
721
722
+ // NB: If this protocol name derivation is changed, check if
723
+ // `DiscoveryBehaviour::new_handler` is still correct.
724
+ fn protocol_name_from_protocol_id ( id : & ProtocolId ) -> Vec < u8 > {
725
+ let mut v = vec ! [ b'/' ] ;
726
+ v. extend_from_slice ( id. as_bytes ( ) ) ;
727
+ v. extend_from_slice ( b"/kad" ) ;
728
+ v
729
+ }
730
+
692
731
#[ cfg( test) ]
693
732
mod tests {
694
733
use crate :: config:: ProtocolId ;
695
734
use futures:: prelude:: * ;
696
735
use libp2p:: identity:: Keypair ;
697
- use libp2p:: Multiaddr ;
736
+ use libp2p:: { Multiaddr , PeerId } ;
698
737
use libp2p:: core:: upgrade;
699
738
use libp2p:: core:: transport:: { Transport , MemoryTransport } ;
700
739
use libp2p:: core:: upgrade:: { InboundUpgradeExt , OutboundUpgradeExt } ;
701
740
use libp2p:: swarm:: Swarm ;
702
741
use std:: { collections:: HashSet , task:: Poll } ;
703
- use super :: { DiscoveryConfig , DiscoveryOut } ;
742
+ use super :: { DiscoveryConfig , DiscoveryOut , protocol_name_from_protocol_id } ;
704
743
705
744
#[ test]
706
745
fn discovery_working ( ) {
707
- let mut user_defined = Vec :: new ( ) ;
746
+ let mut first_swarm_peer_id_and_addr = None ;
747
+ let protocol_id = ProtocolId :: from ( b"dot" . as_ref ( ) ) ;
708
748
709
- // Build swarms whose behaviour is `DiscoveryBehaviour`.
710
- let mut swarms = ( 0 ..25 ) . map ( |_| {
749
+ // Build swarms whose behaviour is `DiscoveryBehaviour`, each aware of
750
+ // the first swarm via `with_user_defined`.
751
+ let mut swarms = ( 0 ..25 ) . map ( |i| {
711
752
let keypair = Keypair :: generate_ed25519 ( ) ;
712
753
let keypair2 = keypair. clone ( ) ;
713
754
@@ -730,23 +771,21 @@ mod tests {
730
771
} ) ;
731
772
732
773
let behaviour = {
733
- let protocol_id: & [ u8 ] = b"/test/kad/1.0.0" ;
734
-
735
774
let mut config = DiscoveryConfig :: new ( keypair. public ( ) ) ;
736
- config. with_user_defined ( user_defined . clone ( ) )
775
+ config. with_user_defined ( first_swarm_peer_id_and_addr . clone ( ) )
737
776
. allow_private_ipv4 ( true )
738
777
. allow_non_globals_in_dht ( true )
739
778
. discovery_limit ( 50 )
740
- . add_protocol ( ProtocolId :: from ( protocol_id) ) ;
779
+ . add_protocol ( protocol_id. clone ( ) ) ;
741
780
742
781
config. finish ( )
743
782
} ;
744
783
745
784
let mut swarm = Swarm :: new ( transport, behaviour, keypair. public ( ) . into_peer_id ( ) ) ;
746
785
let listen_addr: Multiaddr = format ! ( "/memory/{}" , rand:: random:: <u64 >( ) ) . parse ( ) . unwrap ( ) ;
747
786
748
- if user_defined . is_empty ( ) {
749
- user_defined . push ( ( keypair. public ( ) . into_peer_id ( ) , listen_addr. clone ( ) ) ) ;
787
+ if i == 0 {
788
+ first_swarm_peer_id_and_addr = Some ( ( keypair. public ( ) . into_peer_id ( ) , listen_addr. clone ( ) ) )
750
789
}
751
790
752
791
Swarm :: listen_on ( & mut swarm, listen_addr. clone ( ) ) . unwrap ( ) ;
@@ -755,7 +794,10 @@ mod tests {
755
794
756
795
// Build a `Vec<HashSet<PeerId>>` with the list of nodes remaining to be discovered.
757
796
let mut to_discover = ( 0 ..swarms. len ( ) ) . map ( |n| {
758
- ( 0 ..swarms. len ( ) ) . filter ( |p| * p != n)
797
+ ( 0 ..swarms. len ( ) )
798
+ // Skip the first swarm as all other swarms already know it.
799
+ . skip ( 1 )
800
+ . filter ( |p| * p != n)
759
801
. map ( |p| Swarm :: local_peer_id ( & swarms[ p] . 0 ) . clone ( ) )
760
802
. collect :: < HashSet < _ > > ( )
761
803
} ) . collect :: < Vec < _ > > ( ) ;
@@ -766,7 +808,7 @@ mod tests {
766
808
match swarms[ swarm_n] . 0 . poll_next_unpin ( cx) {
767
809
Poll :: Ready ( Some ( e) ) => {
768
810
match e {
769
- DiscoveryOut :: UnroutablePeer ( other) => {
811
+ DiscoveryOut :: UnroutablePeer ( other) | DiscoveryOut :: Discovered ( other ) => {
770
812
// Call `add_self_reported_address` to simulate identify happening.
771
813
let addr = swarms. iter ( ) . find_map ( |( s, a) |
772
814
if s. local_peer_id == other {
@@ -775,12 +817,16 @@ mod tests {
775
817
None
776
818
} )
777
819
. unwrap ( ) ;
778
- swarms[ swarm_n] . 0 . add_self_reported_address ( & other, addr) ;
779
- } ,
780
- DiscoveryOut :: Discovered ( other) => {
820
+ swarms[ swarm_n] . 0 . add_self_reported_address (
821
+ & other,
822
+ [ protocol_name_from_protocol_id ( & protocol_id) ] . iter ( ) ,
823
+ addr,
824
+ ) ;
825
+
781
826
to_discover[ swarm_n] . remove ( & other) ;
782
- }
783
- _ => { }
827
+ } ,
828
+ DiscoveryOut :: RandomKademliaStarted ( _) => { } ,
829
+ e => { panic ! ( "Unexpected event: {:?}" , e) } ,
784
830
}
785
831
continue ' polling
786
832
}
@@ -799,4 +845,103 @@ mod tests {
799
845
800
846
futures:: executor:: block_on ( fut) ;
801
847
}
848
+
849
+ #[ test]
850
+ fn discovery_ignores_peers_with_unknown_protocols ( ) {
851
+ let supported_protocol_id = ProtocolId :: from ( b"a" . as_ref ( ) ) ;
852
+ let unsupported_protocol_id = ProtocolId :: from ( b"b" . as_ref ( ) ) ;
853
+
854
+ let mut discovery = {
855
+ let keypair = Keypair :: generate_ed25519 ( ) ;
856
+ let mut config = DiscoveryConfig :: new ( keypair. public ( ) ) ;
857
+ config. allow_private_ipv4 ( true )
858
+ . allow_non_globals_in_dht ( true )
859
+ . discovery_limit ( 50 )
860
+ . add_protocol ( supported_protocol_id. clone ( ) ) ;
861
+ config. finish ( )
862
+ } ;
863
+
864
+ let remote_peer_id = PeerId :: random ( ) ;
865
+ let remote_addr: Multiaddr = format ! ( "/memory/{}" , rand:: random:: <u64 >( ) ) . parse ( ) . unwrap ( ) ;
866
+
867
+ // Add remote peer with unsupported protocol.
868
+ discovery. add_self_reported_address (
869
+ & remote_peer_id,
870
+ [ protocol_name_from_protocol_id ( & unsupported_protocol_id) ] . iter ( ) ,
871
+ remote_addr. clone ( ) ,
872
+ ) ;
873
+
874
+ for kademlia in discovery. kademlias . values_mut ( ) {
875
+ assert ! (
876
+ kademlia. kbucket( remote_peer_id. clone( ) )
877
+ . expect( "Remote peer id not to be equal to local peer id." )
878
+ . is_empty( ) ,
879
+ "Expect peer with unsupported protocol not to be added."
880
+ ) ;
881
+ }
882
+
883
+ // Add remote peer with supported protocol.
884
+ discovery. add_self_reported_address (
885
+ & remote_peer_id,
886
+ [ protocol_name_from_protocol_id ( & supported_protocol_id) ] . iter ( ) ,
887
+ remote_addr. clone ( ) ,
888
+ ) ;
889
+
890
+ for kademlia in discovery. kademlias . values_mut ( ) {
891
+ assert_eq ! (
892
+ 1 ,
893
+ kademlia. kbucket( remote_peer_id. clone( ) )
894
+ . expect( "Remote peer id not to be equal to local peer id." )
895
+ . num_entries( ) ,
896
+ "Expect peer with supported protocol to be added."
897
+ ) ;
898
+ }
899
+ }
900
+
901
+ #[ test]
902
+ fn discovery_adds_peer_to_kademlia_of_same_protocol_only ( ) {
903
+ let protocol_a = ProtocolId :: from ( b"a" . as_ref ( ) ) ;
904
+ let protocol_b = ProtocolId :: from ( b"b" . as_ref ( ) ) ;
905
+
906
+ let mut discovery = {
907
+ let keypair = Keypair :: generate_ed25519 ( ) ;
908
+ let mut config = DiscoveryConfig :: new ( keypair. public ( ) ) ;
909
+ config. allow_private_ipv4 ( true )
910
+ . allow_non_globals_in_dht ( true )
911
+ . discovery_limit ( 50 )
912
+ . add_protocol ( protocol_a. clone ( ) )
913
+ . add_protocol ( protocol_b. clone ( ) ) ;
914
+ config. finish ( )
915
+ } ;
916
+
917
+ let remote_peer_id = PeerId :: random ( ) ;
918
+ let remote_addr: Multiaddr = format ! ( "/memory/{}" , rand:: random:: <u64 >( ) ) . parse ( ) . unwrap ( ) ;
919
+
920
+ // Add remote peer with `protocol_a` only.
921
+ discovery. add_self_reported_address (
922
+ & remote_peer_id,
923
+ [ protocol_name_from_protocol_id ( & protocol_a) ] . iter ( ) ,
924
+ remote_addr. clone ( ) ,
925
+ ) ;
926
+
927
+ assert_eq ! (
928
+ 1 ,
929
+ discovery. kademlias. get_mut( & protocol_a)
930
+ . expect( "Kademlia instance to exist." )
931
+ . kbucket( remote_peer_id. clone( ) )
932
+ . expect( "Remote peer id not to be equal to local peer id." )
933
+ . num_entries( ) ,
934
+ "Expected remote peer to be added to `protocol_a` Kademlia instance." ,
935
+
936
+ ) ;
937
+
938
+ assert ! (
939
+ discovery. kademlias. get_mut( & protocol_b)
940
+ . expect( "Kademlia instance to exist." )
941
+ . kbucket( remote_peer_id. clone( ) )
942
+ . expect( "Remote peer id not to be equal to local peer id." )
943
+ . is_empty( ) ,
944
+ "Expected remote peer not to be added to `protocol_b` Kademlia instance." ,
945
+ ) ;
946
+ }
802
947
}
0 commit comments