Skip to content

Commit 53d66ba

Browse files
sbackend123janosgacevicljubisamartinconic
authored
feat: multiple underlay addresses (#5204)
Co-authored-by: Janoš Guljaš <[email protected]> Co-authored-by: Ljubisa Gacevic <[email protected]> Co-authored-by: Calin Martinconi <[email protected]>
1 parent 4deaa1e commit 53d66ba

27 files changed

+1387
-321
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ require (
3232
github.com/multiformats/go-multiaddr-dns v0.3.1
3333
github.com/multiformats/go-multihash v0.2.3
3434
github.com/multiformats/go-multistream v0.5.0
35+
github.com/multiformats/go-varint v0.0.7
3536
github.com/opentracing/opentracing-go v1.2.0
3637
github.com/prometheus/client_golang v1.21.1
3738
github.com/spf13/afero v1.6.0
@@ -130,7 +131,6 @@ require (
130131
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
131132
github.com/multiformats/go-multibase v0.2.0 // indirect
132133
github.com/multiformats/go-multicodec v0.9.0 // indirect
133-
github.com/multiformats/go-varint v0.0.7 // indirect
134134
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
135135
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
136136
github.com/opencontainers/runtime-spec v1.2.0 // indirect

pkg/addressbook/addressbook_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func run(t *testing.T, f bookFunc) {
4747
t.Fatal(err)
4848
}
4949

50-
bzzAddr, err := bzz.NewAddress(crypto.NewDefaultSigner(pk), multiaddr, addr1, 1, trxHash)
50+
bzzAddr, err := bzz.NewAddress(crypto.NewDefaultSigner(pk), []ma.Multiaddr{multiaddr}, addr1, 1, trxHash)
5151
if err != nil {
5252
t.Fatal(err)
5353
}

pkg/api/peer.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ func (s *Service) peerConnectHandler(w http.ResponseWriter, r *http.Request) {
3131
return
3232
}
3333

34-
bzzAddr, err := s.p2p.Connect(r.Context(), paths.MultiAddress)
34+
bzzAddr, err := s.p2p.Connect(r.Context(), []multiaddr.Multiaddr{paths.MultiAddress})
35+
3536
if err != nil {
3637
logger.Debug("p2p connect failed", "addresses", paths.MultiAddress, "error", err)
3738
logger.Error(nil, "p2p connect failed", "addresses", paths.MultiAddress)

pkg/api/peer_test.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,17 @@ func TestConnect(t *testing.T) {
4545
t.Fatal(err)
4646
}
4747

48-
bzzAddress, err := bzz.NewAddress(crypto.NewDefaultSigner(privateKey), underlama, overlay, 0, nil)
48+
bzzAddress, err := bzz.NewAddress(crypto.NewDefaultSigner(privateKey), []ma.Multiaddr{underlama}, overlay, 0, nil)
4949
if err != nil {
5050
t.Fatal(err)
5151
}
5252

5353
testServer, _, _, _ := newTestServer(t, testServerOptions{
54-
P2P: mock.New(mock.WithConnectFunc(func(ctx context.Context, addr ma.Multiaddr) (*bzz.Address, error) {
55-
if addr.String() == errorUnderlay {
56-
return nil, testErr
54+
P2P: mock.New(mock.WithConnectFunc(func(ctx context.Context, addrs []ma.Multiaddr) (*bzz.Address, error) {
55+
for _, addr := range addrs {
56+
if addr.String() == errorUnderlay {
57+
return nil, testErr
58+
}
5759
}
5860
return bzzAddress, nil
5961
})),
@@ -81,9 +83,11 @@ func TestConnect(t *testing.T) {
8183
t.Run("error - add peer", func(t *testing.T) {
8284
t.Parallel()
8385
testServer, _, _, _ := newTestServer(t, testServerOptions{
84-
P2P: mock.New(mock.WithConnectFunc(func(ctx context.Context, addr ma.Multiaddr) (*bzz.Address, error) {
85-
if addr.String() == errorUnderlay {
86-
return nil, testErr
86+
P2P: mock.New(mock.WithConnectFunc(func(ctx context.Context, addrs []ma.Multiaddr) (*bzz.Address, error) {
87+
for _, addr := range addrs {
88+
if addr.String() == errorUnderlay {
89+
return nil, testErr
90+
}
8791
}
8892
return bzzAddress, nil
8993
})),

pkg/bzz/address.go

Lines changed: 115 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@ import (
1414
"encoding/json"
1515
"errors"
1616
"fmt"
17+
"slices"
18+
"sort"
1719

1820
"github.com/ethereum/go-ethereum/common"
1921
"github.com/ethersphere/bee/v2/pkg/crypto"
2022
"github.com/ethersphere/bee/v2/pkg/swarm"
2123

2224
ma "github.com/multiformats/go-multiaddr"
25+
manet "github.com/multiformats/go-multiaddr/net"
2326
)
2427

2528
var ErrInvalidAddress = errors.New("invalid address")
@@ -28,33 +31,31 @@ var ErrInvalidAddress = errors.New("invalid address")
2831
// It consists of a peers underlay (physical) address, overlay (topology) address and signature.
2932
// Signature is used to verify the `Overlay/Underlay` pair, as it is based on `underlay|networkID`, signed with the public key of Overlay address
3033
type Address struct {
31-
Underlay ma.Multiaddr
34+
Underlays []ma.Multiaddr
3235
Overlay swarm.Address
3336
Signature []byte
3437
Nonce []byte
3538
EthereumAddress []byte
3639
}
3740

3841
type addressJSON struct {
39-
Overlay string `json:"overlay"`
40-
Underlay string `json:"underlay"`
41-
Signature string `json:"signature"`
42-
Nonce string `json:"transaction"`
42+
Overlay string `json:"overlay"`
43+
Underlay string `json:"underlay"` // For backward compatibility
44+
Underlays []string `json:"underlays"`
45+
Signature string `json:"signature"`
46+
Nonce string `json:"transaction"`
4347
}
4448

45-
func NewAddress(signer crypto.Signer, underlay ma.Multiaddr, overlay swarm.Address, networkID uint64, nonce []byte) (*Address, error) {
46-
underlayBinary, err := underlay.MarshalBinary()
47-
if err != nil {
48-
return nil, err
49-
}
49+
func NewAddress(signer crypto.Signer, underlays []ma.Multiaddr, overlay swarm.Address, networkID uint64, nonce []byte) (*Address, error) {
50+
underlaysBinary := SerializeUnderlays(underlays)
5051

51-
signature, err := signer.Sign(generateSignData(underlayBinary, overlay.Bytes(), networkID))
52+
signature, err := signer.Sign(generateSignData(underlaysBinary, overlay.Bytes(), networkID))
5253
if err != nil {
5354
return nil, err
5455
}
5556

5657
return &Address{
57-
Underlay: underlay,
58+
Underlays: underlays,
5859
Overlay: overlay,
5960
Signature: signature,
6061
Nonce: nonce,
@@ -77,8 +78,13 @@ func ParseAddress(underlay, overlay, signature, nonce []byte, validateOverlay bo
7778
}
7879
}
7980

80-
multiUnderlay, err := ma.NewMultiaddrBytes(underlay)
81+
multiUnderlays, err := DeserializeUnderlays(underlay)
8182
if err != nil {
83+
return nil, fmt.Errorf("deserialize underlays: %w: %w", ErrInvalidAddress, err)
84+
}
85+
86+
if len(multiUnderlays) == 0 {
87+
// no underlays sent
8288
return nil, ErrInvalidAddress
8389
}
8490

@@ -88,7 +94,7 @@ func ParseAddress(underlay, overlay, signature, nonce []byte, validateOverlay bo
8894
}
8995

9096
return &Address{
91-
Underlay: multiUnderlay,
97+
Underlays: multiUnderlays,
9298
Overlay: swarm.NewAddress(overlay),
9399
Signature: signature,
94100
Nonce: nonce,
@@ -109,21 +115,49 @@ func (a *Address) Equal(b *Address) bool {
109115
return a == b
110116
}
111117

112-
return a.Overlay.Equal(b.Overlay) && multiaddrEqual(a.Underlay, b.Underlay) && bytes.Equal(a.Signature, b.Signature) && bytes.Equal(a.Nonce, b.Nonce)
118+
return a.Overlay.Equal(b.Overlay) && AreUnderlaysEqual(a.Underlays, b.Underlays) && bytes.Equal(a.Signature, b.Signature) && bytes.Equal(a.Nonce, b.Nonce)
113119
}
114120

115-
func multiaddrEqual(a, b ma.Multiaddr) bool {
116-
if a == nil || b == nil {
117-
return a == b
121+
func AreUnderlaysEqual(a, b []ma.Multiaddr) bool {
122+
if len(a) != len(b) {
123+
return false
118124
}
119125

120-
return a.Equal(b)
126+
used := make([]bool, len(b))
127+
for i := range len(a) {
128+
found := false
129+
for j := range len(b) {
130+
if used[j] {
131+
continue
132+
}
133+
if a[i].Equal(b[j]) {
134+
used[j] = true
135+
found = true
136+
break
137+
}
138+
}
139+
if !found {
140+
return false
141+
}
142+
}
143+
return true
121144
}
122145

123146
func (a *Address) MarshalJSON() ([]byte, error) {
147+
if len(a.Underlays) == 0 {
148+
return nil, fmt.Errorf("no underlays for %s", a.Overlay)
149+
}
150+
151+
// select the underlay address for backward compatibility
152+
var underlay string
153+
if v := SelectBestAdvertisedAddress(a.Underlays, nil); v != nil {
154+
underlay = v.String()
155+
}
156+
124157
return json.Marshal(&addressJSON{
125158
Overlay: a.Overlay.String(),
126-
Underlay: a.Underlay.String(),
159+
Underlay: underlay,
160+
Underlays: a.underlaysAsStrings(),
127161
Signature: base64.StdEncoding.EncodeToString(a.Signature),
128162
Nonce: common.Bytes2Hex(a.Nonce),
129163
})
@@ -143,23 +177,80 @@ func (a *Address) UnmarshalJSON(b []byte) error {
143177

144178
a.Overlay = addr
145179

146-
m, err := ma.NewMultiaddr(v.Underlay)
180+
// append the underlay for backward compatibility
181+
if !slices.Contains(v.Underlays, v.Underlay) {
182+
v.Underlays = append(v.Underlays, v.Underlay)
183+
}
184+
185+
multiaddrs, err := parseMultiaddrs(v.Underlays)
147186
if err != nil {
148187
return err
149188
}
150189

151-
a.Underlay = m
190+
a.Underlays = multiaddrs
152191
a.Signature, err = base64.StdEncoding.DecodeString(v.Signature)
153192
a.Nonce = common.Hex2Bytes(v.Nonce)
154193
return err
155194
}
156195

157196
func (a *Address) String() string {
158-
return fmt.Sprintf("[Underlay: %v, Overlay %v, Signature %x, Transaction %x]", a.Underlay, a.Overlay, a.Signature, a.Nonce)
197+
return fmt.Sprintf("[Underlay: %v, Overlay %v, Signature %x, Transaction %x]", a.underlaysAsStrings(), a.Overlay, a.Signature, a.Nonce)
159198
}
160199

161200
// ShortString returns shortened versions of bzz address in a format: [Overlay, Underlay]
162201
// It can be used for logging
163202
func (a *Address) ShortString() string {
164-
return fmt.Sprintf("[Overlay: %s, Underlay: %s]", a.Overlay.String(), a.Underlay.String())
203+
return fmt.Sprintf("[Overlay: %s, Underlays: %v]", a.Overlay.String(), a.underlaysAsStrings())
204+
}
205+
206+
func (a *Address) underlaysAsStrings() []string {
207+
underlays := make([]string, len(a.Underlays))
208+
for i, underlay := range a.Underlays {
209+
underlays[i] = underlay.String()
210+
}
211+
return underlays
212+
}
213+
214+
func parseMultiaddrs(addrs []string) ([]ma.Multiaddr, error) {
215+
multiAddrs := make([]ma.Multiaddr, len(addrs))
216+
for i, addr := range addrs {
217+
multiAddr, err := ma.NewMultiaddr(addr)
218+
if err != nil {
219+
return nil, err
220+
}
221+
multiAddrs[i] = multiAddr
222+
}
223+
return multiAddrs, nil
224+
}
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]
165256
}

0 commit comments

Comments
 (0)