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