Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

routed host: search for new multi addresses upon connect failure #1835

Merged
merged 4 commits into from
Oct 31, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions core/peerstore/peerstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ var (
// TempAddrTTL is the ttl used for a short lived address.
TempAddrTTL = time.Minute * 2

// ProviderAddrTTL is the TTL of an address we've received from a provider.
// This is also a temporary address, but lasts longer. After this expires,
// the records we return will require an extra lookup.
ProviderAddrTTL = time.Minute * 30

// RecentlyConnectedAddrTTL is used when we recently connected to a peer.
// It means that we are reasonably certain of the peer's address.
RecentlyConnectedAddrTTL = time.Minute * 30
Expand Down
34 changes: 33 additions & 1 deletion p2p/host/routed/routed.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,39 @@ func (rh *RoutedHost) Connect(ctx context.Context, pi peer.AddrInfo) error {

// if we're here, we got some addrs. let's use our wrapped host to connect.
pi.Addrs = addrs
return rh.host.Connect(ctx, pi)
err := rh.host.Connect(ctx, pi)
if err != nil {
// We couldn't connect. Let's check if we have the most
// up-to-date addresses for the given peer. If there
// are addresses we didn't know about previously, we
// try to connect again.
newAddrs, addrErr := rh.findPeerAddrs(ctx, pi.ID)
if addrErr != nil {
return err // return outer error
}

// Build lookup map
lookup := make(map[ma.Multiaddr]struct{}, len(addrs))
for _, addr := range addrs {
lookup[addr] = struct{}{}
}

// if there's any address that's not in the previous set
// of addresses, try to connect again. If all addresses
// where known previously we return the original error.
for _, newAddr := range newAddrs {
if _, found := lookup[newAddr]; found {
continue
}

pi.Addrs = newAddrs
return rh.host.Connect(ctx, pi)
}

return err
}

return nil
}

func (rh *RoutedHost) findPeerAddrs(ctx context.Context, id peer.ID) ([]ma.Multiaddr, error) {
Expand Down
73 changes: 73 additions & 0 deletions p2p/host/routed/routed_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package routedhost

import (
"context"
"testing"

"github.com/libp2p/go-libp2p/core/peer"
basic "github.com/libp2p/go-libp2p/p2p/host/basic"
swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing"
ma "github.com/multiformats/go-multiaddr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var _ Routing = (*mockRouting)(nil)

type mockRouting struct {
callCount int
findPeerFn func(ctx context.Context, id peer.ID) (peer.AddrInfo, error)
}

func (m *mockRouting) FindPeer(ctx context.Context, pid peer.ID) (peer.AddrInfo, error) {
m.callCount += 1
return m.findPeerFn(ctx, pid)
}

func TestRoutedHost_Connect_obsoleteAddresses(t *testing.T) {
ctx := context.Background()

h1, err := basic.NewHost(swarmt.GenSwarm(t), nil)
require.NoError(t, err)
defer h1.Close()

h2, err := basic.NewHost(swarmt.GenSwarm(t), nil)
require.NoError(t, err)
defer h2.Close()

// construct a wrong multi address for host 2, so that
// the initial connection attempt will fail
// (we have obsolete, old multi address information)
maddr, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1234")
require.NoError(t, err)

// assemble the AddrInfo struct to use for the connection attempt
pi := peer.AddrInfo{
ID: h2.ID(),
Addrs: []ma.Multiaddr{maddr},
}

// Build mock routing module and replace the FindPeer function.
// Now, that function will return the correct multi addresses for host 2
// (we have fetched the most up-to-date data from the DHT)
mr := &mockRouting{
findPeerFn: func(ctx context.Context, pi peer.ID) (peer.AddrInfo, error) {
return peer.AddrInfo{
ID: h2.ID(),
Addrs: h2.Addrs(),
}, nil
},
}

// Build routed host
rh := Wrap(h1, mr)

// Try to connect
err = rh.Connect(ctx, pi)

// Connection establishment should have worked without an error
assert.NoError(t, err)

// The mocked FindPeer function should have been called
assert.Equal(t, 1, mr.callCount)
}