github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/p2p/simulations/mocker.go (about)

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