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 }