From a43881de94eee9b88f8086f821e761e02140dd0d Mon Sep 17 00:00:00 2001 From: colin <102356659+colinlyguo@users.noreply.github.com> Date: Thu, 19 Jun 2025 22:24:05 +0800 Subject: [PATCH 1/4] fix(rollup-verifier): update da-codec dependency (#1207) * fix(rollup-verifier): update da-codec dependency * bump version --- go.mod | 2 +- go.sum | 4 ++-- params/version.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 1c3397b960..ca5088a456 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( github.com/prometheus/tsdb v0.7.1 github.com/rjeczalik/notify v0.9.1 github.com/rs/cors v1.7.0 - github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1 + github.com/scroll-tech/da-codec v0.1.3-0.20250519114140-bfa7133d4ad1 github.com/scroll-tech/zktrie v0.8.4 github.com/shirou/gopsutil v3.21.11+incompatible github.com/sourcegraph/conc v0.3.0 diff --git a/go.sum b/go.sum index a6c9f2ad2a..d13a6b3db1 100644 --- a/go.sum +++ b/go.sum @@ -396,8 +396,8 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1 h1:Dhd58LE1D+dnoxpgLVeQBMF9uweL/fhQfZHWtWSiOlE= -github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1/go.mod h1:yhTS9OVC0xQGhg7DN5iV5KZJvnSIlFWAxDdp+6jxQtY= +github.com/scroll-tech/da-codec v0.1.3-0.20250519114140-bfa7133d4ad1 h1:6aKqJSal+QVdB5HMWMs0JTbAIZ6/iAHJx9qizz0w9dU= +github.com/scroll-tech/da-codec v0.1.3-0.20250519114140-bfa7133d4ad1/go.mod h1:yhTS9OVC0xQGhg7DN5iV5KZJvnSIlFWAxDdp+6jxQtY= github.com/scroll-tech/zktrie v0.8.4 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE= github.com/scroll-tech/zktrie v0.8.4/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= diff --git a/params/version.go b/params/version.go index 01c2565f15..0a9f4e2b00 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 5 // Major version component of the current release VersionMinor = 8 // Minor version component of the current release - VersionPatch = 55 // Patch version component of the current release + VersionPatch = 56 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string ) From 02b33db0bfa5000c8e0df11c3966e622a33803b6 Mon Sep 17 00:00:00 2001 From: Jonas Theis <4181434+jonastheis@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:54:15 +0800 Subject: [PATCH 2/4] feat: add flags to disable broadcast and receiving of tx gossip (#1194) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add flags to disable broadcast and receiving of tx gossip respectively * chore: auto version bump [bot] --------- Co-authored-by: Morty <70688412+yiweichi@users.noreply.github.com> --- cmd/geth/main.go | 2 ++ cmd/geth/usage.go | 2 ++ cmd/utils/flags.go | 18 ++++++++++++++++++ eth/backend.go | 22 ++++++++++++---------- eth/ethconfig/config.go | 3 +++ eth/handler.go | 39 ++++++++++++++++++++++++--------------- eth/handler_eth.go | 3 +++ params/version.go | 2 +- 8 files changed, 65 insertions(+), 26 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index c1fbb08746..efffc9480a 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -176,6 +176,8 @@ var ( utils.CircuitCapacityCheckWorkersFlag, utils.RollupVerifyEnabledFlag, utils.ShadowforkPeersFlag, + utils.TxGossipBroadcastDisabledFlag, + utils.TxGossipReceivingDisabledFlag, utils.DASyncEnabledFlag, utils.DABlockNativeAPIEndpointFlag, utils.DABlobScanAPIEndpointFlag, diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 1d299d339f..60b7e5416b 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -248,6 +248,8 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.DARecoveryProduceBlocksFlag, utils.CircuitCapacityCheckEnabledFlag, utils.CircuitCapacityCheckWorkersFlag, + utils.TxGossipBroadcastDisabledFlag, + utils.TxGossipReceivingDisabledFlag, }, }, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 7371a37700..a2a1655502 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -893,6 +893,16 @@ var ( Usage: "peer ids of shadow fork peers", } + // Tx gossip settings + TxGossipBroadcastDisabledFlag = cli.BoolFlag{ + Name: "txgossip.disablebroadcast", + Usage: "Disable gossip broadcast transactions to other peers", + } + TxGossipReceivingDisabledFlag = cli.BoolFlag{ + Name: "txgossip.disablereceiving", + Usage: "Disable gossip receiving transactions from other peers", + } + // DA syncing settings DASyncEnabledFlag = cli.BoolFlag{ Name: "da.sync", @@ -1790,6 +1800,14 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.ShadowForkPeerIDs = ctx.GlobalStringSlice(ShadowforkPeersFlag.Name) log.Info("Shadow fork peers", "ids", cfg.ShadowForkPeerIDs) } + if ctx.GlobalIsSet(TxGossipBroadcastDisabledFlag.Name) { + cfg.TxGossipBroadcastDisabled = ctx.GlobalBool(TxGossipBroadcastDisabledFlag.Name) + log.Info("Transaction gossip broadcast disabled", "disabled", cfg.TxGossipBroadcastDisabled) + } + if ctx.GlobalIsSet(TxGossipReceivingDisabledFlag.Name) { + cfg.TxGossipReceivingDisabled = ctx.GlobalBool(TxGossipReceivingDisabledFlag.Name) + log.Info("Transaction gossip receiving disabled", "disabled", cfg.TxGossipReceivingDisabled) + } // Cap the cache allowance and tune the garbage collector mem, err := gopsutil.VirtualMemory() diff --git a/eth/backend.go b/eth/backend.go index ad765b835b..0050d3853b 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -273,16 +273,18 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client l1.Client) (*Ether checkpoint = params.TrustedCheckpoints[genesisHash] } if eth.handler, err = newHandler(&handlerConfig{ - Database: chainDb, - Chain: eth.blockchain, - TxPool: eth.txPool, - Network: config.NetworkId, - Sync: config.SyncMode, - BloomCache: uint64(cacheLimit), - EventMux: eth.eventMux, - Checkpoint: checkpoint, - Whitelist: config.Whitelist, - ShadowForkPeerIDs: config.ShadowForkPeerIDs, + Database: chainDb, + Chain: eth.blockchain, + TxPool: eth.txPool, + Network: config.NetworkId, + Sync: config.SyncMode, + BloomCache: uint64(cacheLimit), + EventMux: eth.eventMux, + Checkpoint: checkpoint, + Whitelist: config.Whitelist, + ShadowForkPeerIDs: config.ShadowForkPeerIDs, + DisableTxBroadcast: config.TxGossipBroadcastDisabled, + DisableTxReceiving: config.TxGossipReceivingDisabled, }); err != nil { return nil, err } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 94fa46c34c..0f70eb2ed6 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -230,6 +230,9 @@ type Config struct { // DA syncer options DA da_syncer.Config + + TxGossipBroadcastDisabled bool + TxGossipReceivingDisabled bool } // CreateConsensusEngine creates a consensus engine for the given chain configuration. diff --git a/eth/handler.go b/eth/handler.go index 4755a0c7b7..520723f18a 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -93,6 +93,9 @@ type handlerConfig struct { Checkpoint *params.TrustedCheckpoint // Hard coded checkpoint for sync challenges Whitelist map[uint64]common.Hash // Hard coded whitelist for sync challenged ShadowForkPeerIDs []string // List of peer ids that take part in the shadow-fork + + DisableTxBroadcast bool + DisableTxReceiving bool } type handler struct { @@ -131,7 +134,9 @@ type handler struct { wg sync.WaitGroup peerWG sync.WaitGroup - shadowForkPeerIDs []string + shadowForkPeerIDs []string + disableTxBroadcast bool + disableTxReceiving bool } // newHandler returns a handler for all Ethereum chain management protocol. @@ -141,16 +146,18 @@ func newHandler(config *handlerConfig) (*handler, error) { config.EventMux = new(event.TypeMux) // Nicety initialization for tests } h := &handler{ - networkID: config.Network, - forkFilter: forkid.NewFilter(config.Chain), - eventMux: config.EventMux, - database: config.Database, - txpool: config.TxPool, - chain: config.Chain, - peers: newPeerSet(), - whitelist: config.Whitelist, - quitSync: make(chan struct{}), - shadowForkPeerIDs: config.ShadowForkPeerIDs, + networkID: config.Network, + forkFilter: forkid.NewFilter(config.Chain), + eventMux: config.EventMux, + database: config.Database, + txpool: config.TxPool, + chain: config.Chain, + peers: newPeerSet(), + whitelist: config.Whitelist, + quitSync: make(chan struct{}), + shadowForkPeerIDs: config.ShadowForkPeerIDs, + disableTxBroadcast: config.DisableTxBroadcast, + disableTxReceiving: config.DisableTxReceiving, } if config.Sync == downloader.FullSync { // The database seems empty as the current block is the genesis. Yet the fast @@ -415,10 +422,12 @@ func (h *handler) Start(maxPeers int) { h.maxPeers = maxPeers // broadcast transactions - h.wg.Add(1) - h.txsCh = make(chan core.NewTxsEvent, txChanSize) - h.txsSub = h.txpool.SubscribeNewTxsEvent(h.txsCh) - go h.txBroadcastLoop() + if !h.disableTxBroadcast { + h.wg.Add(1) + h.txsCh = make(chan core.NewTxsEvent, txChanSize) + h.txsSub = h.txpool.SubscribeNewTxsEvent(h.txsCh) + go h.txBroadcastLoop() + } // broadcast mined blocks h.wg.Add(1) diff --git a/eth/handler_eth.go b/eth/handler_eth.go index 1a3aff8aa0..9fc494711f 100644 --- a/eth/handler_eth.go +++ b/eth/handler_eth.go @@ -56,6 +56,9 @@ func (h *ethHandler) PeerInfo(id enode.ID) interface{} { // AcceptTxs retrieves whether transaction processing is enabled on the node // or if inbound transactions should simply be dropped. func (h *ethHandler) AcceptTxs() bool { + if h.disableTxReceiving { + return false + } return atomic.LoadUint32(&h.acceptTxs) == 1 } diff --git a/params/version.go b/params/version.go index 0a9f4e2b00..f8f2cbeb11 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 5 // Major version component of the current release VersionMinor = 8 // Minor version component of the current release - VersionPatch = 56 // Patch version component of the current release + VersionPatch = 57 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string ) From a67863c65587d7324dba37a2ca3640223818a0f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 25 Jun 2025 13:22:25 +0200 Subject: [PATCH 3/4] feat(feynman): upgrade gas oracle predeploy (#1213) * feat(feynman): upgrade gas oracle predeploy * update execution witness * prettify constants * fix fork application order --- cmd/evm/internal/t8ntool/execution.go | 4 ++ consensus/misc/feynman.go | 22 ++++++++ core/chain_makers.go | 3 ++ core/state_processor.go | 5 ++ eth/api.go | 5 ++ miner/scroll_worker.go | 23 +++++---- params/config.go | 5 ++ params/version.go | 2 +- rollup/rcfg/config.go | 72 +++++++++++++++++++++------ 9 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 consensus/misc/feynman.go diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 922d10648e..2f237110dd 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -152,6 +152,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if chainConfig.CurieBlock != nil && chainConfig.CurieBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 { misc.ApplyCurieHardFork(statedb) } + // Apply Feynman hard fork + if chainConfig.IsFeynmanTransitionBlock(pre.Env.Timestamp, pre.Env.ParentTimestamp) { + misc.ApplyFeynmanHardFork(statedb) + } // Apply EIP-2935 if pre.Env.BlockHashes != nil && chainConfig.IsFeynman(pre.Env.Timestamp) { var ( diff --git a/consensus/misc/feynman.go b/consensus/misc/feynman.go new file mode 100644 index 0000000000..b933187cf9 --- /dev/null +++ b/consensus/misc/feynman.go @@ -0,0 +1,22 @@ +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" +) + +// ApplyFeynmanHardFork modifies the state database according to the Feynman hard-fork rules, +// updating the bytecode and storage of the L1GasPriceOracle contract. +func ApplyFeynmanHardFork(statedb *state.StateDB) { + log.Info("Applying Feynman hard fork") + + // update contract byte code + statedb.SetCode(rcfg.L1GasPriceOracleAddress, rcfg.FeynmanL1GasPriceOracleBytecode) + + // initialize new storage slots + statedb.SetState(rcfg.L1GasPriceOracleAddress, rcfg.IsFeynmanSlot, common.BytesToHash([]byte{1})) + statedb.SetState(rcfg.L1GasPriceOracleAddress, rcfg.PenaltyThresholdSlot, common.BigToHash(rcfg.InitialPenaltyThreshold)) + statedb.SetState(rcfg.L1GasPriceOracleAddress, rcfg.PenaltyFactorSlot, common.BigToHash(rcfg.InitialPenaltyFactor)) +} diff --git a/core/chain_makers.go b/core/chain_makers.go index 47a7d9fab8..b37ed9c3ee 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -249,6 +249,9 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse if config.CurieBlock != nil && config.CurieBlock.Cmp(b.header.Number) == 0 { misc.ApplyCurieHardFork(statedb) } + if config.IsFeynmanTransitionBlock(b.Time(), parent.Time()) { + misc.ApplyFeynmanHardFork(statedb) + } // Execute any user modifications to the block if gen != nil { gen(i, b) diff --git a/core/state_processor.go b/core/state_processor.go index ddbc9d43bd..d031bc848a 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -91,6 +91,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg if p.config.CurieBlock != nil && p.config.CurieBlock.Cmp(block.Number()) == 0 { misc.ApplyCurieHardFork(statedb) } + // Apply Feynman hard fork + parent := p.bc.GetHeaderByHash(block.ParentHash()) + if p.config.IsFeynmanTransitionBlock(block.Time(), parent.Time) { + misc.ApplyFeynmanHardFork(statedb) + } blockContext := NewEVMBlockContext(header, p.bc, p.config, nil) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) processorBlockTransactionGauge.Update(int64(block.Transactions().Len())) diff --git a/eth/api.go b/eth/api.go index 0752a3cb15..cac47bce52 100644 --- a/eth/api.go +++ b/eth/api.go @@ -362,6 +362,11 @@ func generateWitness(blockchain *core.BlockChain, block *types.Block) (*stateles // Collect storage locations that prover needs but sequencer might not touch necessarily statedb.GetState(rcfg.L2MessageQueueAddress, rcfg.WithdrawTrieRootSlot) + // Note: scroll-revm detects the Feynman transition block using this storage slot, + // since it does not have access to the parent block timestamp. We need to make + // sure that this is always present in the execution witness. + statedb.GetState(rcfg.L1GasPriceOracleAddress, rcfg.IsFeynmanSlot) + receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, *blockchain.GetVMConfig()) if err != nil { return nil, fmt.Errorf("failed to process block %d: %w", block.Number(), err) diff --git a/miner/scroll_worker.go b/miner/scroll_worker.go index 8af97f1cd7..56b458e4d5 100644 --- a/miner/scroll_worker.go +++ b/miner/scroll_worker.go @@ -464,8 +464,7 @@ func (w *worker) collectPendingL1Messages(startIndex uint64) []types.L1MessageTx } // newWork -func (w *worker) newWork(now time.Time, parentHash common.Hash, reorging bool, reorgReason error) error { - parent := w.chain.GetBlockByHash(parentHash) +func (w *worker) newWork(now time.Time, parent *types.Block, reorging bool, reorgReason error) error { header := &types.Header{ ParentHash: parent.Hash(), Number: new(big.Int).Add(parent.Number(), common.Big1), @@ -586,13 +585,14 @@ func (w *worker) newWork(now time.Time, parentHash common.Hash, reorging bool, r } // tryCommitNewWork -func (w *worker) tryCommitNewWork(now time.Time, parent common.Hash, reorging bool, reorgReason error) (common.Hash, error) { +func (w *worker) tryCommitNewWork(now time.Time, parentHash common.Hash, reorging bool, reorgReason error) (common.Hash, error) { + parent := w.chain.GetBlockByHash(parentHash) err := w.newWork(now, parent, reorging, reorgReason) if err != nil { return common.Hash{}, fmt.Errorf("failed creating new work: %w", err) } - shouldCommit, err := w.handleForks() + shouldCommit, err := w.handleForks(parent) if err != nil { return common.Hash{}, fmt.Errorf("failed handling forks: %w", err) } @@ -626,17 +626,16 @@ func (w *worker) tryCommitNewWork(now time.Time, parent common.Hash, reorging bo } // handleForks -func (w *worker) handleForks() (bool, error) { +func (w *worker) handleForks(parent *types.Block) (bool, error) { // Apply Curie predeployed contract update if w.chainConfig.CurieBlock != nil && w.chainConfig.CurieBlock.Cmp(w.current.header.Number) == 0 { misc.ApplyCurieHardFork(w.current.state) return true, nil } - // Stop/start miner at Euclid fork boundary on zktrie/mpt nodes - if w.chainConfig.IsEuclid(w.current.header.Time) { - parent := w.chain.GetBlockByHash(w.current.header.ParentHash) - return parent != nil && !w.chainConfig.IsEuclid(parent.Time()), nil + // Apply Feynman hard fork + if w.chainConfig.IsFeynmanTransitionBlock(w.current.header.Time, parent.Time()) { + misc.ApplyFeynmanHardFork(w.current.state) } // Apply EIP-2935 @@ -646,6 +645,12 @@ func (w *worker) handleForks() (bool, error) { core.ProcessParentBlockHash(w.current.header.ParentHash, vmenv, w.current.state) } + // Stop/start miner at Euclid fork boundary on zktrie/mpt nodes + if w.chainConfig.IsEuclid(w.current.header.Time) { + parent := w.chain.GetBlockByHash(w.current.header.ParentHash) + return parent != nil && !w.chainConfig.IsEuclid(parent.Time()), nil + } + return false, nil } diff --git a/params/config.go b/params/config.go index eda4340e80..d859f6384d 100644 --- a/params/config.go +++ b/params/config.go @@ -1002,6 +1002,11 @@ func (c *ChainConfig) IsFeynman(now uint64) bool { return isForkedTime(now, c.FeynmanTime) } +// IsFeynmanTransitionBlock returns whether the given block timestamp corresponds to the first Feynman block. +func (c *ChainConfig) IsFeynmanTransitionBlock(blockTimestamp uint64, parentTimestamp uint64) bool { + return isForkedTime(blockTimestamp, c.FeynmanTime) && !isForkedTime(parentTimestamp, c.FeynmanTime) +} + // IsTerminalPoWBlock returns whether the given block is the last block of PoW stage. func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *big.Int) bool { if c.TerminalTotalDifficulty == nil { diff --git a/params/version.go b/params/version.go index f8f2cbeb11..13fa655ad5 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 5 // Major version component of the current release VersionMinor = 8 // Minor version component of the current release - VersionPatch = 57 // Patch version component of the current release + VersionPatch = 58 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string ) diff --git a/rollup/rcfg/config.go b/rollup/rcfg/config.go index 3bac185c83..68626f4f86 100644 --- a/rollup/rcfg/config.go +++ b/rollup/rcfg/config.go @@ -6,9 +6,6 @@ import ( "github.com/scroll-tech/go-ethereum/common" ) -// TODO: -// verify in consensus layer when decentralizing sequencer - var ( // L2MessageQueueAddress is the address of the L2MessageQueue // predeploy @@ -20,24 +17,58 @@ var ( // predeploy // see scroll-tech/scroll/contracts/src/L2/predeploys/L2TxFeeVault.sol ScrollFeeVaultAddress = common.HexToAddress("0x5300000000000000000000000000000000000005") +) + +// L1GasPriceOracle constants. +var ( + // forge inspect src/L2/predeploys/L1GasPriceOracle.sol:L1GasPriceOracle storageLayout + // ╭------------------+---------------------+------+--------+-------╮ + // | Name | Type | Slot | Offset | Bytes | + // +================================================================+ + // | owner | address | 0 | 0 | 20 | + // |------------------+---------------------+------+--------+-------+ + // | l1BaseFee | uint256 | 1 | 0 | 32 | + // |------------------+---------------------+------+--------+-------+ + // | overhead | uint256 | 2 | 0 | 32 | + // |------------------+---------------------+------+--------+-------+ + // | scalar | uint256 | 3 | 0 | 32 | + // |------------------+---------------------+------+--------+-------+ + // | whitelist | contract IWhitelist | 4 | 0 | 20 | + // |------------------+---------------------+------+--------+-------+ + // | l1BlobBaseFee | uint256 | 5 | 0 | 32 | + // |------------------+---------------------+------+--------+-------+ + // | commitScalar | uint256 | 6 | 0 | 32 | + // |------------------+---------------------+------+--------+-------+ + // | blobScalar | uint256 | 7 | 0 | 32 | + // |------------------+---------------------+------+--------+-------+ + // | isCurie | bool | 8 | 0 | 1 | + // |------------------+---------------------+------+--------+-------+ + // | penaltyThreshold | uint256 | 9 | 0 | 32 | + // |------------------+---------------------+------+--------+-------+ + // | penaltyFactor | uint256 | 10 | 0 | 32 | + // |------------------+---------------------+------+--------+-------+ + // | isFeynman | bool | 11 | 0 | 1 | + // ╰------------------+---------------------+------+--------+-------╯ - // L1GasPriceOracleAddress is the address of the L1GasPriceOracle - // predeploy - // see scroll-tech/scroll/contracts/src/L2/predeploys/L1GasPriceOracle.sol L1GasPriceOracleAddress = common.HexToAddress("0x5300000000000000000000000000000000000002") Precision = new(big.Int).SetUint64(1e9) - L1BaseFeeSlot = common.BigToHash(big.NewInt(1)) - OverheadSlot = common.BigToHash(big.NewInt(2)) - ScalarSlot = common.BigToHash(big.NewInt(3)) - // New fields added in the Curie hard fork - L1BlobBaseFeeSlot = common.BigToHash(big.NewInt(5)) - CommitScalarSlot = common.BigToHash(big.NewInt(6)) - BlobScalarSlot = common.BigToHash(big.NewInt(7)) - IsCurieSlot = common.BigToHash(big.NewInt(8)) + // storage layout + L1BaseFeeSlot = common.BigToHash(big.NewInt(1)) + OverheadSlot = common.BigToHash(big.NewInt(2)) + ScalarSlot = common.BigToHash(big.NewInt(3)) + L1BlobBaseFeeSlot = common.BigToHash(big.NewInt(5)) // added in Curie upgrade + CommitScalarSlot = common.BigToHash(big.NewInt(6)) // added in Curie upgrade + BlobScalarSlot = common.BigToHash(big.NewInt(7)) // added in Curie upgrade + IsCurieSlot = common.BigToHash(big.NewInt(8)) // added in Curie upgrade + PenaltyThresholdSlot = common.BigToHash(big.NewInt(9)) // added in Feynman upgrade + PenaltyFactorSlot = common.BigToHash(big.NewInt(10)) // added in Feynman upgrade + IsFeynmanSlot = common.BigToHash(big.NewInt(11)) // added in Feynman upgrade - InitialCommitScalar = big.NewInt(230759955285) - InitialBlobScalar = big.NewInt(417565260) + InitialCommitScalar = big.NewInt(230759955285) + InitialBlobScalar = big.NewInt(417565260) + InitialPenaltyThreshold = new(big.Int).Set(Precision) // init to 1 + InitialPenaltyFactor = new(big.Int).Set(Precision) // init to 1 // CurieL1GasPriceOracleBytecode is the new (blob-enabled) gas price oracle after // the Curie hard fork. Run these commands in the monorepo to verify this bytecode: @@ -48,6 +79,15 @@ var ( // FOUNDRY_EVM_VERSION="cancun" forge build // cat artifacts/src/L1GasPriceOracle.sol/L1GasPriceOracle.json | jq -r .deployedBytecode.object CurieL1GasPriceOracleBytecode = common.Hex2Bytes("608060405234801561000f575f80fd5b5060043610610132575f3560e01c8063715018a6116100b4578063a911d77f11610079578063a911d77f1461024c578063bede39b514610254578063de26c4a114610267578063e88a60ad1461027a578063f2fde38b1461028d578063f45e65d8146102a0575f80fd5b8063715018a6146101eb57806384189161146101f35780638da5cb5b146101fc57806393e59dc114610226578063944b247f14610239575f80fd5b80633d0f963e116100fa5780633d0f963e146101a057806349948e0e146101b3578063519b4bd3146101c65780636a5e67e5146101cf57806370465597146101d8575f80fd5b80630c18c1621461013657806313dad5be1461015257806323e524ac1461016f5780633577afc51461017857806339455d3a1461018d575b5f80fd5b61013f60025481565b6040519081526020015b60405180910390f35b60085461015f9060ff1681565b6040519015158152602001610149565b61013f60065481565b61018b6101863660046109b3565b6102a9565b005b61018b61019b3660046109ca565b61033b565b61018b6101ae3660046109ea565b610438565b61013f6101c1366004610a2b565b6104bb565b61013f60015481565b61013f60075481565b61018b6101e63660046109b3565b6104e0565b61018b61056e565b61013f60055481565b5f5461020e906001600160a01b031681565b6040516001600160a01b039091168152602001610149565b60045461020e906001600160a01b031681565b61018b6102473660046109b3565b6105a2565b61018b61062e565b61018b6102623660046109b3565b61068a565b61013f610275366004610a2b565b610747565b61018b6102883660046109b3565b610764565b61018b61029b3660046109ea565b6107f0565b61013f60035481565b5f546001600160a01b031633146102db5760405162461bcd60e51b81526004016102d290610ad6565b60405180910390fd5b621c9c388111156102ff57604051635742c80560e11b815260040160405180910390fd5b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610382573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103a69190610b0d565b6103c3576040516326b3506d60e11b815260040160405180910390fd5b600182905560058190556040518281527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c449060200160405180910390a16040518181527f9a14bfb5d18c4c3cf14cae19c23d7cf1bcede357ea40ca1f75cd49542c71c214906020015b60405180910390a15050565b5f546001600160a01b031633146104615760405162461bcd60e51b81526004016102d290610ad6565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910161042c565b6008545f9060ff16156104d7576104d18261087b565b92915050565b6104d1826108c1565b5f546001600160a01b031633146105095760405162461bcd60e51b81526004016102d290610ad6565b610519633b9aca006103e8610b40565b81111561053957604051631e44fdeb60e11b815260040160405180910390fd5b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610330565b5f546001600160a01b031633146105975760405162461bcd60e51b81526004016102d290610ad6565b6105a05f610904565b565b5f546001600160a01b031633146105cb5760405162461bcd60e51b81526004016102d290610ad6565b6105d9633b9aca0080610b40565b8111156105f95760405163874f603160e01b815260040160405180910390fd5b60068190556040518181527f2ab3f5a4ebbcbf3c24f62f5454f52f10e1a8c9dcc5acac8f19199ce881a6a10890602001610330565b5f546001600160a01b031633146106575760405162461bcd60e51b81526004016102d290610ad6565b60085460ff161561067b576040516379f9c57560e01b815260040160405180910390fd5b6008805460ff19166001179055565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa1580156106d1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f59190610b0d565b610712576040516326b3506d60e11b815260040160405180910390fd5b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610330565b6008545f9060ff161561075b57505f919050565b6104d182610953565b5f546001600160a01b0316331461078d5760405162461bcd60e51b81526004016102d290610ad6565b61079b633b9aca0080610b40565b8111156107bb5760405163f37ec21560e01b815260040160405180910390fd5b60078190556040518181527f6b332a036d8c3ead57dcb06c87243bd7a2aed015ddf2d0528c2501dae56331aa90602001610330565b5f546001600160a01b031633146108195760405162461bcd60e51b81526004016102d290610ad6565b6001600160a01b03811661086f5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016102d2565b61087881610904565b50565b5f633b9aca0060055483516007546108939190610b40565b61089d9190610b40565b6001546006546108ad9190610b40565b6108b79190610b57565b6104d19190610b6a565b5f806108cc83610953565b90505f600154826108dd9190610b40565b9050633b9aca00600354826108f29190610b40565b6108fc9190610b6a565b949350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80515f908190815b818110156109a45784818151811061097557610975610b89565b01602001516001600160f81b0319165f036109955760048301925061099c565b6010830192505b60010161095b565b50506002540160400192915050565b5f602082840312156109c3575f80fd5b5035919050565b5f80604083850312156109db575f80fd5b50508035926020909101359150565b5f602082840312156109fa575f80fd5b81356001600160a01b0381168114610a10575f80fd5b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610a3b575f80fd5b813567ffffffffffffffff80821115610a52575f80fd5b818401915084601f830112610a65575f80fd5b813581811115610a7757610a77610a17565b604051601f8201601f19908116603f01168101908382118183101715610a9f57610a9f610a17565b81604052828152876020848701011115610ab7575f80fd5b826020860160208301375f928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b5f60208284031215610b1d575f80fd5b81518015158114610a10575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176104d1576104d1610b2c565b808201808211156104d1576104d1610b2c565b5f82610b8457634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603260045260245ffdfea26469706673582212200c2ac583f18be4f94ab169ae6f2ea3a708a7c0d4424746b120b177adb39e626064736f6c63430008180033") + + // FeynmanL1GasPriceOracleBytecode is the new (compression-penalty-enabled) gas price oracle after + // the Feynman hard fork. Run these commands in the scroll-contracts repo to verify this bytecode: + // + // git checkout 0f922b92675ddf0e8ec7f680de85f6d76f98c577 + // yarn + // forge build + // cat artifacts/src/L1GasPriceOracle.sol/L1GasPriceOracle.json | jq -r .deployedBytecode.object + FeynmanL1GasPriceOracleBytecode = common.Hex2Bytes("608060405234801561000f575f80fd5b50600436106101a1575f3560e01c806384189161116100f3578063c63b9e2d11610093578063e88a60ad1161006e578063e88a60ad1461032e578063f2fde38b14610341578063f45e65d814610354578063fe5b04151461035d575f80fd5b8063c63b9e2d146102ff578063c91e514914610312578063de26c4a11461031b575f80fd5b8063944b247f116100ce578063944b247f146102be578063a911d77f146102d1578063aa5e9334146102d9578063bede39b5146102ec575f80fd5b806384189161146102785780638da5cb5b1461028157806393e59dc1146102ab575f80fd5b80633d0f963e1161015e5780636112d6db116101395780636112d6db1461024b5780636a5e67e514610254578063704655971461025d578063715018a614610270575f80fd5b80633d0f963e1461021c57806349948e0e1461022f578063519b4bd314610242575f80fd5b80630c18c162146101a557806313dad5be146101c157806323e524ac146101de5780633577afc5146101e757806339455d3a146101fc5780633b7656bb1461020f575b5f80fd5b6101ae60025481565b6040519081526020015b60405180910390f35b6008546101ce9060ff1681565b60405190151581526020016101b8565b6101ae60065481565b6101fa6101f5366004610c73565b610365565b005b6101fa61020a366004610c8a565b6103f7565b600b546101ce9060ff1681565b6101fa61022a366004610caa565b6104f4565b6101ae61023d366004610ceb565b610577565b6101ae60015481565b6101ae600a5481565b6101ae60075481565b6101fa61026b366004610c73565b6105b0565b6101fa61063e565b6101ae60055481565b5f54610293906001600160a01b031681565b6040516001600160a01b0390911681526020016101b8565b600454610293906001600160a01b031681565b6101fa6102cc366004610c73565b610672565b6101fa6106fe565b6101fa6102e7366004610c73565b61075a565b6101fa6102fa366004610c73565b6107f4565b6101fa61030d366004610c73565b6108b1565b6101ae60095481565b6101ae610329366004610ceb565b61094a565b6101fa61033c366004610c73565b610974565b6101fa61034f366004610caa565b610a00565b6101ae60035481565b6101fa610a8b565b5f546001600160a01b031633146103975760405162461bcd60e51b815260040161038e90610d96565b60405180910390fd5b621c9c388111156103bb57604051635742c80560e11b815260040160405180910390fd5b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa15801561043e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104629190610dcd565b61047f576040516326b3506d60e11b815260040160405180910390fd5b600182905560058190556040518281527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c449060200160405180910390a16040518181527f9a14bfb5d18c4c3cf14cae19c23d7cf1bcede357ea40ca1f75cd49542c71c214906020015b60405180910390a15050565b5f546001600160a01b0316331461051d5760405162461bcd60e51b815260040161038e90610d96565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f791016104e8565b600b545f9060ff16156105935761058d82610ae7565b92915050565b60085460ff16156105a75761058d82610b45565b61058d82610b81565b5f546001600160a01b031633146105d95760405162461bcd60e51b815260040161038e90610d96565b6105e9633b9aca006103e8610e00565b81111561060957604051631e44fdeb60e11b815260040160405180910390fd5b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a906020016103ec565b5f546001600160a01b031633146106675760405162461bcd60e51b815260040161038e90610d96565b6106705f610bc4565b565b5f546001600160a01b0316331461069b5760405162461bcd60e51b815260040161038e90610d96565b6106a9633b9aca0080610e00565b8111156106c95760405163874f603160e01b815260040160405180910390fd5b60068190556040518181527f2ab3f5a4ebbcbf3c24f62f5454f52f10e1a8c9dcc5acac8f19199ce881a6a108906020016103ec565b5f546001600160a01b031633146107275760405162461bcd60e51b815260040161038e90610d96565b60085460ff161561074b576040516379f9c57560e01b815260040160405180910390fd5b6008805460ff19166001179055565b5f546001600160a01b031633146107835760405162461bcd60e51b815260040161038e90610d96565b633b9aca008110806107a1575061079e633b9aca0080610e00565b81115b156107bf5760405163d9b5dcdf60e01b815260040160405180910390fd5b60098190556040518181527fd50d3079c77df569cd58d55d4e5614bfe7066449009425d22bde8e75242f50bb906020016103ec565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa15801561083b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061085f9190610dcd565b61087c576040516326b3506d60e11b815260040160405180910390fd5b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c44906020016103ec565b5f546001600160a01b031633146108da5760405162461bcd60e51b815260040161038e90610d96565b633b9aca008110806108f857506108f5633b9aca0080610e00565b81115b156109155760405162ae184360e01b815260040160405180910390fd5b600a8190556040518181527f8647cebb7e57360673a28415c0bed2f68c42a86c5035f1c9b2eda2b09509288a906020016103ec565b600b545f9060ff168061095f575060085460ff165b1561096b57505f919050565b61058d82610c13565b5f546001600160a01b0316331461099d5760405162461bcd60e51b815260040161038e90610d96565b6109ab633b9aca0080610e00565b8111156109cb5760405163f37ec21560e01b815260040160405180910390fd5b60078190556040518181527f6b332a036d8c3ead57dcb06c87243bd7a2aed015ddf2d0528c2501dae56331aa906020016103ec565b5f546001600160a01b03163314610a295760405162461bcd60e51b815260040161038e90610d96565b6001600160a01b038116610a7f5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f2061646472657373000000604482015260640161038e565b610a8881610bc4565b50565b5f546001600160a01b03163314610ab45760405162461bcd60e51b815260040161038e90610d96565b600b5460ff1615610ad857604051631a7c228b60e21b815260040160405180910390fd5b600b805460ff19166001179055565b5f633b9aca0080600a548451600554600754610b039190610e00565b600154600654610b139190610e00565b610b1d9190610e17565b610b279190610e00565b610b319190610e00565b610b3b9190610e2a565b61058d9190610e2a565b5f633b9aca006005548351600754610b5d9190610e00565b610b679190610e00565b600154600654610b779190610e00565b610b3b9190610e17565b5f80610b8c83610c13565b90505f60015482610b9d9190610e00565b9050633b9aca0060035482610bb29190610e00565b610bbc9190610e2a565b949350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80515f908190815b81811015610c6457848181518110610c3557610c35610e49565b01602001516001600160f81b0319165f03610c5557600483019250610c5c565b6010830192505b600101610c1b565b50506002540160400192915050565b5f60208284031215610c83575f80fd5b5035919050565b5f8060408385031215610c9b575f80fd5b50508035926020909101359150565b5f60208284031215610cba575f80fd5b81356001600160a01b0381168114610cd0575f80fd5b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610cfb575f80fd5b813567ffffffffffffffff80821115610d12575f80fd5b818401915084601f830112610d25575f80fd5b813581811115610d3757610d37610cd7565b604051601f8201601f19908116603f01168101908382118183101715610d5f57610d5f610cd7565b81604052828152876020848701011115610d77575f80fd5b826020860160208301375f928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b5f60208284031215610ddd575f80fd5b81518015158114610cd0575f80fd5b634e487b7160e01b5f52601160045260245ffd5b808202811582820484141761058d5761058d610dec565b8082018082111561058d5761058d610dec565b5f82610e4457634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603260045260245ffdfea164736f6c6343000818000a") ) // L2SystemConfig constants. From c0d89415da01d365e72d8c4a6108ad8dc5136a34 Mon Sep 17 00:00:00 2001 From: Alejandro Ranchal-Pedrosa Date: Thu, 26 Jun 2025 12:08:40 +0200 Subject: [PATCH 4/4] feat(feynman): add new compression-aware rollup fee formula (#1196) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change l1DataFee to rollupFee for Feynman * Update CommitScalarSlot to ExecScalarSlot * Rename l1DataFee to rollupFee * Scale compression ratio to match scalars precision * Use l1DataFee and commitScalar instead of rollupFee and execScalar * Use blocktime not blocknumber * Add unit test * Clarify test comment * feat(feynman): upgrade gas oracle predeploy * address comments * revert renaming CalculateL1DataFee to CalculateRollupFee * simplify EstimateL1DataFeeForMessage * address comments * add a comment based on pr reviews * update formula * tweaks * tweak comments * Estimate compression ratio at rollupFee for Feynman (#1197) * Estimate compression ratio * Update rollup/fees/rollup_fee.go Co-authored-by: Péter Garamvölgyi * Check compressed size smaller and remove unnecessary checks/changes * goimports locally run * rollback format changes * update da-codec's zstd function * apply zstd * tweaks * tweak unit test * make sure compression ratio >= 1 * nit --------- Co-authored-by: Péter Garamvölgyi Co-authored-by: colinlyguo Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com> * refactoring * remove incorrect merge artifact * error handling * update da-codec commit * update da-codec commit --------- Co-authored-by: Péter Garamvölgyi Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com> Co-authored-by: colinlyguo --- accounts/abi/bind/backends/simulated.go | 2 +- cmd/evm/internal/t8ntool/execution.go | 2 +- core/state_prefetcher.go | 2 +- core/state_processor.go | 9 +- core/tx_list.go | 4 +- core/tx_list_test.go | 4 +- core/tx_pool.go | 12 +- eth/state_accessor.go | 2 +- eth/tracers/api.go | 14 +- eth/tracers/api_test.go | 2 +- .../internal/tracetest/calltrace_test.go | 6 +- eth/tracers/tracers_test.go | 2 +- go.mod | 2 +- go.sum | 4 +- internal/ethapi/api.go | 4 +- les/odr_test.go | 4 +- les/state_accessor.go | 2 +- light/odr_test.go | 2 +- light/txpool.go | 4 +- rollup/fees/rollup_fee.go | 152 +++++++++++++++--- rollup/fees/rollup_fee_test.go | 108 +++++++++++++ rollup/tracing/tracing.go | 4 +- tests/state_test_util.go | 2 +- 23 files changed, 282 insertions(+), 67 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 0721676991..a4f51d425b 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -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}) gasPool := new(core.GasPool).AddGas(math.MaxUint64) signer := types.MakeSigner(b.blockchain.Config(), head.Number, head.Time) - l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, head.BaseFee, b.blockchain.Config(), signer, stateDB, head.Number) + l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, head.BaseFee, b.blockchain.Config(), signer, stateDB, head.Number, head.Time) if err != nil { return nil, err } diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 2f237110dd..ff4d50810b 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -184,7 +184,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, snapshot := statedb.Snapshot() evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, chainConfig, new(big.Int).SetUint64(pre.Env.Number)) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) if err != nil { 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()}) diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 7cdbf1dde6..5f31c02185 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -71,7 +71,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c } statedb.SetTxContext(tx.Hash(), i) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, p.config, block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, p.config, block.Number(), block.Time()) if err != nil { return } diff --git a/core/state_processor.go b/core/state_processor.go index d031bc848a..aea19a4e04 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -80,6 +80,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg header = block.Header() blockHash = block.Hash() blockNumber = block.Number() + blockTime = block.Time() allLogs []*types.Log gp = new(GasPool).AddGas(block.GasLimit()) ) @@ -110,7 +111,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.SetTxContext(tx.Hash(), i) - receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv) + receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockTime, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -125,7 +126,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return receipts, allLogs, *usedGas, nil } -func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { +func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockTime uint64, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { defer func(t0 time.Time) { applyTransactionTimer.Update(time.Since(t0)) }(time.Now()) @@ -134,7 +135,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, config, blockNumber) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, config, blockNumber, blockTime) if err != nil { return nil, err } @@ -206,7 +207,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, config, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) - return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) + return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Time, header.Hash(), tx, usedGas, vmenv) } // ProcessParentBlockHash stores the parent block hash in the history storage contract diff --git a/core/tx_list.go b/core/tx_list.go index 73d646a870..fba3b57f0b 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -282,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 // thresholds are also potentially updated. -func (l *txList) Add(tx *types.Transaction, state *state.StateDB, priceBump uint64, chainconfig *params.ChainConfig, blockNumber *big.Int) (bool, *types.Transaction) { +func (l *txList) Add(tx *types.Transaction, state *state.StateDB, priceBump uint64, chainconfig *params.ChainConfig, blockNumber *big.Int, blockTime uint64) (bool, *types.Transaction) { // If there's an older better transaction, abort old := l.txs.Get(tx.Nonce()) if old != nil { @@ -310,7 +310,7 @@ func (l *txList) Add(tx *types.Transaction, state *state.StateDB, priceBump uint l1DataFee := big.NewInt(0) if state != nil && chainconfig != nil { var err error - l1DataFee, err = fees.CalculateL1DataFee(tx, state, chainconfig, blockNumber) + l1DataFee, err = fees.CalculateL1DataFee(tx, state, chainconfig, blockNumber, blockTime) if err != nil { log.Error("Failed to calculate L1 data fee", "err", err, "tx", tx) return false, nil diff --git a/core/tx_list_test.go b/core/tx_list_test.go index 9b1cab4c40..f0f0d9dcc0 100644 --- a/core/tx_list_test.go +++ b/core/tx_list_test.go @@ -38,7 +38,7 @@ func TestStrictTxListAdd(t *testing.T) { // Insert the transactions in a random order list := newTxList(true) for _, v := range rand.Perm(len(txs)) { - list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump, nil, nil) + list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump, nil, nil, 0) } // Verify internal state if len(list.txs.items) != len(txs) { @@ -65,7 +65,7 @@ func BenchmarkTxListAdd(b *testing.B) { for i := 0; i < b.N; i++ { list := newTxList(true) for _, v := range rand.Perm(len(txs)) { - list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump, nil, nil) + list.Add(txs[v], nil, DefaultTxPoolConfig.PriceBump, nil, nil, 0) list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump) } } diff --git a/core/tx_pool.go b/core/tx_pool.go index 3d49800e01..52f4b040d4 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -301,6 +301,7 @@ type TxPool struct { currentState *state.StateDB // Current state in the blockchain head currentHead *big.Int // Current blockchain head + currentTime uint64 // Current blockchain head time pendingNonces *txNoncer // Pending state tracking virtual nonces currentMaxGas uint64 // Current gas limit for transaction caps @@ -838,7 +839,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { // 2. If FeeVault is enabled, perform an additional check for L1 data fees. if pool.chainconfig.Scroll.FeeVaultEnabled() { // Get L1 data fee in current state - l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState, pool.chainconfig, pool.currentHead) + l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState, pool.chainconfig, pool.currentHead, pool.currentTime) if err != nil { return fmt.Errorf("failed to calculate L1 data fee, err: %w", err) } @@ -947,7 +948,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e from, _ := types.Sender(pool.signer, tx) // already validated if list := pool.pending[from]; list != nil && list.Overlaps(tx) { // Nonce already pending, check if required price bump is met - inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead) + inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead, pool.currentTime) if !inserted { log.Debug("Discarding underpriced pending transaction", "hash", hash.Hex(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) pendingDiscardMeter.Mark(1) @@ -1001,7 +1002,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local boo pool.queue[from] = newTxList(false) } - inserted, old := pool.queue[from].Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead) + inserted, old := pool.queue[from].Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead, pool.currentTime) if !inserted { // An older transaction was better, discard this log.Debug("Discarding underpriced queued transaction", "hash", hash.Hex(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) @@ -1068,7 +1069,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T return false } - inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead) + inserted, old := list.Add(tx, pool.currentState, pool.config.PriceBump, pool.chainconfig, pool.currentHead, pool.currentTime) if !inserted { // An older transaction was better, discard this log.Debug("Discarding underpriced pending transaction", "hash", hash.Hex(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) @@ -1603,6 +1604,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { // Update current head pool.currentHead = next + pool.currentTime = newHead.Time } // promoteExecutables moves transactions that have become processable from the @@ -1690,7 +1692,7 @@ func (pool *TxPool) executableTxFilter(costLimit *big.Int) func(tx *types.Transa if pool.chainconfig.Scroll.FeeVaultEnabled() { // recheck L1 data fee, as the oracle price may have changed - l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState, pool.chainconfig, pool.currentHead) + l1DataFee, err := fees.CalculateL1DataFee(tx, pool.currentState, pool.chainconfig, pool.currentHead, pool.currentTime) if err != nil { log.Error("Failed to calculate L1 data fee", "err", err, "tx", tx) return false diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 13b80ded03..f4a75fc594 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -198,7 +198,7 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{}) statedb.SetTxContext(tx.Hash(), idx) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, eth.blockchain.Config(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, eth.blockchain.Config(), block.Number(), block.Time()) if err != nil { return nil, vm.BlockContext{}, nil, err } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 7b66326c09..cdfa276cba 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -292,7 +292,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config TxHash: tx.Hash(), } - l1DataFee, err := fees.CalculateL1DataFee(tx, task.statedb, api.backend.ChainConfig(), task.block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, task.statedb, api.backend.ChainConfig(), task.block.Number(), task.block.Time()) if err != nil { // though it's not a "tracing error", we still need to put it here task.results[i] = &txTraceResult{Error: err.Error()} @@ -557,7 +557,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config ) statedb.SetTxContext(tx.Hash(), i) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number(), block.Time()) if err != nil { log.Warn("Tracing intermediate roots did not complete due to fees.CalculateL1DataFee", "txindex", i, "txhash", tx.Hash(), "err", err) return nil, err @@ -645,7 +645,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac TxHash: txs[task.index].Hash(), } - l1DataFee, err := fees.CalculateL1DataFee(txs[task.index], task.statedb, api.backend.ChainConfig(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(txs[task.index], task.statedb, api.backend.ChainConfig(), block.Number(), block.Time()) if err != nil { // though it's not a "tracing error", we still need to put it here results[task.index] = &txTraceResult{ @@ -679,7 +679,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac msg, _ := tx.AsMessage(signer, block.BaseFee()) statedb.SetTxContext(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number(), block.Time()) if err != nil { failed = err break @@ -804,7 +804,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) statedb.SetTxContext(tx.Hash(), i) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number(), block.Time()) if err == nil { _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee) } @@ -869,7 +869,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * TxIndex: int(index), TxHash: hash, } - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, api.backend.ChainConfig(), block.Number(), block.Time()) if err != nil { return nil, err } @@ -929,7 +929,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } signer := types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) - l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, block.BaseFee(), api.backend.ChainConfig(), signer, statedb, block.Number()) + l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, block.BaseFee(), api.backend.ChainConfig(), signer, statedb, block.Number(), block.Time()) if err != nil { return nil, err } diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index d49e7df80a..47b8b4c6ac 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -173,7 +173,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block return msg, context, statedb, nil } vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{}) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, b.chainConfig, block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, b.chainConfig, block.Number(), block.Time()) if err != nil { return nil, vm.BlockContext{}, nil, err } diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index c3d8bfff39..5ee8905fa4 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -193,7 +193,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, test.Genesis.Config, context.BlockNumber) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, test.Genesis.Config, context.BlockNumber, context.Time.Uint64()) if err != nil { 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}) snap := statedb.Snapshot() - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, test.Genesis.Config, context.BlockNumber) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, test.Genesis.Config, context.BlockNumber, context.Time.Uint64()) if err != nil { b.Fatalf("failed to calculate l1DataFee: %v", err) } @@ -381,7 +381,7 @@ func TestZeroValueToNotExitCall(t *testing.T) { if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, params.MainnetChainConfig, context.BlockNumber) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, params.MainnetChainConfig, context.BlockNumber, context.Time.Uint64()) if err != nil { t.Fatalf("failed to calculate l1DataFee: %v", err) } diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 4a18a9245c..addd2b8cdb 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -112,7 +112,7 @@ func BenchmarkTransactionTrace(b *testing.B) { for i := 0; i < b.N; i++ { snap := statedb.Snapshot() - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, params.AllEthashProtocolChanges, context.BlockNumber) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, params.AllEthashProtocolChanges, context.BlockNumber, context.Time.Uint64()) if err != nil { b.Fatal(err) } diff --git a/go.mod b/go.mod index ca5088a456..9c432ada8f 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( github.com/prometheus/tsdb v0.7.1 github.com/rjeczalik/notify v0.9.1 github.com/rs/cors v1.7.0 - github.com/scroll-tech/da-codec v0.1.3-0.20250519114140-bfa7133d4ad1 + github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6 github.com/scroll-tech/zktrie v0.8.4 github.com/shirou/gopsutil v3.21.11+incompatible github.com/sourcegraph/conc v0.3.0 diff --git a/go.sum b/go.sum index d13a6b3db1..1bfb050837 100644 --- a/go.sum +++ b/go.sum @@ -396,8 +396,8 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/scroll-tech/da-codec v0.1.3-0.20250519114140-bfa7133d4ad1 h1:6aKqJSal+QVdB5HMWMs0JTbAIZ6/iAHJx9qizz0w9dU= -github.com/scroll-tech/da-codec v0.1.3-0.20250519114140-bfa7133d4ad1/go.mod h1:yhTS9OVC0xQGhg7DN5iV5KZJvnSIlFWAxDdp+6jxQtY= +github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6 h1:vb2XLvQwCf+F/ifP6P/lfeiQrHY6+Yb/E3R4KHXLqSE= +github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6/go.mod h1:Z6kN5u2khPhiqHyk172kGB7o38bH/nj7Ilrb/46wZGg= github.com/scroll-tech/zktrie v0.8.4 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE= github.com/scroll-tech/zktrie v0.8.4/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index d9978b7ac5..f41090a8c4 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -999,7 +999,7 @@ func EstimateL1MsgFee(ctx context.Context, b Backend, args TransactionArgs, bloc }() signer := types.MakeSigner(config, header.Number, header.Time) - return fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, evm.StateDB, header.Number) + return fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, evm.StateDB, header.Number, header.Time) } func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { @@ -1595,7 +1595,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH return nil, 0, nil, err } signer := types.MakeSigner(b.ChainConfig(), header.Number, header.Time) - l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, b.ChainConfig(), signer, statedb, header.Number) + l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, b.ChainConfig(), signer, statedb, header.Number, header.Time) if err != nil { return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err) } diff --git a/les/odr_test.go b/les/odr_test.go index e5ab5410f3..a8cee131b7 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -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{}) gp := new(core.GasPool).AddGas(math.MaxUint64) signer := types.MakeSigner(config, header.Number, header.Time) - l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, statedb, header.Number) + l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, statedb, header.Number, header.Time) result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee) 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}) gp := new(core.GasPool).AddGas(math.MaxUint64) signer := types.MakeSigner(config, header.Number, header.Time) - l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, state, header.Number) + l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, state, header.Number, header.Time) result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee) if state.Error() == nil { res = append(res, result.Return()...) diff --git a/les/state_accessor.go b/les/state_accessor.go index c0db2715f4..b2afa28938 100644 --- a/les/state_accessor.go +++ b/les/state_accessor.go @@ -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 vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{}) - l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, leth.blockchain.Config(), block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, leth.blockchain.Config(), block.Number(), block.Time()) if err != nil { return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } diff --git a/light/odr_test.go b/light/odr_test.go index a122207d22..ce5fae9146 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -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}) gp := new(core.GasPool).AddGas(math.MaxUint64) signer := types.MakeSigner(config, header.Number, header.Time) - l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, st, header.Number) + l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config, signer, st, header.Number, header.Time) result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee) res = append(res, result.Return()...) if st.Error() != nil { diff --git a/light/txpool.go b/light/txpool.go index ec311d927e..e366fdc963 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -74,6 +74,7 @@ type TxPool struct { shanghai bool // Fork indicator whether we are in the shanghai stage. currentHead *big.Int // Current blockchain head + currentTime uint64 // Current blockchain head time } // TxRelayBackend provides an interface to the mechanism that forwards transacions @@ -326,6 +327,7 @@ func (pool *TxPool) setNewHead(head *types.Header) { pool.shanghai = pool.config.IsShanghai(next) pool.currentHead = next + pool.currentTime = head.Time } // Stop stops the light transaction pool @@ -408,7 +410,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. if pool.config.Scroll.FeeVaultEnabled() { // Get L1 data fee in current state - l1DataFee, err := fees.CalculateL1DataFee(tx, currentState, pool.config, pool.currentHead) + l1DataFee, err := fees.CalculateL1DataFee(tx, currentState, pool.config, pool.currentHead, pool.currentTime) if err != nil { return fmt.Errorf("failed to calculate L1 data fee, err: %w", err) } diff --git a/rollup/fees/rollup_fee.go b/rollup/fees/rollup_fee.go index ac5fa9fa81..e6540bad19 100644 --- a/rollup/fees/rollup_fee.go +++ b/rollup/fees/rollup_fee.go @@ -2,14 +2,17 @@ package fees import ( "bytes" + "fmt" "math" "math/big" "github.com/holiman/uint256" + "github.com/scroll-tech/da-codec/encoding" "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/crypto" + "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/rollup/rcfg" ) @@ -54,15 +57,17 @@ type StateDB interface { } type gpoState struct { - l1BaseFee *big.Int - overhead *big.Int - scalar *big.Int - l1BlobBaseFee *big.Int - commitScalar *big.Int - blobScalar *big.Int + l1BaseFee *big.Int + overhead *big.Int + scalar *big.Int + l1BlobBaseFee *big.Int + commitScalar *big.Int + blobScalar *big.Int + penaltyThreshold *big.Int + penaltyFactor *big.Int } -func EstimateL1DataFeeForMessage(msg Message, baseFee *big.Int, config *params.ChainConfig, signer types.Signer, state StateDB, blockNumber *big.Int) (*big.Int, error) { +func EstimateL1DataFeeForMessage(msg Message, baseFee *big.Int, config *params.ChainConfig, signer types.Signer, state StateDB, blockNumber *big.Int, blockTime uint64) (*big.Int, error) { if msg.IsL1MessageTx() { return big.NewInt(0), nil } @@ -74,22 +79,7 @@ func EstimateL1DataFeeForMessage(msg Message, baseFee *big.Int, config *params.C return nil, err } - raw, err := tx.MarshalBinary() - if err != nil { - return nil, err - } - - gpoState := readGPOStorageSlots(rcfg.L1GasPriceOracleAddress, state) - - 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 CalculateL1DataFee(tx, state, config, blockNumber, blockTime) } // asUnsignedTx turns a Message into a types.Transaction @@ -173,9 +163,58 @@ func readGPOStorageSlots(addr common.Address, state StateDB) gpoState { gpoState.l1BlobBaseFee = state.GetState(addr, rcfg.L1BlobBaseFeeSlot).Big() gpoState.commitScalar = state.GetState(addr, rcfg.CommitScalarSlot).Big() gpoState.blobScalar = state.GetState(addr, rcfg.BlobScalarSlot).Big() + gpoState.penaltyThreshold = state.GetState(addr, rcfg.PenaltyThresholdSlot).Big() + gpoState.penaltyFactor = state.GetState(addr, rcfg.PenaltyFactorSlot).Big() return gpoState } +// estimateTxCompressionRatio estimates the compression ratio for transaction data using da-codec +// compression_ratio(tx) = size(tx) * PRECISION / size(zstd(tx)) +func estimateTxCompressionRatio(data []byte, blockNumber uint64, blockTime uint64, config *params.ChainConfig) (*big.Int, error) { + if len(data) == 0 { + return nil, fmt.Errorf("raw data is empty") + } + + // Compress data using da-codec + compressed, err := encoding.CompressScrollBatchBytes(data, blockNumber, blockTime, config) + if err != nil { + log.Error("Batch compression failed, using 1.0 compression ratio", "error", err, "data size", len(data), "data", common.Bytes2Hex(data)) + return nil, fmt.Errorf("batch compression failed: %w", err) + } + + if len(compressed) == 0 { + log.Error("Compressed data is empty, using 1.0 compression ratio", "data size", len(data), "data", common.Bytes2Hex(data)) + return nil, fmt.Errorf("compressed data is empty") + } + + // compression_ratio = size(tx) * PRECISION / size(zstd(tx)) + originalSize := new(big.Int).SetUint64(uint64(len(data))) + compressedSize := new(big.Int).SetUint64(uint64(len(compressed))) + + // Make sure compression ratio >= 1 by checking if compressed data is bigger or equal to original data + // This behavior is consistent with DA Batch compression in codecv7 and later versions + if len(compressed) >= len(data) { + log.Debug("Compressed data is bigger or equal to the original data, using 1.0 compression ratio", "original size", len(data), "compressed size", len(compressed)) + return rcfg.Precision, nil + } + + ratio := new(big.Int).Mul(originalSize, rcfg.Precision) + ratio.Div(ratio, compressedSize) + + return ratio, nil +} + +// calculatePenalty computes the penalty multiplier based on compression ratio +// penalty(tx) = compression_ratio(tx) >= penalty_threshold ? 1 * PRECISION : penalty_factor +func calculatePenalty(compressionRatio, penaltyThreshold, penaltyFactor *big.Int) *big.Int { + if compressionRatio.Cmp(penaltyThreshold) >= 0 { + // No penalty + return rcfg.Precision + } + // Apply penalty + return penaltyFactor +} + // calculateEncodedL1DataFee computes the L1 fee for an RLP-encoded tx func calculateEncodedL1DataFee(data []byte, overhead, l1BaseFee *big.Int, scalar *big.Int) *big.Int { l1GasUsed := calculateL1GasUsed(data, overhead) @@ -200,6 +239,51 @@ func calculateEncodedL1DataFeeCurie(data []byte, l1BaseFee *big.Int, l1BlobBaseF return l1DataFee } +// calculateEncodedL1DataFeeFeynman computes the L1 fee for an RLP-encoded tx, post Feynman +// +// Post Feynman formula: +// rollup_fee(tx) = (execScalar * l1BaseFee + blobScalar * l1BlobBaseFee) * size(tx) * penalty(tx) / PRECISION / PRECISION +// +// Where: +// penalty(tx) = compression_ratio(tx) >= penalty_threshold ? 1 * PRECISION : penalty_factor +// +// compression_ratio(tx) = size(tx) * PRECISION / size(zstd(tx)) +// exec_scalar = compression_scalar * (commit_scalar + verification_scalar) +// blob_scalar = compression_scalar * blob_scalar +func calculateEncodedL1DataFeeFeynman( + data []byte, + l1BaseFee *big.Int, + l1BlobBaseFee *big.Int, + execScalar *big.Int, + blobScalar *big.Int, + penaltyThreshold *big.Int, + penaltyFactor *big.Int, + compressionRatio *big.Int, +) *big.Int { + // Calculate penalty multiplier + penalty := calculatePenalty(compressionRatio, penaltyThreshold, penaltyFactor) + + // Transaction size (RLP-encoded) + txSize := big.NewInt(int64(len(data))) + + // Compute gas components + execGas := new(big.Int).Mul(execScalar, l1BaseFee) + blobGas := new(big.Int).Mul(blobScalar, l1BlobBaseFee) + + // fee per byte = execGas + blobGas + feePerByte := new(big.Int).Add(execGas, blobGas) + + // l1DataFee = feePerByte * txSize * penalty + l1DataFee := new(big.Int).Mul(feePerByte, txSize) + l1DataFee.Mul(l1DataFee, penalty) + + // Divide by rcfg.Precision (once for scalars, once for penalty) + l1DataFee.Div(l1DataFee, rcfg.Precision) // account for scalars + l1DataFee.Div(l1DataFee, rcfg.Precision) // accounts for penalty + + 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 // batch submission goes down via contract optimizations. This will not overflow @@ -233,7 +317,7 @@ func mulAndScale(x *big.Int, y *big.Int, precision *big.Int) *big.Int { return new(big.Int).Quo(z, precision) } -func CalculateL1DataFee(tx *types.Transaction, state StateDB, config *params.ChainConfig, blockNumber *big.Int) (*big.Int, error) { +func CalculateL1DataFee(tx *types.Transaction, state StateDB, config *params.ChainConfig, blockNumber *big.Int, blockTime uint64) (*big.Int, error) { if tx.IsL1MessageTx() { return big.NewInt(0), nil } @@ -249,8 +333,26 @@ func CalculateL1DataFee(tx *types.Transaction, state StateDB, config *params.Cha if !config.IsCurie(blockNumber) { l1DataFee = calculateEncodedL1DataFee(raw, gpoState.overhead, gpoState.l1BaseFee, gpoState.scalar) - } else { + } else if !config.IsFeynman(blockTime) { l1DataFee = calculateEncodedL1DataFeeCurie(raw, gpoState.l1BaseFee, gpoState.l1BlobBaseFee, gpoState.commitScalar, gpoState.blobScalar) + } else { + // Calculate compression ratio for Feynman + compressionRatio, err := estimateTxCompressionRatio(raw, blockNumber.Uint64(), blockTime, config) + if err != nil { + return nil, fmt.Errorf("failed to estimate compression ratio: tx hash=%s: %w", tx.Hash().Hex(), err) + } + + // The contract slot for commitScalar is changed to execScalar in Feynman + l1DataFee = calculateEncodedL1DataFeeFeynman( + raw, + gpoState.l1BaseFee, + gpoState.l1BlobBaseFee, + gpoState.commitScalar, // now represents execScalar + gpoState.blobScalar, + gpoState.penaltyThreshold, + gpoState.penaltyFactor, + compressionRatio, + ) } // ensure l1DataFee fits into uint64 for circuit compatibility diff --git a/rollup/fees/rollup_fee_test.go b/rollup/fees/rollup_fee_test.go index b35d8cd9bf..a4197321cf 100644 --- a/rollup/fees/rollup_fee_test.go +++ b/rollup/fees/rollup_fee_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "github.com/scroll-tech/go-ethereum/params" ) func TestL1DataFeeBeforeCurie(t *testing.T) { @@ -31,3 +33,109 @@ func TestL1DataFeeAfterCurie(t *testing.T) { actual := calculateEncodedL1DataFeeCurie(data, l1BaseFee, l1BlobBaseFee, commitScalar, blobScalar) assert.Equal(t, expected, actual) } + +func TestL1DataFeeFeynman(t *testing.T) { + l1BaseFee := new(big.Int).SetInt64(1_000_000_000) + l1BlobBaseFee := new(big.Int).SetInt64(1_000_000_000) + execScalar := new(big.Int).SetInt64(10) + blobScalar := new(big.Int).SetInt64(20) + penaltyThreshold := new(big.Int).SetInt64(6_000_000_000) // 6 * PRECISION + penaltyFactor := new(big.Int).SetInt64(2_000_000_000) // 2 * PRECISION (200% penalty) + + // Test case 1: No penalty (compression ratio >= threshold) + t.Run("no penalty case", func(t *testing.T) { + data := make([]byte, 100) // txSize = 100 + compressionRatio := new(big.Int).SetInt64(6_000_000_000) // exactly at threshold + + // Since compression ratio >= penaltyThreshold, penalty = 1 * PRECISION + // feePerByte = execScalar * l1BaseFee + blobScalar * l1BlobBaseFee = 10 * 1_000_000_000 + 20 * 1_000_000_000 = 30_000_000_000 + // l1DataFee = feePerByte * txSize * penalty / PRECISION / PRECISION + // = 30_000_000_000 * 100 * 1_000_000_000 / 1_000_000_000 / 1_000_000_000 = 3000 + + expected := new(big.Int).SetInt64(3000) + + actual := calculateEncodedL1DataFeeFeynman( + data, + l1BaseFee, + l1BlobBaseFee, + execScalar, + blobScalar, + penaltyThreshold, + penaltyFactor, + compressionRatio, + ) + + assert.Equal(t, expected, actual) + }) + + // Test case 2: With penalty (compression ratio < threshold) + t.Run("with penalty case", func(t *testing.T) { + data := make([]byte, 100) // txSize = 100 + compressionRatio := new(big.Int).SetInt64(5_000_000_000) // below threshold + + // Since compression ratio < penaltyThreshold, penalty = penaltyFactor + // feePerByte = execScalar * l1BaseFee + blobScalar * l1BlobBaseFee = 30_000_000_000 + // l1DataFee = feePerByte * txSize * penaltyFactor / PRECISION / PRECISION + // = 30_000_000_000 * 100 * 2_000_000_000 / 1_000_000_000 / 1_000_000_000 = 6000 + + expected := new(big.Int).SetInt64(6000) + + actual := calculateEncodedL1DataFeeFeynman( + data, + l1BaseFee, + l1BlobBaseFee, + execScalar, + blobScalar, + penaltyThreshold, + penaltyFactor, + compressionRatio, + ) + + assert.Equal(t, expected, actual) + }) +} + +func TestEstimateTxCompressionRatio(t *testing.T) { + // Mock config that would select a specific codec version + // Note: You'll need to adjust this based on your actual params.ChainConfig structure + + t.Run("empty data", func(t *testing.T) { + data := []byte{} + // Should return 1.0 ratio (PRECISION) + ratio, err := estimateTxCompressionRatio(data, 1000000, 1700000000, params.TestChainConfig) + assert.Error(t, err, "raw data is empty") + assert.Nil(t, ratio) + // The exact value depends on rcfg.Precision, but should be the "1.0" equivalent + }) + + t.Run("non-empty data", func(t *testing.T) { + // Create some compressible data + data := make([]byte, 1000) + for i := range data { + data[i] = byte(i % 10) // Create patterns for better compression + } + + ratio, err := estimateTxCompressionRatio(data, 1000000, 1700000000, params.TestChainConfig) + assert.NoError(t, err) + assert.NotNil(t, ratio) + // Should return a ratio > 1.0 (since compressed size < original size) + }) +} + +func TestCalculatePenalty(t *testing.T) { + precision := new(big.Int).SetInt64(1_000_000_000) // PRECISION + penaltyThreshold := new(big.Int).SetInt64(6_000_000_000) // 6 * PRECISION + penaltyFactor := new(big.Int).SetInt64(2_000_000_000) // 2 * PRECISION + + t.Run("no penalty when ratio >= threshold", func(t *testing.T) { + compressionRatio := new(big.Int).SetInt64(6_000_000_000) // exactly at threshold + penalty := calculatePenalty(compressionRatio, penaltyThreshold, penaltyFactor) + assert.Equal(t, precision, penalty) + }) + + t.Run("penalty when ratio < threshold", func(t *testing.T) { + compressionRatio := new(big.Int).SetInt64(5_000_000_000) // below threshold + penalty := calculatePenalty(compressionRatio, penaltyThreshold, penaltyFactor) + assert.Equal(t, penaltyFactor, penalty) + }) +} diff --git a/rollup/tracing/tracing.go b/rollup/tracing/tracing.go index 86fe1381c0..0569a99bc4 100644 --- a/rollup/tracing/tracing.go +++ b/rollup/tracing/tracing.go @@ -239,7 +239,7 @@ func (env *TraceEnv) GetBlockTrace(block *types.Block) (*types.BlockTrace, error msg, _ := tx.AsMessage(env.signer, block.BaseFee()) env.state.SetTxContext(tx.Hash(), i) vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), env.state, env.chainConfig, vm.Config{}) - l1DataFee, err := fees.CalculateL1DataFee(tx, env.state, env.chainConfig, block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, env.state, env.chainConfig, block.Number(), block.Time()) if err != nil { failed = err break @@ -342,7 +342,7 @@ func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.B state.SetTxContext(txctx.TxHash, txctx.TxIndex) // Computes the new state by applying the given message. - l1DataFee, err := fees.CalculateL1DataFee(tx, state, env.chainConfig, block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(tx, state, env.chainConfig, block.Number(), block.Time()) if err != nil { return err } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 6cb06777e5..9f3a5da75a 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -251,7 +251,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh gaspool := new(core.GasPool) gaspool.AddGas(block.GasLimit()) - l1DataFee, err := fees.CalculateL1DataFee(&ttx, statedb, config, block.Number()) + l1DataFee, err := fees.CalculateL1DataFee(&ttx, statedb, config, block.Number(), block.Time()) if err != nil { return nil, nil, common.Hash{}, err }