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