From 37d639064206cb89b50c0f3387179926f9c83a68 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Sun, 29 Jun 2025 16:33:44 +0200 Subject: [PATCH] discovery: use a no-op hash accumluator for local networks If LND is running on a local network, then use deterministic sampling so that we can have deterministic peer bootstrapping. --- discovery/bootstrapper.go | 51 +++++++++++++++++++--- docs/release-notes/release-notes-0.20.0.md | 4 +- server.go | 4 +- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/discovery/bootstrapper.go b/discovery/bootstrapper.go index 81576df3d..dbfdbc7d2 100644 --- a/discovery/bootstrapper.go +++ b/discovery/bootstrapper.go @@ -138,13 +138,26 @@ var _ NetworkPeerBootstrapper = (*ChannelGraphBootstrapper)(nil) // backed by an active autopilot.ChannelGraph instance. This type of network // peer bootstrapper will use the authenticated nodes within the known channel // graph to bootstrap connections. -func NewGraphBootstrapper(cg autopilot.ChannelGraph) (NetworkPeerBootstrapper, - error) { +func NewGraphBootstrapper(cg autopilot.ChannelGraph, + deterministicSampling bool) (NetworkPeerBootstrapper, error) { - hashAccumulator, err := newRandomHashAccumulator() - if err != nil { - return nil, fmt.Errorf("unable to create hash accumulator: %w", - err) + var ( + hashAccumulator hashAccumulator + err error + ) + if deterministicSampling { + // If we're using deterministic sampling, then we'll use a + // no-op hash accumulator that will always return false for + // skipNode. + hashAccumulator = newNoOpHashAccumulator() + } else { + // Otherwise, we'll use a random hash accumulator to sample + // nodes from the channel graph. + hashAccumulator, err = newRandomHashAccumulator() + if err != nil { + return nil, fmt.Errorf("unable to create hash "+ + "accumulator: %w", err) + } } return &ChannelGraphBootstrapper{ @@ -602,3 +615,29 @@ func (r *randomHashAccumulator) rotate() { func (r *randomHashAccumulator) skipNode(pub route.Vertex) bool { return bytes.Compare(r.hash[:], pub[1:]) > 0 } + +// noOpHashAccumulator is a no-op implementation of the hashAccumulator +// interface. This is used when we want deterministic behavior and don't +// want to sample nodes randomly from the channel graph. +type noOpHashAccumulator struct{} + +// newNoOpHashAccumulator returns a new instance of a noOpHashAccumulator. +func newNoOpHashAccumulator() *noOpHashAccumulator { + return &noOpHashAccumulator{} +} + +// rotate is a no-op for the noOpHashAccumulator. +// +// NOTE: this is part of the hashAccumulator interface. +func (*noOpHashAccumulator) rotate() {} + +// skipNode always returns false, meaning that no nodes will be skipped. +// +// NOTE: this is part of the hashAccumulator interface. +func (*noOpHashAccumulator) skipNode(route.Vertex) bool { + return false +} + +// A compile-time assertion to ensure that noOpHashAccumulator meets the +// hashAccumulator interface. +var _ hashAccumulator = (*noOpHashAccumulator)(nil) diff --git a/docs/release-notes/release-notes-0.20.0.md b/docs/release-notes/release-notes-0.20.0.md index 29c8ba532..d0e163c45 100644 --- a/docs/release-notes/release-notes-0.20.0.md +++ b/docs/release-notes/release-notes-0.20.0.md @@ -144,7 +144,9 @@ reader of a payment request. disabling has now been [removed](https://github.com/lightningnetwork/lnd/pull/9967) meaning that any test network scripts that rely on bootstrapping being disabled will need to - explicitly define the `--nobootstrap` flag. + explicitly define the `--nobootstrap` flag. Bootstrapping will now also be + [deterministic](https://github.com/lightningnetwork/lnd/pull/10003) on local + test networks so that bootstrapping behaviour can be tested for. ## Database diff --git a/server.go b/server.go index f6884a1fe..a0089ea38 100644 --- a/server.go +++ b/server.go @@ -3064,7 +3064,9 @@ func initNetworkBootstrappers(s *server) ([]discovery.NetworkPeerBootstrapper, e // this can be used by default if we've already partially seeded the // network. chanGraph := autopilot.ChannelGraphFromDatabase(s.graphDB) - graphBootstrapper, err := discovery.NewGraphBootstrapper(chanGraph) + graphBootstrapper, err := discovery.NewGraphBootstrapper( + chanGraph, s.cfg.Bitcoin.IsLocalNetwork(), + ) if err != nil { return nil, err }