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