diff options
author | holisticode <holistic.computing@gmail.com> | 2017-12-13 02:10:41 +0800 |
---|---|---|
committer | Felix Lange <fjl@users.noreply.github.com> | 2017-12-13 02:10:41 +0800 |
commit | fd777bb2104f15cb7c2f7eede7069ad436e29b57 (patch) | |
tree | 040b0474fa84b4f5e092bd1364fd88a6d0ac586b /p2p/simulations/mocker.go | |
parent | 3da1bf8ca18f54d9bd2c8c110854dc071ee3898b (diff) | |
download | dexon-fd777bb2104f15cb7c2f7eede7069ad436e29b57.tar dexon-fd777bb2104f15cb7c2f7eede7069ad436e29b57.tar.gz dexon-fd777bb2104f15cb7c2f7eede7069ad436e29b57.tar.bz2 dexon-fd777bb2104f15cb7c2f7eede7069ad436e29b57.tar.lz dexon-fd777bb2104f15cb7c2f7eede7069ad436e29b57.tar.xz dexon-fd777bb2104f15cb7c2f7eede7069ad436e29b57.tar.zst dexon-fd777bb2104f15cb7c2f7eede7069ad436e29b57.zip |
p2p/simulations: add mocker functionality (#15207)
This commit adds mocker functionality to p2p/simulations. A
mocker allows to starting/stopping of nodes via the HTTP API.
Diffstat (limited to 'p2p/simulations/mocker.go')
-rw-r--r-- | p2p/simulations/mocker.go | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/p2p/simulations/mocker.go b/p2p/simulations/mocker.go new file mode 100644 index 000000000..c38e28855 --- /dev/null +++ b/p2p/simulations/mocker.go @@ -0,0 +1,192 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// Package simulations simulates p2p networks. +// A mocker simulates starting and stopping real nodes in a network. +package simulations + +import ( + "fmt" + "math/rand" + "sync" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/discover" +) + +//a map of mocker names to its function +var mockerList = map[string]func(net *Network, quit chan struct{}, nodeCount int){ + "startStop": startStop, + "probabilistic": probabilistic, + "boot": boot, +} + +//Lookup a mocker by its name, returns the mockerFn +func LookupMocker(mockerType string) func(net *Network, quit chan struct{}, nodeCount int) { + return mockerList[mockerType] +} + +//Get a list of mockers (keys of the map) +//Useful for frontend to build available mocker selection +func GetMockerList() []string { + list := make([]string, 0, len(mockerList)) + for k := range mockerList { + list = append(list, k) + } + return list +} + +//The boot mockerFn only connects the node in a ring and doesn't do anything else +func boot(net *Network, quit chan struct{}, nodeCount int) { + _, err := connectNodesInRing(net, nodeCount) + if err != nil { + panic("Could not startup node network for mocker") + } +} + +//The startStop mockerFn stops and starts nodes in a defined period (ticker) +func startStop(net *Network, quit chan struct{}, nodeCount int) { + nodes, err := connectNodesInRing(net, nodeCount) + if err != nil { + panic("Could not startup node network for mocker") + } + tick := time.NewTicker(10 * time.Second) + defer tick.Stop() + for { + select { + case <-quit: + log.Info("Terminating simulation loop") + return + case <-tick.C: + id := nodes[rand.Intn(len(nodes))] + log.Info("stopping node", "id", id) + if err := net.Stop(id); err != nil { + log.Error("error stopping node", "id", id, "err", err) + return + } + + select { + case <-quit: + log.Info("Terminating simulation loop") + return + case <-time.After(3 * time.Second): + } + + log.Debug("starting node", "id", id) + if err := net.Start(id); err != nil { + log.Error("error starting node", "id", id, "err", err) + return + } + } + } +} + +//The probabilistic mocker func has a more probabilistic pattern +//(the implementation could probably be improved): +//nodes are connected in a ring, then a varying number of random nodes is selected, +//mocker then stops and starts them in random intervals, and continues the loop +func probabilistic(net *Network, quit chan struct{}, nodeCount int) { + nodes, err := connectNodesInRing(net, nodeCount) + if err != nil { + panic("Could not startup node network for mocker") + } + for { + select { + case <-quit: + log.Info("Terminating simulation loop") + return + default: + } + var lowid, highid int + var wg sync.WaitGroup + randWait := time.Duration(rand.Intn(5000)+1000) * time.Millisecond + rand1 := rand.Intn(nodeCount - 1) + rand2 := rand.Intn(nodeCount - 1) + if rand1 < rand2 { + lowid = rand1 + highid = rand2 + } else if rand1 > rand2 { + highid = rand1 + lowid = rand2 + } else { + if rand1 == 0 { + rand2 = 9 + } else if rand1 == 9 { + rand1 = 0 + } + lowid = rand1 + highid = rand2 + } + var steps = highid - lowid + wg.Add(steps) + for i := lowid; i < highid; i++ { + select { + case <-quit: + log.Info("Terminating simulation loop") + return + case <-time.After(randWait): + } + log.Debug(fmt.Sprintf("node %v shutting down", nodes[i])) + err := net.Stop(nodes[i]) + if err != nil { + log.Error(fmt.Sprintf("Error stopping node %s", nodes[i])) + wg.Done() + continue + } + go func(id discover.NodeID) { + time.Sleep(randWait) + err := net.Start(id) + if err != nil { + log.Error(fmt.Sprintf("Error starting node %s", id)) + } + wg.Done() + }(nodes[i]) + } + wg.Wait() + } + +} + +//connect nodeCount number of nodes in a ring +func connectNodesInRing(net *Network, nodeCount int) ([]discover.NodeID, error) { + ids := make([]discover.NodeID, nodeCount) + for i := 0; i < nodeCount; i++ { + node, err := net.NewNode() + if err != nil { + log.Error("Error creating a node! %s", err) + return nil, err + } + ids[i] = node.ID() + } + + for _, id := range ids { + if err := net.Start(id); err != nil { + log.Error("Error starting a node! %s", err) + return nil, err + } + log.Debug(fmt.Sprintf("node %v starting up", id)) + } + for i, id := range ids { + peerID := ids[(i+1)%len(ids)] + if err := net.Connect(id, peerID); err != nil { + log.Error("Error connecting a node to a peer! %s", err) + return nil, err + } + } + + return ids, nil +} |