diff --git a/core/vm/evm.go b/core/vm/evm.go index baa349422504..d8ca6d911c4e 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -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) @@ -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) @@ -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 } @@ -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 } @@ -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 } @@ -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 } @@ -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) } @@ -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) diff --git a/core/vm/opcode_hooks.go b/core/vm/opcode_hooks.go new file mode 100644 index 000000000000..afb8b2cd9e39 --- /dev/null +++ b/core/vm/opcode_hooks.go @@ -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 . + +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() +}