Skip to content

Commit 1f3f73e

Browse files
authored
fix(libs/header/p2p): shuffle trusted peers before sending a request (#1903)
1 parent e2c640a commit 1f3f73e

File tree

2 files changed

+36
-17
lines changed

2 files changed

+36
-17
lines changed

p2p/exchange.go

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"math/rand"
99
"sort"
10+
"time"
1011

1112
logging "github.com/ipfs/go-log/v2"
1213
"github.com/libp2p/go-libp2p/core/host"
@@ -34,7 +35,7 @@ type Exchange[H header.Header] struct {
3435
protocolID protocol.ID
3536
host host.Host
3637

37-
trustedPeers peer.IDSlice
38+
trustedPeers func() peer.IDSlice
3839
peerTracker *peerTracker
3940

4041
Params ClientParameters
@@ -58,23 +59,29 @@ func NewExchange[H header.Header](
5859
return nil, err
5960
}
6061

61-
return &Exchange[H]{
62-
host: host,
63-
protocolID: protocolID(params.networkID),
64-
trustedPeers: peers,
62+
ex := &Exchange[H]{
63+
host: host,
64+
protocolID: protocolID(params.networkID),
6565
peerTracker: newPeerTracker(
6666
host,
6767
connGater,
6868
),
6969
Params: params,
70-
}, nil
70+
}
71+
72+
ex.trustedPeers = func() peer.IDSlice {
73+
return shufflePeers(peers)
74+
}
75+
return ex, nil
7176
}
7277

7378
func (ex *Exchange[H]) Start(context.Context) error {
7479
ex.ctx, ex.cancel = context.WithCancel(context.Background())
7580
log.Infow("client: starting client", "protocol ID", ex.protocolID)
7681

77-
for _, p := range ex.trustedPeers {
82+
trustedPeers := ex.trustedPeers()
83+
84+
for _, p := range trustedPeers {
7885
// Try to pre-connect to trusted peers.
7986
// We don't really care if we succeed at this point
8087
// and just need any peers in the peerTracker asap
@@ -116,8 +123,9 @@ func (ex *Exchange[H]) Head(ctx context.Context) (H, error) {
116123
headerCh = make(chan H)
117124
)
118125

126+
trustedPeers := ex.trustedPeers()
119127
// request head from each trusted peer
120-
for _, from := range ex.trustedPeers {
128+
for _, from := range trustedPeers {
121129
go func(from peer.ID) {
122130
// request ensures that the result slice will have at least one Header
123131
headers, err := ex.request(ctx, from, req)
@@ -131,9 +139,9 @@ func (ex *Exchange[H]) Head(ctx context.Context) (H, error) {
131139
}(from)
132140
}
133141

134-
result := make([]H, 0, len(ex.trustedPeers))
142+
result := make([]H, 0, len(trustedPeers))
135143
LOOP:
136-
for range ex.trustedPeers {
144+
for range trustedPeers {
137145
select {
138146
case h := <-headerCh:
139147
if !h.IsZero() {
@@ -232,23 +240,22 @@ func (ex *Exchange[H]) performRequest(
232240
return make([]H, 0), nil
233241
}
234242

243+
trustedPeers := ex.trustedPeers()
235244
// TODO: Move this check to constructor(#1671)
236-
if len(ex.trustedPeers) == 0 {
245+
if len(trustedPeers) == 0 {
237246
return nil, fmt.Errorf("no trusted peers")
238247
}
239248
var reqErr error
240-
for i := 0; i < len(ex.trustedPeers); i++ {
241-
//nolint:gosec // G404: Use of weak random number generator
242-
idx := rand.Intn(len(ex.trustedPeers))
243249

250+
for _, peer := range trustedPeers {
244251
ctx, cancel := context.WithTimeout(ctx, ex.Params.TrustedPeersRequestTimeout)
245-
h, err := ex.request(ctx, ex.trustedPeers[idx], req)
252+
h, err := ex.request(ctx, peer, req)
246253
cancel()
247254
switch err {
248255
default:
249256
reqErr = err
250257
log.Debugw("requesting header from trustedPeer failed",
251-
"trustedPeer", ex.trustedPeers[idx], "err", err)
258+
"trustedPeer", peer, "err", err)
252259
continue
253260
case context.Canceled, context.DeadlineExceeded, nil:
254261
return h, err
@@ -295,6 +302,18 @@ func (ex *Exchange[H]) request(
295302
return headers, nil
296303
}
297304

305+
// shufflePeers changes the order of trusted peers.
306+
func shufflePeers(peers peer.IDSlice) peer.IDSlice {
307+
tpeers := make(peer.IDSlice, len(peers))
308+
copy(tpeers, peers)
309+
//nolint:gosec // G404: Use of weak random number generator
310+
rand.New(rand.NewSource(time.Now().UnixNano())).Shuffle(
311+
len(tpeers),
312+
func(i, j int) { tpeers[i], tpeers[j] = tpeers[j], tpeers[i] },
313+
)
314+
return tpeers
315+
}
316+
298317
// bestHead chooses Header that matches the conditions:
299318
// * should have max height among received;
300319
// * should be received at least from 2 peers;

p2p/exchange_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func TestExchange_RequestFullRangeHeaders(t *testing.T) {
9595
connGater, err := conngater.NewBasicConnectionGater(sync.MutexWrap(datastore.NewMapDatastore()))
9696
require.NoError(t, err)
9797
// create new exchange
98-
exchange, err := NewExchange[*test.DummyHeader](hosts[len(hosts)-1], []peer.ID{}, connGater,
98+
exchange, err := NewExchange[*test.DummyHeader](hosts[len(hosts)-1], []peer.ID{hosts[4].ID()}, connGater,
9999
WithNetworkID[ClientParameters](networkID),
100100
WithChainID(networkID),
101101
)

0 commit comments

Comments
 (0)