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