diff --git a/crates/cheatnet/src/lib.rs b/crates/cheatnet/src/lib.rs index a453c37347..4abde6a33a 100644 --- a/crates/cheatnet/src/lib.rs +++ b/crates/cheatnet/src/lib.rs @@ -6,3 +6,4 @@ pub mod predeployment; pub mod runtime_extensions; pub mod state; pub mod sync_client; +pub mod trace_data; diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index 57cd064029..7759f1250b 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -17,7 +17,7 @@ use crate::runtime_extensions::{ storage::{calculate_variable_address, load, store}, }, }; -use crate::state::{CallTrace, CallTraceNode, GasReportData}; +use crate::trace_data::{CallTrace, CallTraceNode, GasReportData}; use anyhow::{Context, Result, anyhow}; use blockifier::bouncer::vm_resources_to_sierra_gas; use blockifier::context::TransactionContext; diff --git a/crates/cheatnet/src/state.rs b/crates/cheatnet/src/state.rs index 0586bba825..7b02489375 100644 --- a/crates/cheatnet/src/state.rs +++ b/crates/cheatnet/src/state.rs @@ -3,26 +3,17 @@ use crate::forking::state::ForkStateReader; use crate::predeployment::erc20::eth::eth_predeployed_contract; use crate::predeployment::erc20::strk::strk_predeployed_contract; use crate::predeployment::predeployed_contract::PredeployedContract; -use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::CallResult; -use crate::runtime_extensions::common::sum_syscall_usage; use crate::runtime_extensions::forge_runtime_extension::cheatcodes::cheat_execution_info::{ ExecutionInfoMock, ResourceBounds, }; use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_events::Event; use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_messages_to_l1::MessageToL1; -use blockifier::blockifier_versioned_constants::VersionedConstants; -use blockifier::execution::call_info::{ExecutionSummary, OrderedEvent, OrderedL2ToL1Message}; +use crate::trace_data::{CallTrace, NotEmptyCallStack, TraceData}; use blockifier::execution::contract_class::RunnableCompiledClass; -use blockifier::execution::entry_point::CallEntryPoint; -use blockifier::execution::syscalls::vm_syscall_utils::SyscallUsageMap; use blockifier::state::errors::StateError::UndeclaredClassHash; use blockifier::state::state_api::{StateReader, StateResult}; -use cairo_annotations::trace_data::L1Resources; use cairo_vm::Felt252; -use cairo_vm::vm::runners::cairo_runner::ExecutionResources; -use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry; use conversions::serde::deserialize::CairoDeserialize; -use conversions::serde::serialize::{BufferWriter, CairoSerialize}; use conversions::string::TryFromHexStr; use indexmap::IndexMap; use runtime::starknet::constants::TEST_CONTRACT_CLASS_HASH; @@ -30,15 +21,13 @@ use runtime::starknet::context::SerializableBlockInfo; use runtime::starknet::state::DictStateReader; use starknet_api::block::BlockInfo; use starknet_api::core::{ChainId, EntryPointSelector}; -use starknet_api::execution_resources::GasVector; use starknet_api::transaction::fields::ContractAddressSalt; -use starknet_api::transaction::fields::GasVectorComputationMode; use starknet_api::{ core::{ClassHash, CompiledClassHash, ContractAddress, Nonce}, state::StorageKey, }; use starknet_types_core::felt::Felt; -use std::cell::{OnceCell, Ref, RefCell}; +use std::cell::RefCell; use std::collections::HashMap; use std::num::NonZeroUsize; use std::rc::Rc; @@ -206,165 +195,6 @@ impl CheatStatus { } } -#[derive(Clone, Debug)] -pub struct GasReportData { - pub execution_summary: ExecutionSummary, - partial_gas_usage: OnceCell, -} - -impl GasReportData { - #[must_use] - pub fn new(execution_summary: ExecutionSummary) -> Self { - Self { - execution_summary, - partial_gas_usage: OnceCell::new(), - } - } - - pub fn get_gas(&self) -> &GasVector { - self.partial_gas_usage.get_or_init(|| { - self.execution_summary.clone().to_partial_gas_vector( - VersionedConstants::latest_constants(), - &GasVectorComputationMode::All, - ) - }) - } -} - -/// Tree structure representing trace of a call. -#[derive(Debug)] -pub struct CallTrace { - // only these are serialized - pub entry_point: CallEntryPoint, - pub nested_calls: Vec, - pub result: CallResult, - // serialize end - - // These also include resources used by internal calls - pub used_execution_resources: ExecutionResources, - pub used_l1_resources: L1Resources, - pub used_syscalls_vm_resources: SyscallUsageMap, - pub used_syscalls_sierra_gas: SyscallUsageMap, - pub vm_trace: Option>, - pub gas_consumed: u64, - pub events: Vec, - pub signature: Vec, - - // This is updated only once after the entire test execution. - pub gas_report_data: Option, -} - -impl CairoSerialize for CallTrace { - fn serialize(&self, output: &mut BufferWriter) { - self.entry_point.serialize(output); - - let visible_calls: Vec<_> = self - .nested_calls - .iter() - .filter_map(CallTraceNode::extract_entry_point_call) - .collect(); - - visible_calls.serialize(output); - - self.result.serialize(output); - } -} - -impl CallTrace { - fn default_successful_call() -> Self { - Self { - entry_point: CallEntryPoint::default(), - used_execution_resources: ExecutionResources::default(), - used_l1_resources: L1Resources::default(), - used_syscalls_vm_resources: SyscallUsageMap::default(), - used_syscalls_sierra_gas: SyscallUsageMap::default(), - nested_calls: vec![], - result: CallResult::Success { ret_data: vec![] }, - vm_trace: None, - gas_consumed: u64::default(), - events: vec![], - signature: vec![], - gas_report_data: None, - } - } - - #[must_use] - pub fn get_total_used_syscalls(&self) -> SyscallUsageMap { - sum_syscall_usage( - self.used_syscalls_vm_resources.clone(), - &self.used_syscalls_sierra_gas, - ) - } -} - -/// Enum representing node of a trace of a call. -#[derive(Clone, Debug)] -pub enum CallTraceNode { - EntryPointCall(Rc>), - DeployWithoutConstructor, -} - -impl CallTraceNode { - #[must_use] - pub fn extract_entry_point_call(&self) -> Option<&Rc>> { - if let CallTraceNode::EntryPointCall(trace) = self { - Some(trace) - } else { - None - } - } -} - -#[derive(Clone, Debug)] -struct CallStackElement { - call_trace: Rc>, - cheated_data: CheatedData, -} - -#[derive(Debug)] -pub struct NotEmptyCallStack(Vec); - -impl NotEmptyCallStack { - pub fn from(elem: Rc>) -> Self { - NotEmptyCallStack(vec![CallStackElement { - call_trace: elem, - cheated_data: CheatedData::default(), - }]) - } - - pub fn push(&mut self, elem: Rc>, cheated_data: CheatedData) { - self.0.push(CallStackElement { - call_trace: elem, - cheated_data, - }); - } - - pub fn top(&mut self) -> Rc> { - let top_val = self.0.last().unwrap(); - top_val.call_trace.clone() - } - - pub fn top_cheated_data(&mut self) -> CheatedData { - let top_val = self.0.last().unwrap(); - top_val.cheated_data.clone() - } - - fn pop(&mut self) -> CallStackElement { - assert!(self.0.len() > 1, "You cannot make NotEmptyCallStack empty"); - self.0.pop().unwrap() - } - - #[must_use] - pub fn size(&self) -> usize { - self.0.len() - } - - #[must_use] - pub fn borrow_full_trace(&self) -> Ref<'_, CallTrace> { - self.0.first().unwrap().call_trace.borrow() - } -} - #[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct CheatedTxInfo { pub version: Option, @@ -399,12 +229,6 @@ pub struct CheatedData { pub tx_info: CheatedTxInfo, } -#[derive(Debug)] -pub struct TraceData { - pub current_call_stack: NotEmptyCallStack, - pub is_vm_trace_needed: bool, -} - pub struct CheatnetState { pub cheated_execution_info_contracts: HashMap, pub global_cheated_execution_info: ExecutionInfoMock, @@ -577,88 +401,3 @@ impl CheatnetState { self.encountered_errors.shift_remove(&class_hash); } } - -impl TraceData { - pub fn enter_nested_call(&mut self, entry_point: CallEntryPoint, cheated_data: CheatedData) { - let new_call = Rc::new(RefCell::new(CallTrace { - entry_point, - ..CallTrace::default_successful_call() - })); - let current_call = self.current_call_stack.top(); - - current_call - .borrow_mut() - .nested_calls - .push(CallTraceNode::EntryPointCall(new_call.clone())); - - self.current_call_stack.push(new_call, cheated_data); - } - - pub fn set_class_hash_for_current_call(&mut self, class_hash: ClassHash) { - let current_call = self.current_call_stack.top(); - current_call.borrow_mut().entry_point.class_hash = Some(class_hash); - } - - pub fn set_vm_trace_for_current_call(&mut self, vm_trace: Vec) { - let current_call = self.current_call_stack.top(); - current_call.borrow_mut().vm_trace = Some(vm_trace); - } - - pub fn update_current_call_result(&mut self, result: CallResult) { - let current_call = self.current_call_stack.top(); - current_call.borrow_mut().result = result; - } - - pub fn clear_current_call_events_and_messages(&mut self) { - let current_call = self.current_call_stack.top(); - current_call.borrow_mut().events.clear(); - current_call - .borrow_mut() - .used_l1_resources - .l2_l1_message_sizes - .clear(); - } - - #[expect(clippy::too_many_arguments)] - pub fn update_current_call( - &mut self, - execution_resources: ExecutionResources, - gas_consumed: u64, - used_syscalls_vm_resources: SyscallUsageMap, - used_syscalls_sierra_gas: SyscallUsageMap, - result: CallResult, - l2_to_l1_messages: &[OrderedL2ToL1Message], - signature: Vec, - events: Vec, - ) { - let current_call = self.current_call_stack.top(); - let mut current_call = current_call.borrow_mut(); - - current_call.used_execution_resources = execution_resources; - current_call.gas_consumed = gas_consumed; - current_call.used_syscalls_vm_resources = used_syscalls_vm_resources; - current_call.used_syscalls_sierra_gas = used_syscalls_sierra_gas; - - current_call.used_l1_resources.l2_l1_message_sizes = l2_to_l1_messages - .iter() - .map(|ordered_message| ordered_message.message.payload.0.len()) - .collect(); - - current_call.result = result; - current_call.signature = signature; - current_call.events = events; - } - - pub fn exit_nested_call(&mut self) { - self.current_call_stack.pop(); - } - - pub fn add_deploy_without_constructor_node(&mut self) { - let current_call = self.current_call_stack.top(); - - current_call - .borrow_mut() - .nested_calls - .push(CallTraceNode::DeployWithoutConstructor); - } -} diff --git a/crates/cheatnet/src/trace_data.rs b/crates/cheatnet/src/trace_data.rs new file mode 100644 index 0000000000..4686482d8d --- /dev/null +++ b/crates/cheatnet/src/trace_data.rs @@ -0,0 +1,267 @@ +use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::CallResult; +use crate::runtime_extensions::common::sum_syscall_usage; +use crate::state::CheatedData; +use blockifier::blockifier_versioned_constants::VersionedConstants; +use blockifier::execution::call_info::{ExecutionSummary, OrderedEvent, OrderedL2ToL1Message}; +use blockifier::execution::entry_point::CallEntryPoint; +use blockifier::execution::syscalls::vm_syscall_utils::SyscallUsageMap; +use cairo_annotations::trace_data::L1Resources; +use cairo_vm::vm::runners::cairo_runner::ExecutionResources; +use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry; +use conversions::serde::serialize::{BufferWriter, CairoSerialize}; +use starknet_api::core::ClassHash; +use starknet_api::execution_resources::GasVector; +use starknet_api::transaction::fields::GasVectorComputationMode; +use starknet_types_core::felt::Felt; +use std::cell::{OnceCell, Ref, RefCell}; +use std::rc::Rc; + +#[derive(Debug)] +pub struct TraceData { + pub current_call_stack: NotEmptyCallStack, + pub is_vm_trace_needed: bool, +} + +#[derive(Debug)] +pub struct NotEmptyCallStack(Vec); + +#[derive(Clone, Debug)] +struct CallStackElement { + call_trace: Rc>, + cheated_data: CheatedData, +} + +/// Tree structure representing trace of a call. +#[derive(Debug)] +pub struct CallTrace { + // only these are serialized + pub entry_point: CallEntryPoint, + pub nested_calls: Vec, + pub result: CallResult, + // serialize end + + // These also include resources used by internal calls + pub used_execution_resources: ExecutionResources, + pub used_l1_resources: L1Resources, + pub used_syscalls_vm_resources: SyscallUsageMap, + pub used_syscalls_sierra_gas: SyscallUsageMap, + pub vm_trace: Option>, + pub gas_consumed: u64, + pub events: Vec, + pub signature: Vec, + + // This is updated only once after the entire test execution. + pub gas_report_data: Option, +} + +/// Enum representing a node of a trace of a call. +#[derive(Clone, Debug)] +pub enum CallTraceNode { + EntryPointCall(Rc>), + DeployWithoutConstructor, +} + +#[derive(Clone, Debug)] +pub struct GasReportData { + pub execution_summary: ExecutionSummary, + partial_gas_usage: OnceCell, +} + +impl TraceData { + pub fn enter_nested_call(&mut self, entry_point: CallEntryPoint, cheated_data: CheatedData) { + let new_call = Rc::new(RefCell::new(CallTrace { + entry_point, + ..CallTrace::default_successful_call() + })); + let current_call = self.current_call_stack.top(); + + current_call + .borrow_mut() + .nested_calls + .push(CallTraceNode::EntryPointCall(new_call.clone())); + + self.current_call_stack.push(new_call, cheated_data); + } + + pub fn set_class_hash_for_current_call(&mut self, class_hash: ClassHash) { + let current_call = self.current_call_stack.top(); + current_call.borrow_mut().entry_point.class_hash = Some(class_hash); + } + + pub fn set_vm_trace_for_current_call(&mut self, vm_trace: Vec) { + let current_call = self.current_call_stack.top(); + current_call.borrow_mut().vm_trace = Some(vm_trace); + } + + pub fn update_current_call_result(&mut self, result: CallResult) { + let current_call = self.current_call_stack.top(); + current_call.borrow_mut().result = result; + } + + pub fn clear_current_call_events_and_messages(&mut self) { + let current_call = self.current_call_stack.top(); + current_call.borrow_mut().events.clear(); + current_call + .borrow_mut() + .used_l1_resources + .l2_l1_message_sizes + .clear(); + } + + #[expect(clippy::too_many_arguments)] + pub fn update_current_call( + &mut self, + execution_resources: ExecutionResources, + gas_consumed: u64, + used_syscalls_vm_resources: SyscallUsageMap, + used_syscalls_sierra_gas: SyscallUsageMap, + result: CallResult, + l2_to_l1_messages: &[OrderedL2ToL1Message], + signature: Vec, + events: Vec, + ) { + let current_call = self.current_call_stack.top(); + let mut current_call = current_call.borrow_mut(); + + current_call.used_execution_resources = execution_resources; + current_call.gas_consumed = gas_consumed; + current_call.used_syscalls_vm_resources = used_syscalls_vm_resources; + current_call.used_syscalls_sierra_gas = used_syscalls_sierra_gas; + + current_call.used_l1_resources.l2_l1_message_sizes = l2_to_l1_messages + .iter() + .map(|ordered_message| ordered_message.message.payload.0.len()) + .collect(); + + current_call.result = result; + current_call.signature = signature; + current_call.events = events; + } + + pub fn exit_nested_call(&mut self) { + self.current_call_stack.pop(); + } + + pub fn add_deploy_without_constructor_node(&mut self) { + let current_call = self.current_call_stack.top(); + + current_call + .borrow_mut() + .nested_calls + .push(CallTraceNode::DeployWithoutConstructor); + } +} + +impl NotEmptyCallStack { + pub fn from(elem: Rc>) -> Self { + NotEmptyCallStack(vec![CallStackElement { + call_trace: elem, + cheated_data: CheatedData::default(), + }]) + } + + pub fn push(&mut self, elem: Rc>, cheated_data: CheatedData) { + self.0.push(CallStackElement { + call_trace: elem, + cheated_data, + }); + } + + pub fn top(&mut self) -> Rc> { + let top_val = self.0.last().unwrap(); + top_val.call_trace.clone() + } + + pub fn top_cheated_data(&mut self) -> CheatedData { + let top_val = self.0.last().unwrap(); + top_val.cheated_data.clone() + } + + fn pop(&mut self) -> CallStackElement { + assert!(self.0.len() > 1, "You cannot make NotEmptyCallStack empty"); + self.0.pop().unwrap() + } + + #[must_use] + pub fn size(&self) -> usize { + self.0.len() + } + + #[must_use] + pub fn borrow_full_trace(&self) -> Ref<'_, CallTrace> { + self.0.first().unwrap().call_trace.borrow() + } +} + +impl CallTrace { + pub(crate) fn default_successful_call() -> Self { + Self { + entry_point: CallEntryPoint::default(), + used_execution_resources: ExecutionResources::default(), + used_l1_resources: L1Resources::default(), + used_syscalls_vm_resources: SyscallUsageMap::default(), + used_syscalls_sierra_gas: SyscallUsageMap::default(), + nested_calls: vec![], + result: CallResult::Success { ret_data: vec![] }, + vm_trace: None, + gas_consumed: u64::default(), + events: vec![], + signature: vec![], + gas_report_data: None, + } + } + + #[must_use] + pub fn get_total_used_syscalls(&self) -> SyscallUsageMap { + sum_syscall_usage( + self.used_syscalls_vm_resources.clone(), + &self.used_syscalls_sierra_gas, + ) + } +} + +impl CallTraceNode { + #[must_use] + pub fn extract_entry_point_call(&self) -> Option<&Rc>> { + if let CallTraceNode::EntryPointCall(trace) = self { + Some(trace) + } else { + None + } + } +} + +impl GasReportData { + #[must_use] + pub fn new(execution_summary: ExecutionSummary) -> Self { + Self { + execution_summary, + partial_gas_usage: OnceCell::new(), + } + } + + pub fn get_gas(&self) -> &GasVector { + self.partial_gas_usage.get_or_init(|| { + self.execution_summary.clone().to_partial_gas_vector( + VersionedConstants::latest_constants(), + &GasVectorComputationMode::All, + ) + }) + } +} + +impl CairoSerialize for CallTrace { + fn serialize(&self, output: &mut BufferWriter) { + self.entry_point.serialize(output); + + let visible_calls: Vec<_> = self + .nested_calls + .iter() + .filter_map(CallTraceNode::extract_entry_point_call) + .collect(); + + visible_calls.serialize(output); + + self.result.serialize(output); + } +} diff --git a/crates/debugging/src/trace/collect.rs b/crates/debugging/src/trace/collect.rs index 952c79245e..ceb81083a6 100644 --- a/crates/debugging/src/trace/collect.rs +++ b/crates/debugging/src/trace/collect.rs @@ -7,7 +7,7 @@ use crate::{Context, Trace}; use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::{ CallFailure, CallResult as CheatnetCallResult, }; -use cheatnet::state::{CallTrace, CallTraceNode}; +use cheatnet::trace_data::{CallTrace, CallTraceNode}; use data_transformer::{reverse_transform_input, reverse_transform_output}; use starknet::core::types::contract::AbiEntry; use starknet_api::core::ClassHash; diff --git a/crates/debugging/src/trace/types.rs b/crates/debugging/src/trace/types.rs index b976a044e3..ade7e650da 100644 --- a/crates/debugging/src/trace/types.rs +++ b/crates/debugging/src/trace/types.rs @@ -5,7 +5,7 @@ use crate::trace::components::{ ContractAddressContainer, ContractNameContainer, EntryPointTypeContainer, GasContainer, }; use crate::tree::TreeSerialize; -use cheatnet::state::CallTrace; +use cheatnet::trace_data::CallTrace; use starknet_api::core::ContractAddress as ApiContractAddress; use starknet_api::execution_resources::GasAmount as ApiGasAmount; use std::fmt; diff --git a/crates/forge-runner/src/build_trace_data.rs b/crates/forge-runner/src/build_trace_data.rs index 90e9c2c98a..721d496e38 100644 --- a/crates/forge-runner/src/build_trace_data.rs +++ b/crates/forge-runner/src/build_trace_data.rs @@ -23,7 +23,7 @@ use camino::{Utf8Path, Utf8PathBuf}; use cheatnet::forking::data::ForkData; use cheatnet::runtime_extensions::common::{get_syscalls_gas_consumed, sum_syscall_usage}; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; -use cheatnet::state::{CallTrace, CallTraceNode}; +use cheatnet::trace_data::{CallTrace, CallTraceNode}; use conversions::IntoConv; use conversions::string::TryFromHexStr; use runtime::starknet::constants::{TEST_CONTRACT_CLASS_HASH, TEST_ENTRY_POINT_SELECTOR}; diff --git a/crates/forge-runner/src/debugging/mod.rs b/crates/forge-runner/src/debugging/mod.rs index ab11b52a05..c3c76f8c19 100644 --- a/crates/forge-runner/src/debugging/mod.rs +++ b/crates/forge-runner/src/debugging/mod.rs @@ -4,7 +4,7 @@ mod trace_verbosity; use cheatnet::forking::data::ForkData; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; -use cheatnet::state::CallTrace; +use cheatnet::trace_data::CallTrace; pub use args::TraceArgs; pub use trace_verbosity::TraceVerbosity; diff --git a/crates/forge-runner/src/running.rs b/crates/forge-runner/src/running.rs index badb5b400b..84378570e6 100644 --- a/crates/forge-runner/src/running.rs +++ b/crates/forge-runner/src/running.rs @@ -27,9 +27,8 @@ use cheatnet::runtime_extensions::forge_runtime_extension::{ get_all_used_resources, update_top_call_l1_resources, update_top_call_resources, update_top_call_vm_trace, }; -use cheatnet::state::{ - BlockInfoReader, CallTrace, CheatnetState, EncounteredErrors, ExtendedStateReader, -}; +use cheatnet::state::{BlockInfoReader, CheatnetState, EncounteredErrors, ExtendedStateReader}; +use cheatnet::trace_data::CallTrace; use execution::finalize_execution; use hints::hints_by_representation; use rand::prelude::StdRng;