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 }