github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/simulations/mocker.go (about)

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