github.com/bcskill/bcschain/v3@v3.4.9-beta2/p2p/simulations/simulation.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
    18  
    19  import (
    20  	"context"
    21  	"time"
    22  
    23  	"github.com/bcskill/bcschain/v3/p2p/discover"
    24  )
    25  
    26  // Simulation provides a framework for running actions in a simulated network
    27  // and then waiting for expectations to be met
    28  type Simulation struct {
    29  	network *Network
    30  }
    31  
    32  // NewSimulation returns a new simulation which runs in the given network
    33  func NewSimulation(network *Network) *Simulation {
    34  	return &Simulation{
    35  		network: network,
    36  	}
    37  }
    38  
    39  // Run performs a step of the simulation by performing the step's action and
    40  // then waiting for the step's expectation to be met
    41  func (s *Simulation) Run(ctx context.Context, step *Step) (result *StepResult) {
    42  	result = newStepResult()
    43  
    44  	result.StartedAt = time.Now()
    45  	defer func() { result.FinishedAt = time.Now() }()
    46  
    47  	// watch network events for the duration of the step
    48  	stop := s.watchNetwork(result)
    49  	defer stop()
    50  
    51  	// perform the action
    52  	if err := step.Action(ctx); err != nil {
    53  		result.Error = err
    54  		return
    55  	}
    56  
    57  	// wait for all node expectations to either pass, error or timeout
    58  	nodes := make(map[discover.NodeID]struct{}, len(step.Expect.Nodes))
    59  	for _, id := range step.Expect.Nodes {
    60  		nodes[id] = struct{}{}
    61  	}
    62  	for len(result.Passes) < len(nodes) {
    63  		select {
    64  		case id := <-step.Trigger:
    65  			// skip if we aren't checking the node
    66  			if _, ok := nodes[id]; !ok {
    67  				continue
    68  			}
    69  
    70  			// skip if the node has already passed
    71  			if _, ok := result.Passes[id]; ok {
    72  				continue
    73  			}
    74  
    75  			// run the node expectation check
    76  			pass, err := step.Expect.Check(ctx, id)
    77  			if err != nil {
    78  				result.Error = err
    79  				return
    80  			}
    81  			if pass {
    82  				result.Passes[id] = time.Now()
    83  			}
    84  		case <-ctx.Done():
    85  			result.Error = ctx.Err()
    86  			return
    87  		}
    88  	}
    89  
    90  	return
    91  }
    92  
    93  func (s *Simulation) watchNetwork(result *StepResult) func() {
    94  	stop := make(chan struct{})
    95  	done := make(chan struct{})
    96  	events := make(chan *Event)
    97  	s.network.Events().Subscribe(events, "simulations.Simulation-watchNetwork")
    98  	go func() {
    99  		defer close(done)
   100  		defer s.network.Events().Unsubscribe(events)
   101  		for {
   102  			select {
   103  			case event, ok := <-events:
   104  				if !ok {
   105  					return
   106  				}
   107  				result.NetworkEvents = append(result.NetworkEvents, event)
   108  			case <-stop:
   109  				return
   110  			}
   111  		}
   112  	}()
   113  	return func() {
   114  		close(stop)
   115  		<-done
   116  	}
   117  }
   118  
   119  type Step struct {
   120  	// Action is the action to perform for this step
   121  	Action func(context.Context) error
   122  
   123  	// Trigger is a channel which receives node ids and triggers an
   124  	// expectation check for that node
   125  	Trigger chan discover.NodeID
   126  
   127  	// Expect is the expectation to wait for when performing this step
   128  	Expect *Expectation
   129  }
   130  
   131  type Expectation struct {
   132  	// Nodes is a list of nodes to check
   133  	Nodes []discover.NodeID
   134  
   135  	// Check checks whether a given node meets the expectation
   136  	Check func(context.Context, discover.NodeID) (bool, error)
   137  }
   138  
   139  func newStepResult() *StepResult {
   140  	return &StepResult{
   141  		Passes: make(map[discover.NodeID]time.Time),
   142  	}
   143  }
   144  
   145  type StepResult struct {
   146  	// Error is the error encountered whilst running the step
   147  	Error error
   148  
   149  	// StartedAt is the time the step started
   150  	StartedAt time.Time
   151  
   152  	// FinishedAt is the time the step finished
   153  	FinishedAt time.Time
   154  
   155  	// Passes are the timestamps of the successful node expectations
   156  	Passes map[discover.NodeID]time.Time
   157  
   158  	// NetworkEvents are the network events which occurred during the step
   159  	NetworkEvents []*Event
   160  }