Skip to content

Commit

Permalink
Fix updating nested traces and add missing docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Xanewok committed Jun 24, 2024
1 parent 5650e2d commit 86d6014
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 171 deletions.
1 change: 1 addition & 0 deletions crates/edr_napi/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ export class Exit {
getReason(): string
}
export type VMTracer = VmTracer
/** N-API bindings for the Rust port of `VMTracer` from Hardhat. */
export class VmTracer {
constructor()
/** Observes a trace, collecting information about the execution of the EVM. */
Expand Down
56 changes: 29 additions & 27 deletions crates/edr_napi/src/trace/message_trace.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
//! Bridging type for the `MessageTrace` interface in Hardhat.
//! Bridging type for the existing `MessageTrace` interface in Hardhat.

use napi::{
bindgen_prelude::{BigInt, Either3, Either4, Uint8Array, Undefined},
Either,
};

use napi_derive::napi;
use serde_json::Value;

Expand Down Expand Up @@ -70,7 +69,32 @@ pub struct CallMessageTrace {
pub code_address: Uint8Array,
}

/// Converts the Rust representation of a `MessageTrace` to the N-API representation.
/// Converts [`edr_solidity::message_trace::MessageTraceStep`] to the N-API
/// representation.
///
/// # Panics
/// This function will panic if the value is mutably borrowed.
pub fn message_trace_step_to_napi(
value: edr_solidity::message_trace::MessageTraceStep,
) -> Either4<EvmStep, PrecompileMessageTrace, CreateMessageTrace, CallMessageTrace> {
match value {
edr_solidity::message_trace::MessageTraceStep::Evm(step) => {
Either4::A(EvmStep { pc: step.pc as u32 })
}
edr_solidity::message_trace::MessageTraceStep::Message(msg) => {
// Immediately drop the borrow lock as it may be
let owned = msg.borrow().clone();
match message_trace_to_napi(owned) {
Either3::A(precompile) => Either4::B(precompile),
Either3::B(create) => Either4::C(create),
Either3::C(call) => Either4::D(call),
}
}
}
}

/// Converts the Rust representation of a `MessageTrace` to the N-API
/// representation.
pub fn message_trace_to_napi(
value: edr_solidity::message_trace::MessageTrace,
) -> Either3<PrecompileMessageTrace, CreateMessageTrace, CallMessageTrace> {
Expand Down Expand Up @@ -105,18 +129,7 @@ pub fn message_trace_to_napi(
.base
.steps
.into_iter()
.map(|step| match step {
edr_solidity::message_trace::MessageTraceStep::Evm(step) => {
Either4::A(EvmStep { pc: step.pc as u32 })
}
edr_solidity::message_trace::MessageTraceStep::Message(msg) => {
match message_trace_to_napi(msg) {
Either3::A(precompile) => Either4::B(precompile),
Either3::B(create) => Either4::C(create),
Either3::C(call) => Either4::D(call),
}
}
})
.map(message_trace_step_to_napi)
.collect(),
// NOTE: We specifically use None as that will be later filled on the JS side
bytecode: None,
Expand All @@ -142,18 +155,7 @@ pub fn message_trace_to_napi(
.base
.steps
.into_iter()
.map(|step| match step {
edr_solidity::message_trace::MessageTraceStep::Evm(step) => {
Either4::A(EvmStep { pc: step.pc as u32 })
}
edr_solidity::message_trace::MessageTraceStep::Message(msg) => {
match message_trace_to_napi(msg) {
Either3::A(precompile) => Either4::B(precompile),
Either3::B(create) => Either4::C(create),
Either3::C(call) => Either4::D(call),
}
}
})
.map(message_trace_step_to_napi)
.collect(),
// NOTE: We specifically use None as that will be later filled on the JS side
bytecode: None,
Expand Down
41 changes: 19 additions & 22 deletions crates/edr_napi/src/trace/vm_tracer.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,49 @@
//! N-API bindings for the Rust port of `VMTracer` from Hardhat.

use napi::{
bindgen_prelude::{Either3, Either4, Undefined},
Either, JsError,
};
use napi_derive::napi;

use crate::trace::message_trace::{
message_trace_to_napi, CallMessageTrace, CreateMessageTrace, PrecompileMessageTrace,
use crate::trace::{
message_trace::{
message_trace_to_napi, CallMessageTrace, CreateMessageTrace, PrecompileMessageTrace,
},
RawTrace,
};
use crate::trace::RawTrace;

/// N-API bindings for the Rust port of `VMTracer` from Hardhat.
#[napi]
pub struct VMTracer(edr_solidity::vm_tracer::VMTracer);
pub struct VMTracer(edr_solidity::vm_tracer::VmTracer);

#[napi]
impl VMTracer {
#[napi(constructor)]
pub fn new() -> napi::Result<Self> {
Ok(Self(edr_solidity::vm_tracer::VMTracer::new()))
Ok(Self(edr_solidity::vm_tracer::VmTracer::new()))
}

/// Observes a trace, collecting information about the execution of the EVM.
#[napi]
pub fn observe(&mut self, trace: &RawTrace) {
for msg in &trace.inner.messages {
match msg.clone() {
edr_evm::trace::TraceMessage::Before(before) => {
self.0.add_before_message(before);
}
edr_evm::trace::TraceMessage::Step(step) => {
self.0.add_step(step);
}
edr_evm::trace::TraceMessage::After(after) => {
self.0.add_after_message(after.execution_result);
}
}
}
self.0.observe(&trace.inner);
}

// Explicitly return undefined as `Option<T>` by default returns `null` in JS
// and the null/undefined checks we use there are strict
// and the null/undefined checks we use in JS are strict
#[napi]
pub fn get_last_top_level_message_trace(
&self,
) -> Either4<PrecompileMessageTrace, CreateMessageTrace, CallMessageTrace, Undefined> {
match self
.0
.get_last_top_level_message_trace()
.cloned()
.get_last_top_level_message_trace_ref()
.map(|x| {
x.try_borrow()
.expect("cannot be executed concurrently with `VMTracer::observe`")
.clone()
})
.map(message_trace_to_napi)
{
Some(Either3::A(precompile)) => Either4::A(precompile),
Expand All @@ -57,7 +54,7 @@ impl VMTracer {
}

// Explicitly return undefined as `Option<T>` by default returns `null` in JS
// and the null/undefined checks we use there are strict
// and the null/undefined checks we use in JS are strict
#[napi]
pub fn get_last_error(&self) -> Either<JsError, Undefined> {
match self.0.get_last_error() {
Expand Down
28 changes: 15 additions & 13 deletions crates/edr_solidity/src/exit.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
//! Naive rewrite of `hardhat-network/provider/vm/exit.ts` from Hardhat.
//! Used together with `VMTracer`.
//! Used together with `VmTracer`.

use std::fmt;

use edr_evm::HaltReason;
use edr_evm::SuccessReason;

#[derive(Clone, Copy)]
/// Represents the exit code of the EVM. Naive Rust port of the `ExitCode` from
/// Hardhat.
#[derive(Clone, Copy, Debug)]
pub enum ExitCode {
/// Execution was successful.
Success = 0,
/// Execution was reverted.
Revert,
/// Execution ran out of gas.
OutOfGas,
/// Execution encountered an internal error.
InternalError,
/// Execution encountered an invalid opcode.
InvalidOpcode,
/// Execution encountered a stack underflow.
StackUnderflow,
/// Create init code size exceeds limit (runtime).
CodesizeExceedsMaximum,
/// Create collision.
CreateCollision,
/// Static state change.
StaticStateChange,
}

Expand All @@ -39,6 +49,7 @@ impl TryFrom<u8> for ExitCode {
}

impl ExitCode {
/// Whether the exit code represents an error.
pub fn is_error(&self) -> bool {
!matches!(self, ExitCode::Success)
}
Expand All @@ -60,16 +71,7 @@ impl fmt::Display for ExitCode {
}
}

impl From<SuccessReason> for ExitCode {
fn from(reason: SuccessReason) -> Self {
match reason {
SuccessReason::Stop => Self::Success,
SuccessReason::Return => Self::Success,
SuccessReason::SelfDestruct => Self::Success,
}
}
}

#[allow(clippy::fallible_impl_from)] // naively ported for now
impl From<HaltReason> for ExitCode {
fn from(halt: HaltReason) -> Self {
match halt {
Expand Down
2 changes: 1 addition & 1 deletion crates/edr_solidity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ pub mod contracts_identifier;

mod opcodes;

pub mod message_trace;
pub mod exit;
pub mod message_trace;
pub mod vm_tracer;
Loading

0 comments on commit 86d6014

Please sign in to comment.