github.com/codingfuture/orig-energi3@v0.8.4/swarm/network/stream/common_test.go (about) 1 // Copyright 2018 The Energi Core Authors 2 // Copyright 2018 The go-ethereum Authors 3 // This file is part of the Energi Core library. 4 // 5 // The Energi Core library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The Energi Core library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 17 18 package stream 19 20 import ( 21 "context" 22 "errors" 23 "flag" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "math/rand" 28 "os" 29 "strings" 30 "sync" 31 "sync/atomic" 32 "time" 33 34 "github.com/ethereum/go-ethereum/log" 35 "github.com/ethereum/go-ethereum/p2p/enode" 36 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 37 p2ptest "github.com/ethereum/go-ethereum/p2p/testing" 38 "github.com/ethereum/go-ethereum/swarm/network" 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 mockmem "github.com/ethereum/go-ethereum/swarm/storage/mock/mem" 43 "github.com/ethereum/go-ethereum/swarm/testutil" 44 colorable "github.com/mattn/go-colorable" 45 ) 46 47 var ( 48 loglevel = flag.Int("loglevel", 2, "verbosity of logs") 49 nodes = flag.Int("nodes", 0, "number of nodes") 50 chunks = flag.Int("chunks", 0, "number of chunks") 51 useMockStore = flag.Bool("mockstore", false, "disabled mock store (default: enabled)") 52 longrunning = flag.Bool("longrunning", false, "do run long-running tests") 53 54 bucketKeyDB = simulation.BucketKey("db") 55 bucketKeyStore = simulation.BucketKey("store") 56 bucketKeyFileStore = simulation.BucketKey("filestore") 57 bucketKeyNetStore = simulation.BucketKey("netstore") 58 bucketKeyDelivery = simulation.BucketKey("delivery") 59 bucketKeyRegistry = simulation.BucketKey("registry") 60 61 chunkSize = 4096 62 pof = network.Pof 63 ) 64 65 func init() { 66 testing.Init() 67 flag.Parse() 68 rand.Seed(time.Now().UnixNano()) 69 70 log.PrintOrigins(true) 71 log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) 72 } 73 74 // newNetStoreAndDelivery is a default constructor for BzzAddr, NetStore and Delivery, used in Simulations 75 func newNetStoreAndDelivery(ctx *adapters.ServiceContext, bucket *sync.Map) (*network.BzzAddr, *storage.NetStore, *Delivery, func(), error) { 76 addr := network.NewAddr(ctx.Config.Node()) 77 78 netStore, delivery, cleanup, err := netStoreAndDeliveryWithAddr(ctx, bucket, addr) 79 if err != nil { 80 return nil, nil, nil, nil, err 81 } 82 83 netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New 84 85 return addr, netStore, delivery, cleanup, nil 86 } 87 88 // newNetStoreAndDeliveryWithBzzAddr is a constructor for NetStore and Delivery, used in Simulations, accepting any BzzAddr 89 func newNetStoreAndDeliveryWithBzzAddr(ctx *adapters.ServiceContext, bucket *sync.Map, addr *network.BzzAddr) (*storage.NetStore, *Delivery, func(), error) { 90 netStore, delivery, cleanup, err := netStoreAndDeliveryWithAddr(ctx, bucket, addr) 91 if err != nil { 92 return nil, nil, nil, err 93 } 94 95 netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New 96 97 return netStore, delivery, cleanup, nil 98 } 99 100 // newNetStoreAndDeliveryWithRequestFunc is a constructor for NetStore and Delivery, used in Simulations, accepting any NetStore.RequestFunc 101 func newNetStoreAndDeliveryWithRequestFunc(ctx *adapters.ServiceContext, bucket *sync.Map, rf network.RequestFunc) (*network.BzzAddr, *storage.NetStore, *Delivery, func(), error) { 102 addr := network.NewAddr(ctx.Config.Node()) 103 104 netStore, delivery, cleanup, err := netStoreAndDeliveryWithAddr(ctx, bucket, addr) 105 if err != nil { 106 return nil, nil, nil, nil, err 107 } 108 109 netStore.NewNetFetcherFunc = network.NewFetcherFactory(rf, true).New 110 111 return addr, netStore, delivery, cleanup, nil 112 } 113 114 func netStoreAndDeliveryWithAddr(ctx *adapters.ServiceContext, bucket *sync.Map, addr *network.BzzAddr) (*storage.NetStore, *Delivery, func(), error) { 115 n := ctx.Config.Node() 116 117 store, datadir, err := createTestLocalStorageForID(n.ID(), addr) 118 if *useMockStore { 119 store, datadir, err = createMockStore(mockmem.NewGlobalStore(), n.ID(), addr) 120 } 121 if err != nil { 122 return nil, nil, nil, err 123 } 124 localStore := store.(*storage.LocalStore) 125 netStore, err := storage.NewNetStore(localStore, nil) 126 if err != nil { 127 return nil, nil, nil, err 128 } 129 130 fileStore := storage.NewFileStore(netStore, storage.NewFileStoreParams()) 131 132 kad := network.NewKademlia(addr.Over(), network.NewKadParams()) 133 delivery := NewDelivery(kad, netStore) 134 135 bucket.Store(bucketKeyStore, store) 136 bucket.Store(bucketKeyDB, netStore) 137 bucket.Store(bucketKeyDelivery, delivery) 138 bucket.Store(bucketKeyFileStore, fileStore) 139 140 cleanup := func() { 141 netStore.Close() 142 os.RemoveAll(datadir) 143 } 144 145 return netStore, delivery, cleanup, nil 146 } 147 148 func newStreamerTester(registryOptions *RegistryOptions) (*p2ptest.ProtocolTester, *Registry, *storage.LocalStore, func(), error) { 149 // setup 150 addr := network.RandomAddr() // tested peers peer address 151 to := network.NewKademlia(addr.OAddr, network.NewKadParams()) 152 153 // temp datadir 154 datadir, err := ioutil.TempDir("", "streamer") 155 if err != nil { 156 return nil, nil, nil, nil, err 157 } 158 removeDataDir := func() { 159 os.RemoveAll(datadir) 160 } 161 162 params := storage.NewDefaultLocalStoreParams() 163 params.Init(datadir) 164 params.BaseKey = addr.Over() 165 166 localStore, err := storage.NewTestLocalStoreForAddr(params) 167 if err != nil { 168 removeDataDir() 169 return nil, nil, nil, nil, err 170 } 171 172 netStore, err := storage.NewNetStore(localStore, nil) 173 if err != nil { 174 removeDataDir() 175 return nil, nil, nil, nil, err 176 } 177 178 delivery := NewDelivery(to, netStore) 179 netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New 180 streamer := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), registryOptions, nil) 181 teardown := func() { 182 streamer.Close() 183 removeDataDir() 184 } 185 protocolTester := p2ptest.NewProtocolTester(addr.ID(), 1, streamer.runProtocol) 186 187 err = waitForPeers(streamer, 10*time.Second, 1) 188 if err != nil { 189 teardown() 190 return nil, nil, nil, nil, errors.New("timeout: peer is not created") 191 } 192 193 return protocolTester, streamer, localStore, teardown, nil 194 } 195 196 func waitForPeers(streamer *Registry, timeout time.Duration, expectedPeers int) error { 197 ticker := time.NewTicker(10 * time.Millisecond) 198 timeoutTimer := time.NewTimer(timeout) 199 for { 200 select { 201 case <-ticker.C: 202 if streamer.peersCount() >= expectedPeers { 203 return nil 204 } 205 case <-timeoutTimer.C: 206 return errors.New("timeout") 207 } 208 } 209 } 210 211 type roundRobinStore struct { 212 index uint32 213 stores []storage.ChunkStore 214 } 215 216 func newRoundRobinStore(stores ...storage.ChunkStore) *roundRobinStore { 217 return &roundRobinStore{ 218 stores: stores, 219 } 220 } 221 222 // not used in this context, only to fulfill ChunkStore interface 223 func (rrs *roundRobinStore) Has(ctx context.Context, addr storage.Address) bool { 224 panic("RoundRobinStor doesn't support HasChunk") 225 } 226 227 func (rrs *roundRobinStore) Get(ctx context.Context, addr storage.Address) (storage.Chunk, error) { 228 return nil, errors.New("get not well defined on round robin store") 229 } 230 231 func (rrs *roundRobinStore) Put(ctx context.Context, chunk storage.Chunk) error { 232 i := atomic.AddUint32(&rrs.index, 1) 233 idx := int(i) % len(rrs.stores) 234 return rrs.stores[idx].Put(ctx, chunk) 235 } 236 237 func (rrs *roundRobinStore) Close() { 238 for _, store := range rrs.stores { 239 store.Close() 240 } 241 } 242 243 func readAll(fileStore *storage.FileStore, hash []byte) (int64, error) { 244 r, _ := fileStore.Retrieve(context.TODO(), hash) 245 buf := make([]byte, 1024) 246 var n int 247 var total int64 248 var err error 249 for (total == 0 || n > 0) && err == nil { 250 n, err = r.ReadAt(buf, total) 251 total += int64(n) 252 } 253 if err != nil && err != io.EOF { 254 return total, err 255 } 256 return total, nil 257 } 258 259 func uploadFilesToNodes(sim *simulation.Simulation) ([]storage.Address, []string, error) { 260 nodes := sim.UpNodeIDs() 261 nodeCnt := len(nodes) 262 log.Debug(fmt.Sprintf("Uploading %d files to nodes", nodeCnt)) 263 //array holding generated files 264 rfiles := make([]string, nodeCnt) 265 //array holding the root hashes of the files 266 rootAddrs := make([]storage.Address, nodeCnt) 267 268 var err error 269 //for every node, generate a file and upload 270 for i, id := range nodes { 271 item, ok := sim.NodeItem(id, bucketKeyFileStore) 272 if !ok { 273 return nil, nil, fmt.Errorf("Error accessing localstore") 274 } 275 fileStore := item.(*storage.FileStore) 276 //generate a file 277 rfiles[i], err = generateRandomFile() 278 if err != nil { 279 return nil, nil, err 280 } 281 //store it (upload it) on the FileStore 282 ctx := context.TODO() 283 rk, wait, err := fileStore.Store(ctx, strings.NewReader(rfiles[i]), int64(len(rfiles[i])), false) 284 log.Debug("Uploaded random string file to node") 285 if err != nil { 286 return nil, nil, err 287 } 288 err = wait(ctx) 289 if err != nil { 290 return nil, nil, err 291 } 292 rootAddrs[i] = rk 293 } 294 return rootAddrs, rfiles, nil 295 } 296 297 //generate a random file (string) 298 func generateRandomFile() (string, error) { 299 //generate a random file size between minFileSize and maxFileSize 300 fileSize := rand.Intn(maxFileSize-minFileSize) + minFileSize 301 log.Debug(fmt.Sprintf("Generated file with filesize %d kB", fileSize)) 302 b := testutil.RandomBytes(1, fileSize*1024) 303 return string(b), nil 304 } 305 306 //create a local store for the given node 307 func createTestLocalStorageForID(id enode.ID, addr *network.BzzAddr) (storage.ChunkStore, string, error) { 308 var datadir string 309 var err error 310 datadir, err = ioutil.TempDir("", fmt.Sprintf("syncer-test-%s", id.TerminalString())) 311 if err != nil { 312 return nil, "", err 313 } 314 var store storage.ChunkStore 315 params := storage.NewDefaultLocalStoreParams() 316 params.ChunkDbPath = datadir 317 params.BaseKey = addr.Over() 318 store, err = storage.NewTestLocalStoreForAddr(params) 319 if err != nil { 320 os.RemoveAll(datadir) 321 return nil, "", err 322 } 323 return store, datadir, nil 324 } 325 326 // watchDisconnections receives simulation peer events in a new goroutine and sets atomic value 327 // disconnected to true in case of a disconnect event. 328 func watchDisconnections(ctx context.Context, sim *simulation.Simulation) (disconnected *boolean) { 329 log.Debug("Watching for disconnections") 330 disconnections := sim.PeerEvents( 331 ctx, 332 sim.NodeIDs(), 333 simulation.NewPeerEventsFilter().Drop(), 334 ) 335 disconnected = new(boolean) 336 go func() { 337 for { 338 select { 339 case <-ctx.Done(): 340 return 341 case d := <-disconnections: 342 if d.Error != nil { 343 log.Error("peer drop event error", "node", d.NodeID, "peer", d.PeerID, "err", d.Error) 344 } else { 345 log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID) 346 } 347 disconnected.set(true) 348 } 349 } 350 }() 351 return disconnected 352 } 353 354 // boolean is used to concurrently set 355 // and read a boolean value. 356 type boolean struct { 357 v bool 358 mu sync.RWMutex 359 } 360 361 // set sets the value. 362 func (b *boolean) set(v bool) { 363 b.mu.Lock() 364 defer b.mu.Unlock() 365 366 b.v = v 367 } 368 369 // bool reads the value. 370 func (b *boolean) bool() bool { 371 b.mu.RLock() 372 defer b.mu.RUnlock() 373 374 return b.v 375 }