github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/rollup/fees/rollup_fee.go (about) 1 package fees 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "math/big" 8 9 "github.com/scroll-tech/go-ethereum/common" 10 "github.com/scroll-tech/go-ethereum/core/types" 11 "github.com/scroll-tech/go-ethereum/crypto" 12 "github.com/scroll-tech/go-ethereum/params" 13 "github.com/scroll-tech/go-ethereum/rollup/rcfg" 14 ) 15 16 var ( 17 // txExtraDataBytes is the number of bytes that we commit to L1 in addition 18 // to the RLP-encoded signed transaction. Note that these are all assumed 19 // to be non-zero. 20 // - tx length prefix: 4 bytes 21 txExtraDataBytes = uint64(4) 22 ) 23 24 // Message represents the interface of a message. 25 // It should be a subset of the methods found on 26 // types.Message 27 type Message interface { 28 From() common.Address 29 To() *common.Address 30 GasPrice() *big.Int 31 Gas() uint64 32 GasFeeCap() *big.Int 33 GasTipCap() *big.Int 34 Value() *big.Int 35 Nonce() uint64 36 Data() []byte 37 AccessList() types.AccessList 38 IsL1MessageTx() bool 39 } 40 41 // StateDB represents the StateDB interface 42 // required to compute the L1 fee 43 type StateDB interface { 44 GetState(common.Address, common.Hash) common.Hash 45 GetBalance(addr common.Address) *big.Int 46 } 47 48 func EstimateL1DataFeeForMessage(msg Message, baseFee, chainID *big.Int, signer types.Signer, state StateDB) (*big.Int, error) { 49 if msg.IsL1MessageTx() { 50 return big.NewInt(0), nil 51 } 52 53 unsigned := asUnsignedTx(msg, baseFee, chainID) 54 // with v=1 55 tx, err := unsigned.WithSignature(signer, append(bytes.Repeat([]byte{0xff}, crypto.SignatureLength-1), 0x01)) 56 if err != nil { 57 return nil, err 58 } 59 60 raw, err := rlpEncode(tx) 61 if err != nil { 62 return nil, err 63 } 64 65 l1BaseFee, overhead, scalar := readGPOStorageSlots(rcfg.L1GasPriceOracleAddress, state) 66 l1DataFee := calculateEncodedL1DataFee(raw, overhead, l1BaseFee, scalar) 67 return l1DataFee, nil 68 } 69 70 // asUnsignedTx turns a Message into a types.Transaction 71 func asUnsignedTx(msg Message, baseFee, chainID *big.Int) *types.Transaction { 72 if baseFee == nil { 73 if msg.AccessList() == nil { 74 return asUnsignedLegacyTx(msg) 75 } 76 77 return asUnsignedAccessListTx(msg, chainID) 78 } 79 80 return asUnsignedDynamicTx(msg, chainID) 81 } 82 83 func asUnsignedLegacyTx(msg Message) *types.Transaction { 84 return types.NewTx(&types.LegacyTx{ 85 Nonce: msg.Nonce(), 86 To: msg.To(), 87 Value: msg.Value(), 88 Gas: msg.Gas(), 89 GasPrice: msg.GasPrice(), 90 Data: msg.Data(), 91 }) 92 } 93 94 func asUnsignedAccessListTx(msg Message, chainID *big.Int) *types.Transaction { 95 return types.NewTx(&types.AccessListTx{ 96 Nonce: msg.Nonce(), 97 To: msg.To(), 98 Value: msg.Value(), 99 Gas: msg.Gas(), 100 GasPrice: msg.GasPrice(), 101 Data: msg.Data(), 102 AccessList: msg.AccessList(), 103 ChainID: chainID, 104 }) 105 } 106 107 func asUnsignedDynamicTx(msg Message, chainID *big.Int) *types.Transaction { 108 return types.NewTx(&types.DynamicFeeTx{ 109 Nonce: msg.Nonce(), 110 To: msg.To(), 111 Value: msg.Value(), 112 Gas: msg.Gas(), 113 GasFeeCap: msg.GasFeeCap(), 114 GasTipCap: msg.GasTipCap(), 115 Data: msg.Data(), 116 AccessList: msg.AccessList(), 117 ChainID: chainID, 118 }) 119 } 120 121 // rlpEncode RLP encodes the transaction into bytes 122 func rlpEncode(tx *types.Transaction) ([]byte, error) { 123 raw := new(bytes.Buffer) 124 if err := tx.EncodeRLP(raw); err != nil { 125 return nil, err 126 } 127 128 return raw.Bytes(), nil 129 } 130 131 func readGPOStorageSlots(addr common.Address, state StateDB) (*big.Int, *big.Int, *big.Int) { 132 l1BaseFee := state.GetState(addr, rcfg.L1BaseFeeSlot) 133 overhead := state.GetState(addr, rcfg.OverheadSlot) 134 scalar := state.GetState(addr, rcfg.ScalarSlot) 135 return l1BaseFee.Big(), overhead.Big(), scalar.Big() 136 } 137 138 // calculateEncodedL1DataFee computes the L1 fee for an RLP-encoded tx 139 func calculateEncodedL1DataFee(data []byte, overhead, l1GasPrice *big.Int, scalar *big.Int) *big.Int { 140 l1GasUsed := CalculateL1GasUsed(data, overhead) 141 l1DataFee := new(big.Int).Mul(l1GasUsed, l1GasPrice) 142 return mulAndScale(l1DataFee, scalar, rcfg.Precision) 143 } 144 145 // CalculateL1GasUsed computes the L1 gas used based on the calldata and 146 // constant sized overhead. The overhead can be decreased as the cost of the 147 // batch submission goes down via contract optimizations. This will not overflow 148 // under standard network conditions. 149 func CalculateL1GasUsed(data []byte, overhead *big.Int) *big.Int { 150 zeroes, ones := zeroesAndOnes(data) 151 zeroesGas := zeroes * params.TxDataZeroGas 152 onesGas := (ones + txExtraDataBytes) * params.TxDataNonZeroGasEIP2028 153 l1Gas := new(big.Int).SetUint64(zeroesGas + onesGas) 154 return new(big.Int).Add(l1Gas, overhead) 155 } 156 157 // zeroesAndOnes counts the number of 0 bytes and non 0 bytes in a byte slice 158 func zeroesAndOnes(data []byte) (uint64, uint64) { 159 var zeroes uint64 160 var ones uint64 161 for _, byt := range data { 162 if byt == 0 { 163 zeroes++ 164 } else { 165 ones++ 166 } 167 } 168 return zeroes, ones 169 } 170 171 // mulAndScale multiplies a big.Int by a big.Int and then scale it by precision, 172 // rounded towards zero 173 func mulAndScale(x *big.Int, y *big.Int, precision *big.Int) *big.Int { 174 z := new(big.Int).Mul(x, y) 175 return new(big.Int).Quo(z, precision) 176 } 177 178 func CalculateL1DataFee(tx *types.Transaction, state StateDB) (*big.Int, error) { 179 if tx.IsL1MessageTx() { 180 return big.NewInt(0), nil 181 } 182 183 raw, err := rlpEncode(tx) 184 if err != nil { 185 return nil, err 186 } 187 188 l1BaseFee, overhead, scalar := readGPOStorageSlots(rcfg.L1GasPriceOracleAddress, state) 189 l1DataFee := calculateEncodedL1DataFee(raw, overhead, l1BaseFee, scalar) 190 return l1DataFee, nil 191 } 192 193 func calculateL2Fee(tx *types.Transaction) *big.Int { 194 l2GasLimit := new(big.Int).SetUint64(tx.Gas()) 195 return new(big.Int).Mul(tx.GasPrice(), l2GasLimit) 196 } 197 198 func VerifyFee(signer types.Signer, tx *types.Transaction, state StateDB) error { 199 from, err := types.Sender(signer, tx) 200 if err != nil { 201 return errors.New("invalid transaction: invalid sender") 202 } 203 204 balance := state.GetBalance(from) 205 l2Fee := calculateL2Fee(tx) 206 l1DataFee, err := CalculateL1DataFee(tx, state) 207 if err != nil { 208 return fmt.Errorf("invalid transaction: %w", err) 209 } 210 211 cost := tx.Value() 212 cost = cost.Add(cost, l2Fee) 213 if balance.Cmp(cost) < 0 { 214 return errors.New("invalid transaction: insufficient funds for gas * price + value") 215 } 216 217 cost = cost.Add(cost, l1DataFee) 218 if balance.Cmp(cost) < 0 { 219 return errors.New("invalid transaction: insufficient funds for l1fee + gas * price + value") 220 } 221 222 // TODO: check GasPrice is in an expected range 223 224 return nil 225 }