mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-14 20:16:36 +00:00
internal/ethapi: add block override to estimateGas (#30695)
Add block overrides to `eth_estimateGas` to align consistency with `eth_call`. https://github.com/ethereum/go-ethereum/issues/27800#issuecomment-1658186166 Fixes https://github.com/ethereum/go-ethereum/issues/28175 --------- Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com>
This commit is contained in:
parent
c1c2507148
commit
88cbfab332
13 changed files with 545 additions and 375 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -37,6 +37,7 @@ profile.cov
|
||||||
|
|
||||||
# IdeaIDE
|
# IdeaIDE
|
||||||
.idea
|
.idea
|
||||||
|
*.iml
|
||||||
|
|
||||||
# VS Code
|
# VS Code
|
||||||
.vscode
|
.vscode
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi/override"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
@ -38,10 +39,11 @@ import (
|
||||||
// these together, it would be excessively hard to test. Splitting the parts out
|
// these together, it would be excessively hard to test. Splitting the parts out
|
||||||
// allows testing without needing a proper live chain.
|
// allows testing without needing a proper live chain.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Config *params.ChainConfig // Chain configuration for hard fork selection
|
Config *params.ChainConfig // Chain configuration for hard fork selection
|
||||||
Chain core.ChainContext // Chain context to access past block hashes
|
Chain core.ChainContext // Chain context to access past block hashes
|
||||||
Header *types.Header // Header defining the block context to execute in
|
Header *types.Header // Header defining the block context to execute in
|
||||||
State *state.StateDB // Pre-state on top of which to estimate the gas
|
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
|
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)
|
evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil)
|
||||||
dirtyState = opts.State.Copy()
|
dirtyState = opts.State.Copy()
|
||||||
)
|
)
|
||||||
|
if opts.BlockOverrides != nil {
|
||||||
|
opts.BlockOverrides.Apply(&evmContext)
|
||||||
|
}
|
||||||
// Lower the basefee to 0 to avoid breaking EVM
|
// Lower the basefee to 0 to avoid breaking EVM
|
||||||
// invariants (basefee < feecap).
|
// invariants (basefee < feecap).
|
||||||
if call.GasPrice.Sign() == 0 {
|
if call.GasPrice.Sign() == 0 {
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi/override"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
|
@ -163,8 +164,8 @@ type TraceConfig struct {
|
||||||
// field to override the state for tracing.
|
// field to override the state for tracing.
|
||||||
type TraceCallConfig struct {
|
type TraceCallConfig struct {
|
||||||
TraceConfig
|
TraceConfig
|
||||||
StateOverrides *ethapi.StateOverride
|
StateOverrides *override.StateOverride
|
||||||
BlockOverrides *ethapi.BlockOverrides
|
BlockOverrides *override.BlockOverrides
|
||||||
TxIndex *hexutil.Uint
|
TxIndex *hexutil.Uint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi/override"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
@ -454,7 +455,7 @@ func TestTraceCall(t *testing.T) {
|
||||||
Input: &hexutil.Bytes{0x43}, // blocknumber
|
Input: &hexutil.Bytes{0x43}, // blocknumber
|
||||||
},
|
},
|
||||||
config: &TraceCallConfig{
|
config: &TraceCallConfig{
|
||||||
BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
|
BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
|
||||||
},
|
},
|
||||||
expectErr: nil,
|
expectErr: nil,
|
||||||
expect: ` {"gas":53018,"failed":false,"returnValue":"","structLogs":[
|
expect: ` {"gas":53018,"failed":false,"returnValue":"","structLogs":[
|
||||||
|
|
@ -698,8 +699,8 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||||
Value: (*hexutil.Big)(big.NewInt(1000)),
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
},
|
},
|
||||||
config: &TraceCallConfig{
|
config: &TraceCallConfig{
|
||||||
StateOverrides: ðapi.StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
|
randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: `{"gas":21000,"failed":false,"returnValue":""}`,
|
want: `{"gas":21000,"failed":false,"returnValue":""}`,
|
||||||
|
|
@ -740,8 +741,8 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||||
},
|
},
|
||||||
config: &TraceCallConfig{
|
config: &TraceCallConfig{
|
||||||
//Tracer: &tracer,
|
//Tracer: &tracer,
|
||||||
StateOverrides: ðapi.StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: ethapi.OverrideAccount{
|
randomAccounts[2].addr: override.OverrideAccount{
|
||||||
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")),
|
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")),
|
||||||
StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}),
|
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")),
|
Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")),
|
||||||
},
|
},
|
||||||
config: &TraceCallConfig{
|
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"}`,
|
want: `{"gas":59537,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000001337"}`,
|
||||||
},
|
},
|
||||||
|
|
@ -777,7 +778,7 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||||
}, // blocknumber
|
}, // blocknumber
|
||||||
},
|
},
|
||||||
config: &TraceCallConfig{
|
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"}`,
|
want: `{"gas":72666,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`,
|
||||||
},
|
},
|
||||||
|
|
@ -807,8 +808,8 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||||
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
|
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
|
||||||
},
|
},
|
||||||
config: &TraceCallConfig{
|
config: &TraceCallConfig{
|
||||||
StateOverrides: ðapi.StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: ethapi.OverrideAccount{
|
randomAccounts[2].addr: override.OverrideAccount{
|
||||||
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")),
|
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -823,8 +824,8 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||||
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
|
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
|
||||||
},
|
},
|
||||||
config: &TraceCallConfig{
|
config: &TraceCallConfig{
|
||||||
StateOverrides: ðapi.StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: ethapi.OverrideAccount{
|
randomAccounts[2].addr: override.OverrideAccount{
|
||||||
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")),
|
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")),
|
||||||
State: newStates([]common.Hash{{}}, []common.Hash{{}}),
|
State: newStates([]common.Hash{{}}, []common.Hash{{}}),
|
||||||
},
|
},
|
||||||
|
|
@ -841,8 +842,8 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||||
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
|
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
|
||||||
},
|
},
|
||||||
config: &TraceCallConfig{
|
config: &TraceCallConfig{
|
||||||
StateOverrides: ðapi.StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
storageAccount: ethapi.OverrideAccount{
|
storageAccount: override.OverrideAccount{
|
||||||
Code: newRPCBytes([]byte{
|
Code: newRPCBytes([]byte{
|
||||||
// SLOAD(3) + SLOAD(4) (which is 0x77)
|
// SLOAD(3) + SLOAD(4) (which is 0x77)
|
||||||
byte(vm.PUSH1), 0x04,
|
byte(vm.PUSH1), 0x04,
|
||||||
|
|
@ -876,8 +877,8 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||||
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
|
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
|
||||||
},
|
},
|
||||||
config: &TraceCallConfig{
|
config: &TraceCallConfig{
|
||||||
StateOverrides: ðapi.StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
storageAccount: ethapi.OverrideAccount{
|
storageAccount: override.OverrideAccount{
|
||||||
Code: newRPCBytes([]byte{
|
Code: newRPCBytes([]byte{
|
||||||
// SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x00)
|
// SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x00)
|
||||||
byte(vm.PUSH1), 0x04,
|
byte(vm.PUSH1), 0x04,
|
||||||
|
|
@ -914,8 +915,8 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||||
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
|
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
|
||||||
},
|
},
|
||||||
config: &TraceCallConfig{
|
config: &TraceCallConfig{
|
||||||
StateOverrides: ðapi.StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
storageAccount: ethapi.OverrideAccount{
|
storageAccount: override.OverrideAccount{
|
||||||
Code: newRPCBytes([]byte{
|
Code: newRPCBytes([]byte{
|
||||||
// SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x44)
|
// SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x44)
|
||||||
byte(vm.PUSH1), 0x04,
|
byte(vm.PUSH1), 0x04,
|
||||||
|
|
|
||||||
|
|
@ -1208,7 +1208,7 @@ func (b *Block) Call(ctx context.Context, args struct {
|
||||||
func (b *Block) EstimateGas(ctx context.Context, args struct {
|
func (b *Block) EstimateGas(ctx context.Context, args struct {
|
||||||
Data ethapi.TransactionArgs
|
Data ethapi.TransactionArgs
|
||||||
}) (hexutil.Uint64, error) {
|
}) (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 {
|
type Pending struct {
|
||||||
|
|
@ -1272,7 +1272,7 @@ func (p *Pending) EstimateGas(ctx context.Context, args struct {
|
||||||
Data ethapi.TransactionArgs
|
Data ethapi.TransactionArgs
|
||||||
}) (hexutil.Uint64, error) {
|
}) (hexutil.Uint64, error) {
|
||||||
latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
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.
|
// Resolver is the top-level object in the GraphQL hierarchy.
|
||||||
|
|
|
||||||
|
|
@ -35,19 +35,18 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/gasestimator"
|
"github.com/ethereum/go-ethereum/eth/gasestimator"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"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/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/holiman/uint256"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// estimateGasErrorRatio is the amount of overestimation eth_estimateGas is
|
// 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
|
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.
|
// ChainContextBackend provides methods required to implement ChainContext.
|
||||||
type ChainContextBackend interface {
|
type ChainContextBackend interface {
|
||||||
Engine() consensus.Engine
|
Engine() consensus.Engine
|
||||||
|
|
@ -818,7 +652,7 @@ func (context *ChainContext) GetHeader(hash common.Hash, number uint64) *types.H
|
||||||
return header
|
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)
|
blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
|
||||||
if blockOverrides != nil {
|
if blockOverrides != nil {
|
||||||
blockOverrides.Apply(&blockCtx)
|
blockOverrides.Apply(&blockCtx)
|
||||||
|
|
@ -897,7 +731,7 @@ func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, ti
|
||||||
return result, nil
|
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())
|
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||||
|
|
||||||
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
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
|
// Note, this function doesn't make and changes in the state/blockchain and is
|
||||||
// useful to execute and retrieve values.
|
// 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 {
|
if blockNrOrHash == nil {
|
||||||
latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
||||||
blockNrOrHash = &latest
|
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
|
// 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 &
|
// there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil &
|
||||||
// non-zero) and `gasCap` (if non-zero).
|
// 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
|
// Retrieve the base state and mutate it with any overrides
|
||||||
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if state == nil || err != nil {
|
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
|
// Construct the gas estimator option from the user input
|
||||||
opts := &gasestimator.Options{
|
opts := &gasestimator.Options{
|
||||||
Config: b.ChainConfig(),
|
Config: b.ChainConfig(),
|
||||||
Chain: NewChainContext(ctx, b),
|
Chain: NewChainContext(ctx, b),
|
||||||
Header: header,
|
Header: header,
|
||||||
State: state,
|
BlockOverrides: blockOverrides,
|
||||||
ErrorRatio: estimateGasErrorRatio,
|
State: state,
|
||||||
|
ErrorRatio: estimateGasErrorRatio,
|
||||||
}
|
}
|
||||||
// Set any required transaction default, but make sure the gas cap itself is not messed with
|
// 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.
|
// 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
|
// value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap
|
||||||
// configuration (if non-zero).
|
// configuration (if non-zero).
|
||||||
// Note: Required blob gas is not computed in this method.
|
// 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)
|
bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
||||||
if blockNrOrHash != nil {
|
if blockNrOrHash != nil {
|
||||||
bNrOrHash = *blockNrOrHash
|
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 .
|
// RPCMarshalHeader converts the given header to the RPC output .
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -34,6 +33,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
|
@ -55,7 +57,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/internal/blocktest"
|
"github.com/ethereum/go-ethereum/internal/blocktest"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
@ -640,6 +641,17 @@ func TestEstimateGas(t *testing.T) {
|
||||||
signer = types.HomesteadSigner{}
|
signer = types.HomesteadSigner{}
|
||||||
randomAccounts = newAccounts(2)
|
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) {
|
api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
|
||||||
// Transfer from account[0] to account[1]
|
// Transfer from account[0] to account[1]
|
||||||
// value: 1000 wei
|
// value: 1000 wei
|
||||||
|
|
@ -648,14 +660,16 @@ func TestEstimateGas(t *testing.T) {
|
||||||
b.AddTx(tx)
|
b.AddTx(tx)
|
||||||
b.SetPoS()
|
b.SetPoS()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
var testSuite = []struct {
|
var testSuite = []struct {
|
||||||
blockNumber rpc.BlockNumber
|
blockNumber rpc.BlockNumber
|
||||||
call TransactionArgs
|
call TransactionArgs
|
||||||
overrides StateOverride
|
overrides override.StateOverride
|
||||||
expectErr error
|
blockOverrides override.BlockOverrides
|
||||||
want uint64
|
expectErr error
|
||||||
|
want uint64
|
||||||
}{
|
}{
|
||||||
// simple transfer on latest block
|
//simple transfer on latest block
|
||||||
{
|
{
|
||||||
blockNumber: rpc.LatestBlockNumber,
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
call: TransactionArgs{
|
call: TransactionArgs{
|
||||||
|
|
@ -687,8 +701,8 @@ func TestEstimateGas(t *testing.T) {
|
||||||
{
|
{
|
||||||
blockNumber: rpc.LatestBlockNumber,
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
call: TransactionArgs{},
|
call: TransactionArgs{},
|
||||||
overrides: StateOverride{
|
overrides: override.StateOverride{
|
||||||
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
|
randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
|
||||||
},
|
},
|
||||||
expectErr: nil,
|
expectErr: nil,
|
||||||
want: 53000,
|
want: 53000,
|
||||||
|
|
@ -700,8 +714,8 @@ func TestEstimateGas(t *testing.T) {
|
||||||
To: &randomAccounts[1].addr,
|
To: &randomAccounts[1].addr,
|
||||||
Value: (*hexutil.Big)(big.NewInt(1000)),
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
},
|
},
|
||||||
overrides: StateOverride{
|
overrides: override.StateOverride{
|
||||||
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(0))},
|
randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(0))},
|
||||||
},
|
},
|
||||||
expectErr: core.ErrInsufficientFunds,
|
expectErr: core.ErrInsufficientFunds,
|
||||||
},
|
},
|
||||||
|
|
@ -758,16 +772,65 @@ func TestEstimateGas(t *testing.T) {
|
||||||
},
|
},
|
||||||
want: 21000,
|
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 {
|
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 tc.expectErr != nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
|
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !errors.Is(err, tc.expectErr) {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -818,9 +881,9 @@ func TestCall(t *testing.T) {
|
||||||
var testSuite = []struct {
|
var testSuite = []struct {
|
||||||
name string
|
name string
|
||||||
blockNumber rpc.BlockNumber
|
blockNumber rpc.BlockNumber
|
||||||
overrides StateOverride
|
overrides override.StateOverride
|
||||||
call TransactionArgs
|
call TransactionArgs
|
||||||
blockOverrides BlockOverrides
|
blockOverrides override.BlockOverrides
|
||||||
expectErr error
|
expectErr error
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
|
|
@ -880,8 +943,8 @@ func TestCall(t *testing.T) {
|
||||||
To: &randomAccounts[1].addr,
|
To: &randomAccounts[1].addr,
|
||||||
Value: (*hexutil.Big)(big.NewInt(1000)),
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
},
|
},
|
||||||
overrides: StateOverride{
|
overrides: override.StateOverride{
|
||||||
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
|
randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
|
||||||
},
|
},
|
||||||
want: "0x",
|
want: "0x",
|
||||||
},
|
},
|
||||||
|
|
@ -920,27 +983,60 @@ func TestCall(t *testing.T) {
|
||||||
To: &randomAccounts[2].addr,
|
To: &randomAccounts[2].addr,
|
||||||
Data: hex2Bytes("8381f58a"), // call number()
|
Data: hex2Bytes("8381f58a"), // call number()
|
||||||
},
|
},
|
||||||
overrides: StateOverride{
|
overrides: override.StateOverride{
|
||||||
randomAccounts[2].addr: OverrideAccount{
|
randomAccounts[2].addr: override.OverrideAccount{
|
||||||
Code: hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"),
|
Code: hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"),
|
||||||
StateDiff: map[common.Hash]common.Hash{{}: common.BigToHash(big.NewInt(123))},
|
StateDiff: map[common.Hash]common.Hash{{}: common.BigToHash(big.NewInt(123))},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: "0x000000000000000000000000000000000000000000000000000000000000007b",
|
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,
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
call: TransactionArgs{
|
call: TransactionArgs{
|
||||||
From: &accounts[1].addr,
|
From: &accounts[1].addr,
|
||||||
Input: &hexutil.Bytes{
|
To: &accounts[2].addr,
|
||||||
0x43, // NUMBER
|
Data: hex2Bytes("0x28b5e32b"), //call
|
||||||
0x60, 0x00, 0x52, // MSTORE offset 0
|
},
|
||||||
0x60, 0x20, 0x60, 0x00, 0xf3,
|
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",
|
want: "0x000000000000000000000000000000000000000000000000000000000000000b",
|
||||||
},
|
},
|
||||||
// Clear storage trie
|
// Clear storage trie
|
||||||
|
|
@ -963,8 +1059,8 @@ func TestCall(t *testing.T) {
|
||||||
// }
|
// }
|
||||||
Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"),
|
Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"),
|
||||||
},
|
},
|
||||||
overrides: StateOverride{
|
overrides: override.StateOverride{
|
||||||
dad: OverrideAccount{
|
dad: override.OverrideAccount{
|
||||||
State: map[common.Hash]common.Hash{},
|
State: map[common.Hash]common.Hash{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -991,7 +1087,7 @@ func TestCall(t *testing.T) {
|
||||||
BlobHashes: []common.Hash{{0x01, 0x22}},
|
BlobHashes: []common.Hash{{0x01, 0x22}},
|
||||||
BlobFeeCap: (*hexutil.Big)(big.NewInt(1)),
|
BlobFeeCap: (*hexutil.Big)(big.NewInt(1)),
|
||||||
},
|
},
|
||||||
overrides: StateOverride{
|
overrides: override.StateOverride{
|
||||||
randomAccounts[2].addr: {
|
randomAccounts[2].addr: {
|
||||||
Code: hex2Bytes("60004960005260206000f3"),
|
Code: hex2Bytes("60004960005260206000f3"),
|
||||||
},
|
},
|
||||||
|
|
@ -1017,8 +1113,8 @@ func TestCall(t *testing.T) {
|
||||||
// }
|
// }
|
||||||
Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"),
|
Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"),
|
||||||
},
|
},
|
||||||
overrides: StateOverride{
|
overrides: override.StateOverride{
|
||||||
dad: OverrideAccount{
|
dad: override.OverrideAccount{
|
||||||
State: map[common.Hash]common.Hash{},
|
State: map[common.Hash]common.Hash{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -1172,8 +1268,8 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "simple",
|
name: "simple",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(1000))},
|
randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(1000))},
|
||||||
},
|
},
|
||||||
Calls: []TransactionArgs{{
|
Calls: []TransactionArgs{{
|
||||||
From: &randomAccounts[0].addr,
|
From: &randomAccounts[0].addr,
|
||||||
|
|
@ -1215,8 +1311,8 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "simple-multi-block",
|
name: "simple-multi-block",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(2000))},
|
randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(2000))},
|
||||||
},
|
},
|
||||||
Calls: []TransactionArgs{
|
Calls: []TransactionArgs{
|
||||||
{
|
{
|
||||||
|
|
@ -1230,8 +1326,8 @@ func TestSimulateV1(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[3].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(0))},
|
randomAccounts[3].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(0))},
|
||||||
},
|
},
|
||||||
Calls: []TransactionArgs{
|
Calls: []TransactionArgs{
|
||||||
{
|
{
|
||||||
|
|
@ -1289,8 +1385,8 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "evm-error",
|
name: "evm-error",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: OverrideAccount{Code: hex2Bytes("f3")},
|
randomAccounts[2].addr: override.OverrideAccount{Code: hex2Bytes("f3")},
|
||||||
},
|
},
|
||||||
Calls: []TransactionArgs{{
|
Calls: []TransactionArgs{{
|
||||||
From: &randomAccounts[0].addr,
|
From: &randomAccounts[0].addr,
|
||||||
|
|
@ -1316,7 +1412,7 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "block-overrides",
|
name: "block-overrides",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
BlockOverrides: &BlockOverrides{
|
BlockOverrides: &override.BlockOverrides{
|
||||||
Number: (*hexutil.Big)(big.NewInt(11)),
|
Number: (*hexutil.Big)(big.NewInt(11)),
|
||||||
FeeRecipient: &cac,
|
FeeRecipient: &cac,
|
||||||
},
|
},
|
||||||
|
|
@ -1331,7 +1427,7 @@ func TestSimulateV1(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
BlockOverrides: &BlockOverrides{
|
BlockOverrides: &override.BlockOverrides{
|
||||||
Number: (*hexutil.Big)(big.NewInt(12)),
|
Number: (*hexutil.Big)(big.NewInt(12)),
|
||||||
},
|
},
|
||||||
Calls: []TransactionArgs{{
|
Calls: []TransactionArgs{{
|
||||||
|
|
@ -1374,7 +1470,7 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "block-number-order",
|
name: "block-number-order",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
BlockOverrides: &BlockOverrides{
|
BlockOverrides: &override.BlockOverrides{
|
||||||
Number: (*hexutil.Big)(big.NewInt(12)),
|
Number: (*hexutil.Big)(big.NewInt(12)),
|
||||||
},
|
},
|
||||||
Calls: []TransactionArgs{{
|
Calls: []TransactionArgs{{
|
||||||
|
|
@ -1386,7 +1482,7 @@ func TestSimulateV1(t *testing.T) {
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
BlockOverrides: &BlockOverrides{
|
BlockOverrides: &override.BlockOverrides{
|
||||||
Number: (*hexutil.Big)(big.NewInt(11)),
|
Number: (*hexutil.Big)(big.NewInt(11)),
|
||||||
},
|
},
|
||||||
Calls: []TransactionArgs{{
|
Calls: []TransactionArgs{{
|
||||||
|
|
@ -1406,8 +1502,8 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "storage-contract",
|
name: "storage-contract",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: OverrideAccount{
|
randomAccounts[2].addr: override.OverrideAccount{
|
||||||
Code: hex2Bytes("608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220404e37f487a89a932dca5e77faaf6ca2de3b991f93d230604b1b8daaef64766264736f6c63430008070033"),
|
Code: hex2Bytes("608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220404e37f487a89a932dca5e77faaf6ca2de3b991f93d230604b1b8daaef64766264736f6c63430008070033"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -1448,8 +1544,8 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "logs",
|
name: "logs",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: OverrideAccount{
|
randomAccounts[2].addr: override.OverrideAccount{
|
||||||
// Yul code:
|
// Yul code:
|
||||||
// object "Test" {
|
// object "Test" {
|
||||||
// code {
|
// code {
|
||||||
|
|
@ -1490,8 +1586,8 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "ecrecover-override",
|
name: "ecrecover-override",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: OverrideAccount{
|
randomAccounts[2].addr: override.OverrideAccount{
|
||||||
// Yul code that returns ecrecover(0, 0, 0, 0).
|
// Yul code that returns ecrecover(0, 0, 0, 0).
|
||||||
// object "Test" {
|
// object "Test" {
|
||||||
// code {
|
// code {
|
||||||
|
|
@ -1518,7 +1614,7 @@ func TestSimulateV1(t *testing.T) {
|
||||||
// }
|
// }
|
||||||
Code: hex2Bytes("6040516000815260006020820152600060408201526000606082015260208160808360015afa60008103603157600080fd5b601482f3"),
|
Code: hex2Bytes("6040516000815260006020820152600060408201526000606082015260208160808360015afa60008103603157600080fd5b601482f3"),
|
||||||
},
|
},
|
||||||
common.BytesToAddress([]byte{0x01}): OverrideAccount{
|
common.BytesToAddress([]byte{0x01}): override.OverrideAccount{
|
||||||
// Yul code that returns the address of the caller.
|
// Yul code that returns the address of the caller.
|
||||||
// object "Test" {
|
// object "Test" {
|
||||||
// code {
|
// code {
|
||||||
|
|
@ -1555,8 +1651,8 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "precompile-move",
|
name: "precompile-move",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
sha256Address: OverrideAccount{
|
sha256Address: override.OverrideAccount{
|
||||||
// Yul code that returns the calldata.
|
// Yul code that returns the calldata.
|
||||||
// object "Test" {
|
// object "Test" {
|
||||||
// code {
|
// code {
|
||||||
|
|
@ -1610,8 +1706,8 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "transfer-logs",
|
name: "transfer-logs",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[0].addr: OverrideAccount{
|
randomAccounts[0].addr: override.OverrideAccount{
|
||||||
Balance: newRPCBalance(big.NewInt(100)),
|
Balance: newRPCBalance(big.NewInt(100)),
|
||||||
// Yul code that transfers 100 wei to address passed in calldata:
|
// Yul code that transfers 100 wei to address passed in calldata:
|
||||||
// object "Test" {
|
// object "Test" {
|
||||||
|
|
@ -1753,8 +1849,8 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "validation-checks-from-contract",
|
name: "validation-checks-from-contract",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: OverrideAccount{
|
randomAccounts[2].addr: override.OverrideAccount{
|
||||||
Balance: newRPCBalance(big.NewInt(2098640803896784)),
|
Balance: newRPCBalance(big.NewInt(2098640803896784)),
|
||||||
Code: hex2Bytes("00"),
|
Code: hex2Bytes("00"),
|
||||||
Nonce: newUint64(1),
|
Nonce: newUint64(1),
|
||||||
|
|
@ -1788,11 +1884,11 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "validation-checks-success",
|
name: "validation-checks-success",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
BlockOverrides: &BlockOverrides{
|
BlockOverrides: &override.BlockOverrides{
|
||||||
BaseFeePerGas: (*hexutil.Big)(big.NewInt(1)),
|
BaseFeePerGas: (*hexutil.Big)(big.NewInt(1)),
|
||||||
},
|
},
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(10000000))},
|
randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(10000000))},
|
||||||
},
|
},
|
||||||
Calls: []TransactionArgs{{
|
Calls: []TransactionArgs{{
|
||||||
From: &randomAccounts[0].addr,
|
From: &randomAccounts[0].addr,
|
||||||
|
|
@ -1821,7 +1917,7 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "clear-storage",
|
name: "clear-storage",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: {
|
randomAccounts[2].addr: {
|
||||||
Code: newBytes(genesis.Alloc[bab].Code),
|
Code: newBytes(genesis.Alloc[bab].Code),
|
||||||
StateDiff: map[common.Hash]common.Hash{
|
StateDiff: map[common.Hash]common.Hash{
|
||||||
|
|
@ -1843,7 +1939,7 @@ func TestSimulateV1(t *testing.T) {
|
||||||
To: &bab,
|
To: &bab,
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: {
|
randomAccounts[2].addr: {
|
||||||
State: map[common.Hash]common.Hash{
|
State: map[common.Hash]common.Hash{
|
||||||
common.BigToHash(big.NewInt(1)): common.BigToHash(big.NewInt(5)),
|
common.BigToHash(big.NewInt(1)): common.BigToHash(big.NewInt(5)),
|
||||||
|
|
@ -1890,10 +1986,10 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "blockhash-opcode",
|
name: "blockhash-opcode",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
BlockOverrides: &BlockOverrides{
|
BlockOverrides: &override.BlockOverrides{
|
||||||
Number: (*hexutil.Big)(big.NewInt(12)),
|
Number: (*hexutil.Big)(big.NewInt(12)),
|
||||||
},
|
},
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: {
|
randomAccounts[2].addr: {
|
||||||
Code: hex2Bytes("600035804060008103601057600080fd5b5050"),
|
Code: hex2Bytes("600035804060008103601057600080fd5b5050"),
|
||||||
},
|
},
|
||||||
|
|
@ -1915,7 +2011,7 @@ func TestSimulateV1(t *testing.T) {
|
||||||
Input: uint256ToBytes(uint256.NewInt(10)),
|
Input: uint256ToBytes(uint256.NewInt(10)),
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
BlockOverrides: &BlockOverrides{
|
BlockOverrides: &override.BlockOverrides{
|
||||||
Number: (*hexutil.Big)(big.NewInt(16)),
|
Number: (*hexutil.Big)(big.NewInt(16)),
|
||||||
},
|
},
|
||||||
Calls: []TransactionArgs{{
|
Calls: []TransactionArgs{{
|
||||||
|
|
@ -2005,7 +2101,7 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "basefee-non-validation",
|
name: "basefee-non-validation",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: {
|
randomAccounts[2].addr: {
|
||||||
// Yul code:
|
// Yul code:
|
||||||
// object "Test" {
|
// object "Test" {
|
||||||
|
|
@ -2040,7 +2136,7 @@ func TestSimulateV1(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
BlockOverrides: &BlockOverrides{
|
BlockOverrides: &override.BlockOverrides{
|
||||||
BaseFeePerGas: (*hexutil.Big)(big.NewInt(1)),
|
BaseFeePerGas: (*hexutil.Big)(big.NewInt(1)),
|
||||||
},
|
},
|
||||||
Calls: []TransactionArgs{{
|
Calls: []TransactionArgs{{
|
||||||
|
|
@ -2113,7 +2209,7 @@ func TestSimulateV1(t *testing.T) {
|
||||||
name: "basefee-validation-mode",
|
name: "basefee-validation-mode",
|
||||||
tag: latest,
|
tag: latest,
|
||||||
blocks: []simBlock{{
|
blocks: []simBlock{{
|
||||||
StateOverrides: &StateOverride{
|
StateOverrides: &override.StateOverride{
|
||||||
randomAccounts[2].addr: {
|
randomAccounts[2].addr: {
|
||||||
// Yul code:
|
// Yul code:
|
||||||
// object "Test" {
|
// 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) {
|
func testRPCResponseWithFile(t *testing.T, testid int, result interface{}, rpc string, file string) {
|
||||||
data, err := json.MarshalIndent(result, "", " ")
|
data, err := json.MarshalIndent(result, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
195
internal/ethapi/override/override.go
Normal file
195
internal/ethapi/override/override.go
Normal 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
|
||||||
|
}
|
||||||
125
internal/ethapi/override/override_test.go
Normal file
125
internal/ethapi/override/override_test.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi/override"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
|
@ -49,8 +50,8 @@ const (
|
||||||
|
|
||||||
// simBlock is a batch of calls to be simulated sequentially.
|
// simBlock is a batch of calls to be simulated sequentially.
|
||||||
type simBlock struct {
|
type simBlock struct {
|
||||||
BlockOverrides *BlockOverrides
|
BlockOverrides *override.BlockOverrides
|
||||||
StateOverrides *StateOverride
|
StateOverrides *override.StateOverride
|
||||||
Calls []TransactionArgs
|
Calls []TransactionArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,7 +304,7 @@ func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) {
|
||||||
)
|
)
|
||||||
for _, block := range blocks {
|
for _, block := range blocks {
|
||||||
if block.BlockOverrides == nil {
|
if block.BlockOverrides == nil {
|
||||||
block.BlockOverrides = new(BlockOverrides)
|
block.BlockOverrides = new(override.BlockOverrides)
|
||||||
}
|
}
|
||||||
if block.BlockOverrides.Number == nil {
|
if block.BlockOverrides.Number == nil {
|
||||||
n := new(big.Int).Add(prevNumber, big.NewInt(1))
|
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++ {
|
for i := uint64(0); i < gap.Uint64(); i++ {
|
||||||
n := new(big.Int).Add(prevNumber, big.NewInt(int64(i+1)))
|
n := new(big.Int).Add(prevNumber, big.NewInt(int64(i+1)))
|
||||||
t := prevTimestamp + timestampIncrement
|
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
|
prevTimestamp = t
|
||||||
res = append(res, b)
|
res = append(res, b)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi/override"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSimulateSanitizeBlockOrder(t *testing.T) {
|
func TestSimulateSanitizeBlockOrder(t *testing.T) {
|
||||||
|
|
@ -45,37 +46,37 @@ func TestSimulateSanitizeBlockOrder(t *testing.T) {
|
||||||
{
|
{
|
||||||
baseNumber: 10,
|
baseNumber: 10,
|
||||||
baseTimestamp: 50,
|
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}},
|
expected: []result{{number: 11, timestamp: 51}, {number: 12, timestamp: 52}, {number: 13, timestamp: 70}, {number: 14, timestamp: 71}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
baseNumber: 10,
|
baseNumber: 10,
|
||||||
baseTimestamp: 50,
|
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}},
|
expected: []result{{number: 11, timestamp: 51}, {number: 12, timestamp: 52}, {number: 13, timestamp: 53}, {number: 14, timestamp: 54}, {number: 15, timestamp: 55}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
baseNumber: 10,
|
baseNumber: 10,
|
||||||
baseTimestamp: 50,
|
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",
|
err: "block numbers must be in order: 12 <= 13",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
baseNumber: 10,
|
baseNumber: 10,
|
||||||
baseTimestamp: 50,
|
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",
|
err: "block timestamps must be in order: 52 <= 52",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
baseNumber: 10,
|
baseNumber: 10,
|
||||||
baseTimestamp: 50,
|
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",
|
err: "block timestamps must be in order: 55 <= 60",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
baseNumber: 10,
|
baseNumber: 10,
|
||||||
baseTimestamp: 50,
|
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",
|
err: "block timestamps must be in order: 61 <= 61",
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas
|
||||||
BlobHashes: args.BlobHashes,
|
BlobHashes: args.BlobHashes,
|
||||||
}
|
}
|
||||||
latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -503,8 +503,8 @@ web3._extend({
|
||||||
new web3._extend.Method({
|
new web3._extend.Method({
|
||||||
name: 'estimateGas',
|
name: 'estimateGas',
|
||||||
call: 'eth_estimateGas',
|
call: 'eth_estimateGas',
|
||||||
params: 3,
|
params: 4,
|
||||||
inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputBlockNumberFormatter, null],
|
inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputBlockNumberFormatter, null, null],
|
||||||
outputFormatter: web3._extend.utils.toDecimal
|
outputFormatter: web3._extend.utils.toDecimal
|
||||||
}),
|
}),
|
||||||
new web3._extend.Method({
|
new web3._extend.Method({
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue