Skip to content

Commit

Permalink
Add backtrace to forge (#2679)
Browse files Browse the repository at this point in the history
<!-- Reference any GitHub issues resolved by this PR -->

Closes #2468

## Introduced changes

<!-- A brief description of the changes -->

- Forge will display backtrace for contracts in format similar to rust

## Checklist

<!-- Make sure all of these are complete -->

- [x] Linked relevant issue
- [x] Updated relevant documentation
- [x] Added relevant tests
- [x] Performed self-review of the code
- [x] Added changes to `CHANGELOG.md`
  • Loading branch information
ksew1 authored Nov 26, 2024
1 parent 6578e28 commit 2afd212
Show file tree
Hide file tree
Showing 17 changed files with 664 additions and 25 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ jobs:
- name: nextest partition ${{ matrix.partition }}/8
run: cargo nextest run --partition 'count:${{ matrix.partition }}/8' --archive-file 'nextest-archive.tar.zst' e2e

test-coverage:
name: Test coverage
test-scarb-2-8-3:
name: Test scarb 2.8.3
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -105,6 +105,7 @@ jobs:
curl -L https://raw.githubusercontent.com/software-mansion/cairo-coverage/main/scripts/install.sh | sh
- run: cargo test --package forge --features scarb_2_8_3 --test main e2e::coverage
- run: cargo test --package forge --features scarb_2_8_3 --test main e2e::backtrace

test-coverage-error:
name: Test coverage error
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{
ContractClassEntryPointExecutionResult, EntryPointExecutionErrorWithLastPc, GetLastPc,
OnErrorLastPc,
};
use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState;
use crate::runtime_extensions::cheatable_starknet_runtime_extension::CheatableStarknetRuntimeExtension;
use crate::runtime_extensions::common::get_relocated_vm_trace;
use blockifier::execution::call_info::CallInfo;
use blockifier::execution::deprecated_syscalls::hint_processor::SyscallCounter;
use blockifier::execution::entry_point_execution::{
finalize_execution, initialize_execution_context, prepare_call_arguments, VmExecutionContext,
};
use blockifier::{
execution::{
contract_class::{ContractClassV1, EntryPointV1},
entry_point::{CallEntryPoint, EntryPointExecutionContext, EntryPointExecutionResult},
entry_point::{CallEntryPoint, EntryPointExecutionContext},
errors::EntryPointExecutionError,
execution_utils::Args,
},
state::state_api::State,
};
use cairo_vm::vm::errors::cairo_run_errors::CairoRunError;
use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry;
use cairo_vm::{
hint_processor::hint_processor_definition::HintProcessor,
vm::runners::cairo_runner::{CairoArg, CairoRunner, ExecutionResources},
Expand All @@ -31,7 +32,7 @@ pub fn execute_entry_point_call_cairo1(
cheatnet_state: &mut CheatnetState, // Added parameter
resources: &mut ExecutionResources,
context: &mut EntryPointExecutionContext,
) -> EntryPointExecutionResult<(CallInfo, SyscallCounter, Option<Vec<RelocatedTraceEntry>>)> {
) -> ContractClassEntryPointExecutionResult {
let VmExecutionContext {
mut runner,
mut syscall_handler,
Expand Down Expand Up @@ -68,7 +69,8 @@ pub fn execute_entry_point_call_cairo1(
&entry_point,
&args,
program_extra_data_length,
)?;
)
.on_error_get_last_pc(&mut runner)?;

let vm_trace = if cheatable_runtime
.extension
Expand All @@ -86,6 +88,7 @@ pub fn execute_entry_point_call_cairo1(
.syscall_counter
.clone();

let last_pc = runner.get_last_pc();
let call_info = finalize_execution(
runner,
cheatable_runtime.extended_runtime.hint_handler,
Expand All @@ -94,8 +97,11 @@ pub fn execute_entry_point_call_cairo1(
program_extra_data_length,
)?;
if call_info.execution.failed {
return Err(EntryPointExecutionError::ExecutionFailed {
error_data: call_info.execution.retdata.0,
return Err(EntryPointExecutionErrorWithLastPc {
source: EntryPointExecutionError::ExecutionFailed {
error_data: call_info.execution.retdata.0,
},
last_pc,
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{
ContractClassEntryPointExecutionResult, OnErrorLastPc,
};
use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState;
use crate::runtime_extensions::deprecated_cheatable_starknet_extension::runtime::{
DeprecatedExtendedRuntime, DeprecatedStarknetRuntime,
};
use crate::runtime_extensions::deprecated_cheatable_starknet_extension::DeprecatedCheatableStarknetRuntimeExtension;
use blockifier::execution::call_info::CallInfo;
use blockifier::execution::contract_class::ContractClassV0;
use blockifier::execution::deprecated_entry_point_execution::{
finalize_execution, initialize_execution_context, prepare_call_arguments, VmExecutionContext,
};
use blockifier::execution::entry_point::{
CallEntryPoint, EntryPointExecutionContext, EntryPointExecutionResult,
};
use blockifier::execution::entry_point::{CallEntryPoint, EntryPointExecutionContext};
use blockifier::execution::errors::EntryPointExecutionError;
use blockifier::execution::execution_utils::Args;
use blockifier::execution::syscalls::hint_processor::SyscallCounter;
use blockifier::state::state_api::State;
use cairo_vm::hint_processor::hint_processor_definition::HintProcessor;
use cairo_vm::vm::runners::cairo_runner::{CairoArg, CairoRunner, ExecutionResources};
use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry;

// blockifier/src/execution/deprecated_execution.rs:36 (execute_entry_point_call)
pub fn execute_entry_point_call_cairo0(
Expand All @@ -27,7 +25,7 @@ pub fn execute_entry_point_call_cairo0(
cheatnet_state: &mut CheatnetState,
resources: &mut ExecutionResources,
context: &mut EntryPointExecutionContext,
) -> EntryPointExecutionResult<(CallInfo, SyscallCounter, Option<Vec<RelocatedTraceEntry>>)> {
) -> ContractClassEntryPointExecutionResult {
let VmExecutionContext {
mut runner,
mut syscall_handler,
Expand Down Expand Up @@ -61,7 +59,8 @@ pub fn execute_entry_point_call_cairo0(
&mut cheatable_syscall_handler,
entry_point_pc,
&args,
)?;
)
.on_error_get_last_pc(&mut runner)?;

let syscall_counter = cheatable_syscall_handler
.extended_runtime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::cell::RefCell;
use super::cairo1_execution::execute_entry_point_call_cairo1;
use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::deprecated::cairo0_execution::execute_entry_point_call_cairo0;
use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState;
use crate::state::{CallTrace, CallTraceNode, CheatStatus};
use crate::state::{CallTrace, CallTraceNode, CheatStatus, EncounteredError};
use blockifier::execution::call_info::{CallExecution, Retdata};
use blockifier::{
execution::{
Expand All @@ -17,7 +17,7 @@ use blockifier::{
},
state::state_api::State,
};
use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
use cairo_vm::vm::runners::cairo_runner::{CairoRunner, ExecutionResources};
use starknet_api::{
core::ClassHash,
deprecated_contract_class::EntryPointType,
Expand All @@ -28,11 +28,17 @@ use std::rc::Rc;
use blockifier::execution::deprecated_syscalls::hint_processor::SyscallCounter;
use starknet_types_core::felt::Felt;
use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry;
use thiserror::Error;
use conversions::FromConv;
use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::{AddressOrClassHash, CallResult};
use crate::runtime_extensions::common::sum_syscall_counters;
use conversions::string::TryFromHexStr;

pub(crate) type ContractClassEntryPointExecutionResult = Result<
(CallInfo, SyscallCounter, Option<Vec<RelocatedTraceEntry>>),
EntryPointExecutionErrorWithLastPc,
>;

// blockifier/src/execution/entry_point.rs:180 (CallEntryPoint::execute)
#[allow(clippy::too_many_lines)]
pub fn execute_call_entry_point(
Expand Down Expand Up @@ -151,7 +157,15 @@ pub fn execute_call_entry_point(
);
Ok(call_info)
}
Err(err) => {
Err(EntryPointExecutionErrorWithLastPc {
source: err,
last_pc: pc,
}) => {
if let Some(pc) = pc {
cheatnet_state
.encountered_errors
.push(EncounteredError { pc, class_hash });
}
exit_error_call(&err, cheatnet_state, resources, entry_point);
Err(err)
}
Expand Down Expand Up @@ -297,3 +311,61 @@ fn aggregate_syscall_counters(trace: &Rc<RefCell<CallTrace>>) -> SyscallCounter
}
result
}

#[derive(Debug, Error)]
#[error("{}", source)]
pub struct EntryPointExecutionErrorWithLastPc {
pub source: EntryPointExecutionError,
pub last_pc: Option<usize>,
}

impl<T> From<T> for EntryPointExecutionErrorWithLastPc
where
T: Into<EntryPointExecutionError>,
{
fn from(value: T) -> Self {
Self {
source: value.into(),
last_pc: None,
}
}
}

pub(crate) trait OnErrorLastPc<T>: Sized {
fn on_error_get_last_pc(
self,
runner: &mut CairoRunner,
) -> Result<T, EntryPointExecutionErrorWithLastPc>;
}

impl<T> OnErrorLastPc<T> for Result<T, EntryPointExecutionError> {
fn on_error_get_last_pc(
self,
runner: &mut CairoRunner,
) -> Result<T, EntryPointExecutionErrorWithLastPc> {
match self {
Err(source) => {
let last_pc = runner.get_last_pc();

Err(EntryPointExecutionErrorWithLastPc { source, last_pc })
}
Ok(value) => Ok(value),
}
}
}

pub trait GetLastPc {
fn get_last_pc(&mut self) -> Option<usize>;
}

impl GetLastPc for CairoRunner {
fn get_last_pc(&mut self) -> Option<usize> {
if self.relocated_trace.is_none() {
self.relocate(true).ok()?;
}
self.relocated_trace
.as_ref()
.and_then(|trace| trace.last())
.map(|entry| entry.pc)
}
}
8 changes: 8 additions & 0 deletions crates/cheatnet/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,12 @@ pub struct TraceData {
pub is_vm_trace_needed: bool,
}

#[derive(Clone)]
pub struct EncounteredError {
pub pc: usize,
pub class_hash: ClassHash,
}

pub struct CheatnetState {
pub cheated_execution_info_contracts: HashMap<ContractAddress, ExecutionInfoMock>,
pub global_cheated_execution_info: ExecutionInfoMock,
Expand All @@ -332,6 +338,7 @@ pub struct CheatnetState {
pub deploy_salt_base: u32,
pub block_info: BlockInfo,
pub trace_data: TraceData,
pub encountered_errors: Vec<EncounteredError>,
}

impl Default for CheatnetState {
Expand All @@ -357,6 +364,7 @@ impl Default for CheatnetState {
current_call_stack: NotEmptyCallStack::from(test_call),
is_vm_trace_needed: false,
},
encountered_errors: vec![],
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/forge-runner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ cairo-lang-sierra-type-size.workspace = true
cairo-lang-sierra-gas.workspace = true
cairo-lang-sierra-ap-change.workspace = true
cairo-lang-test-plugin.workspace = true
cairo-lang-starknet-classes.workspace = true
cairo-annotations.workspace = true
starknet-types-core.workspace = true
starknet_api.workspace = true
Expand Down
Loading

0 comments on commit 2afd212

Please sign in to comment.