github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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/ethereum/go-ethereum/log" 28 "github.com/ethereum/go-ethereum/p2p/enode" 29 "github.com/ethereum/go-ethereum/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 // LookupMocker looks 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 // GetMockerList returns 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 var ( 69 tick = time.NewTicker(10 * time.Second) 70 timer = time.NewTimer(3 * time.Second) 71 ) 72 defer tick.Stop() 73 defer timer.Stop() 74 75 for { 76 select { 77 case <-quit: 78 log.Info("Terminating simulation loop") 79 return 80 case <-tick.C: 81 id := nodes[rand.Intn(len(nodes))] 82 log.Info("stopping node", "id", id) 83 if err := net.Stop(id); err != nil { 84 log.Error("error stopping node", "id", id, "err", err) 85 return 86 } 87 88 timer.Reset(3 * time.Second) 89 select { 90 case <-quit: 91 log.Info("Terminating simulation loop") 92 return 93 case <-timer.C: 94 } 95 96 log.Debug("starting node", "id", id) 97 if err := net.Start(id); err != nil { 98 log.Error("error starting node", "id", id, "err", err) 99 return 100 } 101 } 102 } 103 } 104 105 // The probabilistic mocker func has a more probabilistic pattern 106 // (the implementation could probably be improved): 107 // nodes are connected in a ring, then a varying number of random nodes is selected, 108 // mocker then stops and starts them in random intervals, and continues the loop 109 func probabilistic(net *Network, quit chan struct{}, nodeCount int) { 110 nodes, err := connectNodesInRing(net, nodeCount) 111 if err != nil { 112 select { 113 case <-quit: 114 //error may be due to abortion of mocking; so the quit channel is closed 115 return 116 default: 117 panic("Could not startup node network for mocker") 118 } 119 } 120 for { 121 select { 122 case <-quit: 123 log.Info("Terminating simulation loop") 124 return 125 default: 126 } 127 var lowid, highid int 128 var wg sync.WaitGroup 129 randWait := time.Duration(rand.Intn(5000)+1000) * time.Millisecond 130 rand1 := rand.Intn(nodeCount - 1) 131 rand2 := rand.Intn(nodeCount - 1) 132 if rand1 <= rand2 { 133 lowid = rand1 134 highid = rand2 135 } else if rand1 > rand2 { 136 highid = rand1 137 lowid = rand2 138 } 139 var steps = highid - lowid 140 wg.Add(steps) 141 for i := lowid; i < highid; i++ { 142 select { 143 case <-quit: 144 log.Info("Terminating simulation loop") 145 return 146 case <-time.After(randWait): 147 } 148 log.Debug(fmt.Sprintf("node %v shutting down", nodes[i])) 149 err := net.Stop(nodes[i]) 150 if err != nil { 151 log.Error("Error stopping node", "node", nodes[i]) 152 wg.Done() 153 continue 154 } 155 go func(id enode.ID) { 156 time.Sleep(randWait) 157 err := net.Start(id) 158 if err != nil { 159 log.Error("Error starting node", "node", id) 160 } 161 wg.Done() 162 }(nodes[i]) 163 } 164 wg.Wait() 165 } 166 } 167 168 // connect nodeCount number of nodes in a ring 169 func connectNodesInRing(net *Network, nodeCount int) ([]enode.ID, error) { 170 ids := make([]enode.ID, nodeCount) 171 for i := 0; i < nodeCount; i++ { 172 conf := adapters.RandomNodeConfig() 173 node, err := net.NewNodeWithConfig(conf) 174 if err != nil { 175 log.Error("Error creating a node!", "err", err) 176 return nil, err 177 } 178 ids[i] = node.ID() 179 } 180 181 for _, id := range ids { 182 if err := net.Start(id); err != nil { 183 log.Error("Error starting a node!", "err", err) 184 return nil, err 185 } 186 log.Debug(fmt.Sprintf("node %v starting up", id)) 187 } 188 for i, id := range ids { 189 peerID := ids[(i+1)%len(ids)] 190 if err := net.Connect(id, peerID); err != nil { 191 log.Error("Error connecting a node to a peer!", "err", err) 192 return nil, err 193 } 194 } 195 196 return ids, nil 197 }