mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-14 00:18:03 -05:00
194 lines
6.4 KiB
Go
194 lines
6.4 KiB
Go
package types
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
|
|
"github.com/scroll-tech/go-ethereum/common"
|
|
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
|
"github.com/scroll-tech/go-ethereum/core/types"
|
|
"github.com/scroll-tech/go-ethereum/log"
|
|
)
|
|
|
|
// CalldataNonZeroByteGas is the gas consumption per non zero byte in calldata.
|
|
const CalldataNonZeroByteGas = 16
|
|
|
|
// GetKeccak256Gas calculates the gas cost for computing the keccak256 hash of a given size.
|
|
func GetKeccak256Gas(size uint64) uint64 {
|
|
return GetMemoryExpansionCost(size) + 30 + 6*((size+31)/32)
|
|
}
|
|
|
|
// GetMemoryExpansionCost calculates the cost of memory expansion for a given memoryByteSize.
|
|
func GetMemoryExpansionCost(memoryByteSize uint64) uint64 {
|
|
memorySizeWord := (memoryByteSize + 31) / 32
|
|
memoryCost := (memorySizeWord*memorySizeWord)/512 + (3 * memorySizeWord)
|
|
return memoryCost
|
|
}
|
|
|
|
// WrappedBlock contains the block's Header, Transactions and WithdrawTrieRoot hash.
|
|
type WrappedBlock struct {
|
|
Header *types.Header `json:"header"`
|
|
// Transactions is only used for recover types.Transactions, the from of types.TransactionData field is missing.
|
|
Transactions []*types.TransactionData `json:"transactions"`
|
|
WithdrawRoot common.Hash `json:"withdraw_trie_root,omitempty"`
|
|
RowConsumption *types.RowConsumption `json:"row_consumption"`
|
|
txPayloadLengthCache map[string]uint64
|
|
}
|
|
|
|
// NumL1Messages returns the number of L1 messages in this block.
|
|
// This number is the sum of included and skipped L1 messages.
|
|
func (w *WrappedBlock) NumL1Messages(totalL1MessagePoppedBefore uint64) uint64 {
|
|
var lastQueueIndex *uint64
|
|
for _, txData := range w.Transactions {
|
|
if txData.Type == types.L1MessageTxType {
|
|
lastQueueIndex = &txData.Nonce
|
|
}
|
|
}
|
|
if lastQueueIndex == nil {
|
|
return 0
|
|
}
|
|
// note: last queue index included before this block is totalL1MessagePoppedBefore - 1
|
|
// TODO: cache results
|
|
return *lastQueueIndex - totalL1MessagePoppedBefore + 1
|
|
}
|
|
|
|
// NumL2Transactions returns the number of L2 transactions in this block.
|
|
func (w *WrappedBlock) NumL2Transactions() uint64 {
|
|
var count uint64
|
|
for _, txData := range w.Transactions {
|
|
if txData.Type != types.L1MessageTxType {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
// Encode encodes the WrappedBlock into RollupV2 BlockContext Encoding.
|
|
func (w *WrappedBlock) Encode(totalL1MessagePoppedBefore uint64) ([]byte, error) {
|
|
bytes := make([]byte, 60)
|
|
|
|
if !w.Header.Number.IsUint64() {
|
|
return nil, errors.New("block number is not uint64")
|
|
}
|
|
|
|
// note: numL1Messages includes skipped messages
|
|
numL1Messages := w.NumL1Messages(totalL1MessagePoppedBefore)
|
|
if numL1Messages > math.MaxUint16 {
|
|
return nil, errors.New("number of L1 messages exceeds max uint16")
|
|
}
|
|
|
|
// note: numTransactions includes skipped messages
|
|
numL2Transactions := w.NumL2Transactions()
|
|
numTransactions := numL1Messages + numL2Transactions
|
|
if numTransactions > math.MaxUint16 {
|
|
return nil, errors.New("number of transactions exceeds max uint16")
|
|
}
|
|
|
|
binary.BigEndian.PutUint64(bytes[0:], w.Header.Number.Uint64())
|
|
binary.BigEndian.PutUint64(bytes[8:], w.Header.Time)
|
|
// TODO: [16:47] Currently, baseFee is 0, because we disable EIP-1559.
|
|
binary.BigEndian.PutUint64(bytes[48:], w.Header.GasLimit)
|
|
binary.BigEndian.PutUint16(bytes[56:], uint16(numTransactions))
|
|
binary.BigEndian.PutUint16(bytes[58:], uint16(numL1Messages))
|
|
|
|
return bytes, nil
|
|
}
|
|
|
|
// EstimateL1CommitCalldataSize calculates the calldata size in l1 commit approximately.
|
|
// TODO: The calculation could be more accurate by using 58 + len(l2TxDataBytes) (see Chunk).
|
|
// This needs to be adjusted in the future.
|
|
func (w *WrappedBlock) EstimateL1CommitCalldataSize() uint64 {
|
|
var size uint64
|
|
for _, txData := range w.Transactions {
|
|
if txData.Type == types.L1MessageTxType {
|
|
continue
|
|
}
|
|
size += 4 // 4 bytes payload length
|
|
size += w.getTxPayloadLength(txData)
|
|
}
|
|
size += 60 // 60 bytes BlockContext
|
|
return size
|
|
}
|
|
|
|
// EstimateL1CommitGas calculates the total L1 commit gas for this block approximately.
|
|
func (w *WrappedBlock) EstimateL1CommitGas() uint64 {
|
|
var total uint64
|
|
var numL1Messages uint64
|
|
for _, txData := range w.Transactions {
|
|
if txData.Type == types.L1MessageTxType {
|
|
numL1Messages++
|
|
continue
|
|
}
|
|
|
|
txPayloadLength := w.getTxPayloadLength(txData)
|
|
total += CalldataNonZeroByteGas * txPayloadLength // an over-estimate: treat each byte as non-zero
|
|
total += CalldataNonZeroByteGas * 4 // 4 bytes payload length
|
|
total += GetKeccak256Gas(txPayloadLength) // l2 tx hash
|
|
}
|
|
|
|
// 60 bytes BlockContext calldata
|
|
total += CalldataNonZeroByteGas * 60
|
|
|
|
// sload
|
|
total += 2100 * numL1Messages // numL1Messages times cold sload in L1MessageQueue
|
|
|
|
// staticcall
|
|
total += 100 * numL1Messages // numL1Messages times call to L1MessageQueue
|
|
total += 100 * numL1Messages // numL1Messages times warm address access to L1MessageQueue
|
|
|
|
total += GetMemoryExpansionCost(36) * numL1Messages // staticcall to proxy
|
|
total += 100 * numL1Messages // read admin in proxy
|
|
total += 100 * numL1Messages // read impl in proxy
|
|
total += 100 * numL1Messages // access impl
|
|
total += GetMemoryExpansionCost(36) * numL1Messages // delegatecall to impl
|
|
|
|
return total
|
|
}
|
|
|
|
func (w *WrappedBlock) getTxPayloadLength(txData *types.TransactionData) uint64 {
|
|
if w.txPayloadLengthCache == nil {
|
|
w.txPayloadLengthCache = make(map[string]uint64)
|
|
}
|
|
|
|
if length, exists := w.txPayloadLengthCache[txData.TxHash]; exists {
|
|
return length
|
|
}
|
|
|
|
rlpTxData, err := convertTxDataToRLPEncoding(txData)
|
|
if err != nil {
|
|
log.Crit("convertTxDataToRLPEncoding failed, which should not happen", "hash", txData.TxHash, "err", err)
|
|
return 0
|
|
}
|
|
txPayloadLength := uint64(len(rlpTxData))
|
|
w.txPayloadLengthCache[txData.TxHash] = txPayloadLength
|
|
return txPayloadLength
|
|
}
|
|
|
|
func convertTxDataToRLPEncoding(txData *types.TransactionData) ([]byte, error) {
|
|
data, err := hexutil.Decode(txData.Data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to decode txData.Data: %s, err: %w", txData.Data, err)
|
|
}
|
|
|
|
tx := types.NewTx(&types.LegacyTx{
|
|
Nonce: txData.Nonce,
|
|
To: txData.To,
|
|
Value: txData.Value.ToInt(),
|
|
Gas: txData.Gas,
|
|
GasPrice: txData.GasPrice.ToInt(),
|
|
Data: data,
|
|
V: txData.V.ToInt(),
|
|
R: txData.R.ToInt(),
|
|
S: txData.S.ToInt(),
|
|
})
|
|
|
|
rlpTxData, err := tx.MarshalBinary()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal binary of the tx: %+v, err: %w", tx, err)
|
|
}
|
|
|
|
return rlpTxData, nil
|
|
}
|