netsync: allow sync with non-localhost peers on regtest/simnet
What changed, and why it matters
This change loosens a network safety rule for two private Bitcoin test networks (regtest and simnet). Previously, nodes on these test networks would only accept initial block download from a peer running on the same computer (localhost). Now they will accept it from any reachable peer, such as another node in a Docker container. This is a deliberate usability fix for testing environments, but it removes a guard that helped prevent untrusted remote peers from feeding blocks to a regtest/simnet node. It is not a vulnerability in the public Bitcoin network (mainnet), which is unaffected.
Treat this as a deliberate behavior change rather than a vulnerability. Operators running regtest/simnet nodes on untrusted or shared networks should be aware that any reachable peer can now attempt to act as a sync source. If running in such environments, use firewall rules, authenticated peer whitelisting, or container network isolation to restrict who can reach the P2P port. No patch is required for mainnet nodes.
Security signals we found
Removal of localhost-only peer restriction for regtest/simnet sync candidates
Expansion of exception from regtest to simnet
Change from pointer identity to chainParams.Name value comparison
Reduced trust boundary on private/test networks
Evidence from the diff
In netsync/manager.go, isSyncCandidate() no longer restricts regtest/simnet sync peers to 127.0.0.1/localhost. The net.SplitHostPort check is removed and the exception is broadened to SimNetParams via a switch on chainParams.Name. A separate pointer-equality check in handleBlockMsg() is changed to a value comparison on the Name field. The change is intended to support IBD in container/bridge-network test setups. It reduces the trust boundary for regtest/simnet because any routable peer can now become a sync candidate and supply blocks.
Changed components
netsync/manager.goSyncManager.isSyncCandidate()SyncManager.handleBlockMsg()chaincfg.RegressionNetParamschaincfg.SimNetParamsInspect captured patch +7 / −15
diff --git a/netsync/manager.go b/netsync/manager.go
index 44c5fac..73260c5 100644
--- a/netsync/manager.go
+++ b/netsync/manager.go
@@ -6,7 +6,6 @@ package netsync
import (
"math/rand"
- "net"
"sync"
"sync/atomic"
"time"
@@ -364,19 +363,12 @@ func (sm *SyncManager) isSyncCandidate(peer *peerpkg.Peer) bool {
// Typically a peer is not a candidate for sync if it's not a full node,
// however regression test is special in that the regression tool is
// not a full node and still needs to be considered a sync candidate.
- if sm.chainParams == &chaincfg.RegressionNetParams {
- // The peer is not a candidate if it's not coming from localhost
- // or the hostname can't be determined for some reason.
- host, _, err := net.SplitHostPort(peer.Addr())
- if err != nil {
- return false
- }
-
- if host != "127.0.0.1" && host != "localhost" {
- return false
- }
-
- // Candidate if all checks passed.
+ switch sm.chainParams.Name {
+ case chaincfg.RegressionNetParams.Name, chaincfg.SimNetParams.Name:
+ // In regtest/simnet mode, any peer is a valid sync candidate
+ // regardless of its address or service flags. This allows
+ // syncing from peers on non-localhost networks such as Docker
+ // bridge networks.
return true
}
@@ -715,7 +707,7 @@ func (sm *SyncManager) handleBlockMsg(bmsg *blockMsg) {
// the peer or ignore the block when we're in regression test
// mode in this case so the chain code is actually fed the
// duplicate blocks.
- if sm.chainParams != &chaincfg.RegressionNetParams {
+ if sm.chainParams.Name != chaincfg.RegressionNetParams.Name {
log.Warnf("Got unrequested block %v from %s -- "+
"disconnecting", blockHash, peer.Addr())
peer.Disconnect()
Why this scored 31/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.