package peermanager import ( "context" "crypto/rand" "fmt" "strings" "testing" "time" "github.com/libp2p/go-libp2p/core/host" libp2pProtocol "github.com/libp2p/go-libp2p/core/protocol" "github.com/multiformats/go-multiaddr" "github.com/stretchr/testify/require" "github.com/waku-org/go-waku/tests" wps "github.com/waku-org/go-waku/waku/v2/peerstore" wakuproto "github.com/waku-org/go-waku/waku/v2/protocol" "github.com/waku-org/go-waku/waku/v2/protocol/relay" "github.com/waku-org/go-waku/waku/v2/utils" ) func getAddr(h host.Host) multiaddr.Multiaddr { id, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/p2p/%s", h.ID().Pretty())) var selectedAddr multiaddr.Multiaddr //For now skipping circuit relay addresses as libp2p seems to be returning empty p2p-circuit addresses. for _, addr := range h.Network().ListenAddresses() { if strings.Contains(addr.String(), "p2p-circuit") { continue } selectedAddr = addr } return selectedAddr.Encapsulate(id) } func initTest(t *testing.T) (context.Context, *PeerManager, func()) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) // hosts h1, err := tests.MakeHost(ctx, 0, rand.Reader) require.NoError(t, err) // host 1 is used by peer manager pm := NewPeerManager(10, 20, utils.Logger()) pm.SetHost(h1) return ctx, pm, func() { cancel() h1.Close() } } func TestServiceSlots(t *testing.T) { ctx, pm, deferFn := initTest(t) defer deferFn() h2, err := tests.MakeHost(ctx, 0, rand.Reader) require.NoError(t, err) defer h2.Close() h3, err := tests.MakeHost(ctx, 0, rand.Reader) require.NoError(t, err) defer h3.Close() // protocols protocol := libp2pProtocol.ID("test/protocol") protocol1 := libp2pProtocol.ID("test/protocol1") // add h2 peer to peer manager t.Log(h2.ID()) _, err = pm.AddPeer(getAddr(h2), wps.Static, []string{""}, libp2pProtocol.ID(protocol)) require.NoError(t, err) /////////////// // getting peer for protocol /////////////// // select peer from pm, currently only h2 is set in pm peerID, err := pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: protocol}) require.NoError(t, err) require.Equal(t, peerID, h2.ID()) // add h3 peer to peer manager _, err = pm.AddPeer(getAddr(h3), wps.Static, []string{""}, libp2pProtocol.ID(protocol)) require.NoError(t, err) // check that returned peer is h2 or h3 peer peerID, err = pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: protocol}) require.NoError(t, err) if peerID == h2.ID() || peerID == h3.ID() { //Test success t.Log("Random peer selection per protocol successful") } else { t.FailNow() } /////////////// // getting peer for protocol1 /////////////// h4, err := tests.MakeHost(ctx, 0, rand.Reader) require.NoError(t, err) defer h4.Close() _, err = pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: protocol1}) require.Error(t, err, ErrNoPeersAvailable) // add h4 peer for protocol1 _, err = pm.AddPeer(getAddr(h4), wps.Static, []string{""}, libp2pProtocol.ID(protocol1)) require.NoError(t, err) //Test peer selection for protocol1 peerID, err = pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: protocol1}) require.NoError(t, err) require.Equal(t, peerID, h4.ID()) _, err = pm.SelectPeerByContentTopic(protocol1, "") require.Error(t, wakuproto.ErrInvalidFormat, err) } func TestPeerSelection(t *testing.T) { ctx, pm, deferFn := initTest(t) defer deferFn() h2, err := tests.MakeHost(ctx, 0, rand.Reader) require.NoError(t, err) defer h2.Close() h3, err := tests.MakeHost(ctx, 0, rand.Reader) require.NoError(t, err) defer h3.Close() protocol := libp2pProtocol.ID("test/protocol") _, err = pm.AddPeer(getAddr(h2), wps.Static, []string{"/waku/rs/2/1", "/waku/rs/2/2"}, libp2pProtocol.ID(protocol)) require.NoError(t, err) _, err = pm.AddPeer(getAddr(h3), wps.Static, []string{"/waku/rs/2/1"}, libp2pProtocol.ID(protocol)) require.NoError(t, err) _, err = pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: protocol}) require.NoError(t, err) peerID, err := pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: protocol, PubsubTopic: "/waku/rs/2/2"}) require.NoError(t, err) require.Equal(t, h2.ID(), peerID) _, err = pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: protocol, PubsubTopic: "/waku/rs/2/3"}) require.Error(t, ErrNoPeersAvailable, err) _, err = pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: protocol, PubsubTopic: "/waku/rs/2/1"}) require.NoError(t, err) //Test for selectWithLowestRTT _, err = pm.SelectPeer(PeerSelectionCriteria{SelectionType: LowestRTT, Proto: protocol, PubsubTopic: "/waku/rs/2/1"}) require.NoError(t, err) } func TestDefaultProtocol(t *testing.T) { ctx, pm, deferFn := initTest(t) defer deferFn() /////////////// // check peer for default protocol /////////////// //Test empty peer selection for relay protocol _, err := pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: relay.WakuRelayID_v200}) require.Error(t, err, ErrNoPeersAvailable) /////////////// // getting peer for default protocol /////////////// h5, err := tests.MakeHost(ctx, 0, rand.Reader) require.NoError(t, err) defer h5.Close() //Test peer selection for relay protocol from peer store _, err = pm.AddPeer(getAddr(h5), wps.Static, []string{""}, relay.WakuRelayID_v200) require.NoError(t, err) // since we are not passing peerList, selectPeer fn using filterByProto checks in PeerStore for peers with same protocol. peerID, err := pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: relay.WakuRelayID_v200}) require.NoError(t, err) require.Equal(t, peerID, h5.ID()) } func TestAdditionAndRemovalOfPeer(t *testing.T) { ctx, pm, deferFn := initTest(t) defer deferFn() /////////////// // set h6 peer for protocol2 and remove that peer and check again /////////////// //Test random peer selection protocol2 := libp2pProtocol.ID("test/protocol2") h6, err := tests.MakeHost(ctx, 0, rand.Reader) require.NoError(t, err) defer h6.Close() _, err = pm.AddPeer(getAddr(h6), wps.Static, []string{""}, protocol2) require.NoError(t, err) peerID, err := pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: protocol2}) require.NoError(t, err) require.Equal(t, peerID, h6.ID()) pm.RemovePeer(peerID) _, err = pm.SelectPeer(PeerSelectionCriteria{SelectionType: Automatic, Proto: protocol2}) require.Error(t, err, ErrNoPeersAvailable) } func TestConnectToRelayPeers(t *testing.T) { ctx, pm, deferFn := initTest(t) pc, err := NewPeerConnectionStrategy(pm, 120*time.Second, pm.logger) require.NoError(t, err) pm.SetPeerConnector(pc) err = pc.Start(ctx) require.NoError(t, err) pm.Start(ctx) defer deferFn() pm.connectToRelayPeers() }