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 }