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