github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/network/simulations/discovery/discovery_test.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package discovery
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"errors"
    23  	"flag"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"math/rand"
    27  	"os"
    28  	"path"
    29  	"strings"
    30  	"sync"
    31  	"testing"
    32  	"time"
    33  
    34  	"github.com/FusionFoundation/efsn/common"
    35  	"github.com/FusionFoundation/efsn/log"
    36  	"github.com/FusionFoundation/efsn/node"
    37  	"github.com/FusionFoundation/efsn/p2p"
    38  	"github.com/FusionFoundation/efsn/p2p/discover"
    39  	"github.com/FusionFoundation/efsn/p2p/simulations"
    40  	"github.com/FusionFoundation/efsn/p2p/simulations/adapters"
    41  	"github.com/FusionFoundation/efsn/swarm/network"
    42  	"github.com/FusionFoundation/efsn/swarm/state"
    43  	colorable "github.com/mattn/go-colorable"
    44  )
    45  
    46  // serviceName is used with the exec adapter so the exec'd binary knows which
    47  // service to execute
    48  const serviceName = "discovery"
    49  const testMinProxBinSize = 2
    50  const discoveryPersistenceDatadir = "discovery_persistence_test_store"
    51  
    52  var discoveryPersistencePath = path.Join(os.TempDir(), discoveryPersistenceDatadir)
    53  var discoveryEnabled = true
    54  var persistenceEnabled = false
    55  
    56  var services = adapters.Services{
    57  	serviceName: newService,
    58  }
    59  
    60  func cleanDbStores() error {
    61  	entries, err := ioutil.ReadDir(os.TempDir())
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	for _, f := range entries {
    67  		if strings.HasPrefix(f.Name(), discoveryPersistenceDatadir) {
    68  			os.RemoveAll(path.Join(os.TempDir(), f.Name()))
    69  		}
    70  	}
    71  	return nil
    72  
    73  }
    74  
    75  func getDbStore(nodeID string) (*state.DBStore, error) {
    76  	if _, err := os.Stat(discoveryPersistencePath + "_" + nodeID); os.IsNotExist(err) {
    77  		log.Info(fmt.Sprintf("directory for nodeID %s does not exist. creating...", nodeID))
    78  		ioutil.TempDir("", discoveryPersistencePath+"_"+nodeID)
    79  	}
    80  	log.Info(fmt.Sprintf("opening storage directory for nodeID %s", nodeID))
    81  	store, err := state.NewDBStore(discoveryPersistencePath + "_" + nodeID)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	return store, nil
    86  }
    87  
    88  var (
    89  	nodeCount    = flag.Int("nodes", 10, "number of nodes to create (default 10)")
    90  	initCount    = flag.Int("conns", 1, "number of originally connected peers	 (default 1)")
    91  	snapshotFile = flag.String("snapshot", "", "create snapshot")
    92  	loglevel     = flag.Int("loglevel", 3, "verbosity of logs")
    93  	rawlog       = flag.Bool("rawlog", false, "remove terminal formatting from logs")
    94  )
    95  
    96  func init() {
    97  	flag.Parse()
    98  	// register the discovery service which will run as a devp2p
    99  	// protocol when using the exec adapter
   100  	adapters.RegisterServices(services)
   101  
   102  	log.PrintOrigins(true)
   103  	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(!*rawlog))))
   104  }
   105  
   106  // Benchmarks to test the average time it takes for an N-node ring
   107  // to full a healthy kademlia topology
   108  func BenchmarkDiscovery_8_1(b *testing.B)   { benchmarkDiscovery(b, 8, 1) }
   109  func BenchmarkDiscovery_16_1(b *testing.B)  { benchmarkDiscovery(b, 16, 1) }
   110  func BenchmarkDiscovery_32_1(b *testing.B)  { benchmarkDiscovery(b, 32, 1) }
   111  func BenchmarkDiscovery_64_1(b *testing.B)  { benchmarkDiscovery(b, 64, 1) }
   112  func BenchmarkDiscovery_128_1(b *testing.B) { benchmarkDiscovery(b, 128, 1) }
   113  func BenchmarkDiscovery_256_1(b *testing.B) { benchmarkDiscovery(b, 256, 1) }
   114  
   115  func BenchmarkDiscovery_8_2(b *testing.B)   { benchmarkDiscovery(b, 8, 2) }
   116  func BenchmarkDiscovery_16_2(b *testing.B)  { benchmarkDiscovery(b, 16, 2) }
   117  func BenchmarkDiscovery_32_2(b *testing.B)  { benchmarkDiscovery(b, 32, 2) }
   118  func BenchmarkDiscovery_64_2(b *testing.B)  { benchmarkDiscovery(b, 64, 2) }
   119  func BenchmarkDiscovery_128_2(b *testing.B) { benchmarkDiscovery(b, 128, 2) }
   120  func BenchmarkDiscovery_256_2(b *testing.B) { benchmarkDiscovery(b, 256, 2) }
   121  
   122  func BenchmarkDiscovery_8_4(b *testing.B)   { benchmarkDiscovery(b, 8, 4) }
   123  func BenchmarkDiscovery_16_4(b *testing.B)  { benchmarkDiscovery(b, 16, 4) }
   124  func BenchmarkDiscovery_32_4(b *testing.B)  { benchmarkDiscovery(b, 32, 4) }
   125  func BenchmarkDiscovery_64_4(b *testing.B)  { benchmarkDiscovery(b, 64, 4) }
   126  func BenchmarkDiscovery_128_4(b *testing.B) { benchmarkDiscovery(b, 128, 4) }
   127  func BenchmarkDiscovery_256_4(b *testing.B) { benchmarkDiscovery(b, 256, 4) }
   128  
   129  func TestDiscoverySimulationDockerAdapter(t *testing.T) {
   130  	testDiscoverySimulationDockerAdapter(t, *nodeCount, *initCount)
   131  }
   132  
   133  func testDiscoverySimulationDockerAdapter(t *testing.T, nodes, conns int) {
   134  	adapter, err := adapters.NewDockerAdapter()
   135  	if err != nil {
   136  		if err == adapters.ErrLinuxOnly {
   137  			t.Skip(err)
   138  		} else {
   139  			t.Fatal(err)
   140  		}
   141  	}
   142  	testDiscoverySimulation(t, nodes, conns, adapter)
   143  }
   144  
   145  func TestDiscoverySimulationExecAdapter(t *testing.T) {
   146  	testDiscoverySimulationExecAdapter(t, *nodeCount, *initCount)
   147  }
   148  
   149  func testDiscoverySimulationExecAdapter(t *testing.T, nodes, conns int) {
   150  	baseDir, err := ioutil.TempDir("", "swarm-test")
   151  	if err != nil {
   152  		t.Fatal(err)
   153  	}
   154  	defer os.RemoveAll(baseDir)
   155  	testDiscoverySimulation(t, nodes, conns, adapters.NewExecAdapter(baseDir))
   156  }
   157  
   158  func TestDiscoverySimulationSimAdapter(t *testing.T) {
   159  	testDiscoverySimulationSimAdapter(t, *nodeCount, *initCount)
   160  }
   161  
   162  func TestDiscoveryPersistenceSimulationSimAdapter(t *testing.T) {
   163  	testDiscoveryPersistenceSimulationSimAdapter(t, *nodeCount, *initCount)
   164  }
   165  
   166  func testDiscoveryPersistenceSimulationSimAdapter(t *testing.T, nodes, conns int) {
   167  	testDiscoveryPersistenceSimulation(t, nodes, conns, adapters.NewSimAdapter(services))
   168  }
   169  
   170  func testDiscoverySimulationSimAdapter(t *testing.T, nodes, conns int) {
   171  	testDiscoverySimulation(t, nodes, conns, adapters.NewSimAdapter(services))
   172  }
   173  
   174  func testDiscoverySimulation(t *testing.T, nodes, conns int, adapter adapters.NodeAdapter) {
   175  	startedAt := time.Now()
   176  	result, err := discoverySimulation(nodes, conns, adapter)
   177  	if err != nil {
   178  		t.Fatalf("Setting up simulation failed: %v", err)
   179  	}
   180  	if result.Error != nil {
   181  		t.Fatalf("Simulation failed: %s", result.Error)
   182  	}
   183  	t.Logf("Simulation with %d nodes passed in %s", nodes, result.FinishedAt.Sub(result.StartedAt))
   184  	var min, max time.Duration
   185  	var sum int
   186  	for _, pass := range result.Passes {
   187  		duration := pass.Sub(result.StartedAt)
   188  		if sum == 0 || duration < min {
   189  			min = duration
   190  		}
   191  		if duration > max {
   192  			max = duration
   193  		}
   194  		sum += int(duration.Nanoseconds())
   195  	}
   196  	t.Logf("Min: %s, Max: %s, Average: %s", min, max, time.Duration(sum/len(result.Passes))*time.Nanosecond)
   197  	finishedAt := time.Now()
   198  	t.Logf("Setup: %s, shutdown: %s", result.StartedAt.Sub(startedAt), finishedAt.Sub(result.FinishedAt))
   199  }
   200  
   201  func testDiscoveryPersistenceSimulation(t *testing.T, nodes, conns int, adapter adapters.NodeAdapter) map[int][]byte {
   202  	persistenceEnabled = true
   203  	discoveryEnabled = true
   204  
   205  	result, err := discoveryPersistenceSimulation(nodes, conns, adapter)
   206  
   207  	if err != nil {
   208  		t.Fatalf("Setting up simulation failed: %v", err)
   209  	}
   210  	if result.Error != nil {
   211  		t.Fatalf("Simulation failed: %s", result.Error)
   212  	}
   213  	t.Logf("Simulation with %d nodes passed in %s", nodes, result.FinishedAt.Sub(result.StartedAt))
   214  	// set the discovery and persistence flags again to default so other
   215  	// tests will not be affected
   216  	discoveryEnabled = true
   217  	persistenceEnabled = false
   218  	return nil
   219  }
   220  
   221  func benchmarkDiscovery(b *testing.B, nodes, conns int) {
   222  	for i := 0; i < b.N; i++ {
   223  		result, err := discoverySimulation(nodes, conns, adapters.NewSimAdapter(services))
   224  		if err != nil {
   225  			b.Fatalf("setting up simulation failed: %v", err)
   226  		}
   227  		if result.Error != nil {
   228  			b.Logf("simulation failed: %s", result.Error)
   229  		}
   230  	}
   231  }
   232  
   233  func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simulations.StepResult, error) {
   234  	// create network
   235  	net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
   236  		ID:             "0",
   237  		DefaultService: serviceName,
   238  	})
   239  	defer net.Shutdown()
   240  	trigger := make(chan discover.NodeID)
   241  	ids := make([]discover.NodeID, nodes)
   242  	for i := 0; i < nodes; i++ {
   243  		conf := adapters.RandomNodeConfig()
   244  		node, err := net.NewNodeWithConfig(conf)
   245  		if err != nil {
   246  			return nil, fmt.Errorf("error starting node: %s", err)
   247  		}
   248  		if err := net.Start(node.ID()); err != nil {
   249  			return nil, fmt.Errorf("error starting node %s: %s", node.ID().TerminalString(), err)
   250  		}
   251  		if err := triggerChecks(trigger, net, node.ID()); err != nil {
   252  			return nil, fmt.Errorf("error triggering checks for node %s: %s", node.ID().TerminalString(), err)
   253  		}
   254  		ids[i] = node.ID()
   255  	}
   256  
   257  	// run a simulation which connects the 10 nodes in a ring and waits
   258  	// for full peer discovery
   259  	var addrs [][]byte
   260  	action := func(ctx context.Context) error {
   261  		return nil
   262  	}
   263  	wg := sync.WaitGroup{}
   264  	for i := range ids {
   265  		// collect the overlay addresses, to
   266  		addrs = append(addrs, network.ToOverlayAddr(ids[i].Bytes()))
   267  		for j := 0; j < conns; j++ {
   268  			var k int
   269  			if j == 0 {
   270  				k = (i + 1) % len(ids)
   271  			} else {
   272  				k = rand.Intn(len(ids))
   273  			}
   274  			wg.Add(1)
   275  			go func(i, k int) {
   276  				defer wg.Done()
   277  				net.Connect(ids[i], ids[k])
   278  			}(i, k)
   279  		}
   280  	}
   281  	wg.Wait()
   282  	log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
   283  	// construct the peer pot, so that kademlia health can be checked
   284  	ppmap := network.NewPeerPotMap(testMinProxBinSize, addrs)
   285  	check := func(ctx context.Context, id discover.NodeID) (bool, error) {
   286  		select {
   287  		case <-ctx.Done():
   288  			return false, ctx.Err()
   289  		default:
   290  		}
   291  
   292  		node := net.GetNode(id)
   293  		if node == nil {
   294  			return false, fmt.Errorf("unknown node: %s", id)
   295  		}
   296  		client, err := node.Client()
   297  		if err != nil {
   298  			return false, fmt.Errorf("error getting node client: %s", err)
   299  		}
   300  		healthy := &network.Health{}
   301  		addr := common.Bytes2Hex(network.ToOverlayAddr(id.Bytes()))
   302  		if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); err != nil {
   303  			return false, fmt.Errorf("error getting node health: %s", err)
   304  		}
   305  		log.Debug(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v, saturated: %v\n%v", id, healthy.GotNN, healthy.KnowNN, healthy.Full, healthy.Hive))
   306  		return healthy.KnowNN && healthy.GotNN && healthy.Full, nil
   307  	}
   308  
   309  	// 64 nodes ~ 1min
   310  	// 128 nodes ~
   311  	timeout := 300 * time.Second
   312  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
   313  	defer cancel()
   314  	result := simulations.NewSimulation(net).Run(ctx, &simulations.Step{
   315  		Action:  action,
   316  		Trigger: trigger,
   317  		Expect: &simulations.Expectation{
   318  			Nodes: ids,
   319  			Check: check,
   320  		},
   321  	})
   322  	if result.Error != nil {
   323  		return result, nil
   324  	}
   325  
   326  	if *snapshotFile != "" {
   327  		snap, err := net.Snapshot()
   328  		if err != nil {
   329  			return nil, errors.New("no shapshot dude")
   330  		}
   331  		jsonsnapshot, err := json.Marshal(snap)
   332  		if err != nil {
   333  			return nil, fmt.Errorf("corrupt json snapshot: %v", err)
   334  		}
   335  		log.Info("writing snapshot", "file", *snapshotFile)
   336  		err = ioutil.WriteFile(*snapshotFile, jsonsnapshot, 0755)
   337  		if err != nil {
   338  			return nil, err
   339  		}
   340  	}
   341  	return result, nil
   342  }
   343  
   344  func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simulations.StepResult, error) {
   345  	cleanDbStores()
   346  	defer cleanDbStores()
   347  
   348  	// create network
   349  	net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
   350  		ID:             "0",
   351  		DefaultService: serviceName,
   352  	})
   353  	defer net.Shutdown()
   354  	trigger := make(chan discover.NodeID)
   355  	ids := make([]discover.NodeID, nodes)
   356  	var addrs [][]byte
   357  
   358  	for i := 0; i < nodes; i++ {
   359  		conf := adapters.RandomNodeConfig()
   360  		node, err := net.NewNodeWithConfig(conf)
   361  		if err != nil {
   362  			panic(err)
   363  		}
   364  		if err != nil {
   365  			return nil, fmt.Errorf("error starting node: %s", err)
   366  		}
   367  		if err := net.Start(node.ID()); err != nil {
   368  			return nil, fmt.Errorf("error starting node %s: %s", node.ID().TerminalString(), err)
   369  		}
   370  		if err := triggerChecks(trigger, net, node.ID()); err != nil {
   371  			return nil, fmt.Errorf("error triggering checks for node %s: %s", node.ID().TerminalString(), err)
   372  		}
   373  		ids[i] = node.ID()
   374  		a := network.ToOverlayAddr(ids[i].Bytes())
   375  
   376  		addrs = append(addrs, a)
   377  	}
   378  
   379  	// run a simulation which connects the 10 nodes in a ring and waits
   380  	// for full peer discovery
   381  	ppmap := network.NewPeerPotMap(testMinProxBinSize, addrs)
   382  
   383  	var restartTime time.Time
   384  
   385  	action := func(ctx context.Context) error {
   386  		ticker := time.NewTicker(500 * time.Millisecond)
   387  
   388  		for range ticker.C {
   389  			isHealthy := true
   390  			for _, id := range ids {
   391  				//call Healthy RPC
   392  				node := net.GetNode(id)
   393  				if node == nil {
   394  					return fmt.Errorf("unknown node: %s", id)
   395  				}
   396  				client, err := node.Client()
   397  				if err != nil {
   398  					return fmt.Errorf("error getting node client: %s", err)
   399  				}
   400  				healthy := &network.Health{}
   401  				addr := common.Bytes2Hex(network.ToOverlayAddr(id.Bytes()))
   402  				if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); err != nil {
   403  					return fmt.Errorf("error getting node health: %s", err)
   404  				}
   405  
   406  				log.Info(fmt.Sprintf("NODE: %s, IS HEALTHY: %t", id.String(), healthy.GotNN && healthy.KnowNN && healthy.Full))
   407  				if !healthy.GotNN || !healthy.Full {
   408  					isHealthy = false
   409  					break
   410  				}
   411  			}
   412  			if isHealthy {
   413  				break
   414  			}
   415  		}
   416  		ticker.Stop()
   417  
   418  		log.Info("reached healthy kademlia. starting to shutdown nodes.")
   419  		shutdownStarted := time.Now()
   420  		// stop all ids, then start them again
   421  		for _, id := range ids {
   422  			node := net.GetNode(id)
   423  
   424  			if err := net.Stop(node.ID()); err != nil {
   425  				return fmt.Errorf("error stopping node %s: %s", node.ID().TerminalString(), err)
   426  			}
   427  		}
   428  		log.Info(fmt.Sprintf("shutting down nodes took: %s", time.Since(shutdownStarted)))
   429  		persistenceEnabled = true
   430  		discoveryEnabled = false
   431  		restartTime = time.Now()
   432  		for _, id := range ids {
   433  			node := net.GetNode(id)
   434  			if err := net.Start(node.ID()); err != nil {
   435  				return fmt.Errorf("error starting node %s: %s", node.ID().TerminalString(), err)
   436  			}
   437  			if err := triggerChecks(trigger, net, node.ID()); err != nil {
   438  				return fmt.Errorf("error triggering checks for node %s: %s", node.ID().TerminalString(), err)
   439  			}
   440  		}
   441  
   442  		log.Info(fmt.Sprintf("restarting nodes took: %s", time.Since(restartTime)))
   443  
   444  		return nil
   445  	}
   446  	//connects in a chain
   447  	wg := sync.WaitGroup{}
   448  	//connects in a ring
   449  	for i := range ids {
   450  		for j := 1; j <= conns; j++ {
   451  			k := (i + j) % len(ids)
   452  			if k == i {
   453  				k = (k + 1) % len(ids)
   454  			}
   455  			wg.Add(1)
   456  			go func(i, k int) {
   457  				defer wg.Done()
   458  				net.Connect(ids[i], ids[k])
   459  			}(i, k)
   460  		}
   461  	}
   462  	wg.Wait()
   463  	log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
   464  	// construct the peer pot, so that kademlia health can be checked
   465  	check := func(ctx context.Context, id discover.NodeID) (bool, error) {
   466  		select {
   467  		case <-ctx.Done():
   468  			return false, ctx.Err()
   469  		default:
   470  		}
   471  
   472  		node := net.GetNode(id)
   473  		if node == nil {
   474  			return false, fmt.Errorf("unknown node: %s", id)
   475  		}
   476  		client, err := node.Client()
   477  		if err != nil {
   478  			return false, fmt.Errorf("error getting node client: %s", err)
   479  		}
   480  		healthy := &network.Health{}
   481  		addr := common.Bytes2Hex(network.ToOverlayAddr(id.Bytes()))
   482  		if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); err != nil {
   483  			return false, fmt.Errorf("error getting node health: %s", err)
   484  		}
   485  		log.Info(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v, saturated: %v", id, healthy.GotNN, healthy.KnowNN, healthy.Full))
   486  
   487  		return healthy.KnowNN && healthy.GotNN && healthy.Full, nil
   488  	}
   489  
   490  	// 64 nodes ~ 1min
   491  	// 128 nodes ~
   492  	timeout := 300 * time.Second
   493  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
   494  	defer cancel()
   495  	result := simulations.NewSimulation(net).Run(ctx, &simulations.Step{
   496  		Action:  action,
   497  		Trigger: trigger,
   498  		Expect: &simulations.Expectation{
   499  			Nodes: ids,
   500  			Check: check,
   501  		},
   502  	})
   503  	if result.Error != nil {
   504  		return result, nil
   505  	}
   506  
   507  	return result, nil
   508  }
   509  
   510  // triggerChecks triggers a simulation step check whenever a peer is added or
   511  // removed from the given node, and also every second to avoid a race between
   512  // peer events and kademlia becoming healthy
   513  func triggerChecks(trigger chan discover.NodeID, net *simulations.Network, id discover.NodeID) error {
   514  	node := net.GetNode(id)
   515  	if node == nil {
   516  		return fmt.Errorf("unknown node: %s", id)
   517  	}
   518  	client, err := node.Client()
   519  	if err != nil {
   520  		return err
   521  	}
   522  	events := make(chan *p2p.PeerEvent)
   523  	sub, err := client.Subscribe(context.Background(), "admin", events, "peerEvents")
   524  	if err != nil {
   525  		return fmt.Errorf("error getting peer events for node %v: %s", id, err)
   526  	}
   527  	go func() {
   528  		defer sub.Unsubscribe()
   529  
   530  		tick := time.NewTicker(time.Second)
   531  		defer tick.Stop()
   532  
   533  		for {
   534  			select {
   535  			case <-events:
   536  				trigger <- id
   537  			case <-tick.C:
   538  				trigger <- id
   539  			case err := <-sub.Err():
   540  				if err != nil {
   541  					log.Error(fmt.Sprintf("error getting peer events for node %v", id), "err", err)
   542  				}
   543  				return
   544  			}
   545  		}
   546  	}()
   547  	return nil
   548  }
   549  
   550  func newService(ctx *adapters.ServiceContext) (node.Service, error) {
   551  	host := adapters.ExternalIP()
   552  
   553  	addr := network.NewAddrFromNodeIDAndPort(ctx.Config.ID, host, ctx.Config.Port)
   554  
   555  	kp := network.NewKadParams()
   556  	kp.MinProxBinSize = testMinProxBinSize
   557  
   558  	if ctx.Config.Reachable != nil {
   559  		kp.Reachable = func(o *network.BzzAddr) bool {
   560  			return ctx.Config.Reachable(o.ID())
   561  		}
   562  	}
   563  	kad := network.NewKademlia(addr.Over(), kp)
   564  	hp := network.NewHiveParams()
   565  	hp.KeepAliveInterval = time.Duration(200) * time.Millisecond
   566  	hp.Discovery = discoveryEnabled
   567  
   568  	log.Info(fmt.Sprintf("discovery for nodeID %s is %t", ctx.Config.ID.String(), hp.Discovery))
   569  
   570  	config := &network.BzzConfig{
   571  		OverlayAddr:  addr.Over(),
   572  		UnderlayAddr: addr.Under(),
   573  		HiveParams:   hp,
   574  	}
   575  
   576  	if persistenceEnabled {
   577  		log.Info(fmt.Sprintf("persistence enabled for nodeID %s", ctx.Config.ID.String()))
   578  		store, err := getDbStore(ctx.Config.ID.String())
   579  		if err != nil {
   580  			return nil, err
   581  		}
   582  		return network.NewBzz(config, kad, store, nil, nil), nil
   583  	}
   584  
   585  	return network.NewBzz(config, kad, nil, nil, nil), nil
   586  }