netsync: change isSyncCandidate behavior to include pruned nodes

isSyncCandidate is now changed to return true even if the peer is a
pruned node if and only if our chaintip is within 288 blocks of the
peer.

Rationale:
Pruned nodes that signal NODE_NETWORK_LIMITED MUST serve 288 blocks from
their chaintip.  If our chaintip is within that range, this peer can be
a sync candidate even if they aren't an archival node.
This commit is contained in:
Calvin Kim 2023-09-25 18:33:58 +09:00
parent a09e7b224a
commit b4992febff
2 changed files with 54 additions and 19 deletions

View file

@ -397,20 +397,55 @@ func (sm *SyncManager) isSyncCandidate(peer *peerpkg.Peer) bool {
if host != "127.0.0.1" && host != "localhost" {
return false
}
} else {
// The peer is not a candidate for sync if it's not a full
// node. Additionally, if the segwit soft-fork package has
// activated, then the peer must also be upgraded.
segwitActive, err := sm.chain.IsDeploymentActive(chaincfg.DeploymentSegwit)
if err != nil {
log.Errorf("Unable to query for segwit "+
"soft-fork state: %v", err)
}
nodeServices := peer.Services()
if nodeServices&wire.SFNodeNetwork != wire.SFNodeNetwork ||
(segwitActive && !peer.IsWitnessEnabled()) {
// Candidate if all checks passed.
return true
}
// If the segwit soft-fork package has activated, then the peer must
// also be upgraded.
segwitActive, err := sm.chain.IsDeploymentActive(
chaincfg.DeploymentSegwit,
)
if err != nil {
log.Errorf("Unable to query for segwit soft-fork state: %v",
err)
}
if segwitActive && !peer.IsWitnessEnabled() {
return false
}
var (
nodeServices = peer.Services()
fullNode = nodeServices.HasFlag(wire.SFNodeNetwork)
prunedNode = nodeServices.HasFlag(wire.SFNodeNetworkLimited)
)
switch {
case fullNode:
// Node is a sync candidate if it has all the blocks.
case prunedNode:
// Even if the peer is pruned, if they have the node network
// limited flag, they are able to serve 2 days worth of blocks
// from the current tip. Therefore, check if our chaintip is
// within that range.
bestHeight := sm.chain.BestSnapshot().Height
peerLastBlock := peer.LastBlock()
// bestHeight+1 as we need the peer to serve us the next block,
// not the one we already have.
if bestHeight+1 <=
peerLastBlock-wire.NodeNetworkLimitedBlockThreshold {
return false
}
default:
// If the peer isn't an archival node, and it's not signaling
// NODE_NETWORK_LIMITED, we can't sync off of this node.
return false
}
// Candidate if all checks passed.
@ -428,7 +463,7 @@ func (sm *SyncManager) handleNewPeerMsg(peer *peerpkg.Peer) {
log.Infof("New valid peer %s (%s)", peer, peer.UserAgent())
// Initialize the peer state
// Initialize the peer state.
isSyncCandidate := sm.isSyncCandidate(peer)
sm.peerStates[peer] = &peerSyncState{
syncCandidate: isSyncCandidate,

View file

@ -60,6 +60,12 @@ const (
AddrV2Version uint32 = 70016
)
const (
// NodeNetworkLimitedBlockThreshold is the number of blocks that a node
// broadcasting SFNodeNetworkLimited MUST be able to serve from the tip.
NodeNetworkLimitedBlockThreshold = 288
)
// ServiceFlag identifies services supported by a bitcoin peer.
type ServiceFlag uint64
@ -156,12 +162,6 @@ func (f ServiceFlag) String() string {
return s
}
const (
// NodeNetworkLimitedBlockThreshold is the number of blocks that a node
// broadcasting SFNodeNetworkLimited MUST be able to serve from the tip.
NodeNetworkLimitedBlockThreshold = 288
)
// BitcoinNet represents which bitcoin network a message belongs to.
type BitcoinNet uint32