github.com/bcnmy/go-ethereum@v1.10.27/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 //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 } 133 var steps = highid - lowid 134 wg.Add(steps) 135 for i := lowid; i < highid; i++ { 136 select { 137 case <-quit: 138 log.Info("Terminating simulation loop") 139 return 140 case <-time.After(randWait): 141 } 142 log.Debug(fmt.Sprintf("node %v shutting down", nodes[i])) 143 err := net.Stop(nodes[i]) 144 if err != nil { 145 log.Error("Error stopping node", "node", nodes[i]) 146 wg.Done() 147 continue 148 } 149 go func(id enode.ID) { 150 time.Sleep(randWait) 151 err := net.Start(id) 152 if err != nil { 153 log.Error("Error starting node", "node", id) 154 } 155 wg.Done() 156 }(nodes[i]) 157 } 158 wg.Wait() 159 } 160 } 161 162 //connect nodeCount number of nodes in a ring 163 func connectNodesInRing(net *Network, nodeCount int) ([]enode.ID, error) { 164 ids := make([]enode.ID, nodeCount) 165 for i := 0; i < nodeCount; i++ { 166 conf := adapters.RandomNodeConfig() 167 node, err := net.NewNodeWithConfig(conf) 168 if err != nil { 169 log.Error("Error creating a node!", "err", err) 170 return nil, err 171 } 172 ids[i] = node.ID() 173 } 174 175 for _, id := range ids { 176 if err := net.Start(id); err != nil { 177 log.Error("Error starting a node!", "err", err) 178 return nil, err 179 } 180 log.Debug(fmt.Sprintf("node %v starting up", id)) 181 } 182 for i, id := range ids { 183 peerID := ids[(i+1)%len(ids)] 184 if err := net.Connect(id, peerID); err != nil { 185 log.Error("Error connecting a node to a peer!", "err", err) 186 return nil, err 187 } 188 } 189 190 return ids, nil 191 }