code.vegaprotocol.io/vega@v0.79.0/core/processor/gastimator.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package processor 17 18 import ( 19 "context" 20 "math" 21 22 "code.vegaprotocol.io/vega/core/blockchain/abci" 23 "code.vegaprotocol.io/vega/core/txn" 24 "code.vegaprotocol.io/vega/core/types" 25 "code.vegaprotocol.io/vega/libs/num" 26 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 27 ) 28 29 const ( 30 batchFactor = 0.5 31 pegCostFactor = uint64(50) 32 stopCostFactor = 0.2 33 positionFactor = uint64(1) 34 levelFactor = 0.1 35 high = 10000 36 medium = 100 37 low = 1 38 ) 39 40 type ExecEngine interface { 41 GetMarketCounters() map[string]*types.MarketCounters 42 } 43 44 type Gastimator struct { 45 minBlockCapacity uint64 46 maxGas uint64 47 defaultGas uint64 48 exec ExecEngine 49 marketCounters map[string]*types.MarketCounters 50 } 51 52 func NewGastimator(exec ExecEngine) *Gastimator { 53 return &Gastimator{ 54 exec: exec, 55 marketCounters: map[string]*types.MarketCounters{}, 56 } 57 } 58 59 // OnBlockEnd is called at the end of the block to update the per market counters and return the max gas as defined by the network parameter. 60 func (g *Gastimator) OnBlockEnd() uint64 { 61 g.marketCounters = g.exec.GetMarketCounters() 62 return g.maxGas 63 } 64 65 // OnMaxGasUpdate updates the max gas from the network parameter. 66 func (g *Gastimator) OnMinBlockCapacityUpdate(ctx context.Context, minBlockCapacity *num.Uint) error { 67 g.minBlockCapacity = minBlockCapacity.Uint64() 68 return nil 69 } 70 71 // OnMaxGasUpdate updates the max gas from the network parameter. 72 func (g *Gastimator) OnMaxGasUpdate(ctx context.Context, max *num.Uint) error { 73 g.maxGas = max.Uint64() 74 return nil 75 } 76 77 // OnDefaultGasUpdate updates the default gas wanted per transaction. 78 func (g *Gastimator) OnDefaultGasUpdate(ctx context.Context, def *num.Uint) error { 79 g.defaultGas = def.Uint64() 80 return nil 81 } 82 83 // GetMaxGas returns the current value of max gas. 84 func (g *Gastimator) GetMaxGas() uint64 { 85 return g.maxGas 86 } 87 88 func (g *Gastimator) GetPriority(tx abci.Tx) uint64 { 89 switch tx.Command() { 90 case txn.ProposeCommand, txn.BatchProposeCommand, txn.VoteCommand: 91 return medium 92 default: 93 if tx.Command().IsValidatorCommand() { 94 return high 95 } 96 return low 97 } 98 } 99 100 func (g *Gastimator) CalcGasWantedForTx(tx abci.Tx) (uint64, error) { 101 switch tx.Command() { 102 case txn.DelayedTransactionsWrapper: 103 return 0, nil 104 case txn.SubmitOrderCommand: 105 s := &commandspb.OrderSubmission{} 106 if err := tx.Unmarshal(s); err != nil { 107 return g.maxGas + 1, err 108 } 109 return g.orderGastimate(s.MarketId), nil 110 case txn.AmendOrderCommand: 111 s := &commandspb.OrderAmendment{} 112 if err := tx.Unmarshal(s); err != nil { 113 return g.maxGas + 1, err 114 } 115 return g.orderGastimate(s.MarketId), nil 116 case txn.CancelOrderCommand: 117 s := &commandspb.OrderCancellation{} 118 if err := tx.Unmarshal(s); err != nil { 119 return g.maxGas + 1, err 120 } 121 // if it is a cancel for one market 122 if len(s.MarketId) > 0 && len(s.OrderId) > 0 { 123 return g.cancelOrderGastimate(s.MarketId), nil 124 } 125 // if it is a cancel for all markets 126 return g.defaultGas, nil 127 case txn.BatchMarketInstructions: 128 s := &commandspb.BatchMarketInstructions{} 129 if err := tx.Unmarshal(s); err != nil { 130 return g.maxGas + 1, err 131 } 132 return g.batchGastimate(s), nil 133 case txn.StopOrdersSubmissionCommand: 134 s := &commandspb.StopOrdersSubmission{} 135 if err := tx.Unmarshal(s); err != nil { 136 return g.maxGas + 1, err 137 } 138 var marketId string 139 if s.FallsBelow != nil { 140 marketId = s.FallsBelow.OrderSubmission.MarketId 141 } else { 142 marketId = s.RisesAbove.OrderSubmission.MarketId 143 } 144 145 return g.orderGastimate(marketId), nil 146 case txn.StopOrdersCancellationCommand: 147 s := &commandspb.StopOrdersCancellation{} 148 if err := tx.Unmarshal(s); err != nil { 149 return g.maxGas + 1, err 150 } 151 // if it is a cancel for one market 152 if s.MarketId != nil && s.StopOrderId != nil { 153 return g.cancelOrderGastimate(*s.MarketId), nil 154 } 155 // if it is a cancel for all markets 156 return g.defaultGas, nil 157 158 default: 159 return g.defaultGas, nil 160 } 161 } 162 163 // gasBatch = 164 // the full cost of the first cancellation (i.e. gasCancel) 165 // plus batchFactor times sum of all subsequent cancellations added together (each costing gasOrder) 166 // plus the full cost of the first amendment at gasOrder 167 // plus batchFactor sum of all subsequent amendments added together (each costing gasOrder) 168 // plus the full cost of the first limit order at gasOrder 169 // plus batchFactor sum of all subsequent limit orders added together (each costing gasOrder) 170 // gasBatch = min(maxGas-1,batchFactor). 171 func (g *Gastimator) batchGastimate(batch *commandspb.BatchMarketInstructions) uint64 { 172 totalBatchGas := 0.0 173 for i, os := range batch.Submissions { 174 factor := batchFactor 175 if i == 0 { 176 factor = 1.0 177 } 178 orderGas := g.orderGastimate(os.MarketId) 179 totalBatchGas += factor * float64(orderGas) 180 } 181 for i, os := range batch.Amendments { 182 factor := batchFactor 183 if i == 0 { 184 factor = 1.0 185 } 186 orderGas := g.orderGastimate(os.MarketId) 187 totalBatchGas += factor * float64(orderGas) 188 } 189 for i, os := range batch.Cancellations { 190 factor := batchFactor 191 if i == 0 { 192 factor = 1.0 193 } 194 orderGas := g.cancelOrderGastimate(os.MarketId) 195 totalBatchGas += factor * float64(orderGas) 196 } 197 for i, os := range batch.StopOrdersCancellation { 198 factor := batchFactor 199 if i == 0 { 200 factor = 1.0 201 } 202 if os.MarketId == nil { 203 totalBatchGas += factor * float64(g.defaultGas) 204 } 205 orderGas := g.cancelOrderGastimate(*os.MarketId) 206 totalBatchGas += factor * float64(orderGas) 207 } 208 for i, os := range batch.StopOrdersSubmission { 209 factor := batchFactor 210 if i == 0 { 211 factor = 1.0 212 } 213 var marketId string 214 // if both are nil, marketId will be empty string, yielding default gas 215 // the order is invalid, but validation is applied later. 216 if os.FallsBelow != nil && os.FallsBelow.OrderSubmission != nil { 217 marketId = os.FallsBelow.OrderSubmission.MarketId 218 } else if os.RisesAbove != nil && os.RisesAbove.OrderSubmission != nil { 219 marketId = os.RisesAbove.OrderSubmission.MarketId 220 } 221 orderGas := g.orderGastimate(marketId) 222 totalBatchGas += factor * float64(orderGas) 223 } 224 return uint64(math.Min(float64(uint64(totalBatchGas)), float64(g.maxGas-1))) 225 } 226 227 // gasOrder = network.transaction.defaultgas + peg cost factor x pegs 228 // + position factor x positions 229 // + level factor x levels 230 // gasOrder = min(maxGas-1,gasOrder). 231 func (g *Gastimator) orderGastimate(marketID string) uint64 { 232 if marketCounters, ok := g.marketCounters[marketID]; ok { 233 return uint64(math.Min(float64( 234 g.defaultGas+ 235 uint64(stopCostFactor*float64(marketCounters.StopOrderCounter))+ 236 pegCostFactor*marketCounters.PeggedOrderCounter+ 237 positionFactor*marketCounters.PositionCount+ 238 uint64(levelFactor*float64(marketCounters.OrderbookLevelCount))), 239 math.Max(1.0, float64(g.maxGas/g.minBlockCapacity-1)))) 240 } 241 return g.defaultGas 242 } 243 244 // gasCancel = network.transaction.defaultgas + peg cost factor x pegs 245 // + level factor x levels 246 // gasCancel = min(maxGas-1,gasCancel). 247 func (g *Gastimator) cancelOrderGastimate(marketID string) uint64 { 248 if marketCounters, ok := g.marketCounters[marketID]; ok { 249 return uint64(math.Min(float64( 250 g.defaultGas+ 251 uint64(stopCostFactor*float64(marketCounters.StopOrderCounter))+ 252 pegCostFactor*marketCounters.PeggedOrderCounter+ 253 uint64(0.1*float64(marketCounters.OrderbookLevelCount))), 254 math.Max(1.0, float64(g.maxGas/g.minBlockCapacity-1)))) 255 } 256 return g.defaultGas 257 }