github.com/ava-labs/avalanchego@v1.11.11/network/p2p/router.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package p2p
     5  
     6  import (
     7  	"context"
     8  	"encoding/binary"
     9  	"errors"
    10  	"fmt"
    11  	"strconv"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/prometheus/client_golang/prometheus"
    16  	"go.uber.org/zap"
    17  
    18  	"github.com/ava-labs/avalanchego/ids"
    19  	"github.com/ava-labs/avalanchego/message"
    20  	"github.com/ava-labs/avalanchego/snow/engine/common"
    21  	"github.com/ava-labs/avalanchego/utils/logging"
    22  )
    23  
    24  var (
    25  	ErrExistingAppProtocol = errors.New("existing app protocol")
    26  	ErrUnrequestedResponse = errors.New("unrequested response")
    27  
    28  	_ common.AppHandler = (*router)(nil)
    29  )
    30  
    31  type pendingAppRequest struct {
    32  	handlerID string
    33  	callback  AppResponseCallback
    34  }
    35  
    36  // meteredHandler emits metrics for a Handler
    37  type meteredHandler struct {
    38  	*responder
    39  	metrics
    40  }
    41  
    42  type metrics struct {
    43  	msgTime  *prometheus.GaugeVec
    44  	msgCount *prometheus.CounterVec
    45  }
    46  
    47  func (m *metrics) observe(labels prometheus.Labels, start time.Time) error {
    48  	metricTime, err := m.msgTime.GetMetricWith(labels)
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	metricCount, err := m.msgCount.GetMetricWith(labels)
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	metricTime.Add(float64(time.Since(start)))
    59  	metricCount.Inc()
    60  	return nil
    61  }
    62  
    63  // router routes incoming application messages to the corresponding registered
    64  // app handler. App messages must be made using the registered handler's
    65  // corresponding Client.
    66  type router struct {
    67  	log     logging.Logger
    68  	sender  common.AppSender
    69  	metrics metrics
    70  
    71  	lock               sync.RWMutex
    72  	handlers           map[uint64]*meteredHandler
    73  	pendingAppRequests map[uint32]pendingAppRequest
    74  	requestID          uint32
    75  }
    76  
    77  // newRouter returns a new instance of Router
    78  func newRouter(
    79  	log logging.Logger,
    80  	sender common.AppSender,
    81  	metrics metrics,
    82  ) *router {
    83  	return &router{
    84  		log:                log,
    85  		sender:             sender,
    86  		metrics:            metrics,
    87  		handlers:           make(map[uint64]*meteredHandler),
    88  		pendingAppRequests: make(map[uint32]pendingAppRequest),
    89  		// invariant: sdk uses odd-numbered requestIDs
    90  		requestID: 1,
    91  	}
    92  }
    93  
    94  func (r *router) addHandler(handlerID uint64, handler Handler) error {
    95  	r.lock.Lock()
    96  	defer r.lock.Unlock()
    97  
    98  	if _, ok := r.handlers[handlerID]; ok {
    99  		return fmt.Errorf("failed to register handler id %d: %w", handlerID, ErrExistingAppProtocol)
   100  	}
   101  
   102  	r.handlers[handlerID] = &meteredHandler{
   103  		responder: &responder{
   104  			Handler:   handler,
   105  			handlerID: handlerID,
   106  			log:       r.log,
   107  			sender:    r.sender,
   108  		},
   109  		metrics: r.metrics,
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  // AppRequest routes an AppRequest to a Handler based on the handler prefix. The
   116  // message is dropped if no matching handler can be found.
   117  //
   118  // Any error condition propagated outside Handler application logic is
   119  // considered fatal
   120  func (r *router) AppRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, deadline time.Time, request []byte) error {
   121  	start := time.Now()
   122  	parsedMsg, handler, handlerID, ok := r.parse(request)
   123  	if !ok {
   124  		r.log.Debug("received message for unregistered handler",
   125  			zap.Stringer("messageOp", message.AppRequestOp),
   126  			zap.Stringer("nodeID", nodeID),
   127  			zap.Uint32("requestID", requestID),
   128  			zap.Time("deadline", deadline),
   129  			zap.Binary("message", request),
   130  		)
   131  
   132  		// Send an error back to the requesting peer. Invalid requests that we
   133  		// cannot parse a handler id for are handled the same way as requests
   134  		// for which we do not have a registered handler.
   135  		return r.sender.SendAppError(ctx, nodeID, requestID, ErrUnregisteredHandler.Code, ErrUnregisteredHandler.Message)
   136  	}
   137  
   138  	// call the corresponding handler and send back a response to nodeID
   139  	if err := handler.AppRequest(ctx, nodeID, requestID, deadline, parsedMsg); err != nil {
   140  		return err
   141  	}
   142  
   143  	return r.metrics.observe(
   144  		prometheus.Labels{
   145  			opLabel:      message.AppRequestOp.String(),
   146  			handlerLabel: handlerID,
   147  		},
   148  		start,
   149  	)
   150  }
   151  
   152  // AppRequestFailed routes an AppRequestFailed message to the callback
   153  // corresponding to requestID.
   154  //
   155  // Any error condition propagated outside Handler application logic is
   156  // considered fatal
   157  func (r *router) AppRequestFailed(ctx context.Context, nodeID ids.NodeID, requestID uint32, appErr *common.AppError) error {
   158  	start := time.Now()
   159  	pending, ok := r.clearAppRequest(requestID)
   160  	if !ok {
   161  		// we should never receive a timeout without a corresponding requestID
   162  		return ErrUnrequestedResponse
   163  	}
   164  
   165  	pending.callback(ctx, nodeID, nil, appErr)
   166  
   167  	return r.metrics.observe(
   168  		prometheus.Labels{
   169  			opLabel:      message.AppErrorOp.String(),
   170  			handlerLabel: pending.handlerID,
   171  		},
   172  		start,
   173  	)
   174  }
   175  
   176  // AppResponse routes an AppResponse message to the callback corresponding to
   177  // requestID.
   178  //
   179  // Any error condition propagated outside Handler application logic is
   180  // considered fatal
   181  func (r *router) AppResponse(ctx context.Context, nodeID ids.NodeID, requestID uint32, response []byte) error {
   182  	start := time.Now()
   183  	pending, ok := r.clearAppRequest(requestID)
   184  	if !ok {
   185  		// we should never receive a timeout without a corresponding requestID
   186  		return ErrUnrequestedResponse
   187  	}
   188  
   189  	pending.callback(ctx, nodeID, response, nil)
   190  
   191  	return r.metrics.observe(
   192  		prometheus.Labels{
   193  			opLabel:      message.AppResponseOp.String(),
   194  			handlerLabel: pending.handlerID,
   195  		},
   196  		start,
   197  	)
   198  }
   199  
   200  // AppGossip routes an AppGossip message to a Handler based on the handler
   201  // prefix. The message is dropped if no matching handler can be found.
   202  //
   203  // Any error condition propagated outside Handler application logic is
   204  // considered fatal
   205  func (r *router) AppGossip(ctx context.Context, nodeID ids.NodeID, gossip []byte) error {
   206  	start := time.Now()
   207  	parsedMsg, handler, handlerID, ok := r.parse(gossip)
   208  	if !ok {
   209  		r.log.Debug("received message for unregistered handler",
   210  			zap.Stringer("messageOp", message.AppGossipOp),
   211  			zap.Stringer("nodeID", nodeID),
   212  			zap.Binary("message", gossip),
   213  		)
   214  		return nil
   215  	}
   216  
   217  	handler.AppGossip(ctx, nodeID, parsedMsg)
   218  
   219  	return r.metrics.observe(
   220  		prometheus.Labels{
   221  			opLabel:      message.AppGossipOp.String(),
   222  			handlerLabel: handlerID,
   223  		},
   224  		start,
   225  	)
   226  }
   227  
   228  // Parse parses a gossip or request message and maps it to a corresponding
   229  // handler if present.
   230  //
   231  // Returns:
   232  // - The unprefixed protocol message.
   233  // - The protocol responder.
   234  // - The protocol metric name.
   235  // - A boolean indicating that parsing succeeded.
   236  //
   237  // Invariant: Assumes [r.lock] isn't held.
   238  func (r *router) parse(prefixedMsg []byte) ([]byte, *meteredHandler, string, bool) {
   239  	handlerID, msg, ok := ParseMessage(prefixedMsg)
   240  	if !ok {
   241  		return nil, nil, "", false
   242  	}
   243  
   244  	handlerStr := strconv.FormatUint(handlerID, 10)
   245  
   246  	r.lock.RLock()
   247  	defer r.lock.RUnlock()
   248  
   249  	handler, ok := r.handlers[handlerID]
   250  	return msg, handler, handlerStr, ok
   251  }
   252  
   253  // Invariant: Assumes [r.lock] isn't held.
   254  func (r *router) clearAppRequest(requestID uint32) (pendingAppRequest, bool) {
   255  	r.lock.Lock()
   256  	defer r.lock.Unlock()
   257  
   258  	callback, ok := r.pendingAppRequests[requestID]
   259  	delete(r.pendingAppRequests, requestID)
   260  	return callback, ok
   261  }
   262  
   263  // Parse a gossip or request message.
   264  //
   265  // Returns:
   266  // - The protocol ID.
   267  // - The unprefixed protocol message.
   268  // - A boolean indicating that parsing succeeded.
   269  func ParseMessage(msg []byte) (uint64, []byte, bool) {
   270  	handlerID, bytesRead := binary.Uvarint(msg)
   271  	if bytesRead <= 0 {
   272  		return 0, nil, false
   273  	}
   274  	return handlerID, msg[bytesRead:], true
   275  }