github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/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  	"errors"
     9  	"time"
    10  
    11  	"github.com/prometheus/client_golang/prometheus"
    12  	"go.uber.org/zap"
    13  
    14  	"github.com/MetalBlockchain/metalgo/ids"
    15  	"github.com/MetalBlockchain/metalgo/network/p2p"
    16  	"github.com/MetalBlockchain/metalgo/network/p2p/gossip"
    17  	"github.com/MetalBlockchain/metalgo/snow/engine/common"
    18  	"github.com/MetalBlockchain/metalgo/snow/validators"
    19  	"github.com/MetalBlockchain/metalgo/utils/logging"
    20  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs"
    21  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs/mempool"
    22  )
    23  
    24  const TxGossipHandlerID = 0
    25  
    26  var errMempoolDisabledWithPartialSync = errors.New("mempool is disabled partial syncing")
    27  
    28  type Network struct {
    29  	*p2p.Network
    30  
    31  	log                       logging.Logger
    32  	txVerifier                TxVerifier
    33  	mempool                   *gossipMempool
    34  	partialSyncPrimaryNetwork bool
    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  	txVerifier TxVerifier,
    49  	mempool mempool.Mempool,
    50  	partialSyncPrimaryNetwork bool,
    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 := txMarshaller{}
    61  	validators := p2p.NewValidators(
    62  		p2pNetwork.Peers,
    63  		log,
    64  		subnetID,
    65  		vdrs,
    66  		config.MaxValidatorSetStaleness,
    67  	)
    68  	txGossipClient := p2pNetwork.NewClient(
    69  		TxGossipHandlerID,
    70  		p2p.WithValidatorSampling(validators),
    71  	)
    72  	txGossipMetrics, err := gossip.NewMetrics(registerer, "tx")
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	gossipMempool, err := newGossipMempool(
    78  		mempool,
    79  		registerer,
    80  		log,
    81  		txVerifier,
    82  		config.ExpectedBloomFilterElements,
    83  		config.ExpectedBloomFilterFalsePositiveProbability,
    84  		config.MaxBloomFilterFalsePositiveProbability,
    85  	)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	txPushGossiper, err := gossip.NewPushGossiper[*txs.Tx](
    91  		marshaller,
    92  		gossipMempool,
    93  		validators,
    94  		txGossipClient,
    95  		txGossipMetrics,
    96  		gossip.BranchingFactor{
    97  			StakePercentage: config.PushGossipPercentStake,
    98  			Validators:      config.PushGossipNumValidators,
    99  			Peers:           config.PushGossipNumPeers,
   100  		},
   101  		gossip.BranchingFactor{
   102  			Validators: config.PushRegossipNumValidators,
   103  			Peers:      config.PushRegossipNumPeers,
   104  		},
   105  		config.PushGossipDiscardedCacheSize,
   106  		config.TargetGossipSize,
   107  		config.PushGossipMaxRegossipFrequency,
   108  	)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	var txPullGossiper gossip.Gossiper = gossip.NewPullGossiper[*txs.Tx](
   114  		log,
   115  		marshaller,
   116  		gossipMempool,
   117  		txGossipClient,
   118  		txGossipMetrics,
   119  		config.PullGossipPollSize,
   120  	)
   121  
   122  	// Gossip requests are only served if a node is a validator
   123  	txPullGossiper = gossip.ValidatorGossiper{
   124  		Gossiper:   txPullGossiper,
   125  		NodeID:     nodeID,
   126  		Validators: validators,
   127  	}
   128  
   129  	handler := gossip.NewHandler[*txs.Tx](
   130  		log,
   131  		marshaller,
   132  		gossipMempool,
   133  		txGossipMetrics,
   134  		config.TargetGossipSize,
   135  	)
   136  
   137  	validatorHandler := p2p.NewValidatorHandler(
   138  		p2p.NewThrottlerHandler(
   139  			handler,
   140  			p2p.NewSlidingWindowThrottler(
   141  				config.PullGossipThrottlingPeriod,
   142  				config.PullGossipThrottlingLimit,
   143  			),
   144  			log,
   145  		),
   146  		validators,
   147  		log,
   148  	)
   149  
   150  	// We allow pushing txs between all peers, but only serve gossip requests
   151  	// from validators
   152  	txGossipHandler := txGossipHandler{
   153  		appGossipHandler:  handler,
   154  		appRequestHandler: validatorHandler,
   155  	}
   156  
   157  	if err := p2pNetwork.AddHandler(TxGossipHandlerID, txGossipHandler); err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	return &Network{
   162  		Network:                   p2pNetwork,
   163  		log:                       log,
   164  		txVerifier:                txVerifier,
   165  		mempool:                   gossipMempool,
   166  		partialSyncPrimaryNetwork: partialSyncPrimaryNetwork,
   167  		appSender:                 appSender,
   168  		txPushGossiper:            txPushGossiper,
   169  		txPushGossipFrequency:     config.PushGossipFrequency,
   170  		txPullGossiper:            txPullGossiper,
   171  		txPullGossipFrequency:     config.PullGossipFrequency,
   172  	}, nil
   173  }
   174  
   175  func (n *Network) PushGossip(ctx context.Context) {
   176  	// TODO: Even though the node is running partial sync, we should support
   177  	// issuing transactions from the RPC.
   178  	if n.partialSyncPrimaryNetwork {
   179  		return
   180  	}
   181  
   182  	gossip.Every(ctx, n.log, n.txPushGossiper, n.txPushGossipFrequency)
   183  }
   184  
   185  func (n *Network) PullGossip(ctx context.Context) {
   186  	// If the node is running partial sync, we should not perform any pull
   187  	// gossip.
   188  	if n.partialSyncPrimaryNetwork {
   189  		return
   190  	}
   191  
   192  	gossip.Every(ctx, n.log, n.txPullGossiper, n.txPullGossipFrequency)
   193  }
   194  
   195  func (n *Network) AppGossip(ctx context.Context, nodeID ids.NodeID, msgBytes []byte) error {
   196  	if n.partialSyncPrimaryNetwork {
   197  		n.log.Debug("dropping AppGossip message",
   198  			zap.String("reason", "primary network is not being fully synced"),
   199  		)
   200  		return nil
   201  	}
   202  
   203  	return n.Network.AppGossip(ctx, nodeID, msgBytes)
   204  }
   205  
   206  func (n *Network) IssueTxFromRPC(tx *txs.Tx) error {
   207  	// If we are partially syncing the Primary Network, we should not be
   208  	// maintaining the transaction mempool locally.
   209  	//
   210  	// TODO: We should still push the transaction to some peers when partial
   211  	// syncing.
   212  	if n.partialSyncPrimaryNetwork {
   213  		return errMempoolDisabledWithPartialSync
   214  	}
   215  
   216  	if err := n.mempool.Add(tx); err != nil {
   217  		return err
   218  	}
   219  	n.txPushGossiper.Add(tx)
   220  	return nil
   221  }