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

webrtcprivate: add transport integration tests #2601

Open
wants to merge 8 commits into
base: webrtcprivate/transport
Choose a base branch
from
4 changes: 4 additions & 0 deletions config/config.go
Expand Up @@ -395,6 +395,10 @@ func (cfg *Config) NewNode() (host.Host, error) {
cfg.AutoRelayOpts = append(mtOpts, cfg.AutoRelayOpts...)
}

if cfg.WebRTCPrivate {
cfg.AutoRelayOpts = append(cfg.AutoRelayOpts, autorelay.WithWebRTCSupport())
}

ar, err = autorelay.NewAutoRelay(h, cfg.AutoRelayOpts...)
if err != nil {
return nil, err
Expand Down
113 changes: 113 additions & 0 deletions p2p/host/autorelay/autorelay_test.go
Expand Up @@ -517,3 +517,116 @@ func TestNoBusyLoop0MinInterval(t *testing.T) {
val := atomic.LoadUint64(&calledTimes)
require.Less(t, val, uint64(2))
}

func TestRelayAddrs(t *testing.T) {

t.Run("WithoutWebRTC", func(t *testing.T) {
const numCandidates = 3
var called bool
peerChan := make(chan peer.AddrInfo, numCandidates)
for i := 0; i < numCandidates; i++ {
r := newRelay(t)
t.Cleanup(func() { r.Close() })
peerChan <- peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()}
}
close(peerChan)

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
require.Equal(t, numCandidates, num)
return peerChan
},
autorelay.WithMaxCandidates(numCandidates),
autorelay.WithNumRelays(1),
autorelay.WithBootDelay(0),
autorelay.WithMinInterval(time.Hour),
)
defer h.Close()

require.Eventually(
t,
func() bool {
if numRelays(h) <= 0 {
return false
}
addrs := h.Addrs()
var foundCircuit, foundWebRTC bool
for _, addr := range addrs {
_, cerr := addr.ValueForProtocol(ma.P_CIRCUIT)
_, werr := addr.ValueForProtocol(ma.P_WEBRTC)
foundCircuit = foundCircuit || cerr == nil
foundWebRTC = foundWebRTC || (cerr == nil && werr == nil)
}
return foundCircuit && !foundWebRTC
},
5*time.Second,
100*time.Millisecond,
)
require.Never(
t,
func() bool {
if numRelays(h) <= 0 {
return false
}
addrs := h.Addrs()
var foundWebRTC bool
for _, addr := range addrs {
_, cerr := addr.ValueForProtocol(ma.P_CIRCUIT)
_, werr := addr.ValueForProtocol(ma.P_WEBRTC)
foundWebRTC = foundWebRTC || (cerr == nil && werr == nil)
}
return foundWebRTC
},
500*time.Millisecond,
100*time.Millisecond)
})

t.Run("WithWebRTC", func(t *testing.T) {
const numCandidates = 3
var called bool
peerChan := make(chan peer.AddrInfo, numCandidates)
for i := 0; i < numCandidates; i++ {
r := newRelay(t)
t.Cleanup(func() { r.Close() })
peerChan <- peer.AddrInfo{ID: r.ID(), Addrs: r.Addrs()}
}
close(peerChan)

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
require.Equal(t, numCandidates, num)
return peerChan
},
autorelay.WithMaxCandidates(numCandidates),
autorelay.WithNumRelays(1),
autorelay.WithBootDelay(0),
autorelay.WithMinInterval(time.Hour),
autorelay.WithWebRTCSupport(),
)
defer h.Close()

require.Eventually(
t,
func() bool {
if numRelays(h) <= 0 {
return false
}
addrs := h.Addrs()
var foundCircuit, foundWebRTC bool
for _, addr := range addrs {
_, cerr := addr.ValueForProtocol(ma.P_CIRCUIT)
_, werr := addr.ValueForProtocol(ma.P_WEBRTC)
foundCircuit = foundCircuit || cerr == nil
foundWebRTC = foundWebRTC || (cerr == nil && werr == nil)
}
return foundCircuit && foundWebRTC
},
5*time.Second,
100*time.Millisecond,
)
})
}
11 changes: 11 additions & 0 deletions p2p/host/autorelay/options.go
Expand Up @@ -42,6 +42,9 @@ type config struct {
setMinCandidates bool
// see WithMetricsTracer
metricsTracer MetricsTracer

// see WithWebRTCSupport
webRTCSupport bool
}

var defaultConfig = config{
Expand Down Expand Up @@ -231,3 +234,11 @@ func WithMetricsTracer(mt MetricsTracer) Option {
return nil
}
}

// WithWebRTCSupport configures autorelay to advertise webrtc addresses from host
func WithWebRTCSupport() Option {
return func(c *config) error {
c.webRTCSupport = true
return nil
}
}
18 changes: 18 additions & 0 deletions p2p/host/autorelay/relay_finder.go
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
circuitv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client"
circuitv2_proto "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto"
libp2pwebrtcprivate "github.com/libp2p/go-libp2p/p2p/transport/webrtcprivate"

ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
Expand Down Expand Up @@ -737,9 +738,15 @@ func (rf *relayFinder) relayAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
addrs := cleanupAddressSet(rf.host.Peerstore().Addrs(p))
relayAddrCnt += len(addrs)
circuit := ma.StringCast(fmt.Sprintf("/p2p/%s/p2p-circuit", p))
webrtc := libp2pwebrtcprivate.WebRTCAddr
for _, addr := range addrs {
pub := addr.Encapsulate(circuit)
raddrs = append(raddrs, pub)
if rf.conf.webRTCSupport && isBrowserDialableAddr(addr) {
waddr := pub.Encapsulate(webrtc)
raddrs = append(raddrs, waddr)
relayAddrCnt++
}
}
}

Expand Down Expand Up @@ -808,3 +815,14 @@ func (rf *relayFinder) resetMetrics() {
rf.metricsTracer.RelayAddressCount(0)
rf.metricsTracer.ScheduledWorkUpdated(&scheduledWorkTimes{})
}

var browserProtocols []int = []int{ma.P_WEBTRANSPORT, ma.P_WEBRTC_DIRECT, ma.P_WS, ma.P_WSS}

func isBrowserDialableAddr(addr ma.Multiaddr) bool {
for _, p := range browserProtocols {
if _, err := addr.ValueForProtocol(p); err == nil {
return true
}
}
return false
}
22 changes: 5 additions & 17 deletions p2p/test/swarm/swarm_test.go
Expand Up @@ -276,39 +276,27 @@ func TestDialPeerWebRTC(t *testing.T) {
_, err = client.Reserve(context.Background(), h2, relay1info)
require.NoError(t, err)

webrtcAddr := ma.StringCast(relay1info.Addrs[0].String() + "/p2p/" + relay1info.ID.String() + "/p2p-circuit/webrtc/p2p/" + h2.ID().String())
relayAddrs := ma.StringCast(relay1info.Addrs[0].String() + "/p2p/" + relay1info.ID.String() + "/p2p-circuit/p2p/" + h2.ID().String())
webrtcAddr := ma.StringCast(relay1info.Addrs[0].String() + "/p2p/" + relay1info.ID.String() + "/p2p-circuit/webrtc")
relayAddrs := ma.StringCast(relay1info.Addrs[0].String() + "/p2p/" + relay1info.ID.String() + "/p2p-circuit/")

h1.Peerstore().AddAddrs(h2.ID(), []ma.Multiaddr{webrtcAddr, relayAddrs}, peerstore.TempAddrTTL)

// swarm.DialPeer should connect over transient connections
conn1, err := h1.Network().DialPeer(context.Background(), h2.ID())
require.NoError(t, err)
require.NotNil(t, conn1)
require.Condition(t, func() bool {
_, err1 := conn1.RemoteMultiaddr().ValueForProtocol(ma.P_CIRCUIT)
_, err2 := conn1.RemoteMultiaddr().ValueForProtocol(ma.P_WEBRTC)
return err1 == nil && err2 != nil
})
require.Equal(t, conn1.RemoteMultiaddr(), relayAddrs)

// should connect to webrtc address
ctx := network.WithForceDirectDial(context.Background(), "test")
conn, err := h1.Network().DialPeer(ctx, h2.ID())
require.NoError(t, err)
require.NotNil(t, conn)
require.Condition(t, func() bool {
_, err1 := conn.RemoteMultiaddr().ValueForProtocol(ma.P_CIRCUIT)
_, err2 := conn.RemoteMultiaddr().ValueForProtocol(ma.P_WEBRTC)
return err1 != nil && err2 == nil
})
require.Equal(t, conn.RemoteMultiaddr(), webrtcAddr)

done := make(chan struct{})
h2.SetStreamHandler("test-addr", func(s network.Stream) {
s.Conn().LocalMultiaddr()
_, err1 := conn.RemoteMultiaddr().ValueForProtocol(ma.P_CIRCUIT)
assert.Error(t, err1)
_, err2 := conn.RemoteMultiaddr().ValueForProtocol(ma.P_WEBRTC)
assert.NoError(t, err2)
require.Equal(t, conn.RemoteMultiaddr(), webrtcAddr)
s.Reset()
close(done)
})
Expand Down