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 }