github.com/ethereum-optimism/optimism/l2geth@v0.0.0-20230612200230-50b04ade19e3/rollup/fees/rollup_fee.go (about) 1 package fees 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "math" 9 "math/big" 10 11 "github.com/ethereum-optimism/optimism/l2geth/common" 12 "github.com/ethereum-optimism/optimism/l2geth/core/types" 13 "github.com/ethereum-optimism/optimism/l2geth/params" 14 "github.com/ethereum-optimism/optimism/l2geth/rollup/rcfg" 15 ) 16 17 var ( 18 // ErrGasPriceTooLow represents the error case of then the user pays too little 19 ErrGasPriceTooLow = errors.New("gas price too low") 20 // ErrGasPriceTooHigh represents the error case of when the user pays too much 21 ErrGasPriceTooHigh = errors.New("gas price too high") 22 // ErrInsufficientFunds represents the error case of when the user doesn't 23 // have enough funds to cover the transaction 24 ErrInsufficientFunds = errors.New("insufficient funds for l1Fee + l2Fee + value") 25 // errMissingInput represents the error case of missing required input to 26 // PaysEnough 27 errMissingInput = errors.New("missing input") 28 // ErrL2GasLimitTooLow represents the error case of when a user sends a 29 // transaction to the sequencer with a L2 gas limit that is too small 30 ErrL2GasLimitTooLow = errors.New("L2 gas limit too low") 31 // errTransactionSigned represents the error case of passing in a signed 32 // transaction to the L1 fee calculation routine. The signature is accounted 33 // for externally 34 errTransactionSigned = errors.New("transaction is signed") 35 // big10 is used for decimal scaling 36 big10 = new(big.Int).SetUint64(10) 37 ) 38 39 // Message represents the interface of a message. 40 // It should be a subset of the methods found on 41 // types.Message 42 type Message interface { 43 From() common.Address 44 To() *common.Address 45 GasPrice() *big.Int 46 Gas() uint64 47 Value() *big.Int 48 Nonce() uint64 49 Data() []byte 50 } 51 52 // StateDB represents the StateDB interface 53 // required to compute the L1 fee 54 type StateDB interface { 55 GetState(common.Address, common.Hash) common.Hash 56 } 57 58 // RollupOracle represents the interface of the in 59 // memory cache of the gas price oracle 60 type RollupOracle interface { 61 SuggestL1GasPrice(ctx context.Context) (*big.Int, error) 62 SuggestL2GasPrice(ctx context.Context) (*big.Int, error) 63 SuggestOverhead(ctx context.Context) (*big.Int, error) 64 SuggestScalar(ctx context.Context) (*big.Float, error) 65 } 66 67 // CalculateTotalFee will calculate the total fee given a transaction. 68 // This function is used at the RPC layer to ensure that users 69 // have enough ETH to cover their fee 70 func CalculateTotalFee(tx *types.Transaction, gpo RollupOracle) (*big.Int, error) { 71 // Read the variables from the cache 72 l1GasPrice, err := gpo.SuggestL1GasPrice(context.Background()) 73 if err != nil { 74 return nil, err 75 } 76 overhead, err := gpo.SuggestOverhead(context.Background()) 77 if err != nil { 78 return nil, err 79 } 80 scalar, err := gpo.SuggestScalar(context.Background()) 81 if err != nil { 82 return nil, err 83 } 84 85 unsigned := copyTransaction(tx) 86 raw, err := rlpEncode(unsigned) 87 if err != nil { 88 return nil, err 89 } 90 91 l1Fee := CalculateL1Fee(raw, overhead, l1GasPrice, scalar) 92 l2GasLimit := new(big.Int).SetUint64(tx.Gas()) 93 l2Fee := new(big.Int).Mul(tx.GasPrice(), l2GasLimit) 94 fee := new(big.Int).Add(l1Fee, l2Fee) 95 return fee, nil 96 } 97 98 // CalculateMsgFee will calculate the total fee given a Message. 99 // This function is used during the state transition to transfer 100 // value to the sequencer. Since Messages do not have a signature 101 // and the signature is submitted to L1 in a batch, extra bytes 102 // are padded to the raw transaction 103 func CalculateTotalMsgFee(msg Message, state StateDB, gasUsed *big.Int, gpo *common.Address) (*big.Int, error) { 104 if gpo == nil { 105 gpo = &rcfg.L2GasPriceOracleAddress 106 } 107 108 l1Fee, err := CalculateL1MsgFee(msg, state, gpo) 109 if err != nil { 110 return nil, err 111 } 112 // Multiply the gas price and the gas used to get the L2 fee 113 l2Fee := new(big.Int).Mul(msg.GasPrice(), gasUsed) 114 // Add the L1 cost and the L2 cost to get the total fee being paid 115 fee := new(big.Int).Add(l1Fee, l2Fee) 116 return fee, nil 117 } 118 119 // CalculateL1MsgFee computes the L1 portion of the fee given 120 // a Message and a StateDB 121 func CalculateL1MsgFee(msg Message, state StateDB, gpo *common.Address) (*big.Int, error) { 122 tx := asTransaction(msg) 123 raw, err := rlpEncode(tx) 124 if err != nil { 125 return nil, err 126 } 127 128 if gpo == nil { 129 gpo = &rcfg.L2GasPriceOracleAddress 130 } 131 132 l1GasPrice, overhead, scalar := readGPOStorageSlots(*gpo, state) 133 l1Fee := CalculateL1Fee(raw, overhead, l1GasPrice, scalar) 134 return l1Fee, nil 135 } 136 137 // CalculateL1Fee computes the L1 fee 138 func CalculateL1Fee(data []byte, overhead, l1GasPrice *big.Int, scalar *big.Float) *big.Int { 139 l1GasUsed := CalculateL1GasUsed(data, overhead) 140 l1Fee := new(big.Int).Mul(l1GasUsed, l1GasPrice) 141 return mulByFloat(l1Fee, scalar) 142 } 143 144 // CalculateL1GasUsed computes the L1 gas used based on the calldata and 145 // constant sized overhead. The overhead can be decreased as the cost of the 146 // batch submission goes down via contract optimizations. This will not overflow 147 // under standard network conditions. 148 func CalculateL1GasUsed(data []byte, overhead *big.Int) *big.Int { 149 zeroes, ones := zeroesAndOnes(data) 150 zeroesGas := zeroes * params.TxDataZeroGas 151 onesGas := (ones + 68) * params.TxDataNonZeroGasEIP2028 152 l1Gas := new(big.Int).SetUint64(zeroesGas + onesGas) 153 return new(big.Int).Add(l1Gas, overhead) 154 } 155 156 // DeriveL1GasInfo reads L1 gas related information to be included 157 // on the receipt 158 func DeriveL1GasInfo(msg Message, state StateDB) (*big.Int, *big.Int, *big.Int, *big.Float, error) { 159 tx := asTransaction(msg) 160 raw, err := rlpEncode(tx) 161 if err != nil { 162 return nil, nil, nil, nil, err 163 } 164 165 l1GasPrice, overhead, scalar := readGPOStorageSlots(rcfg.L2GasPriceOracleAddress, state) 166 l1GasUsed := CalculateL1GasUsed(raw, overhead) 167 l1Fee := CalculateL1Fee(raw, overhead, l1GasPrice, scalar) 168 return l1Fee, l1GasPrice, l1GasUsed, scalar, nil 169 } 170 171 func readGPOStorageSlots(addr common.Address, state StateDB) (*big.Int, *big.Int, *big.Float) { 172 l1GasPrice := state.GetState(addr, rcfg.L1GasPriceSlot) 173 overhead := state.GetState(addr, rcfg.OverheadSlot) 174 scalar := state.GetState(addr, rcfg.ScalarSlot) 175 decimals := state.GetState(addr, rcfg.DecimalsSlot) 176 scaled := ScaleDecimals(scalar.Big(), decimals.Big()) 177 return l1GasPrice.Big(), overhead.Big(), scaled 178 } 179 180 // ScaleDecimals will scale a value by decimals 181 func ScaleDecimals(scalar, decimals *big.Int) *big.Float { 182 // 10**decimals 183 divisor := new(big.Int).Exp(big10, decimals, nil) 184 fscalar := new(big.Float).SetInt(scalar) 185 fdivisor := new(big.Float).SetInt(divisor) 186 // fscalar / fdivisor 187 return new(big.Float).Quo(fscalar, fdivisor) 188 } 189 190 // rlpEncode RLP encodes the transaction into bytes 191 // When a signature is not included, set pad to true to 192 // fill in a dummy signature full on non 0 bytes 193 func rlpEncode(tx *types.Transaction) ([]byte, error) { 194 raw := new(bytes.Buffer) 195 if err := tx.EncodeRLP(raw); err != nil { 196 return nil, err 197 } 198 199 r, v, s := tx.RawSignatureValues() 200 if r.Cmp(common.Big0) != 0 || v.Cmp(common.Big0) != 0 || s.Cmp(common.Big0) != 0 { 201 return nil, errTransactionSigned 202 } 203 204 // Slice off the 0 bytes representing the signature 205 b := raw.Bytes() 206 return b[:len(b)-3], nil 207 } 208 209 // asTransaction turns a Message into a types.Transaction 210 func asTransaction(msg Message) *types.Transaction { 211 if msg.To() == nil { 212 return types.NewContractCreation( 213 msg.Nonce(), 214 msg.Value(), 215 msg.Gas(), 216 msg.GasPrice(), 217 msg.Data(), 218 ) 219 } 220 return types.NewTransaction( 221 msg.Nonce(), 222 *msg.To(), 223 msg.Value(), 224 msg.Gas(), 225 msg.GasPrice(), 226 msg.Data(), 227 ) 228 } 229 230 // copyTransaction copies the transaction, removing the signature 231 func copyTransaction(tx *types.Transaction) *types.Transaction { 232 if tx.To() == nil { 233 return types.NewContractCreation( 234 tx.Nonce(), 235 tx.Value(), 236 tx.Gas(), 237 tx.GasPrice(), 238 tx.Data(), 239 ) 240 } 241 return types.NewTransaction( 242 tx.Nonce(), 243 *tx.To(), 244 tx.Value(), 245 tx.Gas(), 246 tx.GasPrice(), 247 tx.Data(), 248 ) 249 } 250 251 // PaysEnoughOpts represent the options to PaysEnough 252 type PaysEnoughOpts struct { 253 UserGasPrice, ExpectedGasPrice *big.Int 254 ThresholdUp, ThresholdDown *big.Float 255 } 256 257 // PaysEnough returns an error if the fee is not large enough 258 // `GasPrice` and `Fee` are required arguments. 259 func PaysEnough(opts *PaysEnoughOpts) error { 260 if opts.UserGasPrice == nil { 261 return fmt.Errorf("%w: no user fee", errMissingInput) 262 } 263 if opts.ExpectedGasPrice == nil { 264 return fmt.Errorf("%w: no expected fee", errMissingInput) 265 } 266 267 fee := new(big.Int).Set(opts.ExpectedGasPrice) 268 // Allow for a downward buffer to protect against L1 gas price volatility 269 if opts.ThresholdDown != nil { 270 fee = mulByFloat(fee, opts.ThresholdDown) 271 } 272 // Protect the sequencer from being underpaid 273 // if user fee < expected fee, return error 274 if opts.UserGasPrice.Cmp(fee) == -1 { 275 return ErrGasPriceTooLow 276 } 277 // Protect users from overpaying by too much 278 if opts.ThresholdUp != nil { 279 // overpaying = user fee - expected fee 280 overpaying := new(big.Int).Sub(opts.UserGasPrice, opts.ExpectedGasPrice) 281 threshold := mulByFloat(opts.ExpectedGasPrice, opts.ThresholdUp) 282 // if overpaying > threshold, return error 283 if overpaying.Cmp(threshold) == 1 { 284 return ErrGasPriceTooHigh 285 } 286 } 287 return nil 288 } 289 290 // zeroesAndOnes counts the number of 0 bytes and non 0 bytes in a byte slice 291 func zeroesAndOnes(data []byte) (uint64, uint64) { 292 var zeroes uint64 293 var ones uint64 294 for _, byt := range data { 295 if byt == 0 { 296 zeroes++ 297 } else { 298 ones++ 299 } 300 } 301 return zeroes, ones 302 } 303 304 // mulByFloat multiplies a big.Int by a float and returns the 305 // big.Int rounded upwards 306 func mulByFloat(num *big.Int, float *big.Float) *big.Int { 307 n := new(big.Float).SetUint64(num.Uint64()) 308 product := n.Mul(n, float) 309 pfloat, _ := product.Float64() 310 rounded := math.Ceil(pfloat) 311 return new(big.Int).SetUint64(uint64(rounded)) 312 }