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