@@ -34,6 +34,15 @@ import (
34
34
// they do not provide each of these services.
35
35
const reqSvcs = wire .SFNodeNetwork
36
36
37
+ const (
38
+ targetPeerCount = 8
39
+
40
+ // Require a subset of all connected outbounded peers to meet some
41
+ // minimum protocol version.
42
+ minVersion = wire .MixVersion
43
+ minVersionTarget = 3
44
+ )
45
+
37
46
// Syncer implements wallet synchronization services by over the Decred wire
38
47
// protocol using Simplified Payment Verification (SPV) with compact filters.
39
48
type Syncer struct {
@@ -451,9 +460,12 @@ func (s *Syncer) peerCandidate(svcs wire.ServiceFlag) (*addrmgr.NetAddress, erro
451
460
return nil , errors .New ("no addresses" )
452
461
}
453
462
463
+ var errBreaksMinVersionTarget = errors .New ("peer uses too low version to satisify " +
464
+ "minimum target version count" )
465
+
454
466
// connectAndRunPeer connects to and runs the syncing process with the specified
455
467
// peer. It blocks until the peer disconnects and logs any errors.
456
- func (s * Syncer ) connectAndRunPeer (ctx context.Context , raddr string ) {
468
+ func (s * Syncer ) connectAndRunPeer (ctx context.Context , raddr string , persistent bool ) {
457
469
// Attempt connection to peer.
458
470
rp , err := s .lp .ConnectOutbound (ctx , raddr , reqSvcs )
459
471
if err != nil {
@@ -467,18 +479,24 @@ func (s *Syncer) connectAndRunPeer(ctx context.Context, raddr string) {
467
479
}
468
480
return
469
481
}
470
- log .Infof ("New peer %v %v %v" , raddr , rp .UA (), rp .Services ())
471
482
472
483
// Track peer as running as opposed to attempting connection.
473
484
s .remotesMu .Lock ()
474
485
delete (s .connectingRemotes , raddr )
486
+ if ! persistent && s .breaksMinVersionTarget (rp ) {
487
+ s .remotesMu .Unlock ()
488
+ log .Debugf ("Disconnecting %v: %v" , raddr , errBreaksMinVersionTarget )
489
+ rp .Disconnect (errBreaksMinVersionTarget )
490
+ return
491
+ }
475
492
s .remotes [raddr ] = rp
476
493
n := len (s .remotes )
477
494
if s .remoteAvailable != nil {
478
495
close (s .remoteAvailable )
479
496
s .remoteAvailable = nil
480
497
}
481
498
s .remotesMu .Unlock ()
499
+ log .Infof ("New peer %v %v version=%d %v" , raddr , rp .UA (), rp .Pver (), rp .Services ())
482
500
s .peerConnected (n , raddr )
483
501
484
502
// Alert disconnection once this peer is done.
@@ -514,9 +532,29 @@ func (s *Syncer) connectAndRunPeer(ctx context.Context, raddr string) {
514
532
}
515
533
}
516
534
535
+ func (s * Syncer ) breaksMinVersionTarget (rp * p2p.RemotePeer ) bool {
536
+ if rp .Pver () >= minVersion {
537
+ return false
538
+ }
539
+ n := len (s .remotes )
540
+ if n < targetPeerCount - minVersionTarget {
541
+ return false
542
+ }
543
+ var meetsMin int
544
+ for _ , rp := range s .remotes {
545
+ if rp .Pver () >= minVersion {
546
+ meetsMin ++
547
+ if meetsMin == minVersionTarget {
548
+ return false
549
+ }
550
+ }
551
+ }
552
+ return true
553
+ }
554
+
517
555
func (s * Syncer ) connectToPersistent (ctx context.Context , raddr string ) error {
518
556
for {
519
- s .connectAndRunPeer (ctx , raddr )
557
+ s .connectAndRunPeer (ctx , raddr , true )
520
558
521
559
// Retry persistent peer after 5 seconds.
522
560
select {
@@ -531,7 +569,7 @@ func (s *Syncer) connectToCandidates(ctx context.Context) error {
531
569
var wg sync.WaitGroup
532
570
defer wg .Wait ()
533
571
534
- sem := make (chan struct {}, 8 )
572
+ sem := make (chan struct {}, targetPeerCount )
535
573
for {
536
574
if ctx .Err () != nil {
537
575
return ctx .Err ()
@@ -555,7 +593,7 @@ func (s *Syncer) connectToCandidates(ctx context.Context) error {
555
593
wg .Add (1 )
556
594
go func () {
557
595
raddr := na .String ()
558
- s .connectAndRunPeer (ctx , raddr )
596
+ s .connectAndRunPeer (ctx , raddr , false )
559
597
wg .Done ()
560
598
<- sem
561
599
}()
0 commit comments