github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/network/network.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 "errors" 9 "time" 10 11 "github.com/prometheus/client_golang/prometheus" 12 "go.uber.org/zap" 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/snow/engine/common" 18 "github.com/MetalBlockchain/metalgo/snow/validators" 19 "github.com/MetalBlockchain/metalgo/utils/logging" 20 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs" 21 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs/mempool" 22 ) 23 24 const TxGossipHandlerID = 0 25 26 var errMempoolDisabledWithPartialSync = errors.New("mempool is disabled partial syncing") 27 28 type Network struct { 29 *p2p.Network 30 31 log logging.Logger 32 txVerifier TxVerifier 33 mempool *gossipMempool 34 partialSyncPrimaryNetwork bool 35 appSender common.AppSender 36 37 txPushGossiper *gossip.PushGossiper[*txs.Tx] 38 txPushGossipFrequency time.Duration 39 txPullGossiper gossip.Gossiper 40 txPullGossipFrequency time.Duration 41 } 42 43 func New( 44 log logging.Logger, 45 nodeID ids.NodeID, 46 subnetID ids.ID, 47 vdrs validators.State, 48 txVerifier TxVerifier, 49 mempool mempool.Mempool, 50 partialSyncPrimaryNetwork bool, 51 appSender common.AppSender, 52 registerer prometheus.Registerer, 53 config Config, 54 ) (*Network, error) { 55 p2pNetwork, err := p2p.NewNetwork(log, appSender, registerer, "p2p") 56 if err != nil { 57 return nil, err 58 } 59 60 marshaller := txMarshaller{} 61 validators := p2p.NewValidators( 62 p2pNetwork.Peers, 63 log, 64 subnetID, 65 vdrs, 66 config.MaxValidatorSetStaleness, 67 ) 68 txGossipClient := p2pNetwork.NewClient( 69 TxGossipHandlerID, 70 p2p.WithValidatorSampling(validators), 71 ) 72 txGossipMetrics, err := gossip.NewMetrics(registerer, "tx") 73 if err != nil { 74 return nil, err 75 } 76 77 gossipMempool, err := newGossipMempool( 78 mempool, 79 registerer, 80 log, 81 txVerifier, 82 config.ExpectedBloomFilterElements, 83 config.ExpectedBloomFilterFalsePositiveProbability, 84 config.MaxBloomFilterFalsePositiveProbability, 85 ) 86 if err != nil { 87 return nil, err 88 } 89 90 txPushGossiper, err := gossip.NewPushGossiper[*txs.Tx]( 91 marshaller, 92 gossipMempool, 93 validators, 94 txGossipClient, 95 txGossipMetrics, 96 gossip.BranchingFactor{ 97 StakePercentage: config.PushGossipPercentStake, 98 Validators: config.PushGossipNumValidators, 99 Peers: config.PushGossipNumPeers, 100 }, 101 gossip.BranchingFactor{ 102 Validators: config.PushRegossipNumValidators, 103 Peers: config.PushRegossipNumPeers, 104 }, 105 config.PushGossipDiscardedCacheSize, 106 config.TargetGossipSize, 107 config.PushGossipMaxRegossipFrequency, 108 ) 109 if err != nil { 110 return nil, err 111 } 112 113 var txPullGossiper gossip.Gossiper = gossip.NewPullGossiper[*txs.Tx]( 114 log, 115 marshaller, 116 gossipMempool, 117 txGossipClient, 118 txGossipMetrics, 119 config.PullGossipPollSize, 120 ) 121 122 // Gossip requests are only served if a node is a validator 123 txPullGossiper = gossip.ValidatorGossiper{ 124 Gossiper: txPullGossiper, 125 NodeID: nodeID, 126 Validators: validators, 127 } 128 129 handler := gossip.NewHandler[*txs.Tx]( 130 log, 131 marshaller, 132 gossipMempool, 133 txGossipMetrics, 134 config.TargetGossipSize, 135 ) 136 137 validatorHandler := p2p.NewValidatorHandler( 138 p2p.NewThrottlerHandler( 139 handler, 140 p2p.NewSlidingWindowThrottler( 141 config.PullGossipThrottlingPeriod, 142 config.PullGossipThrottlingLimit, 143 ), 144 log, 145 ), 146 validators, 147 log, 148 ) 149 150 // We allow pushing txs between all peers, but only serve gossip requests 151 // from validators 152 txGossipHandler := txGossipHandler{ 153 appGossipHandler: handler, 154 appRequestHandler: validatorHandler, 155 } 156 157 if err := p2pNetwork.AddHandler(TxGossipHandlerID, txGossipHandler); err != nil { 158 return nil, err 159 } 160 161 return &Network{ 162 Network: p2pNetwork, 163 log: log, 164 txVerifier: txVerifier, 165 mempool: gossipMempool, 166 partialSyncPrimaryNetwork: partialSyncPrimaryNetwork, 167 appSender: appSender, 168 txPushGossiper: txPushGossiper, 169 txPushGossipFrequency: config.PushGossipFrequency, 170 txPullGossiper: txPullGossiper, 171 txPullGossipFrequency: config.PullGossipFrequency, 172 }, nil 173 } 174 175 func (n *Network) PushGossip(ctx context.Context) { 176 // TODO: Even though the node is running partial sync, we should support 177 // issuing transactions from the RPC. 178 if n.partialSyncPrimaryNetwork { 179 return 180 } 181 182 gossip.Every(ctx, n.log, n.txPushGossiper, n.txPushGossipFrequency) 183 } 184 185 func (n *Network) PullGossip(ctx context.Context) { 186 // If the node is running partial sync, we should not perform any pull 187 // gossip. 188 if n.partialSyncPrimaryNetwork { 189 return 190 } 191 192 gossip.Every(ctx, n.log, n.txPullGossiper, n.txPullGossipFrequency) 193 } 194 195 func (n *Network) AppGossip(ctx context.Context, nodeID ids.NodeID, msgBytes []byte) error { 196 if n.partialSyncPrimaryNetwork { 197 n.log.Debug("dropping AppGossip message", 198 zap.String("reason", "primary network is not being fully synced"), 199 ) 200 return nil 201 } 202 203 return n.Network.AppGossip(ctx, nodeID, msgBytes) 204 } 205 206 func (n *Network) IssueTxFromRPC(tx *txs.Tx) error { 207 // If we are partially syncing the Primary Network, we should not be 208 // maintaining the transaction mempool locally. 209 // 210 // TODO: We should still push the transaction to some peers when partial 211 // syncing. 212 if n.partialSyncPrimaryNetwork { 213 return errMempoolDisabledWithPartialSync 214 } 215 216 if err := n.mempool.Add(tx); err != nil { 217 return err 218 } 219 n.txPushGossiper.Add(tx) 220 return nil 221 }