github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/swarm/network/stream/visualized_snapshot_sync_sim_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  // +build withserver
    18  
    19  package stream
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/p2p"
    28  	"github.com/ethereum/go-ethereum/p2p/discover"
    29  	"github.com/ethereum/go-ethereum/p2p/simulations"
    30  	"github.com/ethereum/go-ethereum/swarm/log"
    31  	"github.com/ethereum/go-ethereum/swarm/network/simulation"
    32  	"github.com/ethereum/go-ethereum/swarm/storage"
    33  )
    34  
    35  /*
    36  The tests in this file need to be executed with
    37  
    38  			-tags=withserver
    39  
    40  Also, they will stall if executed stand-alone, because they wait
    41  for the visualization frontend to send a POST /runsim message.
    42  */
    43  
    44  //setup the sim, evaluate nodeCount and chunkCount and create the sim
    45  func setupSim(serviceMap map[string]simulation.ServiceFunc) (int, int, *simulation.Simulation) {
    46  	nodeCount := *nodes
    47  	chunkCount := *chunks
    48  
    49  	if nodeCount == 0 || chunkCount == 0 {
    50  		nodeCount = 32
    51  		chunkCount = 1
    52  	}
    53  
    54  	//setup the simulation with server, which means the sim won't run
    55  	//until it receives a POST /runsim from the frontend
    56  	sim := simulation.New(serviceMap).WithServer(":8888")
    57  	return nodeCount, chunkCount, sim
    58  }
    59  
    60  //watch for disconnections and wait for healthy
    61  func watchSim(sim *simulation.Simulation) (context.Context, context.CancelFunc) {
    62  	ctx, cancelSimRun := context.WithTimeout(context.Background(), 1*time.Minute)
    63  
    64  	if _, err := sim.WaitTillHealthy(ctx, 2); err != nil {
    65  		panic(err)
    66  	}
    67  
    68  	disconnections := sim.PeerEvents(
    69  		context.Background(),
    70  		sim.NodeIDs(),
    71  		simulation.NewPeerEventsFilter().Type(p2p.PeerEventTypeDrop),
    72  	)
    73  
    74  	go func() {
    75  		for d := range disconnections {
    76  			log.Error("peer drop", "node", d.NodeID, "peer", d.Event.Peer)
    77  			panic("unexpected disconnect")
    78  			cancelSimRun()
    79  		}
    80  	}()
    81  
    82  	return ctx, cancelSimRun
    83  }
    84  
    85  //This test requests bogus hashes into the network
    86  func TestNonExistingHashesWithServer(t *testing.T) {
    87  	nodeCount, _, sim := setupSim(retrievalSimServiceMap)
    88  	defer sim.Close()
    89  
    90  	err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount))
    91  	if err != nil {
    92  		panic(err)
    93  	}
    94  
    95  	ctx, cancelSimRun := watchSim(sim)
    96  	defer cancelSimRun()
    97  
    98  	//in order to get some meaningful visualization, it is beneficial
    99  	//to define a minimum duration of this test
   100  	testDuration := 20 * time.Second
   101  
   102  	result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error {
   103  		//check on the node's FileStore (netstore)
   104  		id := sim.RandomUpNode().ID
   105  		item, ok := sim.NodeItem(id, bucketKeyFileStore)
   106  		if !ok {
   107  			t.Fatalf("No filestore")
   108  		}
   109  		fileStore := item.(*storage.FileStore)
   110  		//create a bogus hash
   111  		fakeHash := storage.GenerateRandomChunk(1000).Address()
   112  		//try to retrieve it - will propagate RetrieveRequestMsg into the network
   113  		reader, _ := fileStore.Retrieve(context.TODO(), fakeHash)
   114  		if _, err := reader.Size(ctx, nil); err != nil {
   115  			log.Debug("expected error for non-existing chunk")
   116  		}
   117  		//sleep so that the frontend can have something to display
   118  		time.Sleep(testDuration)
   119  
   120  		return nil
   121  	})
   122  	if result.Error != nil {
   123  		sendSimTerminatedEvent(sim)
   124  		t.Fatal(result.Error)
   125  	}
   126  
   127  	sendSimTerminatedEvent(sim)
   128  
   129  }
   130  
   131  //send a termination event to the frontend
   132  func sendSimTerminatedEvent(sim *simulation.Simulation) {
   133  	evt := &simulations.Event{
   134  		Type:    EventTypeSimTerminated,
   135  		Control: false,
   136  	}
   137  	sim.Net.Events().Send(evt)
   138  }
   139  
   140  //This test is the same as the snapshot sync test,
   141  //but with a HTTP server
   142  //It also sends some custom events so that the frontend
   143  //can visualize messages like SendOfferedMsg, WantedHashesMsg, DeliveryMsg
   144  func TestSnapshotSyncWithServer(t *testing.T) {
   145  
   146  	nodeCount, chunkCount, sim := setupSim(simServiceMap)
   147  	defer sim.Close()
   148  
   149  	log.Info("Initializing test config")
   150  
   151  	conf := &synctestConfig{}
   152  	//map of discover ID to indexes of chunks expected at that ID
   153  	conf.idToChunksMap = make(map[discover.NodeID][]int)
   154  	//map of overlay address to discover ID
   155  	conf.addrToIDMap = make(map[string]discover.NodeID)
   156  	//array where the generated chunk hashes will be stored
   157  	conf.hashes = make([]storage.Address, 0)
   158  
   159  	err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount))
   160  	if err != nil {
   161  		panic(err)
   162  	}
   163  
   164  	ctx, cancelSimRun := watchSim(sim)
   165  	defer cancelSimRun()
   166  
   167  	//setup filters in the event feed
   168  	offeredHashesFilter := simulation.NewPeerEventsFilter().Type(p2p.PeerEventTypeMsgRecv).Protocol("stream").MsgCode(1)
   169  	wantedFilter := simulation.NewPeerEventsFilter().Type(p2p.PeerEventTypeMsgRecv).Protocol("stream").MsgCode(2)
   170  	deliveryFilter := simulation.NewPeerEventsFilter().Type(p2p.PeerEventTypeMsgRecv).Protocol("stream").MsgCode(6)
   171  	eventC := sim.PeerEvents(ctx, sim.UpNodeIDs(), offeredHashesFilter, wantedFilter, deliveryFilter)
   172  
   173  	quit := make(chan struct{})
   174  
   175  	go func() {
   176  		for e := range eventC {
   177  			select {
   178  			case <-quit:
   179  				fmt.Println("quitting event loop")
   180  				return
   181  			default:
   182  			}
   183  			if e.Error != nil {
   184  				t.Fatal(e.Error)
   185  			}
   186  			if *e.Event.MsgCode == uint64(1) {
   187  				evt := &simulations.Event{
   188  					Type:    EventTypeChunkOffered,
   189  					Node:    sim.Net.GetNode(e.NodeID),
   190  					Control: false,
   191  				}
   192  				sim.Net.Events().Send(evt)
   193  			} else if *e.Event.MsgCode == uint64(2) {
   194  				evt := &simulations.Event{
   195  					Type:    EventTypeChunkWanted,
   196  					Node:    sim.Net.GetNode(e.NodeID),
   197  					Control: false,
   198  				}
   199  				sim.Net.Events().Send(evt)
   200  			} else if *e.Event.MsgCode == uint64(6) {
   201  				evt := &simulations.Event{
   202  					Type:    EventTypeChunkDelivered,
   203  					Node:    sim.Net.GetNode(e.NodeID),
   204  					Control: false,
   205  				}
   206  				sim.Net.Events().Send(evt)
   207  			}
   208  		}
   209  	}()
   210  	//run the sim
   211  	result := runSim(conf, ctx, sim, chunkCount)
   212  
   213  	//send terminated event
   214  	evt := &simulations.Event{
   215  		Type:    EventTypeSimTerminated,
   216  		Control: false,
   217  	}
   218  	sim.Net.Events().Send(evt)
   219  
   220  	if result.Error != nil {
   221  		panic(result.Error)
   222  	}
   223  	close(quit)
   224  	log.Info("Simulation ended")
   225  }