feat: update l1fee calculation (#351)

This commit is contained in:
HAOYUatHZ 2023-06-13 10:57:59 +08:00 committed by GitHub
parent 4e0daeb300
commit f055f50f9d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 341 additions and 198 deletions

View file

@ -42,6 +42,7 @@ import (
"github.com/scroll-tech/go-ethereum/event"
"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/rpc"
)
@ -638,8 +639,13 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
// about the transaction and calling mechanisms.
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)
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, head.BaseFee, b.blockchain.Config().ChainID, signer, stateDB)
if err != nil {
return nil, err
}
return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb()
return core.NewStateTransition(vmEnv, msg, gasPool, l1DataFee).TransitionDb()
}
// SendTransaction updates the pending block to include the given transaction.

View file

@ -37,6 +37,7 @@ import (
"github.com/scroll-tech/go-ethereum/log"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rlp"
"github.com/scroll-tech/go-ethereum/rollup/fees"
"github.com/scroll-tech/go-ethereum/trie"
)
@ -166,8 +167,15 @@ 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)
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()})
continue
}
// (ret []byte, usedGas uint64, failed bool, err error)
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
msgResult, err := core.ApplyMessage(evm, msg, gaspool, l1DataFee)
if err != nil {
statedb.RevertToSnapshot(snapshot)
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err)

View file

@ -17,6 +17,7 @@
package core
import (
"math/big"
"sync/atomic"
"github.com/scroll-tech/go-ethereum/consensus"
@ -24,6 +25,7 @@ import (
"github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/core/vm"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rollup/fees"
)
// statePrefetcher is a basic Prefetcher, which blindly executes a block on top
@ -68,7 +70,13 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
return // Also invalid block, bail out
}
statedb.Prepare(tx.Hash(), i)
if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil {
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
return
}
if err = precacheTransaction(msg, p.config, gaspool, statedb, header, evm, l1DataFee); err != nil {
return // Ugh, something went horribly wrong, bail out
}
// If we're pre-byzantium, pre-load trie nodes for the intermediate root
@ -85,10 +93,10 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
// precacheTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment. The goal is not to execute
// the transaction successfully, rather to warm up touched data slots.
func precacheTransaction(msg types.Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM) error {
func precacheTransaction(msg types.Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM, l1DataFee *big.Int) error {
// Update the evm with the new transaction context.
evm.Reset(NewEVMTxContext(msg), statedb)
// Add addresses to access list if applicable
_, err := ApplyMessage(evm, msg, gaspool)
_, err := ApplyMessage(evm, msg, gaspool, l1DataFee)
return err
}

View file

@ -28,6 +28,7 @@ import (
"github.com/scroll-tech/go-ethereum/core/vm"
"github.com/scroll-tech/go-ethereum/crypto"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rollup/fees"
)
// StateProcessor is a basic Processor, which takes care of transitioning
@ -97,8 +98,13 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
txContext := NewEVMTxContext(msg)
evm.Reset(txContext, statedb)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
return nil, err
}
// Apply the transaction to the current state (included in the env).
result, err := ApplyMessage(evm, msg, gp)
result, err := ApplyMessage(evm, msg, gp, l1DataFee)
if err != nil {
return nil, err
}
@ -139,7 +145,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
receipt.BlockHash = blockHash
receipt.BlockNumber = blockNumber
receipt.TransactionIndex = uint(statedb.TxIndex())
receipt.L1Fee = result.L1Fee
receipt.L1Fee = result.L1DataFee
return receipt, err
}

View file

@ -28,7 +28,6 @@ import (
"github.com/scroll-tech/go-ethereum/crypto/codehash"
"github.com/scroll-tech/go-ethereum/log"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rollup/fees"
)
var emptyKeccakCodeHash = codehash.EmptyKeccakCodeHash
@ -63,8 +62,7 @@ type StateTransition struct {
state vm.StateDB
evm *vm.EVM
// l1 rollup fee
l1Fee *big.Int
l1DataFee *big.Int
}
// Message represents a message sent to a contract.
@ -88,7 +86,7 @@ type Message interface {
// ExecutionResult includes all output after executing given evm
// message no matter the execution itself is successful or not.
type ExecutionResult struct {
L1Fee *big.Int
L1DataFee *big.Int
UsedGas uint64 // Total used gas but include the refunded gas
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
@ -181,12 +179,7 @@ func toWordSize(size uint64) uint64 {
}
// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
l1Fee := new(big.Int)
if evm.ChainConfig().Scroll.FeeVaultEnabled() {
l1Fee, _ = fees.CalculateL1MsgFee(msg, evm.StateDB)
}
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool, l1DataFee *big.Int) *StateTransition {
return &StateTransition{
gp: gp,
evm: evm,
@ -197,7 +190,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
value: msg.Value(),
data: msg.Data(),
state: evm.StateDB,
l1Fee: l1Fee,
l1DataFee: l1DataFee,
}
}
@ -208,8 +201,8 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
// the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) (*ExecutionResult, error) {
return NewStateTransition(evm, msg, gp).TransitionDb()
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, l1DataFee *big.Int) (*ExecutionResult, error) {
return NewStateTransition(evm, msg, gp, l1DataFee).TransitionDb()
}
// to returns the recipient of the message.
@ -225,9 +218,12 @@ func (st *StateTransition) buyGas() error {
mgval = mgval.Mul(mgval, st.gasPrice)
if st.evm.ChainConfig().Scroll.FeeVaultEnabled() {
// always add l1fee, because all tx are L2-to-L1 ATM
log.Debug("Adding L1 fee", "l1_fee", st.l1Fee)
mgval = mgval.Add(mgval, st.l1Fee)
// should be fine to add st.l1DataFee even without `L1MessageTx` check, since L1MessageTx will come with 0 l1DataFee,
// but double check to make sure
if !st.msg.IsL1MessageTx() {
log.Debug("Adding L1DataFee", "l1DataFee", st.l1DataFee)
mgval = mgval.Add(mgval, st.l1DataFee)
}
}
balanceCheck := mgval
@ -236,8 +232,11 @@ func (st *StateTransition) buyGas() error {
balanceCheck = balanceCheck.Mul(balanceCheck, st.gasFeeCap)
balanceCheck.Add(balanceCheck, st.value)
if st.evm.ChainConfig().Scroll.FeeVaultEnabled() {
// always add l1fee, because all tx are L2-to-L1 ATM
balanceCheck.Add(balanceCheck, st.l1Fee)
// should be fine to add st.l1DataFee even without `L1MessageTx` check, since L1MessageTx will come with 0 l1DataFee,
// but double check to make sure
if !st.msg.IsL1MessageTx() {
balanceCheck.Add(balanceCheck, st.l1DataFee)
}
}
}
if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 {
@ -391,7 +390,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
// no refunds for l1 messages
if st.msg.IsL1MessageTx() {
return &ExecutionResult{
L1Fee: big.NewInt(0),
L1DataFee: big.NewInt(0),
UsedGas: st.gasUsed(),
Err: vmerr,
ReturnData: ret,
@ -416,17 +415,17 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
if st.evm.ChainConfig().Scroll.FeeVaultEnabled() {
// The L2 Fee is the same as the fee that is charged in the normal geth
// codepath. Add the L1 fee to the L2 fee for the total fee that is sent
// codepath. Add the L1DataFee to the L2 fee for the total fee that is sent
// to the sequencer.
l2Fee := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)
fee := new(big.Int).Add(st.l1Fee, l2Fee)
fee := new(big.Int).Add(st.l1DataFee, l2Fee)
st.state.AddBalance(st.evm.FeeRecipient(), fee)
} else {
st.state.AddBalance(st.evm.FeeRecipient(), new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))
}
return &ExecutionResult{
L1Fee: st.l1Fee,
L1DataFee: st.l1DataFee,
UsedGas: st.gasUsed(),
Err: vmerr,
ReturnData: ret,

View file

@ -695,7 +695,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
if pool.chainconfig.Scroll.FeeVaultEnabled() {
if err := fees.VerifyFee(pool.signer, tx, pool.currentState); err != nil {
log.Trace("Discarding insufficient l1fee transaction", "hash", hash, "err", err)
log.Trace("Discarding insufficient l1DataFee transaction", "hash", hash, "err", err)
invalidTxMeter.Mark(1)
return false, err
}

View file

@ -43,32 +43,32 @@ var (
// sideeffects used during testing.
testTxPoolConfig TxPoolConfig
// noL1feeConfig is a chain config without L1fee enabled.
noL1feeConfig *params.ChainConfig
// noL1DataFeeConfig is a chain config without L1DataFee enabled.
noL1DataFeeConfig *params.ChainConfig
// eip1559Config is a chain config with EIP-1559 enabled at block 0.
eip1559Config *params.ChainConfig
// eip1559NoL1feeConfig is a chain config with EIP-1559 enabled at block 0 but not enabling L1fee.
eip1559NoL1feeConfig *params.ChainConfig
// eip1559NoL1DataFeeConfig is a chain config with EIP-1559 enabled at block 0 but not enabling L1DataFee.
eip1559NoL1DataFeeConfig *params.ChainConfig
)
func init() {
testTxPoolConfig = DefaultTxPoolConfig
testTxPoolConfig.Journal = ""
cpy0 := *params.TestNoL1feeChainConfig
noL1feeConfig = &cpy0
cpy0 := *params.TestNoL1DataFeeChainConfig
noL1DataFeeConfig = &cpy0
cpy1 := *params.TestChainConfig
eip1559Config = &cpy1
eip1559Config.BerlinBlock = common.Big0
eip1559Config.LondonBlock = common.Big0
cpy2 := *params.TestNoL1feeChainConfig
eip1559NoL1feeConfig = &cpy2
eip1559NoL1feeConfig.BerlinBlock = common.Big0
eip1559NoL1feeConfig.LondonBlock = common.Big0
cpy2 := *params.TestNoL1DataFeeChainConfig
eip1559NoL1DataFeeConfig = &cpy2
eip1559NoL1DataFeeConfig.BerlinBlock = common.Big0
eip1559NoL1DataFeeConfig.LondonBlock = common.Big0
}
type testBlockChain struct {
@ -290,7 +290,7 @@ func testSetNonce(pool *TxPool, addr common.Address, nonce uint64) {
func TestInvalidTransactions(t *testing.T) {
t.Parallel()
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
tx := transaction(0, 100, key)
@ -327,7 +327,7 @@ func TestInvalidTransactions(t *testing.T) {
func TestTransactionQueue(t *testing.T) {
t.Parallel()
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
tx := transaction(0, 100, key)
@ -358,7 +358,7 @@ func TestTransactionQueue(t *testing.T) {
func TestTransactionQueue2(t *testing.T) {
t.Parallel()
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
tx1 := transaction(0, 100, key)
@ -384,7 +384,7 @@ func TestTransactionQueue2(t *testing.T) {
func TestTransactionNegativeValue(t *testing.T) {
t.Parallel()
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil), types.HomesteadSigner{}, key)
@ -398,7 +398,7 @@ func TestTransactionNegativeValue(t *testing.T) {
func TestTransactionTipAboveFeeCap(t *testing.T) {
t.Parallel()
pool, key := setupTxPoolWithConfig(eip1559NoL1feeConfig)
pool, key := setupTxPoolWithConfig(eip1559NoL1DataFeeConfig)
defer pool.Stop()
tx := dynamicFeeTx(0, 100, big.NewInt(1), big.NewInt(2), key)
@ -411,7 +411,7 @@ func TestTransactionTipAboveFeeCap(t *testing.T) {
func TestTransactionVeryHighValues(t *testing.T) {
t.Parallel()
pool, key := setupTxPoolWithConfig(eip1559NoL1feeConfig)
pool, key := setupTxPoolWithConfig(eip1559NoL1DataFeeConfig)
defer pool.Stop()
veryBigNumber := big.NewInt(1)
@ -431,7 +431,7 @@ func TestTransactionVeryHighValues(t *testing.T) {
func TestTransactionChainFork(t *testing.T) {
t.Parallel()
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
@ -460,7 +460,7 @@ func TestTransactionChainFork(t *testing.T) {
func TestTransactionDoubleNonce(t *testing.T) {
t.Parallel()
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
@ -511,7 +511,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
func TestTransactionMissingNonce(t *testing.T) {
t.Parallel()
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
@ -535,7 +535,7 @@ func TestTransactionNonceRecovery(t *testing.T) {
t.Parallel()
const n = 10
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
@ -561,7 +561,7 @@ func TestTransactionDropping(t *testing.T) {
t.Parallel()
// Create a test account and fund it
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@ -779,7 +779,7 @@ func TestTransactionGapFilling(t *testing.T) {
t.Parallel()
// Create a test account and fund it
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@ -833,7 +833,7 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
t.Parallel()
// Create a test account and fund it
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@ -1114,7 +1114,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
t.Parallel()
// Create a test account and fund it
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@ -1203,7 +1203,7 @@ func TestTransactionAllowedTxSize(t *testing.T) {
t.Parallel()
// Create a test account and fund it
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@ -1463,7 +1463,7 @@ func TestTransactionPoolRepricingDynamicFee(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
pool, _ := setupTxPoolWithConfig(eip1559NoL1feeConfig)
pool, _ := setupTxPoolWithConfig(eip1559NoL1DataFeeConfig)
defer pool.Stop()
// Keep track of transaction events to ensure all executables get announced
@ -1834,7 +1834,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) {
t.Parallel()
pool, _ := setupTxPoolWithConfig(eip1559NoL1feeConfig)
pool, _ := setupTxPoolWithConfig(eip1559NoL1DataFeeConfig)
defer pool.Stop()
pool.config.GlobalSlots = 2
@ -1941,7 +1941,7 @@ func TestTransactionPoolUnderpricingDynamicFee(t *testing.T) {
func TestDualHeapEviction(t *testing.T) {
t.Parallel()
pool, _ := setupTxPoolWithConfig(eip1559NoL1feeConfig)
pool, _ := setupTxPoolWithConfig(eip1559NoL1DataFeeConfig)
defer pool.Stop()
pool.config.GlobalSlots = 10
@ -2144,7 +2144,7 @@ func TestTransactionReplacementDynamicFee(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
pool, key := setupTxPoolWithConfig(eip1559NoL1feeConfig)
pool, key := setupTxPoolWithConfig(eip1559NoL1DataFeeConfig)
defer pool.Stop()
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
@ -2443,7 +2443,7 @@ func BenchmarkPendingDemotion10000(b *testing.B) { benchmarkPendingDemotion(b, 1
func benchmarkPendingDemotion(b *testing.B, size int) {
// Add a batch of transactions to a pool one by one
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@ -2468,7 +2468,7 @@ func BenchmarkFuturePromotion10000(b *testing.B) { benchmarkFuturePromotion(b, 1
func benchmarkFuturePromotion(b *testing.B, size int) {
// Add a batch of transactions to a pool one by one
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
@ -2496,7 +2496,7 @@ func BenchmarkPoolBatchLocalInsert10000(b *testing.B) { benchmarkPoolBatchInsert
func benchmarkPoolBatchInsert(b *testing.B, size int, local bool) {
// Generate a batch of transactions to enqueue into the pool
pool, key := setupTxPoolWithConfig(noL1feeConfig)
pool, key := setupTxPoolWithConfig(noL1DataFeeConfig)
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)

View file

@ -44,7 +44,7 @@ type StorageTrace struct {
// while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value
type ExecutionResult struct {
L1Fee *hexutil.Big `json:"l1Fee,omitempty"`
L1DataFee *hexutil.Big `json:"l1DataFee,omitempty"`
Gas uint64 `json:"gas"`
Failed bool `json:"failed"`
ReturnValue string `json:"returnValue"`

View file

@ -27,6 +27,7 @@ import (
"github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/core/vm"
"github.com/scroll-tech/go-ethereum/log"
"github.com/scroll-tech/go-ethereum/rollup/fees"
"github.com/scroll-tech/go-ethereum/trie"
)
@ -191,7 +192,11 @@ 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.Prepare(tx.Hash(), idx)
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
return nil, vm.BlockContext{}, nil, err
}
if _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), l1DataFee); err != nil {
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state

View file

@ -283,7 +283,16 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
TxIndex: i,
TxHash: tx.Hash(),
}
res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
l1DataFee, err := fees.CalculateL1DataFee(tx, task.statedb)
if err != nil {
// though it's not a "tracing error", we still need to put it here
task.results[i] = &txTraceResult{Error: err.Error()}
log.Warn("CalculateL1DataFee failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
break
}
res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config, l1DataFee)
if err != nil {
task.results[i] = &txTraceResult{Error: err.Error()}
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
@ -534,7 +543,14 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{})
)
statedb.Prepare(tx.Hash(), i)
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
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
}
if _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee); err != nil {
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
// We intentionally don't return the error here: if we do, then the RPC server will not
// return the roots. Most likely, the caller already knows that a certain transaction fails to
@ -608,7 +624,14 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
TxIndex: task.index,
TxHash: txs[task.index].Hash(),
}
res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
l1DataFee, err := fees.CalculateL1DataFee(txs[task.index], task.statedb)
if err != nil {
// though it's not a "tracing error", we still need to put it here
results[task.index] = &txTraceResult{Error: err.Error()}
continue
}
res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config, l1DataFee)
if err != nil {
results[task.index] = &txTraceResult{Error: err.Error()}
continue
@ -627,7 +650,12 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
msg, _ := tx.AsMessage(signer, block.BaseFee())
statedb.Prepare(tx.Hash(), i)
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
failed = err
break
}
if _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee); err != nil {
failed = err
break
}
@ -740,7 +768,10 @@ 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.Prepare(tx.Hash(), i)
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err == nil {
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee)
}
if writer != nil {
writer.Flush()
}
@ -777,7 +808,7 @@ func containsTx(block *types.Block, hash common.Hash) bool {
// TraceTransaction returns the structured logs created during the execution of EVM
// and returns them as a JSON object.
func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
_, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
tx, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
if err != nil {
return nil, err
}
@ -802,7 +833,11 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
TxIndex: int(index),
TxHash: hash,
}
return api.traceTx(ctx, msg, txctx, vmctx, statedb, config)
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
return nil, err
}
return api.traceTx(ctx, msg, txctx, vmctx, statedb, config, l1DataFee)
}
// TraceCall lets you trace a given eth_call. It collects the structured logs
@ -856,13 +891,20 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
Reexec: config.Reexec,
}
}
return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig)
signer := types.MakeSigner(api.backend.ChainConfig(), block.Number())
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, block.BaseFee(), api.backend.ChainConfig().ChainID, signer, statedb)
if err != nil {
return nil, err
}
return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig, l1DataFee)
}
// traceTx configures a new tracer according to the provided configuration, and
// executes the given message in the provided environment. The return value will
// be tracer dependent.
func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig, l1DataFee *big.Int) (interface{}, error) {
// Assemble the structured logger or the JavaScript tracer
var (
tracer vm.EVMLogger
@ -899,20 +941,15 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
// Run the transaction with tracing enabled.
vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
// If gasPrice is 0, make sure that the account has sufficient balance to cover `l1Fee`.
if api.backend.ChainConfig().Scroll.FeeVaultEnabled() && message.GasPrice().Cmp(big.NewInt(0)) == 0 {
l1Fee, err := fees.CalculateL1MsgFee(message, vmenv.StateDB)
if err != nil {
return nil, err
}
statedb.AddBalance(message.From(), l1Fee)
// If gasPrice is 0, make sure that the account has sufficient balance to cover `l1DataFee`.
if message.GasPrice().Cmp(big.NewInt(0)) == 0 {
statedb.AddBalance(message.From(), l1DataFee)
}
// Call Prepare to clear out the statedb access list
statedb.Prepare(txctx.TxHash, txctx.TxIndex)
result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), l1DataFee)
if err != nil {
return nil, fmt.Errorf("tracing failed: %w", err)
}
@ -930,7 +967,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
Failed: result.Failed(),
ReturnValue: returnVal,
StructLogs: vm.FormatLogs(tracer.StructLogs()),
L1Fee: (*hexutil.Big)(result.L1Fee),
L1DataFee: (*hexutil.Big)(result.L1DataFee),
}, nil
case Tracer:

View file

@ -16,6 +16,7 @@ import (
"github.com/scroll-tech/go-ethereum/core/vm"
"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/rcfg"
"github.com/scroll-tech/go-ethereum/rollup/withdrawtrie"
"github.com/scroll-tech/go-ethereum/rpc"
@ -182,7 +183,12 @@ func (api *API) getBlockTrace(block *types.Block, env *traceEnv) (*types.BlockTr
msg, _ := tx.AsMessage(env.signer, block.BaseFee())
env.state.Prepare(tx.Hash(), i)
vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), env.state, api.backend.ChainConfig(), vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
l1DataFee, err := fees.CalculateL1DataFee(tx, env.state)
if err != nil {
failed = err
break
}
if _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee); err != nil {
failed = err
break
}
@ -258,7 +264,11 @@ func (api *API) getTxResult(env *traceEnv, state *state.StateDB, index int, bloc
state.Prepare(txctx.TxHash, txctx.TxIndex)
// Computes the new state by applying the given message.
result, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
l1DataFee, err := fees.CalculateL1DataFee(tx, state)
if err != nil {
return fmt.Errorf("tracing failed: %w", err)
}
result, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee)
if err != nil {
return fmt.Errorf("tracing failed: %w", err)
}
@ -389,7 +399,7 @@ func (api *API) getTxResult(env *traceEnv, state *state.StateDB, index int, bloc
To: receiver,
AccountCreated: createdAcc,
AccountsAfter: after,
L1Fee: (*hexutil.Big)(result.L1Fee),
L1DataFee: (*hexutil.Big)(result.L1DataFee),
Gas: result.UsedGas,
Failed: result.Failed(),
ReturnValue: fmt.Sprintf("%x", returnVal),

View file

@ -164,7 +164,7 @@ func checkStructLogs(t *testing.T, expect []*txTraceResult, actual []*types.Exec
assert.Equal(t, len(expect), len(actual))
for i, val := range expect {
trace1, trace2 := val.Result.(*types.ExecutionResult), actual[i]
assert.Equal(t, trace1.L1Fee, trace2.L1Fee)
assert.Equal(t, trace1.L1DataFee, trace2.L1DataFee)
assert.Equal(t, trace1.Failed, trace2.Failed)
assert.Equal(t, trace1.Gas, trace2.Gas)
assert.Equal(t, trace1.ReturnValue, trace2.ReturnValue)

View file

@ -42,6 +42,7 @@ import (
"github.com/scroll-tech/go-ethereum/ethdb"
"github.com/scroll-tech/go-ethereum/internal/ethapi"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rollup/fees"
"github.com/scroll-tech/go-ethereum/rpc"
)
@ -172,7 +173,11 @@ 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{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
return nil, vm.BlockContext{}, nil, err
}
if _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), l1DataFee); err != nil {
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
@ -218,7 +223,7 @@ func TestTraceCall(t *testing.T) {
config: nil,
expectErr: nil,
expect: &types.ExecutionResult{
L1Fee: (*hexutil.Big)(big.NewInt(0)),
L1DataFee: (*hexutil.Big)(big.NewInt(0)),
Gas: params.TxGas,
Failed: false,
ReturnValue: "",
@ -236,7 +241,7 @@ func TestTraceCall(t *testing.T) {
config: nil,
expectErr: nil,
expect: &types.ExecutionResult{
L1Fee: (*hexutil.Big)(big.NewInt(0)),
L1DataFee: (*hexutil.Big)(big.NewInt(0)),
Gas: params.TxGas,
Failed: false,
ReturnValue: "",
@ -266,7 +271,7 @@ func TestTraceCall(t *testing.T) {
config: nil,
expectErr: nil,
expect: &types.ExecutionResult{
L1Fee: (*hexutil.Big)(big.NewInt(0)),
L1DataFee: (*hexutil.Big)(big.NewInt(0)),
Gas: params.TxGas,
Failed: false,
ReturnValue: "",
@ -284,7 +289,7 @@ func TestTraceCall(t *testing.T) {
config: nil,
expectErr: nil,
expect: &types.ExecutionResult{
L1Fee: (*hexutil.Big)(big.NewInt(0)),
L1DataFee: (*hexutil.Big)(big.NewInt(0)),
Gas: params.TxGas,
Failed: false,
ReturnValue: "",
@ -338,7 +343,7 @@ func TestTraceTransaction(t *testing.T) {
t.Errorf("Failed to trace transaction %v", err)
}
if !reflect.DeepEqual(result, &types.ExecutionResult{
L1Fee: (*hexutil.Big)(big.NewInt(0)),
L1DataFee: (*hexutil.Big)(big.NewInt(0)),
Gas: params.TxGas,
Failed: false,
ReturnValue: "",

View file

@ -37,6 +37,7 @@ import (
"github.com/scroll-tech/go-ethereum/eth/tracers"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rlp"
"github.com/scroll-tech/go-ethereum/rollup/fees"
"github.com/scroll-tech/go-ethereum/tests"
// Force-load native and js pacakges, to trigger registration
@ -192,7 +193,11 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
t.Fatalf("failed to calculate l1DataFee: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), l1DataFee)
if _, err = st.TransitionDb(); err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
@ -303,7 +308,11 @@ 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()
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
b.Fatalf("failed to calculate l1DataFee: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), l1DataFee)
if _, err = st.TransitionDb(); err != nil {
b.Fatalf("failed to execute transaction: %v", err)
}
@ -372,7 +381,11 @@ func TestZeroValueToNotExitCall(t *testing.T) {
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
t.Fatalf("failed to calculate l1DataFee: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), l1DataFee)
if _, err = st.TransitionDb(); err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}

View file

@ -28,6 +28,7 @@ import (
"github.com/scroll-tech/go-ethereum/core/vm"
"github.com/scroll-tech/go-ethereum/crypto"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rollup/fees"
"github.com/scroll-tech/go-ethereum/tests"
)
@ -111,7 +112,11 @@ func BenchmarkTransactionTrace(b *testing.B) {
for i := 0; i < b.N; i++ {
snap := statedb.Snapshot()
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
b.Fatal(err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), l1DataFee)
_, err = st.TransitionDb()
if err != nil {
b.Fatal(err)

View file

@ -334,7 +334,7 @@ func testCallContractNoGas(t *testing.T, client *rpc.Client) {
}
// this would fail with `insufficient funds for gas * price + value`
// before we started considering l1fee for 0 gas calls.
// before we started considering l1DataFee for 0 gas calls.
if _, err := ec.CallContract(context.Background(), msg, big.NewInt(0), nil); err != nil {
t.Fatalf("unexpected error: %v", err)
}

View file

@ -907,7 +907,7 @@ func newRPCBalance(balance *big.Int) **hexutil.Big {
return &rpcBalance
}
func CalculateL1MsgFee(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64, config *params.ChainConfig) (*big.Int, error) {
func EstimateL1MsgFee(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64, config *params.ChainConfig) (*big.Int, error) {
if !config.Scroll.FeeVaultEnabled() {
return big.NewInt(0), nil
}
@ -947,7 +947,8 @@ func CalculateL1MsgFee(ctx context.Context, b Backend, args TransactionArgs, blo
evm.Cancel()
}()
return fees.CalculateL1MsgFee(msg, evm.StateDB)
signer := types.MakeSigner(config, header.Number)
return fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config.ChainID, signer, evm.StateDB)
}
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
@ -990,7 +991,14 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
// Execute the message.
gp := new(core.GasPool).AddGas(math.MaxUint64)
result, err := core.ApplyMessage(evm, msg, gp)
signer := types.MakeSigner(b.ChainConfig(), header.Number)
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, b.ChainConfig().ChainID, signer, state)
if err != nil {
return nil, err
}
result, err := core.ApplyMessage(evm, msg, gp, l1DataFee)
if err := vmError(); err != nil {
return nil, err
}
@ -1043,7 +1051,7 @@ func (e *revertError) ErrorData() interface{} {
// useful to execute and retrieve values.
func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) {
// If gasPrice is 0 and no state override is set, make sure
// that the account has sufficient balance to cover `l1Fee`.
// that the account has sufficient balance to cover `l1DataFee`.
isGasPriceZero := args.GasPrice == nil || args.GasPrice.ToInt().Cmp(big.NewInt(0)) == 0
if overrides == nil {
@ -1052,13 +1060,13 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl
_, isOverrideSet := (*overrides)[args.from()]
if isGasPriceZero && !isOverrideSet {
l1Fee, err := CalculateL1MsgFee(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap(), s.b.ChainConfig())
l1DataFee, err := EstimateL1MsgFee(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap(), s.b.ChainConfig())
if err != nil {
return nil, err
}
(*overrides)[args.from()] = OverrideAccount{
BalanceAdd: newRPCBalance(l1Fee),
BalanceAdd: newRPCBalance(l1DataFee),
}
}
@ -1127,14 +1135,14 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
}
// account for l1 fee
l1Fee, err := CalculateL1MsgFee(ctx, b, args, blockNrOrHash, nil, 0, gasCap, b.ChainConfig())
l1DataFee, err := EstimateL1MsgFee(ctx, b, args, blockNrOrHash, nil, 0, gasCap, b.ChainConfig())
if err != nil {
return 0, err
}
if l1Fee.Cmp(available) >= 0 {
if l1DataFee.Cmp(available) >= 0 {
return 0, errors.New("insufficient funds for l1 fee")
}
available.Sub(available, l1Fee)
available.Sub(available, l1DataFee)
allowance := new(big.Int).Div(available, feeCap)
@ -1501,7 +1509,12 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
if err != nil {
return nil, 0, nil, err
}
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
signer := types.MakeSigner(b.ChainConfig(), header.Number)
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, b.ChainConfig().ChainID, signer, statedb)
if err != nil {
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
}
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee)
if err != nil {
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
}

View file

@ -37,6 +37,7 @@ import (
"github.com/scroll-tech/go-ethereum/light"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rlp"
"github.com/scroll-tech/go-ethereum/rollup/fees"
)
type odrTestFn func(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte
@ -143,7 +144,9 @@ 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)
result, _ := core.ApplyMessage(vmenv, msg, gp)
signer := types.MakeSigner(config, header.Number)
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config.ChainID, signer, statedb)
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
res = append(res, result.Return()...)
}
} else {
@ -155,7 +158,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
txContext := core.NewEVMTxContext(msg)
vmenv := vm.NewEVM(context, txContext, state, config, vm.Config{NoBaseFee: true})
gp := new(core.GasPool).AddGas(math.MaxUint64)
result, _ := core.ApplyMessage(vmenv, msg, gp)
signer := types.MakeSigner(config, header.Number)
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config.ChainID, signer, state)
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
if state.Error() == nil {
res = append(res, result.Return()...)
}

View file

@ -26,6 +26,7 @@ import (
"github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/core/vm"
"github.com/scroll-tech/go-ethereum/light"
"github.com/scroll-tech/go-ethereum/rollup/fees"
)
// stateAtBlock retrieves the state database associated with a certain block.
@ -64,7 +65,11 @@ 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{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
if err != nil {
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
if _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), l1DataFee); err != nil {
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state

View file

@ -36,6 +36,7 @@ import (
"github.com/scroll-tech/go-ethereum/ethdb"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rlp"
"github.com/scroll-tech/go-ethereum/rollup/fees"
"github.com/scroll-tech/go-ethereum/trie"
)
@ -199,7 +200,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
context := core.NewEVMBlockContext(header, chain, nil)
vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true})
gp := new(core.GasPool).AddGas(math.MaxUint64)
result, _ := core.ApplyMessage(vmenv, msg, gp)
signer := types.MakeSigner(config, header.Number)
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config.ChainID, signer, st)
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
res = append(res, result.Return()...)
if st.Error() != nil {
return res, st.Error()

View file

@ -340,7 +340,7 @@ var (
}}
TestRules = TestChainConfig.Rules(new(big.Int))
TestNoL1feeChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil,
TestNoL1DataFeeChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil,
ScrollConfig{
UseZktrie: false,
FeeVaultAddress: nil,

View file

@ -23,8 +23,8 @@ import (
const (
VersionMajor = 4 // Major version component of the current release
VersionMinor = 1 // Minor version component of the current release
VersionPatch = 1 // Patch version component of the current release
VersionMinor = 2 // Minor version component of the current release
VersionPatch = 0 // Patch version component of the current release
VersionMeta = "sepolia" // Version metadata to append to the version string
)

View file

@ -8,24 +8,17 @@ import (
"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/params"
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
)
var (
// errTransactionSigned represents the error case of passing in a signed
// transaction to the L1 fee calculation routine. The signature is accounted
// for externally
errTransactionSigned = errors.New("transaction is signed")
// txExtraDataBytes is the number of bytes that we commit to L1 in addition
// to the RLP-encoded unsigned transaction. Note that these are all assumed
// to the RLP-encoded signed transaction. Note that these are all assumed
// to be non-zero.
// - tx length prefix: 4 bytes
// - sig.r: 32 bytes + 1 byte rlp prefix
// - sig.s: 32 bytes + 1 byte rlp prefix
// - sig.v: 3 bytes + 1 byte rlp prefix
txExtraDataBytes = uint64(74)
txExtraDataBytes = uint64(4)
)
// Message represents the interface of a message.
@ -36,9 +29,13 @@ type Message interface {
To() *common.Address
GasPrice() *big.Int
Gas() uint64
GasFeeCap() *big.Int
GasTipCap() *big.Int
Value() *big.Int
Nonce() uint64
Data() []byte
AccessList() types.AccessList
IsL1MessageTx() bool
}
// StateDB represents the StateDB interface
@ -48,59 +45,87 @@ type StateDB interface {
GetBalance(addr common.Address) *big.Int
}
// CalculateL1MsgFee computes the L1 portion of the fee given
// a Message and a StateDB
// Reference: https://github.com/ethereum-optimism/optimism/blob/develop/l2geth/rollup/fees/rollup_fee.go
func CalculateL1MsgFee(msg Message, state StateDB) (*big.Int, error) {
tx := asTransaction(msg)
func EstimateL1DataFeeForMessage(msg Message, baseFee, chainID *big.Int, signer types.Signer, state StateDB) (*big.Int, error) {
if msg.IsL1MessageTx() {
return big.NewInt(0), nil
}
unsigned := asUnsignedTx(msg, baseFee, chainID)
// with v=1
tx, err := unsigned.WithSignature(signer, append(bytes.Repeat([]byte{0xff}, crypto.SignatureLength-1), 0x01))
if err != nil {
return nil, err
}
raw, err := rlpEncode(tx)
if err != nil {
return nil, err
}
l1BaseFee, overhead, scalar := readGPOStorageSlots(rcfg.L1GasPriceOracleAddress, state)
l1Fee := CalculateL1Fee(raw, overhead, l1BaseFee, scalar)
return l1Fee, nil
l1DataFee := calculateEncodedL1DataFee(raw, overhead, l1BaseFee, scalar)
return l1DataFee, nil
}
// asTransaction turns a Message into a types.Transaction
func asTransaction(msg Message) *types.Transaction {
if msg.To() == nil {
return types.NewContractCreation(
msg.Nonce(),
msg.Value(),
msg.Gas(),
msg.GasPrice(),
msg.Data(),
)
// asUnsignedTx turns a Message into a types.Transaction
func asUnsignedTx(msg Message, baseFee, chainID *big.Int) *types.Transaction {
if baseFee == nil {
if msg.AccessList() == nil {
return asUnsignedLegacyTx(msg)
}
return asUnsignedAccessListTx(msg, chainID)
}
return types.NewTransaction(
msg.Nonce(),
*msg.To(),
msg.Value(),
msg.Gas(),
msg.GasPrice(),
msg.Data(),
)
return asUnsignedDynamicTx(msg, chainID)
}
func asUnsignedLegacyTx(msg Message) *types.Transaction {
return types.NewTx(&types.LegacyTx{
Nonce: msg.Nonce(),
To: msg.To(),
Value: msg.Value(),
Gas: msg.Gas(),
GasPrice: msg.GasPrice(),
Data: msg.Data(),
})
}
func asUnsignedAccessListTx(msg Message, chainID *big.Int) *types.Transaction {
return types.NewTx(&types.AccessListTx{
Nonce: msg.Nonce(),
To: msg.To(),
Value: msg.Value(),
Gas: msg.Gas(),
GasPrice: msg.GasPrice(),
Data: msg.Data(),
AccessList: msg.AccessList(),
ChainID: chainID,
})
}
func asUnsignedDynamicTx(msg Message, chainID *big.Int) *types.Transaction {
return types.NewTx(&types.DynamicFeeTx{
Nonce: msg.Nonce(),
To: msg.To(),
Value: msg.Value(),
Gas: msg.Gas(),
GasFeeCap: msg.GasFeeCap(),
GasTipCap: msg.GasTipCap(),
Data: msg.Data(),
AccessList: msg.AccessList(),
ChainID: chainID,
})
}
// rlpEncode RLP encodes the transaction into bytes
// When a signature is not included, set pad to true to
// fill in a dummy signature full on non 0 bytes
func rlpEncode(tx *types.Transaction) ([]byte, error) {
raw := new(bytes.Buffer)
if err := tx.EncodeRLP(raw); err != nil {
return nil, err
}
r, v, s := tx.RawSignatureValues()
if r.Cmp(common.Big0) != 0 || v.Cmp(common.Big0) != 0 || s.Cmp(common.Big0) != 0 {
return nil, errTransactionSigned
}
// Slice off the 0 bytes representing the signature
b := raw.Bytes()
return b[:len(b)-3], nil
return raw.Bytes(), nil
}
func readGPOStorageSlots(addr common.Address, state StateDB) (*big.Int, *big.Int, *big.Int) {
@ -110,11 +135,11 @@ func readGPOStorageSlots(addr common.Address, state StateDB) (*big.Int, *big.Int
return l1BaseFee.Big(), overhead.Big(), scalar.Big()
}
// CalculateL1Fee computes the L1 fee
func CalculateL1Fee(data []byte, overhead, l1GasPrice *big.Int, scalar *big.Int) *big.Int {
// calculateEncodedL1DataFee computes the L1 fee for an RLP-encoded tx
func calculateEncodedL1DataFee(data []byte, overhead, l1GasPrice *big.Int, scalar *big.Int) *big.Int {
l1GasUsed := CalculateL1GasUsed(data, overhead)
l1Fee := new(big.Int).Mul(l1GasUsed, l1GasPrice)
return mulAndScale(l1Fee, scalar, rcfg.Precision)
l1DataFee := new(big.Int).Mul(l1GasUsed, l1GasPrice)
return mulAndScale(l1DataFee, scalar, rcfg.Precision)
}
// CalculateL1GasUsed computes the L1 gas used based on the calldata and
@ -150,41 +175,24 @@ func mulAndScale(x *big.Int, y *big.Int, precision *big.Int) *big.Int {
return new(big.Int).Quo(z, precision)
}
// copyTransaction copies the transaction, removing the signature
func copyTransaction(tx *types.Transaction) *types.Transaction {
if tx.To() == nil {
return types.NewContractCreation(
tx.Nonce(),
tx.Value(),
tx.Gas(),
tx.GasPrice(),
tx.Data(),
)
func CalculateL1DataFee(tx *types.Transaction, state StateDB) (*big.Int, error) {
if tx.IsL1MessageTx() {
return big.NewInt(0), nil
}
return types.NewTransaction(
tx.Nonce(),
*tx.To(),
tx.Value(),
tx.Gas(),
tx.GasPrice(),
tx.Data(),
)
}
func CalculateFees(tx *types.Transaction, state StateDB) (*big.Int, *big.Int, *big.Int, error) {
unsigned := copyTransaction(tx)
raw, err := rlpEncode(unsigned)
raw, err := rlpEncode(tx)
if err != nil {
return nil, nil, nil, err
return nil, err
}
l1BaseFee, overhead, scalar := readGPOStorageSlots(rcfg.L1GasPriceOracleAddress, state)
l1Fee := CalculateL1Fee(raw, overhead, l1BaseFee, scalar)
l1DataFee := calculateEncodedL1DataFee(raw, overhead, l1BaseFee, scalar)
return l1DataFee, nil
}
func calculateL2Fee(tx *types.Transaction) *big.Int {
l2GasLimit := new(big.Int).SetUint64(tx.Gas())
l2Fee := new(big.Int).Mul(tx.GasPrice(), l2GasLimit)
fee := new(big.Int).Add(l1Fee, l2Fee)
return l1Fee, l2Fee, fee, nil
return new(big.Int).Mul(tx.GasPrice(), l2GasLimit)
}
func VerifyFee(signer types.Signer, tx *types.Transaction, state StateDB) error {
@ -194,8 +202,8 @@ func VerifyFee(signer types.Signer, tx *types.Transaction, state StateDB) error
}
balance := state.GetBalance(from)
l1Fee, l2Fee, _, err := CalculateFees(tx, state)
l2Fee := calculateL2Fee(tx)
l1DataFee, err := CalculateL1DataFee(tx, state)
if err != nil {
return fmt.Errorf("invalid transaction: %w", err)
}
@ -206,7 +214,7 @@ func VerifyFee(signer types.Signer, tx *types.Transaction, state StateDB) error
return errors.New("invalid transaction: insufficient funds for gas * price + value")
}
cost = cost.Add(cost, l1Fee)
cost = cost.Add(cost, l1DataFee)
if balance.Cmp(cost) < 0 {
return errors.New("invalid transaction: insufficient funds for l1fee + gas * price + value")
}

View file

@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestCalculateL1Fee(t *testing.T) {
func TestCalculateEncodedL1DataFee(t *testing.T) {
l1BaseFee := new(big.Int).SetUint64(15000000)
data := []byte{0, 10, 1, 0}
@ -15,6 +15,6 @@ func TestCalculateL1Fee(t *testing.T) {
scalar := new(big.Int).SetUint64(10)
expected := new(big.Int).SetUint64(184) // 184.2
actual := CalculateL1Fee(data, overhead, l1BaseFee, scalar)
actual := calculateEncodedL1DataFee(data, overhead, l1BaseFee, scalar)
assert.Equal(t, expected, actual)
}

View file

@ -39,6 +39,7 @@ import (
"github.com/scroll-tech/go-ethereum/ethdb"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rlp"
"github.com/scroll-tech/go-ethereum/rollup/fees"
)
// StateTest checks transaction processing without block context.
@ -201,9 +202,9 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
return nil, nil, common.Hash{}, err
}
var ttx types.Transaction
// Try to recover tx with current signer
if len(post.TxBytes) != 0 {
var ttx types.Transaction
err := ttx.UnmarshalBinary(post.TxBytes)
if err != nil {
return nil, nil, common.Hash{}, err
@ -225,7 +226,13 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
snapshot := statedb.Snapshot()
gaspool := new(core.GasPool)
gaspool.AddGas(block.GasLimit())
if _, err := core.ApplyMessage(evm, msg, gaspool); err != nil {
l1DataFee, err := fees.CalculateL1DataFee(&ttx, statedb)
if err != nil {
return nil, nil, common.Hash{}, err
}
if _, err = core.ApplyMessage(evm, msg, gaspool, l1DataFee); err != nil {
statedb.RevertToSnapshot(snapshot)
}