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() {}