Skip to content

Commit 579f776

Browse files
fix(p2p): improve reacher address selection and transport filtering (#5347)
1 parent 668bd8b commit 579f776

File tree

10 files changed

+742
-191
lines changed

10 files changed

+742
-191
lines changed

pkg/bzz/address.go

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,12 @@ import (
1515
"errors"
1616
"fmt"
1717
"slices"
18-
"sort"
1918

2019
"github.com/ethereum/go-ethereum/common"
2120
"github.com/ethersphere/bee/v2/pkg/crypto"
2221
"github.com/ethersphere/bee/v2/pkg/swarm"
2322

2423
ma "github.com/multiformats/go-multiaddr"
25-
manet "github.com/multiformats/go-multiaddr/net"
2624
)
2725

2826
var ErrInvalidAddress = errors.New("invalid address")
@@ -222,35 +220,3 @@ func parseMultiaddrs(addrs []string) ([]ma.Multiaddr, error) {
222220
}
223221
return multiAddrs, nil
224222
}
225-
226-
func SelectBestAdvertisedAddress(addrs []ma.Multiaddr, fallback ma.Multiaddr) ma.Multiaddr {
227-
if len(addrs) == 0 {
228-
return fallback
229-
}
230-
231-
hasTCPProtocol := func(addr ma.Multiaddr) bool {
232-
_, err := addr.ValueForProtocol(ma.P_TCP)
233-
return err == nil
234-
}
235-
236-
// Sort addresses to prioritize TCP over other protocols
237-
sort.SliceStable(addrs, func(i, j int) bool {
238-
iTCP := hasTCPProtocol(addrs[i])
239-
jTCP := hasTCPProtocol(addrs[j])
240-
return iTCP && !jTCP
241-
})
242-
243-
for _, addr := range addrs {
244-
if manet.IsPublicAddr(addr) {
245-
return addr
246-
}
247-
}
248-
249-
for _, addr := range addrs {
250-
if !manet.IsPrivateAddr(addr) {
251-
return addr
252-
}
253-
}
254-
255-
return addrs[0]
256-
}

pkg/bzz/address_test.go

Lines changed: 0 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -64,134 +64,6 @@ func TestBzzAddress(t *testing.T) {
6464
}
6565
}
6666

67-
func TestSelectBestAdvertisedAddress(t *testing.T) {
68-
t.Parallel()
69-
70-
mustMultiaddr := func(s string) multiaddr.Multiaddr {
71-
addr, err := multiaddr.NewMultiaddr(s)
72-
if err != nil {
73-
t.Fatalf("failed to create multiaddr %s: %v", s, err)
74-
}
75-
return addr
76-
}
77-
78-
tests := []struct {
79-
name string
80-
addrs []multiaddr.Multiaddr
81-
fallback multiaddr.Multiaddr
82-
expected multiaddr.Multiaddr
83-
}{
84-
{
85-
name: "empty addresses returns fallback",
86-
addrs: []multiaddr.Multiaddr{},
87-
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
88-
expected: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
89-
},
90-
{
91-
name: "nil addresses returns fallback",
92-
addrs: nil,
93-
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
94-
expected: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
95-
},
96-
{
97-
name: "prefers public addresses",
98-
addrs: []multiaddr.Multiaddr{
99-
mustMultiaddr("/ip4/192.168.1.1/tcp/8080"), // private
100-
mustMultiaddr("/ip4/8.8.8.8/tcp/8080"), // public
101-
mustMultiaddr("/ip4/10.0.0.1/tcp/8080"), // private
102-
},
103-
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
104-
expected: mustMultiaddr("/ip4/8.8.8.8/tcp/8080"),
105-
},
106-
{
107-
name: "prefers first public address when multiple exist",
108-
addrs: []multiaddr.Multiaddr{
109-
mustMultiaddr("/ip4/192.168.1.1/tcp/8080"), // private
110-
mustMultiaddr("/ip4/8.8.8.8/tcp/8080"), // public
111-
mustMultiaddr("/ip4/1.1.1.1/tcp/8080"), // public
112-
},
113-
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
114-
expected: mustMultiaddr("/ip4/8.8.8.8/tcp/8080"),
115-
},
116-
{
117-
name: "prefers non-private when no public addresses",
118-
addrs: []multiaddr.Multiaddr{
119-
mustMultiaddr("/ip4/127.0.0.1/tcp/8080"), // loopback
120-
mustMultiaddr("/ip4/192.168.1.1/tcp/8080"), // private but not loopback
121-
mustMultiaddr("/ip4/10.0.0.1/tcp/8080"), // private but not loopback
122-
},
123-
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
124-
expected: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
125-
},
126-
{
127-
name: "returns first address when all are loopback",
128-
addrs: []multiaddr.Multiaddr{
129-
mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
130-
mustMultiaddr("/ip4/127.0.0.1/tcp/8081"),
131-
mustMultiaddr("/ip6/::1/tcp/8080"),
132-
},
133-
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
134-
expected: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
135-
},
136-
{
137-
name: "sorts TCP addresses first",
138-
addrs: []multiaddr.Multiaddr{
139-
mustMultiaddr("/ip4/192.168.1.1/udp/8080"), // UDP
140-
mustMultiaddr("/ip4/1.1.1.1/udp/8080"), // UDP public
141-
mustMultiaddr("/ip4/8.8.8.8/tcp/8080"), // TCP public
142-
},
143-
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
144-
expected: mustMultiaddr("/ip4/8.8.8.8/tcp/8080"),
145-
},
146-
{
147-
name: "handles IPv6 addresses",
148-
addrs: []multiaddr.Multiaddr{
149-
mustMultiaddr("/ip6/::1/tcp/8080"), // loopback
150-
mustMultiaddr("/ip6/2001:db8::1/tcp/8080"), // public IPv6
151-
mustMultiaddr("/ip4/192.168.1.1/tcp/8080"), // private IPv4
152-
},
153-
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
154-
expected: mustMultiaddr("/ip6/2001:db8::1/tcp/8080"),
155-
},
156-
{
157-
name: "handles mixed protocols with preference order",
158-
addrs: []multiaddr.Multiaddr{
159-
mustMultiaddr("/ip4/192.168.1.1/udp/8080"), // private UDP
160-
mustMultiaddr("/ip4/192.168.1.2/tcp/8080"), // private TCP
161-
mustMultiaddr("/ip4/127.0.0.1/tcp/8080"), // loopback TCP
162-
},
163-
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
164-
expected: mustMultiaddr("/ip4/192.168.1.2/tcp/8080"), // first TCP, and it's non-loopback
165-
},
166-
{
167-
name: "single address",
168-
addrs: []multiaddr.Multiaddr{
169-
mustMultiaddr("/ip4/192.168.1.1/tcp/8080"),
170-
},
171-
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
172-
expected: mustMultiaddr("/ip4/192.168.1.1/tcp/8080"),
173-
},
174-
{
175-
name: "websocket addresses",
176-
addrs: []multiaddr.Multiaddr{
177-
mustMultiaddr("/ip4/127.0.0.1/tcp/8080/ws"),
178-
mustMultiaddr("/ip4/8.8.8.8/tcp/8080/ws"), // public with websocket
179-
},
180-
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
181-
expected: mustMultiaddr("/ip4/8.8.8.8/tcp/8080/ws"),
182-
},
183-
}
184-
185-
for _, tt := range tests {
186-
t.Run(tt.name, func(t *testing.T) {
187-
result := bzz.SelectBestAdvertisedAddress(tt.addrs, tt.fallback)
188-
if !result.Equal(tt.expected) {
189-
t.Errorf("SelectBestAdvertisedAddress() = %v, want %v", result, tt.expected)
190-
}
191-
})
192-
}
193-
}
194-
19567
func TestAreUnderlaysEqual(t *testing.T) {
19668
// --- Test Data Initialization ---
19769
addr1 := mustNewMultiaddr(t, "/ip4/127.0.0.1/tcp/8001")

pkg/bzz/transport.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright 2026 The Swarm Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package bzz
6+
7+
import (
8+
"sort"
9+
10+
ma "github.com/multiformats/go-multiaddr"
11+
manet "github.com/multiformats/go-multiaddr/net"
12+
)
13+
14+
// TransportType represents the transport protocol of a multiaddress.
15+
type TransportType int
16+
17+
const (
18+
// TransportUnknown indicates an unrecognized transport.
19+
TransportUnknown TransportType = iota
20+
// TransportTCP indicates plain TCP without WebSocket.
21+
TransportTCP
22+
// TransportWS indicates WebSocket without TLS.
23+
TransportWS
24+
// TransportWSS indicates WebSocket with TLS (secure).
25+
TransportWSS
26+
)
27+
28+
// String returns a string representation of the transport type.
29+
func (t TransportType) String() string {
30+
switch t {
31+
case TransportTCP:
32+
return "tcp"
33+
case TransportWS:
34+
return "ws"
35+
case TransportWSS:
36+
return "wss"
37+
default:
38+
return "unknown"
39+
}
40+
}
41+
42+
// Priority returns the sorting priority for the transport type.
43+
// Lower value = higher priority: TCP (0) > WS (1) > WSS (2) > Unknown (3)
44+
func (t TransportType) Priority() int {
45+
switch t {
46+
case TransportTCP:
47+
return 0
48+
case TransportWS:
49+
return 1
50+
case TransportWSS:
51+
return 2
52+
default:
53+
return 3
54+
}
55+
}
56+
57+
// ClassifyTransport returns the transport type of a multiaddress.
58+
// It distinguishes between plain TCP, WebSocket (WS), and secure WebSocket (WSS).
59+
func ClassifyTransport(addr ma.Multiaddr) TransportType {
60+
if addr == nil {
61+
return TransportUnknown
62+
}
63+
64+
hasProtocol := func(p int) bool {
65+
_, err := addr.ValueForProtocol(p)
66+
return err == nil
67+
}
68+
69+
hasWS := hasProtocol(ma.P_WS)
70+
hasTLS := hasProtocol(ma.P_TLS)
71+
hasTCP := hasProtocol(ma.P_TCP)
72+
73+
switch {
74+
case hasWS && hasTLS:
75+
return TransportWSS
76+
case hasWS:
77+
return TransportWS
78+
case hasTCP:
79+
return TransportTCP
80+
default:
81+
return TransportUnknown
82+
}
83+
}
84+
85+
func SelectBestAdvertisedAddress(addrs []ma.Multiaddr, fallback ma.Multiaddr) ma.Multiaddr {
86+
if len(addrs) == 0 {
87+
return fallback
88+
}
89+
90+
// Sort addresses: first by transport priority (TCP > WS > WSS), preserving relative order
91+
sort.SliceStable(addrs, func(i, j int) bool {
92+
return ClassifyTransport(addrs[i]).Priority() < ClassifyTransport(addrs[j]).Priority()
93+
})
94+
95+
for _, addr := range addrs {
96+
if manet.IsPublicAddr(addr) {
97+
return addr
98+
}
99+
}
100+
101+
for _, addr := range addrs {
102+
if !manet.IsPrivateAddr(addr) {
103+
return addr
104+
}
105+
}
106+
107+
return addrs[0]
108+
}

0 commit comments

Comments
 (0)