From 70b3dea7b2f0b4f9e30daf8a98794baae3135127 Mon Sep 17 00:00:00 2001 From: Dori Medini Date: Sat, 19 Oct 2024 19:42:15 +0300 Subject: [PATCH] feat(blockifier): structured cairo1 stack trace --- crates/blockifier/src/execution/errors.rs | 3 +- .../blockifier/src/execution/stack_trace.rs | 78 ++++++++++++++----- crates/blockifier/src/transaction/errors.rs | 4 +- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/crates/blockifier/src/execution/errors.rs b/crates/blockifier/src/execution/errors.rs index ec4aa81eada..55aead5b93a 100644 --- a/crates/blockifier/src/execution/errors.rs +++ b/crates/blockifier/src/execution/errors.rs @@ -13,6 +13,7 @@ use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector}; use thiserror::Error; use crate::execution::entry_point::ConstructorContext; +use crate::execution::stack_trace::Cairo1RevertStack; use crate::state::errors::StateError; // TODO(AlonH, 21/12/2022): Implement Display for all types that appear in errors. @@ -80,7 +81,7 @@ pub enum EntryPointExecutionError { #[error(transparent)] CairoRunError(#[from] CairoRunError), #[error("Execution failed. Failure reason:\n{error_trace}.")] - ExecutionFailed { error_trace: String }, + ExecutionFailed { error_trace: Cairo1RevertStack }, #[error("Internal error: {0}")] InternalError(String), #[error("Invalid input: {input_descriptor}; {info}")] diff --git a/crates/blockifier/src/execution/stack_trace.rs b/crates/blockifier/src/execution/stack_trace.rs index 9012c015180..69d20380e76 100644 --- a/crates/blockifier/src/execution/stack_trace.rs +++ b/crates/blockifier/src/execution/stack_trace.rs @@ -1,3 +1,5 @@ +use std::fmt::{Display, Formatter}; + use cairo_vm::types::relocatable::Relocatable; use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; use cairo_vm::vm::errors::hint_errors::HintError; @@ -6,10 +8,10 @@ use itertools::Itertools; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector}; use starknet_api::execution_utils::format_panic_data; -use super::deprecated_syscalls::hint_processor::DeprecatedSyscallExecutionError; -use super::syscalls::hint_processor::SyscallExecutionError; -use crate::execution::call_info::CallInfo; +use crate::execution::call_info::{CallInfo, Retdata}; +use crate::execution::deprecated_syscalls::hint_processor::DeprecatedSyscallExecutionError; use crate::execution::errors::{ConstructorEntryPointExecutionError, EntryPointExecutionError}; +use crate::execution::syscalls::hint_processor::SyscallExecutionError; use crate::transaction::errors::TransactionExecutionError; #[cfg(test)] @@ -154,23 +156,59 @@ impl ErrorStack { } } -pub fn extract_trailing_cairo1_revert_trace(root_callinfo: &CallInfo) -> String { - root_callinfo - .tail_iter() - .map(|callinfo| { - format!( - "Error in contract (contract address: {:#064x}, class hash: {}, selector: \ - {:#064x}):\n{}", - callinfo.call.storage_address.0.key(), - match callinfo.call.class_hash { - Some(class_hash) => format!("{:#064x}", class_hash.0), - None => "_".to_string(), - }, - callinfo.call.entry_point_selector.0, - format_panic_data(&callinfo.execution.retdata.0) - ) - }) - .join("\n\n") +#[derive(Debug)] +pub struct Cairo1RevertFrame { + pub contract_address: ContractAddress, + pub class_hash: Option, + pub selector: EntryPointSelector, + pub retdata: Retdata, +} + +impl From<&CallInfo> for Cairo1RevertFrame { + fn from(callinfo: &CallInfo) -> Self { + Self { + contract_address: callinfo.call.storage_address, + class_hash: callinfo.call.class_hash, + selector: callinfo.call.entry_point_selector, + retdata: callinfo.execution.retdata.clone(), + } + } +} + +impl Display for Cairo1RevertFrame { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Error in contract (contract address: {:#064x}, class hash: {}, selector: \ + {:#064x}):\n{}", + self.contract_address.0.key(), + match self.class_hash { + Some(class_hash) => format!("{:#064x}", class_hash.0), + None => "_".to_string(), + }, + self.selector.0, + format_panic_data(&self.retdata.0) + ) + } +} + +#[derive(Debug)] +pub struct Cairo1RevertStack { + pub stack: Vec, +} + +impl Display for Cairo1RevertStack { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.stack.iter().map(|frame| frame.to_string()).collect::>().join("\n\n") + ) + } +} + +pub fn extract_trailing_cairo1_revert_trace(root_callinfo: &CallInfo) -> Cairo1RevertStack { + Cairo1RevertStack { stack: root_callinfo.tail_iter().map(Cairo1RevertFrame::from).collect() } } /// Extracts the error trace from a `TransactionExecutionError`. This is a top level function. diff --git a/crates/blockifier/src/transaction/errors.rs b/crates/blockifier/src/transaction/errors.rs index adcf00744f6..1905c78eac0 100644 --- a/crates/blockifier/src/transaction/errors.rs +++ b/crates/blockifier/src/transaction/errors.rs @@ -11,7 +11,7 @@ use thiserror::Error; use crate::bouncer::BouncerWeights; use crate::execution::call_info::Retdata; use crate::execution::errors::{ConstructorEntryPointExecutionError, EntryPointExecutionError}; -use crate::execution::stack_trace::gen_tx_execution_error_trace; +use crate::execution::stack_trace::{gen_tx_execution_error_trace, Cairo1RevertStack}; use crate::fee::fee_checks::FeeCheckError; use crate::state::errors::StateError; @@ -104,7 +104,7 @@ pub enum TransactionExecutionError { #[error(transparent)] FromStr(#[from] FromStrError), #[error("The `validate` entry point panicked with:\n{panic_reason}.")] - PanicInValidate { panic_reason: String }, + PanicInValidate { panic_reason: Cairo1RevertStack }, #[error("The `validate` entry point should return `VALID`. Got {actual:?}.")] InvalidValidateReturnData { actual: Retdata }, #[error(