Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(evm): OpCodesHooks for CREATE and CALL opcodes #28

Merged
merged 9 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 32 additions & 32 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,28 +97,26 @@
// 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
// // preExecuteCallback is a callback function that is called before executing
// // CALL, CALLCODE, DELEGATECALL and STATICCALL opcodes.
// preExecuteCallback preExecuteCallbackType
facs95 marked this conversation as resolved.
Show resolved Hide resolved

func dummyCallback(evm *EVM, addr common.Address) error {
return nil
// hooks is a set of hooks that can be used to intercept and modify the
// behavior of the EVM when executing certain 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(),

Check failure on line 119 in core/vm/evm.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

File is not `goimports`-ed (goimports)
}
// set the default precompiles
evm.activePrecompiles = DefaultActivePrecompiles(evm.chainRules)
Expand All @@ -128,17 +126,17 @@
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 +179,7 @@
// 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 +271,7 @@
// 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 +323,7 @@
// 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 +366,7 @@
// 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 +529,9 @@

// 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 +541,9 @@
// 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()
}
Loading