github.com/jincm/wesharechain@v0.0.0-20210122032815-1537409ce26a/chain/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 //define a wrapper object to be able to pass around data 137 wrapper := &netWrapper{} 138 139 sim := simulation.New(map[string]simulation.ServiceFunc{ 140 "streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) { 141 addr, netStore, delivery, clean, err := newNetStoreAndDeliveryWithRequestFunc(ctx, bucket, dummyRequestFromPeers) 142 if err != nil { 143 return nil, nil, err 144 } 145 146 r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{ 147 Retrieval: RetrievalDisabled, 148 Syncing: SyncingAutoSubscribe, 149 SyncUpdateDelay: 3 * time.Second, 150 }, nil) 151 152 tr := &testRegistry{ 153 Registry: r, 154 w: wrapper, 155 } 156 157 bucket.Store(bucketKeyRegistry, tr) 158 159 cleanup = func() { 160 tr.Close() 161 clean() 162 } 163 164 return tr, cleanup, nil 165 }, 166 }).WithServer(":8888") //start with the HTTP server 167 168 nodeCount, chunkCount, sim := setupSim(simServiceMap) 169 defer sim.Close() 170 171 log.Info(fmt.Sprintf("Running the simulation with %d nodes and %d chunks", nodeCount, chunkCount)) 172 log.Info("Initializing test config") 173 174 conf := &synctestConfig{} 175 //map of discover ID to indexes of chunks expected at that ID 176 conf.idToChunksMap = make(map[enode.ID][]int) 177 //map of overlay address to discover ID 178 conf.addrToIDMap = make(map[string]enode.ID) 179 //array where the generated chunk hashes will be stored 180 conf.hashes = make([]storage.Address, 0) 181 //pass the network to the wrapper object 182 wrapper.setNetwork(sim.Net) 183 err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount)) 184 if err != nil { 185 panic(err) 186 } 187 188 //run the sim 189 result := runSim(conf, ctx, sim, chunkCount) 190 191 //send terminated event 192 evt := &simulations.Event{ 193 Type: EventTypeSimTerminated, 194 Control: false, 195 } 196 go sim.Net.Events().Send(evt) 197 198 if result.Error != nil { 199 panic(result.Error) 200 } 201 log.Info("Simulation ended") 202 } 203 204 //testRegistry embeds registry 205 //it allows to replace the protocol run function 206 type testRegistry struct { 207 *Registry 208 w *netWrapper 209 } 210 211 //Protocols replaces the protocol's run function 212 func (tr *testRegistry) Protocols() []p2p.Protocol { 213 regProto := tr.Registry.Protocols() 214 //set the `stream` protocol's run function with the testRegistry's one 215 regProto[0].Run = tr.runProto 216 return regProto 217 } 218 219 //runProto is the new overwritten protocol's run function for this test 220 func (tr *testRegistry) runProto(p *p2p.Peer, rw p2p.MsgReadWriter) error { 221 //create a custom rw message ReadWriter 222 testRw := &testMsgReadWriter{ 223 MsgReadWriter: rw, 224 Peer: p, 225 w: tr.w, 226 Registry: tr.Registry, 227 } 228 //now run the actual upper layer `Registry`'s protocol function 229 return tr.runProtocol(p, testRw) 230 } 231 232 //testMsgReadWriter is a custom rw 233 //it will allow us to re-use the message twice 234 type testMsgReadWriter struct { 235 *Registry 236 p2p.MsgReadWriter 237 *p2p.Peer 238 w *netWrapper 239 } 240 241 //netWrapper wrapper object so we can pass data around 242 type netWrapper struct { 243 net *simulations.Network 244 } 245 246 //set the network to the wrapper for later use (used inside the custom rw) 247 func (w *netWrapper) setNetwork(n *simulations.Network) { 248 w.net = n 249 } 250 251 //get he network from the wrapper (used inside the custom rw) 252 func (w *netWrapper) getNetwork() *simulations.Network { 253 return w.net 254 } 255 256 // ReadMsg reads a message from the underlying MsgReadWriter and emits a 257 // "message received" event 258 //we do this because we are interested in the Payload of the message for custom use 259 //in this test, but messages can only be consumed once (stream io.Reader) 260 func (ev *testMsgReadWriter) ReadMsg() (p2p.Msg, error) { 261 //read the message from the underlying rw 262 msg, err := ev.MsgReadWriter.ReadMsg() 263 if err != nil { 264 return msg, err 265 } 266 267 //don't do anything with message codes we actually are not needing/reading 268 subCodes := []uint64{1, 2, 10} 269 found := false 270 for _, c := range subCodes { 271 if c == msg.Code { 272 found = true 273 } 274 } 275 //just return if not a msg code we are interested in 276 if !found { 277 return msg, nil 278 } 279 280 //we use a io.TeeReader so that we can read the message twice 281 //the Payload is a io.Reader, so if we read from it, the actual protocol handler 282 //cannot access it anymore. 283 //But we need that handler to be able to consume the message as normal, 284 //as if we would not do anything here with that message 285 var buf bytes.Buffer 286 tee := io.TeeReader(msg.Payload, &buf) 287 288 mcp := &p2p.Msg{ 289 Code: msg.Code, 290 Size: msg.Size, 291 ReceivedAt: msg.ReceivedAt, 292 Payload: tee, 293 } 294 //assign the copy for later use 295 msg.Payload = &buf 296 297 //now let's look into the message 298 var wmsg protocols.WrappedMsg 299 err = mcp.Decode(&wmsg) 300 if err != nil { 301 log.Error(err.Error()) 302 return msg, err 303 } 304 //create a new message from the code 305 val, ok := ev.Registry.GetSpec().NewMsg(mcp.Code) 306 if !ok { 307 return msg, errors.New(fmt.Sprintf("Invalid message code: %v", msg.Code)) 308 } 309 //decode it 310 if err := rlp.DecodeBytes(wmsg.Payload, val); err != nil { 311 return msg, errors.New(fmt.Sprintf("Decoding error <= %v: %v", msg, err)) 312 } 313 //now for every message type we are interested in, create a custom event and send it 314 var evt *simulations.Event 315 switch val := val.(type) { 316 case *OfferedHashesMsg: 317 evt = &simulations.Event{ 318 Type: EventTypeChunkOffered, 319 Node: ev.w.getNetwork().GetNode(ev.ID()), 320 Control: false, 321 Data: val.Hashes, 322 } 323 case *WantedHashesMsg: 324 evt = &simulations.Event{ 325 Type: EventTypeChunkWanted, 326 Node: ev.w.getNetwork().GetNode(ev.ID()), 327 Control: false, 328 } 329 case *ChunkDeliveryMsgSyncing: 330 evt = &simulations.Event{ 331 Type: EventTypeChunkDelivered, 332 Node: ev.w.getNetwork().GetNode(ev.ID()), 333 Control: false, 334 Data: val.Addr.String(), 335 } 336 } 337 if evt != nil { 338 //send custom event to feed; frontend will listen to it and display 339 ev.w.getNetwork().Events().Send(evt) 340 } 341 return msg, nil 342 }