github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/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/platformvm/txs"
    19  	"github.com/MetalBlockchain/metalgo/vms/txs/mempool"
    20  
    21  	pmempool "github.com/MetalBlockchain/metalgo/vms/platformvm/txs/mempool"
    22  )
    23  
    24  var (
    25  	_ p2p.Handler                = (*txGossipHandler)(nil)
    26  	_ gossip.Marshaller[*txs.Tx] = (*txMarshaller)(nil)
    27  	_ gossip.Gossipable          = (*txs.Tx)(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 txMarshaller struct{}
    59  
    60  func (txMarshaller) MarshalGossip(tx *txs.Tx) ([]byte, error) {
    61  	return tx.Bytes(), nil
    62  }
    63  
    64  func (txMarshaller) UnmarshalGossip(bytes []byte) (*txs.Tx, error) {
    65  	return txs.Parse(txs.Codec, bytes)
    66  }
    67  
    68  func newGossipMempool(
    69  	mempool pmempool.Mempool,
    70  	registerer prometheus.Registerer,
    71  	log logging.Logger,
    72  	txVerifier TxVerifier,
    73  	minTargetElements int,
    74  	targetFalsePositiveProbability,
    75  	resetFalsePositiveProbability float64,
    76  ) (*gossipMempool, error) {
    77  	bloom, err := gossip.NewBloomFilter(registerer, "mempool_bloom_filter", minTargetElements, targetFalsePositiveProbability, resetFalsePositiveProbability)
    78  	return &gossipMempool{
    79  		Mempool:    mempool,
    80  		log:        log,
    81  		txVerifier: txVerifier,
    82  		bloom:      bloom,
    83  	}, err
    84  }
    85  
    86  type gossipMempool struct {
    87  	pmempool.Mempool
    88  	log        logging.Logger
    89  	txVerifier TxVerifier
    90  
    91  	lock  sync.RWMutex
    92  	bloom *gossip.BloomFilter
    93  }
    94  
    95  func (g *gossipMempool) Add(tx *txs.Tx) error {
    96  	txID := tx.ID()
    97  	if _, ok := g.Mempool.Get(txID); ok {
    98  		return fmt.Errorf("tx %s dropped: %w", txID, mempool.ErrDuplicateTx)
    99  	}
   100  
   101  	if reason := g.Mempool.GetDropReason(txID); reason != nil {
   102  		// If the tx is being dropped - just ignore it
   103  		//
   104  		// TODO: Should we allow re-verification of the transaction even if it
   105  		// failed previously?
   106  		return reason
   107  	}
   108  
   109  	if err := g.txVerifier.VerifyTx(tx); err != nil {
   110  		g.Mempool.MarkDropped(txID, err)
   111  		return err
   112  	}
   113  
   114  	if err := g.Mempool.Add(tx); err != nil {
   115  		g.Mempool.MarkDropped(txID, err)
   116  		return err
   117  	}
   118  
   119  	g.lock.Lock()
   120  	defer g.lock.Unlock()
   121  
   122  	g.bloom.Add(tx)
   123  	reset, err := gossip.ResetBloomFilterIfNeeded(g.bloom, g.Mempool.Len()*bloomChurnMultiplier)
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	if reset {
   129  		g.log.Debug("resetting bloom filter")
   130  		g.Mempool.Iterate(func(tx *txs.Tx) bool {
   131  			g.bloom.Add(tx)
   132  			return true
   133  		})
   134  	}
   135  
   136  	g.Mempool.RequestBuildBlock(false)
   137  	return nil
   138  }
   139  
   140  func (g *gossipMempool) Has(txID ids.ID) bool {
   141  	_, ok := g.Mempool.Get(txID)
   142  	return ok
   143  }
   144  
   145  func (g *gossipMempool) GetFilter() (bloom []byte, salt []byte) {
   146  	g.lock.RLock()
   147  	defer g.lock.RUnlock()
   148  
   149  	return g.bloom.Marshal()
   150  }