github.com/dotlike13/wemix30_go@v1.8.23/swarm/network/stream/visualized_snapshot_sync_sim_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 // +build withserver 18 19 package stream 20 21 import ( 22 "bytes" 23 "context" 24 "errors" 25 "fmt" 26 "io" 27 "sync" 28 "testing" 29 "time" 30 31 "github.com/ethereum/go-ethereum/node" 32 "github.com/ethereum/go-ethereum/p2p" 33 "github.com/ethereum/go-ethereum/p2p/enode" 34 "github.com/ethereum/go-ethereum/p2p/protocols" 35 "github.com/ethereum/go-ethereum/p2p/simulations" 36 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 37 "github.com/ethereum/go-ethereum/rlp" 38 "github.com/ethereum/go-ethereum/swarm/log" 39 "github.com/ethereum/go-ethereum/swarm/network/simulation" 40 "github.com/ethereum/go-ethereum/swarm/state" 41 "github.com/ethereum/go-ethereum/swarm/storage" 42 ) 43 44 /* 45 The tests in this file need to be executed with 46 47 -tags=withserver 48 49 Also, they will stall if executed stand-alone, because they wait 50 for the visualization frontend to send a POST /runsim message. 51 */ 52 53 //setup the sim, evaluate nodeCount and chunkCount and create the sim 54 func setupSim(serviceMap map[string]simulation.ServiceFunc) (int, int, *simulation.Simulation) { 55 nodeCount := *nodes 56 chunkCount := *chunks 57 58 if nodeCount == 0 || chunkCount == 0 { 59 nodeCount = 32 60 chunkCount = 1 61 } 62 63 //setup the simulation with server, which means the sim won't run 64 //until it receives a POST /runsim from the frontend 65 sim := simulation.New(serviceMap).WithServer(":8888") 66 return nodeCount, chunkCount, sim 67 } 68 69 //This test requests bogus hashes into the network 70 func TestNonExistingHashesWithServer(t *testing.T) { 71 72 nodeCount, _, sim := setupSim(retrievalSimServiceMap) 73 defer sim.Close() 74 75 err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount)) 76 if err != nil { 77 panic(err) 78 } 79 80 //in order to get some meaningful visualization, it is beneficial 81 //to define a minimum duration of this test 82 testDuration := 20 * time.Second 83 84 result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) (err error) { 85 disconnected := watchDisconnections(ctx, sim) 86 defer func() { 87 if err != nil { 88 if yes, ok := disconnected.Load().(bool); ok && yes { 89 err = errors.New("disconnect events received") 90 } 91 } 92 }() 93 94 //check on the node's FileStore (netstore) 95 id := sim.Net.GetRandomUpNode().ID() 96 item, ok := sim.NodeItem(id, bucketKeyFileStore) 97 if !ok { 98 return errors.New("No filestore") 99 } 100 fileStore := item.(*storage.FileStore) 101 //create a bogus hash 102 fakeHash := storage.GenerateRandomChunk(1000).Address() 103 //try to retrieve it - will propagate RetrieveRequestMsg into the network 104 reader, _ := fileStore.Retrieve(context.TODO(), fakeHash) 105 if _, err := reader.Size(ctx, nil); err != nil { 106 log.Debug("expected error for non-existing chunk") 107 } 108 //sleep so that the frontend can have something to display 109 time.Sleep(testDuration) 110 111 return nil 112 }) 113 if result.Error != nil { 114 sendSimTerminatedEvent(sim) 115 t.Fatal(result.Error) 116 } 117 118 sendSimTerminatedEvent(sim) 119 120 } 121 122 //send a termination event to the frontend 123 func sendSimTerminatedEvent(sim *simulation.Simulation) { 124 evt := &simulations.Event{ 125 Type: EventTypeSimTerminated, 126 Control: false, 127 } 128 sim.Net.Events().Send(evt) 129 } 130 131 //This test is the same as the snapshot sync test, 132 //but with a HTTP server 133 //It also sends some custom events so that the frontend 134 //can visualize messages like SendOfferedMsg, WantedHashesMsg, DeliveryMsg 135 func TestSnapshotSyncWithServer(t *testing.T) { 136 //t.Skip("temporarily disabled as simulations.WaitTillHealthy cannot be trusted") 137 138 //define a wrapper object to be able to pass around data 139 wrapper := &netWrapper{} 140 141 nodeCount := *nodes 142 chunkCount := *chunks 143 144 if nodeCount == 0 || chunkCount == 0 { 145 nodeCount = 32 146 chunkCount = 1 147 } 148 149 log.Info(fmt.Sprintf("Running the simulation with %d nodes and %d chunks", nodeCount, chunkCount)) 150 151 sim := simulation.New(map[string]simulation.ServiceFunc{ 152 "streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) { 153 addr, netStore, delivery, clean, err := newNetStoreAndDeliveryWithRequestFunc(ctx, bucket, dummyRequestFromPeers) 154 if err != nil { 155 return nil, nil, err 156 } 157 158 r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{ 159 Retrieval: RetrievalDisabled, 160 Syncing: SyncingAutoSubscribe, 161 SyncUpdateDelay: 3 * time.Second, 162 }, nil) 163 164 tr := &testRegistry{ 165 Registry: r, 166 w: wrapper, 167 } 168 169 bucket.Store(bucketKeyRegistry, tr) 170 171 cleanup = func() { 172 tr.Close() 173 clean() 174 } 175 176 return tr, cleanup, nil 177 }, 178 }).WithServer(":8888") //start with the HTTP server 179 180 nodeCount, chunkCount, sim := setupSim(simServiceMap) 181 defer sim.Close() 182 183 log.Info("Initializing test config") 184 185 conf := &synctestConfig{} 186 //map of discover ID to indexes of chunks expected at that ID 187 conf.idToChunksMap = make(map[enode.ID][]int) 188 //map of overlay address to discover ID 189 conf.addrToIDMap = make(map[string]enode.ID) 190 //array where the generated chunk hashes will be stored 191 conf.hashes = make([]storage.Address, 0) 192 //pass the network to the wrapper object 193 wrapper.setNetwork(sim.Net) 194 err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount)) 195 if err != nil { 196 panic(err) 197 } 198 199 //run the sim 200 result := runSim(conf, ctx, sim, chunkCount) 201 202 //send terminated event 203 evt := &simulations.Event{ 204 Type: EventTypeSimTerminated, 205 Control: false, 206 } 207 go sim.Net.Events().Send(evt) 208 209 if result.Error != nil { 210 panic(result.Error) 211 } 212 log.Info("Simulation ended") 213 } 214 215 //testRegistry embeds registry 216 //it allows to replace the protocol run function 217 type testRegistry struct { 218 *Registry 219 w *netWrapper 220 } 221 222 //Protocols replaces the protocol's run function 223 func (tr *testRegistry) Protocols() []p2p.Protocol { 224 regProto := tr.Registry.Protocols() 225 //set the `stream` protocol's run function with the testRegistry's one 226 regProto[0].Run = tr.runProto 227 return regProto 228 } 229 230 //runProto is the new overwritten protocol's run function for this test 231 func (tr *testRegistry) runProto(p *p2p.Peer, rw p2p.MsgReadWriter) error { 232 //create a custom rw message ReadWriter 233 testRw := &testMsgReadWriter{ 234 MsgReadWriter: rw, 235 Peer: p, 236 w: tr.w, 237 Registry: tr.Registry, 238 } 239 //now run the actual upper layer `Registry`'s protocol function 240 return tr.runProtocol(p, testRw) 241 } 242 243 //testMsgReadWriter is a custom rw 244 //it will allow us to re-use the message twice 245 type testMsgReadWriter struct { 246 *Registry 247 p2p.MsgReadWriter 248 *p2p.Peer 249 w *netWrapper 250 } 251 252 //netWrapper wrapper object so we can pass data around 253 type netWrapper struct { 254 net *simulations.Network 255 } 256 257 //set the network to the wrapper for later use (used inside the custom rw) 258 func (w *netWrapper) setNetwork(n *simulations.Network) { 259 w.net = n 260 } 261 262 //get he network from the wrapper (used inside the custom rw) 263 func (w *netWrapper) getNetwork() *simulations.Network { 264 return w.net 265 } 266 267 // ReadMsg reads a message from the underlying MsgReadWriter and emits a 268 // "message received" event 269 //we do this because we are interested in the Payload of the message for custom use 270 //in this test, but messages can only be consumed once (stream io.Reader) 271 func (ev *testMsgReadWriter) ReadMsg() (p2p.Msg, error) { 272 //read the message from the underlying rw 273 msg, err := ev.MsgReadWriter.ReadMsg() 274 if err != nil { 275 return msg, err 276 } 277 278 //don't do anything with message codes we actually are not needing/reading 279 subCodes := []uint64{1, 2, 10} 280 found := false 281 for _, c := range subCodes { 282 if c == msg.Code { 283 found = true 284 } 285 } 286 //just return if not a msg code we are interested in 287 if !found { 288 return msg, nil 289 } 290 291 //we use a io.TeeReader so that we can read the message twice 292 //the Payload is a io.Reader, so if we read from it, the actual protocol handler 293 //cannot access it anymore. 294 //But we need that handler to be able to consume the message as normal, 295 //as if we would not do anything here with that message 296 var buf bytes.Buffer 297 tee := io.TeeReader(msg.Payload, &buf) 298 299 mcp := &p2p.Msg{ 300 Code: msg.Code, 301 Size: msg.Size, 302 ReceivedAt: msg.ReceivedAt, 303 Payload: tee, 304 } 305 //assign the copy for later use 306 msg.Payload = &buf 307 308 //now let's look into the message 309 var wmsg protocols.WrappedMsg 310 err = mcp.Decode(&wmsg) 311 if err != nil { 312 log.Error(err.Error()) 313 return msg, err 314 } 315 //create a new message from the code 316 val, ok := ev.Registry.GetSpec().NewMsg(mcp.Code) 317 if !ok { 318 return msg, errors.New(fmt.Sprintf("Invalid message code: %v", msg.Code)) 319 } 320 //decode it 321 if err := rlp.DecodeBytes(wmsg.Payload, val); err != nil { 322 return msg, errors.New(fmt.Sprintf("Decoding error <= %v: %v", msg, err)) 323 } 324 //now for every message type we are interested in, create a custom event and send it 325 var evt *simulations.Event 326 switch val := val.(type) { 327 case *OfferedHashesMsg: 328 evt = &simulations.Event{ 329 Type: EventTypeChunkOffered, 330 Node: ev.w.getNetwork().GetNode(ev.ID()), 331 Control: false, 332 Data: val.Hashes, 333 } 334 case *WantedHashesMsg: 335 evt = &simulations.Event{ 336 Type: EventTypeChunkWanted, 337 Node: ev.w.getNetwork().GetNode(ev.ID()), 338 Control: false, 339 } 340 case *ChunkDeliveryMsgSyncing: 341 evt = &simulations.Event{ 342 Type: EventTypeChunkDelivered, 343 Node: ev.w.getNetwork().GetNode(ev.ID()), 344 Control: false, 345 Data: val.Addr.String(), 346 } 347 } 348 if evt != nil { 349 //send custom event to feed; frontend will listen to it and display 350 ev.w.getNetwork().Events().Send(evt) 351 } 352 return msg, nil 353 }