7
7
"fmt"
8
8
"math/rand"
9
9
"sort"
10
+ "time"
10
11
11
12
logging "github.com/ipfs/go-log/v2"
12
13
"github.com/libp2p/go-libp2p/core/host"
@@ -34,7 +35,7 @@ type Exchange[H header.Header] struct {
34
35
protocolID protocol.ID
35
36
host host.Host
36
37
37
- trustedPeers peer.IDSlice
38
+ trustedPeers func () peer.IDSlice
38
39
peerTracker * peerTracker
39
40
40
41
Params ClientParameters
@@ -58,23 +59,29 @@ func NewExchange[H header.Header](
58
59
return nil , err
59
60
}
60
61
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 ),
65
65
peerTracker : newPeerTracker (
66
66
host ,
67
67
connGater ,
68
68
),
69
69
Params : params ,
70
- }, nil
70
+ }
71
+
72
+ ex .trustedPeers = func () peer.IDSlice {
73
+ return shufflePeers (peers )
74
+ }
75
+ return ex , nil
71
76
}
72
77
73
78
func (ex * Exchange [H ]) Start (context.Context ) error {
74
79
ex .ctx , ex .cancel = context .WithCancel (context .Background ())
75
80
log .Infow ("client: starting client" , "protocol ID" , ex .protocolID )
76
81
77
- for _ , p := range ex .trustedPeers {
82
+ trustedPeers := ex .trustedPeers ()
83
+
84
+ for _ , p := range trustedPeers {
78
85
// Try to pre-connect to trusted peers.
79
86
// We don't really care if we succeed at this point
80
87
// and just need any peers in the peerTracker asap
@@ -116,8 +123,9 @@ func (ex *Exchange[H]) Head(ctx context.Context) (H, error) {
116
123
headerCh = make (chan H )
117
124
)
118
125
126
+ trustedPeers := ex .trustedPeers ()
119
127
// request head from each trusted peer
120
- for _ , from := range ex . trustedPeers {
128
+ for _ , from := range trustedPeers {
121
129
go func (from peer.ID ) {
122
130
// request ensures that the result slice will have at least one Header
123
131
headers , err := ex .request (ctx , from , req )
@@ -131,9 +139,9 @@ func (ex *Exchange[H]) Head(ctx context.Context) (H, error) {
131
139
}(from )
132
140
}
133
141
134
- result := make ([]H , 0 , len (ex . trustedPeers ))
142
+ result := make ([]H , 0 , len (trustedPeers ))
135
143
LOOP:
136
- for range ex . trustedPeers {
144
+ for range trustedPeers {
137
145
select {
138
146
case h := <- headerCh :
139
147
if ! h .IsZero () {
@@ -232,23 +240,22 @@ func (ex *Exchange[H]) performRequest(
232
240
return make ([]H , 0 ), nil
233
241
}
234
242
243
+ trustedPeers := ex .trustedPeers ()
235
244
// TODO: Move this check to constructor(#1671)
236
- if len (ex . trustedPeers ) == 0 {
245
+ if len (trustedPeers ) == 0 {
237
246
return nil , fmt .Errorf ("no trusted peers" )
238
247
}
239
248
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 ))
243
249
250
+ for _ , peer := range trustedPeers {
244
251
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 )
246
253
cancel ()
247
254
switch err {
248
255
default :
249
256
reqErr = err
250
257
log .Debugw ("requesting header from trustedPeer failed" ,
251
- "trustedPeer" , ex . trustedPeers [ idx ] , "err" , err )
258
+ "trustedPeer" , peer , "err" , err )
252
259
continue
253
260
case context .Canceled , context .DeadlineExceeded , nil :
254
261
return h , err
@@ -295,6 +302,18 @@ func (ex *Exchange[H]) request(
295
302
return headers , nil
296
303
}
297
304
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
+
298
317
// bestHead chooses Header that matches the conditions:
299
318
// * should have max height among received;
300
319
// * should be received at least from 2 peers;
0 commit comments