github.com/klaytn/klaytn@v1.12.1/networks/p2p/simulations/mocker.go (about)

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