Skip to content
New issue

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

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

Already on GitHub? Sign in 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 all 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, err := rh.findPeerAddrs(ctx, pi.ID)
if err != nil {
return fmt.Errorf("failed to find peers: %w", err)
}

// 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++
return m.findPeerFn(ctx, pid)
}

func TestRoutedHostConnectToObsoleteAddresses(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)
}