diff --git a/core/vm/evm.go b/core/vm/evm.go index 5e0bbd4dfaac..58845c3ef450 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -140,7 +140,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStat chainRules: chainConfig.Rules(blockCtx.BlockNumber), } - evm.interpreter = NewEVMInterpreter(evm, config) + evm.interpreter = NewEVMInterpreter(evm) return evm } @@ -167,6 +167,12 @@ 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) +} + // 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 8fe10a5ba484..7cab2f21cdfb 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -843,9 +843,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 017a089c7472..d7515c4f092a 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -196,7 +196,7 @@ func TestAddMod(t *testing.T) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) pc = uint64(0) ) tests := []struct { @@ -291,7 +291,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { var ( env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter @@ -526,7 +526,7 @@ func TestOpMstore(t *testing.T) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter @@ -552,7 +552,7 @@ func BenchmarkOpMstore(bench *testing.B) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter @@ -574,7 +574,7 @@ func BenchmarkOpKeccak256(bench *testing.B) { env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) env.interpreter = evmInterpreter mem.Resize(32) @@ -679,7 +679,7 @@ func TestRandom(t *testing.T) { env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) - evmInterpreter = NewEVMInterpreter(env, env.Config) + evmInterpreter = NewEVMInterpreter(env) ) opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) if len(stack.data) != 1 { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 669769e4cd54..e31eba29f09f 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -30,10 +30,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 - - JumpTable *JumpTable // EVM instruction table, automatically populated if unset - - 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, @@ -54,8 +51,8 @@ type keccakState interface { // EVMInterpreter represents an EVM interpreter type EVMInterpreter struct { - evm *EVM - cfg Config + evm *EVM + table *JumpTable hasher keccakState // Keccak256 hasher instance shared across opcodes hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes @@ -65,53 +62,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 { - switch { - case evm.chainRules.IsEIP1559: - cfg.JumpTable = &eip1559InstructionSet - case evm.chainRules.IsShanghai: - cfg.JumpTable = &shanghaiInstructionSet - case evm.chainRules.IsMerge: - cfg.JumpTable = &mergeInstructionSet - case evm.chainRules.IsLondon: - cfg.JumpTable = &londonInstructionSet - case evm.chainRules.IsBerlin: - cfg.JumpTable = &berlinInstructionSet - case evm.chainRules.IsIstanbul: - cfg.JumpTable = &istanbulInstructionSet - case evm.chainRules.IsConstantinople: - cfg.JumpTable = &constantinopleInstructionSet - case evm.chainRules.IsByzantium: - cfg.JumpTable = &byzantiumInstructionSet - case evm.chainRules.IsEIP158: - cfg.JumpTable = &spuriousDragonInstructionSet - case evm.chainRules.IsEIP150: - cfg.JumpTable = &tangerineWhistleInstructionSet - case evm.chainRules.IsHomestead: - cfg.JumpTable = &homesteadInstructionSet - default: - cfg.JumpTable = &frontierInstructionSet - } - var extraEips []int - for _, eip := range cfg.ExtraEips { - copy := *cfg.JumpTable - if err := EnableEIP(eip, ©); 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.JumpTable = © - } - cfg.ExtraEips = extraEips + var table *JumpTable + switch { + case evm.chainRules.IsEIP1559: + table = &eip1559InstructionSet + 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 } - - return &EVMInterpreter{ - evm: evm, - cfg: cfg, + 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, table: table} } // Run loops and evaluates the contract's code with the given input data and returns @@ -170,13 +164,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(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + in.evm.Config.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) } else { - in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) + in.evm.Config.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) } } }() @@ -186,7 +180,7 @@ 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 } @@ -194,7 +188,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.cfg.JumpTable[op] + operation := in.table[op] // Validate stack if sLen := stack.len(); sLen < operation.minStack { return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack} @@ -238,8 +232,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( mem.Resize(memorySize) } - if in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + if in.evm.Config.Debug { + in.evm.Config.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index f50b95f27ba4..86fe9fa8ddad 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -1039,3 +1039,14 @@ func newFrontierInstructionSet() JumpTable { return tbl } + +func copyJumpTable(source *JumpTable) *JumpTable { + dest := *source + for i, op := range source { + if op != nil { + opCopy := *op + dest[i] = &opCopy + } + } + return &dest +}