88 "container/list"
99 "math/rand"
1010 "net"
11+ "sort"
1112 "sync"
1213 "sync/atomic"
1314 "time"
@@ -28,6 +29,11 @@ const (
2829 // more.
2930 minInFlightBlocks = 10
3031
32+ // minSyncPeerMedianHeights is the minimum number of valid sync
33+ // peer candidates to trust for updating the sync peer due
34+ // to it being behind.
35+ minSyncPeerMedianHeights = 5
36+
3137 // maxNetworkViolations is the max number of network violations a
3238 // sync peer can have before a new sync peer is found.
3339 maxNetworkViolations = 3
@@ -525,6 +531,13 @@ func (sm *SyncManager) handleCheckSyncPeer() {
525531 return
526532 }
527533
534+ // Check if a majority of our sync peer candidates are at a greater height than our current sync peer.
535+ // If they are, it is time to select a new peer, as ours is obviously behind.
536+ if sm .topBlock () < sm .medianSyncPeerCandidateBlockHeight () {
537+ sm .updateSyncPeer ()
538+ return
539+ }
540+
528541 // Update network stats at the end of this tick.
529542 defer sm .syncPeerState .updateNetwork (sm .syncPeer )
530543
@@ -537,7 +550,6 @@ func (sm *SyncManager) handleCheckSyncPeer() {
537550
538551 // Don't update sync peers if you have all the available
539552 // blocks.
540-
541553 best := sm .chain .BestSnapshot ()
542554
543555 if sm .topBlock () == best .Height || sm .chain .UtxoCacheFlushInProgress () || (sm .fastSyncMode && best .Height == sm .lastCheckpoint ().Height ) {
@@ -550,6 +562,45 @@ func (sm *SyncManager) handleCheckSyncPeer() {
550562 sm .updateSyncPeer ()
551563}
552564
565+ // medianSyncPeerCandidateBlockHeight returns the median block height of sync peer candidates.
566+ func (sm * SyncManager ) medianSyncPeerCandidateBlockHeight () int32 {
567+ heights := []int32 {}
568+
569+ for peer , state := range sm .peerStates {
570+ if ! state .syncCandidate {
571+ continue
572+ }
573+
574+ // Peer isn't connected, skip.
575+ if ! peer .Connected () {
576+ continue
577+ }
578+
579+ topBlock := peer .LastBlock ()
580+ if topBlock < peer .StartingHeight () {
581+ topBlock = peer .StartingHeight ()
582+ }
583+
584+ heights = append (heights , topBlock )
585+ }
586+
587+ // Make sure we have enough heights to trust the data.
588+ // If we only have 1 or 2 that could be gamed easily!
589+ if len (heights ) < minSyncPeerMedianHeights {
590+ return 0
591+ }
592+
593+ sort .Slice (heights , func (i , j int ) bool { return heights [i ] < heights [j ] })
594+
595+ mNumber := len (heights ) / 2
596+
597+ if len (heights )% 2 != 0 {
598+ return heights [mNumber ]
599+ }
600+
601+ return (heights [mNumber - 1 ] + heights [mNumber ]) / 2
602+ }
603+
553604// topBlock returns the best chains top block height
554605func (sm * SyncManager ) topBlock () int32 {
555606 if sm .syncPeer .LastBlock () > sm .syncPeer .StartingHeight () {
0 commit comments