Skip to content
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
34 changes: 0 additions & 34 deletions pkg/bzz/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@ import (
"errors"
"fmt"
"slices"
"sort"

"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/v2/pkg/crypto"
"github.com/ethersphere/bee/v2/pkg/swarm"

ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)

var ErrInvalidAddress = errors.New("invalid address")
Expand Down Expand Up @@ -222,35 +220,3 @@ func parseMultiaddrs(addrs []string) ([]ma.Multiaddr, error) {
}
return multiAddrs, nil
}

func SelectBestAdvertisedAddress(addrs []ma.Multiaddr, fallback ma.Multiaddr) ma.Multiaddr {
if len(addrs) == 0 {
return fallback
}

hasTCPProtocol := func(addr ma.Multiaddr) bool {
_, err := addr.ValueForProtocol(ma.P_TCP)
return err == nil
}

// Sort addresses to prioritize TCP over other protocols
sort.SliceStable(addrs, func(i, j int) bool {
iTCP := hasTCPProtocol(addrs[i])
jTCP := hasTCPProtocol(addrs[j])
return iTCP && !jTCP
})

for _, addr := range addrs {
if manet.IsPublicAddr(addr) {
return addr
}
}

for _, addr := range addrs {
if !manet.IsPrivateAddr(addr) {
return addr
}
}

return addrs[0]
}
128 changes: 0 additions & 128 deletions pkg/bzz/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,134 +64,6 @@ func TestBzzAddress(t *testing.T) {
}
}

func TestSelectBestAdvertisedAddress(t *testing.T) {
t.Parallel()

mustMultiaddr := func(s string) multiaddr.Multiaddr {
addr, err := multiaddr.NewMultiaddr(s)
if err != nil {
t.Fatalf("failed to create multiaddr %s: %v", s, err)
}
return addr
}

tests := []struct {
name string
addrs []multiaddr.Multiaddr
fallback multiaddr.Multiaddr
expected multiaddr.Multiaddr
}{
{
name: "empty addresses returns fallback",
addrs: []multiaddr.Multiaddr{},
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
expected: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
},
{
name: "nil addresses returns fallback",
addrs: nil,
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
expected: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
},
{
name: "prefers public addresses",
addrs: []multiaddr.Multiaddr{
mustMultiaddr("/ip4/192.168.1.1/tcp/8080"), // private
mustMultiaddr("/ip4/8.8.8.8/tcp/8080"), // public
mustMultiaddr("/ip4/10.0.0.1/tcp/8080"), // private
},
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
expected: mustMultiaddr("/ip4/8.8.8.8/tcp/8080"),
},
{
name: "prefers first public address when multiple exist",
addrs: []multiaddr.Multiaddr{
mustMultiaddr("/ip4/192.168.1.1/tcp/8080"), // private
mustMultiaddr("/ip4/8.8.8.8/tcp/8080"), // public
mustMultiaddr("/ip4/1.1.1.1/tcp/8080"), // public
},
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
expected: mustMultiaddr("/ip4/8.8.8.8/tcp/8080"),
},
{
name: "prefers non-private when no public addresses",
addrs: []multiaddr.Multiaddr{
mustMultiaddr("/ip4/127.0.0.1/tcp/8080"), // loopback
mustMultiaddr("/ip4/192.168.1.1/tcp/8080"), // private but not loopback
mustMultiaddr("/ip4/10.0.0.1/tcp/8080"), // private but not loopback
},
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
expected: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
},
{
name: "returns first address when all are loopback",
addrs: []multiaddr.Multiaddr{
mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
mustMultiaddr("/ip4/127.0.0.1/tcp/8081"),
mustMultiaddr("/ip6/::1/tcp/8080"),
},
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
expected: mustMultiaddr("/ip4/127.0.0.1/tcp/8080"),
},
{
name: "sorts TCP addresses first",
addrs: []multiaddr.Multiaddr{
mustMultiaddr("/ip4/192.168.1.1/udp/8080"), // UDP
mustMultiaddr("/ip4/1.1.1.1/udp/8080"), // UDP public
mustMultiaddr("/ip4/8.8.8.8/tcp/8080"), // TCP public
},
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
expected: mustMultiaddr("/ip4/8.8.8.8/tcp/8080"),
},
{
name: "handles IPv6 addresses",
addrs: []multiaddr.Multiaddr{
mustMultiaddr("/ip6/::1/tcp/8080"), // loopback
mustMultiaddr("/ip6/2001:db8::1/tcp/8080"), // public IPv6
mustMultiaddr("/ip4/192.168.1.1/tcp/8080"), // private IPv4
},
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
expected: mustMultiaddr("/ip6/2001:db8::1/tcp/8080"),
},
{
name: "handles mixed protocols with preference order",
addrs: []multiaddr.Multiaddr{
mustMultiaddr("/ip4/192.168.1.1/udp/8080"), // private UDP
mustMultiaddr("/ip4/192.168.1.2/tcp/8080"), // private TCP
mustMultiaddr("/ip4/127.0.0.1/tcp/8080"), // loopback TCP
},
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
expected: mustMultiaddr("/ip4/192.168.1.2/tcp/8080"), // first TCP, and it's non-loopback
},
{
name: "single address",
addrs: []multiaddr.Multiaddr{
mustMultiaddr("/ip4/192.168.1.1/tcp/8080"),
},
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
expected: mustMultiaddr("/ip4/192.168.1.1/tcp/8080"),
},
{
name: "websocket addresses",
addrs: []multiaddr.Multiaddr{
mustMultiaddr("/ip4/127.0.0.1/tcp/8080/ws"),
mustMultiaddr("/ip4/8.8.8.8/tcp/8080/ws"), // public with websocket
},
fallback: mustMultiaddr("/ip4/127.0.0.1/tcp/9999"),
expected: mustMultiaddr("/ip4/8.8.8.8/tcp/8080/ws"),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := bzz.SelectBestAdvertisedAddress(tt.addrs, tt.fallback)
if !result.Equal(tt.expected) {
t.Errorf("SelectBestAdvertisedAddress() = %v, want %v", result, tt.expected)
}
})
}
}

func TestAreUnderlaysEqual(t *testing.T) {
// --- Test Data Initialization ---
addr1 := mustNewMultiaddr(t, "/ip4/127.0.0.1/tcp/8001")
Expand Down
108 changes: 108 additions & 0 deletions pkg/bzz/transport.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2026 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bzz

import (
"sort"

ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)

// TransportType represents the transport protocol of a multiaddress.
type TransportType int

const (
// TransportUnknown indicates an unrecognized transport.
TransportUnknown TransportType = iota
// TransportTCP indicates plain TCP without WebSocket.
TransportTCP
// TransportWS indicates WebSocket without TLS.
TransportWS
// TransportWSS indicates WebSocket with TLS (secure).
TransportWSS
)

// String returns a string representation of the transport type.
func (t TransportType) String() string {
switch t {
case TransportTCP:
return "tcp"
case TransportWS:
return "ws"
case TransportWSS:
return "wss"
default:
return "unknown"
}
}

// Priority returns the sorting priority for the transport type.
// Lower value = higher priority: TCP (0) > WS (1) > WSS (2) > Unknown (3)
func (t TransportType) Priority() int {
switch t {
case TransportTCP:
return 0
case TransportWS:
return 1
case TransportWSS:
return 2
default:
return 3
}
}

// ClassifyTransport returns the transport type of a multiaddress.
// It distinguishes between plain TCP, WebSocket (WS), and secure WebSocket (WSS).
func ClassifyTransport(addr ma.Multiaddr) TransportType {
if addr == nil {
return TransportUnknown
}

hasProtocol := func(p int) bool {
_, err := addr.ValueForProtocol(p)
return err == nil
}

hasWS := hasProtocol(ma.P_WS)
hasTLS := hasProtocol(ma.P_TLS)
hasTCP := hasProtocol(ma.P_TCP)

switch {
case hasWS && hasTLS:
return TransportWSS
case hasWS:
return TransportWS
case hasTCP:
return TransportTCP
default:
return TransportUnknown
}
}

func SelectBestAdvertisedAddress(addrs []ma.Multiaddr, fallback ma.Multiaddr) ma.Multiaddr {
if len(addrs) == 0 {
return fallback
}

// Sort addresses: first by transport priority (TCP > WS > WSS), preserving relative order
sort.SliceStable(addrs, func(i, j int) bool {
return ClassifyTransport(addrs[i]).Priority() < ClassifyTransport(addrs[j]).Priority()
})

for _, addr := range addrs {
if manet.IsPublicAddr(addr) {
return addr
}
}

for _, addr := range addrs {
if !manet.IsPrivateAddr(addr) {
return addr
}
}

return addrs[0]
}
Loading
Loading