diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 273e5c14013d..274bc201ba4f 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -742,16 +742,17 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
// Execute the call.
msg := &core.Message{
- From: call.From,
- To: call.To,
- Value: call.Value,
- GasLimit: call.Gas,
- GasPrice: call.GasPrice,
- GasFeeCap: call.GasFeeCap,
- GasTipCap: call.GasTipCap,
- Data: call.Data,
- AccessList: call.AccessList,
- SkipAccountChecks: true,
+ From: call.From,
+ To: call.To,
+ Value: call.Value,
+ GasLimit: call.Gas,
+ GasPrice: call.GasPrice,
+ GasFeeCap: call.GasFeeCap,
+ GasTipCap: call.GasTipCap,
+ Data: call.Data,
+ AccessList: call.AccessList,
+ SkipNonceChecks: true,
+ SkipFromEOACheck: true,
}
feeCapacity := state.GetTRC21FeeCapacityFromState(stateDB)
if msg.To != nil {
diff --git a/core/state_processor.go b/core/state_processor.go
index 42e42f0c853b..48251abcdeaa 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -440,9 +440,18 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
}
*usedGas += result.UsedGas
+ if balanceFee != nil && result.Failed() {
+ state.PayFeeWithTRC21TxFail(statedb, msg.From, *to)
+ }
+
+ return MakeReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), result.UsedGas, balanceFee != nil, nil
+}
+
+// MakeReceipt generates the receipt object for a transaction given its execution result.
+func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt {
// Create a new receipt for the transaction, storing the intermediate root and gas used
// by the tx.
- receipt = &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
+ receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas}
if result.Failed() {
receipt.Status = types.ReceiptStatusFailed
} else {
@@ -452,7 +461,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
receipt.GasUsed = result.UsedGas
// If the transaction created a contract, store the creation address in the receipt.
- if msg.To == nil {
+ if tx.To() == nil {
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
}
@@ -462,10 +471,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
receipt.BlockHash = blockHash
receipt.BlockNumber = blockNumber
receipt.TransactionIndex = uint(statedb.TxIndex())
- if balanceFee != nil && result.Failed() {
- state.PayFeeWithTRC21TxFail(statedb, msg.From, *to)
- }
- return receipt, result.UsedGas, balanceFee != nil, nil
+ return receipt
}
func getCoinbaseOwner(bc *BlockChain, statedb *state.StateDB, header *types.Header, author *common.Address) common.Address {
diff --git a/core/state_transition.go b/core/state_transition.go
index 97488e6223f7..827e1edeb153 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -128,26 +128,30 @@ type Message struct {
Data []byte
AccessList types.AccessList
- // When SkipAccountCheckss is true, the message nonce is not checked against the
- // account nonce in state. It also disables checking that the sender is an EOA.
+ // When SkipNonceChecks is true, the message nonce is not checked against the
+ // account nonce in state.
// This field will be set to true for operations like RPC eth_call.
- SkipAccountChecks bool
+ SkipNonceChecks bool
+
+ // When SkipFromEOACheck is true, the message sender is not checked to be an EOA.
+ SkipFromEOACheck bool
}
// TransactionToMessage converts a transaction into a Message.
func TransactionToMessage(tx *types.Transaction, s types.Signer, balanceFee, blockNumber, baseFee *big.Int) (*Message, error) {
msg := &Message{
- Nonce: tx.Nonce(),
- GasLimit: tx.Gas(),
- GasPrice: new(big.Int).Set(tx.GasPrice()),
- GasFeeCap: new(big.Int).Set(tx.GasFeeCap()),
- GasTipCap: new(big.Int).Set(tx.GasTipCap()),
- To: tx.To(),
- Value: tx.Value(),
- Data: tx.Data(),
- AccessList: tx.AccessList(),
- SkipAccountChecks: false,
- BalanceTokenFee: balanceFee,
+ Nonce: tx.Nonce(),
+ GasLimit: tx.Gas(),
+ GasPrice: new(big.Int).Set(tx.GasPrice()),
+ GasFeeCap: new(big.Int).Set(tx.GasFeeCap()),
+ GasTipCap: new(big.Int).Set(tx.GasTipCap()),
+ To: tx.To(),
+ Value: tx.Value(),
+ Data: tx.Data(),
+ AccessList: tx.AccessList(),
+ SkipNonceChecks: false,
+ SkipFromEOACheck: false,
+ BalanceTokenFee: balanceFee,
}
if balanceFee != nil {
@@ -282,7 +286,7 @@ func (st *StateTransition) buyGas() error {
func (st *StateTransition) preCheck() error {
// Only check transactions that are not fake
msg := st.msg
- if !msg.SkipAccountChecks {
+ if !msg.SkipNonceChecks {
// Make sure this transaction's nonce is correct.
stNonce := st.state.GetNonce(msg.From)
if msgNonce := msg.Nonce; stNonce < msgNonce {
@@ -295,13 +299,15 @@ func (st *StateTransition) preCheck() error {
return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
msg.From.Hex(), stNonce)
}
+ }
+ if !msg.SkipFromEOACheck {
// Make sure the sender is an EOA
- if codeHash := st.state.GetCodeHash(msg.From); codeHash != types.EmptyCodeHash && codeHash != (common.Hash{}) {
+ codeHash := st.state.GetCodeHash(msg.From)
+ if codeHash != (common.Hash{}) && codeHash != types.EmptyCodeHash {
return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
msg.From.Hex(), codeHash)
}
}
-
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) {
// Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call)
diff --git a/core/token_validator.go b/core/token_validator.go
index 9b09ccd7d37a..174a793e77b3 100644
--- a/core/token_validator.go
+++ b/core/token_validator.go
@@ -87,16 +87,17 @@ func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext,
// Execute the call.
msg := &Message{
- From: call.From,
- To: call.To,
- Value: call.Value,
- GasLimit: call.Gas,
- GasPrice: call.GasPrice,
- GasFeeCap: call.GasFeeCap,
- GasTipCap: call.GasTipCap,
- Data: call.Data,
- AccessList: call.AccessList,
- SkipAccountChecks: true,
+ From: call.From,
+ To: call.To,
+ Value: call.Value,
+ GasLimit: call.Gas,
+ GasPrice: call.GasPrice,
+ GasFeeCap: call.GasFeeCap,
+ GasTipCap: call.GasTipCap,
+ Data: call.Data,
+ AccessList: call.AccessList,
+ SkipNonceChecks: true,
+ SkipFromEOACheck: true,
}
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
if msg.To != nil {
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index e2f6a3ae7297..14c47186bbeb 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -20,6 +20,7 @@ import (
"crypto/sha256"
"encoding/binary"
"errors"
+ "maps"
"math"
"math/big"
@@ -41,9 +42,12 @@ type PrecompiledContract interface {
Run(input []byte) ([]byte, error) // Run runs the precompiled contract
}
+// PrecompiledContracts contains the precompiled contracts supported at the given fork.
+type PrecompiledContracts map[common.Address]PrecompiledContract
+
// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
// contracts used in the Frontier and Homestead releases.
-var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{
+var PrecompiledContractsHomestead = PrecompiledContracts{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
@@ -52,7 +56,7 @@ var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{
// PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum
// contracts used in the Byzantium release.
-var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
+var PrecompiledContractsByzantium = PrecompiledContracts{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
@@ -69,7 +73,7 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
// PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum
// contracts used in the Istanbul release.
-var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
+var PrecompiledContractsIstanbul = PrecompiledContracts{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
@@ -85,7 +89,7 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{42}): &XDCxEpochPrice{},
}
-var PrecompiledContractsXDCv2 = map[common.Address]PrecompiledContract{
+var PrecompiledContractsXDCv2 = PrecompiledContracts{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
@@ -97,7 +101,7 @@ var PrecompiledContractsXDCv2 = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{9}): &blake2F{},
}
-var PrecompiledContractsEIP1559 = map[common.Address]PrecompiledContract{
+var PrecompiledContractsEIP1559 = PrecompiledContracts{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
@@ -135,7 +139,27 @@ func init() {
}
}
-// ActivePrecompiles returns the precompiles enabled with the current configuration.
+func activePrecompiledContracts(rules params.Rules) PrecompiledContracts {
+ switch {
+ case rules.IsEIP1559:
+ return PrecompiledContractsEIP1559
+ case rules.IsXDCxDisable:
+ return PrecompiledContractsXDCv2
+ case rules.IsIstanbul:
+ return PrecompiledContractsIstanbul
+ case rules.IsByzantium:
+ return PrecompiledContractsByzantium
+ default:
+ return PrecompiledContractsHomestead
+ }
+}
+
+// ActivePrecompiledContracts returns a copy of precompiled contracts enabled with the current configuration.
+func ActivePrecompiledContracts(rules params.Rules) PrecompiledContracts {
+ return maps.Clone(activePrecompiledContracts(rules))
+}
+
+// ActivePrecompiles returns the precompile addresses enabled with the current configuration.
func ActivePrecompiles(rules params.Rules) []common.Address {
switch {
case rules.IsEIP1559:
diff --git a/core/vm/evm.go b/core/vm/evm.go
index dcace041ea9b..d3d278fda14d 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -41,20 +41,7 @@ type (
)
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
- var precompiles map[common.Address]PrecompiledContract
- switch {
- case evm.chainRules.IsEIP1559:
- precompiles = PrecompiledContractsEIP1559
- case evm.chainRules.IsXDCxDisable:
- precompiles = PrecompiledContractsXDCv2
- case evm.chainRules.IsIstanbul:
- precompiles = PrecompiledContractsIstanbul
- case evm.chainRules.IsByzantium:
- precompiles = PrecompiledContractsByzantium
- default:
- precompiles = PrecompiledContractsHomestead
- }
- p, ok := precompiles[addr]
+ p, ok := evm.precompiles[addr]
return p, ok
}
@@ -124,19 +111,13 @@ type EVM struct {
// available gas is calculated in gasCall* according to the 63/64 rule and later
// applied in opCall*.
callGasTemp uint64
+ // precompiles holds the precompiled contracts for the current epoch
+ precompiles map[common.Address]PrecompiledContract
}
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
// only ever be used *once*.
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, config Config) *EVM {
- // If basefee tracking is disabled (eth_call, eth_estimateGas, etc), and no
- // gas prices were specified, lower the basefee to 0 to avoid breaking EVM
- // invariants (basefee < feecap)
- if config.NoBaseFee {
- if txCtx.GasPrice.BitLen() == 0 {
- blockCtx.BaseFee = new(big.Int)
- }
- }
evm := &EVM{
Context: blockCtx,
TxContext: txCtx,
@@ -146,11 +127,18 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStat
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber),
}
-
+ evm.precompiles = activePrecompiledContracts(evm.chainRules)
evm.interpreter = NewEVMInterpreter(evm)
return evm
}
+// SetPrecompiles sets the precompiled contracts for the EVM.
+// This method is only used through RPC calls.
+// It is not thread-safe.
+func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) {
+ evm.precompiles = precompiles
+}
+
// Reset resets the EVM with a new transaction context.Reset
// This is not threadsafe and should only be done very cautiously.
func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) {
diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go
index d531bfbf41b5..7f0583f7cadf 100644
--- a/eth/gasestimator/gasestimator.go
+++ b/eth/gasestimator/gasestimator.go
@@ -170,8 +170,13 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio
evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil)
dirtyState = opts.State.Copy()
- evm = vm.NewEVM(evmContext, msgContext, dirtyState, nil, opts.Config, vm.Config{NoBaseFee: true})
)
+ // Lower the basefee to 0 to avoid breaking EVM
+ // invariants (basefee < feecap).
+ if msgContext.GasPrice.Sign() == 0 {
+ evmContext.BaseFee = new(big.Int)
+ }
+ evm := vm.NewEVM(evmContext, msgContext, dirtyState, nil, opts.Config, vm.Config{NoBaseFee: true})
// Monitor the outer context and interrupt the EVM upon cancellation. To avoid
// a dangling goroutine until the outer estimation finishes, create an internal
// context for the lifetime of this method call.
diff --git a/eth/tracers/api.go b/eth/tracers/api.go
index 458e033b3ce5..082e39206f65 100644
--- a/eth/tracers/api.go
+++ b/eth/tracers/api.go
@@ -812,20 +812,28 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
// Apply the customization rules if required.
if config != nil {
- if err := config.StateOverrides.Apply(statedb); err != nil {
+ config.BlockOverrides.Apply(&vmctx)
+ rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber)
+
+ precompiles := vm.ActivePrecompiledContracts(rules)
+ if err := config.StateOverrides.Apply(statedb, precompiles); err != nil {
return nil, err
}
- config.BlockOverrides.Apply(&vmctx)
}
// Execute the trace
if err := args.CallDefaults(api.backend.RPCGasCap(), vmctx.BaseFee, api.backend.ChainConfig().ChainID); err != nil {
return nil, err
}
var (
- msg = args.ToMessage(api.backend, vmctx.BaseFee)
- tx = args.ToTransaction()
+ msg = args.ToMessage(api.backend, vmctx.BaseFee, true, true)
+ tx = args.ToTransaction(types.LegacyTxType)
traceConfig *TraceConfig
)
+ // Lower the basefee to 0 to avoid breaking EVM
+ // invariants (basefee < feecap).
+ if msg.GasPrice.Sign() == 0 {
+ vmctx.BaseFee = new(big.Int)
+ }
if config != nil {
traceConfig = &config.TraceConfig
}
diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go
index ac2255bd3df7..94960319fa61 100644
--- a/ethclient/gethclient/gethclient.go
+++ b/ethclient/gethclient/gethclient.go
@@ -287,9 +287,9 @@ func (o BlockOverrides) MarshalJSON() ([]byte, error) {
Difficulty *hexutil.Big `json:"difficulty,omitempty"`
Time hexutil.Uint64 `json:"time,omitempty"`
GasLimit hexutil.Uint64 `json:"gasLimit,omitempty"`
- Coinbase *common.Address `json:"coinbase,omitempty"`
- Random *common.Hash `json:"random,omitempty"`
- BaseFee *hexutil.Big `json:"baseFee,omitempty"`
+ Coinbase *common.Address `json:"feeRecipient,omitempty"`
+ Random *common.Hash `json:"prevRandao,omitempty"`
+ BaseFee *hexutil.Big `json:"baseFeePerGas,omitempty"`
}
output := override{
diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go
index 89cfa7bf1b65..0a5502171a8a 100644
--- a/ethclient/gethclient/gethclient_test.go
+++ b/ethclient/gethclient/gethclient_test.go
@@ -87,7 +87,7 @@ func TestBlockOverridesMarshal(t *testing.T) {
bo: BlockOverrides{
Coinbase: common.HexToAddress("0x1111111111111111111111111111111111111111"),
},
- want: `{"coinbase":"0x1111111111111111111111111111111111111111"}`,
+ want: `{"feeRecipient":"0x1111111111111111111111111111111111111111"}`,
},
{
bo: BlockOverrides{
@@ -97,7 +97,7 @@ func TestBlockOverridesMarshal(t *testing.T) {
GasLimit: 4,
BaseFee: big.NewInt(5),
},
- want: `{"number":"0x1","difficulty":"0x2","time":"0x3","gasLimit":"0x4","baseFee":"0x5"}`,
+ want: `{"number":"0x1","difficulty":"0x2","time":"0x3","gasLimit":"0x4","baseFeePerGas":"0x5"}`,
},
} {
marshalled, err := json.Marshal(&tt.bo)
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index dcf48f138c55..6d10f5367573 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -21,7 +21,7 @@ import (
"context"
"errors"
"fmt"
- gomath "math"
+ "maps"
"math/big"
"strings"
"time"
@@ -561,22 +561,54 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp
// if statDiff is set, all diff will be applied first and then execute the call
// message.
type OverrideAccount struct {
- Nonce *hexutil.Uint64 `json:"nonce"`
- Code *hexutil.Bytes `json:"code"`
- Balance *hexutil.Big `json:"balance"`
- State map[common.Hash]common.Hash `json:"state"`
- StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
+ Nonce *hexutil.Uint64 `json:"nonce"`
+ Code *hexutil.Bytes `json:"code"`
+ Balance *hexutil.Big `json:"balance"`
+ State map[common.Hash]common.Hash `json:"state"`
+ StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
+ MovePrecompileTo *common.Address `json:"movePrecompileToAddress"`
}
// StateOverride is the collection of overridden accounts.
type StateOverride map[common.Address]OverrideAccount
+func (diff *StateOverride) has(address common.Address) bool {
+ _, ok := (*diff)[address]
+ return ok
+}
+
// Apply overrides the fields of specified accounts into the given state.
-func (diff *StateOverride) Apply(statedb *state.StateDB) error {
+func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles vm.PrecompiledContracts) error {
if diff == nil {
return nil
}
+ // Tracks destinations of precompiles that were moved.
+ dirtyAddrs := make(map[common.Address]struct{})
for addr, account := range *diff {
+ // If a precompile was moved to this address already, it can't be overridden.
+ if _, ok := dirtyAddrs[addr]; ok {
+ return fmt.Errorf("account %s has already been overridden by a precompile", addr.Hex())
+ }
+ p, isPrecompile := precompiles[addr]
+ // The MoveTo feature makes it possible to move a precompile
+ // code to another address. If the target address is another precompile
+ // the code for the latter is lost for this session.
+ // Note the destination account is not cleared upon move.
+ if account.MovePrecompileTo != nil {
+ if !isPrecompile {
+ return fmt.Errorf("account %s is not a precompile", addr.Hex())
+ }
+ // Refuse to move a precompile to an address that has been
+ // or will be overridden.
+ if diff.has(*account.MovePrecompileTo) {
+ return fmt.Errorf("account %s is already overridden", account.MovePrecompileTo.Hex())
+ }
+ precompiles[*account.MovePrecompileTo] = p
+ dirtyAddrs[*account.MovePrecompileTo] = struct{}{}
+ }
+ if isPrecompile {
+ delete(precompiles, addr)
+ }
// Override account nonce.
if account.Nonce != nil {
statedb.SetNonce(addr, uint64(*account.Nonce))
@@ -612,41 +644,74 @@ func (diff *StateOverride) Apply(statedb *state.StateDB) error {
// BlockOverrides is a set of header fields to override.
type BlockOverrides struct {
- Number *hexutil.Big
- Difficulty *hexutil.Big
- Time *hexutil.Uint64
- GasLimit *hexutil.Uint64
- Coinbase *common.Address
- Random *common.Hash
- BaseFee *hexutil.Big
+ Number *hexutil.Big
+ Difficulty *hexutil.Big
+ Time *hexutil.Uint64
+ GasLimit *hexutil.Uint64
+ FeeRecipient *common.Address
+ PrevRandao *common.Hash
+ BaseFeePerGas *hexutil.Big
}
// Apply overrides the given header fields into the given block context.
-func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
- if diff == nil {
+func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
+ if o == nil {
return
}
- if diff.Number != nil {
- blockCtx.BlockNumber = diff.Number.ToInt()
+ if o.Number != nil {
+ blockCtx.BlockNumber = o.Number.ToInt()
+ }
+ if o.Difficulty != nil {
+ blockCtx.Difficulty = o.Difficulty.ToInt()
+ }
+ if o.Time != nil {
+ blockCtx.Time = uint64(*o.Time)
+ }
+ if o.GasLimit != nil {
+ blockCtx.GasLimit = uint64(*o.GasLimit)
+ }
+ if o.FeeRecipient != nil {
+ blockCtx.Coinbase = *o.FeeRecipient
+ }
+ if o.PrevRandao != nil {
+ blockCtx.Random = o.PrevRandao
}
- if diff.Difficulty != nil {
- blockCtx.Difficulty = diff.Difficulty.ToInt()
+ if o.BaseFeePerGas != nil {
+ blockCtx.BaseFee = o.BaseFeePerGas.ToInt()
}
- if diff.Time != nil {
- blockCtx.Time = uint64(*diff.Time)
+}
+
+// MakeHeader returns a new header object with the overridden
+// fields.
+// Note: MakeHeader ignores BlobBaseFee if set. That's because
+// header has no such field.
+func (o *BlockOverrides) MakeHeader(header *types.Header) *types.Header {
+ if o == nil {
+ return header
+ }
+ h := types.CopyHeader(header)
+ if o.Number != nil {
+ h.Number = o.Number.ToInt()
}
- if diff.GasLimit != nil {
- blockCtx.GasLimit = uint64(*diff.GasLimit)
+ if o.Difficulty != nil {
+ h.Difficulty = o.Difficulty.ToInt()
}
- if diff.Coinbase != nil {
- blockCtx.Coinbase = *diff.Coinbase
+ if o.Time != nil {
+ h.Time = uint64(*o.Time)
}
- if diff.Random != nil {
- blockCtx.Random = diff.Random
+ if o.GasLimit != nil {
+ h.GasLimit = uint64(*o.GasLimit)
}
- if diff.BaseFee != nil {
- blockCtx.BaseFee = diff.BaseFee.ToInt()
+ if o.FeeRecipient != nil {
+ h.Coinbase = *o.FeeRecipient
}
+ if o.PrevRandao != nil {
+ h.MixDigest = *o.PrevRandao
+ }
+ if o.BaseFeePerGas != nil {
+ h.BaseFee = o.BaseFeePerGas.ToInt()
+ }
+ return h
}
func (api *BlockChainAPI) GetBlockSignersByHash(ctx context.Context, blockHash common.Hash) ([]common.Address, error) {
@@ -1167,7 +1232,14 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
}
func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, block *types.Block, overrides *StateOverride, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
- if err := overrides.Apply(state); err != nil {
+ header := block.Header()
+ blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
+ if blockOverrides != nil {
+ blockOverrides.Apply(&blockCtx)
+ }
+ rules := b.ChainConfig().Rules(blockCtx.BlockNumber)
+ precompiles := maps.Clone(vm.ActivePrecompiledContracts(rules))
+ if err := overrides.Apply(state, precompiles); err != nil {
return nil, err
}
@@ -1182,7 +1254,10 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S
// Make sure the context is cancelled when the call has completed
// this makes sure resources are cleaned up.
defer cancel()
+ return applyMessage(ctx, b, args, state, block, timeout, new(core.GasPool).AddGas(globalGasCap), &blockCtx, &vm.Config{NoBaseFee: true}, precompiles, true)
+}
+func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, block *types.Block, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config, precompiles vm.PrecompiledContracts, skipChecks bool) (*core.ExecutionResult, error) {
header := block.Header()
author, err := b.Engine().Author(header)
if err != nil {
@@ -1194,20 +1269,32 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S
}
// Get a new instance of the EVM.
- blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
- if blockOverrides != nil {
- blockOverrides.Apply(&blockCtx)
- }
- if err := args.CallDefaults(globalGasCap, blockCtx.BaseFee, b.ChainConfig().ChainID); err != nil {
+ if err := args.CallDefaults(gp.Gas(), blockContext.BaseFee, b.ChainConfig().ChainID); err != nil {
return nil, err
}
- msg := args.ToMessage(b, blockCtx.BaseFee)
+ msg := args.ToMessage(b, header.BaseFee, skipChecks, skipChecks)
msg.BalanceTokenFee = new(big.Int).SetUint64(msg.GasLimit)
msg.BalanceTokenFee.Mul(msg.BalanceTokenFee, msg.GasPrice)
- evm, vmError, err := b.GetEVM(ctx, msg, state, XDCxState, header, &vm.Config{NoBaseFee: true}, &blockCtx)
+ // Lower the basefee to 0 to avoid breaking EVM
+ // invariants (basefee < feecap).
+ if msg.GasPrice.Sign() == 0 {
+ blockContext.BaseFee = new(big.Int)
+ }
+ evm, vmError, err := b.GetEVM(ctx, msg, state, XDCxState, header, vmConfig, blockContext)
if err != nil {
return nil, err
}
+ if err := vmError(); err != nil {
+ return nil, err
+ }
+ if precompiles != nil {
+ evm.SetPrecompiles(precompiles)
+ }
+
+ return applyMessageWithEVM(ctx, evm, msg, state, timeout, gp)
+}
+
+func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, state *state.StateDB, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) {
// Wait for the context to be done and cancel the evm. Even if the
// EVM has finished, cancelling may be done (repeatedly)
go func() {
@@ -1216,9 +1303,8 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S
}()
// Execute the message.
- gp := new(core.GasPool).AddGas(gomath.MaxUint64)
result, err := core.ApplyMessage(evm, msg, gp, common.Address{})
- if err := vmError(); err != nil {
+ if err := state.Error(); err != nil {
return nil, err
}
@@ -1254,6 +1340,41 @@ func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockN
return result.Return(), result.Err
}
+// SimulateV1 executes series of transactions on top of a base state.
+// The transactions are packed into blocks. For each block, block header
+// fields can be overridden. The state can also be overridden prior to
+// execution of each block.
+//
+// Note, this function doesn't make any changes in the state/blockchain and is
+// useful to execute and retrieve values.
+func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrOrHash *rpc.BlockNumberOrHash) ([]map[string]interface{}, error) {
+ if len(opts.BlockStateCalls) == 0 {
+ return nil, &invalidParamsError{message: "empty input"}
+ } else if len(opts.BlockStateCalls) > maxSimulateBlocks {
+ return nil, &clientLimitExceededError{message: "too many blocks"}
+ }
+ if blockNrOrHash == nil {
+ n := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
+ blockNrOrHash = &n
+ }
+ state, base, err := api.b.StateAndHeaderByNumberOrHash(ctx, *blockNrOrHash)
+ if state == nil || err != nil {
+ return nil, err
+ }
+ sim := &simulator{
+ b: api.b,
+ state: state,
+ base: base,
+ chainConfig: api.b.ChainConfig(),
+ // Each tx and all the series of txes shouldn't consume more gas than cap
+ gp: new(core.GasPool).AddGas(api.b.RPCGasCap()),
+ traceTransfers: opts.TraceTransfers,
+ validate: opts.Validation,
+ fullTx: opts.ReturnFullTransactions,
+ }
+ return sim.execute(ctx, opts.BlockStateCalls)
+}
+
// DoEstimateGas returns the lowest possible gas limit that allows the transaction to run
// successfully at block `blockNrOrHash`. It returns error if the transaction would revert, or if
// there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil &
@@ -1264,7 +1385,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
if state == nil || err != nil {
return 0, err
}
- if err = overrides.Apply(state); err != nil {
+ if err = overrides.Apply(state, nil); err != nil {
return 0, err
}
// Construct the gas estimator option from the user input
@@ -1282,7 +1403,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
if err := args.CallDefaults(gasCap, header.BaseFee, b.ChainConfig().ChainID); err != nil {
return 0, err
}
- call := args.ToMessage(b, header.BaseFee)
+ call := args.ToMessage(b, header.BaseFee, true, true)
// Run the gas estimation andwrap any revertals into a custom return
estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap)
@@ -1718,7 +1839,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
statedb := db.Copy()
// Set the accesslist to the last al
args.AccessList = &accessList
- msg := args.ToMessage(b, header.BaseFee)
+ msg := args.ToMessage(b, header.BaseFee, true, true)
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
var balanceTokenFee *big.Int
@@ -1734,9 +1855,14 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
if err != nil {
return nil, 0, nil, err
}
+ // Lower the basefee to 0 to avoid breaking EVM
+ // invariants (basefee < feecap).
+ if msg.GasPrice.Sign() == 0 {
+ vmenv.Context.BaseFee = new(big.Int)
+ }
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit), common.Address{})
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(types.LegacyTxType).Hash(), err)
}
if tracer.Equal(prevTracer) {
return accessList, res.UsedGas, res.Err, nil
@@ -2025,7 +2151,7 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr
return common.Hash{}, err
}
// Assemble the transaction and sign with the wallet
- tx := args.ToTransaction()
+ tx := args.ToTransaction(types.LegacyTxType)
var chainID *big.Int
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
@@ -2047,7 +2173,7 @@ func (s *TransactionAPI) FillTransaction(ctx context.Context, args TransactionAr
return nil, err
}
// Assemble the transaction and obtain rlp
- tx := args.ToTransaction()
+ tx := args.ToTransaction(types.LegacyTxType)
data, err := tx.MarshalBinary()
if err != nil {
return nil, err
@@ -2950,7 +3076,7 @@ func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionAr
return nil, err
}
// Before actually sign the transaction, ensure the transaction fee is reasonable.
- tx := args.ToTransaction()
+ tx := args.ToTransaction(types.LegacyTxType)
if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
return nil, err
}
@@ -2998,7 +3124,7 @@ func (s *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, g
if err := sendArgs.setDefaults(ctx, s.b, false); err != nil {
return common.Hash{}, err
}
- matchTx := sendArgs.ToTransaction()
+ matchTx := sendArgs.ToTransaction(types.LegacyTxType)
// Before replacing the old transaction, ensure the _new_ transaction fee is reasonable.
var price = matchTx.GasPrice()
@@ -3029,7 +3155,7 @@ func (s *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, g
if gasLimit != nil && *gasLimit != 0 {
sendArgs.Gas = gasLimit
}
- signedTx, err := s.sign(sendArgs.from(), sendArgs.ToTransaction())
+ signedTx, err := s.sign(sendArgs.from(), sendArgs.ToTransaction(types.LegacyTxType))
if err != nil {
return common.Hash{}, err
}
diff --git a/internal/ethapi/errors.go b/internal/ethapi/errors.go
index 207f363cca89..81b3dfb548f2 100644
--- a/internal/ethapi/errors.go
+++ b/internal/ethapi/errors.go
@@ -17,10 +17,12 @@
package ethapi
import (
+ "errors"
"fmt"
"github.com/XinFinOrg/XDPoSChain/accounts/abi"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
+ "github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/vm"
)
@@ -55,3 +57,93 @@ func newRevertError(revert []byte) *revertError {
reason: hexutil.Encode(revert),
}
}
+
+type callError struct {
+ Message string `json:"message"`
+ Code int `json:"code"`
+ Data string `json:"data,omitempty"`
+}
+
+type invalidTxError struct {
+ Message string `json:"message"`
+ Code int `json:"code"`
+}
+
+func (e *invalidTxError) Error() string { return e.Message }
+func (e *invalidTxError) ErrorCode() int { return e.Code }
+
+const (
+ errCodeNonceTooHigh = -38011
+ errCodeNonceTooLow = -38010
+ errCodeIntrinsicGas = -38013
+ errCodeInsufficientFunds = -38014
+ errCodeBlockGasLimitReached = -38015
+ errCodeBlockNumberInvalid = -38020
+ errCodeBlockTimestampInvalid = -38021
+ errCodeSenderIsNotEOA = -38024
+ errCodeMaxInitCodeSizeExceeded = -38025
+ errCodeClientLimitExceeded = -38026
+ errCodeInternalError = -32603
+ errCodeInvalidParams = -32602
+ errCodeReverted = -32000
+ errCodeVMError = -32015
+)
+
+func txValidationError(err error) *invalidTxError {
+ if err == nil {
+ return nil
+ }
+ switch {
+ case errors.Is(err, core.ErrNonceTooHigh):
+ return &invalidTxError{Message: err.Error(), Code: errCodeNonceTooHigh}
+ case errors.Is(err, core.ErrNonceTooLow):
+ return &invalidTxError{Message: err.Error(), Code: errCodeNonceTooLow}
+ case errors.Is(err, core.ErrSenderNoEOA):
+ return &invalidTxError{Message: err.Error(), Code: errCodeSenderIsNotEOA}
+ case errors.Is(err, core.ErrFeeCapVeryHigh):
+ return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
+ case errors.Is(err, core.ErrTipVeryHigh):
+ return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
+ case errors.Is(err, core.ErrTipAboveFeeCap):
+ return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
+ case errors.Is(err, core.ErrFeeCapTooLow):
+ return &invalidTxError{Message: err.Error(), Code: errCodeInvalidParams}
+ case errors.Is(err, core.ErrInsufficientFunds):
+ return &invalidTxError{Message: err.Error(), Code: errCodeInsufficientFunds}
+ case errors.Is(err, core.ErrIntrinsicGas):
+ return &invalidTxError{Message: err.Error(), Code: errCodeIntrinsicGas}
+ case errors.Is(err, core.ErrInsufficientFundsForTransfer):
+ return &invalidTxError{Message: err.Error(), Code: errCodeInsufficientFunds}
+ case errors.Is(err, core.ErrMaxInitCodeSizeExceeded):
+ return &invalidTxError{Message: err.Error(), Code: errCodeMaxInitCodeSizeExceeded}
+ }
+ return &invalidTxError{
+ Message: err.Error(),
+ Code: errCodeInternalError,
+ }
+}
+
+type invalidParamsError struct{ message string }
+
+func (e *invalidParamsError) Error() string { return e.message }
+func (e *invalidParamsError) ErrorCode() int { return errCodeInvalidParams }
+
+type clientLimitExceededError struct{ message string }
+
+func (e *clientLimitExceededError) Error() string { return e.message }
+func (e *clientLimitExceededError) ErrorCode() int { return errCodeClientLimitExceeded }
+
+type invalidBlockNumberError struct{ message string }
+
+func (e *invalidBlockNumberError) Error() string { return e.message }
+func (e *invalidBlockNumberError) ErrorCode() int { return errCodeBlockNumberInvalid }
+
+type invalidBlockTimestampError struct{ message string }
+
+func (e *invalidBlockTimestampError) Error() string { return e.message }
+func (e *invalidBlockTimestampError) ErrorCode() int { return errCodeBlockTimestampInvalid }
+
+type blockGasLimitReachedError struct{ message string }
+
+func (e *blockGasLimitReachedError) Error() string { return e.message }
+func (e *blockGasLimitReachedError) ErrorCode() int { return errCodeBlockGasLimitReached }
diff --git a/internal/ethapi/logtracer.go b/internal/ethapi/logtracer.go
new file mode 100644
index 000000000000..867a178def6c
--- /dev/null
+++ b/internal/ethapi/logtracer.go
@@ -0,0 +1,151 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package ethapi
+
+import (
+ "math/big"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/core/tracing"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/core/vm"
+)
+
+var (
+ // keccak256("Transfer(address,address,uint256)")
+ transferTopic = common.HexToHash("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")
+ // ERC-7528
+ transferAddress = common.HexToAddress("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE")
+)
+
+// tracer is a simple tracer that records all logs and
+// ether transfers. Transfers are recorded as if they
+// were logs. Transfer events include:
+// - tx value
+// - call value
+// - self destructs
+//
+// The log format for a transfer is:
+// - address: 0x0000000000000000000000000000000000000000
+// - data: Value
+// - topics:
+// - Transfer(address,address,uint256)
+// - Sender address
+// - Recipient address
+type tracer struct {
+ // logs keeps logs for all open call frames.
+ // This lets us clear logs for failed calls.
+ logs [][]*types.Log
+ count int
+ traceTransfers bool
+ blockNumber uint64
+ blockHash common.Hash
+ txHash common.Hash
+ txIdx uint
+}
+
+func newTracer(traceTransfers bool, blockNumber uint64, blockHash, txHash common.Hash, txIndex uint) *tracer {
+ return &tracer{
+ traceTransfers: traceTransfers,
+ blockNumber: blockNumber,
+ blockHash: blockHash,
+ txHash: txHash,
+ txIdx: txIndex,
+ }
+}
+
+func (t *tracer) Hooks() *tracing.Hooks {
+ return &tracing.Hooks{
+ OnEnter: t.onEnter,
+ OnExit: t.onExit,
+ OnLog: t.onLog,
+ }
+}
+
+func (t *tracer) onEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+ t.logs = append(t.logs, make([]*types.Log, 0))
+ if vm.OpCode(typ) != vm.DELEGATECALL && value != nil && value.Cmp(common.Big0) > 0 {
+ t.captureTransfer(from, to, value)
+ }
+}
+
+func (t *tracer) onExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
+ if depth == 0 {
+ t.onEnd(reverted)
+ return
+ }
+ size := len(t.logs)
+ if size <= 1 {
+ return
+ }
+ // pop call
+ call := t.logs[size-1]
+ t.logs = t.logs[:size-1]
+ size--
+
+ // Clear logs if call failed.
+ if !reverted {
+ t.logs[size-1] = append(t.logs[size-1], call...)
+ }
+}
+
+func (t *tracer) onEnd(reverted bool) {
+ if reverted {
+ t.logs[0] = nil
+ }
+}
+
+func (t *tracer) onLog(log *types.Log) {
+ t.captureLog(log.Address, log.Topics, log.Data)
+}
+
+func (t *tracer) captureLog(address common.Address, topics []common.Hash, data []byte) {
+ t.logs[len(t.logs)-1] = append(t.logs[len(t.logs)-1], &types.Log{
+ Address: address,
+ Topics: topics,
+ Data: data,
+ BlockNumber: t.blockNumber,
+ BlockHash: t.blockHash,
+ TxHash: t.txHash,
+ TxIndex: t.txIdx,
+ Index: uint(t.count),
+ })
+ t.count++
+}
+
+func (t *tracer) captureTransfer(from, to common.Address, value *big.Int) {
+ if !t.traceTransfers {
+ return
+ }
+ topics := []common.Hash{
+ transferTopic,
+ common.BytesToHash(from.Bytes()),
+ common.BytesToHash(to.Bytes()),
+ }
+ t.captureLog(transferAddress, topics, common.BigToHash(value).Bytes())
+}
+
+// reset prepares the tracer for the next transaction.
+func (t *tracer) reset(txHash common.Hash, txIdx uint) {
+ t.logs = nil
+ t.txHash = txHash
+ t.txIdx = txIdx
+}
+
+func (t *tracer) Logs() []*types.Log {
+ return t.logs[0]
+}
diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go
new file mode 100644
index 000000000000..47785017aaa2
--- /dev/null
+++ b/internal/ethapi/simulate.go
@@ -0,0 +1,386 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package ethapi
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "maps"
+ "math/big"
+ "time"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/hexutil"
+ "github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
+ "github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/state"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/core/vm"
+ "github.com/XinFinOrg/XDPoSChain/params"
+ "github.com/XinFinOrg/XDPoSChain/rpc"
+ "github.com/XinFinOrg/XDPoSChain/trie"
+)
+
+const (
+ // maxSimulateBlocks is the maximum number of blocks that can be simulated
+ // in a single request.
+ maxSimulateBlocks = 256
+
+ // timestampIncrement is the default increment between block timestamps.
+ timestampIncrement = 1
+)
+
+// simBlock is a batch of calls to be simulated sequentially.
+type simBlock struct {
+ BlockOverrides *BlockOverrides
+ StateOverrides *StateOverride
+ Calls []TransactionArgs
+}
+
+// simCallResult is the result of a simulated call.
+type simCallResult struct {
+ ReturnValue hexutil.Bytes `json:"returnData"`
+ Logs []*types.Log `json:"logs"`
+ GasUsed hexutil.Uint64 `json:"gasUsed"`
+ Status hexutil.Uint64 `json:"status"`
+ Error *callError `json:"error,omitempty"`
+}
+
+func (r *simCallResult) MarshalJSON() ([]byte, error) {
+ type callResultAlias simCallResult
+ // Marshal logs to be an empty array instead of nil when empty
+ if r.Logs == nil {
+ r.Logs = []*types.Log{}
+ }
+ return json.Marshal((*callResultAlias)(r))
+}
+
+// simOpts are the inputs to eth_simulateV1.
+type simOpts struct {
+ BlockStateCalls []simBlock
+ TraceTransfers bool
+ Validation bool
+ ReturnFullTransactions bool
+}
+
+// simulator is a stateful object that simulates a series of blocks.
+// it is not safe for concurrent use.
+type simulator struct {
+ b Backend
+ state *state.StateDB
+ base *types.Header
+ chainConfig *params.ChainConfig
+ gp *core.GasPool
+ traceTransfers bool
+ validate bool
+ fullTx bool
+}
+
+// execute runs the simulation of a series of blocks.
+func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]map[string]interface{}, error) {
+ if err := ctx.Err(); err != nil {
+ return nil, err
+ }
+ var (
+ cancel context.CancelFunc
+ timeout = sim.b.RPCEVMTimeout()
+ )
+ 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()
+
+ var err error
+ blocks, err = sim.sanitizeChain(blocks)
+ if err != nil {
+ return nil, err
+ }
+ // Prepare block headers with preliminary fields for the response.
+ headers, err := sim.makeHeaders(blocks)
+ if err != nil {
+ return nil, err
+ }
+ var (
+ results = make([]map[string]interface{}, len(blocks))
+ parent = sim.base
+ // Assume same total difficulty for all simulated blocks.
+ td = sim.b.GetTd(ctx, sim.base.Hash())
+ )
+ for bi, block := range blocks {
+ result, callResults, err := sim.processBlock(ctx, &block, headers[bi], parent, headers[:bi], timeout)
+ if err != nil {
+ return nil, err
+ }
+ enc := RPCMarshalBlock(result, true, sim.fullTx, sim.chainConfig)
+ enc["totalDifficulty"] = (*hexutil.Big)(td)
+ enc["calls"] = callResults
+ results[bi] = enc
+
+ parent = headers[bi]
+ }
+ return results, nil
+}
+
+func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, parent *types.Header, headers []*types.Header, timeout time.Duration) (*types.Block, []simCallResult, error) {
+ // Set header fields that depend only on parent block.
+ // Parent hash is needed for evm.GetHashFn to work.
+ header.ParentHash = parent.Hash()
+ if sim.chainConfig.IsLondon(header.Number) {
+ // In non-validation mode base fee is set to 0 if it is not overridden.
+ // This is because it creates an edge case in EVM where gasPrice < baseFee.
+ // Base fee could have been overridden.
+ if header.BaseFee == nil {
+ if sim.validate {
+ header.BaseFee = eip1559.CalcBaseFee(sim.chainConfig, parent)
+ } else {
+ header.BaseFee = big.NewInt(0)
+ }
+ }
+ }
+ blockContext := core.NewEVMBlockContext(header, sim.newSimulatedChainContext(ctx, headers), nil)
+ precompiles := sim.activePrecompiles(sim.base)
+ // State overrides are applied prior to execution of a block
+ if err := block.StateOverrides.Apply(sim.state, precompiles); err != nil {
+ return nil, nil, err
+ }
+ var (
+ gasUsed uint64
+ txes = make([]*types.Transaction, len(block.Calls))
+ callResults = make([]simCallResult, len(block.Calls))
+ receipts = make([]*types.Receipt, len(block.Calls))
+ // Block hash will be repaired after execution.
+ tracer = newTracer(sim.traceTransfers, blockContext.BlockNumber.Uint64(), common.Hash{}, common.Hash{}, 0)
+ vmConfig = &vm.Config{
+ NoBaseFee: !sim.validate,
+ Tracer: tracer.Hooks(),
+ }
+ evm = vm.NewEVM(blockContext, vm.TxContext{GasPrice: new(big.Int)}, sim.state, nil, sim.chainConfig, *vmConfig)
+ )
+ sim.state.SetLogger(tracer.Hooks())
+ // It is possible to override precompiles with EVM bytecode, or
+ // move them to another address.
+ if precompiles != nil {
+ evm.SetPrecompiles(precompiles)
+ }
+ for i, call := range block.Calls {
+ if err := ctx.Err(); err != nil {
+ return nil, nil, err
+ }
+ if err := sim.sanitizeCall(&call, sim.state, header, blockContext, &gasUsed); err != nil {
+ return nil, nil, err
+ }
+ tx := call.ToTransaction(types.DynamicFeeTxType)
+ txes[i] = tx
+ tracer.reset(tx.Hash(), uint(i))
+ // EoA check is always skipped, even in validation mode.
+ msg := call.ToMessage(sim.b, header.BaseFee, !sim.validate, true)
+ evm.Reset(core.NewEVMTxContext(msg), sim.state)
+ result, err := applyMessageWithEVM(ctx, evm, msg, sim.state, timeout, sim.gp)
+ if err != nil {
+ txErr := txValidationError(err)
+ return nil, nil, txErr
+ }
+ // Update the state with pending changes.
+ var root []byte
+ if sim.chainConfig.IsByzantium(blockContext.BlockNumber) {
+ sim.state.Finalise(true)
+ } else {
+ root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes()
+ }
+ gasUsed += result.UsedGas
+ receipts[i] = core.MakeReceipt(evm, result, sim.state, blockContext.BlockNumber, common.Hash{}, tx, gasUsed, root)
+ logs := tracer.Logs()
+ callRes := simCallResult{ReturnValue: result.Return(), Logs: logs, GasUsed: hexutil.Uint64(result.UsedGas)}
+ if result.Failed() {
+ callRes.Status = hexutil.Uint64(types.ReceiptStatusFailed)
+ if errors.Is(result.Err, vm.ErrExecutionReverted) {
+ // If the result contains a revert reason, try to unpack it.
+ revertErr := newRevertError(result.Revert())
+ callRes.Error = &callError{Message: revertErr.Error(), Code: errCodeReverted, Data: revertErr.ErrorData().(string)}
+ } else {
+ callRes.Error = &callError{Message: result.Err.Error(), Code: errCodeVMError}
+ }
+ } else {
+ callRes.Status = hexutil.Uint64(types.ReceiptStatusSuccessful)
+ }
+ callResults[i] = callRes
+ }
+ header.Root = sim.state.IntermediateRoot(true)
+ header.GasUsed = gasUsed
+ b := types.NewBlock(header, &types.Body{Transactions: txes}, receipts, trie.NewStackTrie(nil))
+ repairLogs(callResults, b.Hash())
+ return b, callResults, nil
+}
+
+// repairLogs updates the block hash in the logs present in the result of
+// a simulated block. This is needed as during execution when logs are collected
+// the block hash is not known.
+func repairLogs(calls []simCallResult, hash common.Hash) {
+ for i := range calls {
+ for j := range calls[i].Logs {
+ calls[i].Logs[j].BlockHash = hash
+ }
+ }
+}
+
+func (sim *simulator) sanitizeCall(call *TransactionArgs, state *state.StateDB, header *types.Header, blockContext vm.BlockContext, gasUsed *uint64) error {
+ if call.Nonce == nil {
+ nonce := state.GetNonce(call.from())
+ call.Nonce = (*hexutil.Uint64)(&nonce)
+ }
+ // Let the call run wild unless explicitly specified.
+ if call.Gas == nil {
+ remaining := blockContext.GasLimit - *gasUsed
+ call.Gas = (*hexutil.Uint64)(&remaining)
+ }
+ if *gasUsed+uint64(*call.Gas) > blockContext.GasLimit {
+ return &blockGasLimitReachedError{fmt.Sprintf("block gas limit reached: %d >= %d", gasUsed, blockContext.GasLimit)}
+ }
+ if err := call.CallDefaults(sim.gp.Gas(), header.BaseFee, sim.chainConfig.ChainID); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (sim *simulator) activePrecompiles(base *types.Header) vm.PrecompiledContracts {
+ var (
+ rules = sim.chainConfig.Rules(base.Number)
+ )
+ return maps.Clone(vm.ActivePrecompiledContracts(rules))
+}
+
+// sanitizeChain checks the chain integrity. Specifically it checks that
+// block numbers and timestamp are strictly increasing, setting default values
+// when necessary. Gaps in block numbers are filled with empty blocks.
+// Note: It modifies the block's override object.
+func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) {
+ var (
+ res = make([]simBlock, 0, len(blocks))
+ base = sim.base
+ prevNumber = base.Number
+ prevTimestamp = base.Time
+ )
+ for _, block := range blocks {
+ if block.BlockOverrides == nil {
+ block.BlockOverrides = new(BlockOverrides)
+ }
+ if block.BlockOverrides.Number == nil {
+ n := new(big.Int).Add(prevNumber, big.NewInt(1))
+ block.BlockOverrides.Number = (*hexutil.Big)(n)
+ }
+ diff := new(big.Int).Sub(block.BlockOverrides.Number.ToInt(), prevNumber)
+ if diff.Cmp(common.Big0) <= 0 {
+ return nil, &invalidBlockNumberError{fmt.Sprintf("block numbers must be in order: %d <= %d", block.BlockOverrides.Number.ToInt().Uint64(), prevNumber)}
+ }
+ if total := new(big.Int).Sub(block.BlockOverrides.Number.ToInt(), base.Number); total.Cmp(big.NewInt(maxSimulateBlocks)) > 0 {
+ return nil, &clientLimitExceededError{message: "too many blocks"}
+ }
+ if diff.Cmp(big.NewInt(1)) > 0 {
+ // Fill the gap with empty blocks.
+ gap := new(big.Int).Sub(diff, big.NewInt(1))
+ // Assign block number to the empty blocks.
+ for i := uint64(0); i < gap.Uint64(); i++ {
+ n := new(big.Int).Add(prevNumber, big.NewInt(int64(i+1)))
+ t := prevTimestamp + timestampIncrement
+ b := simBlock{BlockOverrides: &BlockOverrides{Number: (*hexutil.Big)(n), Time: (*hexutil.Uint64)(&t)}}
+ prevTimestamp = t
+ res = append(res, b)
+ }
+ }
+ // Only append block after filling a potential gap.
+ prevNumber = block.BlockOverrides.Number.ToInt()
+ var t uint64
+ if block.BlockOverrides.Time == nil {
+ t = prevTimestamp + timestampIncrement
+ block.BlockOverrides.Time = (*hexutil.Uint64)(&t)
+ } else {
+ t = uint64(*block.BlockOverrides.Time)
+ if t <= prevTimestamp {
+ return nil, &invalidBlockTimestampError{fmt.Sprintf("block timestamps must be in order: %d <= %d", t, prevTimestamp)}
+ }
+ }
+ prevTimestamp = t
+ res = append(res, block)
+ }
+ return res, nil
+}
+
+// makeHeaders makes header object with preliminary fields based on a simulated block.
+// Some fields have to be filled post-execution.
+// It assumes blocks are in order and numbers have been validated.
+func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) {
+ var (
+ res = make([]*types.Header, len(blocks))
+ base = sim.base
+ header = base
+ )
+ for bi, block := range blocks {
+ if block.BlockOverrides == nil || block.BlockOverrides.Number == nil {
+ return nil, errors.New("empty block number")
+ }
+ overrides := block.BlockOverrides
+
+ header = overrides.MakeHeader(&types.Header{
+ UncleHash: types.EmptyUncleHash,
+ ReceiptHash: types.EmptyReceiptsHash,
+ TxHash: types.EmptyTxsHash,
+ Coinbase: header.Coinbase,
+ Difficulty: header.Difficulty,
+ GasLimit: header.GasLimit,
+ })
+ res[bi] = header
+ }
+ return res, nil
+}
+
+func (sim *simulator) newSimulatedChainContext(ctx context.Context, headers []*types.Header) *ChainContext {
+ return NewChainContext(ctx, &simBackend{base: sim.base, b: sim.b, headers: headers})
+}
+
+type simBackend struct {
+ b ChainContextBackend
+ base *types.Header
+ headers []*types.Header
+}
+
+func (b *simBackend) Engine() consensus.Engine {
+ return b.b.Engine()
+}
+
+func (b *simBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
+ if uint64(number) == b.base.Number.Uint64() {
+ return b.base, nil
+ }
+ if uint64(number) < b.base.Number.Uint64() {
+ // Resolve canonical header.
+ return b.b.HeaderByNumber(ctx, number)
+ }
+ // Simulated block.
+ for _, header := range b.headers {
+ if header.Number.Uint64() == uint64(number) {
+ return header, nil
+ }
+ }
+ return nil, errors.New("header not found")
+}
diff --git a/internal/ethapi/simulate_test.go b/internal/ethapi/simulate_test.go
new file mode 100644
index 000000000000..0b625912a569
--- /dev/null
+++ b/internal/ethapi/simulate_test.go
@@ -0,0 +1,125 @@
+// Copyright 2024 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package ethapi
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain/common/hexutil"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+)
+
+func TestSimulateSanitizeBlockOrder(t *testing.T) {
+ type result struct {
+ number uint64
+ timestamp uint64
+ }
+ for i, tc := range []struct {
+ baseNumber int
+ baseTimestamp uint64
+ blocks []simBlock
+ expected []result
+ err string
+ }{
+ {
+ baseNumber: 10,
+ baseTimestamp: 50,
+ blocks: []simBlock{{}, {}, {}},
+ expected: []result{{number: 11, timestamp: 51}, {number: 12, timestamp: 52}, {number: 13, timestamp: 53}},
+ },
+ {
+ baseNumber: 10,
+ baseTimestamp: 50,
+ blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13), Time: newUint64(70)}}, {}},
+ expected: []result{{number: 11, timestamp: 51}, {number: 12, timestamp: 52}, {number: 13, timestamp: 70}, {number: 14, timestamp: 71}},
+ },
+ {
+ baseNumber: 10,
+ baseTimestamp: 50,
+ blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(11)}}, {BlockOverrides: &BlockOverrides{Number: newInt(14)}}, {}},
+ expected: []result{{number: 11, timestamp: 51}, {number: 12, timestamp: 52}, {number: 13, timestamp: 53}, {number: 14, timestamp: 54}, {number: 15, timestamp: 55}},
+ },
+ {
+ baseNumber: 10,
+ baseTimestamp: 50,
+ blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13)}}, {BlockOverrides: &BlockOverrides{Number: newInt(12)}}},
+ err: "block numbers must be in order: 12 <= 13",
+ },
+ {
+ baseNumber: 10,
+ baseTimestamp: 50,
+ blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13), Time: newUint64(52)}}},
+ err: "block timestamps must be in order: 52 <= 52",
+ },
+ {
+ baseNumber: 10,
+ baseTimestamp: 50,
+ blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(11), Time: newUint64(60)}}, {BlockOverrides: &BlockOverrides{Number: newInt(12), Time: newUint64(55)}}},
+ err: "block timestamps must be in order: 55 <= 60",
+ },
+ {
+ baseNumber: 10,
+ baseTimestamp: 50,
+ blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(11), Time: newUint64(60)}}, {BlockOverrides: &BlockOverrides{Number: newInt(13), Time: newUint64(61)}}},
+ err: "block timestamps must be in order: 61 <= 61",
+ },
+ } {
+ sim := &simulator{base: &types.Header{Number: big.NewInt(int64(tc.baseNumber)), Time: tc.baseTimestamp}}
+ res, err := sim.sanitizeChain(tc.blocks)
+ if err != nil {
+ if err.Error() == tc.err {
+ continue
+ } else {
+ t.Fatalf("testcase %d: error mismatch. Want '%s', have '%s'", i, tc.err, err.Error())
+ }
+ }
+ if err == nil && tc.err != "" {
+ t.Fatalf("testcase %d: expected err", i)
+ }
+ if len(res) != len(tc.expected) {
+ t.Errorf("testcase %d: mismatch number of blocks. Want %d, have %d", i, len(tc.expected), len(res))
+ }
+ for bi, b := range res {
+ if b.BlockOverrides == nil {
+ t.Fatalf("testcase %d: block overrides nil", i)
+ }
+ if b.BlockOverrides.Number == nil {
+ t.Fatalf("testcase %d: block number not set", i)
+ }
+ if b.BlockOverrides.Time == nil {
+ t.Fatalf("testcase %d: block time not set", i)
+ }
+ if uint64(*b.BlockOverrides.Time) != tc.expected[bi].timestamp {
+ t.Errorf("testcase %d: block timestamp mismatch. Want %d, have %d", i, tc.expected[bi].timestamp, uint64(*b.BlockOverrides.Time))
+ }
+ have := b.BlockOverrides.Number.ToInt().Uint64()
+ if have != tc.expected[bi].number {
+ t.Errorf("testcase %d: block number mismatch. Want %d, have %d", i, tc.expected[bi].number, have)
+ }
+ }
+ }
+}
+
+func newInt(n int64) *hexutil.Big {
+ return (*hexutil.Big)(big.NewInt(n))
+}
+
+func newUint64(v uint64) *hexutil.Uint64 {
+ rpcUint64 := hexutil.Uint64(v)
+ return &rpcUint64
+}
diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go
index 42db87b974ed..621f353d3e01 100644
--- a/internal/ethapi/transaction_args.go
+++ b/internal/ethapi/transaction_args.go
@@ -279,7 +279,7 @@ func (args *TransactionArgs) CallDefaults(globalGasCap uint64, baseFee *big.Int,
// ToMessage converts the transaction arguments to the Message type used by the
// core evm. This method is used in calls and traces that do not require a real
// live transaction.
-func (args *TransactionArgs) ToMessage(b AccountBackend, baseFee *big.Int) *core.Message {
+func (args *TransactionArgs) ToMessage(b AccountBackend, baseFee *big.Int, skipNonceCheck, skipEoACheck bool) *core.Message {
var (
gasPrice *big.Int
gasFeeCap *big.Int
@@ -323,25 +323,38 @@ func (args *TransactionArgs) ToMessage(b AccountBackend, baseFee *big.Int) *core
}
}
return &core.Message{
- From: addr,
- To: args.To,
- Value: (*big.Int)(args.Value),
- GasLimit: uint64(*args.Gas),
- GasPrice: gasPrice,
- GasFeeCap: gasFeeCap,
- GasTipCap: gasTipCap,
- Data: args.data(),
- AccessList: accessList,
- SkipAccountChecks: true,
+ From: addr,
+ To: args.To,
+ Value: (*big.Int)(args.Value),
+ Nonce: uint64(*args.Nonce),
+ GasLimit: uint64(*args.Gas),
+ GasPrice: gasPrice,
+ GasFeeCap: gasFeeCap,
+ GasTipCap: gasTipCap,
+ Data: args.data(),
+ AccessList: accessList,
+ SkipNonceChecks: skipNonceCheck,
+ SkipFromEOACheck: skipEoACheck,
}
}
-// toTransaction converts the arguments to a transaction.
+// ToTransaction converts the arguments to a transaction.
// This assumes that setDefaults has been called.
-func (args *TransactionArgs) toTransaction() *types.Transaction {
- var data types.TxData
+func (args *TransactionArgs) ToTransaction(defaultType int) *types.Transaction {
+ usedType := types.LegacyTxType
switch {
- case args.MaxFeePerGas != nil:
+ case args.MaxFeePerGas != nil || defaultType == types.DynamicFeeTxType:
+ usedType = types.DynamicFeeTxType
+ case args.AccessList != nil || defaultType == types.AccessListTxType:
+ usedType = types.AccessListTxType
+ }
+ // Make it possible to default to newer tx, but use legacy if gasprice is provided
+ if args.GasPrice != nil {
+ usedType = types.LegacyTxType
+ }
+ var data types.TxData
+ switch usedType {
+ case types.DynamicFeeTxType:
al := types.AccessList{}
if args.AccessList != nil {
al = *args.AccessList
@@ -357,7 +370,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
Data: args.data(),
AccessList: al,
}
- case args.AccessList != nil:
+ case types.AccessListTxType:
data = &types.AccessListTx{
To: args.To,
ChainID: (*big.Int)(args.ChainID),
@@ -380,9 +393,3 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
}
return types.NewTx(data)
}
-
-// ToTransaction converts the arguments to a transaction.
-// This assumes that setDefaults has been called.
-func (args *TransactionArgs) ToTransaction() *types.Transaction {
- return args.toTransaction()
-}
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index 1dd31ee74c8a..2de4b23d51e5 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -596,6 +596,12 @@ web3._extend({
params: 4,
inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputDefaultBlockNumberFormatter, null, null],
}),
+ new web3._extend.Method({
+ name: 'simulateV1',
+ call: 'eth_simulateV1',
+ params: 2,
+ inputFormatter: [null, web3._extend.formatters.inputDefaultBlockNumberFormatter],
+ }),
new web3._extend.Method({
name: 'getBlockReceipts',
call: 'eth_getBlockReceipts',
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index f03e9670fda9..936664a64342 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -286,17 +286,18 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int, baseFee *big
}
msg := &core.Message{
- From: from,
- To: to,
- Nonce: tx.Nonce,
- Value: value,
- GasLimit: gasLimit,
- GasPrice: tx.GasPrice,
- GasFeeCap: tx.MaxFeePerGas,
- GasTipCap: tx.MaxPriorityFeePerGas,
- Data: data,
- AccessList: accessList,
- SkipAccountChecks: false,
+ From: from,
+ To: to,
+ Nonce: tx.Nonce,
+ Value: value,
+ GasLimit: gasLimit,
+ GasPrice: tx.GasPrice,
+ GasFeeCap: tx.MaxFeePerGas,
+ GasTipCap: tx.MaxPriorityFeePerGas,
+ Data: data,
+ AccessList: accessList,
+ SkipNonceChecks: false,
+ SkipFromEOACheck: false,
}
return msg, nil
}