github.com/celestiaorg/celestia-node@v0.15.0-beta.1/share/p2p/discovery/discovery_test.go (about) 1 // go:build !race 2 3 package discovery 4 5 import ( 6 "context" 7 "testing" 8 "time" 9 10 dht "github.com/libp2p/go-libp2p-kad-dht" 11 "github.com/libp2p/go-libp2p/core/discovery" 12 "github.com/libp2p/go-libp2p/core/host" 13 "github.com/libp2p/go-libp2p/core/peer" 14 "github.com/libp2p/go-libp2p/p2p/discovery/routing" 15 basic "github.com/libp2p/go-libp2p/p2p/host/basic" 16 "github.com/libp2p/go-libp2p/p2p/host/eventbus" 17 swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 ) 21 22 const ( 23 fullNodesTag = "full" 24 ) 25 26 func TestDiscovery(t *testing.T) { 27 const nodes = 10 // higher number brings higher coverage 28 29 discoveryRetryTimeout = time.Millisecond * 100 // defined in discovery.go 30 31 ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) 32 t.Cleanup(cancel) 33 34 tn := newTestnet(ctx, t) 35 36 type peerUpdate struct { 37 peerID peer.ID 38 isAdded bool 39 } 40 updateCh := make(chan peerUpdate) 41 submit := func(peerID peer.ID, isAdded bool) { 42 updateCh <- peerUpdate{peerID: peerID, isAdded: isAdded} 43 } 44 45 host, routingDisc := tn.peer() 46 params := DefaultParameters() 47 params.PeersLimit = nodes 48 49 // start discovery listener service for peerA 50 peerA := tn.startNewDiscovery(params, host, routingDisc, fullNodesTag, 51 WithOnPeersUpdate(submit), 52 ) 53 54 // start discovery advertisement services for other peers 55 params.AdvertiseInterval = time.Millisecond * 100 56 discs := make([]*Discovery, nodes) 57 for i := range discs { 58 host, routingDisc := tn.peer() 59 disc, err := NewDiscovery(params, host, routingDisc, fullNodesTag) 60 require.NoError(t, err) 61 go disc.Advertise(tn.ctx) 62 discs[i] = tn.startNewDiscovery(params, host, routingDisc, fullNodesTag) 63 64 select { 65 case res := <-updateCh: 66 require.Equal(t, discs[i].host.ID(), res.peerID) 67 require.True(t, res.isAdded) 68 case <-ctx.Done(): 69 t.Fatal("did not discover peer in time") 70 } 71 } 72 73 assert.EqualValues(t, nodes, peerA.set.Size()) 74 75 // disconnect peerA from all peers and check that notifications are received on updateCh channel 76 for _, disc := range discs { 77 peerID := disc.host.ID() 78 err := peerA.host.Network().ClosePeer(peerID) 79 require.NoError(t, err) 80 81 select { 82 case res := <-updateCh: 83 require.Equal(t, peerID, res.peerID) 84 require.False(t, res.isAdded) 85 case <-ctx.Done(): 86 t.Fatal("did not disconnect from peer in time") 87 } 88 } 89 90 assert.EqualValues(t, 0, peerA.set.Size()) 91 } 92 93 func TestDiscoveryTagged(t *testing.T) { 94 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 95 t.Cleanup(cancel) 96 97 tn := newTestnet(ctx, t) 98 99 // launch 2 peers, that advertise with different tags 100 adv1, routingDisc1 := tn.peer() 101 adv2, routingDisc2 := tn.peer() 102 103 // sub will discover both peers, but on different tags 104 sub, routingDisc := tn.peer() 105 106 params := DefaultParameters() 107 108 // create 2 discovery services for sub, each with a different tag 109 done1 := make(chan struct{}) 110 tn.startNewDiscovery(params, sub, routingDisc, "tag1", 111 WithOnPeersUpdate(checkPeer(t, adv1.ID(), done1))) 112 113 done2 := make(chan struct{}) 114 tn.startNewDiscovery(params, sub, routingDisc, "tag2", 115 WithOnPeersUpdate(checkPeer(t, adv2.ID(), done2))) 116 117 // run discovery services for advertisers 118 ds1 := tn.startNewDiscovery(params, adv1, routingDisc1, "tag1") 119 go ds1.Advertise(tn.ctx) 120 121 ds2 := tn.startNewDiscovery(params, adv2, routingDisc2, "tag2") 122 go ds2.Advertise(tn.ctx) 123 124 // wait for discovery services to discover each other on different tags 125 select { 126 case <-done1: 127 case <-ctx.Done(): 128 t.Fatal("did not discover peer in time") 129 } 130 131 select { 132 case <-done2: 133 case <-ctx.Done(): 134 t.Fatal("did not discover peer in time") 135 } 136 } 137 138 type testnet struct { 139 ctx context.Context 140 T *testing.T 141 142 bootstrapper peer.AddrInfo 143 } 144 145 func newTestnet(ctx context.Context, t *testing.T) *testnet { 146 bus := eventbus.NewBus() 147 swarm := swarmt.GenSwarm(t, swarmt.OptDisableTCP, swarmt.EventBus(bus)) 148 hst, err := basic.NewHost(swarm, &basic.HostOpts{EventBus: bus}) 149 require.NoError(t, err) 150 hst.Start() 151 152 _, err = dht.New(ctx, hst, 153 dht.Mode(dht.ModeServer), 154 dht.BootstrapPeers(), 155 dht.ProtocolPrefix("/test"), 156 ) 157 require.NoError(t, err) 158 159 return &testnet{ctx: ctx, T: t, bootstrapper: *host.InfoFromHost(hst)} 160 } 161 162 func (t *testnet) startNewDiscovery( 163 params *Parameters, 164 hst host.Host, 165 routingDisc discovery.Discovery, 166 tag string, 167 opts ...Option, 168 ) *Discovery { 169 disc, err := NewDiscovery(params, hst, routingDisc, tag, opts...) 170 require.NoError(t.T, err) 171 err = disc.Start(t.ctx) 172 require.NoError(t.T, err) 173 t.T.Cleanup(func() { 174 err := disc.Stop(t.ctx) 175 require.NoError(t.T, err) 176 }) 177 return disc 178 } 179 180 func (t *testnet) peer() (host.Host, discovery.Discovery) { 181 bus := eventbus.NewBus() 182 swarm := swarmt.GenSwarm(t.T, swarmt.OptDisableTCP, swarmt.EventBus(bus)) 183 hst, err := basic.NewHost(swarm, &basic.HostOpts{EventBus: bus}) 184 require.NoError(t.T, err) 185 hst.Start() 186 187 err = hst.Connect(t.ctx, t.bootstrapper) 188 require.NoError(t.T, err) 189 190 dht, err := dht.New(t.ctx, hst, 191 dht.Mode(dht.ModeServer), 192 dht.ProtocolPrefix("/test"), 193 // needed to reduce connections to peers on DHT level 194 dht.BucketSize(1), 195 ) 196 require.NoError(t.T, err) 197 198 err = dht.Bootstrap(t.ctx) 199 require.NoError(t.T, err) 200 201 return hst, routing.NewRoutingDiscovery(dht) 202 } 203 204 func checkPeer(t *testing.T, expected peer.ID, done chan struct{}) func(peerID peer.ID, isAdded bool) { 205 return func(peerID peer.ID, isAdded bool) { 206 defer close(done) 207 require.Equal(t, expected, peerID) 208 require.True(t, isAdded) 209 } 210 }