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
Changes from 4 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
88 changes: 58 additions & 30 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,28 +97,54 @@ 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
// // 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

// 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
}

type preExecuteCallbackType func(evm *EVM, addr common.Address) error
// 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 struct {
// CallHook is called before executing a CALL, CALLCODE, DELEGATECALL and STATICCALL opcodes.
CallHook func(evm *EVM, recipient common.Address) error
// CreateHook is called before executing a CREATE and CREATE2 opcodes.
CreateHook func(evm *EVM, caller common.Address) error
}

func dummyCallback(evm *EVM, addr common.Address) error {
return nil
func newNoopOpCodeHooks() opCodeHooks {
return opCodeHooks{
CallHook: func(evm *EVM, recipient common.Address) error { return nil },
CreateHook: func(evm *EVM, caller common.Address) error { return nil },
}
}

func NewDefaultOpCodeHooks() opCodeHooks {
return newNoopOpCodeHooks()
}

// type preExecuteCallbackType func(evm *EVM, addr common.Address) error
//
// func dummyCallback(evm *EVM, addr common.Address) error {
// return nil
// }

// 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),
// preExecuteCallback: dummyCallback,
facs95 marked this conversation as resolved.
Show resolved Hide resolved
hooks: newNoopOpCodeHooks(),
}
// set the default precompiles
evm.activePrecompiles = DefaultActivePrecompiles(evm.chainRules)
Expand All @@ -128,17 +154,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 preExecuteCallback. The returned EVM is
facs95 marked this conversation as resolved.
Show resolved Hide resolved
// 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 +207,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, addr); err != nil {
return nil, gas, err
}

Expand Down Expand Up @@ -274,8 +299,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, addr); err != nil {
return nil, gas, err
}

Expand Down Expand Up @@ -327,8 +351,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, addr); err != nil {
return nil, gas, err
}

Expand Down Expand Up @@ -371,8 +394,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, addr); err != nil {
return nil, gas, err
}

Expand Down Expand Up @@ -535,6 +557,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 +569,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
Loading