github.com/dim4egster/coreth@v0.10.2/peer/network.go (about)

     1  // (c) 2019-2022, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package peer
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"sync"
    11  	"time"
    12  
    13  	"golang.org/x/sync/semaphore"
    14  
    15  	"github.com/ethereum/go-ethereum/log"
    16  
    17  	"github.com/dim4egster/qmallgo/codec"
    18  	"github.com/dim4egster/qmallgo/ids"
    19  	"github.com/dim4egster/qmallgo/snow/engine/common"
    20  	"github.com/dim4egster/qmallgo/snow/validators"
    21  	"github.com/dim4egster/qmallgo/version"
    22  
    23  	"github.com/dim4egster/coreth/peer/stats"
    24  	"github.com/dim4egster/coreth/plugin/evm/message"
    25  )
    26  
    27  // Minimum amount of time to handle a request
    28  const minRequestHandlingDuration = 100 * time.Millisecond
    29  
    30  var (
    31  	errAcquiringSemaphore                      = errors.New("error acquiring semaphore")
    32  	_                     Network              = &network{}
    33  	_                     validators.Connector = &network{}
    34  	_                     common.AppHandler    = &network{}
    35  )
    36  
    37  type Network interface {
    38  	validators.Connector
    39  	common.AppHandler
    40  
    41  	// RequestAny synchronously sends request to a randomly chosen peer with a
    42  	// node version greater than or equal to minVersion.
    43  	// Returns the ID of the chosen peer, and an error if the request could not
    44  	// be sent to a peer with the desired [minVersion].
    45  	RequestAny(minVersion *version.Application, message []byte, handler message.ResponseHandler) (ids.NodeID, error)
    46  
    47  	// Request sends message to given nodeID, notifying handler when there's a response or timeout
    48  	Request(nodeID ids.NodeID, message []byte, handler message.ResponseHandler) error
    49  
    50  	// Gossip sends given gossip message to peers
    51  	Gossip(gossip []byte) error
    52  
    53  	// Shutdown stops all peer channel listeners and marks the node to have stopped
    54  	// n.Start() can be called again but the peers will have to be reconnected
    55  	// by calling OnPeerConnected for each peer
    56  	Shutdown()
    57  
    58  	// SetGossipHandler sets the provided gossip handler as the gossip handler
    59  	SetGossipHandler(handler message.GossipHandler)
    60  
    61  	// SetRequestHandler sets the provided request handler as the request handler
    62  	SetRequestHandler(handler message.RequestHandler)
    63  
    64  	// Size returns the size of the network in number of connected peers
    65  	Size() uint32
    66  
    67  	// TrackBandwidth should be called for each valid request with the bandwidth
    68  	// (length of response divided by request time), and with 0 if the response is invalid.
    69  	TrackBandwidth(nodeID ids.NodeID, bandwidth float64)
    70  }
    71  
    72  // network is an implementation of Network that processes message requests for
    73  // each peer in linear fashion
    74  type network struct {
    75  	lock                       sync.RWMutex                       // lock for mutating state of this Network struct
    76  	self                       ids.NodeID                         // NodeID of this node
    77  	requestIDGen               uint32                             // requestID counter used to track outbound requests
    78  	outstandingRequestHandlers map[uint32]message.ResponseHandler // maps qmallgo requestID => message.ResponseHandler
    79  	activeRequests             *semaphore.Weighted                // controls maximum number of active outbound requests
    80  	appSender                  common.AppSender                   // qmallgo AppSender for sending messages
    81  	codec                      codec.Manager                      // Codec used for parsing messages
    82  	requestHandler             message.RequestHandler             // maps request type => handler
    83  	gossipHandler              message.GossipHandler              // maps gossip type => handler
    84  	peers                      *peerTracker                       // tracking of peers & bandwidth
    85  	stats                      stats.RequestHandlerStats          // Provide request handler metrics
    86  }
    87  
    88  func NewNetwork(appSender common.AppSender, codec codec.Manager, self ids.NodeID, maxActiveRequests int64) Network {
    89  	return &network{
    90  		appSender:                  appSender,
    91  		codec:                      codec,
    92  		self:                       self,
    93  		outstandingRequestHandlers: make(map[uint32]message.ResponseHandler),
    94  		activeRequests:             semaphore.NewWeighted(maxActiveRequests),
    95  		gossipHandler:              message.NoopMempoolGossipHandler{},
    96  		requestHandler:             message.NoopRequestHandler{},
    97  		peers:                      NewPeerTracker(),
    98  		stats:                      stats.NewRequestHandlerStats(),
    99  	}
   100  }
   101  
   102  // RequestAny synchronously sends request to a randomly chosen peer with a
   103  // node version greater than or equal to minVersion. If minVersion is nil,
   104  // the request will be sent to any peer regardless of their version.
   105  // Returns the ID of the chosen peer, and an error if the request could not
   106  // be sent to a peer with the desired [minVersion].
   107  func (n *network) RequestAny(minVersion *version.Application, request []byte, handler message.ResponseHandler) (ids.NodeID, error) {
   108  	// Take a slot from total [activeRequests] and block until a slot becomes available.
   109  	if err := n.activeRequests.Acquire(context.Background(), 1); err != nil {
   110  		return ids.EmptyNodeID, errAcquiringSemaphore
   111  	}
   112  
   113  	n.lock.Lock()
   114  	defer n.lock.Unlock()
   115  	if nodeID, ok := n.peers.GetAnyPeer(minVersion); ok {
   116  		return nodeID, n.request(nodeID, request, handler)
   117  	}
   118  
   119  	n.activeRequests.Release(1)
   120  	return ids.EmptyNodeID, fmt.Errorf("no peers found matching version %s out of %d peers", minVersion, n.peers.Size())
   121  }
   122  
   123  // Request sends request message bytes to specified nodeID, notifying the responseHandler on response or failure
   124  func (n *network) Request(nodeID ids.NodeID, request []byte, responseHandler message.ResponseHandler) error {
   125  	if nodeID == ids.EmptyNodeID {
   126  		return fmt.Errorf("cannot send request to empty nodeID, nodeID=%s, requestLen=%d", nodeID, len(request))
   127  	}
   128  
   129  	// Take a slot from total [activeRequests] and block until a slot becomes available.
   130  	if err := n.activeRequests.Acquire(context.Background(), 1); err != nil {
   131  		return errAcquiringSemaphore
   132  	}
   133  
   134  	n.lock.Lock()
   135  	defer n.lock.Unlock()
   136  
   137  	return n.request(nodeID, request, responseHandler)
   138  }
   139  
   140  // request sends request message bytes to specified nodeID and adds [responseHandler] to [outstandingRequestHandlers]
   141  // so that it can be invoked when the network receives either a response or failure message.
   142  // Assumes [nodeID] is never [self] since we guarantee [self] will not be added to the [peers] map.
   143  // Releases active requests semaphore if there was an error in sending the request
   144  // Returns an error if [appSender] is unable to make the request.
   145  // Assumes write lock is held
   146  func (n *network) request(nodeID ids.NodeID, request []byte, responseHandler message.ResponseHandler) error {
   147  	log.Debug("sending request to peer", "nodeID", nodeID, "requestLen", len(request))
   148  	n.peers.TrackPeer(nodeID)
   149  
   150  	// generate requestID
   151  	requestID := n.requestIDGen
   152  	n.requestIDGen++
   153  
   154  	n.outstandingRequestHandlers[requestID] = responseHandler
   155  
   156  	nodeIDs := ids.NewNodeIDSet(1)
   157  	nodeIDs.Add(nodeID)
   158  
   159  	// send app request to the peer
   160  	// on failure: release the activeRequests slot, mark message as processed and return fatal error
   161  	// Send app request to [nodeID].
   162  	// On failure, release the slot from active requests and [outstandingRequestHandlers].
   163  	if err := n.appSender.SendAppRequest(nodeIDs, requestID, request); err != nil {
   164  		n.activeRequests.Release(1)
   165  		delete(n.outstandingRequestHandlers, requestID)
   166  		return err
   167  	}
   168  
   169  	log.Debug("sent request message to peer", "nodeID", nodeID, "requestID", requestID)
   170  	return nil
   171  }
   172  
   173  // AppRequest is called by qmallgo -> VM when there is an incoming AppRequest from a peer
   174  // error returned by this function is expected to be treated as fatal by the engine
   175  // returns error if the requestHandler returns an error
   176  // sends a response back to the sender if length of response returned by the handler is >0
   177  // expects the deadline to not have been passed
   178  func (n *network) AppRequest(nodeID ids.NodeID, requestID uint32, deadline time.Time, request []byte) error {
   179  	n.lock.RLock()
   180  	defer n.lock.RUnlock()
   181  
   182  	log.Debug("received AppRequest from node", "nodeID", nodeID, "requestID", requestID, "requestLen", len(request))
   183  
   184  	var req message.Request
   185  	if _, err := n.codec.Unmarshal(request, &req); err != nil {
   186  		log.Debug("failed to unmarshal app request", "nodeID", nodeID, "requestID", requestID, "requestLen", len(request), "err", err)
   187  		return nil
   188  	}
   189  
   190  	// calculate how much time is left until the deadline
   191  	timeTillDeadline := time.Until(deadline)
   192  	n.stats.UpdateTimeUntilDeadline(timeTillDeadline)
   193  
   194  	// bufferedDeadline is half the time till actual deadline so that the message has a reasonable chance
   195  	// of completing its processing and sending the response to the peer.
   196  	timeTillDeadline = time.Duration(timeTillDeadline.Nanoseconds() / 2)
   197  	bufferedDeadline := time.Now().Add(timeTillDeadline)
   198  
   199  	// check if we have enough time to handle this request
   200  	if time.Until(bufferedDeadline) < minRequestHandlingDuration {
   201  		// Drop the request if we already missed the deadline to respond.
   202  		log.Debug("deadline to process AppRequest has expired, skipping", "nodeID", nodeID, "requestID", requestID, "req", req)
   203  		n.stats.IncDeadlineDroppedRequest()
   204  		return nil
   205  	}
   206  
   207  	log.Debug("processing incoming request", "nodeID", nodeID, "requestID", requestID, "req", req)
   208  	ctx, cancel := context.WithDeadline(context.Background(), bufferedDeadline)
   209  	defer cancel()
   210  
   211  	responseBytes, err := req.Handle(ctx, nodeID, requestID, n.requestHandler)
   212  	switch {
   213  	case err != nil && err != context.DeadlineExceeded:
   214  		return err // Return a fatal error
   215  	case responseBytes != nil:
   216  		return n.appSender.SendAppResponse(nodeID, requestID, responseBytes) // Propagate fatal error
   217  	default:
   218  		return nil
   219  	}
   220  }
   221  
   222  // AppResponse is invoked when there is a response received from a peer regarding a request
   223  // Error returned by this function is expected to be treated as fatal by the engine
   224  // If [requestID] is not known, this function will emit a log and return a nil error.
   225  // If the response handler returns an error it is propagated as a fatal error.
   226  func (n *network) AppResponse(nodeID ids.NodeID, requestID uint32, response []byte) error {
   227  	n.lock.Lock()
   228  	defer n.lock.Unlock()
   229  
   230  	log.Debug("received AppResponse from peer", "nodeID", nodeID, "requestID", requestID)
   231  
   232  	handler, exists := n.getRequestHandler(requestID)
   233  	if !exists {
   234  		// Should never happen since the engine should be managing outstanding requests
   235  		log.Error("received response to unknown request", "nodeID", nodeID, "requestID", requestID, "responseLen", len(response))
   236  		return nil
   237  	}
   238  
   239  	return handler.OnResponse(nodeID, requestID, response)
   240  }
   241  
   242  // AppRequestFailed can be called by the qmallgo -> VM in following cases:
   243  // - node is benched
   244  // - failed to send message to [nodeID] due to a network issue
   245  // - timeout
   246  // error returned by this function is expected to be treated as fatal by the engine
   247  // returns error only when the response handler returns an error
   248  func (n *network) AppRequestFailed(nodeID ids.NodeID, requestID uint32) error {
   249  	n.lock.Lock()
   250  	defer n.lock.Unlock()
   251  	log.Debug("received AppRequestFailed from peer", "nodeID", nodeID, "requestID", requestID)
   252  
   253  	handler, exists := n.getRequestHandler(requestID)
   254  	if !exists {
   255  		// Should never happen since the engine should be managing outstanding requests
   256  		log.Error("received request failed to unknown request", "nodeID", nodeID, "requestID", requestID)
   257  		return nil
   258  	}
   259  
   260  	return handler.OnFailure(nodeID, requestID)
   261  }
   262  
   263  // getRequestHandler fetches the handler for [requestID] and marks the request with [requestID] as having been fulfilled.
   264  // This is called by either [AppResponse] or [AppRequestFailed].
   265  // assumes that the write lock is held.
   266  func (n *network) getRequestHandler(requestID uint32) (message.ResponseHandler, bool) {
   267  	handler, exists := n.outstandingRequestHandlers[requestID]
   268  	if !exists {
   269  		return nil, false
   270  	}
   271  	// mark message as processed, release activeRequests slot
   272  	delete(n.outstandingRequestHandlers, requestID)
   273  	n.activeRequests.Release(1)
   274  	return handler, true
   275  }
   276  
   277  // Gossip sends given gossip message to peers
   278  func (n *network) Gossip(gossip []byte) error {
   279  	return n.appSender.SendAppGossip(gossip)
   280  }
   281  
   282  // AppGossip is called by qmallgo -> VM when there is an incoming AppGossip from a peer
   283  // error returned by this function is expected to be treated as fatal by the engine
   284  // returns error if request could not be parsed as message.Request or when the requestHandler returns an error
   285  func (n *network) AppGossip(nodeID ids.NodeID, gossipBytes []byte) error {
   286  	var gossipMsg message.GossipMessage
   287  	if _, err := n.codec.Unmarshal(gossipBytes, &gossipMsg); err != nil {
   288  		log.Debug("could not parse app gossip", "nodeID", nodeID, "gossipLen", len(gossipBytes), "err", err)
   289  		return nil
   290  	}
   291  
   292  	log.Debug("processing AppGossip from node", "nodeID", nodeID, "msg", gossipMsg)
   293  	return gossipMsg.Handle(n.gossipHandler, nodeID)
   294  }
   295  
   296  // Connected adds the given nodeID to the peer list so that it can receive messages
   297  func (n *network) Connected(nodeID ids.NodeID, nodeVersion *version.Application) error {
   298  	log.Debug("adding new peer", "nodeID", nodeID)
   299  
   300  	n.lock.Lock()
   301  	defer n.lock.Unlock()
   302  
   303  	if nodeID == n.self {
   304  		log.Debug("skipping registering self as peer")
   305  		return nil
   306  	}
   307  
   308  	n.peers.Connected(nodeID, nodeVersion)
   309  	return nil
   310  }
   311  
   312  // Disconnected removes given [nodeID] from the peer list
   313  func (n *network) Disconnected(nodeID ids.NodeID) error {
   314  	log.Debug("disconnecting peer", "nodeID", nodeID)
   315  	n.lock.Lock()
   316  	defer n.lock.Unlock()
   317  
   318  	n.peers.Disconnected(nodeID)
   319  	return nil
   320  }
   321  
   322  // Shutdown disconnects all peers
   323  func (n *network) Shutdown() {
   324  	n.lock.Lock()
   325  	defer n.lock.Unlock()
   326  
   327  	// reset peers
   328  	n.peers = NewPeerTracker()
   329  }
   330  
   331  func (n *network) SetGossipHandler(handler message.GossipHandler) {
   332  	n.lock.Lock()
   333  	defer n.lock.Unlock()
   334  
   335  	n.gossipHandler = handler
   336  }
   337  
   338  func (n *network) SetRequestHandler(handler message.RequestHandler) {
   339  	n.lock.Lock()
   340  	defer n.lock.Unlock()
   341  
   342  	n.requestHandler = handler
   343  }
   344  
   345  func (n *network) Size() uint32 {
   346  	n.lock.RLock()
   347  	defer n.lock.RUnlock()
   348  
   349  	return uint32(n.peers.Size())
   350  }
   351  
   352  func (n *network) TrackBandwidth(nodeID ids.NodeID, bandwidth float64) {
   353  	n.lock.Lock()
   354  	defer n.lock.Unlock()
   355  
   356  	n.peers.TrackBandwidth(nodeID, bandwidth)
   357  }