github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/simulations/network_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 19:16:42</date> 10 //</624450107168788480> 11 12 13 package simulations 14 15 import ( 16 "context" 17 "encoding/json" 18 "fmt" 19 "strconv" 20 "strings" 21 "testing" 22 "time" 23 24 "github.com/ethereum/go-ethereum/log" 25 "github.com/ethereum/go-ethereum/node" 26 "github.com/ethereum/go-ethereum/p2p/enode" 27 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 28 ) 29 30 //测试使用最小服务创建的快照只包含预期的连接 31 //当加载此快照时,网络仅包含这些相同的连接 32 func TestSnapshot(t *testing.T) { 33 34 //第一部分 35 //从环网创建快照 36 37 //这是一个最小的服务,其协议在退出前只接受一条消息或关闭连接 38 adapter := adapters.NewSimAdapter(adapters.Services{ 39 "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) { 40 return NewNoopService(nil), nil 41 }, 42 }) 43 44 //创建网络 45 network := NewNetwork(adapter, &NetworkConfig{ 46 DefaultService: "noopwoop", 47 }) 48 //\t要考虑成为网络成员,请在关机时设置为true threadsafe 49 runningOne := true 50 defer func() { 51 if runningOne { 52 network.Shutdown() 53 } 54 }() 55 56 //创建和启动节点 57 nodeCount := 20 58 ids := make([]enode.ID, nodeCount) 59 for i := 0; i < nodeCount; i++ { 60 conf := adapters.RandomNodeConfig() 61 node, err := network.NewNodeWithConfig(conf) 62 if err != nil { 63 t.Fatalf("error creating node: %s", err) 64 } 65 if err := network.Start(node.ID()); err != nil { 66 t.Fatalf("error starting node: %s", err) 67 } 68 ids[i] = node.ID() 69 } 70 71 //订阅对等事件 72 evC := make(chan *Event) 73 sub := network.Events().Subscribe(evC) 74 defer sub.Unsubscribe() 75 76 //连接环中的节点 77 //生成单独的线程以避免事件侦听器中的死锁 78 go func() { 79 for i, id := range ids { 80 peerID := ids[(i+1)%len(ids)] 81 if err := network.Connect(id, peerID); err != nil { 82 t.Fatal(err) 83 } 84 } 85 }() 86 87 //收集达到预期数量的连接事件 88 ctx, cancel := context.WithTimeout(context.TODO(), time.Second) 89 defer cancel() 90 checkIds := make(map[enode.ID][]enode.ID) 91 connEventCount := nodeCount 92 OUTER: 93 for { 94 select { 95 case <-ctx.Done(): 96 t.Fatal(ctx.Err()) 97 case ev := <-evC: 98 if ev.Type == EventTypeConn && !ev.Control { 99 100 //断开时失败 101 if !ev.Conn.Up { 102 t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other) 103 } 104 checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other) 105 checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One) 106 connEventCount-- 107 log.Debug("ev", "count", connEventCount) 108 if connEventCount == 0 { 109 break OUTER 110 } 111 } 112 } 113 } 114 115 //创建当前网络的快照 116 snap, err := network.Snapshot() 117 if err != nil { 118 t.Fatal(err) 119 } 120 j, err := json.Marshal(snap) 121 if err != nil { 122 t.Fatal(err) 123 } 124 log.Debug("snapshot taken", "nodes", len(snap.Nodes), "conns", len(snap.Conns), "json", string(j)) 125 126 //验证对齐元素编号是否签出 127 if len(checkIds) != len(snap.Conns) || len(checkIds) != len(snap.Nodes) { 128 t.Fatalf("snapshot wrong node,conn counts %d,%d != %d", len(snap.Nodes), len(snap.Conns), len(checkIds)) 129 } 130 131 //关闭SIM网络 132 runningOne = false 133 sub.Unsubscribe() 134 network.Shutdown() 135 136 //检查快照中是否有所有预期的连接 137 for nodid, nodConns := range checkIds { 138 for _, nodConn := range nodConns { 139 var match bool 140 for _, snapConn := range snap.Conns { 141 if snapConn.One == nodid && snapConn.Other == nodConn { 142 match = true 143 break 144 } else if snapConn.Other == nodid && snapConn.One == nodConn { 145 match = true 146 break 147 } 148 } 149 if !match { 150 t.Fatalf("snapshot missing conn %v -> %v", nodid, nodConn) 151 } 152 } 153 } 154 log.Info("snapshot checked") 155 156 //第二部分 157 //加载快照并验证是否形成完全相同的连接 158 159 adapter = adapters.NewSimAdapter(adapters.Services{ 160 "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) { 161 return NewNoopService(nil), nil 162 }, 163 }) 164 network = NewNetwork(adapter, &NetworkConfig{ 165 DefaultService: "noopwoop", 166 }) 167 defer func() { 168 network.Shutdown() 169 }() 170 171 //订阅对等事件 172 //每个节点启动和连接事件将生成一个额外的控制事件 173 //因此,将计数乘以2 174 evC = make(chan *Event, (len(snap.Conns)*2)+(len(snap.Nodes)*2)) 175 sub = network.Events().Subscribe(evC) 176 defer sub.Unsubscribe() 177 178 //加载快照 179 //生成单独的线程以避免事件侦听器中的死锁 180 err = network.Load(snap) 181 if err != nil { 182 t.Fatal(err) 183 } 184 185 //收集达到预期数量的连接事件 186 ctx, cancel = context.WithTimeout(context.TODO(), time.Second*3) 187 defer cancel() 188 189 connEventCount = nodeCount 190 191 OUTER_TWO: 192 for { 193 select { 194 case <-ctx.Done(): 195 t.Fatal(ctx.Err()) 196 case ev := <-evC: 197 if ev.Type == EventTypeConn && !ev.Control { 198 199 //断开时失败 200 if !ev.Conn.Up { 201 t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other) 202 } 203 log.Debug("conn", "on", ev.Conn.One, "other", ev.Conn.Other) 204 checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other) 205 checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One) 206 connEventCount-- 207 log.Debug("ev", "count", connEventCount) 208 if connEventCount == 0 { 209 break OUTER_TWO 210 } 211 } 212 } 213 } 214 215 //检查网络中的所有预期连接 216 for _, snapConn := range snap.Conns { 217 var match bool 218 for nodid, nodConns := range checkIds { 219 for _, nodConn := range nodConns { 220 if snapConn.One == nodid && snapConn.Other == nodConn { 221 match = true 222 break 223 } else if snapConn.Other == nodid && snapConn.One == nodConn { 224 match = true 225 break 226 } 227 } 228 } 229 if !match { 230 t.Fatalf("network missing conn %v -> %v", snapConn.One, snapConn.Other) 231 } 232 } 233 234 //验证在合理时间内收集到的连接事件之后,网络没有生成任何其他附加连接事件。 235 ctx, cancel = context.WithTimeout(context.TODO(), time.Second) 236 defer cancel() 237 select { 238 case <-ctx.Done(): 239 case ev := <-evC: 240 if ev.Type == EventTypeConn { 241 t.Fatalf("Superfluous conn found %v -> %v", ev.Conn.One, ev.Conn.Other) 242 } 243 } 244 245 //此测试验证快照中的所有连接 246 //在网络中创建。 247 t.Run("conns after load", func(t *testing.T) { 248 //创建新网络。 249 n := NewNetwork( 250 adapters.NewSimAdapter(adapters.Services{ 251 "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) { 252 return NewNoopService(nil), nil 253 }, 254 }), 255 &NetworkConfig{ 256 DefaultService: "noopwoop", 257 }, 258 ) 259 defer n.Shutdown() 260 261 //加载同一快照。 262 err := n.Load(snap) 263 if err != nil { 264 t.Fatal(err) 265 } 266 267 //检查快照中的每个连接 268 //如果它也在网络中。 269 for _, c := range snap.Conns { 270 if n.GetConn(c.One, c.Other) == nil { 271 t.Errorf("missing connection: %s -> %s", c.One, c.Other) 272 } 273 } 274 }) 275 } 276 277 //TestNetworkSimulation使用每个节点创建多节点仿真网络 278 //在环形拓扑中连接,检查所有节点是否成功握手 279 //彼此之间,快照完全代表所需的拓扑 280 func TestNetworkSimulation(t *testing.T) { 281 //使用20个testservice节点创建模拟网络 282 adapter := adapters.NewSimAdapter(adapters.Services{ 283 "test": newTestService, 284 }) 285 network := NewNetwork(adapter, &NetworkConfig{ 286 DefaultService: "test", 287 }) 288 defer network.Shutdown() 289 nodeCount := 20 290 ids := make([]enode.ID, nodeCount) 291 for i := 0; i < nodeCount; i++ { 292 conf := adapters.RandomNodeConfig() 293 node, err := network.NewNodeWithConfig(conf) 294 if err != nil { 295 t.Fatalf("error creating node: %s", err) 296 } 297 if err := network.Start(node.ID()); err != nil { 298 t.Fatalf("error starting node: %s", err) 299 } 300 ids[i] = node.ID() 301 } 302 303 //执行连接环中节点的检查(因此每个节点 304 //然后检查所有节点 305 //通过检查他们的对等计数进行了两次握手 306 action := func(_ context.Context) error { 307 for i, id := range ids { 308 peerID := ids[(i+1)%len(ids)] 309 if err := network.Connect(id, peerID); err != nil { 310 return err 311 } 312 } 313 return nil 314 } 315 check := func(ctx context.Context, id enode.ID) (bool, error) { 316 //检查一下我们的时间没有用完 317 select { 318 case <-ctx.Done(): 319 return false, ctx.Err() 320 default: 321 } 322 323 //获取节点 324 node := network.GetNode(id) 325 if node == nil { 326 return false, fmt.Errorf("unknown node: %s", id) 327 } 328 329 //检查它是否有两个同龄人 330 client, err := node.Client() 331 if err != nil { 332 return false, err 333 } 334 var peerCount int64 335 if err := client.CallContext(ctx, &peerCount, "test_peerCount"); err != nil { 336 return false, err 337 } 338 switch { 339 case peerCount < 2: 340 return false, nil 341 case peerCount == 2: 342 return true, nil 343 default: 344 return false, fmt.Errorf("unexpected peerCount: %d", peerCount) 345 } 346 } 347 348 timeout := 30 * time.Second 349 ctx, cancel := context.WithTimeout(context.Background(), timeout) 350 defer cancel() 351 352 //每100毫秒触发一次检查 353 trigger := make(chan enode.ID) 354 go triggerChecks(ctx, ids, trigger, 100*time.Millisecond) 355 356 result := NewSimulation(network).Run(ctx, &Step{ 357 Action: action, 358 Trigger: trigger, 359 Expect: &Expectation{ 360 Nodes: ids, 361 Check: check, 362 }, 363 }) 364 if result.Error != nil { 365 t.Fatalf("simulation failed: %s", result.Error) 366 } 367 368 //获取网络快照并检查它是否包含正确的拓扑 369 snap, err := network.Snapshot() 370 if err != nil { 371 t.Fatal(err) 372 } 373 if len(snap.Nodes) != nodeCount { 374 t.Fatalf("expected snapshot to contain %d nodes, got %d", nodeCount, len(snap.Nodes)) 375 } 376 if len(snap.Conns) != nodeCount { 377 t.Fatalf("expected snapshot to contain %d connections, got %d", nodeCount, len(snap.Conns)) 378 } 379 for i, id := range ids { 380 conn := snap.Conns[i] 381 if conn.One != id { 382 t.Fatalf("expected conn[%d].One to be %s, got %s", i, id, conn.One) 383 } 384 peerID := ids[(i+1)%len(ids)] 385 if conn.Other != peerID { 386 t.Fatalf("expected conn[%d].Other to be %s, got %s", i, peerID, conn.Other) 387 } 388 } 389 } 390 391 func triggerChecks(ctx context.Context, ids []enode.ID, trigger chan enode.ID, interval time.Duration) { 392 tick := time.NewTicker(interval) 393 defer tick.Stop() 394 for { 395 select { 396 case <-tick.C: 397 for _, id := range ids { 398 select { 399 case trigger <- id: 400 case <-ctx.Done(): 401 return 402 } 403 } 404 case <-ctx.Done(): 405 return 406 } 407 } 408 } 409 410 //\todo:重构以实现shapshots 411 //一旦将配置方法从 412 //swarm/网络/仿真/connect.go 413 func BenchmarkMinimalService(b *testing.B) { 414 b.Run("ring/32", benchmarkMinimalServiceTmp) 415 } 416 417 func benchmarkMinimalServiceTmp(b *testing.B) { 418 419 //停止计时器以丢弃设置时间污染 420 args := strings.Split(b.Name(), "/") 421 nodeCount, err := strconv.ParseInt(args[2], 10, 16) 422 if err != nil { 423 b.Fatal(err) 424 } 425 426 for i := 0; i < b.N; i++ { 427 //这是一个最小的服务,其协议将在协议运行时关闭通道。 428 //使服务启动和协议实际运行所需的时间成为可能 429 protoCMap := make(map[enode.ID]map[enode.ID]chan struct{}) 430 adapter := adapters.NewSimAdapter(adapters.Services{ 431 "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) { 432 protoCMap[ctx.Config.ID] = make(map[enode.ID]chan struct{}) 433 svc := NewNoopService(protoCMap[ctx.Config.ID]) 434 return svc, nil 435 }, 436 }) 437 438 //创建网络 439 network := NewNetwork(adapter, &NetworkConfig{ 440 DefaultService: "noopwoop", 441 }) 442 defer network.Shutdown() 443 444 //创建和启动节点 445 ids := make([]enode.ID, nodeCount) 446 for i := 0; i < int(nodeCount); i++ { 447 conf := adapters.RandomNodeConfig() 448 node, err := network.NewNodeWithConfig(conf) 449 if err != nil { 450 b.Fatalf("error creating node: %s", err) 451 } 452 if err := network.Start(node.ID()); err != nil { 453 b.Fatalf("error starting node: %s", err) 454 } 455 ids[i] = node.ID() 456 } 457 458 //准备,设置,去 459 b.ResetTimer() 460 461 //连接环中的节点 462 for i, id := range ids { 463 peerID := ids[(i+1)%len(ids)] 464 if err := network.Connect(id, peerID); err != nil { 465 b.Fatal(err) 466 } 467 } 468 469 //等待所有协议发出关闭信号 470 ctx, cancel := context.WithTimeout(context.TODO(), time.Second) 471 defer cancel() 472 for nodid, peers := range protoCMap { 473 for peerid, peerC := range peers { 474 log.Debug("getting ", "node", nodid, "peer", peerid) 475 select { 476 case <-ctx.Done(): 477 b.Fatal(ctx.Err()) 478 case <-peerC: 479 } 480 } 481 } 482 } 483 } 484