From 505567b1e88aae42baa1736f4f8ab7e3d7b617b8 Mon Sep 17 00:00:00 2001 From: sukun Date: Fri, 27 Jan 2023 14:06:58 +0530 Subject: [PATCH 1/5] Split libp2p.EnableAutoRelay into 2 functions Provide two specific ways to enable the autorelay subsystem libp2p.EnableAutoRelayWithStaticRelays libp2p.EnableAutoRelayWithPeerSource --- options.go | 29 +++++++ p2p/host/autorelay/autorelay_test.go | 106 +++++++++++++++++------ p2p/host/autorelay/options.go | 11 ++- p2p/protocol/holepunch/holepunch_test.go | 4 +- 4 files changed, 120 insertions(+), 30 deletions(-) diff --git a/options.go b/options.go index 381706f7d2..9167f25125 100644 --- a/options.go +++ b/options.go @@ -4,6 +4,7 @@ package libp2p // those are in defaults.go). import ( + "context" "crypto/rand" "encoding/binary" "errors" @@ -16,6 +17,7 @@ import ( "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/metrics" "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" "github.com/libp2p/go-libp2p/core/pnet" "github.com/libp2p/go-libp2p/core/protocol" @@ -307,6 +309,8 @@ func EnableRelayService(opts ...relayv2.Option) Option { // // This subsystem performs automatic address rewriting to advertise relay addresses when it // detects that the node is publicly unreachable (e.g. behind a NAT). +// +// Deprecated: Use EnableAutoRelayWithStaticRelays or EnableAutoRelayWithPeerSource func EnableAutoRelay(opts ...autorelay.Option) Option { return func(cfg *Config) error { cfg.EnableAutoRelay = true @@ -315,6 +319,31 @@ func EnableAutoRelay(opts ...autorelay.Option) Option { } } +// EnableAutoRelayWithStaticRelays configures libp2p to enable the AutoRelay subsystem using +// the provided slice of relays as relay candidates +// This subsystem performs automatic address rewriting to advertise relay addresses when it +// detects that the node is publicly unreachable (e.g. behind a NAT). +func EnableAutoRelayWithStaticRelays(static []peer.AddrInfo, opts ...autorelay.Option) Option { + return func(cfg *Config) error { + cfg.EnableAutoRelay = true + cfg.AutoRelayOpts = append([]autorelay.Option{autorelay.WithStaticRelays(static)}, opts...) + return nil + } +} + +// EnableAutoRelayWithPeerSource configures libp2p to enable the AutoRelay subsystem using +// the provided peerSource callback to get more relay candidates +// This subsystem performs automatic address rewriting to advertise relay addresses when it +// detects that the node is publicly unreachable (e.g. behind a NAT). +func EnableAutoRelayWithPeerSource(peerSource func(context.Context, int) <-chan peer.AddrInfo, + opts ...autorelay.Option) Option { + return func(cfg *Config) error { + cfg.EnableAutoRelay = true + cfg.AutoRelayOpts = append([]autorelay.Option{autorelay.WithPeerSource(peerSource, 30*time.Second)}, opts...) + return nil + } +} + // ForceReachabilityPublic overrides automatic reachability detection in the AutoNAT subsystem, // forcing the local node to believe it is reachable externally. func ForceReachabilityPublic() Option { diff --git a/p2p/host/autorelay/autorelay_test.go b/p2p/host/autorelay/autorelay_test.go index fd0cdc94d0..143aedc9c2 100644 --- a/p2p/host/autorelay/autorelay_test.go +++ b/p2p/host/autorelay/autorelay_test.go @@ -62,6 +62,27 @@ func newPrivateNode(t *testing.T, opts ...autorelay.Option) host.Host { return h } +func newPrivateNodeWithPeerSource(t *testing.T, peerSource func(context.Context, int) <-chan peer.AddrInfo, + opts ...autorelay.Option) host.Host { + t.Helper() + h, err := libp2p.New( + libp2p.ForceReachabilityPrivate(), + libp2p.EnableAutoRelayWithPeerSource(peerSource, opts...), + ) + require.NoError(t, err) + return h +} + +func newPrivateNodeWithStaticRelays(t *testing.T, static []peer.AddrInfo, opts ...autorelay.Option) host.Host { + t.Helper() + h, err := libp2p.New( + libp2p.ForceReachabilityPrivate(), + libp2p.EnableAutoRelayWithStaticRelays(static, opts...), + ) + require.NoError(t, err) + return h +} + func newRelay(t *testing.T) host.Host { t.Helper() h, err := libp2p.New( @@ -116,8 +137,8 @@ func newRelayV1(t *testing.T) host.Host { func TestSingleCandidate(t *testing.T) { var counter int - h := newPrivateNode(t, - autorelay.WithPeerSource(func(_ context.Context, num int) <-chan peer.AddrInfo { + h := newPrivateNodeWithPeerSource(t, + func(_ context.Context, num int) <-chan peer.AddrInfo { counter++ require.Equal(t, 1, num) peerChan := make(chan peer.AddrInfo, num) @@ -126,10 +147,11 @@ func TestSingleCandidate(t *testing.T) { t.Cleanup(func() { r.Close() }) peerChan <- peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()} return peerChan - }, time.Hour), + }, autorelay.WithMaxCandidates(1), autorelay.WithNumRelays(99999), autorelay.WithBootDelay(0), + autorelay.WithMinInterval(time.Hour), ) defer h.Close() @@ -150,16 +172,17 @@ func TestSingleRelay(t *testing.T) { } close(peerChan) - h := newPrivateNode(t, - autorelay.WithPeerSource(func(_ context.Context, num int) <-chan peer.AddrInfo { + h := newPrivateNodeWithPeerSource(t, + func(_ context.Context, num int) <-chan peer.AddrInfo { require.False(t, called, "expected the peer source callback to only have been called once") called = true require.Equal(t, numCandidates, num) return peerChan - }, time.Hour), + }, autorelay.WithMaxCandidates(numCandidates), autorelay.WithNumRelays(1), autorelay.WithBootDelay(0), + autorelay.WithMinInterval(time.Hour), ) defer h.Close() @@ -177,16 +200,17 @@ func TestPreferRelayV2(t *testing.T) { t.Fatal("used relay v1") }) - h := newPrivateNode(t, - autorelay.WithPeerSource(func(context.Context, int) <-chan peer.AddrInfo { + h := newPrivateNodeWithPeerSource(t, + func(context.Context, int) <-chan peer.AddrInfo { peerChan := make(chan peer.AddrInfo, 1) defer close(peerChan) peerChan <- peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()} return peerChan - }, time.Hour), + }, autorelay.WithMaxCandidates(1), autorelay.WithNumRelays(99999), autorelay.WithBootDelay(0), + autorelay.WithMinInterval(time.Hour), ) defer h.Close() @@ -195,11 +219,12 @@ func TestPreferRelayV2(t *testing.T) { func TestWaitForCandidates(t *testing.T) { peerChan := make(chan peer.AddrInfo) - h := newPrivateNode(t, - autorelay.WithPeerSource(func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, time.Hour), + h := newPrivateNodeWithPeerSource(t, + func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, autorelay.WithMinCandidates(2), autorelay.WithNumRelays(1), autorelay.WithBootDelay(time.Hour), + autorelay.WithMinInterval(time.Hour), ) defer h.Close() @@ -243,19 +268,20 @@ func TestBackoff(t *testing.T) { }) var counter int32 // to be used atomically - h := newPrivateNode(t, - autorelay.WithPeerSource(func(context.Context, int) <-chan peer.AddrInfo { + h := newPrivateNodeWithPeerSource(t, + func(context.Context, int) <-chan peer.AddrInfo { // always return the same node, and make sure we don't try to connect to it too frequently atomic.AddInt32(&counter, 1) peerChan := make(chan peer.AddrInfo, 1) peerChan <- peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()} close(peerChan) return peerChan - }, time.Second), + }, autorelay.WithNumRelays(1), autorelay.WithBootDelay(0), autorelay.WithBackoff(backoff), autorelay.WithClock(cl), + autorelay.WithMinInterval(time.Second), ) defer h.Close() @@ -280,8 +306,8 @@ func TestStaticRelays(t *testing.T) { staticRelays = append(staticRelays, peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()}) } - h := newPrivateNode(t, - autorelay.WithStaticRelays(staticRelays), + h := newPrivateNodeWithStaticRelays(t, + staticRelays, autorelay.WithNumRelays(1), ) defer h.Close() @@ -297,9 +323,10 @@ func TestRelayV1(t *testing.T) { peerChan <- peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()} close(peerChan) - h := newPrivateNode(t, - autorelay.WithPeerSource(func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, time.Hour), + h := newPrivateNodeWithPeerSource(t, + func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, autorelay.WithBootDelay(0), + autorelay.WithMinInterval(time.Hour), ) defer h.Close() @@ -313,10 +340,11 @@ func TestRelayV1(t *testing.T) { peerChan <- peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()} close(peerChan) - h := newPrivateNode(t, - autorelay.WithPeerSource(func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, time.Hour), + h := newPrivateNodeWithPeerSource(t, + func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, autorelay.WithBootDelay(0), autorelay.WithCircuitV1Support(), + autorelay.WithMinInterval(time.Hour), ) defer h.Close() @@ -337,12 +365,13 @@ func TestConnectOnDisconnect(t *testing.T) { peerChan <- peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()} relays = append(relays, r) } - h := newPrivateNode(t, - autorelay.WithPeerSource(func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, time.Hour), + h := newPrivateNodeWithPeerSource(t, + func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, autorelay.WithMinCandidates(1), autorelay.WithMaxCandidates(num), autorelay.WithNumRelays(1), autorelay.WithBootDelay(0), + autorelay.WithMinInterval(time.Hour), ) defer h.Close() @@ -386,19 +415,20 @@ func TestMaxAge(t *testing.T) { peerChans <- peerChan2 close(peerChans) - h := newPrivateNode(t, - autorelay.WithPeerSource(func(context.Context, int) <-chan peer.AddrInfo { + h := newPrivateNodeWithPeerSource(t, + func(context.Context, int) <-chan peer.AddrInfo { c, ok := <-peerChans if !ok { t.Fatal("unexpected call to PeerSource") } return c - }, time.Second), + }, autorelay.WithNumRelays(1), autorelay.WithMaxCandidates(100), autorelay.WithBootDelay(0), autorelay.WithMaxCandidateAge(20*time.Minute), autorelay.WithClock(cl), + autorelay.WithMinInterval(time.Second), ) defer h.Close() @@ -507,8 +537,8 @@ func TestReconnectToStaticRelays(t *testing.T) { staticRelays = append(staticRelays, peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()}) } - h := newPrivateNode(t, - autorelay.WithStaticRelays(staticRelays), + h := newPrivateNodeWithStaticRelays(t, + staticRelays, autorelay.WithClock(cl), ) @@ -532,3 +562,25 @@ func TestReconnectToStaticRelays(t *testing.T) { cl.Add(time.Hour) expectDeltaInAddrUpdated(t, addrUpdated, -1) } + +func TestMinInterval(t *testing.T) { + h := newPrivateNodeWithPeerSource(t, + func(context.Context, int) <-chan peer.AddrInfo { + peerChan := make(chan peer.AddrInfo, 1) + defer close(peerChan) + r1 := newRelay(t) + t.Cleanup(func() { r1.Close() }) + peerChan <- peer.AddrInfo{ID: r1.ID(), Addrs: r1.Addrs()} + return peerChan + }, + autorelay.WithMinCandidates(2), + autorelay.WithNumRelays(1), + autorelay.WithBootDelay(time.Hour), + autorelay.WithMinInterval(1*time.Second), + ) + defer h.Close() + + // The second call to peerSource should happen after 1 second + require.Never(t, func() bool { return numRelays(h) > 0 }, 1*time.Second, 100*time.Millisecond) + require.Eventually(t, func() bool { return numRelays(h) > 0 }, 2*time.Second, 100*time.Millisecond) +} diff --git a/p2p/host/autorelay/options.go b/p2p/host/autorelay/options.go index fd13284979..25cff50738 100644 --- a/p2p/host/autorelay/options.go +++ b/p2p/host/autorelay/options.go @@ -91,7 +91,7 @@ func WithPeerSource(f func(ctx context.Context, numPeers int) <-chan peer.AddrIn return errAlreadyHavePeerSource } c.peerSource = f - c.minInterval = minInterval + WithMinInterval(minInterval)(c) return nil } } @@ -174,3 +174,12 @@ func WithClock(cl clock.Clock) Option { return nil } } + +// WithMinInterval sets the minimum interval after which peerSource callback will be called for more +// candidates even if AutoRelay needs new candidates +func WithMinInterval(interval time.Duration) Option { + return func(c *config) error { + c.minInterval = interval + return nil + } +} diff --git a/p2p/protocol/holepunch/holepunch_test.go b/p2p/protocol/holepunch/holepunch_test.go index 0e9c520188..b71bf2a49f 100644 --- a/p2p/protocol/holepunch/holepunch_test.go +++ b/p2p/protocol/holepunch/holepunch_test.go @@ -418,9 +418,9 @@ func mkHostWithStaticAutoRelay(t *testing.T, relay host.Host) host.Host { h, err := libp2p.New( libp2p.ListenAddrs(ma.StringCast("/ip4/127.0.0.1/tcp/0")), libp2p.EnableRelay(), - libp2p.EnableAutoRelay( + libp2p.EnableAutoRelayWithStaticRelays( + []peer.AddrInfo{pi}, autorelay.WithCircuitV1Support(), - autorelay.WithStaticRelays([]peer.AddrInfo{pi}), ), libp2p.ForceReachabilityPrivate(), libp2p.ResourceManager(&network.NullResourceManager{}), From 11d89018a30cfb96cbf36e5293c8ee18bd4b2fbb Mon Sep 17 00:00:00 2001 From: sukun Date: Mon, 6 Feb 2023 13:42:01 +0530 Subject: [PATCH 2/5] remove minInterval from WithPeerSource --- options.go | 6 ++-- p2p/host/autorelay/autorelay_test.go | 50 ++++++++-------------------- p2p/host/autorelay/options.go | 14 ++++---- 3 files changed, 24 insertions(+), 46 deletions(-) diff --git a/options.go b/options.go index 9167f25125..1961443405 100644 --- a/options.go +++ b/options.go @@ -320,7 +320,7 @@ func EnableAutoRelay(opts ...autorelay.Option) Option { } // EnableAutoRelayWithStaticRelays configures libp2p to enable the AutoRelay subsystem using -// the provided slice of relays as relay candidates +// the provided relays as relay candidates. // This subsystem performs automatic address rewriting to advertise relay addresses when it // detects that the node is publicly unreachable (e.g. behind a NAT). func EnableAutoRelayWithStaticRelays(static []peer.AddrInfo, opts ...autorelay.Option) Option { @@ -332,14 +332,14 @@ func EnableAutoRelayWithStaticRelays(static []peer.AddrInfo, opts ...autorelay.O } // EnableAutoRelayWithPeerSource configures libp2p to enable the AutoRelay subsystem using -// the provided peerSource callback to get more relay candidates +// the provided peerSource callback to get more relay candidates. // This subsystem performs automatic address rewriting to advertise relay addresses when it // detects that the node is publicly unreachable (e.g. behind a NAT). func EnableAutoRelayWithPeerSource(peerSource func(context.Context, int) <-chan peer.AddrInfo, opts ...autorelay.Option) Option { return func(cfg *Config) error { cfg.EnableAutoRelay = true - cfg.AutoRelayOpts = append([]autorelay.Option{autorelay.WithPeerSource(peerSource, 30*time.Second)}, opts...) + cfg.AutoRelayOpts = append([]autorelay.Option{autorelay.WithPeerSource(peerSource)}, opts...) return nil } } diff --git a/p2p/host/autorelay/autorelay_test.go b/p2p/host/autorelay/autorelay_test.go index 143aedc9c2..768c8ec2ad 100644 --- a/p2p/host/autorelay/autorelay_test.go +++ b/p2p/host/autorelay/autorelay_test.go @@ -52,17 +52,7 @@ func usedRelays(h host.Host) []peer.ID { return peers } -func newPrivateNode(t *testing.T, opts ...autorelay.Option) host.Host { - t.Helper() - h, err := libp2p.New( - libp2p.ForceReachabilityPrivate(), - libp2p.EnableAutoRelay(opts...), - ) - require.NoError(t, err) - return h -} - -func newPrivateNodeWithPeerSource(t *testing.T, peerSource func(context.Context, int) <-chan peer.AddrInfo, +func newPrivateNode(t *testing.T, peerSource func(context.Context, int) <-chan peer.AddrInfo, opts ...autorelay.Option) host.Host { t.Helper() h, err := libp2p.New( @@ -137,7 +127,7 @@ func newRelayV1(t *testing.T) host.Host { func TestSingleCandidate(t *testing.T) { var counter int - h := newPrivateNodeWithPeerSource(t, + h := newPrivateNode(t, func(_ context.Context, num int) <-chan peer.AddrInfo { counter++ require.Equal(t, 1, num) @@ -172,7 +162,7 @@ func TestSingleRelay(t *testing.T) { } close(peerChan) - h := newPrivateNodeWithPeerSource(t, + h := newPrivateNode(t, func(_ context.Context, num int) <-chan peer.AddrInfo { require.False(t, called, "expected the peer source callback to only have been called once") called = true @@ -200,7 +190,7 @@ func TestPreferRelayV2(t *testing.T) { t.Fatal("used relay v1") }) - h := newPrivateNodeWithPeerSource(t, + h := newPrivateNode(t, func(context.Context, int) <-chan peer.AddrInfo { peerChan := make(chan peer.AddrInfo, 1) defer close(peerChan) @@ -219,7 +209,7 @@ func TestPreferRelayV2(t *testing.T) { func TestWaitForCandidates(t *testing.T) { peerChan := make(chan peer.AddrInfo) - h := newPrivateNodeWithPeerSource(t, + h := newPrivateNode(t, func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, autorelay.WithMinCandidates(2), autorelay.WithNumRelays(1), @@ -268,7 +258,7 @@ func TestBackoff(t *testing.T) { }) var counter int32 // to be used atomically - h := newPrivateNodeWithPeerSource(t, + h := newPrivateNode(t, func(context.Context, int) <-chan peer.AddrInfo { // always return the same node, and make sure we don't try to connect to it too frequently atomic.AddInt32(&counter, 1) @@ -323,7 +313,7 @@ func TestRelayV1(t *testing.T) { peerChan <- peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()} close(peerChan) - h := newPrivateNodeWithPeerSource(t, + h := newPrivateNode(t, func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, autorelay.WithBootDelay(0), autorelay.WithMinInterval(time.Hour), @@ -340,7 +330,7 @@ func TestRelayV1(t *testing.T) { peerChan <- peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()} close(peerChan) - h := newPrivateNodeWithPeerSource(t, + h := newPrivateNode(t, func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, autorelay.WithBootDelay(0), autorelay.WithCircuitV1Support(), @@ -365,7 +355,7 @@ func TestConnectOnDisconnect(t *testing.T) { peerChan <- peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()} relays = append(relays, r) } - h := newPrivateNodeWithPeerSource(t, + h := newPrivateNode(t, func(context.Context, int) <-chan peer.AddrInfo { return peerChan }, autorelay.WithMinCandidates(1), autorelay.WithMaxCandidates(num), @@ -415,7 +405,7 @@ func TestMaxAge(t *testing.T) { peerChans <- peerChan2 close(peerChans) - h := newPrivateNodeWithPeerSource(t, + h := newPrivateNode(t, func(context.Context, int) <-chan peer.AddrInfo { c, ok := <-peerChans if !ok { @@ -479,18 +469,6 @@ func TestMaxAge(t *testing.T) { require.Contains(t, ids, relays[0]) } -func TestIncorrectInit(t *testing.T) { - // Check if we panic if we do not correctly initialize the autorelay system. - // Common since it's easy to initialize without passing in the correct options: https://github.com/libp2p/go-libp2p/issues/1852 - - defer func() { - if r := recover(); r == nil { - t.Errorf("Expected to panic") - } - }() - _ = newPrivateNode(t) -} - func expectDeltaInAddrUpdated(t *testing.T, addrUpdated event.Subscription, expectedDelta int) { t.Helper() delta := 0 @@ -564,7 +542,7 @@ func TestReconnectToStaticRelays(t *testing.T) { } func TestMinInterval(t *testing.T) { - h := newPrivateNodeWithPeerSource(t, + h := newPrivateNode(t, func(context.Context, int) <-chan peer.AddrInfo { peerChan := make(chan peer.AddrInfo, 1) defer close(peerChan) @@ -576,11 +554,11 @@ func TestMinInterval(t *testing.T) { autorelay.WithMinCandidates(2), autorelay.WithNumRelays(1), autorelay.WithBootDelay(time.Hour), - autorelay.WithMinInterval(1*time.Second), + autorelay.WithMinInterval(500*time.Millisecond), ) defer h.Close() // The second call to peerSource should happen after 1 second - require.Never(t, func() bool { return numRelays(h) > 0 }, 1*time.Second, 100*time.Millisecond) - require.Eventually(t, func() bool { return numRelays(h) > 0 }, 2*time.Second, 100*time.Millisecond) + require.Never(t, func() bool { return numRelays(h) > 0 }, 500*time.Millisecond, 100*time.Millisecond) + require.Eventually(t, func() bool { return numRelays(h) > 0 }, 1*time.Second, 100*time.Millisecond) } diff --git a/p2p/host/autorelay/options.go b/p2p/host/autorelay/options.go index 25cff50738..961ab934b1 100644 --- a/p2p/host/autorelay/options.go +++ b/p2p/host/autorelay/options.go @@ -40,6 +40,7 @@ var defaultConfig = config{ backoff: time.Hour, desiredRelays: 2, maxCandidateAge: 30 * time.Minute, + minInterval: 30 * time.Second, } var ( @@ -65,7 +66,7 @@ func WithStaticRelays(static []peer.AddrInfo) Option { c <- static[i] } return c - }, 30*time.Second)(c) + })(c) WithMinCandidates(len(static))(c) WithMaxCandidates(len(static))(c) WithNumRelays(len(static))(c) @@ -75,23 +76,22 @@ func WithStaticRelays(static []peer.AddrInfo) Option { } // WithPeerSource defines a callback for AutoRelay to query for more relay candidates. -// AutoRelay will call this function when it needs new candidates is connected to the desired number of -// relays, and it has enough candidates (in case we get disconnected from one of the relays). +// AutoRelay will call this function when it needs new candidates because it is not connected +// to the desired number of relays or we get disconnected from one of the relays. // Implementations must send *at most* numPeers, and close the channel when they don't intend to provide // any more peers. // AutoRelay will not call the callback again until the channel is closed. // Implementations should send new peers, but may send peers they sent before. AutoRelay implements // a per-peer backoff (see WithBackoff). -// minInterval is the minimum interval this callback is called with, even if AutoRelay needs new candidates. +// See WithMinInterval for setting the minimum interval between calls to the callback. // The context.Context passed MAY be canceled when AutoRelay feels satisfied, it will be canceled when the node is shutting down. // If the channel is canceled you MUST close the output channel at some point. -func WithPeerSource(f func(ctx context.Context, numPeers int) <-chan peer.AddrInfo, minInterval time.Duration) Option { +func WithPeerSource(f func(ctx context.Context, numPeers int) <-chan peer.AddrInfo) Option { return func(c *config) error { if c.peerSource != nil { return errAlreadyHavePeerSource } c.peerSource = f - WithMinInterval(minInterval)(c) return nil } } @@ -176,7 +176,7 @@ func WithClock(cl clock.Clock) Option { } // WithMinInterval sets the minimum interval after which peerSource callback will be called for more -// candidates even if AutoRelay needs new candidates +// candidates even if AutoRelay needs new candidates. func WithMinInterval(interval time.Duration) Option { return func(c *config) error { c.minInterval = interval From e7fc349ac0bffc1a9b95246d851b165fb6fd44ca Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 6 Feb 2023 12:22:34 -0800 Subject: [PATCH 3/5] Use PeerSource type --- options.go | 13 ++++++------- p2p/host/autorelay/options.go | 27 +++++++++++++++------------ p2p/host/autorelay/relay_finder.go | 4 ++-- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/options.go b/options.go index 1961443405..c97bc9c8ee 100644 --- a/options.go +++ b/options.go @@ -4,7 +4,6 @@ package libp2p // those are in defaults.go). import ( - "context" "crypto/rand" "encoding/binary" "errors" @@ -331,12 +330,12 @@ func EnableAutoRelayWithStaticRelays(static []peer.AddrInfo, opts ...autorelay.O } } -// EnableAutoRelayWithPeerSource configures libp2p to enable the AutoRelay subsystem using -// the provided peerSource callback to get more relay candidates. -// This subsystem performs automatic address rewriting to advertise relay addresses when it -// detects that the node is publicly unreachable (e.g. behind a NAT). -func EnableAutoRelayWithPeerSource(peerSource func(context.Context, int) <-chan peer.AddrInfo, - opts ...autorelay.Option) Option { +// EnableAutoRelayWithPeerSource configures libp2p to enable the AutoRelay +// subsystem using the provided PeerSource callback to get more relay +// candidates. This subsystem performs automatic address rewriting to advertise +// relay addresses when it detects that the node is publicly unreachable (e.g. +// behind a NAT). +func EnableAutoRelayWithPeerSource(peerSource autorelay.PeerSource, opts ...autorelay.Option) Option { return func(cfg *Config) error { cfg.EnableAutoRelay = true cfg.AutoRelayOpts = append([]autorelay.Option{autorelay.WithPeerSource(peerSource)}, opts...) diff --git a/p2p/host/autorelay/options.go b/p2p/host/autorelay/options.go index 961ab934b1..517d7bd705 100644 --- a/p2p/host/autorelay/options.go +++ b/p2p/host/autorelay/options.go @@ -10,9 +10,22 @@ import ( "github.com/benbjohnson/clock" ) +// AutoRelay will call this function when it needs new candidates because it is +// not connected to the desired number of relays or we get disconnected from one +// of the relays. Implementations must send *at most* numPeers, and close the +// channel when they don't intend to provide any more peers. AutoRelay will not +// call the callback again until the channel is closed. Implementations should +// send new peers, but may send peers they sent before. AutoRelay implements a +// per-peer backoff (see WithBackoff). See WithMinInterval for setting the +// minimum interval between calls to the callback. The context.Context passed +// MAY be canceled when AutoRelay feels satisfied, it will be canceled when the +// node is shutting down. If the channel is canceled you MUST close the output +// channel at some point. +type PeerSource func(ctx context.Context, num int) <-chan peer.AddrInfo + type config struct { clock clock.Clock - peerSource func(ctx context.Context, num int) <-chan peer.AddrInfo + peerSource PeerSource // minimum interval used to call the peerSource callback minInterval time.Duration // see WithMinCandidates @@ -76,17 +89,7 @@ func WithStaticRelays(static []peer.AddrInfo) Option { } // WithPeerSource defines a callback for AutoRelay to query for more relay candidates. -// AutoRelay will call this function when it needs new candidates because it is not connected -// to the desired number of relays or we get disconnected from one of the relays. -// Implementations must send *at most* numPeers, and close the channel when they don't intend to provide -// any more peers. -// AutoRelay will not call the callback again until the channel is closed. -// Implementations should send new peers, but may send peers they sent before. AutoRelay implements -// a per-peer backoff (see WithBackoff). -// See WithMinInterval for setting the minimum interval between calls to the callback. -// The context.Context passed MAY be canceled when AutoRelay feels satisfied, it will be canceled when the node is shutting down. -// If the channel is canceled you MUST close the output channel at some point. -func WithPeerSource(f func(ctx context.Context, numPeers int) <-chan peer.AddrInfo) Option { +func WithPeerSource(f PeerSource) Option { return func(c *config) error { if c.peerSource != nil { return errAlreadyHavePeerSource diff --git a/p2p/host/autorelay/relay_finder.go b/p2p/host/autorelay/relay_finder.go index 851d1422e5..7a084db2fa 100644 --- a/p2p/host/autorelay/relay_finder.go +++ b/p2p/host/autorelay/relay_finder.go @@ -59,7 +59,7 @@ type relayFinder struct { ctxCancel context.CancelFunc ctxCancelMx sync.Mutex - peerSource func(context.Context, int) <-chan peer.AddrInfo + peerSource PeerSource candidateFound chan struct{} // receives every time we find a new relay candidate candidateMx sync.Mutex @@ -82,7 +82,7 @@ type relayFinder struct { cachedAddrsExpiry time.Time } -func newRelayFinder(host *basic.BasicHost, peerSource func(context.Context, int) <-chan peer.AddrInfo, conf *config) *relayFinder { +func newRelayFinder(host *basic.BasicHost, peerSource PeerSource, conf *config) *relayFinder { if peerSource == nil { panic("Can not create a new relayFinder. Need a Peer Source fn or a list of static relays. Refer to the documentation around `libp2p.EnableAutoRelay`") } From 40fd4c8dbe185b1003210954168985b9393aaa33 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 6 Feb 2023 16:07:57 -0800 Subject: [PATCH 4/5] Fix typo --- p2p/host/autorelay/options.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/host/autorelay/options.go b/p2p/host/autorelay/options.go index 517d7bd705..830a3ccff7 100644 --- a/p2p/host/autorelay/options.go +++ b/p2p/host/autorelay/options.go @@ -19,7 +19,7 @@ import ( // per-peer backoff (see WithBackoff). See WithMinInterval for setting the // minimum interval between calls to the callback. The context.Context passed // MAY be canceled when AutoRelay feels satisfied, it will be canceled when the -// node is shutting down. If the channel is canceled you MUST close the output +// node is shutting down. If the context is canceled you MUST close the output // channel at some point. type PeerSource func(ctx context.Context, num int) <-chan peer.AddrInfo From 0d698fae7aa04a4025d88b01aaeba8a18e574d29 Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Mon, 6 Feb 2023 16:24:21 -0800 Subject: [PATCH 5/5] Update p2p/host/autorelay/options.go Co-authored-by: Marten Seemann --- p2p/host/autorelay/options.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/host/autorelay/options.go b/p2p/host/autorelay/options.go index 830a3ccff7..f8d1414c52 100644 --- a/p2p/host/autorelay/options.go +++ b/p2p/host/autorelay/options.go @@ -18,7 +18,7 @@ import ( // send new peers, but may send peers they sent before. AutoRelay implements a // per-peer backoff (see WithBackoff). See WithMinInterval for setting the // minimum interval between calls to the callback. The context.Context passed -// MAY be canceled when AutoRelay feels satisfied, it will be canceled when the +// may be canceled when AutoRelay feels satisfied, it will be canceled when the // node is shutting down. If the context is canceled you MUST close the output // channel at some point. type PeerSource func(ctx context.Context, num int) <-chan peer.AddrInfo