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  }