Skip to content

Commit

Permalink
core/vm: improve EVM instance reusability (#26341)
Browse files Browse the repository at this point in the history
This change improves reusability of the EVM struct. Two methods are added:

- SetBlockContext(...)
- SetTracer(...)

Other attributes like the TransactionContext and the StateDB can already be updated.
BlockContext and Tracer are partially not updateable right now. This change fixes it and
opens the potential to reuse an EVM struct in more ways.

Co-authored-by: Felix Lange <[email protected]>
  • Loading branch information
lmittmann and fjl authored Feb 5, 2023
1 parent 3a79a99 commit 877d217
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 69 deletions.
10 changes: 9 additions & 1 deletion core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -160,6 +160,14 @@ 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
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
// 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
Expand Down
6 changes: 3 additions & 3 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
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 @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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}
Expand Down Expand Up @@ -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)
Expand Down
110 changes: 51 additions & 59 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +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

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,
Expand All @@ -45,8 +42,8 @@ type ScopeContext struct {

// EVMInterpreter represents an EVM interpreter
type EVMInterpreter struct {
evm *EVM
cfg Config
evm *EVM
table *JumpTable

hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
Expand All @@ -56,53 +53,48 @@ 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.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
if len(cfg.ExtraEips) > 0 {
// Deep-copy jumptable to prevent modification of opcodes in other tables
cfg.JumpTable = copyJumpTable(cfg.JumpTable)
}
for _, eip := range cfg.ExtraEips {
if err := EnableEIP(eip, cfg.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
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
}

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
Expand Down Expand Up @@ -160,13 +152,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)
}
}
}()
Expand All @@ -176,14 +168,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.table[op]
cost = operation.constantGas // For tracing
// Validate stack
if sLen := stack.len(); sLen < operation.minStack {
Expand Down Expand Up @@ -221,15 +213,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
Expand Down

0 comments on commit 877d217

Please sign in to comment.