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  }