github.com/daragao/go-ethereum@v1.8.14-0.20180809141559-45eaef243198/swarm/network/stream/intervals_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 package stream 18 19 import ( 20 "context" 21 crand "crypto/rand" 22 "encoding/binary" 23 "fmt" 24 "io" 25 "os" 26 "sync" 27 "testing" 28 "time" 29 30 "github.com/ethereum/go-ethereum/log" 31 "github.com/ethereum/go-ethereum/node" 32 "github.com/ethereum/go-ethereum/p2p" 33 "github.com/ethereum/go-ethereum/p2p/discover" 34 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 35 "github.com/ethereum/go-ethereum/swarm/network" 36 "github.com/ethereum/go-ethereum/swarm/network/simulation" 37 "github.com/ethereum/go-ethereum/swarm/state" 38 "github.com/ethereum/go-ethereum/swarm/storage" 39 ) 40 41 func TestIntervals(t *testing.T) { 42 testIntervals(t, true, nil, false) 43 testIntervals(t, false, NewRange(9, 26), false) 44 testIntervals(t, true, NewRange(9, 26), false) 45 46 testIntervals(t, true, nil, true) 47 testIntervals(t, false, NewRange(9, 26), true) 48 testIntervals(t, true, NewRange(9, 26), true) 49 } 50 51 func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) { 52 nodes := 2 53 chunkCount := dataChunkCount 54 externalStreamName := "externalStream" 55 externalStreamSessionAt := uint64(50) 56 externalStreamMaxKeys := uint64(100) 57 58 sim := simulation.New(map[string]simulation.ServiceFunc{ 59 "intervalsStreamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) { 60 61 id := ctx.Config.ID 62 addr := network.NewAddrFromNodeID(id) 63 store, datadir, err := createTestLocalStorageForID(id, addr) 64 if err != nil { 65 return nil, nil, err 66 } 67 bucket.Store(bucketKeyStore, store) 68 cleanup = func() { 69 store.Close() 70 os.RemoveAll(datadir) 71 } 72 localStore := store.(*storage.LocalStore) 73 db := storage.NewDBAPI(localStore) 74 kad := network.NewKademlia(addr.Over(), network.NewKadParams()) 75 delivery := NewDelivery(kad, db) 76 77 r := NewRegistry(addr, delivery, db, state.NewInmemoryStore(), &RegistryOptions{ 78 SkipCheck: skipCheck, 79 }) 80 bucket.Store(bucketKeyRegistry, r) 81 82 r.RegisterClientFunc(externalStreamName, func(p *Peer, t string, live bool) (Client, error) { 83 return newTestExternalClient(db), nil 84 }) 85 r.RegisterServerFunc(externalStreamName, func(p *Peer, t string, live bool) (Server, error) { 86 return newTestExternalServer(t, externalStreamSessionAt, externalStreamMaxKeys, nil), nil 87 }) 88 89 fileStore := storage.NewFileStore(localStore, storage.NewFileStoreParams()) 90 bucket.Store(bucketKeyFileStore, fileStore) 91 92 return r, cleanup, nil 93 94 }, 95 }) 96 defer sim.Close() 97 98 log.Info("Adding nodes to simulation") 99 _, err := sim.AddNodesAndConnectChain(nodes) 100 if err != nil { 101 t.Fatal(err) 102 } 103 104 ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) 105 defer cancel() 106 107 result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error { 108 nodeIDs := sim.UpNodeIDs() 109 storer := nodeIDs[0] 110 checker := nodeIDs[1] 111 112 item, ok := sim.NodeItem(storer, bucketKeyFileStore) 113 if !ok { 114 return fmt.Errorf("No filestore") 115 } 116 fileStore := item.(*storage.FileStore) 117 118 size := chunkCount * chunkSize 119 _, wait, err := fileStore.Store(ctx, io.LimitReader(crand.Reader, int64(size)), int64(size), false) 120 if err != nil { 121 log.Error("Store error: %v", "err", err) 122 t.Fatal(err) 123 } 124 err = wait(ctx) 125 if err != nil { 126 log.Error("Wait error: %v", "err", err) 127 t.Fatal(err) 128 } 129 130 item, ok = sim.NodeItem(checker, bucketKeyRegistry) 131 if !ok { 132 return fmt.Errorf("No registry") 133 } 134 registry := item.(*Registry) 135 136 liveErrC := make(chan error) 137 historyErrC := make(chan error) 138 139 if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { 140 log.Error("WaitKademlia error: %v", "err", err) 141 return err 142 } 143 144 log.Debug("Watching for disconnections") 145 disconnections := sim.PeerEvents( 146 context.Background(), 147 sim.NodeIDs(), 148 simulation.NewPeerEventsFilter().Type(p2p.PeerEventTypeDrop), 149 ) 150 151 go func() { 152 for d := range disconnections { 153 if d.Error != nil { 154 log.Error("peer drop", "node", d.NodeID, "peer", d.Event.Peer) 155 t.Fatal(d.Error) 156 } 157 } 158 }() 159 160 go func() { 161 if !live { 162 close(liveErrC) 163 return 164 } 165 166 var err error 167 defer func() { 168 liveErrC <- err 169 }() 170 171 // live stream 172 var liveHashesChan chan []byte 173 liveHashesChan, err = getHashes(ctx, registry, storer, NewStream(externalStreamName, "", true)) 174 if err != nil { 175 log.Error("Subscription error: %v", "err", err) 176 return 177 } 178 i := externalStreamSessionAt 179 180 // we have subscribed, enable notifications 181 err = enableNotifications(registry, storer, NewStream(externalStreamName, "", true)) 182 if err != nil { 183 return 184 } 185 186 for { 187 select { 188 case hash := <-liveHashesChan: 189 h := binary.BigEndian.Uint64(hash) 190 if h != i { 191 err = fmt.Errorf("expected live hash %d, got %d", i, h) 192 return 193 } 194 i++ 195 if i > externalStreamMaxKeys { 196 return 197 } 198 case <-ctx.Done(): 199 return 200 } 201 } 202 }() 203 204 go func() { 205 if live && history == nil { 206 close(historyErrC) 207 return 208 } 209 210 var err error 211 defer func() { 212 historyErrC <- err 213 }() 214 215 // history stream 216 var historyHashesChan chan []byte 217 historyHashesChan, err = getHashes(ctx, registry, storer, NewStream(externalStreamName, "", false)) 218 if err != nil { 219 return 220 } 221 222 var i uint64 223 historyTo := externalStreamMaxKeys 224 if history != nil { 225 i = history.From 226 if history.To != 0 { 227 historyTo = history.To 228 } 229 } 230 231 // we have subscribed, enable notifications 232 err = enableNotifications(registry, storer, NewStream(externalStreamName, "", false)) 233 if err != nil { 234 return 235 } 236 237 for { 238 select { 239 case hash := <-historyHashesChan: 240 h := binary.BigEndian.Uint64(hash) 241 if h != i { 242 err = fmt.Errorf("expected history hash %d, got %d", i, h) 243 return 244 } 245 i++ 246 if i > historyTo { 247 return 248 } 249 case <-ctx.Done(): 250 return 251 } 252 } 253 }() 254 255 err = registry.Subscribe(storer, NewStream(externalStreamName, "", live), history, Top) 256 if err != nil { 257 return err 258 } 259 if err := <-liveErrC; err != nil { 260 return err 261 } 262 if err := <-historyErrC; err != nil { 263 return err 264 } 265 266 return nil 267 }) 268 269 if result.Error != nil { 270 t.Fatal(result.Error) 271 } 272 } 273 274 func getHashes(ctx context.Context, r *Registry, peerID discover.NodeID, s Stream) (chan []byte, error) { 275 peer := r.getPeer(peerID) 276 277 client, err := peer.getClient(ctx, s) 278 if err != nil { 279 return nil, err 280 } 281 282 c := client.Client.(*testExternalClient) 283 284 return c.hashes, nil 285 } 286 287 func enableNotifications(r *Registry, peerID discover.NodeID, s Stream) error { 288 peer := r.getPeer(peerID) 289 290 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 291 defer cancel() 292 293 client, err := peer.getClient(ctx, s) 294 if err != nil { 295 return err 296 } 297 298 close(client.Client.(*testExternalClient).enableNotificationsC) 299 300 return nil 301 } 302 303 type testExternalClient struct { 304 hashes chan []byte 305 db *storage.DBAPI 306 enableNotificationsC chan struct{} 307 } 308 309 func newTestExternalClient(db *storage.DBAPI) *testExternalClient { 310 return &testExternalClient{ 311 hashes: make(chan []byte), 312 db: db, 313 enableNotificationsC: make(chan struct{}), 314 } 315 } 316 317 func (c *testExternalClient) NeedData(ctx context.Context, hash []byte) func() { 318 chunk, _ := c.db.GetOrCreateRequest(ctx, hash) 319 if chunk.ReqC == nil { 320 return nil 321 } 322 c.hashes <- hash 323 //NOTE: This was failing on go1.9.x with a deadlock. 324 //Sometimes this function would just block 325 //It is commented now, but it may be well worth after the chunk refactor 326 //to re-enable this and see if the problem has been addressed 327 /* 328 return func() { 329 return chunk.WaitToStore() 330 } 331 */ 332 return nil 333 } 334 335 func (c *testExternalClient) BatchDone(Stream, uint64, []byte, []byte) func() (*TakeoverProof, error) { 336 return nil 337 } 338 339 func (c *testExternalClient) Close() {} 340 341 const testExternalServerBatchSize = 10 342 343 type testExternalServer struct { 344 t string 345 keyFunc func(key []byte, index uint64) 346 sessionAt uint64 347 maxKeys uint64 348 } 349 350 func newTestExternalServer(t string, sessionAt, maxKeys uint64, keyFunc func(key []byte, index uint64)) *testExternalServer { 351 if keyFunc == nil { 352 keyFunc = binary.BigEndian.PutUint64 353 } 354 return &testExternalServer{ 355 t: t, 356 keyFunc: keyFunc, 357 sessionAt: sessionAt, 358 maxKeys: maxKeys, 359 } 360 } 361 362 func (s *testExternalServer) SetNextBatch(from uint64, to uint64) ([]byte, uint64, uint64, *HandoverProof, error) { 363 if from == 0 && to == 0 { 364 from = s.sessionAt 365 to = s.sessionAt + testExternalServerBatchSize 366 } 367 if to-from > testExternalServerBatchSize { 368 to = from + testExternalServerBatchSize - 1 369 } 370 if from >= s.maxKeys && to > s.maxKeys { 371 return nil, 0, 0, nil, io.EOF 372 } 373 if to > s.maxKeys { 374 to = s.maxKeys 375 } 376 b := make([]byte, HashSize*(to-from+1)) 377 for i := from; i <= to; i++ { 378 s.keyFunc(b[(i-from)*HashSize:(i-from+1)*HashSize], i) 379 } 380 return b, from, to, nil, nil 381 } 382 383 func (s *testExternalServer) GetData(context.Context, []byte) ([]byte, error) { 384 return make([]byte, 4096), nil 385 } 386 387 func (s *testExternalServer) Close() {}