diff --git a/p2p/host/basic/basic_host.go b/p2p/host/basic/basic_host.go index b5d252e9d2..761a39b664 100644 --- a/p2p/host/basic/basic_host.go +++ b/p2p/host/basic/basic_host.go @@ -500,6 +500,7 @@ func (h *BasicHost) makeUpdatedAddrEvent(prev, current []ma.Multiaddr) *event.Ev return nil } prevmap := make(map[string]ma.Multiaddr, len(prev)) + currmap := make(map[string]ma.Multiaddr, len(current)) evt := &event.EvtLocalAddressesUpdated{Diffs: true} addrsAdded := false @@ -507,6 +508,9 @@ func (h *BasicHost) makeUpdatedAddrEvent(prev, current []ma.Multiaddr) *event.Ev prevmap[string(addr.Bytes())] = addr } for _, addr := range current { + currmap[string(addr.Bytes())] = addr + } + for _, addr := range currmap { _, ok := prevmap[string(addr.Bytes())] updated := event.UpdatedAddress{Address: addr} if ok { @@ -831,7 +835,7 @@ func (h *BasicHost) Addrs() []ma.Multiaddr { // Make a copy. Consumers can modify the slice elements res := make([]ma.Multiaddr, len(addrs)) copy(res, addrs) - return res + return ma.Unique(res) } // NormalizeMultiaddr returns a multiaddr suitable for equality checks. diff --git a/p2p/host/basic/basic_host_test.go b/p2p/host/basic/basic_host_test.go index 9014e1b4df..1ab98aae9d 100644 --- a/p2p/host/basic/basic_host_test.go +++ b/p2p/host/basic/basic_host_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/libp2p/go-libp2p-testing/race" "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" @@ -248,6 +249,63 @@ func TestAllAddrs(t *testing.T) { require.True(t, ma.Contains(h.AllAddrs(), firstAddr), "should still contain the original addr") } +func TestAllAddrsUnique(t *testing.T) { + if race.WithRace() { + t.Skip("updates addrChangeTickrInterval which might be racy") + } + oldInterval := addrChangeTickrInterval + addrChangeTickrInterval = 100 * time.Millisecond + defer func() { + addrChangeTickrInterval = oldInterval + }() + sendNewAddrs := make(chan struct{}) + opts := HostOpts{ + AddrsFactory: func(addrs []ma.Multiaddr) []ma.Multiaddr { + select { + case <-sendNewAddrs: + return []ma.Multiaddr{ + ma.StringCast("/ip4/1.2.3.4/tcp/1"), + ma.StringCast("/ip4/1.2.3.4/tcp/1"), + ma.StringCast("/ip4/1.2.3.4/tcp/1"), + ma.StringCast("/ip4/1.2.3.4/udp/1/quic-v1"), + ma.StringCast("/ip4/1.2.3.4/udp/1/quic-v1"), + } + default: + return nil + } + }, + } + // no listen addrs + h, err := NewHost(swarmt.GenSwarm(t, swarmt.OptDialOnly), &opts) + require.NoError(t, err) + defer h.Close() + h.Start() + + sub, err := h.EventBus().Subscribe(&event.EvtLocalAddressesUpdated{}) + require.NoError(t, err) + out := make(chan int) + done := make(chan struct{}) + go func() { + cnt := 0 + for { + select { + case <-sub.Out(): + cnt++ + case <-done: + out <- cnt + return + } + } + }() + close(sendNewAddrs) + require.Len(t, h.Addrs(), 2) + require.ElementsMatch(t, []ma.Multiaddr{ma.StringCast("/ip4/1.2.3.4/tcp/1"), ma.StringCast("/ip4/1.2.3.4/udp/1/quic-v1")}, h.Addrs()) + time.Sleep(2*addrChangeTickrInterval + 1*time.Second) // the background loop runs every 5 seconds. Wait for 2x that time. + close(done) + cnt := <-out + require.Equal(t, 1, cnt) +} + // getHostPair gets a new pair of hosts. // The first host initiates the connection to the second host. func getHostPair(t *testing.T) (host.Host, host.Host) {