feat: update L1 data fee in Curie hard fork (#755)

* update fee calculation

* add missing GPO slots to trace

* add placeholder for contract update logic

* nit

* update fee calculation

* update formula

* update GPO slots

* update L1GPO bytecode

* apply Curie in new worker

* move bytecode to config

* create an empty block for curie hard fork

* initialize L1GasPriceOracle storage slots

* add comments

* add test

* add IsCurie to traces and tests

* group GPO storage slots into a struct

* update unit test

* chore: auto version bump [bot]

* trigger ci

* update bytecode

* remove leading 0x

* update comments

* include rollup fee tests in CI

---------

Co-authored-by: Ömer Faruk Irmak <omerfirmak@gmail.com>
Co-authored-by: Thegaram <Thegaram@users.noreply.github.com>
This commit is contained in:
Péter Garamvölgyi 2024-05-28 14:12:39 +02:00 committed by GitHub
parent 9f961ddc71
commit bfe803001b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 316 additions and 70 deletions

View file

@ -40,7 +40,7 @@ test: all
# genesis test # genesis test
cd ${PWD}/cmd/geth; go test -test.run TestCustomGenesis cd ${PWD}/cmd/geth; go test -test.run TestCustomGenesis
# module test # module test
$(GORUN) build/ci.go test ./consensus ./core ./eth ./miner ./node ./trie $(GORUN) build/ci.go test ./consensus ./core ./eth ./miner ./node ./trie ./rollup/fees
lint: ## Run linters. lint: ## Run linters.
$(GORUN) build/ci.go lint $(GORUN) build/ci.go lint

View file

@ -640,7 +640,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true}) vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
gasPool := new(core.GasPool).AddGas(math.MaxUint64) gasPool := new(core.GasPool).AddGas(math.MaxUint64)
signer := types.MakeSigner(b.blockchain.Config(), head.Number) signer := types.MakeSigner(b.blockchain.Config(), head.Number)
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, head.BaseFee, b.blockchain.Config().ChainID, signer, stateDB) l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, head.BaseFee, b.blockchain.Config(), signer, stateDB, head.Number)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -148,6 +148,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 { chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
misc.ApplyDAOHardFork(statedb) misc.ApplyDAOHardFork(statedb)
} }
// Apply Curie hard fork
if chainConfig.CurieBlock != nil && chainConfig.CurieBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
misc.ApplyCurieHardFork(statedb)
}
for i, tx := range txs { for i, tx := range txs {
msg, err := tx.AsMessage(signer, pre.Env.BaseFee) msg, err := tx.AsMessage(signer, pre.Env.BaseFee)
@ -167,7 +171,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
snapshot := statedb.Snapshot() snapshot := statedb.Snapshot()
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, chainConfig, new(big.Int).SetUint64(pre.Env.Number))
if err != nil { if err != nil {
log.Info("rejected tx due to fees.CalculateL1DataFee", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err) log.Info("rejected tx due to fees.CalculateL1DataFee", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err)
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})

23
consensus/misc/curie.go Normal file
View file

@ -0,0 +1,23 @@
package misc
import (
"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/core/state"
"github.com/scroll-tech/go-ethereum/log"
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
)
// ApplyCurieHardFork modifies the state database according to the Curie hard-fork rules,
// updating the bytecode and storage of the L1GasPriceOracle contract.
func ApplyCurieHardFork(statedb *state.StateDB) {
log.Info("Applying Curie hard fork")
// update contract byte code
statedb.SetCode(rcfg.L1GasPriceOracleAddress, rcfg.CurieL1GasPriceOracleBytecode)
// initialize new storage slots
statedb.SetState(rcfg.L1GasPriceOracleAddress, rcfg.IsCurieSlot, common.BytesToHash([]byte{1}))
statedb.SetState(rcfg.L1GasPriceOracleAddress, rcfg.L1BlobBaseFeeSlot, common.BytesToHash([]byte{1}))
statedb.SetState(rcfg.L1GasPriceOracleAddress, rcfg.CommitScalarSlot, common.BigToHash(rcfg.InitialCommitScalar))
statedb.SetState(rcfg.L1GasPriceOracleAddress, rcfg.BlobScalarSlot, common.BigToHash(rcfg.InitialBlobScalar))
}

View file

@ -17,6 +17,7 @@
package core package core
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -39,6 +40,7 @@ import (
"github.com/scroll-tech/go-ethereum/crypto" "github.com/scroll-tech/go-ethereum/crypto"
"github.com/scroll-tech/go-ethereum/ethdb" "github.com/scroll-tech/go-ethereum/ethdb"
"github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
"github.com/scroll-tech/go-ethereum/trie" "github.com/scroll-tech/go-ethereum/trie"
) )
@ -3712,3 +3714,78 @@ func TestTransientStorageReset(t *testing.T) {
t.Fatalf("Unexpected dirty storage slot") t.Fatalf("Unexpected dirty storage slot")
} }
} }
func TestCurieTransition(t *testing.T) {
// Set fork blocks in config
// (we make a deep copy to avoid interference with other tests)
var config *params.ChainConfig
b, _ := json.Marshal(params.AllEthashProtocolChanges)
json.Unmarshal(b, &config)
config.CurieBlock = big.NewInt(2)
config.DescartesBlock = nil
var (
db = rawdb.NewMemoryDatabase()
gspec = &Genesis{Config: config}
genesis = gspec.MustCommit(db)
)
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
defer blockchain.Stop()
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, nil)
if _, err := blockchain.InsertChain(blocks); err != nil {
t.Fatal(err)
}
latestBlock := uint64(4)
assert.Equal(t, latestBlock, blockchain.CurrentHeader().Number.Uint64())
for ii := uint64(0); ii <= latestBlock; ii++ {
block := blockchain.GetBlockByNumber(ii)
number := block.Number().Uint64()
baseFee := block.BaseFee()
statedb, _ := state.New(block.Root(), state.NewDatabase(db), nil)
code := statedb.GetCode(rcfg.L1GasPriceOracleAddress)
codeSize := statedb.GetCodeSize(rcfg.L1GasPriceOracleAddress)
keccakCodeHash := statedb.GetKeccakCodeHash(rcfg.L1GasPriceOracleAddress)
poseidonCodeHash := statedb.GetPoseidonCodeHash(rcfg.L1GasPriceOracleAddress)
l1BlobBaseFee := statedb.GetState(rcfg.L1GasPriceOracleAddress, rcfg.L1BlobBaseFeeSlot)
commitScalar := statedb.GetState(rcfg.L1GasPriceOracleAddress, rcfg.CommitScalarSlot)
blobScalar := statedb.GetState(rcfg.L1GasPriceOracleAddress, rcfg.BlobScalarSlot)
isCurie := statedb.GetState(rcfg.L1GasPriceOracleAddress, rcfg.IsCurieSlot)
if number < config.CurieBlock.Uint64() {
assert.Nil(t, baseFee, "Expected zero base fee before Curie")
// we don't have predeploys configured in this test,
// so there is no gas oracle deployed before Curie
assert.Nil(t, code)
assert.Equal(t, uint64(0), codeSize)
assert.Equal(t, common.Hash{}, keccakCodeHash)
assert.Equal(t, common.Hash{}, poseidonCodeHash)
assert.Equal(t, common.Hash{}, l1BlobBaseFee)
assert.Equal(t, common.Hash{}, commitScalar)
assert.Equal(t, common.Hash{}, blobScalar)
assert.Equal(t, common.Hash{}, isCurie)
} else {
assert.NotNil(t, baseFee, "Expected nonzero base fee after Curie")
// all gas oracle entries updated
assert.NotNil(t, code)
assert.NotEqual(t, uint64(0), codeSize)
assert.NotEqual(t, common.Hash{}, keccakCodeHash)
assert.NotEqual(t, common.Hash{}, poseidonCodeHash)
assert.NotEqual(t, common.Hash{}, l1BlobBaseFee)
assert.NotEqual(t, common.Hash{}, commitScalar)
assert.NotEqual(t, common.Hash{}, blobScalar)
assert.NotEqual(t, common.Hash{}, isCurie)
}
}
}

View file

@ -240,6 +240,9 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 { if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 {
misc.ApplyDAOHardFork(statedb) misc.ApplyDAOHardFork(statedb)
} }
if config.CurieBlock != nil && config.CurieBlock.Cmp(b.header.Number) == 0 {
misc.ApplyCurieHardFork(statedb)
}
// Execute any user modifications to the block // Execute any user modifications to the block
if gen != nil { if gen != nil {
gen(i, b) gen(i, b)

View file

@ -71,7 +71,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
} }
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, p.config, block.Number())
if err != nil { if err != nil {
return return
} }

View file

@ -86,6 +86,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(statedb) misc.ApplyDAOHardFork(statedb)
} }
// Apply Curie hard fork
if p.config.CurieBlock != nil && p.config.CurieBlock.Cmp(block.Number()) == 0 {
misc.ApplyCurieHardFork(statedb)
}
blockContext := NewEVMBlockContext(header, p.bc, p.config, nil) blockContext := NewEVMBlockContext(header, p.bc, p.config, nil)
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
processorBlockTransactionGauge.Update(int64(block.Transactions().Len())) processorBlockTransactionGauge.Update(int64(block.Transactions().Len()))
@ -120,7 +124,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
txContext := NewEVMTxContext(msg) txContext := NewEVMTxContext(msg)
evm.Reset(txContext, statedb) evm.Reset(txContext, statedb)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, config, blockNumber)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -29,6 +29,7 @@ import (
"github.com/scroll-tech/go-ethereum/core/state" "github.com/scroll-tech/go-ethereum/core/state"
"github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/log"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rollup/fees" "github.com/scroll-tech/go-ethereum/rollup/fees"
) )
@ -281,7 +282,7 @@ func (l *txList) Overlaps(tx *types.Transaction) bool {
// //
// If the new transaction is accepted into the list, the lists' cost and gas // If the new transaction is accepted into the list, the lists' cost and gas
// thresholds are also potentially updated. // thresholds are also potentially updated.
func (l *txList) Add(tx *types.Transaction, state *state.StateDB, priceBump uint64) (bool, *types.Transaction) { func (l *txList) Add(tx *types.Transaction, state *state.StateDB, priceBump uint64, chainconfig *params.ChainConfig, blockNumber *big.Int) (bool, *types.Transaction) {
// If there's an older better transaction, abort // If there's an older better transaction, abort
old := l.txs.Get(tx.Nonce()) old := l.txs.Get(tx.Nonce())
if old != nil { if old != nil {
@ -307,9 +308,9 @@ func (l *txList) Add(tx *types.Transaction, state *state.StateDB, priceBump uint
} }
// Otherwise overwrite the old transaction with the current one // Otherwise overwrite the old transaction with the current one
l1DataFee := big.NewInt(0) l1DataFee := big.NewInt(0)
if state != nil { if state != nil && chainconfig != nil {
var err error var err error
l1DataFee, err = fees.CalculateL1DataFee(tx, state) l1DataFee, err = fees.CalculateL1DataFee(tx, state, chainconfig, blockNumber)
if err != nil { if err != nil {
log.Error("Failed to calculate L1 data fee", "err", err, "tx", tx) log.Error("Failed to calculate L1 data fee", "err", err, "tx", tx)
return false, nil return false, nil

View file

@ -38,7 +38,7 @@ func TestStrictTxListAdd(t *testing.T) {
// Insert the transactions in a random order // Insert the transactions in a random order
list := newTxList(true) list := newTxList(true)
for _, v := range rand.Perm(len(txs)) { for _, v := range rand.Perm(len(txs)) {
list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump) list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump, nil, nil)
} }
// Verify internal state // Verify internal state
if len(list.txs.items) != len(txs) { if len(list.txs.items) != len(txs) {
@ -65,7 +65,7 @@ func BenchmarkTxListAdd(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
list := newTxList(true) list := newTxList(true)
for _, v := range rand.Perm(len(txs)) { for _, v := range rand.Perm(len(txs)) {
list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump) list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump, nil, nil)
list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump) list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump)
} }
} }

View file

@ -248,6 +248,7 @@ type TxPool struct {
shanghai bool // Fork indicator whether we are in the Shanghai stage. shanghai bool // Fork indicator whether we are in the Shanghai stage.
currentState *state.StateDB // Current state in the blockchain head currentState *state.StateDB // Current state in the blockchain head
currentHead *big.Int // Current blockchain head
pendingNonces *txNoncer // Pending state tracking virtual nonces pendingNonces *txNoncer // Pending state tracking virtual nonces
currentMaxGas uint64 // Current gas limit for transaction caps currentMaxGas uint64 // Current gas limit for transaction caps
@ -664,7 +665,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
// 2. If FeeVault is enabled, perform an additional check for L1 data fees. // 2. If FeeVault is enabled, perform an additional check for L1 data fees.
if pool.chainconfig.Scroll.FeeVaultEnabled() { if pool.chainconfig.Scroll.FeeVaultEnabled() {
// Get L1 data fee in current state // Get L1 data fee in current state
l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState) l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState, pool.chainconfig, pool.currentHead)
if err != nil { if err != nil {
return fmt.Errorf("failed to calculate L1 data fee, err: %w", err) return fmt.Errorf("failed to calculate L1 data fee, err: %w", err)
} }
@ -751,7 +752,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
from, _ := types.Sender(pool.signer, tx) // already validated from, _ := types.Sender(pool.signer, tx) // already validated
if list := pool.pending[from]; list != nil && list.Overlaps(tx) { if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
// Nonce already pending, check if required price bump is met // Nonce already pending, check if required price bump is met
inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump) inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead)
if !inserted { if !inserted {
pendingDiscardMeter.Mark(1) pendingDiscardMeter.Mark(1)
return false, ErrReplaceUnderpriced return false, ErrReplaceUnderpriced
@ -802,7 +803,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local boo
pool.queue[from] = newTxList(false) pool.queue[from] = newTxList(false)
} }
inserted, old := pool.queue[from].Add(tx, pool.currentState, pool.config.PriceBump) inserted, old := pool.queue[from].Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead)
if !inserted { if !inserted {
// An older transaction was better, discard this // An older transaction was better, discard this
queuedDiscardMeter.Mark(1) queuedDiscardMeter.Mark(1)
@ -856,7 +857,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
} }
list := pool.pending[addr] list := pool.pending[addr]
inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump) inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead)
if !inserted { if !inserted {
// An older transaction was better, discard this // An older transaction was better, discard this
pool.all.Remove(hash) pool.all.Remove(hash)
@ -1357,6 +1358,9 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
pool.eip2718 = pool.chainconfig.IsCurie(next) pool.eip2718 = pool.chainconfig.IsCurie(next)
pool.eip1559 = pool.chainconfig.IsCurie(next) pool.eip1559 = pool.chainconfig.IsCurie(next)
pool.shanghai = pool.chainconfig.IsShanghai(next) pool.shanghai = pool.chainconfig.IsShanghai(next)
// Update current head
pool.currentHead = next
} }
// promoteExecutables moves transactions that have become processable from the // promoteExecutables moves transactions that have become processable from the
@ -1435,7 +1439,7 @@ func (pool *TxPool) executableTxFilter(costLimit *big.Int) func(tx *types.Transa
if pool.chainconfig.Scroll.FeeVaultEnabled() { if pool.chainconfig.Scroll.FeeVaultEnabled() {
// recheck L1 data fee, as the oracle price may have changed // recheck L1 data fee, as the oracle price may have changed
l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState) l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState, pool.chainconfig, pool.currentHead)
if err != nil { if err != nil {
log.Error("Failed to calculate L1 data fee", "err", err, "tx", tx) log.Error("Failed to calculate L1 data fee", "err", err, "tx", tx)
return false return false

View file

@ -192,7 +192,7 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec
// Not yet the searched for transaction, execute on top of the current state // Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{}) vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
statedb.SetTxContext(tx.Hash(), idx) statedb.SetTxContext(tx.Hash(), idx)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, eth.blockchain.Config(), block.Number())
if err != nil { if err != nil {
return nil, vm.BlockContext{}, nil, err return nil, vm.BlockContext{}, nil, err
} }

View file

@ -285,7 +285,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
TxHash: tx.Hash(), TxHash: tx.Hash(),
} }
l1DataFee, err := fees.CalculateL1DataFee(tx, task.statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, task.statedb, api.backend.ChainConfig(), task.block.Number())
if err != nil { if err != nil {
// though it's not a "tracing error", we still need to put it here // though it's not a "tracing error", we still need to put it here
task.results[i] = &txTraceResult{Error: err.Error()} task.results[i] = &txTraceResult{Error: err.Error()}
@ -545,7 +545,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
) )
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number())
if err != nil { if err != nil {
log.Warn("Tracing intermediate roots did not complete due to fees.CalculateL1DataFee", "txindex", i, "txhash", tx.Hash(), "err", err) log.Warn("Tracing intermediate roots did not complete due to fees.CalculateL1DataFee", "txindex", i, "txhash", tx.Hash(), "err", err)
return nil, err return nil, err
@ -626,7 +626,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
TxHash: txs[task.index].Hash(), TxHash: txs[task.index].Hash(),
} }
l1DataFee, err := fees.CalculateL1DataFee(txs[task.index], task.statedb) l1DataFee, err := fees.CalculateL1DataFee(txs[task.index], task.statedb, api.backend.ChainConfig(), block.Number())
if err != nil { if err != nil {
// though it's not a "tracing error", we still need to put it here // though it's not a "tracing error", we still need to put it here
results[task.index] = &txTraceResult{Error: err.Error()} results[task.index] = &txTraceResult{Error: err.Error()}
@ -651,7 +651,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
msg, _ := tx.AsMessage(signer, block.BaseFee()) msg, _ := tx.AsMessage(signer, block.BaseFee())
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number())
if err != nil { if err != nil {
failed = err failed = err
break break
@ -769,7 +769,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
// Execute the transaction and flush any traces to disk // Execute the transaction and flush any traces to disk
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number())
if err == nil { if err == nil {
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee) _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee)
} }
@ -834,7 +834,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
TxIndex: int(index), TxIndex: int(index),
TxHash: hash, TxHash: hash,
} }
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -894,7 +894,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
} }
signer := types.MakeSigner(api.backend.ChainConfig(), block.Number()) signer := types.MakeSigner(api.backend.ChainConfig(), block.Number())
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, block.BaseFee(), api.backend.ChainConfig().ChainID, signer, statedb) l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, block.BaseFee(), api.backend.ChainConfig(), signer, statedb, block.Number())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -173,7 +173,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
return msg, context, statedb, nil return msg, context, statedb, nil
} }
vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{}) vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{})
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, b.chainConfig, block.Number())
if err != nil { if err != nil {
return nil, vm.BlockContext{}, nil, err return nil, vm.BlockContext{}, nil, err
} }

View file

@ -193,7 +193,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err) t.Fatalf("failed to prepare transaction for tracing: %v", err)
} }
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, test.Genesis.Config, context.BlockNumber)
if err != nil { if err != nil {
t.Fatalf("failed to calculate l1DataFee: %v", err) t.Fatalf("failed to calculate l1DataFee: %v", err)
} }
@ -308,7 +308,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
} }
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
snap := statedb.Snapshot() snap := statedb.Snapshot()
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, test.Genesis.Config, context.BlockNumber)
if err != nil { if err != nil {
b.Fatalf("failed to calculate l1DataFee: %v", err) b.Fatalf("failed to calculate l1DataFee: %v", err)
} }
@ -381,7 +381,7 @@ func TestZeroValueToNotExitCall(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err) t.Fatalf("failed to prepare transaction for tracing: %v", err)
} }
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, params.MainnetChainConfig, context.BlockNumber)
if err != nil { if err != nil {
t.Fatalf("failed to calculate l1DataFee: %v", err) t.Fatalf("failed to calculate l1DataFee: %v", err)
} }

View file

@ -112,7 +112,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
snap := statedb.Snapshot() snap := statedb.Snapshot()
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, params.AllEthashProtocolChanges, context.BlockNumber)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }

View file

@ -200,6 +200,10 @@ var genesis = &core.Genesis{
rcfg.L1BaseFeeSlot: common.BigToHash(big.NewInt(10000)), rcfg.L1BaseFeeSlot: common.BigToHash(big.NewInt(10000)),
rcfg.OverheadSlot: common.BigToHash(big.NewInt(10000)), rcfg.OverheadSlot: common.BigToHash(big.NewInt(10000)),
rcfg.ScalarSlot: common.BigToHash(big.NewInt(10000)), rcfg.ScalarSlot: common.BigToHash(big.NewInt(10000)),
rcfg.L1BlobBaseFeeSlot: common.BigToHash(big.NewInt(10000)),
rcfg.CommitScalarSlot: common.BigToHash(big.NewInt(10000)),
rcfg.BlobScalarSlot: common.BigToHash(big.NewInt(10000)),
rcfg.IsCurieSlot: common.BytesToHash([]byte{1}),
}, },
}, },
}, },

View file

@ -84,6 +84,10 @@ func generateTestChain() (*core.Genesis, []*types.Block) {
rcfg.L1BaseFeeSlot: common.BigToHash(big.NewInt(10000)), rcfg.L1BaseFeeSlot: common.BigToHash(big.NewInt(10000)),
rcfg.OverheadSlot: common.BigToHash(big.NewInt(10000)), rcfg.OverheadSlot: common.BigToHash(big.NewInt(10000)),
rcfg.ScalarSlot: common.BigToHash(big.NewInt(10000)), rcfg.ScalarSlot: common.BigToHash(big.NewInt(10000)),
rcfg.L1BlobBaseFeeSlot: common.BigToHash(big.NewInt(10000)),
rcfg.CommitScalarSlot: common.BigToHash(big.NewInt(10000)),
rcfg.BlobScalarSlot: common.BigToHash(big.NewInt(10000)),
rcfg.IsCurieSlot: common.BytesToHash([]byte{1}),
}, },
}, },
}, },

View file

@ -952,7 +952,7 @@ func EstimateL1MsgFee(ctx context.Context, b Backend, args TransactionArgs, bloc
}() }()
signer := types.MakeSigner(config, header.Number) signer := types.MakeSigner(config, header.Number)
return fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config.ChainID, signer, evm.StateDB) return fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, evm.StateDB, header.Number)
} }
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
@ -1496,7 +1496,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
return nil, 0, nil, err return nil, 0, nil, err
} }
signer := types.MakeSigner(b.ChainConfig(), header.Number) signer := types.MakeSigner(b.ChainConfig(), header.Number)
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, b.ChainConfig().ChainID, signer, statedb) l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, b.ChainConfig(), signer, statedb, header.Number)
if err != nil { if err != nil {
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err) return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
} }

View file

@ -145,7 +145,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxUint64) gp := new(core.GasPool).AddGas(math.MaxUint64)
signer := types.MakeSigner(config, header.Number) signer := types.MakeSigner(config, header.Number)
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config.ChainID, signer, statedb) l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, statedb, header.Number)
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee) result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
res = append(res, result.Return()...) res = append(res, result.Return()...)
} }
@ -159,7 +159,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
vmenv := vm.NewEVM(context, txContext, state, config, vm.Config{NoBaseFee: true}) vmenv := vm.NewEVM(context, txContext, state, config, vm.Config{NoBaseFee: true})
gp := new(core.GasPool).AddGas(math.MaxUint64) gp := new(core.GasPool).AddGas(math.MaxUint64)
signer := types.MakeSigner(config, header.Number) signer := types.MakeSigner(config, header.Number)
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config.ChainID, signer, state) l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, state, header.Number)
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee) result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
if state.Error() == nil { if state.Error() == nil {
res = append(res, result.Return()...) res = append(res, result.Return()...)

View file

@ -65,7 +65,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.
} }
// Not yet the searched for transaction, execute on top of the current state // Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{}) vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{})
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb) l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, leth.blockchain.Config(), block.Number())
if err != nil { if err != nil {
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
} }

View file

@ -201,7 +201,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true}) vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true})
gp := new(core.GasPool).AddGas(math.MaxUint64) gp := new(core.GasPool).AddGas(math.MaxUint64)
signer := types.MakeSigner(config, header.Number) signer := types.MakeSigner(config, header.Number)
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config.ChainID, signer, st) l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, st, header.Number)
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee) result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
res = append(res, result.Return()...) res = append(res, result.Return()...)
if st.Error() != nil { if st.Error() != nil {

View file

@ -72,6 +72,8 @@ type TxPool struct {
istanbul bool // Fork indicator whether we are in the istanbul stage. istanbul bool // Fork indicator whether we are in the istanbul stage.
eip2718 bool // Fork indicator whether we are in the eip2718 stage. eip2718 bool // Fork indicator whether we are in the eip2718 stage.
shanghai bool // Fork indicator whether we are in the shanghai stage. shanghai bool // Fork indicator whether we are in the shanghai stage.
currentHead *big.Int // Current blockchain head
} }
// TxRelayBackend provides an interface to the mechanism that forwards transacions // TxRelayBackend provides an interface to the mechanism that forwards transacions
@ -79,8 +81,11 @@ type TxPool struct {
// //
// Send instructs backend to forward new transactions // Send instructs backend to forward new transactions
// NewHead notifies backend about a new head after processed by the tx pool, // NewHead notifies backend about a new head after processed by the tx pool,
//
// including mined and rolled back transactions since the last event // including mined and rolled back transactions since the last event
//
// Discard notifies backend about transactions that should be discarded either // Discard notifies backend about transactions that should be discarded either
//
// because they have been replaced by a re-send or because they have been mined // because they have been replaced by a re-send or because they have been mined
// long ago and no rollback is expected // long ago and no rollback is expected
type TxRelayBackend interface { type TxRelayBackend interface {
@ -319,6 +324,8 @@ func (pool *TxPool) setNewHead(head *types.Header) {
pool.istanbul = pool.config.IsIstanbul(next) pool.istanbul = pool.config.IsIstanbul(next)
pool.eip2718 = pool.config.IsBerlin(next) pool.eip2718 = pool.config.IsBerlin(next)
pool.shanghai = pool.config.IsShanghai(next) pool.shanghai = pool.config.IsShanghai(next)
pool.currentHead = next
} }
// Stop stops the light transaction pool // Stop stops the light transaction pool
@ -387,7 +394,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
// 2. If FeeVault is enabled, perform an additional check for L1 data fees. // 2. If FeeVault is enabled, perform an additional check for L1 data fees.
if pool.config.Scroll.FeeVaultEnabled() { if pool.config.Scroll.FeeVaultEnabled() {
// Get L1 data fee in current state // Get L1 data fee in current state
l1DataFee, err := fees.CalculateL1DataFee(tx, currentState) l1DataFee, err := fees.CalculateL1DataFee(tx, currentState, pool.config, pool.currentHead)
if err != nil { if err != nil {
return fmt.Errorf("failed to calculate L1 data fee, err: %w", err) return fmt.Errorf("failed to calculate L1 data fee, err: %w", err)
} }

View file

@ -461,6 +461,32 @@ func (w *worker) startNewPipeline(timestamp int64) {
return return
} }
// Apply special state transition at Curie block
if w.chainConfig.CurieBlock != nil && w.chainConfig.CurieBlock.Cmp(header.Number) == 0 {
misc.ApplyCurieHardFork(parentState)
// zkEVM requirement: Curie transition block contains 0 transactions, bypass pipeline.
err = w.commit(&pipeline.Result{
// Note: Signer nodes will not store CCC results for empty blocks in their database.
// In practice, this is acceptable, since this block will never overflow, and follower
// nodes will still store CCC results.
Rows: &types.RowConsumption{},
FinalBlock: &pipeline.BlockCandidate{
Header: header,
State: parentState,
Txs: types.Transactions{},
Receipts: types.Receipts{},
CoalescedLogs: []*types.Log{},
},
})
if err != nil {
log.Error("failed to commit Curie fork block", "reason", err)
}
return
}
// fetch l1Txs // fetch l1Txs
var l1Messages []types.L1MessageTx var l1Messages []types.L1MessageTx
if w.chainConfig.Scroll.ShouldIncludeL1Messages() { if w.chainConfig.Scroll.ShouldIncludeL1Messages() {

View file

@ -1411,6 +1411,13 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64)
if w.chainConfig.DAOForkSupport && w.chainConfig.DAOForkBlock != nil && w.chainConfig.DAOForkBlock.Cmp(header.Number) == 0 { if w.chainConfig.DAOForkSupport && w.chainConfig.DAOForkBlock != nil && w.chainConfig.DAOForkBlock.Cmp(header.Number) == 0 {
misc.ApplyDAOHardFork(env.state) misc.ApplyDAOHardFork(env.state)
} }
if w.chainConfig.CurieBlock != nil && w.chainConfig.CurieBlock.Cmp(header.Number) == 0 {
misc.ApplyCurieHardFork(env.state)
// zkEVM requirement: Curie transition block has 0 transactions
w.commit(nil, w.fullTaskHook, true, tstart)
return
}
// Accumulate the uncles for the current block // Accumulate the uncles for the current block
uncles := make([]*types.Header, 0, 2) uncles := make([]*types.Header, 0, 2)
commitUncles := func(blocks map[common.Hash]*types.Block) { commitUncles := func(blocks map[common.Hash]*types.Block) {

View file

@ -24,7 +24,7 @@ import (
const ( const (
VersionMajor = 5 // Major version component of the current release VersionMajor = 5 // Major version component of the current release
VersionMinor = 3 // Minor version component of the current release VersionMinor = 3 // Minor version component of the current release
VersionPatch = 26 // Patch version component of the current release VersionPatch = 27 // Patch version component of the current release
VersionMeta = "mainnet" // Version metadata to append to the version string VersionMeta = "mainnet" // Version metadata to append to the version string
) )

View file

@ -43,12 +43,21 @@ type StateDB interface {
GetBalance(addr common.Address) *big.Int GetBalance(addr common.Address) *big.Int
} }
func EstimateL1DataFeeForMessage(msg Message, baseFee, chainID *big.Int, signer types.Signer, state StateDB) (*big.Int, error) { type gpoState struct {
l1BaseFee *big.Int
overhead *big.Int
scalar *big.Int
l1BlobBaseFee *big.Int
commitScalar *big.Int
blobScalar *big.Int
}
func EstimateL1DataFeeForMessage(msg Message, baseFee *big.Int, config *params.ChainConfig, signer types.Signer, state StateDB, blockNumber *big.Int) (*big.Int, error) {
if msg.IsL1MessageTx() { if msg.IsL1MessageTx() {
return big.NewInt(0), nil return big.NewInt(0), nil
} }
unsigned := asUnsignedTx(msg, baseFee, chainID) unsigned := asUnsignedTx(msg, baseFee, config.ChainID)
// with v=1 // with v=1
tx, err := unsigned.WithSignature(signer, append(bytes.Repeat([]byte{0xff}, crypto.SignatureLength-1), 0x01)) tx, err := unsigned.WithSignature(signer, append(bytes.Repeat([]byte{0xff}, crypto.SignatureLength-1), 0x01))
if err != nil { if err != nil {
@ -60,8 +69,16 @@ func EstimateL1DataFeeForMessage(msg Message, baseFee, chainID *big.Int, signer
return nil, err return nil, err
} }
l1BaseFee, overhead, scalar := readGPOStorageSlots(rcfg.L1GasPriceOracleAddress, state) gpoState := readGPOStorageSlots(rcfg.L1GasPriceOracleAddress, state)
l1DataFee := calculateEncodedL1DataFee(raw, overhead, l1BaseFee, scalar)
var l1DataFee *big.Int
if !config.IsCurie(blockNumber) {
l1DataFee = calculateEncodedL1DataFee(raw, gpoState.overhead, gpoState.l1BaseFee, gpoState.scalar)
} else {
l1DataFee = calculateEncodedL1DataFeeCurie(raw, gpoState.l1BaseFee, gpoState.l1BlobBaseFee, gpoState.commitScalar, gpoState.blobScalar)
}
return l1DataFee, nil return l1DataFee, nil
} }
@ -126,25 +143,46 @@ func rlpEncode(tx *types.Transaction) ([]byte, error) {
return raw.Bytes(), nil return raw.Bytes(), nil
} }
func readGPOStorageSlots(addr common.Address, state StateDB) (*big.Int, *big.Int, *big.Int) { func readGPOStorageSlots(addr common.Address, state StateDB) gpoState {
l1BaseFee := state.GetState(addr, rcfg.L1BaseFeeSlot) var gpoState gpoState
overhead := state.GetState(addr, rcfg.OverheadSlot) gpoState.l1BaseFee = state.GetState(addr, rcfg.L1BaseFeeSlot).Big()
scalar := state.GetState(addr, rcfg.ScalarSlot) gpoState.overhead = state.GetState(addr, rcfg.OverheadSlot).Big()
return l1BaseFee.Big(), overhead.Big(), scalar.Big() gpoState.scalar = state.GetState(addr, rcfg.ScalarSlot).Big()
gpoState.l1BlobBaseFee = state.GetState(addr, rcfg.L1BlobBaseFeeSlot).Big()
gpoState.commitScalar = state.GetState(addr, rcfg.CommitScalarSlot).Big()
gpoState.blobScalar = state.GetState(addr, rcfg.BlobScalarSlot).Big()
return gpoState
} }
// calculateEncodedL1DataFee computes the L1 fee for an RLP-encoded tx // calculateEncodedL1DataFee computes the L1 fee for an RLP-encoded tx
func calculateEncodedL1DataFee(data []byte, overhead, l1GasPrice *big.Int, scalar *big.Int) *big.Int { func calculateEncodedL1DataFee(data []byte, overhead, l1BaseFee *big.Int, scalar *big.Int) *big.Int {
l1GasUsed := CalculateL1GasUsed(data, overhead) l1GasUsed := calculateL1GasUsed(data, overhead)
l1DataFee := new(big.Int).Mul(l1GasUsed, l1GasPrice) l1DataFee := new(big.Int).Mul(l1GasUsed, l1BaseFee)
return mulAndScale(l1DataFee, scalar, rcfg.Precision) return mulAndScale(l1DataFee, scalar, rcfg.Precision)
} }
// CalculateL1GasUsed computes the L1 gas used based on the calldata and // calculateEncodedL1DataFeeCurie computes the L1 fee for an RLP-encoded tx, post Curie
func calculateEncodedL1DataFeeCurie(data []byte, l1BaseFee *big.Int, l1BlobBaseFee *big.Int, commitScalar *big.Int, blobScalar *big.Int) *big.Int {
// calldata component of commit fees (calldata gas + execution)
calldataGas := new(big.Int).Mul(commitScalar, l1BaseFee)
// blob component of commit fees
blobGas := big.NewInt(int64(len(data)))
blobGas = new(big.Int).Mul(blobGas, l1BlobBaseFee)
blobGas = new(big.Int).Mul(blobGas, blobScalar)
// combined
l1DataFee := new(big.Int).Add(calldataGas, blobGas)
l1DataFee = new(big.Int).Quo(l1DataFee, rcfg.Precision)
return l1DataFee
}
// calculateL1GasUsed computes the L1 gas used based on the calldata and
// constant sized overhead. The overhead can be decreased as the cost of the // constant sized overhead. The overhead can be decreased as the cost of the
// batch submission goes down via contract optimizations. This will not overflow // batch submission goes down via contract optimizations. This will not overflow
// under standard network conditions. // under standard network conditions.
func CalculateL1GasUsed(data []byte, overhead *big.Int) *big.Int { func calculateL1GasUsed(data []byte, overhead *big.Int) *big.Int {
zeroes, ones := zeroesAndOnes(data) zeroes, ones := zeroesAndOnes(data)
zeroesGas := zeroes * params.TxDataZeroGas zeroesGas := zeroes * params.TxDataZeroGas
onesGas := (ones + txExtraDataBytes) * params.TxDataNonZeroGasEIP2028 onesGas := (ones + txExtraDataBytes) * params.TxDataNonZeroGasEIP2028
@ -173,7 +211,7 @@ func mulAndScale(x *big.Int, y *big.Int, precision *big.Int) *big.Int {
return new(big.Int).Quo(z, precision) return new(big.Int).Quo(z, precision)
} }
func CalculateL1DataFee(tx *types.Transaction, state StateDB) (*big.Int, error) { func CalculateL1DataFee(tx *types.Transaction, state StateDB, config *params.ChainConfig, blockNumber *big.Int) (*big.Int, error) {
if tx.IsL1MessageTx() { if tx.IsL1MessageTx() {
return big.NewInt(0), nil return big.NewInt(0), nil
} }
@ -183,8 +221,16 @@ func CalculateL1DataFee(tx *types.Transaction, state StateDB) (*big.Int, error)
return nil, err return nil, err
} }
l1BaseFee, overhead, scalar := readGPOStorageSlots(rcfg.L1GasPriceOracleAddress, state) gpoState := readGPOStorageSlots(rcfg.L1GasPriceOracleAddress, state)
l1DataFee := calculateEncodedL1DataFee(raw, overhead, l1BaseFee, scalar)
var l1DataFee *big.Int
if !config.IsCurie(blockNumber) {
l1DataFee = calculateEncodedL1DataFee(raw, gpoState.overhead, gpoState.l1BaseFee, gpoState.scalar)
} else {
l1DataFee = calculateEncodedL1DataFeeCurie(raw, gpoState.l1BaseFee, gpoState.l1BlobBaseFee, gpoState.commitScalar, gpoState.blobScalar)
}
return l1DataFee, nil return l1DataFee, nil
} }

View file

@ -7,14 +7,27 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestCalculateEncodedL1DataFee(t *testing.T) { func TestL1DataFeeBeforeCurie(t *testing.T) {
l1BaseFee := new(big.Int).SetUint64(15000000) l1BaseFee := new(big.Int).SetUint64(15000000)
data := []byte{0, 10, 1, 0}
overhead := new(big.Int).SetUint64(100) overhead := new(big.Int).SetUint64(100)
scalar := new(big.Int).SetUint64(10) scalar := new(big.Int).SetUint64(10)
expected := new(big.Int).SetUint64(184) // 184.2 data := []byte{0, 10, 1, 0}
expected := new(big.Int).SetUint64(30) // 30.6
actual := calculateEncodedL1DataFee(data, overhead, l1BaseFee, scalar) actual := calculateEncodedL1DataFee(data, overhead, l1BaseFee, scalar)
assert.Equal(t, expected, actual) assert.Equal(t, expected, actual)
} }
func TestL1DataFeeAfterCurie(t *testing.T) {
l1BaseFee := new(big.Int).SetUint64(1500000000)
l1BlobBaseFee := new(big.Int).SetUint64(150000000)
commitScalar := new(big.Int).SetUint64(10)
blobScalar := new(big.Int).SetUint64(10)
data := []byte{0, 10, 1, 0}
expected := new(big.Int).SetUint64(21)
actual := calculateEncodedL1DataFeeCurie(data, l1BaseFee, l1BlobBaseFee, commitScalar, blobScalar)
assert.Equal(t, expected, actual)
}

View file

@ -51,7 +51,7 @@ type Pipeline struct {
parent *types.Block parent *types.Block
start time.Time start time.Time
// accumalators // accumulators
ccc *circuitcapacitychecker.CircuitCapacityChecker ccc *circuitcapacitychecker.CircuitCapacityChecker
Header types.Header Header types.Header
state *state.StateDB state *state.StateDB

File diff suppressed because one or more lines are too long

View file

@ -229,7 +229,7 @@ func (env *TraceEnv) GetBlockTrace(block *types.Block) (*types.BlockTrace, error
msg, _ := tx.AsMessage(env.signer, block.BaseFee()) msg, _ := tx.AsMessage(env.signer, block.BaseFee())
env.state.SetTxContext(tx.Hash(), i) env.state.SetTxContext(tx.Hash(), i)
vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), env.state, env.chainConfig, vm.Config{}) vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), env.state, env.chainConfig, vm.Config{})
l1DataFee, err := fees.CalculateL1DataFee(tx, env.state) l1DataFee, err := fees.CalculateL1DataFee(tx, env.state, env.chainConfig, block.Number())
if err != nil { if err != nil {
failed = err failed = err
break break
@ -335,7 +335,7 @@ func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.B
state.SetTxContext(txctx.TxHash, txctx.TxIndex) state.SetTxContext(txctx.TxHash, txctx.TxIndex)
// Computes the new state by applying the given message. // Computes the new state by applying the given message.
l1DataFee, err := fees.CalculateL1DataFee(tx, state) l1DataFee, err := fees.CalculateL1DataFee(tx, state, env.chainConfig, block.Number())
if err != nil { if err != nil {
return err return err
} }
@ -530,6 +530,10 @@ func (env *TraceEnv) fillBlockTrace(block *types.Block) (*types.BlockTrace, erro
rcfg.L1BaseFeeSlot, rcfg.L1BaseFeeSlot,
rcfg.OverheadSlot, rcfg.OverheadSlot,
rcfg.ScalarSlot, rcfg.ScalarSlot,
rcfg.L1BlobBaseFeeSlot,
rcfg.CommitScalarSlot,
rcfg.BlobScalarSlot,
rcfg.IsCurieSlot,
}, },
} }

View file

@ -227,7 +227,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
gaspool := new(core.GasPool) gaspool := new(core.GasPool)
gaspool.AddGas(block.GasLimit()) gaspool.AddGas(block.GasLimit())
l1DataFee, err := fees.CalculateL1DataFee(&ttx, statedb) l1DataFee, err := fees.CalculateL1DataFee(&ttx, statedb, config, block.Number())
if err != nil { if err != nil {
return nil, nil, common.Hash{}, err return nil, nil, common.Hash{}, err
} }