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