github.com/codingfuture/orig-energi3@v0.8.4/swarm/network/stream/common_test.go (about)

     1  // Copyright 2018 The Energi Core Authors
     2  // Copyright 2018 The go-ethereum Authors
     3  // This file is part of the Energi Core library.
     4  //
     5  // The Energi Core library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The Energi Core library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package stream
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"flag"
    24  	"fmt"
    25  	"io"
    26  	"io/ioutil"
    27  	"math/rand"
    28  	"os"
    29  	"strings"
    30  	"sync"
    31  	"sync/atomic"
    32  	"time"
    33  
    34  	"github.com/ethereum/go-ethereum/log"
    35  	"github.com/ethereum/go-ethereum/p2p/enode"
    36  	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
    37  	p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
    38  	"github.com/ethereum/go-ethereum/swarm/network"
    39  	"github.com/ethereum/go-ethereum/swarm/network/simulation"
    40  	"github.com/ethereum/go-ethereum/swarm/state"
    41  	"github.com/ethereum/go-ethereum/swarm/storage"
    42  	mockmem "github.com/ethereum/go-ethereum/swarm/storage/mock/mem"
    43  	"github.com/ethereum/go-ethereum/swarm/testutil"
    44  	colorable "github.com/mattn/go-colorable"
    45  )
    46  
    47  var (
    48  	loglevel     = flag.Int("loglevel", 2, "verbosity of logs")
    49  	nodes        = flag.Int("nodes", 0, "number of nodes")
    50  	chunks       = flag.Int("chunks", 0, "number of chunks")
    51  	useMockStore = flag.Bool("mockstore", false, "disabled mock store (default: enabled)")
    52  	longrunning  = flag.Bool("longrunning", false, "do run long-running tests")
    53  
    54  	bucketKeyDB        = simulation.BucketKey("db")
    55  	bucketKeyStore     = simulation.BucketKey("store")
    56  	bucketKeyFileStore = simulation.BucketKey("filestore")
    57  	bucketKeyNetStore  = simulation.BucketKey("netstore")
    58  	bucketKeyDelivery  = simulation.BucketKey("delivery")
    59  	bucketKeyRegistry  = simulation.BucketKey("registry")
    60  
    61  	chunkSize = 4096
    62  	pof       = network.Pof
    63  )
    64  
    65  func init() {
    66  	testing.Init()
    67  	flag.Parse()
    68  	rand.Seed(time.Now().UnixNano())
    69  
    70  	log.PrintOrigins(true)
    71  	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
    72  }
    73  
    74  // newNetStoreAndDelivery is a default constructor for BzzAddr, NetStore and Delivery, used in Simulations
    75  func newNetStoreAndDelivery(ctx *adapters.ServiceContext, bucket *sync.Map) (*network.BzzAddr, *storage.NetStore, *Delivery, func(), error) {
    76  	addr := network.NewAddr(ctx.Config.Node())
    77  
    78  	netStore, delivery, cleanup, err := netStoreAndDeliveryWithAddr(ctx, bucket, addr)
    79  	if err != nil {
    80  		return nil, nil, nil, nil, err
    81  	}
    82  
    83  	netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
    84  
    85  	return addr, netStore, delivery, cleanup, nil
    86  }
    87  
    88  // newNetStoreAndDeliveryWithBzzAddr is a constructor for NetStore and Delivery, used in Simulations, accepting any BzzAddr
    89  func newNetStoreAndDeliveryWithBzzAddr(ctx *adapters.ServiceContext, bucket *sync.Map, addr *network.BzzAddr) (*storage.NetStore, *Delivery, func(), error) {
    90  	netStore, delivery, cleanup, err := netStoreAndDeliveryWithAddr(ctx, bucket, addr)
    91  	if err != nil {
    92  		return nil, nil, nil, err
    93  	}
    94  
    95  	netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
    96  
    97  	return netStore, delivery, cleanup, nil
    98  }
    99  
   100  // newNetStoreAndDeliveryWithRequestFunc is a constructor for NetStore and Delivery, used in Simulations, accepting any NetStore.RequestFunc
   101  func newNetStoreAndDeliveryWithRequestFunc(ctx *adapters.ServiceContext, bucket *sync.Map, rf network.RequestFunc) (*network.BzzAddr, *storage.NetStore, *Delivery, func(), error) {
   102  	addr := network.NewAddr(ctx.Config.Node())
   103  
   104  	netStore, delivery, cleanup, err := netStoreAndDeliveryWithAddr(ctx, bucket, addr)
   105  	if err != nil {
   106  		return nil, nil, nil, nil, err
   107  	}
   108  
   109  	netStore.NewNetFetcherFunc = network.NewFetcherFactory(rf, true).New
   110  
   111  	return addr, netStore, delivery, cleanup, nil
   112  }
   113  
   114  func netStoreAndDeliveryWithAddr(ctx *adapters.ServiceContext, bucket *sync.Map, addr *network.BzzAddr) (*storage.NetStore, *Delivery, func(), error) {
   115  	n := ctx.Config.Node()
   116  
   117  	store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
   118  	if *useMockStore {
   119  		store, datadir, err = createMockStore(mockmem.NewGlobalStore(), n.ID(), addr)
   120  	}
   121  	if err != nil {
   122  		return nil, nil, nil, err
   123  	}
   124  	localStore := store.(*storage.LocalStore)
   125  	netStore, err := storage.NewNetStore(localStore, nil)
   126  	if err != nil {
   127  		return nil, nil, nil, err
   128  	}
   129  
   130  	fileStore := storage.NewFileStore(netStore, storage.NewFileStoreParams())
   131  
   132  	kad := network.NewKademlia(addr.Over(), network.NewKadParams())
   133  	delivery := NewDelivery(kad, netStore)
   134  
   135  	bucket.Store(bucketKeyStore, store)
   136  	bucket.Store(bucketKeyDB, netStore)
   137  	bucket.Store(bucketKeyDelivery, delivery)
   138  	bucket.Store(bucketKeyFileStore, fileStore)
   139  
   140  	cleanup := func() {
   141  		netStore.Close()
   142  		os.RemoveAll(datadir)
   143  	}
   144  
   145  	return netStore, delivery, cleanup, nil
   146  }
   147  
   148  func newStreamerTester(registryOptions *RegistryOptions) (*p2ptest.ProtocolTester, *Registry, *storage.LocalStore, func(), error) {
   149  	// setup
   150  	addr := network.RandomAddr() // tested peers peer address
   151  	to := network.NewKademlia(addr.OAddr, network.NewKadParams())
   152  
   153  	// temp datadir
   154  	datadir, err := ioutil.TempDir("", "streamer")
   155  	if err != nil {
   156  		return nil, nil, nil, nil, err
   157  	}
   158  	removeDataDir := func() {
   159  		os.RemoveAll(datadir)
   160  	}
   161  
   162  	params := storage.NewDefaultLocalStoreParams()
   163  	params.Init(datadir)
   164  	params.BaseKey = addr.Over()
   165  
   166  	localStore, err := storage.NewTestLocalStoreForAddr(params)
   167  	if err != nil {
   168  		removeDataDir()
   169  		return nil, nil, nil, nil, err
   170  	}
   171  
   172  	netStore, err := storage.NewNetStore(localStore, nil)
   173  	if err != nil {
   174  		removeDataDir()
   175  		return nil, nil, nil, nil, err
   176  	}
   177  
   178  	delivery := NewDelivery(to, netStore)
   179  	netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
   180  	streamer := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), registryOptions, nil)
   181  	teardown := func() {
   182  		streamer.Close()
   183  		removeDataDir()
   184  	}
   185  	protocolTester := p2ptest.NewProtocolTester(addr.ID(), 1, streamer.runProtocol)
   186  
   187  	err = waitForPeers(streamer, 10*time.Second, 1)
   188  	if err != nil {
   189  		teardown()
   190  		return nil, nil, nil, nil, errors.New("timeout: peer is not created")
   191  	}
   192  
   193  	return protocolTester, streamer, localStore, teardown, nil
   194  }
   195  
   196  func waitForPeers(streamer *Registry, timeout time.Duration, expectedPeers int) error {
   197  	ticker := time.NewTicker(10 * time.Millisecond)
   198  	timeoutTimer := time.NewTimer(timeout)
   199  	for {
   200  		select {
   201  		case <-ticker.C:
   202  			if streamer.peersCount() >= expectedPeers {
   203  				return nil
   204  			}
   205  		case <-timeoutTimer.C:
   206  			return errors.New("timeout")
   207  		}
   208  	}
   209  }
   210  
   211  type roundRobinStore struct {
   212  	index  uint32
   213  	stores []storage.ChunkStore
   214  }
   215  
   216  func newRoundRobinStore(stores ...storage.ChunkStore) *roundRobinStore {
   217  	return &roundRobinStore{
   218  		stores: stores,
   219  	}
   220  }
   221  
   222  // not used in this context, only to fulfill ChunkStore interface
   223  func (rrs *roundRobinStore) Has(ctx context.Context, addr storage.Address) bool {
   224  	panic("RoundRobinStor doesn't support HasChunk")
   225  }
   226  
   227  func (rrs *roundRobinStore) Get(ctx context.Context, addr storage.Address) (storage.Chunk, error) {
   228  	return nil, errors.New("get not well defined on round robin store")
   229  }
   230  
   231  func (rrs *roundRobinStore) Put(ctx context.Context, chunk storage.Chunk) error {
   232  	i := atomic.AddUint32(&rrs.index, 1)
   233  	idx := int(i) % len(rrs.stores)
   234  	return rrs.stores[idx].Put(ctx, chunk)
   235  }
   236  
   237  func (rrs *roundRobinStore) Close() {
   238  	for _, store := range rrs.stores {
   239  		store.Close()
   240  	}
   241  }
   242  
   243  func readAll(fileStore *storage.FileStore, hash []byte) (int64, error) {
   244  	r, _ := fileStore.Retrieve(context.TODO(), hash)
   245  	buf := make([]byte, 1024)
   246  	var n int
   247  	var total int64
   248  	var err error
   249  	for (total == 0 || n > 0) && err == nil {
   250  		n, err = r.ReadAt(buf, total)
   251  		total += int64(n)
   252  	}
   253  	if err != nil && err != io.EOF {
   254  		return total, err
   255  	}
   256  	return total, nil
   257  }
   258  
   259  func uploadFilesToNodes(sim *simulation.Simulation) ([]storage.Address, []string, error) {
   260  	nodes := sim.UpNodeIDs()
   261  	nodeCnt := len(nodes)
   262  	log.Debug(fmt.Sprintf("Uploading %d files to nodes", nodeCnt))
   263  	//array holding generated files
   264  	rfiles := make([]string, nodeCnt)
   265  	//array holding the root hashes of the files
   266  	rootAddrs := make([]storage.Address, nodeCnt)
   267  
   268  	var err error
   269  	//for every node, generate a file and upload
   270  	for i, id := range nodes {
   271  		item, ok := sim.NodeItem(id, bucketKeyFileStore)
   272  		if !ok {
   273  			return nil, nil, fmt.Errorf("Error accessing localstore")
   274  		}
   275  		fileStore := item.(*storage.FileStore)
   276  		//generate a file
   277  		rfiles[i], err = generateRandomFile()
   278  		if err != nil {
   279  			return nil, nil, err
   280  		}
   281  		//store it (upload it) on the FileStore
   282  		ctx := context.TODO()
   283  		rk, wait, err := fileStore.Store(ctx, strings.NewReader(rfiles[i]), int64(len(rfiles[i])), false)
   284  		log.Debug("Uploaded random string file to node")
   285  		if err != nil {
   286  			return nil, nil, err
   287  		}
   288  		err = wait(ctx)
   289  		if err != nil {
   290  			return nil, nil, err
   291  		}
   292  		rootAddrs[i] = rk
   293  	}
   294  	return rootAddrs, rfiles, nil
   295  }
   296  
   297  //generate a random file (string)
   298  func generateRandomFile() (string, error) {
   299  	//generate a random file size between minFileSize and maxFileSize
   300  	fileSize := rand.Intn(maxFileSize-minFileSize) + minFileSize
   301  	log.Debug(fmt.Sprintf("Generated file with filesize %d kB", fileSize))
   302  	b := testutil.RandomBytes(1, fileSize*1024)
   303  	return string(b), nil
   304  }
   305  
   306  //create a local store for the given node
   307  func createTestLocalStorageForID(id enode.ID, addr *network.BzzAddr) (storage.ChunkStore, string, error) {
   308  	var datadir string
   309  	var err error
   310  	datadir, err = ioutil.TempDir("", fmt.Sprintf("syncer-test-%s", id.TerminalString()))
   311  	if err != nil {
   312  		return nil, "", err
   313  	}
   314  	var store storage.ChunkStore
   315  	params := storage.NewDefaultLocalStoreParams()
   316  	params.ChunkDbPath = datadir
   317  	params.BaseKey = addr.Over()
   318  	store, err = storage.NewTestLocalStoreForAddr(params)
   319  	if err != nil {
   320  		os.RemoveAll(datadir)
   321  		return nil, "", err
   322  	}
   323  	return store, datadir, nil
   324  }
   325  
   326  // watchDisconnections receives simulation peer events in a new goroutine and sets atomic value
   327  // disconnected to true in case of a disconnect event.
   328  func watchDisconnections(ctx context.Context, sim *simulation.Simulation) (disconnected *boolean) {
   329  	log.Debug("Watching for disconnections")
   330  	disconnections := sim.PeerEvents(
   331  		ctx,
   332  		sim.NodeIDs(),
   333  		simulation.NewPeerEventsFilter().Drop(),
   334  	)
   335  	disconnected = new(boolean)
   336  	go func() {
   337  		for {
   338  			select {
   339  			case <-ctx.Done():
   340  				return
   341  			case d := <-disconnections:
   342  				if d.Error != nil {
   343  					log.Error("peer drop event error", "node", d.NodeID, "peer", d.PeerID, "err", d.Error)
   344  				} else {
   345  					log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID)
   346  				}
   347  				disconnected.set(true)
   348  			}
   349  		}
   350  	}()
   351  	return disconnected
   352  }
   353  
   354  // boolean is used to concurrently set
   355  // and read a boolean value.
   356  type boolean struct {
   357  	v  bool
   358  	mu sync.RWMutex
   359  }
   360  
   361  // set sets the value.
   362  func (b *boolean) set(v bool) {
   363  	b.mu.Lock()
   364  	defer b.mu.Unlock()
   365  
   366  	b.v = v
   367  }
   368  
   369  // bool reads the value.
   370  func (b *boolean) bool() bool {
   371  	b.mu.RLock()
   372  	defer b.mu.RUnlock()
   373  
   374  	return b.v
   375  }