From b4992febff11fefc1940c67edfbcac97b627efd1 Mon Sep 17 00:00:00 2001 From: Calvin Kim Date: Mon, 25 Sep 2023 18:33:58 +0900 Subject: [PATCH] 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. --- netsync/manager.go | 61 ++++++++++++++++++++++++++++++++++++---------- wire/protocol.go | 12 ++++----- 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/netsync/manager.go b/netsync/manager.go index 523437a0..fa3cf3d0 100644 --- a/netsync/manager.go +++ b/netsync/manager.go @@ -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, diff --git a/wire/protocol.go b/wire/protocol.go index 7e2de3de..baeec053 100644 --- a/wire/protocol.go +++ b/wire/protocol.go @@ -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