This commit is contained in:
Antony Denyer 2024-11-28 15:33:15 +01:00 committed by GitHub
commit af87cdc8cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 545 additions and 375 deletions

1
.gitignore vendored
View File

@ -37,6 +37,7 @@ profile.cov
# IdeaIDE
.idea
*.iml
# VS Code
.vscode

View File

@ -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
}
@ -222,6 +224,9 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio
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 msgContext.GasPrice.Sign() == 0 {

View File

@ -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
}

View File

@ -43,6 +43,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"
)
@ -367,7 +368,7 @@ func TestTraceCall(t *testing.T) {
Input: &hexutil.Bytes{0x43}, // blocknumber
},
config: &TraceCallConfig{
BlockOverrides: &ethapi.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":[
@ -611,8 +612,8 @@ func TestTracingWithOverrides(t *testing.T) {
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: &TraceCallConfig{
StateOverrides: &ethapi.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":""}`,
@ -653,8 +654,8 @@ func TestTracingWithOverrides(t *testing.T) {
},
config: &TraceCallConfig{
//Tracer: &tracer,
StateOverrides: &ethapi.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))}),
},
@ -670,7 +671,7 @@ func TestTracingWithOverrides(t *testing.T) {
Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")),
},
config: &TraceCallConfig{
BlockOverrides: &ethapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
},
want: `{"gas":59537,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000001337"}`,
},
@ -690,7 +691,7 @@ func TestTracingWithOverrides(t *testing.T) {
}, // blocknumber
},
config: &TraceCallConfig{
BlockOverrides: &ethapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
},
want: `{"gas":72666,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`,
},
@ -720,8 +721,8 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
},
config: &TraceCallConfig{
StateOverrides: &ethapi.StateOverride{
randomAccounts[2].addr: ethapi.OverrideAccount{
StateOverrides: &override.StateOverride{
randomAccounts[2].addr: override.OverrideAccount{
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")),
},
},
@ -736,8 +737,8 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
},
config: &TraceCallConfig{
StateOverrides: &ethapi.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{{}}),
},
@ -754,8 +755,8 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
},
config: &TraceCallConfig{
StateOverrides: &ethapi.StateOverride{
storageAccount: ethapi.OverrideAccount{
StateOverrides: &override.StateOverride{
storageAccount: override.OverrideAccount{
Code: newRPCBytes([]byte{
// SLOAD(3) + SLOAD(4) (which is 0x77)
byte(vm.PUSH1), 0x04,
@ -789,8 +790,8 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
},
config: &TraceCallConfig{
StateOverrides: &ethapi.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,
@ -827,8 +828,8 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
},
config: &TraceCallConfig{
StateOverrides: &ethapi.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,

View File

@ -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.

View File

@ -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)
@ -898,7 +732,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)
@ -914,7 +748,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
@ -973,7 +807,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 {
@ -984,11 +818,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.
@ -1017,12 +852,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 .

View File

@ -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 {

View File

@ -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 <http://www.gnu.org/licenses/>.
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
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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
}

View File

@ -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
}
@ -304,7 +305,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))
@ -324,7 +325,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)
}

View File

@ -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",
},
} {

View File

@ -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
}

View File

@ -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({