Skip to content

Commit c7d087a

Browse files
authored
Merge pull request #411 from gcash/sync-peer-behind
[Feature] Select new sync peer if median of sync peer candidates are ahead of us
2 parents 1158d45 + 99df0e9 commit c7d087a

File tree

1 file changed

+52
-1
lines changed

1 file changed

+52
-1
lines changed

netsync/manager.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
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
554605
func (sm *SyncManager) topBlock() int32 {
555606
if sm.syncPeer.LastBlock() > sm.syncPeer.StartingHeight() {

0 commit comments

Comments
 (0)