Skip to main content

cairo_annotations/
trace_data.rs

1use crate::felt_deserialize::deserialize as felt_deserialize;
2use camino::Utf8PathBuf;
3use serde::{Deserialize, Serialize};
4use starknet_types_core::felt::Felt;
5use std::collections::HashMap;
6use std::ops::{AddAssign, SubAssign};
7use strum::VariantArray;
8use strum_macros::{Display, EnumString, VariantArray};
9
10#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
11pub struct ClassHash(#[serde(deserialize_with = "felt_deserialize")] pub Felt);
12
13#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
14pub struct ContractAddress(#[serde(deserialize_with = "felt_deserialize")] pub Felt);
15
16#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
17pub struct EntryPointSelector(#[serde(deserialize_with = "felt_deserialize")] pub Felt);
18
19/// Versioned representation of `CallTrace`.
20///
21/// Always prefer using this enum when Serializing/Deserializing instead of inner ones.
22#[derive(Debug, Clone, Serialize, Deserialize)]
23#[serde(untagged)]
24pub enum VersionedCallTrace {
25    V1(CallTraceV1),
26}
27
28/// Tree structure representing trace of a call.
29/// This struct should be serialized and used as an input to cairo-profiler.
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct CallTraceV1 {
32    pub entry_point: CallEntryPoint,
33    #[serde(rename = "used_execution_resources")]
34    pub cumulative_resources: ExecutionResources,
35    pub used_l1_resources: L1Resources,
36    pub nested_calls: Vec<CallTraceNode>,
37    pub cairo_execution_info: Option<CairoExecutionInfo>,
38}
39
40/// Struct needed for function level profiling.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct CairoExecutionInfo {
43    /// Path to a file with serialized `ContractClass` or `VersionedProgram`.
44    pub source_sierra_path: Utf8PathBuf,
45    pub casm_level_info: CasmLevelInfo,
46    /// `enable-gas` option from `[cairo]` section in `Scarb.toml`
47    pub enable_gas: Option<bool>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct CasmLevelInfo {
52    pub run_with_call_header: bool,
53    pub vm_trace: Vec<TraceEntry>,
54    /// Executable program offset information
55    pub program_offset: Option<usize>,
56}
57
58/// Enum representing node of a trace of a call.
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub enum CallTraceNode {
61    EntryPointCall(Box<CallTraceV1>),
62    DeployWithoutConstructor,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct TraceEntry {
67    pub pc: usize,
68    pub ap: usize,
69    pub fp: usize,
70}
71
72type SyscallCounter = HashMap<DeprecatedSyscallSelector, SyscallUsage>;
73
74#[derive(Debug, Default, Clone, Deserialize, Serialize)]
75pub struct SyscallUsage {
76    pub call_count: usize,
77    pub linear_factor: usize,
78}
79
80#[derive(Debug, Default, Clone, Deserialize, Serialize)]
81pub struct ExecutionResources {
82    pub vm_resources: VmExecutionResources,
83    pub gas_consumed: Option<u64>,
84    /// Present for `snforge` >= `0.46.0`.
85    pub syscall_counter: Option<SyscallCounter>,
86}
87
88#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
89pub struct VmExecutionResources {
90    pub n_steps: usize,
91    pub n_memory_holes: usize,
92    pub builtin_instance_counter: HashMap<String, usize>,
93}
94
95#[derive(
96    Clone,
97    Copy,
98    Debug,
99    Display,
100    Deserialize,
101    Serialize,
102    Eq,
103    Hash,
104    PartialEq,
105    EnumString,
106    VariantArray,
107)]
108pub enum DeprecatedSyscallSelector {
109    CallContract,
110    DelegateCall,
111    DelegateL1Handler,
112    Deploy,
113    EmitEvent,
114    GetBlockHash,
115    GetBlockNumber,
116    GetBlockTimestamp,
117    GetCallerAddress,
118    GetContractAddress,
119    GetClassHashAt,
120    GetExecutionInfo,
121    GetSequencerAddress,
122    GetTxInfo,
123    GetTxSignature,
124    Keccak,
125    LibraryCall,
126    LibraryCallL1Handler,
127    ReplaceClass,
128    Secp256k1Add,
129    Secp256k1GetPointFromX,
130    Secp256k1GetXy,
131    Secp256k1Mul,
132    Secp256k1New,
133    Secp256r1Add,
134    Secp256r1GetPointFromX,
135    Secp256r1GetXy,
136    Secp256r1Mul,
137    Secp256r1New,
138    SendMessageToL1,
139    StorageRead,
140    StorageWrite,
141    Sha256ProcessBlock,
142    Sha512ProcessBlock,
143    KeccakRound,
144    MetaTxV0,
145}
146
147impl DeprecatedSyscallSelector {
148    #[must_use]
149    pub fn all() -> &'static [Self] {
150        Self::VARIANTS
151    }
152}
153
154#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
155pub struct CallEntryPoint {
156    pub class_hash: Option<ClassHash>,
157    pub entry_point_type: EntryPointType,
158    pub entry_point_selector: EntryPointSelector,
159    pub contract_address: ContractAddress,
160    pub call_type: CallType,
161
162    /// Contract name to display instead of contract address
163    pub contract_name: Option<String>,
164    /// Function name to display instead of entry point selector
165    pub function_name: Option<String>,
166    /// Calldata length to use for syscall cost estimation
167    /// Present for `snforge` >= `0.48.0`.
168    pub calldata_len: Option<usize>,
169    /// Events information to use for l2 gas cost estimation
170    /// Present for `snforge` >= `0.49.0`
171    pub events_summary: Option<Vec<SummedUpEvent>>,
172    /// Signature length to use for l2 gas cost estimation
173    /// Present for `snforge` >= `0.49.0`
174    pub signature_len: Option<usize>,
175}
176
177#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Deserialize, Serialize)]
178pub enum CallType {
179    #[default]
180    Call = 0,
181    Delegate = 1,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
185pub enum EntryPointType {
186    #[serde(rename = "CONSTRUCTOR")]
187    Constructor,
188    #[serde(rename = "EXTERNAL")]
189    #[default]
190    External,
191    #[serde(rename = "L1_HANDLER")]
192    L1Handler,
193}
194
195#[derive(Clone, Default, Debug, Serialize, Deserialize)]
196pub struct L1Resources {
197    pub l2_l1_message_sizes: Vec<usize>,
198}
199
200impl AddAssign<&VmExecutionResources> for VmExecutionResources {
201    fn add_assign(&mut self, other: &VmExecutionResources) {
202        self.n_steps += other.n_steps;
203        self.n_memory_holes += other.n_memory_holes;
204
205        for (key, value) in &other.builtin_instance_counter {
206            *self
207                .builtin_instance_counter
208                .entry(key.clone())
209                .or_default() += *value;
210        }
211    }
212}
213
214impl SubAssign<&VmExecutionResources> for VmExecutionResources {
215    fn sub_assign(&mut self, other: &VmExecutionResources) {
216        self.n_steps = self.n_steps.saturating_sub(other.n_steps);
217        self.n_memory_holes = self.n_memory_holes.saturating_sub(other.n_memory_holes);
218
219        for (key, value) in &other.builtin_instance_counter {
220            if let Some(self_value) = self.builtin_instance_counter.get_mut(key) {
221                *self_value = self_value.saturating_sub(*value);
222            }
223        }
224        // Remove entries where the value is 0
225        self.builtin_instance_counter.retain(|_, value| *value > 0);
226    }
227}
228
229impl AddAssign<&ExecutionResources> for ExecutionResources {
230    fn add_assign(&mut self, other: &ExecutionResources) {
231        self.vm_resources += &other.vm_resources;
232        self.gas_consumed = match (self.gas_consumed, other.gas_consumed) {
233            (Some(self_gas), Some(other_gas)) => Some(self_gas + other_gas),
234            (Some(self_gas), None) => Some(self_gas),
235            (None, Some(other_gas)) => Some(other_gas),
236            (None, None) => None,
237        };
238
239        if let Some(other_counter) = &other.syscall_counter {
240            let self_counter = self.syscall_counter.get_or_insert_with(HashMap::new);
241            for (&selector, usage) in other_counter {
242                self_counter
243                    .entry(selector)
244                    .and_modify(|existing| {
245                        existing.call_count += usage.call_count;
246                        existing.linear_factor += usage.linear_factor;
247                    })
248                    .or_insert_with(|| usage.clone());
249            }
250        }
251    }
252}
253
254impl SubAssign<&ExecutionResources> for ExecutionResources {
255    fn sub_assign(&mut self, other: &ExecutionResources) {
256        self.vm_resources -= &other.vm_resources;
257
258        if let Some(other_gas) = other.gas_consumed
259            && let Some(self_gas) = &mut self.gas_consumed
260        {
261            *self_gas = self_gas.saturating_sub(other_gas);
262        }
263
264        if let Some(self_counter) = &mut self.syscall_counter
265            && let Some(other_counter) = &other.syscall_counter
266        {
267            for (selector, usage) in other_counter {
268                if let Some(self_usage) = self_counter.get_mut(selector) {
269                    self_usage.call_count = self_usage.call_count.saturating_sub(usage.call_count);
270                    self_usage.linear_factor =
271                        self_usage.linear_factor.saturating_sub(usage.linear_factor);
272                }
273            }
274            // Remove entries where both values are 0
275            self_counter.retain(|_, usage| usage.call_count > 0 || usage.linear_factor > 0);
276        }
277    }
278}
279
280#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
281pub struct SummedUpEvent {
282    pub keys_len: usize,
283    pub data_len: usize,
284}