diff --git a/dht.go b/dht.go index ae82c1396..c5fbeeb54 100644 --- a/dht.go +++ b/dht.go @@ -126,10 +126,10 @@ type IpfsDHT struct { autoRefresh bool - // A set of bootstrap peers to fallback on if all other attempts to fix + // A function returning a set of bootstrap peers to fallback on if all other attempts to fix // the routing table fail (or, e.g., this is the first time this node is // connecting to the network). - bootstrapPeers []peer.AddrInfo + bootstrapPeers func() []peer.AddrInfo maxRecordAge time.Duration @@ -473,15 +473,16 @@ func (dht *IpfsDHT) fixLowPeers(ctx context.Context) { // We should first use non-bootstrap peers we knew of from previous // snapshots of the Routing Table before we connect to the bootstrappers. // See https://github.com/libp2p/go-libp2p-kad-dht/issues/387. - if dht.routingTable.Size() == 0 { - if len(dht.bootstrapPeers) == 0 { + if dht.routingTable.Size() == 0 && dht.bootstrapPeers != nil { + bootstrapPeers := dht.bootstrapPeers() + if len(bootstrapPeers) == 0 { // No point in continuing, we have no peers! return } found := 0 - for _, i := range rand.Perm(len(dht.bootstrapPeers)) { - ai := dht.bootstrapPeers[i] + for _, i := range rand.Perm(len(bootstrapPeers)) { + ai := bootstrapPeers[i] err := dht.Host().Connect(ctx, ai) if err == nil { found++ diff --git a/dht_options.go b/dht_options.go index 1f4e47afe..0ef777913 100644 --- a/dht_options.go +++ b/dht_options.go @@ -269,7 +269,18 @@ func RoutingTableFilter(filter RouteTableFilterFunc) Option { // and refresh our Routing Table if it becomes empty. func BootstrapPeers(bootstrappers ...peer.AddrInfo) Option { return func(c *dhtcfg.Config) error { - c.BootstrapPeers = bootstrappers + c.BootstrapPeers = func() []peer.AddrInfo { + return bootstrappers + } + return nil + } +} + +// BootstrapPeersFunc configures the function that returns the bootstrapping nodes that we will +// connect to to seed and refresh our Routing Table if it becomes empty. +func BootstrapPeersFunc(getBootstrapPeers func() []peer.AddrInfo) Option { + return func(c *dhtcfg.Config) error { + c.BootstrapPeers = getBootstrapPeers return nil } } diff --git a/dht_test.go b/dht_test.go index 3c1f2bca0..87d9f858f 100644 --- a/dht_test.go +++ b/dht_test.go @@ -2051,6 +2051,39 @@ func TestBootStrapWhenRTIsEmpty(t *testing.T) { }, 5*time.Second, 500*time.Millisecond) } +func TestBootstrapPeersFunc(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + var lock sync.Mutex + + bootstrapFuncA := func() []peer.AddrInfo { + return []peer.AddrInfo{} + } + dhtA := setupDHT(ctx, t, false, BootstrapPeersFunc(bootstrapFuncA)) + + bootstrapPeersB := []peer.AddrInfo{} + bootstrapFuncB := func() []peer.AddrInfo { + lock.Lock() + defer lock.Unlock() + return bootstrapPeersB + } + + dhtB := setupDHT(ctx, t, false, BootstrapPeersFunc(bootstrapFuncB)) + require.Equal(t, 0, len(dhtB.host.Network().Peers())) + + addrA := peer.AddrInfo{ + ID: dhtA.self, + Addrs: dhtA.host.Addrs(), + } + + lock.Lock() + bootstrapPeersB = []peer.AddrInfo{addrA} + lock.Unlock() + + dhtB.fixLowPeers(ctx) + require.NotEqual(t, 0, len(dhtB.host.Network().Peers())) +} + func TestPreconnectedNodes(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/fullrt/dht.go b/fullrt/dht.go index 331f324ff..dfee22807 100644 --- a/fullrt/dht.go +++ b/fullrt/dht.go @@ -142,7 +142,7 @@ func NewFullRT(h host.Host, protocolPrefix protocol.ID, options ...Option) (*Ful var bsPeers []*peer.AddrInfo - for _, ai := range dhtcfg.BootstrapPeers { + for _, ai := range dhtcfg.BootstrapPeers() { tmpai := ai bsPeers = append(bsPeers, &tmpai) } diff --git a/internal/config/config.go b/internal/config/config.go index 8e805688c..75b980e3f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -57,7 +57,7 @@ type Config struct { DiversityFilter peerdiversity.PeerIPGroupFilter } - BootstrapPeers []peer.AddrInfo + BootstrapPeers func() []peer.AddrInfo // test specific Config options DisableFixLowPeers bool