github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/network/stream/visualized_snapshot_sync_sim_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:44</date> 10 //</624450116014575616> 11 12 13 //+使用服务器生成 14 15 package stream 16 17 import ( 18 "bytes" 19 "context" 20 "errors" 21 "fmt" 22 "io" 23 "os" 24 "sync" 25 "testing" 26 "time" 27 28 "github.com/ethereum/go-ethereum/node" 29 "github.com/ethereum/go-ethereum/p2p" 30 "github.com/ethereum/go-ethereum/p2p/enode" 31 "github.com/ethereum/go-ethereum/p2p/protocols" 32 "github.com/ethereum/go-ethereum/p2p/simulations" 33 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 34 "github.com/ethereum/go-ethereum/rlp" 35 "github.com/ethereum/go-ethereum/swarm/log" 36 "github.com/ethereum/go-ethereum/swarm/network" 37 "github.com/ethereum/go-ethereum/swarm/network/simulation" 38 "github.com/ethereum/go-ethereum/swarm/state" 39 "github.com/ethereum/go-ethereum/swarm/storage" 40 ) 41 42 /* 43 此文件中的测试需要使用 44 45 -tags=带服务器 46 47 另外,如果单独执行,它们将暂停,因为它们等待 48 用于可视化前端发送post/runsim消息。 49 **/ 50 51 52 //设置SIM,评估nodeCount和chunkCount并创建SIM 53 func setupSim(serviceMap map[string]simulation.ServiceFunc) (int, int, *simulation.Simulation) { 54 nodeCount := *nodes 55 chunkCount := *chunks 56 57 if nodeCount == 0 || chunkCount == 0 { 58 nodeCount = 32 59 chunkCount = 1 60 } 61 62 //使用服务器设置模拟,这意味着SIM卡无法运行 63 //直到它从前端接收到一个post/runsim 64 sim := simulation.New(serviceMap).WithServer(":8888") 65 return nodeCount, chunkCount, sim 66 } 67 68 //注意断开连接并等待健康 69 func watchSim(sim *simulation.Simulation) (context.Context, context.CancelFunc) { 70 ctx, cancelSimRun := context.WithTimeout(context.Background(), 1*time.Minute) 71 72 if _, err := sim.WaitTillHealthy(ctx); err != nil { 73 panic(err) 74 } 75 76 disconnections := sim.PeerEvents( 77 context.Background(), 78 sim.NodeIDs(), 79 simulation.NewPeerEventsFilter().Drop(), 80 ) 81 82 go func() { 83 for d := range disconnections { 84 log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID) 85 panic("unexpected disconnect") 86 cancelSimRun() 87 } 88 }() 89 90 return ctx, cancelSimRun 91 } 92 93 //此测试请求网络中存在伪造哈希 94 func TestNonExistingHashesWithServer(t *testing.T) { 95 96 nodeCount, _, sim := setupSim(retrievalSimServiceMap) 97 defer sim.Close() 98 99 err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount)) 100 if err != nil { 101 panic(err) 102 } 103 104 ctx, cancelSimRun := watchSim(sim) 105 defer cancelSimRun() 106 107 //为了获得一些有意义的可视化效果,这是有益的 108 //定义此测试的最短持续时间 109 testDuration := 20 * time.Second 110 111 result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error { 112 //检查节点的文件存储(netstore) 113 id := sim.Net.GetRandomUpNode().ID() 114 item, ok := sim.NodeItem(id, bucketKeyFileStore) 115 if !ok { 116 t.Fatalf("No filestore") 117 } 118 fileStore := item.(*storage.FileStore) 119 //创建伪哈希 120 fakeHash := storage.GenerateRandomChunk(1000).Address() 121 //尝试检索它-将把retrieverequestmsg传播到网络中 122 reader, _ := fileStore.Retrieve(context.TODO(), fakeHash) 123 if _, err := reader.Size(ctx, nil); err != nil { 124 log.Debug("expected error for non-existing chunk") 125 } 126 //睡眠,以便前端可以显示一些内容 127 time.Sleep(testDuration) 128 129 return nil 130 }) 131 if result.Error != nil { 132 sendSimTerminatedEvent(sim) 133 t.Fatal(result.Error) 134 } 135 136 sendSimTerminatedEvent(sim) 137 138 } 139 140 //向前端发送终止事件 141 func sendSimTerminatedEvent(sim *simulation.Simulation) { 142 evt := &simulations.Event{ 143 Type: EventTypeSimTerminated, 144 Control: false, 145 } 146 sim.Net.Events().Send(evt) 147 } 148 149 //此测试与快照同步测试相同, 150 //但是使用HTTP服务器 151 //它还发送一些自定义事件,以便前端 152 //可以可视化消息,如sendOfferedMsg、wantedHashesMsg、deliveryMsg 153 func TestSnapshotSyncWithServer(t *testing.T) { 154 //t.skip(“暂时禁用为Simulations.WaittillHealthy,不可信任”)。 155 156 //定义一个包装对象以便能够传递数据 157 wrapper := &netWrapper{} 158 159 nodeCount := *nodes 160 chunkCount := *chunks 161 162 if nodeCount == 0 || chunkCount == 0 { 163 nodeCount = 32 164 chunkCount = 1 165 } 166 167 log.Info(fmt.Sprintf("Running the simulation with %d nodes and %d chunks", nodeCount, chunkCount)) 168 169 sim := simulation.New(map[string]simulation.ServiceFunc{ 170 "streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) { 171 n := ctx.Config.Node() 172 addr := network.NewAddr(n) 173 store, datadir, err := createTestLocalStorageForID(n.ID(), addr) 174 if err != nil { 175 return nil, nil, err 176 } 177 bucket.Store(bucketKeyStore, store) 178 localStore := store.(*storage.LocalStore) 179 netStore, err := storage.NewNetStore(localStore, nil) 180 if err != nil { 181 return nil, nil, err 182 } 183 kad := network.NewKademlia(addr.Over(), network.NewKadParams()) 184 delivery := NewDelivery(kad, netStore) 185 netStore.NewNetFetcherFunc = network.NewFetcherFactory(dummyRequestFromPeers, true).New 186 187 r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{ 188 Retrieval: RetrievalDisabled, 189 Syncing: SyncingAutoSubscribe, 190 SyncUpdateDelay: 3 * time.Second, 191 }, nil) 192 193 tr := &testRegistry{ 194 Registry: r, 195 w: wrapper, 196 } 197 198 bucket.Store(bucketKeyRegistry, tr) 199 200 cleanup = func() { 201 netStore.Close() 202 tr.Close() 203 os.RemoveAll(datadir) 204 } 205 206 return tr, cleanup, nil 207 }, 208 }).WithServer(":8888") //从HTTP服务器开始 209 210 nodeCount, chunkCount, sim := setupSim(simServiceMap) 211 defer sim.Close() 212 213 log.Info("Initializing test config") 214 215 conf := &synctestConfig{} 216 //发现ID到该ID处预期的块索引的映射 217 conf.idToChunksMap = make(map[enode.ID][]int) 218 //发现ID的覆盖地址映射 219 conf.addrToIDMap = make(map[string]enode.ID) 220 //存储生成的块哈希的数组 221 conf.hashes = make([]storage.Address, 0) 222 //将网络传递到包装对象 223 wrapper.setNetwork(sim.Net) 224 err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount)) 225 if err != nil { 226 panic(err) 227 } 228 229 ctx, cancelSimRun := watchSim(sim) 230 defer cancelSimRun() 231 232 //运行SIM 233 result := runSim(conf, ctx, sim, chunkCount) 234 235 //发送终止事件 236 evt := &simulations.Event{ 237 Type: EventTypeSimTerminated, 238 Control: false, 239 } 240 go sim.Net.Events().Send(evt) 241 242 if result.Error != nil { 243 panic(result.Error) 244 } 245 log.Info("Simulation ended") 246 } 247 248 //TestRegistry嵌入注册表 249 //它允许替换协议运行功能 250 type testRegistry struct { 251 *Registry 252 w *netWrapper 253 } 254 255 //协议替换协议的运行功能 256 func (tr *testRegistry) Protocols() []p2p.Protocol { 257 regProto := tr.Registry.Protocols() 258 //用testregistry的run函数设置'stream'协议的run函数 259 regProto[0].Run = tr.runProto 260 return regProto 261 } 262 263 //runproto是此测试的新覆盖协议的run函数 264 func (tr *testRegistry) runProto(p *p2p.Peer, rw p2p.MsgReadWriter) error { 265 //创建自定义的rw消息读写器 266 testRw := &testMsgReadWriter{ 267 MsgReadWriter: rw, 268 Peer: p, 269 w: tr.w, 270 Registry: tr.Registry, 271 } 272 //现在运行实际的上层“注册表”的协议函数 273 return tr.runProtocol(p, testRw) 274 } 275 276 //testmsgreadwriter是一个自定义的rw 277 //它将允许我们重复使用该消息两次 278 type testMsgReadWriter struct { 279 *Registry 280 p2p.MsgReadWriter 281 *p2p.Peer 282 w *netWrapper 283 } 284 285 //NetRapper包装器对象,以便我们可以传递数据 286 type netWrapper struct { 287 net *simulations.Network 288 } 289 290 //将网络设置为包装器以供以后使用(在自定义rw中使用) 291 func (w *netWrapper) setNetwork(n *simulations.Network) { 292 w.net = n 293 } 294 295 //从包装器获取网络(在自定义rw中使用) 296 func (w *netWrapper) getNetwork() *simulations.Network { 297 return w.net 298 } 299 300 //readmsg从基础msgreadwriter读取消息并发出 301 //“收到消息”事件 302 //我们这样做是因为我们对定制使用的消息的有效负载感兴趣 303 //在此测试中,但消息只能使用一次(stream io.reader) 304 func (ev *testMsgReadWriter) ReadMsg() (p2p.Msg, error) { 305 //从底层的rw读取消息 306 msg, err := ev.MsgReadWriter.ReadMsg() 307 if err != nil { 308 return msg, err 309 } 310 311 //不要对我们实际上不需要/不需要阅读的消息代码做任何事情 312 subCodes := []uint64{1, 2, 10} 313 found := false 314 for _, c := range subCodes { 315 if c == msg.Code { 316 found = true 317 } 318 } 319 //如果不是我们感兴趣的消息代码,请返回 320 if !found { 321 return msg, nil 322 } 323 324 //我们使用IO.teeReader,这样我们可以读两次消息 325 //有效负载是一个IO.reader,所以如果我们从中读取,实际的协议处理程序 326 //无法再访问它。 327 //但是我们需要这个处理程序能够正常地使用消息, 328 //好像我们不会在这里用那个信息做任何事情 329 var buf bytes.Buffer 330 tee := io.TeeReader(msg.Payload, &buf) 331 332 mcp := &p2p.Msg{ 333 Code: msg.Code, 334 Size: msg.Size, 335 ReceivedAt: msg.ReceivedAt, 336 Payload: tee, 337 } 338 //分配副本供以后使用 339 msg.Payload = &buf 340 341 //现在让我们看看这个消息 342 var wmsg protocols.WrappedMsg 343 err = mcp.Decode(&wmsg) 344 if err != nil { 345 log.Error(err.Error()) 346 return msg, err 347 } 348 //从代码创建新消息 349 val, ok := ev.Registry.GetSpec().NewMsg(mcp.Code) 350 if !ok { 351 return msg, errors.New(fmt.Sprintf("Invalid message code: %v", msg.Code)) 352 } 353 //解码它 354 if err := rlp.DecodeBytes(wmsg.Payload, val); err != nil { 355 return msg, errors.New(fmt.Sprintf("Decoding error <= %v: %v", msg, err)) 356 } 357 //现在,对于我们感兴趣的每种消息类型,创建一个自定义事件并发送它 358 var evt *simulations.Event 359 switch val := val.(type) { 360 case *OfferedHashesMsg: 361 evt = &simulations.Event{ 362 Type: EventTypeChunkOffered, 363 Node: ev.w.getNetwork().GetNode(ev.ID()), 364 Control: false, 365 Data: val.Hashes, 366 } 367 case *WantedHashesMsg: 368 evt = &simulations.Event{ 369 Type: EventTypeChunkWanted, 370 Node: ev.w.getNetwork().GetNode(ev.ID()), 371 Control: false, 372 } 373 case *ChunkDeliveryMsgSyncing: 374 evt = &simulations.Event{ 375 Type: EventTypeChunkDelivered, 376 Node: ev.w.getNetwork().GetNode(ev.ID()), 377 Control: false, 378 Data: val.Addr.String(), 379 } 380 } 381 if evt != nil { 382 //将自定义事件发送到订阅源;前端将侦听该事件并显示 383 ev.w.getNetwork().Events().Send(evt) 384 } 385 return msg, nil 386 } 387