diff --git a/network/p2p/peerstore/peerstore.go b/network/p2p/peerstore/peerstore.go index 83ee62c09e..dd85c52efc 100644 --- a/network/p2p/peerstore/peerstore.go +++ b/network/p2p/peerstore/peerstore.go @@ -240,14 +240,19 @@ func (ps *PeerStore) ReplacePeerList(addressesThey []*peer.AddrInfo, networkName } for _, info := range addressesThey { + if len(info.Addrs) == 0 { + // skip entries without addresses + continue + } data, _ := ps.Get(info.ID, psmdkAddressData) if data != nil { // we already have this - // update the networkName and role + // update the networkName and role, and refresh the address TTL ad := data.(addressData) ad.networkNames[networkName] = true ad.roles.Add(role) _ = ps.Put(info.ID, psmdkAddressData, ad) + ps.AddAddrs(info.ID, info.Addrs, libp2p.AddressTTL) // do not remove this entry delete(removeItems, info.ID) @@ -273,6 +278,10 @@ func (ps *PeerStore) AddPersistentPeers(addrInfo []*peer.AddrInfo, networkName s defer ps.lock.Unlock() for _, info := range addrInfo { + if len(info.Addrs) == 0 { + // skip entries without addresses + continue + } data, _ := ps.Get(info.ID, psmdkAddressData) if data != nil { // we already have this. @@ -280,6 +289,8 @@ func (ps *PeerStore) AddPersistentPeers(addrInfo []*peer.AddrInfo, networkName s ad := data.(addressData) ad.roles.AddPersistent(role) _ = ps.Put(info.ID, psmdkAddressData, ad) + // refresh the address TTL + ps.AddAddrs(info.ID, info.Addrs, libp2p.PermanentAddrTTL) } else { // we don't have this item. add it. ps.AddAddrs(info.ID, info.Addrs, libp2p.PermanentAddrTTL) diff --git a/network/p2p/peerstore/peerstore_test.go b/network/p2p/peerstore/peerstore_test.go index fcff51d0c6..0bfdb7bcc5 100644 --- a/network/p2p/peerstore/peerstore_test.go +++ b/network/p2p/peerstore/peerstore_test.go @@ -610,3 +610,57 @@ func TestReplacePeerList(t *testing.T) { require.Contains(t, res, info) } } + +// TestReplacePeerListRefreshesAddresses simulates addresses expiring +// by calling ReplacePeerList + UpdateAddrs(newTTL=0) + ReplacePeerList again +func TestReplacePeerListRefreshesAddresses(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + relaysSet := []string{"relay1:4160", "relay2:4160", "relay3:4160"} + infoRelaySet := make([]*peer.AddrInfo, 0) + for _, addr := range relaysSet { + info, err := peerInfoFromDomainPort(addr) + require.NoError(t, err) + infoRelaySet = append(infoRelaySet, info) + } + + ph, err := MakePhonebook(1, time.Millisecond) + require.NoError(t, err) + + ph.ReplacePeerList(infoRelaySet, "default", phonebook.RelayRole) + + for _, info := range infoRelaySet { + addrs := ph.Addrs(info.ID) + require.NotEmpty(t, addrs) + } + + // Simulate addr book GC run by setting zero TTL + for _, info := range infoRelaySet { + ph.UpdateAddrs(info.ID, libp2p.AddressTTL, 0) + } + + // Verify metadata still exists but addresses are now empty + for _, info := range infoRelaySet { + addrs := ph.Addrs(info.ID) + require.Empty(t, addrs) + data, err := ph.Get(info.ID, psmdkAddressData) + require.NoError(t, err) + require.NotNil(t, data) + } + + // Re-add the peer list, which should refresh the addresses + ph.ReplacePeerList(infoRelaySet, "default", phonebook.RelayRole) + + // Verify addresses are restored + for _, info := range infoRelaySet { + addrs := ph.Addrs(info.ID) + require.NotEmpty(t, addrs) + } + + result := ph.GetAddresses(len(relaysSet), phonebook.RelayRole) + require.Equal(t, len(relaysSet), len(result)) + for _, info := range result { + require.NotEmpty(t, info.Addrs) + } +}