github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/simulations/simulation.go (about)

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