|
| 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 | +} |
0 commit comments