diff --git a/.gitignore b/.gitignore
index ac0f4efdfb..7000fedd25 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@ profile.cov
# IdeaIDE
.idea
+*.iml
# VS Code
.vscode
diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go
index 9b24bfbf96..a6c4718cf4 100644
--- a/eth/gasestimator/gasestimator.go
+++ b/eth/gasestimator/gasestimator.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/internal/ethapi/override"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
@@ -38,10 +39,11 @@ import (
// these together, it would be excessively hard to test. Splitting the parts out
// allows testing without needing a proper live chain.
type Options struct {
- Config *params.ChainConfig // Chain configuration for hard fork selection
- Chain core.ChainContext // Chain context to access past block hashes
- Header *types.Header // Header defining the block context to execute in
- State *state.StateDB // Pre-state on top of which to estimate the gas
+ Config *params.ChainConfig // Chain configuration for hard fork selection
+ Chain core.ChainContext // Chain context to access past block hashes
+ Header *types.Header // Header defining the block context to execute in
+ State *state.StateDB // Pre-state on top of which to estimate the gas
+ BlockOverrides *override.BlockOverrides // Block overrides to apply during the estimation
ErrorRatio float64 // Allowed overestimation ratio for faster estimation termination
}
@@ -220,6 +222,9 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio
evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil)
dirtyState = opts.State.Copy()
)
+ if opts.BlockOverrides != nil {
+ opts.BlockOverrides.Apply(&evmContext)
+ }
// Lower the basefee to 0 to avoid breaking EVM
// invariants (basefee < feecap).
if call.GasPrice.Sign() == 0 {
diff --git a/eth/tracers/api.go b/eth/tracers/api.go
index c9bf5a4223..22163030de 100644
--- a/eth/tracers/api.go
+++ b/eth/tracers/api.go
@@ -39,6 +39,7 @@ import (
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/ethapi"
+ "github.com/ethereum/go-ethereum/internal/ethapi/override"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
@@ -163,8 +164,8 @@ type TraceConfig struct {
// field to override the state for tracing.
type TraceCallConfig struct {
TraceConfig
- StateOverrides *ethapi.StateOverride
- BlockOverrides *ethapi.BlockOverrides
+ StateOverrides *override.StateOverride
+ BlockOverrides *override.BlockOverrides
TxIndex *hexutil.Uint
}
diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go
index e786853ede..13a7b0aaae 100644
--- a/eth/tracers/api_test.go
+++ b/eth/tracers/api_test.go
@@ -44,6 +44,7 @@ import (
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/ethapi"
+ "github.com/ethereum/go-ethereum/internal/ethapi/override"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -454,7 +455,7 @@ func TestTraceCall(t *testing.T) {
Input: &hexutil.Bytes{0x43}, // blocknumber
},
config: &TraceCallConfig{
- BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
+ BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
},
expectErr: nil,
expect: ` {"gas":53018,"failed":false,"returnValue":"","structLogs":[
@@ -698,8 +699,8 @@ func TestTracingWithOverrides(t *testing.T) {
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: &TraceCallConfig{
- StateOverrides: ðapi.StateOverride{
- randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
+ StateOverrides: &override.StateOverride{
+ randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
},
},
want: `{"gas":21000,"failed":false,"returnValue":""}`,
@@ -740,8 +741,8 @@ func TestTracingWithOverrides(t *testing.T) {
},
config: &TraceCallConfig{
//Tracer: &tracer,
- StateOverrides: ðapi.StateOverride{
- randomAccounts[2].addr: ethapi.OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ randomAccounts[2].addr: override.OverrideAccount{
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")),
StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}),
},
@@ -757,7 +758,7 @@ func TestTracingWithOverrides(t *testing.T) {
Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")),
},
config: &TraceCallConfig{
- BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
+ BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
},
want: `{"gas":59537,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000001337"}`,
},
@@ -777,7 +778,7 @@ func TestTracingWithOverrides(t *testing.T) {
}, // blocknumber
},
config: &TraceCallConfig{
- BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
+ BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
},
want: `{"gas":72666,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`,
},
@@ -807,8 +808,8 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
},
config: &TraceCallConfig{
- StateOverrides: ðapi.StateOverride{
- randomAccounts[2].addr: ethapi.OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ randomAccounts[2].addr: override.OverrideAccount{
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")),
},
},
@@ -823,8 +824,8 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
},
config: &TraceCallConfig{
- StateOverrides: ðapi.StateOverride{
- randomAccounts[2].addr: ethapi.OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ randomAccounts[2].addr: override.OverrideAccount{
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")),
State: newStates([]common.Hash{{}}, []common.Hash{{}}),
},
@@ -841,8 +842,8 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
},
config: &TraceCallConfig{
- StateOverrides: ðapi.StateOverride{
- storageAccount: ethapi.OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ storageAccount: override.OverrideAccount{
Code: newRPCBytes([]byte{
// SLOAD(3) + SLOAD(4) (which is 0x77)
byte(vm.PUSH1), 0x04,
@@ -876,8 +877,8 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
},
config: &TraceCallConfig{
- StateOverrides: ðapi.StateOverride{
- storageAccount: ethapi.OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ storageAccount: override.OverrideAccount{
Code: newRPCBytes([]byte{
// SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x00)
byte(vm.PUSH1), 0x04,
@@ -914,8 +915,8 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
},
config: &TraceCallConfig{
- StateOverrides: ðapi.StateOverride{
- storageAccount: ethapi.OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ storageAccount: override.OverrideAccount{
Code: newRPCBytes([]byte{
// SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x44)
byte(vm.PUSH1), 0x04,
diff --git a/graphql/graphql.go b/graphql/graphql.go
index 57dfd14ecd..6e364de6d9 100644
--- a/graphql/graphql.go
+++ b/graphql/graphql.go
@@ -1208,7 +1208,7 @@ func (b *Block) Call(ctx context.Context, args struct {
func (b *Block) EstimateGas(ctx context.Context, args struct {
Data ethapi.TransactionArgs
}) (hexutil.Uint64, error) {
- return ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, b.r.backend.RPCGasCap())
+ return ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, nil, b.r.backend.RPCGasCap())
}
type Pending struct {
@@ -1272,7 +1272,7 @@ func (p *Pending) EstimateGas(ctx context.Context, args struct {
Data ethapi.TransactionArgs
}) (hexutil.Uint64, error) {
latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
- return ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, latestBlockNr, nil, p.r.backend.RPCGasCap())
+ return ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, latestBlockNr, nil, nil, p.r.backend.RPCGasCap())
}
// Resolver is the top-level object in the GraphQL hierarchy.
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 067d07ba7a..de75c697ef 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -35,19 +35,18 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/gasestimator"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
+ "github.com/ethereum/go-ethereum/internal/ethapi/override"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
- "github.com/holiman/uint256"
)
// estimateGasErrorRatio is the amount of overestimation eth_estimateGas is
@@ -621,171 +620,6 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp
return result, nil
}
-// OverrideAccount indicates the overriding fields of account during the execution
-// of a message call.
-// Note, state and stateDiff can't be specified at the same time. If state is
-// set, message execution will only use the data in the given state. Otherwise
-// if stateDiff 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"`
- 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, 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))
- }
- // Override account(contract) code.
- if account.Code != nil {
- statedb.SetCode(addr, *account.Code)
- }
- // Override account balance.
- if account.Balance != nil {
- u256Balance, _ := uint256.FromBig((*big.Int)(account.Balance))
- statedb.SetBalance(addr, u256Balance, tracing.BalanceChangeUnspecified)
- }
- if account.State != nil && account.StateDiff != nil {
- return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
- }
- // Replace entire state if caller requires.
- if account.State != nil {
- statedb.SetStorage(addr, account.State)
- }
- // Apply state diff into specified accounts.
- if account.StateDiff != nil {
- for key, value := range account.StateDiff {
- statedb.SetState(addr, key, value)
- }
- }
- }
- // Now finalize the changes. Finalize is normally performed between transactions.
- // By using finalize, the overrides are semantically behaving as
- // if they were created in a transaction just before the tracing occur.
- statedb.Finalise(false)
- return nil
-}
-
-// BlockOverrides is a set of header fields to override.
-type BlockOverrides struct {
- Number *hexutil.Big
- Difficulty *hexutil.Big // No-op if we're simulating post-merge calls.
- Time *hexutil.Uint64
- GasLimit *hexutil.Uint64
- FeeRecipient *common.Address
- PrevRandao *common.Hash
- BaseFeePerGas *hexutil.Big
- BlobBaseFee *hexutil.Big
-}
-
-// Apply overrides the given header fields into the given block context.
-func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
- if o == nil {
- return
- }
- 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 o.BaseFeePerGas != nil {
- blockCtx.BaseFee = o.BaseFeePerGas.ToInt()
- }
- if o.BlobBaseFee != nil {
- blockCtx.BlobBaseFee = o.BlobBaseFee.ToInt()
- }
-}
-
-// 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 o.Difficulty != nil {
- h.Difficulty = o.Difficulty.ToInt()
- }
- if o.Time != nil {
- h.Time = uint64(*o.Time)
- }
- if o.GasLimit != nil {
- h.GasLimit = uint64(*o.GasLimit)
- }
- 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
-}
-
// ChainContextBackend provides methods required to implement ChainContext.
type ChainContextBackend interface {
Engine() consensus.Engine
@@ -818,7 +652,7 @@ func (context *ChainContext) GetHeader(hash common.Hash, number uint64) *types.H
return header
}
-func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *StateOverride, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
+func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
if blockOverrides != nil {
blockOverrides.Apply(&blockCtx)
@@ -897,7 +731,7 @@ func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, ti
return result, nil
}
-func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
+func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
@@ -913,7 +747,7 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
//
// Note, this function doesn't make and changes in the state/blockchain and is
// useful to execute and retrieve values.
-func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides) (hexutil.Bytes, error) {
+func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides) (hexutil.Bytes, error) {
if blockNrOrHash == nil {
latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
blockNrOrHash = &latest
@@ -972,7 +806,7 @@ func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrO
// 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 &
// non-zero) and `gasCap` (if non-zero).
-func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) {
+func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, gasCap uint64) (hexutil.Uint64, error) {
// Retrieve the base state and mutate it with any overrides
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if state == nil || err != nil {
@@ -983,11 +817,12 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
}
// Construct the gas estimator option from the user input
opts := &gasestimator.Options{
- Config: b.ChainConfig(),
- Chain: NewChainContext(ctx, b),
- Header: header,
- State: state,
- ErrorRatio: estimateGasErrorRatio,
+ Config: b.ChainConfig(),
+ Chain: NewChainContext(ctx, b),
+ Header: header,
+ BlockOverrides: blockOverrides,
+ State: state,
+ ErrorRatio: estimateGasErrorRatio,
}
// Set any required transaction default, but make sure the gas cap itself is not messed with
// if it was not specified in the original argument list.
@@ -1016,12 +851,12 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
// value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap
// configuration (if non-zero).
// Note: Required blob gas is not computed in this method.
-func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Uint64, error) {
+func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides) (hexutil.Uint64, error) {
bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
if blockNrOrHash != nil {
bNrOrHash = *blockNrOrHash
}
- return DoEstimateGas(ctx, api.b, args, bNrOrHash, overrides, api.b.RPCGasCap())
+ return DoEstimateGas(ctx, api.b, args, bNrOrHash, overrides, blockOverrides, api.b.RPCGasCap())
}
// RPCMarshalHeader converts the given header to the RPC output .
diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go
index 1f5f2dd1d5..ae2ec9f0f0 100644
--- a/internal/ethapi/api_test.go
+++ b/internal/ethapi/api_test.go
@@ -24,7 +24,6 @@ import (
"encoding/json"
"errors"
"fmt"
- "maps"
"math/big"
"os"
"path/filepath"
@@ -34,6 +33,9 @@ import (
"testing"
"time"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/internal/ethapi/override"
+
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
@@ -55,7 +57,6 @@ import (
"github.com/ethereum/go-ethereum/internal/blocktest"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
- "github.com/ethereum/go-ethereum/triedb"
"github.com/holiman/uint256"
"github.com/stretchr/testify/require"
)
@@ -640,6 +641,17 @@ func TestEstimateGas(t *testing.T) {
signer = types.HomesteadSigner{}
randomAccounts = newAccounts(2)
)
+ packRevert := func(revertMessage string) []byte {
+ var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
+ stringType, _ := abi.NewType("string", "", nil)
+ args := abi.Arguments{
+ {Type: stringType},
+ }
+ encodedMessage, _ := args.Pack(revertMessage)
+
+ return append(revertSelector, encodedMessage...)
+ }
+
api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
@@ -648,14 +660,16 @@ func TestEstimateGas(t *testing.T) {
b.AddTx(tx)
b.SetPoS()
}))
+
var testSuite = []struct {
- blockNumber rpc.BlockNumber
- call TransactionArgs
- overrides StateOverride
- expectErr error
- want uint64
+ blockNumber rpc.BlockNumber
+ call TransactionArgs
+ overrides override.StateOverride
+ blockOverrides override.BlockOverrides
+ expectErr error
+ want uint64
}{
- // simple transfer on latest block
+ //simple transfer on latest block
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
@@ -687,8 +701,8 @@ func TestEstimateGas(t *testing.T) {
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{},
- overrides: StateOverride{
- randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
+ overrides: override.StateOverride{
+ randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
},
expectErr: nil,
want: 53000,
@@ -700,8 +714,8 @@ func TestEstimateGas(t *testing.T) {
To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
- overrides: StateOverride{
- randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(0))},
+ overrides: override.StateOverride{
+ randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(0))},
},
expectErr: core.ErrInsufficientFunds,
},
@@ -758,16 +772,65 @@ func TestEstimateGas(t *testing.T) {
},
want: 21000,
},
+ // // SPDX-License-Identifier: GPL-3.0
+ //pragma solidity >=0.8.2 <0.9.0;
+ //
+ //contract BlockOverridesTest {
+ // function call() public view returns (uint256) {
+ // return block.number;
+ // }
+ //
+ // function estimate() public view {
+ // revert(string.concat("block ", uint2str(block.number)));
+ // }
+ //
+ // function uint2str(uint256 _i) internal pure returns (string memory str) {
+ // if (_i == 0) {
+ // return "0";
+ // }
+ // uint256 j = _i;
+ // uint256 length;
+ // while (j != 0) {
+ // length++;
+ // j /= 10;
+ // }
+ // bytes memory bstr = new bytes(length);
+ // uint256 k = length;
+ // j = _i;
+ // while (j != 0) {
+ // bstr[--k] = bytes1(uint8(48 + (j % 10)));
+ // j /= 10;
+ // }
+ // str = string(bstr);
+ // }
+ //}
+ {
+ blockNumber: rpc.LatestBlockNumber,
+ call: TransactionArgs{
+ From: &accounts[0].addr,
+ To: &accounts[1].addr,
+ Data: hex2Bytes("0x3592d016"), //estimate
+ },
+ overrides: override.StateOverride{
+ accounts[1].addr: override.OverrideAccount{
+ Code: hex2Bytes("608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806328b5e32b146100385780633592d0161461004b575b5f5ffd5b4360405190815260200160405180910390f35b610053610055565b005b61005e4361009d565b60405160200161006e91906101a5565b60408051601f198184030181529082905262461bcd60e51b8252610094916004016101cd565b60405180910390fd5b6060815f036100c35750506040805180820190915260018152600360fc1b602082015290565b815f5b81156100ec57806100d681610216565b91506100e59050600a83610242565b91506100c6565b5f8167ffffffffffffffff81111561010657610106610255565b6040519080825280601f01601f191660200182016040528015610130576020820181803683370190505b508593509050815b831561019c57610149600a85610269565b61015490603061027c565b60f81b8261016183610295565b92508281518110610174576101746102aa565b60200101906001600160f81b03191690815f1a905350610195600a85610242565b9350610138565b50949350505050565b650313637b1b5960d51b81525f82518060208501600685015e5f920160060191825250919050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b634e487b7160e01b5f52601160045260245ffd5b5f6001820161022757610227610202565b5060010190565b634e487b7160e01b5f52601260045260245ffd5b5f826102505761025061022e565b500490565b634e487b7160e01b5f52604160045260245ffd5b5f826102775761027761022e565b500690565b8082018082111561028f5761028f610202565b92915050565b5f816102a3576102a3610202565b505f190190565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220a253cad1e2e3523b8c053c1d0cd1e39d7f3bafcedd73440a244872701f05dab264736f6c634300081c0033"),
+ },
+ },
+ blockOverrides: override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))},
+ expectErr: newRevertError(packRevert("block 11")),
+ },
}
for i, tc := range testSuite {
- result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides)
+ result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides)
if tc.expectErr != nil {
if err == nil {
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
continue
}
if !errors.Is(err, tc.expectErr) {
- t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err)
+ if !reflect.DeepEqual(err, tc.expectErr) {
+ t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err)
+ }
}
continue
}
@@ -818,9 +881,9 @@ func TestCall(t *testing.T) {
var testSuite = []struct {
name string
blockNumber rpc.BlockNumber
- overrides StateOverride
+ overrides override.StateOverride
call TransactionArgs
- blockOverrides BlockOverrides
+ blockOverrides override.BlockOverrides
expectErr error
want string
}{
@@ -880,8 +943,8 @@ func TestCall(t *testing.T) {
To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
- overrides: StateOverride{
- randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
+ overrides: override.StateOverride{
+ randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
},
want: "0x",
},
@@ -920,27 +983,60 @@ func TestCall(t *testing.T) {
To: &randomAccounts[2].addr,
Data: hex2Bytes("8381f58a"), // call number()
},
- overrides: StateOverride{
- randomAccounts[2].addr: OverrideAccount{
+ overrides: override.StateOverride{
+ randomAccounts[2].addr: override.OverrideAccount{
Code: hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"),
StateDiff: map[common.Hash]common.Hash{{}: common.BigToHash(big.NewInt(123))},
},
},
want: "0x000000000000000000000000000000000000000000000000000000000000007b",
},
- // Block overrides should work
+ // // SPDX-License-Identifier: GPL-3.0
+ //pragma solidity >=0.8.2 <0.9.0;
+ //
+ //contract BlockOverridesTest {
+ // function call() public view returns (uint256) {
+ // return block.number;
+ // }
+ //
+ // function estimate() public view {
+ // revert(string.concat("block ", uint2str(block.number)));
+ // }
+ //
+ // function uint2str(uint256 _i) internal pure returns (string memory str) {
+ // if (_i == 0) {
+ // return "0";
+ // }
+ // uint256 j = _i;
+ // uint256 length;
+ // while (j != 0) {
+ // length++;
+ // j /= 10;
+ // }
+ // bytes memory bstr = new bytes(length);
+ // uint256 k = length;
+ // j = _i;
+ // while (j != 0) {
+ // bstr[--k] = bytes1(uint8(48 + (j % 10)));
+ // j /= 10;
+ // }
+ // str = string(bstr);
+ // }
+ //}
{
- name: "block-override",
+ name: "block-override-with-state-override",
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[1].addr,
- Input: &hexutil.Bytes{
- 0x43, // NUMBER
- 0x60, 0x00, 0x52, // MSTORE offset 0
- 0x60, 0x20, 0x60, 0x00, 0xf3,
+ To: &accounts[2].addr,
+ Data: hex2Bytes("0x28b5e32b"), //call
+ },
+ overrides: override.StateOverride{
+ accounts[2].addr: override.OverrideAccount{
+ Code: hex2Bytes("608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806328b5e32b146100385780633592d0161461004b575b5f5ffd5b4360405190815260200160405180910390f35b610053610055565b005b61005e4361009d565b60405160200161006e91906101a5565b60408051601f198184030181529082905262461bcd60e51b8252610094916004016101cd565b60405180910390fd5b6060815f036100c35750506040805180820190915260018152600360fc1b602082015290565b815f5b81156100ec57806100d681610216565b91506100e59050600a83610242565b91506100c6565b5f8167ffffffffffffffff81111561010657610106610255565b6040519080825280601f01601f191660200182016040528015610130576020820181803683370190505b508593509050815b831561019c57610149600a85610269565b61015490603061027c565b60f81b8261016183610295565b92508281518110610174576101746102aa565b60200101906001600160f81b03191690815f1a905350610195600a85610242565b9350610138565b50949350505050565b650313637b1b5960d51b81525f82518060208501600685015e5f920160060191825250919050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b634e487b7160e01b5f52601160045260245ffd5b5f6001820161022757610227610202565b5060010190565b634e487b7160e01b5f52601260045260245ffd5b5f826102505761025061022e565b500490565b634e487b7160e01b5f52604160045260245ffd5b5f826102775761027761022e565b500690565b8082018082111561028f5761028f610202565b92915050565b5f816102a3576102a3610202565b505f190190565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220a253cad1e2e3523b8c053c1d0cd1e39d7f3bafcedd73440a244872701f05dab264736f6c634300081c0033"),
},
},
- blockOverrides: BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))},
+ blockOverrides: override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))},
want: "0x000000000000000000000000000000000000000000000000000000000000000b",
},
// Clear storage trie
@@ -963,8 +1059,8 @@ func TestCall(t *testing.T) {
// }
Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"),
},
- overrides: StateOverride{
- dad: OverrideAccount{
+ overrides: override.StateOverride{
+ dad: override.OverrideAccount{
State: map[common.Hash]common.Hash{},
},
},
@@ -991,7 +1087,7 @@ func TestCall(t *testing.T) {
BlobHashes: []common.Hash{{0x01, 0x22}},
BlobFeeCap: (*hexutil.Big)(big.NewInt(1)),
},
- overrides: StateOverride{
+ overrides: override.StateOverride{
randomAccounts[2].addr: {
Code: hex2Bytes("60004960005260206000f3"),
},
@@ -1017,8 +1113,8 @@ func TestCall(t *testing.T) {
// }
Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"),
},
- overrides: StateOverride{
- dad: OverrideAccount{
+ overrides: override.StateOverride{
+ dad: override.OverrideAccount{
State: map[common.Hash]common.Hash{},
},
},
@@ -1172,8 +1268,8 @@ func TestSimulateV1(t *testing.T) {
name: "simple",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
- randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(1000))},
+ StateOverrides: &override.StateOverride{
+ randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(1000))},
},
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
@@ -1215,8 +1311,8 @@ func TestSimulateV1(t *testing.T) {
name: "simple-multi-block",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
- randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(2000))},
+ StateOverrides: &override.StateOverride{
+ randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(2000))},
},
Calls: []TransactionArgs{
{
@@ -1230,8 +1326,8 @@ func TestSimulateV1(t *testing.T) {
},
},
}, {
- StateOverrides: &StateOverride{
- randomAccounts[3].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(0))},
+ StateOverrides: &override.StateOverride{
+ randomAccounts[3].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(0))},
},
Calls: []TransactionArgs{
{
@@ -1289,8 +1385,8 @@ func TestSimulateV1(t *testing.T) {
name: "evm-error",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
- randomAccounts[2].addr: OverrideAccount{Code: hex2Bytes("f3")},
+ StateOverrides: &override.StateOverride{
+ randomAccounts[2].addr: override.OverrideAccount{Code: hex2Bytes("f3")},
},
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
@@ -1316,7 +1412,7 @@ func TestSimulateV1(t *testing.T) {
name: "block-overrides",
tag: latest,
blocks: []simBlock{{
- BlockOverrides: &BlockOverrides{
+ BlockOverrides: &override.BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(11)),
FeeRecipient: &cac,
},
@@ -1331,7 +1427,7 @@ func TestSimulateV1(t *testing.T) {
},
},
}, {
- BlockOverrides: &BlockOverrides{
+ BlockOverrides: &override.BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(12)),
},
Calls: []TransactionArgs{{
@@ -1374,7 +1470,7 @@ func TestSimulateV1(t *testing.T) {
name: "block-number-order",
tag: latest,
blocks: []simBlock{{
- BlockOverrides: &BlockOverrides{
+ BlockOverrides: &override.BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(12)),
},
Calls: []TransactionArgs{{
@@ -1386,7 +1482,7 @@ func TestSimulateV1(t *testing.T) {
},
}},
}, {
- BlockOverrides: &BlockOverrides{
+ BlockOverrides: &override.BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(11)),
},
Calls: []TransactionArgs{{
@@ -1406,8 +1502,8 @@ func TestSimulateV1(t *testing.T) {
name: "storage-contract",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
- randomAccounts[2].addr: OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ randomAccounts[2].addr: override.OverrideAccount{
Code: hex2Bytes("608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220404e37f487a89a932dca5e77faaf6ca2de3b991f93d230604b1b8daaef64766264736f6c63430008070033"),
},
},
@@ -1448,8 +1544,8 @@ func TestSimulateV1(t *testing.T) {
name: "logs",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
- randomAccounts[2].addr: OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ randomAccounts[2].addr: override.OverrideAccount{
// Yul code:
// object "Test" {
// code {
@@ -1490,8 +1586,8 @@ func TestSimulateV1(t *testing.T) {
name: "ecrecover-override",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
- randomAccounts[2].addr: OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ randomAccounts[2].addr: override.OverrideAccount{
// Yul code that returns ecrecover(0, 0, 0, 0).
// object "Test" {
// code {
@@ -1518,7 +1614,7 @@ func TestSimulateV1(t *testing.T) {
// }
Code: hex2Bytes("6040516000815260006020820152600060408201526000606082015260208160808360015afa60008103603157600080fd5b601482f3"),
},
- common.BytesToAddress([]byte{0x01}): OverrideAccount{
+ common.BytesToAddress([]byte{0x01}): override.OverrideAccount{
// Yul code that returns the address of the caller.
// object "Test" {
// code {
@@ -1555,8 +1651,8 @@ func TestSimulateV1(t *testing.T) {
name: "precompile-move",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
- sha256Address: OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ sha256Address: override.OverrideAccount{
// Yul code that returns the calldata.
// object "Test" {
// code {
@@ -1610,8 +1706,8 @@ func TestSimulateV1(t *testing.T) {
name: "transfer-logs",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
- randomAccounts[0].addr: OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ randomAccounts[0].addr: override.OverrideAccount{
Balance: newRPCBalance(big.NewInt(100)),
// Yul code that transfers 100 wei to address passed in calldata:
// object "Test" {
@@ -1753,8 +1849,8 @@ func TestSimulateV1(t *testing.T) {
name: "validation-checks-from-contract",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
- randomAccounts[2].addr: OverrideAccount{
+ StateOverrides: &override.StateOverride{
+ randomAccounts[2].addr: override.OverrideAccount{
Balance: newRPCBalance(big.NewInt(2098640803896784)),
Code: hex2Bytes("00"),
Nonce: newUint64(1),
@@ -1788,11 +1884,11 @@ func TestSimulateV1(t *testing.T) {
name: "validation-checks-success",
tag: latest,
blocks: []simBlock{{
- BlockOverrides: &BlockOverrides{
+ BlockOverrides: &override.BlockOverrides{
BaseFeePerGas: (*hexutil.Big)(big.NewInt(1)),
},
- StateOverrides: &StateOverride{
- randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(10000000))},
+ StateOverrides: &override.StateOverride{
+ randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(10000000))},
},
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
@@ -1821,7 +1917,7 @@ func TestSimulateV1(t *testing.T) {
name: "clear-storage",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
+ StateOverrides: &override.StateOverride{
randomAccounts[2].addr: {
Code: newBytes(genesis.Alloc[bab].Code),
StateDiff: map[common.Hash]common.Hash{
@@ -1843,7 +1939,7 @@ func TestSimulateV1(t *testing.T) {
To: &bab,
}},
}, {
- StateOverrides: &StateOverride{
+ StateOverrides: &override.StateOverride{
randomAccounts[2].addr: {
State: map[common.Hash]common.Hash{
common.BigToHash(big.NewInt(1)): common.BigToHash(big.NewInt(5)),
@@ -1890,10 +1986,10 @@ func TestSimulateV1(t *testing.T) {
name: "blockhash-opcode",
tag: latest,
blocks: []simBlock{{
- BlockOverrides: &BlockOverrides{
+ BlockOverrides: &override.BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(12)),
},
- StateOverrides: &StateOverride{
+ StateOverrides: &override.StateOverride{
randomAccounts[2].addr: {
Code: hex2Bytes("600035804060008103601057600080fd5b5050"),
},
@@ -1915,7 +2011,7 @@ func TestSimulateV1(t *testing.T) {
Input: uint256ToBytes(uint256.NewInt(10)),
}},
}, {
- BlockOverrides: &BlockOverrides{
+ BlockOverrides: &override.BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(16)),
},
Calls: []TransactionArgs{{
@@ -2005,7 +2101,7 @@ func TestSimulateV1(t *testing.T) {
name: "basefee-non-validation",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
+ StateOverrides: &override.StateOverride{
randomAccounts[2].addr: {
// Yul code:
// object "Test" {
@@ -2040,7 +2136,7 @@ func TestSimulateV1(t *testing.T) {
},
},
}, {
- BlockOverrides: &BlockOverrides{
+ BlockOverrides: &override.BlockOverrides{
BaseFeePerGas: (*hexutil.Big)(big.NewInt(1)),
},
Calls: []TransactionArgs{{
@@ -2113,7 +2209,7 @@ func TestSimulateV1(t *testing.T) {
name: "basefee-validation-mode",
tag: latest,
blocks: []simBlock{{
- StateOverrides: &StateOverride{
+ StateOverrides: &override.StateOverride{
randomAccounts[2].addr: {
// Yul code:
// object "Test" {
@@ -3286,97 +3382,6 @@ func TestRPCGetBlockReceipts(t *testing.T) {
}
}
-type precompileContract struct{}
-
-func (p *precompileContract) RequiredGas(input []byte) uint64 { return 0 }
-
-func (p *precompileContract) Run(input []byte) ([]byte, error) { return nil, nil }
-
-func TestStateOverrideMovePrecompile(t *testing.T) {
- db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
- statedb, err := state.New(common.Hash{}, db)
- if err != nil {
- t.Fatalf("failed to create statedb: %v", err)
- }
- precompiles := map[common.Address]vm.PrecompiledContract{
- common.BytesToAddress([]byte{0x1}): &precompileContract{},
- common.BytesToAddress([]byte{0x2}): &precompileContract{},
- }
- bytes2Addr := func(b []byte) *common.Address {
- a := common.BytesToAddress(b)
- return &a
- }
- var testSuite = []struct {
- overrides StateOverride
- expectedPrecompiles map[common.Address]struct{}
- fail bool
- }{
- {
- overrides: StateOverride{
- common.BytesToAddress([]byte{0x1}): {
- Code: hex2Bytes("0xff"),
- MovePrecompileTo: bytes2Addr([]byte{0x2}),
- },
- common.BytesToAddress([]byte{0x2}): {
- Code: hex2Bytes("0x00"),
- },
- },
- // 0x2 has already been touched by the moveTo.
- fail: true,
- }, {
- overrides: StateOverride{
- common.BytesToAddress([]byte{0x1}): {
- Code: hex2Bytes("0xff"),
- MovePrecompileTo: bytes2Addr([]byte{0xff}),
- },
- common.BytesToAddress([]byte{0x3}): {
- Code: hex2Bytes("0x00"),
- MovePrecompileTo: bytes2Addr([]byte{0xfe}),
- },
- },
- // 0x3 is not a precompile.
- fail: true,
- }, {
- overrides: StateOverride{
- common.BytesToAddress([]byte{0x1}): {
- Code: hex2Bytes("0xff"),
- MovePrecompileTo: bytes2Addr([]byte{0xff}),
- },
- common.BytesToAddress([]byte{0x2}): {
- Code: hex2Bytes("0x00"),
- MovePrecompileTo: bytes2Addr([]byte{0xfe}),
- },
- },
- expectedPrecompiles: map[common.Address]struct{}{common.BytesToAddress([]byte{0xfe}): {}, common.BytesToAddress([]byte{0xff}): {}},
- },
- }
-
- for i, tt := range testSuite {
- cpy := maps.Clone(precompiles)
- // Apply overrides
- err := tt.overrides.Apply(statedb, cpy)
- if tt.fail {
- if err == nil {
- t.Errorf("test %d: want error, have nothing", i)
- }
- continue
- }
- if err != nil {
- t.Errorf("test %d: want no error, have %v", i, err)
- continue
- }
- // Precompile keys
- if len(cpy) != len(tt.expectedPrecompiles) {
- t.Errorf("test %d: precompile mismatch, want %d, have %d", i, len(tt.expectedPrecompiles), len(cpy))
- }
- for k := range tt.expectedPrecompiles {
- if _, ok := cpy[k]; !ok {
- t.Errorf("test %d: precompile not found: %s", i, k.String())
- }
- }
- }
-}
-
func testRPCResponseWithFile(t *testing.T, testid int, result interface{}, rpc string, file string) {
data, err := json.MarshalIndent(result, "", " ")
if err != nil {
diff --git a/internal/ethapi/override/override.go b/internal/ethapi/override/override.go
new file mode 100644
index 0000000000..70b6210275
--- /dev/null
+++ b/internal/ethapi/override/override.go
@@ -0,0 +1,195 @@
+// 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 override
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/tracing"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/holiman/uint256"
+)
+
+// OverrideAccount indicates the overriding fields of account during the execution
+// of a message call.
+// Note, state and stateDiff can't be specified at the same time. If state is
+// set, message execution will only use the data in the given state. Otherwise
+// if stateDiff 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"`
+ 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, 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))
+ }
+ // Override account(contract) code.
+ if account.Code != nil {
+ statedb.SetCode(addr, *account.Code)
+ }
+ // Override account balance.
+ if account.Balance != nil {
+ u256Balance, _ := uint256.FromBig((*big.Int)(account.Balance))
+ statedb.SetBalance(addr, u256Balance, tracing.BalanceChangeUnspecified)
+ }
+ if account.State != nil && account.StateDiff != nil {
+ return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
+ }
+ // Replace entire state if caller requires.
+ if account.State != nil {
+ statedb.SetStorage(addr, account.State)
+ }
+ // Apply state diff into specified accounts.
+ if account.StateDiff != nil {
+ for key, value := range account.StateDiff {
+ statedb.SetState(addr, key, value)
+ }
+ }
+ }
+ // Now finalize the changes. Finalize is normally performed between transactions.
+ // By using finalize, the overrides are semantically behaving as
+ // if they were created in a transaction just before the tracing occur.
+ statedb.Finalise(false)
+ return nil
+}
+
+// BlockOverrides is a set of header fields to override.
+type BlockOverrides struct {
+ Number *hexutil.Big
+ Difficulty *hexutil.Big // No-op if we're simulating post-merge calls.
+ Time *hexutil.Uint64
+ GasLimit *hexutil.Uint64
+ FeeRecipient *common.Address
+ PrevRandao *common.Hash
+ BaseFeePerGas *hexutil.Big
+ BlobBaseFee *hexutil.Big
+}
+
+// Apply overrides the given header fields into the given block context.
+func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
+ if o == nil {
+ return
+ }
+ 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 o.BaseFeePerGas != nil {
+ blockCtx.BaseFee = o.BaseFeePerGas.ToInt()
+ }
+ if o.BlobBaseFee != nil {
+ blockCtx.BlobBaseFee = o.BlobBaseFee.ToInt()
+ }
+}
+
+// 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 o.Difficulty != nil {
+ h.Difficulty = o.Difficulty.ToInt()
+ }
+ if o.Time != nil {
+ h.Time = uint64(*o.Time)
+ }
+ if o.GasLimit != nil {
+ h.GasLimit = uint64(*o.GasLimit)
+ }
+ 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
+}
diff --git a/internal/ethapi/override/override_test.go b/internal/ethapi/override/override_test.go
new file mode 100644
index 0000000000..07ed6442b2
--- /dev/null
+++ b/internal/ethapi/override/override_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 override
+
+import (
+ "maps"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/triedb"
+)
+
+type precompileContract struct{}
+
+func (p *precompileContract) RequiredGas(input []byte) uint64 { return 0 }
+
+func (p *precompileContract) Run(input []byte) ([]byte, error) { return nil, nil }
+
+func TestStateOverrideMovePrecompile(t *testing.T) {
+ db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
+ statedb, err := state.New(common.Hash{}, db)
+ if err != nil {
+ t.Fatalf("failed to create statedb: %v", err)
+ }
+ precompiles := map[common.Address]vm.PrecompiledContract{
+ common.BytesToAddress([]byte{0x1}): &precompileContract{},
+ common.BytesToAddress([]byte{0x2}): &precompileContract{},
+ }
+ bytes2Addr := func(b []byte) *common.Address {
+ a := common.BytesToAddress(b)
+ return &a
+ }
+ var testSuite = []struct {
+ overrides StateOverride
+ expectedPrecompiles map[common.Address]struct{}
+ fail bool
+ }{
+ {
+ overrides: StateOverride{
+ common.BytesToAddress([]byte{0x1}): {
+ Code: hex2Bytes("0xff"),
+ MovePrecompileTo: bytes2Addr([]byte{0x2}),
+ },
+ common.BytesToAddress([]byte{0x2}): {
+ Code: hex2Bytes("0x00"),
+ },
+ },
+ // 0x2 has already been touched by the moveTo.
+ fail: true,
+ }, {
+ overrides: StateOverride{
+ common.BytesToAddress([]byte{0x1}): {
+ Code: hex2Bytes("0xff"),
+ MovePrecompileTo: bytes2Addr([]byte{0xff}),
+ },
+ common.BytesToAddress([]byte{0x3}): {
+ Code: hex2Bytes("0x00"),
+ MovePrecompileTo: bytes2Addr([]byte{0xfe}),
+ },
+ },
+ // 0x3 is not a precompile.
+ fail: true,
+ }, {
+ overrides: StateOverride{
+ common.BytesToAddress([]byte{0x1}): {
+ Code: hex2Bytes("0xff"),
+ MovePrecompileTo: bytes2Addr([]byte{0xff}),
+ },
+ common.BytesToAddress([]byte{0x2}): {
+ Code: hex2Bytes("0x00"),
+ MovePrecompileTo: bytes2Addr([]byte{0xfe}),
+ },
+ },
+ expectedPrecompiles: map[common.Address]struct{}{common.BytesToAddress([]byte{0xfe}): {}, common.BytesToAddress([]byte{0xff}): {}},
+ },
+ }
+
+ for i, tt := range testSuite {
+ cpy := maps.Clone(precompiles)
+ // Apply overrides
+ err := tt.overrides.Apply(statedb, cpy)
+ if tt.fail {
+ if err == nil {
+ t.Errorf("test %d: want error, have nothing", i)
+ }
+ continue
+ }
+ if err != nil {
+ t.Errorf("test %d: want no error, have %v", i, err)
+ continue
+ }
+ // Precompile keys
+ if len(cpy) != len(tt.expectedPrecompiles) {
+ t.Errorf("test %d: precompile mismatch, want %d, have %d", i, len(tt.expectedPrecompiles), len(cpy))
+ }
+ for k := range tt.expectedPrecompiles {
+ if _, ok := cpy[k]; !ok {
+ t.Errorf("test %d: precompile not found: %s", i, k.String())
+ }
+ }
+ }
+}
+
+func hex2Bytes(str string) *hexutil.Bytes {
+ rpcBytes := hexutil.Bytes(common.FromHex(str))
+ return &rpcBytes
+}
diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go
index f6647c7ba4..5bd0079a93 100644
--- a/internal/ethapi/simulate.go
+++ b/internal/ethapi/simulate.go
@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/internal/ethapi/override"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
@@ -49,8 +50,8 @@ const (
// simBlock is a batch of calls to be simulated sequentially.
type simBlock struct {
- BlockOverrides *BlockOverrides
- StateOverrides *StateOverride
+ BlockOverrides *override.BlockOverrides
+ StateOverrides *override.StateOverride
Calls []TransactionArgs
}
@@ -303,7 +304,7 @@ func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) {
)
for _, block := range blocks {
if block.BlockOverrides == nil {
- block.BlockOverrides = new(BlockOverrides)
+ block.BlockOverrides = new(override.BlockOverrides)
}
if block.BlockOverrides.Number == nil {
n := new(big.Int).Add(prevNumber, big.NewInt(1))
@@ -323,7 +324,7 @@ func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) {
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)}}
+ b := simBlock{BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(n), Time: (*hexutil.Uint64)(&t)}}
prevTimestamp = t
res = append(res, b)
}
diff --git a/internal/ethapi/simulate_test.go b/internal/ethapi/simulate_test.go
index 37883924ac..52ae40cad0 100644
--- a/internal/ethapi/simulate_test.go
+++ b/internal/ethapi/simulate_test.go
@@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/internal/ethapi/override"
)
func TestSimulateSanitizeBlockOrder(t *testing.T) {
@@ -45,37 +46,37 @@ func TestSimulateSanitizeBlockOrder(t *testing.T) {
{
baseNumber: 10,
baseTimestamp: 50,
- blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13), Time: newUint64(70)}}, {}},
+ blocks: []simBlock{{BlockOverrides: &override.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)}}, {}},
+ blocks: []simBlock{{BlockOverrides: &override.BlockOverrides{Number: newInt(11)}}, {BlockOverrides: &override.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)}}},
+ blocks: []simBlock{{BlockOverrides: &override.BlockOverrides{Number: newInt(13)}}, {BlockOverrides: &override.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)}}},
+ blocks: []simBlock{{BlockOverrides: &override.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)}}},
+ blocks: []simBlock{{BlockOverrides: &override.BlockOverrides{Number: newInt(11), Time: newUint64(60)}}, {BlockOverrides: &override.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)}}},
+ blocks: []simBlock{{BlockOverrides: &override.BlockOverrides{Number: newInt(11), Time: newUint64(60)}}, {BlockOverrides: &override.BlockOverrides{Number: newInt(13), Time: newUint64(61)}}},
err: "block timestamps must be in order: 61 <= 61",
},
} {
diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go
index 91e05544dc..2a0508b147 100644
--- a/internal/ethapi/transaction_args.go
+++ b/internal/ethapi/transaction_args.go
@@ -160,7 +160,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas
BlobHashes: args.BlobHashes,
}
latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
- estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, b.RPCGasCap())
+ estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, nil, b.RPCGasCap())
if err != nil {
return err
}
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index 0c346bbf79..8ac8f44958 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -503,8 +503,8 @@ web3._extend({
new web3._extend.Method({
name: 'estimateGas',
call: 'eth_estimateGas',
- params: 3,
- inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputBlockNumberFormatter, null],
+ params: 4,
+ inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputBlockNumberFormatter, null, null],
outputFormatter: web3._extend.utils.toDecimal
}),
new web3._extend.Method({