github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/network/network.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package network
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	"github.com/prometheus/client_golang/prometheus"
    11  
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  	"github.com/MetalBlockchain/metalgo/network/p2p"
    14  	"github.com/MetalBlockchain/metalgo/network/p2p/gossip"
    15  	"github.com/MetalBlockchain/metalgo/snow/engine/common"
    16  	"github.com/MetalBlockchain/metalgo/snow/validators"
    17  	"github.com/MetalBlockchain/metalgo/utils/logging"
    18  	"github.com/MetalBlockchain/metalgo/vms/avm/txs"
    19  	"github.com/MetalBlockchain/metalgo/vms/avm/txs/mempool"
    20  )
    21  
    22  const txGossipHandlerID = 0
    23  
    24  var (
    25  	_ common.AppHandler    = (*Network)(nil)
    26  	_ validators.Connector = (*Network)(nil)
    27  )
    28  
    29  type Network struct {
    30  	*p2p.Network
    31  
    32  	log       logging.Logger
    33  	parser    txs.Parser
    34  	mempool   *gossipMempool
    35  	appSender common.AppSender
    36  
    37  	txPushGossiper        *gossip.PushGossiper[*txs.Tx]
    38  	txPushGossipFrequency time.Duration
    39  	txPullGossiper        gossip.Gossiper
    40  	txPullGossipFrequency time.Duration
    41  }
    42  
    43  func New(
    44  	log logging.Logger,
    45  	nodeID ids.NodeID,
    46  	subnetID ids.ID,
    47  	vdrs validators.State,
    48  	parser txs.Parser,
    49  	txVerifier TxVerifier,
    50  	mempool mempool.Mempool,
    51  	appSender common.AppSender,
    52  	registerer prometheus.Registerer,
    53  	config Config,
    54  ) (*Network, error) {
    55  	p2pNetwork, err := p2p.NewNetwork(log, appSender, registerer, "p2p")
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	marshaller := &txParser{
    61  		parser: parser,
    62  	}
    63  	validators := p2p.NewValidators(
    64  		p2pNetwork.Peers,
    65  		log,
    66  		subnetID,
    67  		vdrs,
    68  		config.MaxValidatorSetStaleness,
    69  	)
    70  	txGossipClient := p2pNetwork.NewClient(
    71  		txGossipHandlerID,
    72  		p2p.WithValidatorSampling(validators),
    73  	)
    74  	txGossipMetrics, err := gossip.NewMetrics(registerer, "tx")
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	gossipMempool, err := newGossipMempool(
    80  		mempool,
    81  		registerer,
    82  		log,
    83  		txVerifier,
    84  		parser,
    85  		config.ExpectedBloomFilterElements,
    86  		config.ExpectedBloomFilterFalsePositiveProbability,
    87  		config.MaxBloomFilterFalsePositiveProbability,
    88  	)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	txPushGossiper, err := gossip.NewPushGossiper[*txs.Tx](
    94  		marshaller,
    95  		gossipMempool,
    96  		validators,
    97  		txGossipClient,
    98  		txGossipMetrics,
    99  		gossip.BranchingFactor{
   100  			StakePercentage: config.PushGossipPercentStake,
   101  			Validators:      config.PushGossipNumValidators,
   102  			Peers:           config.PushGossipNumPeers,
   103  		},
   104  		gossip.BranchingFactor{
   105  			Validators: config.PushRegossipNumValidators,
   106  			Peers:      config.PushRegossipNumPeers,
   107  		},
   108  		config.PushGossipDiscardedCacheSize,
   109  		config.TargetGossipSize,
   110  		config.PushGossipMaxRegossipFrequency,
   111  	)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	var txPullGossiper gossip.Gossiper = gossip.NewPullGossiper[*txs.Tx](
   117  		log,
   118  		marshaller,
   119  		gossipMempool,
   120  		txGossipClient,
   121  		txGossipMetrics,
   122  		config.PullGossipPollSize,
   123  	)
   124  
   125  	// Gossip requests are only served if a node is a validator
   126  	txPullGossiper = gossip.ValidatorGossiper{
   127  		Gossiper:   txPullGossiper,
   128  		NodeID:     nodeID,
   129  		Validators: validators,
   130  	}
   131  
   132  	handler := gossip.NewHandler[*txs.Tx](
   133  		log,
   134  		marshaller,
   135  		gossipMempool,
   136  		txGossipMetrics,
   137  		config.TargetGossipSize,
   138  	)
   139  
   140  	validatorHandler := p2p.NewValidatorHandler(
   141  		p2p.NewThrottlerHandler(
   142  			handler,
   143  			p2p.NewSlidingWindowThrottler(
   144  				config.PullGossipThrottlingPeriod,
   145  				config.PullGossipThrottlingLimit,
   146  			),
   147  			log,
   148  		),
   149  		validators,
   150  		log,
   151  	)
   152  
   153  	// We allow pushing txs between all peers, but only serve gossip requests
   154  	// from validators
   155  	txGossipHandler := txGossipHandler{
   156  		appGossipHandler:  handler,
   157  		appRequestHandler: validatorHandler,
   158  	}
   159  
   160  	if err := p2pNetwork.AddHandler(txGossipHandlerID, txGossipHandler); err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	return &Network{
   165  		Network:               p2pNetwork,
   166  		log:                   log,
   167  		parser:                parser,
   168  		mempool:               gossipMempool,
   169  		appSender:             appSender,
   170  		txPushGossiper:        txPushGossiper,
   171  		txPushGossipFrequency: config.PushGossipFrequency,
   172  		txPullGossiper:        txPullGossiper,
   173  		txPullGossipFrequency: config.PullGossipFrequency,
   174  	}, nil
   175  }
   176  
   177  func (n *Network) PushGossip(ctx context.Context) {
   178  	gossip.Every(ctx, n.log, n.txPushGossiper, n.txPushGossipFrequency)
   179  }
   180  
   181  func (n *Network) PullGossip(ctx context.Context) {
   182  	gossip.Every(ctx, n.log, n.txPullGossiper, n.txPullGossipFrequency)
   183  }
   184  
   185  // IssueTxFromRPC attempts to add a tx to the mempool, after verifying it. If
   186  // the tx is added to the mempool, it will attempt to push gossip the tx to
   187  // random peers in the network.
   188  //
   189  // If the tx is already in the mempool, mempool.ErrDuplicateTx will be
   190  // returned.
   191  // If the tx is not added to the mempool, an error will be returned.
   192  func (n *Network) IssueTxFromRPC(tx *txs.Tx) error {
   193  	if err := n.mempool.Add(tx); err != nil {
   194  		return err
   195  	}
   196  	n.txPushGossiper.Add(tx)
   197  	return nil
   198  }
   199  
   200  // IssueTxFromRPCWithoutVerification attempts to add a tx to the mempool,
   201  // without first verifying it. If the tx is added to the mempool, it will
   202  // attempt to push gossip the tx to random peers in the network.
   203  //
   204  // If the tx is already in the mempool, mempool.ErrDuplicateTx will be
   205  // returned.
   206  // If the tx is not added to the mempool, an error will be returned.
   207  func (n *Network) IssueTxFromRPCWithoutVerification(tx *txs.Tx) error {
   208  	if err := n.mempool.AddWithoutVerification(tx); err != nil {
   209  		return err
   210  	}
   211  	n.txPushGossiper.Add(tx)
   212  	return nil
   213  }