github.com/MetalBlockchain/metalgo@v1.11.9/network/p2p/gossip/handler.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package gossip
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	"go.uber.org/zap"
    11  
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  	"github.com/MetalBlockchain/metalgo/network/p2p"
    14  	"github.com/MetalBlockchain/metalgo/utils/bloom"
    15  	"github.com/MetalBlockchain/metalgo/utils/logging"
    16  )
    17  
    18  var _ p2p.Handler = (*Handler[*testTx])(nil)
    19  
    20  func NewHandler[T Gossipable](
    21  	log logging.Logger,
    22  	marshaller Marshaller[T],
    23  	set Set[T],
    24  	metrics Metrics,
    25  	targetResponseSize int,
    26  ) *Handler[T] {
    27  	return &Handler[T]{
    28  		Handler:            p2p.NoOpHandler{},
    29  		log:                log,
    30  		marshaller:         marshaller,
    31  		set:                set,
    32  		metrics:            metrics,
    33  		targetResponseSize: targetResponseSize,
    34  	}
    35  }
    36  
    37  type Handler[T Gossipable] struct {
    38  	p2p.Handler
    39  	marshaller         Marshaller[T]
    40  	log                logging.Logger
    41  	set                Set[T]
    42  	metrics            Metrics
    43  	targetResponseSize int
    44  }
    45  
    46  func (h Handler[T]) AppRequest(_ context.Context, _ ids.NodeID, _ time.Time, requestBytes []byte) ([]byte, error) {
    47  	filter, salt, err := ParseAppRequest(requestBytes)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	responseSize := 0
    53  	gossipBytes := make([][]byte, 0)
    54  	h.set.Iterate(func(gossipable T) bool {
    55  		gossipID := gossipable.GossipID()
    56  
    57  		// filter out what the requesting peer already knows about
    58  		if bloom.Contains(filter, gossipID[:], salt[:]) {
    59  			return true
    60  		}
    61  
    62  		var bytes []byte
    63  		bytes, err = h.marshaller.MarshalGossip(gossipable)
    64  		if err != nil {
    65  			return false
    66  		}
    67  
    68  		// check that this doesn't exceed our maximum configured target response
    69  		// size
    70  		gossipBytes = append(gossipBytes, bytes)
    71  		responseSize += len(bytes)
    72  
    73  		return responseSize <= h.targetResponseSize
    74  	})
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	if err := h.metrics.observeMessage(sentPullLabels, len(gossipBytes), responseSize); err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	return MarshalAppResponse(gossipBytes)
    84  }
    85  
    86  func (h Handler[_]) AppGossip(_ context.Context, nodeID ids.NodeID, gossipBytes []byte) {
    87  	gossip, err := ParseAppGossip(gossipBytes)
    88  	if err != nil {
    89  		h.log.Debug("failed to unmarshal gossip", zap.Error(err))
    90  		return
    91  	}
    92  
    93  	receivedBytes := 0
    94  	for _, bytes := range gossip {
    95  		receivedBytes += len(bytes)
    96  		gossipable, err := h.marshaller.UnmarshalGossip(bytes)
    97  		if err != nil {
    98  			h.log.Debug("failed to unmarshal gossip",
    99  				zap.Stringer("nodeID", nodeID),
   100  				zap.Error(err),
   101  			)
   102  			continue
   103  		}
   104  
   105  		if err := h.set.Add(gossipable); err != nil {
   106  			h.log.Debug(
   107  				"failed to add gossip to the known set",
   108  				zap.Stringer("nodeID", nodeID),
   109  				zap.Stringer("id", gossipable.GossipID()),
   110  				zap.Error(err),
   111  			)
   112  		}
   113  	}
   114  
   115  	if err := h.metrics.observeMessage(receivedPushLabels, len(gossip), receivedBytes); err != nil {
   116  		h.log.Error("failed to update metrics",
   117  			zap.Error(err),
   118  		)
   119  	}
   120  }