github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/p2p/simulations/mocker.go (about)

     1  // Copyright 2017 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 simulations simulates p2p networks.
    18  // A mocker simulates starting and stopping real nodes in a network.
    19  package simulations
    20  
    21  import (
    22  	"fmt"
    23  	"math/rand"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/log"
    28  	"github.com/ethereum/go-ethereum/p2p/enode"
    29  	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
    30  )
    31  
    32  // a map of mocker names to its function
    33  var mockerList = map[string]func(net *Network, quit chan struct{}, nodeCount int){
    34  	"startStop":     startStop,
    35  	"probabilistic": probabilistic,
    36  	"boot":          boot,
    37  }
    38  
    39  // LookupMocker looks a mocker by its name, returns the mockerFn
    40  func LookupMocker(mockerType string) func(net *Network, quit chan struct{}, nodeCount int) {
    41  	return mockerList[mockerType]
    42  }
    43  
    44  // GetMockerList returns a list of mockers (keys of the map)
    45  // Useful for frontend to build available mocker selection
    46  func GetMockerList() []string {
    47  	list := make([]string, 0, len(mockerList))
    48  	for k := range mockerList {
    49  		list = append(list, k)
    50  	}
    51  	return list
    52  }
    53  
    54  // The boot mockerFn only connects the node in a ring and doesn't do anything else
    55  func boot(net *Network, quit chan struct{}, nodeCount int) {
    56  	_, err := connectNodesInRing(net, nodeCount)
    57  	if err != nil {
    58  		panic("Could not startup node network for mocker")
    59  	}
    60  }
    61  
    62  // The startStop mockerFn stops and starts nodes in a defined period (ticker)
    63  func startStop(net *Network, quit chan struct{}, nodeCount int) {
    64  	nodes, err := connectNodesInRing(net, nodeCount)
    65  	if err != nil {
    66  		panic("Could not startup node network for mocker")
    67  	}
    68  	var (
    69  		tick  = time.NewTicker(10 * time.Second)
    70  		timer = time.NewTimer(3 * time.Second)
    71  	)
    72  	defer tick.Stop()
    73  	defer timer.Stop()
    74  
    75  	for {
    76  		select {
    77  		case <-quit:
    78  			log.Info("Terminating simulation loop")
    79  			return
    80  		case <-tick.C:
    81  			id := nodes[rand.Intn(len(nodes))]
    82  			log.Info("stopping node", "id", id)
    83  			if err := net.Stop(id); err != nil {
    84  				log.Error("error stopping node", "id", id, "err", err)
    85  				return
    86  			}
    87  
    88  			timer.Reset(3 * time.Second)
    89  			select {
    90  			case <-quit:
    91  				log.Info("Terminating simulation loop")
    92  				return
    93  			case <-timer.C:
    94  			}
    95  
    96  			log.Debug("starting node", "id", id)
    97  			if err := net.Start(id); err != nil {
    98  				log.Error("error starting node", "id", id, "err", err)
    99  				return
   100  			}
   101  		}
   102  	}
   103  }
   104  
   105  // The probabilistic mocker func has a more probabilistic pattern
   106  // (the implementation could probably be improved):
   107  // nodes are connected in a ring, then a varying number of random nodes is selected,
   108  // mocker then stops and starts them in random intervals, and continues the loop
   109  func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
   110  	nodes, err := connectNodesInRing(net, nodeCount)
   111  	if err != nil {
   112  		select {
   113  		case <-quit:
   114  			//error may be due to abortion of mocking; so the quit channel is closed
   115  			return
   116  		default:
   117  			panic("Could not startup node network for mocker")
   118  		}
   119  	}
   120  	for {
   121  		select {
   122  		case <-quit:
   123  			log.Info("Terminating simulation loop")
   124  			return
   125  		default:
   126  		}
   127  		var lowid, highid int
   128  		var wg sync.WaitGroup
   129  		randWait := time.Duration(rand.Intn(5000)+1000) * time.Millisecond
   130  		rand1 := rand.Intn(nodeCount - 1)
   131  		rand2 := rand.Intn(nodeCount - 1)
   132  		if rand1 <= rand2 {
   133  			lowid = rand1
   134  			highid = rand2
   135  		} else if rand1 > rand2 {
   136  			highid = rand1
   137  			lowid = rand2
   138  		}
   139  		var steps = highid - lowid
   140  		wg.Add(steps)
   141  		for i := lowid; i < highid; i++ {
   142  			select {
   143  			case <-quit:
   144  				log.Info("Terminating simulation loop")
   145  				return
   146  			case <-time.After(randWait):
   147  			}
   148  			log.Debug(fmt.Sprintf("node %v shutting down", nodes[i]))
   149  			err := net.Stop(nodes[i])
   150  			if err != nil {
   151  				log.Error("Error stopping node", "node", nodes[i])
   152  				wg.Done()
   153  				continue
   154  			}
   155  			go func(id enode.ID) {
   156  				time.Sleep(randWait)
   157  				err := net.Start(id)
   158  				if err != nil {
   159  					log.Error("Error starting node", "node", id)
   160  				}
   161  				wg.Done()
   162  			}(nodes[i])
   163  		}
   164  		wg.Wait()
   165  	}
   166  }
   167  
   168  // connect nodeCount number of nodes in a ring
   169  func connectNodesInRing(net *Network, nodeCount int) ([]enode.ID, error) {
   170  	ids := make([]enode.ID, nodeCount)
   171  	for i := 0; i < nodeCount; i++ {
   172  		conf := adapters.RandomNodeConfig()
   173  		node, err := net.NewNodeWithConfig(conf)
   174  		if err != nil {
   175  			log.Error("Error creating a node!", "err", err)
   176  			return nil, err
   177  		}
   178  		ids[i] = node.ID()
   179  	}
   180  
   181  	for _, id := range ids {
   182  		if err := net.Start(id); err != nil {
   183  			log.Error("Error starting a node!", "err", err)
   184  			return nil, err
   185  		}
   186  		log.Debug(fmt.Sprintf("node %v starting up", id))
   187  	}
   188  	for i, id := range ids {
   189  		peerID := ids[(i+1)%len(ids)]
   190  		if err := net.Connect(id, peerID); err != nil {
   191  			log.Error("Error connecting a node to a peer!", "err", err)
   192  			return nil, err
   193  		}
   194  	}
   195  
   196  	return ids, nil
   197  }