fix: invoke enter,exit only for non root calls (#5686)

This commit is contained in:
Matthias Seitz
2023-12-05 00:36:36 +01:00
committed by GitHub
parent f7b08d4ab7
commit 55f4c30210

View File

@ -40,8 +40,16 @@ pub struct JsInspector {
result_fn: JsObject,
fault_fn: JsObject,
/// EVM inspector hook functions
// EVM inspector hook functions
/// Invoked when the EVM enters a new call that is _NOT_ the top level call.
///
/// Corresponds to [Inspector::call] and [Inspector::create_end] but is also invoked on
/// [Inspector::selfdestruct].
enter_fn: Option<JsObject>,
/// Invoked when the EVM exits a call that is _NOT_ the top level call.
///
/// Corresponds to [Inspector::call_end] and [Inspector::create_end] but also invoked after
/// selfdestruct.
exit_fn: Option<JsObject>,
/// Executed before each instruction is executed.
step_fn: Option<JsObject>,
@ -62,6 +70,7 @@ impl JsInspector {
/// - `fault`: a function that will be called when the transaction fails.
///
/// Optional functions are invoked during inspection:
/// - `setup`: a function that will be called before the inspection starts.
/// - `enter`: a function that will be called when the execution enters a new call.
/// - `exit`: a function that will be called when the execution exits a call.
/// - `step`: a function that will be called when the execution steps to the next instruction.
@ -246,6 +255,29 @@ impl JsInspector {
self.call_stack.last().expect("call stack is empty")
}
#[inline]
fn pop_call(&mut self) {
self.call_stack.pop();
}
/// Returns true whether the active call is the root call.
#[inline]
fn is_root_call_active(&self) -> bool {
self.call_stack.len() == 1
}
/// Returns true if there's an enter function and the active call is not the root call.
#[inline]
fn can_call_enter(&self) -> bool {
self.enter_fn.is_some() && !self.is_root_call_active()
}
/// Returns true if there's an exit function and the active call is not the root call.
#[inline]
fn can_call_exit(&mut self) -> bool {
self.enter_fn.is_some() && !self.is_root_call_active()
}
/// Pushes a new call to the stack
fn push_call(
&mut self,
@ -265,10 +297,6 @@ impl JsInspector {
self.active_call()
}
fn pop_call(&mut self) {
self.call_stack.pop();
}
/// Registers the precompiles in the JS context
fn register_precompiles(&mut self, precompiles: &Precompiles) {
if !self.precompiles_registered {
@ -376,7 +404,7 @@ where
inputs.gas_limit,
);
if self.enter_fn.is_some() {
if self.can_call_enter() {
let call = self.active_call();
let frame = CallFrame {
contract: call.contract.clone(),
@ -399,7 +427,7 @@ where
ret: InstructionResult,
out: Bytes,
) -> (InstructionResult, Gas, Bytes) {
if self.exit_fn.is_some() {
if self.can_call_exit() {
let frame_result =
FrameResult { gas_used: remaining_gas.spend(), output: out.clone(), error: None };
if let Err(err) = self.try_exit(frame_result) {
@ -431,7 +459,7 @@ where
inputs.gas_limit,
);
if self.enter_fn.is_some() {
if self.can_call_enter() {
let call = self.active_call();
let frame =
CallFrame { contract: call.contract.clone(), kind: call.kind, gas: call.gas_limit };
@ -452,7 +480,7 @@ where
remaining_gas: Gas,
out: Bytes,
) -> (InstructionResult, Option<Address>, Gas, Bytes) {
if self.exit_fn.is_some() {
if self.can_call_exit() {
let frame_result =
FrameResult { gas_used: remaining_gas.spend(), output: out.clone(), error: None };
if let Err(err) = self.try_exit(frame_result) {
@ -466,12 +494,20 @@ where
}
fn selfdestruct(&mut self, _contract: Address, _target: Address, _value: U256) {
// This is exempt from the root call constraint, because selfdestruct is treated as a
// new scope that is entered and immediately exited.
if self.enter_fn.is_some() {
let call = self.active_call();
let frame =
CallFrame { contract: call.contract.clone(), kind: call.kind, gas: call.gas_limit };
let _ = self.try_enter(frame);
}
// exit with empty frame result ref <https://github.com/ethereum/go-ethereum/blob/0004c6b229b787281760b14fb9460ffd9c2496f1/core/vm/instructions.go#L829-L829>
if self.exit_fn.is_some() {
let frame_result = FrameResult { gas_used: 0, output: Bytes::new(), error: None };
let _ = self.try_exit(frame_result);
}
}
}