Skip to content

Commit

Permalink
core, internal: support various eth_call invocations post 1559 (ether…
Browse files Browse the repository at this point in the history
  • Loading branch information
gzliudan committed Sep 9, 2024
1 parent a41200f commit aecb464
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 61 deletions.
37 changes: 20 additions & 17 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,23 +223,26 @@ func (st *StateTransition) preCheck() error {
}
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) {
if l := st.gasFeeCap.BitLen(); l > 256 {
return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
msg.From().Hex(), l)
}
if l := st.gasTipCap.BitLen(); l > 256 {
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh,
msg.From().Hex(), l)
}
if st.gasFeeCap.Cmp(st.gasTipCap) < 0 {
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap,
msg.From().Hex(), st.gasTipCap, st.gasFeeCap)
}
// This will panic if baseFee is nil, but basefee presence is verified
// as part of header validation.
if st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 {
return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow,
msg.From().Hex(), st.gasFeeCap, st.evm.Context.BaseFee)
// Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call)
if !st.evm.Config.NoBaseFee || st.gasFeeCap.BitLen() > 0 || st.gasTipCap.BitLen() > 0 {
if l := st.gasFeeCap.BitLen(); l > 256 {
return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
msg.From().Hex(), l)
}
if l := st.gasTipCap.BitLen(); l > 256 {
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh,
msg.From().Hex(), l)
}
if st.gasFeeCap.Cmp(st.gasTipCap) < 0 {
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap,
msg.From().Hex(), st.gasTipCap, st.gasFeeCap)
}
// This will panic if baseFee is nil, but basefee presence is verified
// as part of header validation.
if st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 {
return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow,
msg.From().Hex(), st.gasFeeCap, st.evm.Context.BaseFee)
}
}
}
return st.buyGas()
Expand Down
58 changes: 29 additions & 29 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ type EVM struct {
chainRules params.Rules
// virtual machine configuration options used to initialise the
// evm.
vmConfig Config
Config Config
// global (to this context) ethereum virtual machine
// used throughout the execution of the tx.
interpreters []Interpreter
Expand All @@ -177,20 +177,20 @@ type EVM struct {

// NewEVM returns a new EVM. The returned EVM is not thread safe and should
// only ever be used *once*.
func NewEVM(ctx Context, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
func NewEVM(ctx Context, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, config Config) *EVM {
evm := &EVM{
Context: ctx,
StateDB: statedb,
tradingStateDB: tradingStateDB,
vmConfig: vmConfig,
Config: config,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(ctx.BlockNumber),
interpreters: make([]Interpreter, 0, 1),
}

// vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
// as we always want to have the built-in EVM as the failover option.
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, config))
evm.interpreter = evm.interpreters[0]

return evm
Expand Down Expand Up @@ -233,13 +233,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
_, isPrecompile := evm.precompile2(addr)
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug {
if evm.Config.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
} else {
evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.vmConfig.Tracer.CaptureExit(ret, 0, nil)
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.Config.Tracer.CaptureExit(ret, 0, nil)
}
}
return nil, gas, nil
Expand All @@ -249,18 +249,18 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)

// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug {
if evm.Config.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)

defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
}(gas, time.Now())
} else {
// Handle tracer events for entering and exiting a call frame
evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
}
Expand Down Expand Up @@ -312,10 +312,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
)

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.vmConfig.Debug {
evm.vmConfig.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
if evm.Config.Debug {
evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}

Expand Down Expand Up @@ -351,10 +351,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
)

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.vmConfig.Debug {
evm.vmConfig.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
if evm.Config.Debug {
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}

Expand Down Expand Up @@ -400,10 +400,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
}

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.vmConfig.Debug {
evm.vmConfig.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
if evm.Config.Debug {
evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}

Expand Down Expand Up @@ -471,11 +471,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
contract := NewContract(caller, AccountRef(address), value, gas)
contract.SetCodeOptionalHash(&address, codeAndHash)

if evm.vmConfig.Debug {
if evm.Config.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
} else {
evm.vmConfig.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
}
}
start := time.Now()
Expand Down Expand Up @@ -515,11 +515,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
}

if evm.vmConfig.Debug {
if evm.Config.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
} else {
evm.vmConfig.Tracer.CaptureExit(ret, gas-contract.Gas, err)
evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
}
}
return ret, address, contract.Gas, err
Expand Down
2 changes: 1 addition & 1 deletion core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
interpreter.hasher.Read(interpreter.hasherBuf[:])

evm := interpreter.evm
if evm.vmConfig.EnablePreimageRecording {
if evm.Config.EnablePreimageRecording {
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
}

Expand Down
12 changes: 6 additions & 6 deletions core/vm/instructions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func TestAddMod(t *testing.T) {
var (
env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
evmInterpreter = NewEVMInterpreter(env, env.Config)
pc = uint64(0)
)
tests := []struct {
Expand Down Expand Up @@ -291,7 +291,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
var (
env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
evmInterpreter = NewEVMInterpreter(env, env.Config)
)

env.interpreter = evmInterpreter
Expand Down Expand Up @@ -526,7 +526,7 @@ func TestOpMstore(t *testing.T) {
env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
evmInterpreter = NewEVMInterpreter(env, env.Config)
)

env.interpreter = evmInterpreter
Expand All @@ -552,7 +552,7 @@ func BenchmarkOpMstore(bench *testing.B) {
env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
evmInterpreter = NewEVMInterpreter(env, env.Config)
)

env.interpreter = evmInterpreter
Expand All @@ -574,7 +574,7 @@ func BenchmarkOpKeccak256(bench *testing.B) {
env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
evmInterpreter = NewEVMInterpreter(env, env.Config)
)
env.interpreter = evmInterpreter
mem.Resize(32)
Expand Down Expand Up @@ -679,7 +679,7 @@ func TestRandom(t *testing.T) {
env = NewEVM(Context{Random: &tt.random}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
evmInterpreter = NewEVMInterpreter(env, env.Config)
)
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
if len(stack.data) != 1 {
Expand Down
1 change: 1 addition & 0 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
type Config struct {
Debug bool // Enables debugging
Tracer EVMLogger // Opcode logger
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages

JumpTable *JumpTable // EVM instruction table, automatically populated if unset
Expand Down
8 changes: 4 additions & 4 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1257,7 +1257,7 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno
return candidatesWithStakeInfo, nil
}

func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, vmCfg vm.Config, timeout time.Duration, globalGasCap uint64) ([]byte, uint64, bool, error, error) {
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) ([]byte, uint64, bool, error, error) {
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())

statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
Expand Down Expand Up @@ -1306,7 +1306,7 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
}

// Get a new instance of the EVM.
evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vmCfg)
evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vm.Config{NoBaseFee: true})
if err != nil {
return nil, 0, false, err, nil
}
Expand Down Expand Up @@ -1376,7 +1376,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl
if args.To != nil && *args.To == common.MasternodeVotingSMCBinary {
timeout = 0
}
result, _, failed, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, vm.Config{}, timeout, s.b.RPCGasCap())
result, _, failed, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, timeout, s.b.RPCGasCap())
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1432,7 +1432,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
executable := func(gas uint64) (bool, []byte, error, error) {
args.Gas = (*hexutil.Uint64)(&gas)

res, _, failed, err, vmErr := DoCall(ctx, b, args, blockNrOrHash, nil, vm.Config{}, 0, gasCap)
res, _, failed, err, vmErr := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap)
if err != nil {
if errors.Is(err, vm.ErrOutOfGas) || errors.Is(err, core.ErrIntrinsicGas) {
return false, nil, nil, nil // Special case, raise gas limit
Expand Down
12 changes: 10 additions & 2 deletions internal/ethapi/transaction_args.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,9 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
return nil
}

// ToMessage converts TransactionArgs to the Message type used by the core evm
// ToMessage converts th transaction arguments to the Message type used by the
// core evm. This method is used in calls and traces that do not require a real
// live transaction.
func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap uint64, baseFee *big.Int) (types.Message, error) {
// Reject invalid combinations of pre- and post-1559 fee styles
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
Expand Down Expand Up @@ -207,9 +209,11 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap
} else {
// A basefee is provided, necessitating 1559-type execution
if args.GasPrice != nil {
// User specified the legacy gas field, convert to 1559 gas typing
gasPrice = args.GasPrice.ToInt()
gasFeeCap, gasTipCap = gasPrice, gasPrice
} else {
// User specified 1559 gas feilds (or none), use those
gasFeeCap = new(big.Int)
if args.MaxFeePerGas != nil {
gasFeeCap = args.MaxFeePerGas.ToInt()
Expand All @@ -218,7 +222,11 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap
if args.MaxPriorityFeePerGas != nil {
gasTipCap = args.MaxPriorityFeePerGas.ToInt()
}
gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFee), gasFeeCap)
// Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
gasPrice = new(big.Int)
if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 {
gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFee), gasFeeCap)
}
}
}
value := new(big.Int)
Expand Down
2 changes: 1 addition & 1 deletion internal/jsre/deps/bindata.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion internal/jsre/deps/web3.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit aecb464

Please sign in to comment.