github.com/klaytn/klaytn@v1.12.1/networks/p2p/simulations/simulation.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2017 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from p2p/simulations/simulation.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package simulations 22 23 import ( 24 "context" 25 "time" 26 27 "github.com/klaytn/klaytn/networks/p2p/discover" 28 ) 29 30 // Simulation provides a framework for running actions in a simulated network 31 // and then waiting for expectations to be met 32 type Simulation struct { 33 network *Network 34 } 35 36 // NewSimulation returns a new simulation which runs in the given network 37 func NewSimulation(network *Network) *Simulation { 38 return &Simulation{ 39 network: network, 40 } 41 } 42 43 // Run performs a step of the simulation by performing the step's action and 44 // then waiting for the step's expectation to be met 45 func (s *Simulation) Run(ctx context.Context, step *Step) (result *StepResult) { 46 result = newStepResult() 47 48 result.StartedAt = time.Now() 49 defer func() { result.FinishedAt = time.Now() }() 50 51 // watch network events for the duration of the step 52 stop := s.watchNetwork(result) 53 defer stop() 54 55 // perform the action 56 if err := step.Action(ctx); err != nil { 57 result.Error = err 58 return 59 } 60 61 // wait for all node expectations to either pass, error or timeout 62 nodes := make(map[discover.NodeID]struct{}, len(step.Expect.Nodes)) 63 for _, id := range step.Expect.Nodes { 64 nodes[id] = struct{}{} 65 } 66 for len(result.Passes) < len(nodes) { 67 select { 68 case id := <-step.Trigger: 69 // skip if we aren't checking the node 70 if _, ok := nodes[id]; !ok { 71 continue 72 } 73 74 // skip if the node has already passed 75 if _, ok := result.Passes[id]; ok { 76 continue 77 } 78 79 // run the node expectation check 80 pass, err := step.Expect.Check(ctx, id) 81 if err != nil { 82 result.Error = err 83 return 84 } 85 if pass { 86 result.Passes[id] = time.Now() 87 } 88 case <-ctx.Done(): 89 result.Error = ctx.Err() 90 return 91 } 92 } 93 94 return 95 } 96 97 func (s *Simulation) watchNetwork(result *StepResult) func() { 98 stop := make(chan struct{}) 99 done := make(chan struct{}) 100 events := make(chan *Event) 101 sub := s.network.Events().Subscribe(events) 102 go func() { 103 defer close(done) 104 defer sub.Unsubscribe() 105 for { 106 select { 107 case event := <-events: 108 result.NetworkEvents = append(result.NetworkEvents, event) 109 case <-stop: 110 return 111 } 112 } 113 }() 114 return func() { 115 close(stop) 116 <-done 117 } 118 } 119 120 type Step struct { 121 // Action is the action to perform for this step 122 Action func(context.Context) error 123 124 // Trigger is a channel which receives node ids and triggers an 125 // expectation check for that node 126 Trigger chan discover.NodeID 127 128 // Expect is the expectation to wait for when performing this step 129 Expect *Expectation 130 } 131 132 type Expectation struct { 133 // Nodes is a list of nodes to check 134 Nodes []discover.NodeID 135 136 // Check checks whether a given node meets the expectation 137 Check func(context.Context, discover.NodeID) (bool, error) 138 } 139 140 func newStepResult() *StepResult { 141 return &StepResult{ 142 Passes: make(map[discover.NodeID]time.Time), 143 } 144 } 145 146 type StepResult struct { 147 // Error is the error encountered whilst running the step 148 Error error 149 150 // StartedAt is the time the step started 151 StartedAt time.Time 152 153 // FinishedAt is the time the step finished 154 FinishedAt time.Time 155 156 // Passes are the timestamps of the successful node expectations 157 Passes map[discover.NodeID]time.Time 158 159 // NetworkEvents are the network events which occurred during the step 160 NetworkEvents []*Event 161 }