mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-30 16:43:46 +00:00
* init * more * more * add L1BlockContainerAddress * finish * add more comments * rename `UsingSVM` to `UsingScroll` * update logic * refactor(rollup): add UsingScroll into ChainConfig * update * update comments * fee=l2fee+l1fee (#221) add l1fee into calc * improve * format note content. * feat(l1Fee): Add l1Fee in trace. (#223) * Add l1fee in trace. * Delete comment. --------- Co-authored-by: maskpp <maskpp266@gmail.com>
153 lines
4.4 KiB
Go
153 lines
4.4 KiB
Go
package fees
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"math"
|
|
"math/big"
|
|
|
|
"github.com/scroll-tech/go-ethereum/common"
|
|
"github.com/scroll-tech/go-ethereum/core/types"
|
|
"github.com/scroll-tech/go-ethereum/params"
|
|
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
|
|
)
|
|
|
|
var (
|
|
// errTransactionSigned represents the error case of passing in a signed
|
|
// transaction to the L1 fee calculation routine. The signature is accounted
|
|
// for externally
|
|
errTransactionSigned = errors.New("transaction is signed")
|
|
)
|
|
|
|
// Message represents the interface of a message.
|
|
// It should be a subset of the methods found on
|
|
// types.Message
|
|
type Message interface {
|
|
From() common.Address
|
|
To() *common.Address
|
|
GasPrice() *big.Int
|
|
Gas() uint64
|
|
Value() *big.Int
|
|
Nonce() uint64
|
|
Data() []byte
|
|
}
|
|
|
|
// StateDB represents the StateDB interface
|
|
// required to compute the L1 fee
|
|
type StateDB interface {
|
|
GetState(common.Address, common.Hash) common.Hash
|
|
}
|
|
|
|
// CalculateL1MsgFee computes the L1 portion of the fee given
|
|
// a Message and a StateDB
|
|
// Reference: https://github.com/ethereum-optimism/optimism/blob/develop/l2geth/rollup/fees/rollup_fee.go
|
|
func CalculateL1MsgFee(msg Message, state StateDB) (*big.Int, error) {
|
|
tx := asTransaction(msg)
|
|
raw, err := rlpEncode(tx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
l1BaseFee, overhead, scalar := readGPOStorageSlots(rcfg.L1GasPriceOracleAddress, state)
|
|
l1Fee := CalculateL1Fee(raw, overhead, l1BaseFee, scalar)
|
|
return l1Fee, nil
|
|
}
|
|
|
|
// asTransaction turns a Message into a types.Transaction
|
|
func asTransaction(msg Message) *types.Transaction {
|
|
if msg.To() == nil {
|
|
return types.NewContractCreation(
|
|
msg.Nonce(),
|
|
msg.Value(),
|
|
msg.Gas(),
|
|
msg.GasPrice(),
|
|
msg.Data(),
|
|
)
|
|
}
|
|
return types.NewTransaction(
|
|
msg.Nonce(),
|
|
*msg.To(),
|
|
msg.Value(),
|
|
msg.Gas(),
|
|
msg.GasPrice(),
|
|
msg.Data(),
|
|
)
|
|
}
|
|
|
|
// rlpEncode RLP encodes the transaction into bytes
|
|
// When a signature is not included, set pad to true to
|
|
// fill in a dummy signature full on non 0 bytes
|
|
func rlpEncode(tx *types.Transaction) ([]byte, error) {
|
|
raw := new(bytes.Buffer)
|
|
if err := tx.EncodeRLP(raw); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r, v, s := tx.RawSignatureValues()
|
|
if r.Cmp(common.Big0) != 0 || v.Cmp(common.Big0) != 0 || s.Cmp(common.Big0) != 0 {
|
|
return nil, errTransactionSigned
|
|
}
|
|
|
|
// Slice off the 0 bytes representing the signature
|
|
b := raw.Bytes()
|
|
return b[:len(b)-3], nil
|
|
}
|
|
|
|
func readGPOStorageSlots(addr common.Address, state StateDB) (*big.Int, *big.Int, *big.Float) {
|
|
l1BaseFee := state.GetState(addr, rcfg.L1BaseFeeSlot)
|
|
overhead := state.GetState(addr, rcfg.OverheadSlot)
|
|
scalar := state.GetState(addr, rcfg.ScalarSlot)
|
|
scaled := ScalePrecision(scalar.Big(), rcfg.Precision)
|
|
return l1BaseFee.Big(), overhead.Big(), scaled
|
|
}
|
|
|
|
// ScalePrecision will scale a value by precision
|
|
func ScalePrecision(scalar, precision *big.Int) *big.Float {
|
|
fscalar := new(big.Float).SetInt(scalar)
|
|
fdivisor := new(big.Float).SetInt(precision)
|
|
// fscalar / fdivisor
|
|
return new(big.Float).Quo(fscalar, fdivisor)
|
|
}
|
|
|
|
// CalculateL1Fee computes the L1 fee
|
|
func CalculateL1Fee(data []byte, overhead, l1GasPrice *big.Int, scalar *big.Float) *big.Int {
|
|
l1GasUsed := CalculateL1GasUsed(data, overhead)
|
|
l1Fee := new(big.Int).Mul(l1GasUsed, l1GasPrice)
|
|
return mulByFloat(l1Fee, scalar)
|
|
}
|
|
|
|
// CalculateL1GasUsed computes the L1 gas used based on the calldata and
|
|
// constant sized overhead. The overhead can be decreased as the cost of the
|
|
// batch submission goes down via contract optimizations. This will not overflow
|
|
// under standard network conditions.
|
|
func CalculateL1GasUsed(data []byte, overhead *big.Int) *big.Int {
|
|
zeroes, ones := zeroesAndOnes(data)
|
|
zeroesGas := zeroes * params.TxDataZeroGas
|
|
onesGas := (ones + 68) * params.TxDataNonZeroGasEIP2028
|
|
l1Gas := new(big.Int).SetUint64(zeroesGas + onesGas)
|
|
return new(big.Int).Add(l1Gas, overhead)
|
|
}
|
|
|
|
// zeroesAndOnes counts the number of 0 bytes and non 0 bytes in a byte slice
|
|
func zeroesAndOnes(data []byte) (uint64, uint64) {
|
|
var zeroes uint64
|
|
var ones uint64
|
|
for _, byt := range data {
|
|
if byt == 0 {
|
|
zeroes++
|
|
} else {
|
|
ones++
|
|
}
|
|
}
|
|
return zeroes, ones
|
|
}
|
|
|
|
// mulByFloat multiplies a big.Int by a float and returns the
|
|
// big.Int rounded upwards
|
|
func mulByFloat(num *big.Int, float *big.Float) *big.Int {
|
|
n := new(big.Float).SetUint64(num.Uint64())
|
|
product := n.Mul(n, float)
|
|
pfloat, _ := product.Float64()
|
|
rounded := math.Ceil(pfloat)
|
|
return new(big.Int).SetUint64(uint64(rounded))
|
|
}
|