diff --git a/discovery_test.go b/discovery_test.go index 55a048b..7ff5619 100644 --- a/discovery_test.go +++ b/discovery_test.go @@ -7,18 +7,20 @@ import ( "github.com/stretchr/testify/require" ) -var testAdvertisementSleepDuration = time.Millisecond * 100 +var testAdvertPropagationDelay = time.Millisecond * 100 func TestHost_Discover(t *testing.T) { - ha := newHost(t, basicTestConfig(t)) + namespaces := []string{"", "test"} + + ha := newHost(t, basicTestConfig(t, namespaces)) err := ha.Start() require.NoError(t, err) - hb := newHost(t, basicTestConfig(t)) + hb := newHost(t, basicTestConfig(t, namespaces)) err = hb.Start() require.NoError(t, err) - hc := newHost(t, basicTestConfig(t)) + hc := newHost(t, basicTestConfig(t, namespaces)) err = hc.Start() require.NoError(t, err) @@ -29,17 +31,15 @@ func TestHost_Discover(t *testing.T) { err = hc.h.Connect(hc.ctx, hb.AddrInfo()) require.NoError(t, err) - require.GreaterOrEqual(t, len(ha.h.Network().Peers()), 1) + // now that the nodes can form a DHT, advertise + ha.Advertise() + hb.Advertise() + hc.Addresses() + time.Sleep(testAdvertPropagationDelay) + + require.GreaterOrEqual(t, len(ha.h.Network().Peers()), 2) require.GreaterOrEqual(t, len(hb.h.Network().Peers()), 2) - require.GreaterOrEqual(t, len(hc.h.Network().Peers()), 1) - - providedNamesapces := func() []string { - return []string{"test"} - } - ha.SetAdvertisedNamespacesFunc(providedNamesapces) - hb.SetAdvertisedNamespacesFunc(providedNamesapces) - hc.SetAdvertisedNamespacesFunc(providedNamesapces) - time.Sleep(testAdvertisementSleepDuration) + require.GreaterOrEqual(t, len(hc.h.Network().Peers()), 2) peerIDs, err := hc.Discover("test", time.Second) require.NoError(t, err) diff --git a/host.go b/host.go index a10b429..8c30a57 100644 --- a/host.go +++ b/host.go @@ -51,6 +51,11 @@ type Config struct { Bootnodes []string ProtocolID string ListenIP string + + // AdvertisedNamespacesFunc is called to determine which namespaces should + // be advertised in the DHT. In most use cases, the passed function should, + // at minimum, return the empty ("") namespace. + AdvertisedNamespacesFunc func() []string } // QUIC will have better performance in high-bandwidth protocols if you increase a socket @@ -152,11 +157,12 @@ func NewHost(cfg *Config) (*Host, error) { ds: ds, bootnodes: bns, discovery: &discovery{ - ctx: ourCtx, - dht: dht, - h: routedHost, - rd: libp2pdiscovery.NewRoutingDiscovery(dht), - advertiseCh: make(chan struct{}), + ctx: ourCtx, + dht: dht, + h: routedHost, + rd: libp2pdiscovery.NewRoutingDiscovery(dht), + advertiseCh: make(chan struct{}), + advertisedNamespaces: cfg.AdvertisedNamespacesFunc, }, } @@ -264,13 +270,6 @@ func (h *Host) SetStreamHandler(pid string, handler func(libp2pnetwork.Stream)) log.Debugf("supporting protocol %s", protocol.ID(h.protocolID+pid)) } -// SetAdvertisedNamespacesFunc sets the function that is called to determine -// which namespaces should be advertised in the DHT. In most use cases, the -// passed function should, at minimum, return the empty ("") namespace. -func (h *Host) SetAdvertisedNamespacesFunc(fn func() []string) { - h.discovery.setAdvertisedNamespacesFunc(fn) -} - // Connectedness returns the connectedness state of a given peer. func (h *Host) Connectedness(who peer.ID) libp2pnetwork.Connectedness { return h.h.Network().Connectedness(who) diff --git a/host_test.go b/host_test.go index 6cce2f8..1b51adc 100644 --- a/host_test.go +++ b/host_test.go @@ -17,7 +17,7 @@ func init() { _ = logging.SetLogLevel("p2pnet", "debug") } -func basicTestConfig(t *testing.T) *Config { +func basicTestConfig(t *testing.T, namespaces []string) *Config { // t.TempDir() is unique on every call. Don't reuse this config with multiple hosts. tmpDir := t.TempDir() return &Config{ @@ -27,6 +27,9 @@ func basicTestConfig(t *testing.T) *Config { KeyFile: path.Join(tmpDir, "node.key"), Bootnodes: nil, ListenIP: "127.0.0.1", + AdvertisedNamespacesFunc: func() []string { + return namespaces + }, } } @@ -41,7 +44,7 @@ func newHost(t *testing.T, cfg *Config) *Host { } func TestNewHost(t *testing.T) { - h := newHost(t, basicTestConfig(t)) + h := newHost(t, basicTestConfig(t, []string{""})) err := h.Start() require.NoError(t, err) @@ -53,40 +56,37 @@ func TestNewHost(t *testing.T) { } func TestAdvertiseDiscover(t *testing.T) { - h1 := newHost(t, basicTestConfig(t)) + nameSpaces := []string{"", "one", "two", "three"} + + h1 := newHost(t, basicTestConfig(t, nameSpaces)) err := h1.Start() require.NoError(t, err) h1Addresses := h1.Addresses() require.NotEmpty(t, h1Addresses) - cfgH2 := basicTestConfig(t) + cfgH2 := basicTestConfig(t, nameSpaces) cfgH2.Bootnodes = []string{h1Addresses[0].String()} h2 := newHost(t, cfgH2) err = h2.Start() require.NoError(t, err) - nameSpaces := []string{"", "one", "two", "three"} - advertisedNamespaces := func() []string { - return nameSpaces - } - h1.SetAdvertisedNamespacesFunc(advertisedNamespaces) - - // Advertise only puts the namespaces to advertise into a channel. It - // doesn't block until the advertisements are actually sent. - time.Sleep(500 * time.Millisecond) + // h1's first advertisement attempt failed, as h2 was not yet online to + // form a DHT with. Run h1's advertisement loop now instead of waiting. + h1.Advertise() + time.Sleep(testAdvertPropagationDelay) for _, ns := range nameSpaces { peerIDs, err := h2.Discover(ns, time.Second*3) - require.NoError(t, err) - require.Len(t, peerIDs, 1) - require.Equal(t, h1.PeerID(), peerIDs[0]) + require.NoError(t, err, "namespace=%q", ns) + require.Len(t, peerIDs, 1, "namespace=%q", ns) + require.Equal(t, h1.PeerID(), peerIDs[0], "namespace=%q", ns) } } func TestHost_ConnectToSelf(t *testing.T) { - h := newHost(t, basicTestConfig(t)) + h := newHost(t, basicTestConfig(t, nil)) err := h.Start() require.NoError(t, err)