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 }