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