github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/network/simulation/events.go (about)

     1  // Copyright 2018 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 simulation
    18  
    19  import (
    20  	"context"
    21  	"sync"
    22  
    23  	"github.com/FusionFoundation/efsn/p2p/discover"
    24  
    25  	"github.com/FusionFoundation/efsn/p2p"
    26  )
    27  
    28  // PeerEvent is the type of the channel returned by Simulation.PeerEvents.
    29  type PeerEvent struct {
    30  	// NodeID is the ID of node that the event is caught on.
    31  	NodeID discover.NodeID
    32  	// Event is the event that is caught.
    33  	Event *p2p.PeerEvent
    34  	// Error is the error that may have happened during event watching.
    35  	Error error
    36  }
    37  
    38  // PeerEventsFilter defines a filter on PeerEvents to exclude messages with
    39  // defined properties. Use PeerEventsFilter methods to set required options.
    40  type PeerEventsFilter struct {
    41  	t        *p2p.PeerEventType
    42  	protocol *string
    43  	msgCode  *uint64
    44  }
    45  
    46  // NewPeerEventsFilter returns a new PeerEventsFilter instance.
    47  func NewPeerEventsFilter() *PeerEventsFilter {
    48  	return &PeerEventsFilter{}
    49  }
    50  
    51  // Type sets the filter to only one peer event type.
    52  func (f *PeerEventsFilter) Type(t p2p.PeerEventType) *PeerEventsFilter {
    53  	f.t = &t
    54  	return f
    55  }
    56  
    57  // Protocol sets the filter to only one message protocol.
    58  func (f *PeerEventsFilter) Protocol(p string) *PeerEventsFilter {
    59  	f.protocol = &p
    60  	return f
    61  }
    62  
    63  // MsgCode sets the filter to only one msg code.
    64  func (f *PeerEventsFilter) MsgCode(c uint64) *PeerEventsFilter {
    65  	f.msgCode = &c
    66  	return f
    67  }
    68  
    69  // PeerEvents returns a channel of events that are captured by admin peerEvents
    70  // subscription nodes with provided NodeIDs. Additional filters can be set to ignore
    71  // events that are not relevant.
    72  func (s *Simulation) PeerEvents(ctx context.Context, ids []discover.NodeID, filters ...*PeerEventsFilter) <-chan PeerEvent {
    73  	eventC := make(chan PeerEvent)
    74  
    75  	// wait group to make sure all subscriptions to admin peerEvents are established
    76  	// before this function returns.
    77  	var subsWG sync.WaitGroup
    78  	for _, id := range ids {
    79  		s.shutdownWG.Add(1)
    80  		subsWG.Add(1)
    81  		go func(id discover.NodeID) {
    82  			defer s.shutdownWG.Done()
    83  
    84  			client, err := s.Net.GetNode(id).Client()
    85  			if err != nil {
    86  				subsWG.Done()
    87  				eventC <- PeerEvent{NodeID: id, Error: err}
    88  				return
    89  			}
    90  			events := make(chan *p2p.PeerEvent)
    91  			sub, err := client.Subscribe(ctx, "admin", events, "peerEvents")
    92  			if err != nil {
    93  				subsWG.Done()
    94  				eventC <- PeerEvent{NodeID: id, Error: err}
    95  				return
    96  			}
    97  			defer sub.Unsubscribe()
    98  
    99  			subsWG.Done()
   100  
   101  			for {
   102  				select {
   103  				case <-ctx.Done():
   104  					if err := ctx.Err(); err != nil {
   105  						select {
   106  						case eventC <- PeerEvent{NodeID: id, Error: err}:
   107  						case <-s.Done():
   108  						}
   109  					}
   110  					return
   111  				case <-s.Done():
   112  					return
   113  				case e := <-events:
   114  					match := len(filters) == 0 // if there are no filters match all events
   115  					for _, f := range filters {
   116  						if f.t != nil && *f.t != e.Type {
   117  							continue
   118  						}
   119  						if f.protocol != nil && *f.protocol != e.Protocol {
   120  							continue
   121  						}
   122  						if f.msgCode != nil && e.MsgCode != nil && *f.msgCode != *e.MsgCode {
   123  							continue
   124  						}
   125  						// all filter parameters matched, break the loop
   126  						match = true
   127  						break
   128  					}
   129  					if match {
   130  						select {
   131  						case eventC <- PeerEvent{NodeID: id, Event: e}:
   132  						case <-ctx.Done():
   133  							if err := ctx.Err(); err != nil {
   134  								select {
   135  								case eventC <- PeerEvent{NodeID: id, Error: err}:
   136  								case <-s.Done():
   137  								}
   138  							}
   139  							return
   140  						case <-s.Done():
   141  							return
   142  						}
   143  					}
   144  				case err := <-sub.Err():
   145  					if err != nil {
   146  						select {
   147  						case eventC <- PeerEvent{NodeID: id, Error: err}:
   148  						case <-ctx.Done():
   149  							if err := ctx.Err(); err != nil {
   150  								select {
   151  								case eventC <- PeerEvent{NodeID: id, Error: err}:
   152  								case <-s.Done():
   153  								}
   154  							}
   155  							return
   156  						case <-s.Done():
   157  							return
   158  						}
   159  					}
   160  				}
   161  			}
   162  		}(id)
   163  	}
   164  
   165  	// wait all subscriptions
   166  	subsWG.Wait()
   167  	return eventC
   168  }