github.com/ava-labs/avalanchego@v1.11.11/vms/avm/network/gossip.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  	"fmt"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/prometheus/client_golang/prometheus"
    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/utils/logging"
    19  	"github.com/ava-labs/avalanchego/vms/avm/txs"
    20  	"github.com/ava-labs/avalanchego/vms/txs/mempool"
    21  
    22  	xmempool "github.com/ava-labs/avalanchego/vms/avm/txs/mempool"
    23  )
    24  
    25  var (
    26  	_ p2p.Handler                = (*txGossipHandler)(nil)
    27  	_ gossip.Set[*txs.Tx]        = (*gossipMempool)(nil)
    28  	_ gossip.Marshaller[*txs.Tx] = (*txParser)(nil)
    29  )
    30  
    31  // bloomChurnMultiplier is the number used to multiply the size of the mempool
    32  // to determine how large of a bloom filter to create.
    33  const bloomChurnMultiplier = 3
    34  
    35  // txGossipHandler is the handler called when serving gossip messages
    36  type txGossipHandler struct {
    37  	p2p.NoOpHandler
    38  	appGossipHandler  p2p.Handler
    39  	appRequestHandler p2p.Handler
    40  }
    41  
    42  func (t txGossipHandler) AppGossip(
    43  	ctx context.Context,
    44  	nodeID ids.NodeID,
    45  	gossipBytes []byte,
    46  ) {
    47  	t.appGossipHandler.AppGossip(ctx, nodeID, gossipBytes)
    48  }
    49  
    50  func (t txGossipHandler) AppRequest(
    51  	ctx context.Context,
    52  	nodeID ids.NodeID,
    53  	deadline time.Time,
    54  	requestBytes []byte,
    55  ) ([]byte, *common.AppError) {
    56  	return t.appRequestHandler.AppRequest(ctx, nodeID, deadline, requestBytes)
    57  }
    58  
    59  type txParser struct {
    60  	parser txs.Parser
    61  }
    62  
    63  func (*txParser) MarshalGossip(tx *txs.Tx) ([]byte, error) {
    64  	return tx.Bytes(), nil
    65  }
    66  
    67  func (g *txParser) UnmarshalGossip(bytes []byte) (*txs.Tx, error) {
    68  	return g.parser.ParseTx(bytes)
    69  }
    70  
    71  func newGossipMempool(
    72  	mempool xmempool.Mempool,
    73  	registerer prometheus.Registerer,
    74  	log logging.Logger,
    75  	txVerifier TxVerifier,
    76  	parser txs.Parser,
    77  	minTargetElements int,
    78  	targetFalsePositiveProbability,
    79  	resetFalsePositiveProbability float64,
    80  ) (*gossipMempool, error) {
    81  	bloom, err := gossip.NewBloomFilter(registerer, "mempool_bloom_filter", minTargetElements, targetFalsePositiveProbability, resetFalsePositiveProbability)
    82  	return &gossipMempool{
    83  		Mempool:    mempool,
    84  		log:        log,
    85  		txVerifier: txVerifier,
    86  		parser:     parser,
    87  		bloom:      bloom,
    88  	}, err
    89  }
    90  
    91  type gossipMempool struct {
    92  	xmempool.Mempool
    93  	log        logging.Logger
    94  	txVerifier TxVerifier
    95  	parser     txs.Parser
    96  
    97  	lock  sync.RWMutex
    98  	bloom *gossip.BloomFilter
    99  }
   100  
   101  // Add is called by the p2p SDK when handling transactions that were pushed to
   102  // us and when handling transactions that were pulled from a peer. If this
   103  // returns a nil error while handling push gossip, the p2p SDK will queue the
   104  // transaction to push gossip as well.
   105  func (g *gossipMempool) Add(tx *txs.Tx) error {
   106  	txID := tx.ID()
   107  	if _, ok := g.Mempool.Get(txID); ok {
   108  		return fmt.Errorf("attempted to issue %w: %s ", mempool.ErrDuplicateTx, txID)
   109  	}
   110  
   111  	if reason := g.Mempool.GetDropReason(txID); reason != nil {
   112  		// If the tx is being dropped - just ignore it
   113  		//
   114  		// TODO: Should we allow re-verification of the transaction even if it
   115  		// failed previously?
   116  		return reason
   117  	}
   118  
   119  	// Verify the tx at the currently preferred state
   120  	if err := g.txVerifier.VerifyTx(tx); err != nil {
   121  		g.Mempool.MarkDropped(txID, err)
   122  		return err
   123  	}
   124  
   125  	return g.AddWithoutVerification(tx)
   126  }
   127  
   128  func (g *gossipMempool) Has(txID ids.ID) bool {
   129  	_, ok := g.Mempool.Get(txID)
   130  	return ok
   131  }
   132  
   133  func (g *gossipMempool) AddWithoutVerification(tx *txs.Tx) error {
   134  	if err := g.Mempool.Add(tx); err != nil {
   135  		g.Mempool.MarkDropped(tx.ID(), err)
   136  		return err
   137  	}
   138  
   139  	g.lock.Lock()
   140  	defer g.lock.Unlock()
   141  
   142  	g.bloom.Add(tx)
   143  	reset, err := gossip.ResetBloomFilterIfNeeded(g.bloom, g.Mempool.Len()*bloomChurnMultiplier)
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	if reset {
   149  		g.log.Debug("resetting bloom filter")
   150  		g.Mempool.Iterate(func(tx *txs.Tx) bool {
   151  			g.bloom.Add(tx)
   152  			return true
   153  		})
   154  	}
   155  
   156  	g.Mempool.RequestBuildBlock()
   157  	return nil
   158  }
   159  
   160  func (g *gossipMempool) Iterate(f func(*txs.Tx) bool) {
   161  	g.Mempool.Iterate(f)
   162  }
   163  
   164  func (g *gossipMempool) GetFilter() (bloom []byte, salt []byte) {
   165  	g.lock.RLock()
   166  	defer g.lock.RUnlock()
   167  
   168  	return g.bloom.Marshal()
   169  }