Skip to content

Commit

Permalink
feat(evm): OpCodesHooks for CREATE and CALL opcodes (#28)
Browse files Browse the repository at this point in the history
Co-authored-by: Tom <54514587+GAtom22@users.noreply.github.com>
  • Loading branch information
facs95 and GAtom22 authored May 7, 2024
1 parent 8552773 commit 8a48273
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 33 deletions.
63 changes: 30 additions & 33 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,28 +97,23 @@ type EVM struct {
// activePrecompiles defines the precompiles that are currently active
activePrecompiles []common.Address

// preExecuteCallback is a callback function that is called before executing
// CALL, CALLCODE, DELEGATECALL and STATICCALL opcodes.
preExecuteCallback preExecuteCallbackType
}

type preExecuteCallbackType func(evm *EVM, addr common.Address) error

func dummyCallback(evm *EVM, addr common.Address) error {
return nil
// hooks is a set of functions that can be used to intercept and modify the
// behavior of the EVM when executing certain opcodes.
// The hooks are called before the execution of the respective opcodes.
hooks OpCodeHooks
}

// NewEVM returns a new EVM. The returned EVM is not thread safe and should
// only ever be used *once*.
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
evm := &EVM{
Context: blockCtx,
TxContext: txCtx,
StateDB: statedb,
Config: config,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil),
preExecuteCallback: dummyCallback,
Context: blockCtx,
TxContext: txCtx,
StateDB: statedb,
Config: config,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil),
hooks: newNoopOpCodeHooks(),
}
// set the default precompiles
evm.activePrecompiles = DefaultActivePrecompiles(evm.chainRules)
Expand All @@ -128,17 +123,17 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
return evm
}

// NewEVMWithCallback returns a new EVM and takes a custom preExecuteCallback. The returned EVM is
// NewEVMWithHooks returns a new EVM and takes a custom OpCodeHooks. The returned EVM is
// not thread safe and should only ever be used *once*.
func NewEVMWithCallback(callback preExecuteCallbackType, blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
func NewEVMWithHooks(hooks OpCodeHooks, blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
evm := &EVM{
Context: blockCtx,
TxContext: txCtx,
StateDB: statedb,
Config: config,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil),
preExecuteCallback: callback,
Context: blockCtx,
TxContext: txCtx,
StateDB: statedb,
Config: config,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil),
hooks: hooks,
}
// set the default precompiles
evm.activePrecompiles = DefaultActivePrecompiles(evm.chainRules)
Expand Down Expand Up @@ -181,8 +176,7 @@ func (evm *EVM) WithInterpreter(interpreter Interpreter) {
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
err = evm.preExecuteCallback(evm, addr)
if err != nil {
if err = evm.hooks.CallHook(evm, caller.Address(), addr); err != nil {
return nil, gas, err
}

Expand Down Expand Up @@ -274,8 +268,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context.
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
err = evm.preExecuteCallback(evm, addr)
if err != nil {
if err = evm.hooks.CallHook(evm, caller.Address(), addr); err != nil {
return nil, gas, err
}

Expand Down Expand Up @@ -327,8 +320,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// DelegateCall differs from CallCode in the sense that it executes the given address'
// code with the caller as context and the caller is set to the caller of the caller.
func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
err = evm.preExecuteCallback(evm, addr)
if err != nil {
if err = evm.hooks.CallHook(evm, caller.Address(), addr); err != nil {
return nil, gas, err
}

Expand Down Expand Up @@ -371,8 +363,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// Opcodes that attempt to perform such modifications will result in exceptions
// instead of performing the modifications.
func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
err = evm.preExecuteCallback(evm, addr)
if err != nil {
if err = evm.hooks.CallHook(evm, caller.Address(), addr); err != nil {
return nil, gas, err
}

Expand Down Expand Up @@ -535,6 +526,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,

// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
if err = evm.hooks.CreateHook(evm, caller.Address()); err != nil {
return nil, common.Address{}, gas, err
}
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}
Expand All @@ -544,6 +538,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
if err = evm.hooks.CreateHook(evm, caller.Address()); err != nil {
return nil, common.Address{}, gas, err
}
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
Expand Down
48 changes: 48 additions & 0 deletions core/vm/opcode_hooks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2014 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 vm

import "github.com/ethereum/go-ethereum/common"

// OpCodeHooks is a set of hooks that can be used to intercept and modify the
// behavior of the EVM when executing certain opcodes.
// The hooks are called before the execution of the respective opcodes.
type OpCodeHooks interface {
// CallHook is called before executing a CALL, CALLCODE, DELEGATECALL and STATICCALL opcodes.
CallHook(evm *EVM, caller common.Address, recipient common.Address) error
// CreateHook is called before executing a CREATE and CREATE2 opcodes.
CreateHook(evm *EVM, caller common.Address) error
}

type NoopOpCodeHooks struct {
}

func (NoopOpCodeHooks) CallHook(evm *EVM, caller common.Address, recipient common.Address) error {
return nil
}

func (NoopOpCodeHooks) CreateHook(evm *EVM, caller common.Address) error {
return nil
}

func newNoopOpCodeHooks() OpCodeHooks {
return NoopOpCodeHooks{}
}

func NewDefaultOpCodeHooks() OpCodeHooks {
return newNoopOpCodeHooks()
}

0 comments on commit 8a48273

Please sign in to comment.