github.com/etsc3259/etsc@v0.0.0-20190109113336-a9c2c10f9c95/swarm/network/stream/intervals_test.go (about)

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