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