github.com/letterj/go-ethereum@v1.8.22-0.20190204142846-520024dfd689/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 "encoding/binary" 22 "errors" 23 "fmt" 24 "sync" 25 "sync/atomic" 26 "testing" 27 "time" 28 29 "github.com/ethereum/go-ethereum/log" 30 "github.com/ethereum/go-ethereum/node" 31 "github.com/ethereum/go-ethereum/p2p/enode" 32 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 33 "github.com/ethereum/go-ethereum/swarm/network/simulation" 34 "github.com/ethereum/go-ethereum/swarm/state" 35 "github.com/ethereum/go-ethereum/swarm/storage" 36 "github.com/ethereum/go-ethereum/swarm/testutil" 37 ) 38 39 func TestIntervalsLive(t *testing.T) { 40 testIntervals(t, true, nil, false) 41 testIntervals(t, true, nil, true) 42 } 43 44 func TestIntervalsHistory(t *testing.T) { 45 testIntervals(t, false, NewRange(9, 26), false) 46 testIntervals(t, false, NewRange(9, 26), true) 47 } 48 49 func TestIntervalsLiveAndHistory(t *testing.T) { 50 testIntervals(t, true, NewRange(9, 26), false) 51 testIntervals(t, true, NewRange(9, 26), true) 52 } 53 54 func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) { 55 56 nodes := 2 57 chunkCount := dataChunkCount 58 externalStreamName := "externalStream" 59 externalStreamSessionAt := uint64(50) 60 externalStreamMaxKeys := uint64(100) 61 62 sim := simulation.New(map[string]simulation.ServiceFunc{ 63 "intervalsStreamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (node.Service, func(), error) { 64 addr, netStore, delivery, clean, err := newNetStoreAndDelivery(ctx, bucket) 65 if err != nil { 66 return nil, nil, err 67 } 68 69 r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{ 70 Retrieval: RetrievalDisabled, 71 Syncing: SyncingRegisterOnly, 72 SkipCheck: skipCheck, 73 }, nil) 74 bucket.Store(bucketKeyRegistry, r) 75 76 r.RegisterClientFunc(externalStreamName, func(p *Peer, t string, live bool) (Client, error) { 77 return newTestExternalClient(netStore), nil 78 }) 79 r.RegisterServerFunc(externalStreamName, func(p *Peer, t string, live bool) (Server, error) { 80 return newTestExternalServer(t, externalStreamSessionAt, externalStreamMaxKeys, nil), nil 81 }) 82 83 cleanup := func() { 84 r.Close() 85 clean() 86 } 87 88 return r, cleanup, nil 89 }, 90 }) 91 defer sim.Close() 92 93 log.Info("Adding nodes to simulation") 94 _, err := sim.AddNodesAndConnectChain(nodes) 95 if err != nil { 96 t.Fatal(err) 97 } 98 99 ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) 100 defer cancel() 101 102 if _, err := sim.WaitTillHealthy(ctx); err != nil { 103 t.Fatal(err) 104 } 105 106 result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) (err error) { 107 nodeIDs := sim.UpNodeIDs() 108 storer := nodeIDs[0] 109 checker := nodeIDs[1] 110 111 item, ok := sim.NodeItem(storer, bucketKeyFileStore) 112 if !ok { 113 return fmt.Errorf("No filestore") 114 } 115 fileStore := item.(*storage.FileStore) 116 117 size := chunkCount * chunkSize 118 119 _, wait, err := fileStore.Store(ctx, testutil.RandomReader(1, 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 log.Debug("Watching for disconnections") 140 disconnections := sim.PeerEvents( 141 context.Background(), 142 sim.NodeIDs(), 143 simulation.NewPeerEventsFilter().Drop(), 144 ) 145 146 err = registry.Subscribe(storer, NewStream(externalStreamName, "", live), history, Top) 147 if err != nil { 148 return err 149 } 150 151 var disconnected atomic.Value 152 go func() { 153 for d := range disconnections { 154 if d.Error != nil { 155 log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID) 156 disconnected.Store(true) 157 } 158 } 159 }() 160 defer func() { 161 if err != nil { 162 if yes, ok := disconnected.Load().(bool); ok && yes { 163 err = errors.New("disconnect events received") 164 } 165 } 166 }() 167 168 go func() { 169 if !live { 170 close(liveErrC) 171 return 172 } 173 174 var err error 175 defer func() { 176 liveErrC <- err 177 }() 178 179 // live stream 180 var liveHashesChan chan []byte 181 liveHashesChan, err = getHashes(ctx, registry, storer, NewStream(externalStreamName, "", true)) 182 if err != nil { 183 log.Error("get hashes", "err", err) 184 return 185 } 186 i := externalStreamSessionAt 187 188 // we have subscribed, enable notifications 189 err = enableNotifications(registry, storer, NewStream(externalStreamName, "", true)) 190 if err != nil { 191 return 192 } 193 194 for { 195 select { 196 case hash := <-liveHashesChan: 197 h := binary.BigEndian.Uint64(hash) 198 if h != i { 199 err = fmt.Errorf("expected live hash %d, got %d", i, h) 200 return 201 } 202 i++ 203 if i > externalStreamMaxKeys { 204 return 205 } 206 case <-ctx.Done(): 207 return 208 } 209 } 210 }() 211 212 go func() { 213 if live && history == nil { 214 close(historyErrC) 215 return 216 } 217 218 var err error 219 defer func() { 220 historyErrC <- err 221 }() 222 223 // history stream 224 var historyHashesChan chan []byte 225 historyHashesChan, err = getHashes(ctx, registry, storer, NewStream(externalStreamName, "", false)) 226 if err != nil { 227 log.Error("get hashes", "err", err) 228 return 229 } 230 231 var i uint64 232 historyTo := externalStreamMaxKeys 233 if history != nil { 234 i = history.From 235 if history.To != 0 { 236 historyTo = history.To 237 } 238 } 239 240 // we have subscribed, enable notifications 241 err = enableNotifications(registry, storer, NewStream(externalStreamName, "", false)) 242 if err != nil { 243 return 244 } 245 246 for { 247 select { 248 case hash := <-historyHashesChan: 249 h := binary.BigEndian.Uint64(hash) 250 if h != i { 251 err = fmt.Errorf("expected history hash %d, got %d", i, h) 252 return 253 } 254 i++ 255 if i > historyTo { 256 return 257 } 258 case <-ctx.Done(): 259 return 260 } 261 } 262 }() 263 264 if err := <-liveErrC; err != nil { 265 return err 266 } 267 if err := <-historyErrC; err != nil { 268 return err 269 } 270 271 return nil 272 }) 273 274 if result.Error != nil { 275 t.Fatal(result.Error) 276 } 277 } 278 279 func getHashes(ctx context.Context, r *Registry, peerID enode.ID, s Stream) (chan []byte, error) { 280 peer := r.getPeer(peerID) 281 282 client, err := peer.getClient(ctx, s) 283 if err != nil { 284 return nil, err 285 } 286 287 c := client.Client.(*testExternalClient) 288 289 return c.hashes, nil 290 } 291 292 func enableNotifications(r *Registry, peerID enode.ID, s Stream) error { 293 peer := r.getPeer(peerID) 294 295 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 296 defer cancel() 297 298 client, err := peer.getClient(ctx, s) 299 if err != nil { 300 return err 301 } 302 303 close(client.Client.(*testExternalClient).enableNotificationsC) 304 305 return nil 306 } 307 308 type testExternalClient struct { 309 hashes chan []byte 310 store storage.SyncChunkStore 311 enableNotificationsC chan struct{} 312 } 313 314 func newTestExternalClient(store storage.SyncChunkStore) *testExternalClient { 315 return &testExternalClient{ 316 hashes: make(chan []byte), 317 store: store, 318 enableNotificationsC: make(chan struct{}), 319 } 320 } 321 322 func (c *testExternalClient) NeedData(ctx context.Context, hash []byte) func(context.Context) error { 323 wait := c.store.FetchFunc(ctx, storage.Address(hash)) 324 if wait == nil { 325 return nil 326 } 327 select { 328 case c.hashes <- hash: 329 case <-ctx.Done(): 330 log.Warn("testExternalClient NeedData context", "err", ctx.Err()) 331 return func(_ context.Context) error { 332 return ctx.Err() 333 } 334 } 335 return wait 336 } 337 338 func (c *testExternalClient) BatchDone(Stream, uint64, []byte, []byte) func() (*TakeoverProof, error) { 339 return nil 340 } 341 342 func (c *testExternalClient) Close() {} 343 344 type testExternalServer struct { 345 t string 346 keyFunc func(key []byte, index uint64) 347 sessionAt uint64 348 maxKeys uint64 349 } 350 351 func newTestExternalServer(t string, sessionAt, maxKeys uint64, keyFunc func(key []byte, index uint64)) *testExternalServer { 352 if keyFunc == nil { 353 keyFunc = binary.BigEndian.PutUint64 354 } 355 return &testExternalServer{ 356 t: t, 357 keyFunc: keyFunc, 358 sessionAt: sessionAt, 359 maxKeys: maxKeys, 360 } 361 } 362 363 func (s *testExternalServer) SessionIndex() (uint64, error) { 364 return s.sessionAt, nil 365 } 366 367 func (s *testExternalServer) SetNextBatch(from uint64, to uint64) ([]byte, uint64, uint64, *HandoverProof, error) { 368 if to > s.maxKeys { 369 to = s.maxKeys 370 } 371 b := make([]byte, HashSize*(to-from+1)) 372 for i := from; i <= to; i++ { 373 s.keyFunc(b[(i-from)*HashSize:(i-from+1)*HashSize], i) 374 } 375 return b, from, to, nil, nil 376 } 377 378 func (s *testExternalServer) GetData(context.Context, []byte) ([]byte, error) { 379 return make([]byte, 4096), nil 380 } 381 382 func (s *testExternalServer) Close() {}