From f2b27fae60f68093f804ec26e4899263f8ea0406 Mon Sep 17 00:00:00 2001 From: lmittmann Date: Sat, 10 Dec 2022 10:49:59 +0100 Subject: [PATCH 1/5] improve EVM reusability --- core/vm/evm.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/core/vm/evm.go b/core/vm/evm.go index f6a1557e821..cbafe22ca40 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -160,6 +160,28 @@ func (evm *EVM) Interpreter() *EVMInterpreter { return evm.interpreter } +// SetBlockContext updates the block context of the EVM. +func (evm *EVM) SetBlockContext(blockCtx BlockContext) { + evm.Context = blockCtx + evm.chainRules = evm.chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil) +} + +// SetTracer updates the EVM tracer and enables debugging. If the tracer is nil +// any set tracer is removed and debugging is disabled. +func (evm *EVM) SetTracer(tracer EVMLogger) { + if tracer == nil { + evm.Config.Debug = false + evm.Config.Tracer = nil + evm.interpreter.cfg.Debug = false + evm.interpreter.cfg.Tracer = nil + } else { + evm.Config.Debug = true + evm.Config.Tracer = tracer + evm.interpreter.cfg.Debug = true + evm.interpreter.cfg.Tracer = tracer + } +} + // Call executes the contract associated with the addr with the given input as // parameters. It also handles any necessary value transfer required and takes // the necessary steps to create accounts and reverses the state in case of an From b58bf558a8a4dd467d744d27746f05f02b5b10de Mon Sep 17 00:00:00 2001 From: lmittmann Date: Wed, 14 Dec 2022 23:17:31 +0100 Subject: [PATCH 2/5] dropped `EVM.SetTracer` * dropped `vm.Config` copy from `EVMInterpreter`, using evm.Config instead --- core/vm/evm.go | 18 +---------- core/vm/instructions.go | 6 ++-- core/vm/instructions_test.go | 12 ++++---- core/vm/interpreter.go | 58 +++++++++++++++++------------------- 4 files changed, 37 insertions(+), 57 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index cbafe22ca40..ec5bfd0bea0 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -133,7 +133,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time), } - evm.interpreter = NewEVMInterpreter(evm, config) + evm.interpreter = NewEVMInterpreter(evm) return evm } @@ -166,22 +166,6 @@ func (evm *EVM) SetBlockContext(blockCtx BlockContext) { evm.chainRules = evm.chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil) } -// SetTracer updates the EVM tracer and enables debugging. If the tracer is nil -// any set tracer is removed and debugging is disabled. -func (evm *EVM) SetTracer(tracer EVMLogger) { - if tracer == nil { - evm.Config.Debug = false - evm.Config.Tracer = nil - evm.interpreter.cfg.Debug = false - evm.interpreter.cfg.Tracer = nil - } else { - evm.Config.Debug = true - evm.Config.Tracer = tracer - evm.interpreter.cfg.Debug = true - evm.interpreter.cfg.Tracer = tracer - } -} - // Call executes the contract associated with the addr with the given input as // parameters. It also handles any necessary value transfer required and takes // the necessary steps to create accounts and reverses the state in case of an diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 886ff6323d5..77b6e02bfcc 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -824,9 +824,9 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance) interpreter.evm.StateDB.Suicide(scope.Contract.Address()) - if interpreter.cfg.Debug { - interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) - interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil) + if interpreter.evm.Config.Debug { + interpreter.evm.Config.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + interpreter.evm.Config.Tracer.CaptureExit([]byte{}, 0, nil) } return nil, errStopToken } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index b4144a66fae..61f001a692c 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -203,7 +203,7 @@ func TestAddMod(t *testing.T) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) stack = newstack() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) pc = uint64(0) ) tests := []struct { @@ -293,7 +293,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) stack = newstack() scope = &ScopeContext{nil, stack, nil} - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter @@ -534,7 +534,7 @@ func TestOpMstore(t *testing.T) { env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter @@ -560,7 +560,7 @@ func BenchmarkOpMstore(bench *testing.B) { env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter @@ -583,7 +583,7 @@ func TestOpTstore(t *testing.T) { env = NewEVM(BlockContext{}, TxContext{}, statedb, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) caller = common.Address{} to = common.Address{1} contractRef = contractRef{caller} @@ -625,7 +625,7 @@ func BenchmarkOpKeccak256(bench *testing.B) { env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter mem.Resize(32) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 7b040aac9e1..17fee99dff1 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -46,7 +46,6 @@ type ScopeContext struct { // EVMInterpreter represents an EVM interpreter type EVMInterpreter struct { evm *EVM - cfg Config hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes @@ -56,53 +55,50 @@ type EVMInterpreter struct { } // NewEVMInterpreter returns a new instance of the Interpreter. -func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { +func NewEVMInterpreter(evm *EVM) *EVMInterpreter { // If jump table was not initialised we set the default one. - if cfg.JumpTable == nil { + if evm.Config.JumpTable == nil { switch { case evm.chainRules.IsShanghai: cfg.JumpTable = &shanghaiInstructionSet case evm.chainRules.IsMerge: - cfg.JumpTable = &mergeInstructionSet + evm.Config.JumpTable = &mergeInstructionSet case evm.chainRules.IsLondon: - cfg.JumpTable = &londonInstructionSet + evm.Config.JumpTable = &londonInstructionSet case evm.chainRules.IsBerlin: - cfg.JumpTable = &berlinInstructionSet + evm.Config.JumpTable = &berlinInstructionSet case evm.chainRules.IsIstanbul: - cfg.JumpTable = &istanbulInstructionSet + evm.Config.JumpTable = &istanbulInstructionSet case evm.chainRules.IsConstantinople: - cfg.JumpTable = &constantinopleInstructionSet + evm.Config.JumpTable = &constantinopleInstructionSet case evm.chainRules.IsByzantium: - cfg.JumpTable = &byzantiumInstructionSet + evm.Config.JumpTable = &byzantiumInstructionSet case evm.chainRules.IsEIP158: - cfg.JumpTable = &spuriousDragonInstructionSet + evm.Config.JumpTable = &spuriousDragonInstructionSet case evm.chainRules.IsEIP150: - cfg.JumpTable = &tangerineWhistleInstructionSet + evm.Config.JumpTable = &tangerineWhistleInstructionSet case evm.chainRules.IsHomestead: - cfg.JumpTable = &homesteadInstructionSet + evm.Config.JumpTable = &homesteadInstructionSet default: - cfg.JumpTable = &frontierInstructionSet + evm.Config.JumpTable = &frontierInstructionSet } var extraEips []int - if len(cfg.ExtraEips) > 0 { + if len(evm.Config.ExtraEips) > 0 { // Deep-copy jumptable to prevent modification of opcodes in other tables - cfg.JumpTable = copyJumpTable(cfg.JumpTable) + evm.Config.JumpTable = copyJumpTable(evm.Config.JumpTable) } - for _, eip := range cfg.ExtraEips { - if err := EnableEIP(eip, cfg.JumpTable); err != nil { + for _, eip := range evm.Config.ExtraEips { + if err := EnableEIP(eip, evm.Config.JumpTable); err != nil { // Disable it, so caller can check if it's activated or not log.Error("EIP activation failed", "eip", eip, "error", err) } else { extraEips = append(extraEips, eip) } } - cfg.ExtraEips = extraEips + evm.Config.ExtraEips = extraEips } - return &EVMInterpreter{ - evm: evm, - cfg: cfg, - } + return &EVMInterpreter{evm: evm} } // Run loops and evaluates the contract's code with the given input data and returns @@ -160,13 +156,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( }() contract.Input = input - if in.cfg.Debug { + if in.evm.Config.Debug { defer func() { if err != nil { if !logged { - in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + in.evm.Config.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) } else { - in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) + in.evm.Config.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) } } }() @@ -176,14 +172,14 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // the execution of one of the operations or until the done flag is set by the // parent context. for { - if in.cfg.Debug { + if in.evm.Config.Debug { // Capture pre-execution values for tracing. logged, pcCopy, gasCopy = false, pc, contract.Gas } // Get the operation from the jump table and validate the stack to ensure there are // enough stack items available to perform the operation. op = contract.GetOp(pc) - operation := in.cfg.JumpTable[op] + operation := in.evm.Config.JumpTable[op] cost = operation.constantGas // For tracing // Validate stack if sLen := stack.len(); sLen < operation.minStack { @@ -221,15 +217,15 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( return nil, ErrOutOfGas } // Do tracing before memory expansion - if in.cfg.Debug { - in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + if in.evm.Config.Debug { + in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } if memorySize > 0 { mem.Resize(memorySize) } - } else if in.cfg.Debug { - in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + } else if in.evm.Config.Debug { + in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } // execute the operation From a7988b8797184c9ffe7013fa381f8c597d7a8412 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sat, 4 Feb 2023 23:02:52 +0100 Subject: [PATCH 3/5] core/vm: update SetBlockContext for Shanghai Need to pass the timestamp as well because Shanghai is a time-activated fork. --- core/vm/evm.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index ec5bfd0bea0..bc7b0ab964f 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -163,7 +163,9 @@ func (evm *EVM) Interpreter() *EVMInterpreter { // SetBlockContext updates the block context of the EVM. func (evm *EVM) SetBlockContext(blockCtx BlockContext) { evm.Context = blockCtx - evm.chainRules = evm.chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil) + num := blockCtx.BlockNumber + timestamp := blockCtx.Time + evm.chainRules = evm.chainConfig.Rules(num, blockCtx.Random != nil, timestamp) } // Call executes the contract associated with the addr with the given input as From acfaba300df81899634f477188c9cceac18dd2d9 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sat, 4 Feb 2023 23:03:50 +0100 Subject: [PATCH 4/5] core/vm: move jumptable into EVMInterpreter --- core/vm/interpreter.go | 85 ++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 17fee99dff1..04e48fd9456 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -30,8 +30,6 @@ type Config struct { 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 - ExtraEips []int // Additional EIPS that are to be enabled } @@ -45,7 +43,8 @@ type ScopeContext struct { // EVMInterpreter represents an EVM interpreter type EVMInterpreter struct { - evm *EVM + evm *EVM + table *JumpTable hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes @@ -57,48 +56,46 @@ type EVMInterpreter struct { // NewEVMInterpreter returns a new instance of the Interpreter. func NewEVMInterpreter(evm *EVM) *EVMInterpreter { // If jump table was not initialised we set the default one. - if evm.Config.JumpTable == nil { - switch { - case evm.chainRules.IsShanghai: - cfg.JumpTable = &shanghaiInstructionSet - case evm.chainRules.IsMerge: - evm.Config.JumpTable = &mergeInstructionSet - case evm.chainRules.IsLondon: - evm.Config.JumpTable = &londonInstructionSet - case evm.chainRules.IsBerlin: - evm.Config.JumpTable = &berlinInstructionSet - case evm.chainRules.IsIstanbul: - evm.Config.JumpTable = &istanbulInstructionSet - case evm.chainRules.IsConstantinople: - evm.Config.JumpTable = &constantinopleInstructionSet - case evm.chainRules.IsByzantium: - evm.Config.JumpTable = &byzantiumInstructionSet - case evm.chainRules.IsEIP158: - evm.Config.JumpTable = &spuriousDragonInstructionSet - case evm.chainRules.IsEIP150: - evm.Config.JumpTable = &tangerineWhistleInstructionSet - case evm.chainRules.IsHomestead: - evm.Config.JumpTable = &homesteadInstructionSet - default: - evm.Config.JumpTable = &frontierInstructionSet - } - var extraEips []int - if len(evm.Config.ExtraEips) > 0 { - // Deep-copy jumptable to prevent modification of opcodes in other tables - evm.Config.JumpTable = copyJumpTable(evm.Config.JumpTable) - } - for _, eip := range evm.Config.ExtraEips { - if err := EnableEIP(eip, evm.Config.JumpTable); err != nil { - // Disable it, so caller can check if it's activated or not - log.Error("EIP activation failed", "eip", eip, "error", err) - } else { - extraEips = append(extraEips, eip) - } + var table *JumpTable + switch { + case evm.chainRules.IsShanghai: + table = &shanghaiInstructionSet + case evm.chainRules.IsMerge: + table = &mergeInstructionSet + case evm.chainRules.IsLondon: + table = &londonInstructionSet + case evm.chainRules.IsBerlin: + table = &berlinInstructionSet + case evm.chainRules.IsIstanbul: + table = &istanbulInstructionSet + case evm.chainRules.IsConstantinople: + table = &constantinopleInstructionSet + case evm.chainRules.IsByzantium: + table = &byzantiumInstructionSet + case evm.chainRules.IsEIP158: + table = &spuriousDragonInstructionSet + case evm.chainRules.IsEIP150: + table = &tangerineWhistleInstructionSet + case evm.chainRules.IsHomestead: + table = &homesteadInstructionSet + default: + table = &frontierInstructionSet + } + var extraEips []int + if len(evm.Config.ExtraEips) > 0 { + // Deep-copy jumptable to prevent modification of opcodes in other tables + table = copyJumpTable(table) + } + for _, eip := range evm.Config.ExtraEips { + if err := EnableEIP(eip, table); err != nil { + // Disable it, so caller can check if it's activated or not + log.Error("EIP activation failed", "eip", eip, "error", err) + } else { + extraEips = append(extraEips, eip) } - evm.Config.ExtraEips = extraEips } - - return &EVMInterpreter{evm: evm} + evm.Config.ExtraEips = extraEips + return &EVMInterpreter{evm: evm, table: table} } // Run loops and evaluates the contract's code with the given input data and returns @@ -179,7 +176,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // Get the operation from the jump table and validate the stack to ensure there are // enough stack items available to perform the operation. op = contract.GetOp(pc) - operation := in.evm.Config.JumpTable[op] + operation := in.table[op] cost = operation.constantGas // For tracing // Validate stack if sLen := stack.len(); sLen < operation.minStack { From db4bda9386186d9ce8b0f970fdd59a288808c299 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sat, 4 Feb 2023 23:10:08 +0100 Subject: [PATCH 5/5] core/vm: remove blank line --- core/vm/interpreter.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 04e48fd9456..0ab520b90f0 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -29,8 +29,7 @@ type Config struct { 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 - - ExtraEips []int // Additional EIPS that are to be enabled + ExtraEips []int // Additional EIPS that are to be enabled } // ScopeContext contains the things that are per-call, such as stack and memory,