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