mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
feat: l1datafee (#611)
* update params/config.go * update core/state_transition.go update core/state_transition.go * update core/state_prefetcher.go * update core/state_processor.go * update core/types/gen_receipt_json.go * update core/types/receipt.go * update internal/ethapi/api.go * update Message type and interface * update accounts/abi/bind/backends/simulated.go * update eth/tracers/api.go * eth/tracers/logger/logger.go WIP * update eth/state_accessor.go update eth/state_accessor.go * update les/state_accessor.go * update cmd/evm/internal/t8ntool/execution.go * update core/rawdb/accessors_chain.go * update eth/api_backend.go * update les/api_backend.go * update `ValidateTransactionWithState` * update light/txpool.go * refactor `ErrInsufficientFundsWithL1DataFee` * clean up `rollup/fees/rollup_fee.go` * update tests/state_test_util.go * fix tests
This commit is contained in:
parent
adfec9a0a3
commit
07d5fddde8
27 changed files with 362 additions and 108 deletions
|
|
@ -42,6 +42,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -704,8 +705,13 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||||
evmContext := core.NewEVMBlockContext(header, b.blockchain, nil)
|
evmContext := core.NewEVMBlockContext(header, b.blockchain, nil)
|
||||||
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
|
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
|
||||||
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
|
signer := types.MakeSigner(b.blockchain.Config(), header.Number, header.Time)
|
||||||
|
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, b.blockchain.Config().ChainID, signer, stateDB)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return core.ApplyMessage(vmEnv, msg, gasPool)
|
return core.ApplyMessage(vmEnv, msg, gasPool, l1DataFee)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTransaction updates the pending block to include the given transaction.
|
// SendTransaction updates the pending block to include the given transaction.
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
|
|
@ -220,8 +221,16 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
)
|
)
|
||||||
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
|
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()})
|
||||||
|
gaspool.SetGas(prevGas)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// (ret []byte, usedGas uint64, failed bool, err error)
|
// (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 {
|
if err != nil {
|
||||||
statedb.RevertToSnapshot(snapshot)
|
statedb.RevertToSnapshot(snapshot)
|
||||||
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,10 @@ var (
|
||||||
// is higher than the balance of the user's account.
|
// is higher than the balance of the user's account.
|
||||||
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
||||||
|
|
||||||
|
// ErrInsufficientFundsWithL1DataFee is returned if (the total cost of executing a transaction + L1DataFee)
|
||||||
|
// is higher than the balance of the user's account.
|
||||||
|
ErrInsufficientFundsWithL1DataFee = errors.New("insufficient funds for l1fee + gas * price + value")
|
||||||
|
|
||||||
// ErrGasUintOverflow is returned when calculating gas usage.
|
// ErrGasUintOverflow is returned when calculating gas usage.
|
||||||
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -688,6 +688,7 @@ type storedReceiptRLP struct {
|
||||||
PostStateOrStatus []byte
|
PostStateOrStatus []byte
|
||||||
CumulativeGasUsed uint64
|
CumulativeGasUsed uint64
|
||||||
Logs []*types.Log
|
Logs []*types.Log
|
||||||
|
L1Fee *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReceiptLogs is a barebone version of ReceiptForStorage which only keeps
|
// ReceiptLogs is a barebone version of ReceiptForStorage which only keeps
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
|
@ -24,6 +25,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
)
|
)
|
||||||
|
|
||||||
// statePrefetcher is a basic Prefetcher, which blindly executes a block on top
|
// 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
|
return // Also invalid block, bail out
|
||||||
}
|
}
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(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
|
return // Ugh, something went horribly wrong, bail out
|
||||||
}
|
}
|
||||||
// If we're pre-byzantium, pre-load trie nodes for the intermediate root
|
// 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
|
// 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
|
// and uses the input parameters for its environment. The goal is not to execute
|
||||||
// the transaction successfully, rather to warm up touched data slots.
|
// the transaction successfully, rather to warm up touched data slots.
|
||||||
func precacheTransaction(msg *Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM) error {
|
func precacheTransaction(msg *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.
|
// Update the evm with the new transaction context.
|
||||||
evm.Reset(NewEVMTxContext(msg), statedb)
|
evm.Reset(NewEVMTxContext(msg), statedb)
|
||||||
// Add addresses to access list if applicable
|
// Add addresses to access list if applicable
|
||||||
_, err := ApplyMessage(evm, msg, gaspool)
|
_, err := ApplyMessage(evm, msg, gaspool, l1DataFee)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StateProcessor is a basic Processor, which takes care of transitioning
|
// StateProcessor is a basic Processor, which takes care of transitioning
|
||||||
|
|
@ -109,8 +110,13 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
|
||||||
txContext := NewEVMTxContext(msg)
|
txContext := NewEVMTxContext(msg)
|
||||||
evm.Reset(txContext, statedb)
|
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).
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -151,6 +157,7 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
|
||||||
receipt.BlockHash = blockHash
|
receipt.BlockHash = blockHash
|
||||||
receipt.BlockNumber = blockNumber
|
receipt.BlockNumber = blockNumber
|
||||||
receipt.TransactionIndex = uint(statedb.TxIndex())
|
receipt.TransactionIndex = uint(statedb.TxIndex())
|
||||||
|
receipt.L1Fee = result.L1DataFee
|
||||||
return receipt, err
|
return receipt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
cmath "github.com/ethereum/go-ethereum/common/math"
|
cmath "github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -149,6 +150,18 @@ type Message struct {
|
||||||
IsL1MessageTx bool
|
IsL1MessageTx bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Message) GetFrom() common.Address { return m.From }
|
||||||
|
func (m *Message) GetTo() *common.Address { return m.To }
|
||||||
|
func (m *Message) GetGasPrice() *big.Int { return m.GasPrice }
|
||||||
|
func (m *Message) GetGasLimit() uint64 { return m.GasLimit }
|
||||||
|
func (m *Message) GetGasFeeCap() *big.Int { return m.GasFeeCap }
|
||||||
|
func (m *Message) GetGasTipCap() *big.Int { return m.GasTipCap }
|
||||||
|
func (m *Message) GetValue() *big.Int { return m.Value }
|
||||||
|
func (m *Message) GetNonce() uint64 { return m.Nonce }
|
||||||
|
func (m *Message) GetData() []byte { return m.Data }
|
||||||
|
func (m *Message) GetAccessList() types.AccessList { return m.AccessList }
|
||||||
|
func (m *Message) GetIsL1MessageTx() bool { return m.IsL1MessageTx }
|
||||||
|
|
||||||
// TransactionToMessage converts a transaction into a Message.
|
// TransactionToMessage converts a transaction into a Message.
|
||||||
func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) {
|
func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) {
|
||||||
msg := &Message{
|
msg := &Message{
|
||||||
|
|
@ -182,8 +195,8 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
|
||||||
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
// 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
|
// indicates a core error meaning that the message would always fail for that particular
|
||||||
// state and would never be accepted within a block.
|
// state and would never be accepted within a block.
|
||||||
func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, error) {
|
func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool, l1DataFee *big.Int) (*ExecutionResult, error) {
|
||||||
return NewStateTransition(evm, msg, gp).TransitionDb()
|
return NewStateTransition(evm, msg, gp, l1DataFee).TransitionDb()
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateTransition represents a state transition.
|
// StateTransition represents a state transition.
|
||||||
|
|
@ -215,15 +228,18 @@ type StateTransition struct {
|
||||||
initialGas uint64
|
initialGas uint64
|
||||||
state vm.StateDB
|
state vm.StateDB
|
||||||
evm *vm.EVM
|
evm *vm.EVM
|
||||||
|
|
||||||
|
l1DataFee *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStateTransition initialises and returns a new state transition object.
|
// NewStateTransition initialises and returns a new state transition object.
|
||||||
func NewStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *StateTransition {
|
func NewStateTransition(evm *vm.EVM, msg *Message, gp *GasPool, l1DataFee *big.Int) *StateTransition {
|
||||||
return &StateTransition{
|
return &StateTransition{
|
||||||
gp: gp,
|
gp: gp,
|
||||||
evm: evm,
|
evm: evm,
|
||||||
msg: msg,
|
msg: msg,
|
||||||
state: evm.StateDB,
|
state: evm.StateDB,
|
||||||
|
l1DataFee: l1DataFee,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,11 +254,28 @@ func (st *StateTransition) to() common.Address {
|
||||||
func (st *StateTransition) buyGas() error {
|
func (st *StateTransition) buyGas() error {
|
||||||
mgval := new(big.Int).SetUint64(st.msg.GasLimit)
|
mgval := new(big.Int).SetUint64(st.msg.GasLimit)
|
||||||
mgval = mgval.Mul(mgval, st.msg.GasPrice)
|
mgval = mgval.Mul(mgval, st.msg.GasPrice)
|
||||||
|
|
||||||
|
if st.evm.ChainConfig().Scroll.FeeVaultEnabled() {
|
||||||
|
// 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 := new(big.Int).Set(mgval)
|
balanceCheck := new(big.Int).Set(mgval)
|
||||||
if st.msg.GasFeeCap != nil {
|
if st.msg.GasFeeCap != nil {
|
||||||
balanceCheck.SetUint64(st.msg.GasLimit)
|
balanceCheck.SetUint64(st.msg.GasLimit)
|
||||||
balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap)
|
balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap)
|
||||||
balanceCheck.Add(balanceCheck, st.msg.Value)
|
balanceCheck.Add(balanceCheck, st.msg.Value)
|
||||||
|
if st.evm.ChainConfig().Scroll.FeeVaultEnabled() {
|
||||||
|
// 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 st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) {
|
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) {
|
||||||
if blobGas := st.blobGasUsed(); blobGas > 0 {
|
if blobGas := st.blobGasUsed(); blobGas > 0 {
|
||||||
|
|
@ -455,12 +488,17 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
// are 0. This avoids a negative effectiveTip being applied to
|
// are 0. This avoids a negative effectiveTip being applied to
|
||||||
// the coinbase when simulating calls.
|
// the coinbase when simulating calls.
|
||||||
} else {
|
} else {
|
||||||
|
// The L2 Fee is the same as the fee that is charged in the normal geth
|
||||||
|
// codepath. Add the L1DataFee to the L2 fee for the total fee that is sent
|
||||||
|
// to the sequencer.
|
||||||
fee := new(big.Int).SetUint64(st.gasUsed())
|
fee := new(big.Int).SetUint64(st.gasUsed())
|
||||||
fee.Mul(fee, effectiveTip)
|
fee.Mul(fee, effectiveTip)
|
||||||
st.state.AddBalance(st.evm.Context.Coinbase, fee)
|
fee.Add(fee, st.l1DataFee)
|
||||||
|
st.state.AddBalance(st.evm.Context.Coinbase, fee) // TODO: change to `st.evm.FeeRecipient()`
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ExecutionResult{
|
return &ExecutionResult{
|
||||||
|
L1DataFee: st.l1DataFee,
|
||||||
UsedGas: st.gasUsed(),
|
UsedGas: st.gasUsed(),
|
||||||
Err: vmerr,
|
Err: vmerr,
|
||||||
ReturnData: ret,
|
ReturnData: ret,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValidationOptions define certain differences between transaction validation
|
// ValidationOptions define certain differences between transaction validation
|
||||||
|
|
@ -219,9 +220,23 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
|
||||||
balance = opts.State.GetBalance(from)
|
balance = opts.State.GetBalance(from)
|
||||||
cost = tx.Cost()
|
cost = tx.Cost()
|
||||||
)
|
)
|
||||||
|
// 1. Check balance >= transaction cost (V + GP * GL) to maintain compatibility with the logic without considering L1 data fee.
|
||||||
if balance.Cmp(cost) < 0 {
|
if balance.Cmp(cost) < 0 {
|
||||||
return fmt.Errorf("%w: balance %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, cost, new(big.Int).Sub(cost, balance))
|
return fmt.Errorf("%w: balance %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, cost, new(big.Int).Sub(cost, balance))
|
||||||
}
|
}
|
||||||
|
// 2. Perform an additional check for L1 data fees.
|
||||||
|
// Always perform the check, because it's not easy to check FeeVault here
|
||||||
|
// Get L1 data fee in current state
|
||||||
|
l1DataFee, err := fees.CalculateL1DataFee(tx, opts.State)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to calculate L1 data fee, err: %w", err)
|
||||||
|
}
|
||||||
|
// Transactor should have enough funds to cover the costs
|
||||||
|
// cost == L1 data fee + V + GP * GL
|
||||||
|
cost = new(big.Int).Add(tx.Cost(), l1DataFee)
|
||||||
|
if balance.Cmp(cost) < 0 {
|
||||||
|
return fmt.Errorf("invalid transaction: %w", core.ErrInsufficientFundsWithL1DataFee)
|
||||||
|
}
|
||||||
// Ensure the transactor has enough funds to cover for replacements or nonce
|
// Ensure the transactor has enough funds to cover for replacements or nonce
|
||||||
// expansions without overdrafts
|
// expansions without overdrafts
|
||||||
spent := opts.ExistingExpenditure(from)
|
spent := opts.ExistingExpenditure(from)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||||
BlockHash common.Hash `json:"blockHash,omitempty"`
|
BlockHash common.Hash `json:"blockHash,omitempty"`
|
||||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||||
TransactionIndex hexutil.Uint `json:"transactionIndex"`
|
TransactionIndex hexutil.Uint `json:"transactionIndex"`
|
||||||
|
L1Fee *hexutil.Big `json:"l1Fee,omitempty"`
|
||||||
}
|
}
|
||||||
var enc Receipt
|
var enc Receipt
|
||||||
enc.Type = hexutil.Uint64(r.Type)
|
enc.Type = hexutil.Uint64(r.Type)
|
||||||
|
|
@ -48,6 +49,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||||
enc.BlockHash = r.BlockHash
|
enc.BlockHash = r.BlockHash
|
||||||
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
|
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
|
||||||
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
|
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
|
||||||
|
enc.L1Fee = (*hexutil.Big)(r.L1Fee)
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,6 +71,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||||
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||||
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
|
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
|
||||||
|
L1Fee *hexutil.Big `json:"l1Fee,omitempty"`
|
||||||
}
|
}
|
||||||
var dec Receipt
|
var dec Receipt
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
@ -124,5 +127,8 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||||
if dec.TransactionIndex != nil {
|
if dec.TransactionIndex != nil {
|
||||||
r.TransactionIndex = uint(*dec.TransactionIndex)
|
r.TransactionIndex = uint(*dec.TransactionIndex)
|
||||||
}
|
}
|
||||||
|
if dec.L1Fee != nil {
|
||||||
|
r.L1Fee = (*big.Int)(dec.L1Fee)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,9 @@ type Receipt struct {
|
||||||
BlockHash common.Hash `json:"blockHash,omitempty"`
|
BlockHash common.Hash `json:"blockHash,omitempty"`
|
||||||
BlockNumber *big.Int `json:"blockNumber,omitempty"`
|
BlockNumber *big.Int `json:"blockNumber,omitempty"`
|
||||||
TransactionIndex uint `json:"transactionIndex"`
|
TransactionIndex uint `json:"transactionIndex"`
|
||||||
|
|
||||||
|
// Scroll rollup
|
||||||
|
L1Fee *big.Int `json:"l1Fee,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type receiptMarshaling struct {
|
type receiptMarshaling struct {
|
||||||
|
|
@ -84,6 +87,7 @@ type receiptMarshaling struct {
|
||||||
BlobGasPrice *hexutil.Big
|
BlobGasPrice *hexutil.Big
|
||||||
BlockNumber *hexutil.Big
|
BlockNumber *hexutil.Big
|
||||||
TransactionIndex hexutil.Uint
|
TransactionIndex hexutil.Uint
|
||||||
|
L1Fee *hexutil.Big
|
||||||
}
|
}
|
||||||
|
|
||||||
// receiptRLP is the consensus encoding of a receipt.
|
// receiptRLP is the consensus encoding of a receipt.
|
||||||
|
|
@ -99,6 +103,7 @@ type storedReceiptRLP struct {
|
||||||
PostStateOrStatus []byte
|
PostStateOrStatus []byte
|
||||||
CumulativeGasUsed uint64
|
CumulativeGasUsed uint64
|
||||||
Logs []*Log
|
Logs []*Log
|
||||||
|
L1Fee *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
||||||
|
|
@ -264,6 +269,10 @@ type ReceiptForStorage Receipt
|
||||||
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
||||||
// into an RLP stream.
|
// into an RLP stream.
|
||||||
func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
|
func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
|
||||||
|
if r.L1Fee == nil {
|
||||||
|
r.L1Fee = big.NewInt(0)
|
||||||
|
}
|
||||||
|
|
||||||
w := rlp.NewEncoderBuffer(_w)
|
w := rlp.NewEncoderBuffer(_w)
|
||||||
outerList := w.List()
|
outerList := w.List()
|
||||||
w.WriteBytes((*Receipt)(r).statusEncoding())
|
w.WriteBytes((*Receipt)(r).statusEncoding())
|
||||||
|
|
@ -275,6 +284,7 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.ListEnd(logList)
|
w.ListEnd(logList)
|
||||||
|
w.WriteBigInt(r.L1Fee)
|
||||||
w.ListEnd(outerList)
|
w.ListEnd(outerList)
|
||||||
return w.Flush()
|
return w.Flush()
|
||||||
}
|
}
|
||||||
|
|
@ -292,6 +302,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||||
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
||||||
r.Logs = stored.Logs
|
r.Logs = stored.Logs
|
||||||
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
|
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
|
||||||
|
r.L1Fee = stored.L1Fee
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,7 @@ func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||||
|
// will `VerifyFee` & `validateTx` in txPool.Add
|
||||||
return b.eth.txPool.Add([]*types.Transaction{signedTx}, true, false)[0]
|
return b.eth.txPool.Add([]*types.Transaction{signedTx}, true, false)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -248,7 +249,11 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
|
||||||
statedb.SetTxContext(tx.Hash(), idx)
|
statedb.SetTxContext(tx.Hash(), idx)
|
||||||
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, nil, err
|
||||||
|
}
|
||||||
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), l1DataFee); err != nil {
|
||||||
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
// Ensure any modifications are committed to the state
|
// Ensure any modifications are committed to the state
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -41,6 +42,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -276,7 +278,14 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
|
||||||
TxIndex: i,
|
TxIndex: i,
|
||||||
TxHash: tx.Hash(),
|
TxHash: tx.Hash(),
|
||||||
}
|
}
|
||||||
res, err := api.traceTx(ctx, 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{TxHash: tx.Hash(), Error: err.Error()}
|
||||||
|
log.Warn("CalculateL1DataFee failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config, l1DataFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()}
|
task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()}
|
||||||
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
|
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
|
||||||
|
|
@ -535,7 +544,12 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
|
||||||
vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{})
|
vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{})
|
||||||
)
|
)
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); 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.GasLimit), l1DataFee); err != nil {
|
||||||
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
|
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
|
// 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
|
// return the roots. Most likely, the caller already knows that a certain transaction fails to
|
||||||
|
|
@ -611,7 +625,11 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
TxIndex: i,
|
TxIndex: i,
|
||||||
TxHash: tx.Hash(),
|
TxHash: tx.Hash(),
|
||||||
}
|
}
|
||||||
res, err := api.traceTx(ctx, msg, txctx, blockCtx, statedb, config)
|
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res, err := api.traceTx(ctx, msg, txctx, blockCtx, statedb, config, l1DataFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -654,7 +672,13 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
|
||||||
TxIndex: task.index,
|
TxIndex: task.index,
|
||||||
TxHash: txs[task.index].Hash(),
|
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{TxHash: txs[task.index].Hash(), Error: err.Error()}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config, l1DataFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
|
results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
|
||||||
continue
|
continue
|
||||||
|
|
@ -681,7 +705,12 @@ txloop:
|
||||||
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
|
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
|
||||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil {
|
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
|
||||||
|
if err != nil {
|
||||||
|
failed = err
|
||||||
|
break txloop
|
||||||
|
}
|
||||||
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit), l1DataFee); err != nil {
|
||||||
failed = err
|
failed = err
|
||||||
break txloop
|
break txloop
|
||||||
}
|
}
|
||||||
|
|
@ -788,7 +817,10 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
||||||
// Execute the transaction and flush any traces to disk
|
// Execute the transaction and flush any traces to disk
|
||||||
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
|
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
l1DataFee, err := fees.CalculateL1DataFee(tx, statedb)
|
||||||
|
if err == nil {
|
||||||
|
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit), l1DataFee)
|
||||||
|
}
|
||||||
if writer != nil {
|
if writer != nil {
|
||||||
writer.Flush()
|
writer.Flush()
|
||||||
}
|
}
|
||||||
|
|
@ -857,7 +889,11 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
|
||||||
TxIndex: int(index),
|
TxIndex: int(index),
|
||||||
TxHash: hash,
|
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
|
// TraceCall lets you trace a given eth_call. It collects the structured logs
|
||||||
|
|
@ -916,13 +952,18 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
||||||
if config != nil {
|
if config != nil {
|
||||||
traceConfig = &config.TraceConfig
|
traceConfig = &config.TraceConfig
|
||||||
}
|
}
|
||||||
return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig)
|
signer := types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time())
|
||||||
|
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
|
// traceTx configures a new tracer according to the provided configuration, and
|
||||||
// executes the given message in the provided environment. The return value will
|
// executes the given message in the provided environment. The return value will
|
||||||
// be tracer dependent.
|
// 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) {
|
||||||
var (
|
var (
|
||||||
tracer Tracer
|
tracer Tracer
|
||||||
err error
|
err error
|
||||||
|
|
@ -942,6 +983,11 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte
|
||||||
}
|
}
|
||||||
vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer, NoBaseFee: true})
|
vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer, NoBaseFee: true})
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
// Define a meaningful timeout of a single transaction trace
|
// Define a meaningful timeout of a single transaction trace
|
||||||
if config.Timeout != nil {
|
if config.Timeout != nil {
|
||||||
if timeout, err = time.ParseDuration(*config.Timeout); err != nil {
|
if timeout, err = time.ParseDuration(*config.Timeout); err != nil {
|
||||||
|
|
@ -961,7 +1007,7 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte
|
||||||
|
|
||||||
// Call Prepare to clear out the statedb access list
|
// Call Prepare to clear out the statedb access list
|
||||||
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
||||||
if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit)); err != nil {
|
if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit), l1DataFee); err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %w", err)
|
return nil, fmt.Errorf("tracing failed: %w", err)
|
||||||
}
|
}
|
||||||
return tracer.GetResult()
|
return tracer.GetResult()
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
@ -177,7 +178,11 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
|
||||||
return msg, context, statedb, release, nil
|
return msg, context, statedb, release, nil
|
||||||
}
|
}
|
||||||
vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{})
|
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, nil, fmt.Errorf("transaction %#x CalculateL1DataFee 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, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -150,7 +151,11 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
vmRet, err := core.ApplyMessage(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)
|
||||||
|
}
|
||||||
|
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), l1DataFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to execute transaction: %v", err)
|
t.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -251,7 +256,11 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
||||||
}
|
}
|
||||||
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
|
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
|
||||||
snap := statedb.Snapshot()
|
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 {
|
if _, err = st.TransitionDb(); err != nil {
|
||||||
b.Fatalf("failed to execute transaction: %v", err)
|
b.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -388,7 +397,12 @@ func TestInternals(t *testing.T) {
|
||||||
GasTipCap: big.NewInt(0),
|
GasTipCap: big.NewInt(0),
|
||||||
SkipAccountChecks: false,
|
SkipAccountChecks: false,
|
||||||
}
|
}
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
signer := types.MakeSigner(params.MainnetChainConfig, context.BlockNumber, context.Time)
|
||||||
|
l1DataFee, err := fees.EstimateL1DataFeeForMessage(msg, nil, params.MainnetChainConfig.ChainID, signer, statedb)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %v: failed to estimate L1DataFee: %v", tc.name, err)
|
||||||
|
}
|
||||||
|
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(msg.GasLimit), l1DataFee)
|
||||||
if _, err := st.TransitionDb(); err != nil {
|
if _, err := st.TransitionDb(); err != nil {
|
||||||
t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err)
|
t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
|
|
||||||
// Force-load the native, to trigger registration
|
// Force-load the native, to trigger registration
|
||||||
|
|
@ -114,7 +115,11 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to prepare transaction for tracing: %v", err)
|
return fmt.Errorf("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 {
|
||||||
|
return fmt.Errorf("failed to calculate L1DataFee: %v", err)
|
||||||
|
}
|
||||||
|
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()), l1DataFee)
|
||||||
|
|
||||||
if _, err = st.TransitionDb(); err != nil {
|
if _, err = st.TransitionDb(); err != nil {
|
||||||
return fmt.Errorf("failed to execute transaction: %v", err)
|
return fmt.Errorf("failed to execute transaction: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -121,7 +122,11 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
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 {
|
if _, err = st.TransitionDb(); err != nil {
|
||||||
t.Fatalf("failed to execute transaction: %v", err)
|
t.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,7 @@ func (l *StructLogger) GetResult() (json.RawMessage, error) {
|
||||||
Failed: failed,
|
Failed: failed,
|
||||||
ReturnValue: returnVal,
|
ReturnValue: returnVal,
|
||||||
StructLogs: formatLogs(l.StructLogs()),
|
StructLogs: formatLogs(l.StructLogs()),
|
||||||
|
// L1DataFee: (*hexutil.Big)(result.L1DataFee),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -99,7 +100,11 @@ func BenchmarkTransactionTrace(b *testing.B) {
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
snap := statedb.Snapshot()
|
snap := statedb.Snapshot()
|
||||||
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()
|
_, err = st.TransitionDb()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/tyler-smith/go-bip39"
|
"github.com/tyler-smith/go-bip39"
|
||||||
|
|
@ -1099,7 +1100,7 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S
|
||||||
|
|
||||||
// Execute the message.
|
// Execute the message.
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
result, err := core.ApplyMessage(evm, msg, gp)
|
result, err := core.ApplyMessage(evm, msg, gp, common.Big0)
|
||||||
if err := vmError(); err != nil {
|
if err := vmError(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1177,6 +1178,48 @@ func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrO
|
||||||
return result.Return(), result.Err
|
return result.Return(), result.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
|
if state == nil || err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := overrides.Apply(state); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Setup context so it may be cancelled the call has completed
|
||||||
|
// or, in case of unmetered gas, setup a context with a timeout.
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
if timeout > 0 {
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, timeout)
|
||||||
|
} else {
|
||||||
|
ctx, cancel = context.WithCancel(ctx)
|
||||||
|
}
|
||||||
|
// Make sure the context is cancelled when the call has completed
|
||||||
|
// this makes sure resources are cleaned up.
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Get a new instance of the EVM.
|
||||||
|
msg, err := args.ToMessage(globalGasCap, header.BaseFee)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
|
||||||
|
evm, _ := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}, &blockCtx)
|
||||||
|
// Wait for the context to be done and cancel the evm. Even if the
|
||||||
|
// EVM has finished, cancelling may be done (repeatedly)
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
evm.Cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
signer := types.MakeSigner(config, header.Number, header.Time)
|
||||||
|
return fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config.ChainID, signer, evm.StateDB)
|
||||||
|
}
|
||||||
|
|
||||||
// executeEstimate is a helper that executes the transaction under a given gas limit and returns
|
// executeEstimate is a helper that executes the transaction under a given gas limit and returns
|
||||||
// true if the transaction fails for a reason that might be related to not enough gas. A non-nil
|
// true if the transaction fails for a reason that might be related to not enough gas. A non-nil
|
||||||
// error means execution failed due to reasons unrelated to the gas limit.
|
// error means execution failed due to reasons unrelated to the gas limit.
|
||||||
|
|
@ -1244,12 +1287,25 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
||||||
if feeCap.BitLen() != 0 {
|
if feeCap.BitLen() != 0 {
|
||||||
balance := state.GetBalance(*args.From) // from can't be nil
|
balance := state.GetBalance(*args.From) // from can't be nil
|
||||||
available := new(big.Int).Set(balance)
|
available := new(big.Int).Set(balance)
|
||||||
|
|
||||||
|
// account for tx value
|
||||||
if args.Value != nil {
|
if args.Value != nil {
|
||||||
if args.Value.ToInt().Cmp(available) >= 0 {
|
if args.Value.ToInt().Cmp(available) >= 0 {
|
||||||
return 0, core.ErrInsufficientFundsForTransfer
|
return 0, core.ErrInsufficientFundsForTransfer
|
||||||
}
|
}
|
||||||
available.Sub(available, args.Value.ToInt())
|
available.Sub(available, args.Value.ToInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// account for l1 fee
|
||||||
|
l1DataFee, err := EstimateL1MsgFee(ctx, b, args, blockNrOrHash, nil, 0, gasCap, b.ChainConfig())
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if l1DataFee.Cmp(available) >= 0 {
|
||||||
|
return 0, errors.New("insufficient funds for l1 fee")
|
||||||
|
}
|
||||||
|
available.Sub(available, l1DataFee)
|
||||||
|
|
||||||
allowance := new(big.Int).Div(available, feeCap)
|
allowance := new(big.Int).Div(available, feeCap)
|
||||||
|
|
||||||
// If the allowance is larger than maximum uint64, skip checking
|
// If the allowance is larger than maximum uint64, skip checking
|
||||||
|
|
@ -1655,7 +1711,12 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
||||||
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
|
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
|
||||||
config := vm.Config{Tracer: tracer, NoBaseFee: true}
|
config := vm.Config{Tracer: tracer, NoBaseFee: true}
|
||||||
vmenv, _ := b.GetEVM(ctx, msg, statedb, header, &config, nil)
|
vmenv, _ := b.GetEVM(ctx, msg, statedb, header, &config, nil)
|
||||||
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
signer := types.MakeSigner(b.ChainConfig(), header.Number, header.Time)
|
||||||
|
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.GasLimit), l1DataFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
|
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
|
||||||
}
|
}
|
||||||
|
|
@ -1834,6 +1895,7 @@ func marshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber u
|
||||||
"logsBloom": receipt.Bloom,
|
"logsBloom": receipt.Bloom,
|
||||||
"type": hexutil.Uint(tx.Type()),
|
"type": hexutil.Uint(tx.Type()),
|
||||||
"effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice),
|
"effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice),
|
||||||
|
"l1Fee": (*hexutil.Big)(receipt.L1Fee),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign receipt status or post state.
|
// Assign receipt status or post state.
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,7 @@ func (b *LesApiBackend) GetEVM(ctx context.Context, msg *core.Message, state *st
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||||
|
// will `VerifyFee` & `validateTx` in txPool.Add
|
||||||
return b.eth.txPool.Add(ctx, signedTx)
|
return b.eth.txPool.Add(ctx, signedTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
"github.com/ethereum/go-ethereum/light"
|
"github.com/ethereum/go-ethereum/light"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
)
|
)
|
||||||
|
|
||||||
// noopReleaser is returned in case there is no operation expected
|
// noopReleaser is returned in case there is no operation expected
|
||||||
|
|
@ -69,7 +70,11 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.
|
||||||
}
|
}
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{})
|
||||||
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, 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, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
// Ensure any modifications are committed to the state
|
// Ensure any modifications are committed to the state
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||||
)
|
)
|
||||||
|
|
@ -217,7 +218,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
|
||||||
context := core.NewEVMBlockContext(header, chain, nil)
|
context := core.NewEVMBlockContext(header, chain, nil)
|
||||||
vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true})
|
vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true})
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
result, _ := core.ApplyMessage(vmenv, msg, gp)
|
signer := types.MakeSigner(config, header.Number, header.Time)
|
||||||
|
l1DataFee, _ := fees.EstimateL1DataFeeForMessage(msg, header.BaseFee, config.ChainID, signer, st)
|
||||||
|
result, _ := core.ApplyMessage(vmenv, msg, gp, l1DataFee)
|
||||||
res = append(res, result.Return()...)
|
res = append(res, result.Return()...)
|
||||||
if st.Error() != nil {
|
if st.Error() != nil {
|
||||||
return res, st.Error()
|
return res, st.Error()
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -379,11 +380,25 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
|
||||||
return txpool.ErrNegativeValue
|
return txpool.ErrNegativeValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. Check balance >= transaction cost (V + GP * GL) to maintain compatibility with the logic without considering L1 data fee.
|
||||||
// Transactor should have enough funds to cover the costs
|
// Transactor should have enough funds to cover the costs
|
||||||
// cost == V + GP * GL
|
// cost == V + GP * GL
|
||||||
if b := currentState.GetBalance(from); b.Cmp(tx.Cost()) < 0 {
|
if b := currentState.GetBalance(from); b.Cmp(tx.Cost()) < 0 {
|
||||||
return core.ErrInsufficientFunds
|
return core.ErrInsufficientFunds
|
||||||
}
|
}
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to calculate L1 data fee, err: %w", err)
|
||||||
|
}
|
||||||
|
// Transactor should have enough funds to cover the costs
|
||||||
|
// cost == L1 data fee + V + GP * GL
|
||||||
|
if b := currentState.GetBalance(from); b.Cmp(new(big.Int).Add(tx.Cost(), l1DataFee)) < 0 {
|
||||||
|
return fmt.Errorf("invalid transaction: %w", core.ErrInsufficientFundsWithL1DataFee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Should supply enough intrinsic gas
|
// Should supply enough intrinsic gas
|
||||||
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
|
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
|
||||||
|
|
|
||||||
|
|
@ -345,6 +345,9 @@ type ScrollConfig struct {
|
||||||
// Maximum tx payload size of blocks that we produce [optional]
|
// Maximum tx payload size of blocks that we produce [optional]
|
||||||
MaxTxPayloadBytesPerBlock *int `json:"maxTxPayloadBytesPerBlock,omitempty"`
|
MaxTxPayloadBytesPerBlock *int `json:"maxTxPayloadBytesPerBlock,omitempty"`
|
||||||
|
|
||||||
|
// Transaction fee vault address [optional]
|
||||||
|
FeeVaultAddress *common.Address `json:"feeVaultAddress,omitempty"`
|
||||||
|
|
||||||
// L1 config
|
// L1 config
|
||||||
L1Config *L1Config `json:"l1Config,omitempty"`
|
L1Config *L1Config `json:"l1Config,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
@ -366,6 +369,10 @@ func (c *L1Config) String() string {
|
||||||
c.L1ChainId, c.L1MessageQueueAddress.Hex(), c.NumL1MessagesPerBlock, c.ScrollChainAddress.Hex())
|
c.L1ChainId, c.L1MessageQueueAddress.Hex(), c.NumL1MessagesPerBlock, c.ScrollChainAddress.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s ScrollConfig) FeeVaultEnabled() bool {
|
||||||
|
return s.FeeVaultAddress != nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s ScrollConfig) ShouldIncludeL1Messages() bool {
|
func (s ScrollConfig) ShouldIncludeL1Messages() bool {
|
||||||
return s.L1Config != nil && s.L1Config.NumL1MessagesPerBlock > 0
|
return s.L1Config != nil && s.L1Config.NumL1MessagesPerBlock > 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@ package fees
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -25,17 +23,17 @@ var (
|
||||||
// It should be a subset of the methods found on
|
// It should be a subset of the methods found on
|
||||||
// types.Message
|
// types.Message
|
||||||
type Message interface {
|
type Message interface {
|
||||||
From() common.Address
|
GetFrom() common.Address
|
||||||
To() *common.Address
|
GetTo() *common.Address
|
||||||
GasPrice() *big.Int
|
GetGasPrice() *big.Int
|
||||||
Gas() uint64
|
GetGasLimit() uint64
|
||||||
GasFeeCap() *big.Int
|
GetGasFeeCap() *big.Int
|
||||||
GasTipCap() *big.Int
|
GetGasTipCap() *big.Int
|
||||||
Value() *big.Int
|
GetValue() *big.Int
|
||||||
Nonce() uint64
|
GetNonce() uint64
|
||||||
Data() []byte
|
GetData() []byte
|
||||||
AccessList() types.AccessList
|
GetAccessList() types.AccessList
|
||||||
IsL1MessageTx() bool
|
GetIsL1MessageTx() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateDB represents the StateDB interface
|
// StateDB represents the StateDB interface
|
||||||
|
|
@ -46,7 +44,7 @@ type StateDB interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
func EstimateL1DataFeeForMessage(msg Message, baseFee, chainID *big.Int, signer types.Signer, state StateDB) (*big.Int, error) {
|
func EstimateL1DataFeeForMessage(msg Message, baseFee, chainID *big.Int, signer types.Signer, state StateDB) (*big.Int, error) {
|
||||||
if msg.IsL1MessageTx() {
|
if msg.GetIsL1MessageTx() {
|
||||||
return big.NewInt(0), nil
|
return big.NewInt(0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,7 +68,7 @@ func EstimateL1DataFeeForMessage(msg Message, baseFee, chainID *big.Int, signer
|
||||||
// asUnsignedTx turns a Message into a types.Transaction
|
// asUnsignedTx turns a Message into a types.Transaction
|
||||||
func asUnsignedTx(msg Message, baseFee, chainID *big.Int) *types.Transaction {
|
func asUnsignedTx(msg Message, baseFee, chainID *big.Int) *types.Transaction {
|
||||||
if baseFee == nil {
|
if baseFee == nil {
|
||||||
if msg.AccessList() == nil {
|
if msg.GetAccessList() == nil {
|
||||||
return asUnsignedLegacyTx(msg)
|
return asUnsignedLegacyTx(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,38 +80,38 @@ func asUnsignedTx(msg Message, baseFee, chainID *big.Int) *types.Transaction {
|
||||||
|
|
||||||
func asUnsignedLegacyTx(msg Message) *types.Transaction {
|
func asUnsignedLegacyTx(msg Message) *types.Transaction {
|
||||||
return types.NewTx(&types.LegacyTx{
|
return types.NewTx(&types.LegacyTx{
|
||||||
Nonce: msg.Nonce(),
|
Nonce: msg.GetNonce(),
|
||||||
To: msg.To(),
|
To: msg.GetTo(),
|
||||||
Value: msg.Value(),
|
Value: msg.GetValue(),
|
||||||
Gas: msg.Gas(),
|
Gas: msg.GetGasLimit(),
|
||||||
GasPrice: msg.GasPrice(),
|
GasPrice: msg.GetGasPrice(),
|
||||||
Data: msg.Data(),
|
Data: msg.GetData(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func asUnsignedAccessListTx(msg Message, chainID *big.Int) *types.Transaction {
|
func asUnsignedAccessListTx(msg Message, chainID *big.Int) *types.Transaction {
|
||||||
return types.NewTx(&types.AccessListTx{
|
return types.NewTx(&types.AccessListTx{
|
||||||
Nonce: msg.Nonce(),
|
Nonce: msg.GetNonce(),
|
||||||
To: msg.To(),
|
To: msg.GetTo(),
|
||||||
Value: msg.Value(),
|
Value: msg.GetValue(),
|
||||||
Gas: msg.Gas(),
|
Gas: msg.GetGasLimit(),
|
||||||
GasPrice: msg.GasPrice(),
|
GasPrice: msg.GetGasPrice(),
|
||||||
Data: msg.Data(),
|
Data: msg.GetData(),
|
||||||
AccessList: msg.AccessList(),
|
AccessList: msg.GetAccessList(),
|
||||||
ChainID: chainID,
|
ChainID: chainID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func asUnsignedDynamicTx(msg Message, chainID *big.Int) *types.Transaction {
|
func asUnsignedDynamicTx(msg Message, chainID *big.Int) *types.Transaction {
|
||||||
return types.NewTx(&types.DynamicFeeTx{
|
return types.NewTx(&types.DynamicFeeTx{
|
||||||
Nonce: msg.Nonce(),
|
Nonce: msg.GetNonce(),
|
||||||
To: msg.To(),
|
To: msg.GetTo(),
|
||||||
Value: msg.Value(),
|
Value: msg.GetValue(),
|
||||||
Gas: msg.Gas(),
|
Gas: msg.GetGasLimit(),
|
||||||
GasFeeCap: msg.GasFeeCap(),
|
GasFeeCap: msg.GetGasFeeCap(),
|
||||||
GasTipCap: msg.GasTipCap(),
|
GasTipCap: msg.GetGasTipCap(),
|
||||||
Data: msg.Data(),
|
Data: msg.GetData(),
|
||||||
AccessList: msg.AccessList(),
|
AccessList: msg.GetAccessList(),
|
||||||
ChainID: chainID,
|
ChainID: chainID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -189,37 +187,3 @@ func CalculateL1DataFee(tx *types.Transaction, state StateDB) (*big.Int, error)
|
||||||
l1DataFee := calculateEncodedL1DataFee(raw, overhead, l1BaseFee, scalar)
|
l1DataFee := calculateEncodedL1DataFee(raw, overhead, l1BaseFee, scalar)
|
||||||
return l1DataFee, nil
|
return l1DataFee, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateL2Fee(tx *types.Transaction) *big.Int {
|
|
||||||
l2GasLimit := new(big.Int).SetUint64(tx.Gas())
|
|
||||||
return new(big.Int).Mul(tx.GasPrice(), l2GasLimit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func VerifyFee(signer types.Signer, tx *types.Transaction, state StateDB) error {
|
|
||||||
from, err := types.Sender(signer, tx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("invalid transaction: invalid sender")
|
|
||||||
}
|
|
||||||
|
|
||||||
balance := state.GetBalance(from)
|
|
||||||
l2Fee := calculateL2Fee(tx)
|
|
||||||
l1DataFee, err := CalculateL1DataFee(tx, state)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid transaction: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cost := tx.Value()
|
|
||||||
cost = cost.Add(cost, l2Fee)
|
|
||||||
if balance.Cmp(cost) < 0 {
|
|
||||||
return errors.New("invalid transaction: insufficient funds for gas * price + value")
|
|
||||||
}
|
|
||||||
|
|
||||||
cost = cost.Add(cost, l1DataFee)
|
|
||||||
if balance.Cmp(cost) < 0 {
|
|
||||||
return errors.New("invalid transaction: insufficient funds for l1fee + gas * price + value")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: check GasPrice is in an expected range
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/fees"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
||||||
|
|
@ -254,9 +255,9 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
|
||||||
return nil, nil, nil, common.Hash{}, err
|
return nil, nil, nil, common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ttx types.Transaction
|
||||||
// Try to recover tx with current signer
|
// Try to recover tx with current signer
|
||||||
if len(post.TxBytes) != 0 {
|
if len(post.TxBytes) != 0 {
|
||||||
var ttx types.Transaction
|
|
||||||
err := ttx.UnmarshalBinary(post.TxBytes)
|
err := ttx.UnmarshalBinary(post.TxBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
triedb.Close()
|
triedb.Close()
|
||||||
|
|
@ -289,7 +290,11 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
|
||||||
snapshot := statedb.Snapshot()
|
snapshot := statedb.Snapshot()
|
||||||
gaspool := new(core.GasPool)
|
gaspool := new(core.GasPool)
|
||||||
gaspool.AddGas(block.GasLimit())
|
gaspool.AddGas(block.GasLimit())
|
||||||
_, err = core.ApplyMessage(evm, msg, gaspool)
|
l1DataFee, err := fees.CalculateL1DataFee(&ttx, statedb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, common.Hash{}, err
|
||||||
|
}
|
||||||
|
_, err = core.ApplyMessage(evm, msg, gaspool, l1DataFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
statedb.RevertToSnapshot(snapshot)
|
statedb.RevertToSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue