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  }