From 4fcb333b61f9d57aaa7a83358eb1cf89dd17e337 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 8 Oct 2025 09:35:36 +0200 Subject: [PATCH 1/5] Store execution summary in trace data --- .../forge_runtime_extension/mod.rs | 61 ++++++++++++++++++- crates/cheatnet/src/state.rs | 18 +++++- crates/forge-runner/src/running.rs | 6 +- 3 files changed, 79 insertions(+), 6 deletions(-) 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 2cafde01ef..c12e641c3b 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -17,14 +17,17 @@ use crate::runtime_extensions::{ storage::{calculate_variable_address, load, store}, }, }; -use crate::state::{CallTrace, CallTraceNode}; +use crate::state::{CallTrace, CallTraceNode, GasReportData}; use anyhow::{Context, Result, anyhow}; use blockifier::bouncer::vm_resources_to_sierra_gas; use blockifier::context::TransactionContext; -use blockifier::execution::call_info::CallInfo; +use blockifier::execution::call_info::{ + CallInfo, CallSummary, ChargedResources, EventSummary, ExecutionSummary, OrderedEvent, +}; use blockifier::execution::contract_class::TrackedResource; use blockifier::execution::syscalls::vm_syscall_utils::{SyscallSelector, SyscallUsageMap}; use blockifier::state::errors::StateError; +use blockifier::utils::u64_from_usize; use cairo_vm::vm::runners::cairo_runner::CairoRunner; use cairo_vm::vm::{ errors::hint_errors::HintError, runners::cairo_runner::ExecutionResources, @@ -42,10 +45,11 @@ use runtime::{ }; use scarb_oracle_hint_service::OracleHintService; use starknet::signers::SigningKey; +use starknet_api::execution_resources::GasAmount; use starknet_api::{contract_class::EntryPointType::L1Handler, core::ClassHash}; use starknet_types_core::felt::Felt; use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::rc::Rc; use std::sync::{Arc, Mutex}; @@ -738,6 +742,57 @@ pub fn update_top_call_vm_trace(runtime: &mut ForgeRuntime, cairo_runner: &mut C } } +pub fn compute_and_store_execution_summary(trace: &Rc>) -> ExecutionSummary { + let execution_summary = if trace.borrow().nested_calls.is_empty() { + get_call_execution_summary(trace) + } else { + let mut nested_calls_summaries = vec![]; + for nested_call in &trace.borrow().nested_calls { + if let CallTraceNode::EntryPointCall(nested_call) = nested_call { + nested_calls_summaries.push(compute_and_store_execution_summary(nested_call)); + } + } + let mut current_call_summary = + get_call_execution_summary(trace) + nested_calls_summaries.into_iter().sum(); + + // vm_resources and gas_consumed of a call already contains the resources of its inner calls. + current_call_summary.charged_resources.vm_resources = + trace.borrow().used_execution_resources.clone(); + current_call_summary.charged_resources.gas_consumed = + GasAmount(trace.borrow().gas_consumed); + current_call_summary + }; + + trace.borrow_mut().gas_report_data = Some(GasReportData::new(execution_summary.clone())); + + execution_summary +} + +fn get_call_execution_summary(trace: &Rc>) -> ExecutionSummary { + let current_call = trace.borrow(); + ExecutionSummary { + charged_resources: ChargedResources { + vm_resources: current_call.used_execution_resources.clone(), + gas_consumed: GasAmount(current_call.gas_consumed), + }, + l2_to_l1_payload_lengths: current_call.used_l1_resources.l2_l1_message_sizes.clone(), + event_summary: { + let mut event_summary = EventSummary { + n_events: current_call.events.len(), + ..Default::default() + }; + for OrderedEvent { event, .. } in ¤t_call.events { + event_summary.total_event_data_size += u64_from_usize(event.data.0.len()); + event_summary.total_event_keys += u64_from_usize(event.keys.len()); + } + event_summary + }, + call_summary: CallSummary::default(), + executed_class_hashes: HashSet::default(), + visited_storage_entries: HashSet::default(), + } +} + fn add_sierra_gas_resources(top_call: &Rc>) -> u64 { let mut gas_consumed = top_call.borrow().gas_consumed; for nested_call in &top_call.borrow().nested_calls { diff --git a/crates/cheatnet/src/state.rs b/crates/cheatnet/src/state.rs index ed57186e4a..e6890450b7 100644 --- a/crates/cheatnet/src/state.rs +++ b/crates/cheatnet/src/state.rs @@ -10,7 +10,7 @@ use crate::runtime_extensions::forge_runtime_extension::cheatcodes::cheat_execut }; 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::execution::call_info::{OrderedEvent, OrderedL2ToL1Message}; +use blockifier::execution::call_info::{ExecutionSummary, OrderedEvent, OrderedL2ToL1Message}; use blockifier::execution::contract_class::RunnableCompiledClass; use blockifier::execution::entry_point::CallEntryPoint; use blockifier::execution::syscalls::vm_syscall_utils::SyscallUsageMap; @@ -203,6 +203,18 @@ impl CheatStatus { } } +#[derive(Clone, Debug)] +pub struct GasReportData { + pub execution_summary: ExecutionSummary, +} + +impl GasReportData { + #[must_use] + pub fn new(execution_summary: ExecutionSummary) -> Self { + Self { execution_summary } + } +} + /// Tree structure representing trace of a call. #[derive(Debug)] pub struct CallTrace { @@ -221,6 +233,9 @@ pub struct CallTrace { 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 { @@ -253,6 +268,7 @@ impl CallTrace { gas_consumed: u64::default(), events: vec![], signature: vec![], + gas_report_data: None, } } diff --git a/crates/forge-runner/src/running.rs b/crates/forge-runner/src/running.rs index 73905b60a1..badb5b400b 100644 --- a/crates/forge-runner/src/running.rs +++ b/crates/forge-runner/src/running.rs @@ -23,8 +23,9 @@ use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::CallToBl use cheatnet::runtime_extensions::call_to_blockifier_runtime_extension::rpc::UsedResources; use cheatnet::runtime_extensions::cheatable_starknet_runtime_extension::CheatableStarknetRuntimeExtension; use cheatnet::runtime_extensions::forge_runtime_extension::{ - ForgeExtension, ForgeRuntime, add_resources_to_top_call, get_all_used_resources, - update_top_call_l1_resources, update_top_call_resources, update_top_call_vm_trace, + ForgeExtension, ForgeRuntime, add_resources_to_top_call, compute_and_store_execution_summary, + 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, @@ -332,6 +333,7 @@ pub fn run_test_case( update_top_call_resources(&mut forge_runtime, tracked_resource); update_top_call_l1_resources(&mut forge_runtime); + compute_and_store_execution_summary(&call_trace_ref); let fuzzer_args = forge_runtime .extended_runtime From 3bf2f34b06fe18884bdcc14899446c6ba67d6eff Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Fri, 10 Oct 2025 11:08:54 +0200 Subject: [PATCH 2/5] Add comments to new functions --- .../src/runtime_extensions/forge_runtime_extension/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 c12e641c3b..c694801513 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -755,7 +755,7 @@ pub fn compute_and_store_execution_summary(trace: &Rc>) -> Ex let mut current_call_summary = get_call_execution_summary(trace) + nested_calls_summaries.into_iter().sum(); - // vm_resources and gas_consumed of a call already contains the resources of its inner calls. + // vm_resources and gas_consumed of a call already contain the resources of its inner calls. current_call_summary.charged_resources.vm_resources = trace.borrow().used_execution_resources.clone(); current_call_summary.charged_resources.gas_consumed = @@ -768,6 +768,7 @@ pub fn compute_and_store_execution_summary(trace: &Rc>) -> Ex execution_summary } +// Based on blockifier/src/execution/call_info.rs (summarize) fn get_call_execution_summary(trace: &Rc>) -> ExecutionSummary { let current_call = trace.borrow(); ExecutionSummary { @@ -787,6 +788,7 @@ fn get_call_execution_summary(trace: &Rc>) -> ExecutionSummar } event_summary }, + // Fields below are not relevant for partial gas calculation. call_summary: CallSummary::default(), executed_class_hashes: HashSet::default(), visited_storage_entries: HashSet::default(), From 206083a897580db30f9d94748b557b9878dca9d5 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Tue, 14 Oct 2025 12:17:00 +0200 Subject: [PATCH 3/5] Remove pub --- crates/cheatnet/src/state.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/cheatnet/src/state.rs b/crates/cheatnet/src/state.rs index e6890450b7..810b347d99 100644 --- a/crates/cheatnet/src/state.rs +++ b/crates/cheatnet/src/state.rs @@ -203,9 +203,10 @@ impl CheatStatus { } } -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct GasReportData { - pub execution_summary: ExecutionSummary, + #[expect(dead_code)] + execution_summary: ExecutionSummary, } impl GasReportData { From ea4328cd001e5c332e5c5c97474bd4e0fce3352e Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Thu, 16 Oct 2025 13:41:34 +0200 Subject: [PATCH 4/5] Change signature of `compute_and_store_execution_summary` --- .../forge_runtime_extension/mod.rs | 14 ++++++++++---- crates/cheatnet/src/state.rs | 3 +-- 2 files changed, 11 insertions(+), 6 deletions(-) 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 c694801513..2403540a33 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -742,14 +742,22 @@ pub fn update_top_call_vm_trace(runtime: &mut ForgeRuntime, cairo_runner: &mut C } } -pub fn compute_and_store_execution_summary(trace: &Rc>) -> ExecutionSummary { +pub fn compute_and_store_execution_summary(trace: &Rc>) { let execution_summary = if trace.borrow().nested_calls.is_empty() { get_call_execution_summary(trace) } else { let mut nested_calls_summaries = vec![]; for nested_call in &trace.borrow().nested_calls { if let CallTraceNode::EntryPointCall(nested_call) = nested_call { - nested_calls_summaries.push(compute_and_store_execution_summary(nested_call)); + compute_and_store_execution_summary(nested_call); + nested_calls_summaries.push( + nested_call + .borrow() + .gas_report_data + .as_ref() + .expect("Gas report data must be set after calling `compute_and_store_execution_summary`") + .execution_summary + .clone()); } } let mut current_call_summary = @@ -764,8 +772,6 @@ pub fn compute_and_store_execution_summary(trace: &Rc>) -> Ex }; trace.borrow_mut().gas_report_data = Some(GasReportData::new(execution_summary.clone())); - - execution_summary } // Based on blockifier/src/execution/call_info.rs (summarize) diff --git a/crates/cheatnet/src/state.rs b/crates/cheatnet/src/state.rs index 810b347d99..1eccc926f6 100644 --- a/crates/cheatnet/src/state.rs +++ b/crates/cheatnet/src/state.rs @@ -205,8 +205,7 @@ impl CheatStatus { #[derive(Debug)] pub struct GasReportData { - #[expect(dead_code)] - execution_summary: ExecutionSummary, + pub execution_summary: ExecutionSummary, } impl GasReportData { From e2675f3d94af5f11f81e6b7c2e144adc94a35012 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Thu, 16 Oct 2025 13:57:33 +0200 Subject: [PATCH 5/5] Rename `get_call_execution_summary` -> `get_execution_summary_without_nested_calls` --- .../src/runtime_extensions/forge_runtime_extension/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 2403540a33..57cd064029 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -744,7 +744,7 @@ pub fn update_top_call_vm_trace(runtime: &mut ForgeRuntime, cairo_runner: &mut C pub fn compute_and_store_execution_summary(trace: &Rc>) { let execution_summary = if trace.borrow().nested_calls.is_empty() { - get_call_execution_summary(trace) + get_execution_summary_without_nested_calls(trace) } else { let mut nested_calls_summaries = vec![]; for nested_call in &trace.borrow().nested_calls { @@ -760,8 +760,8 @@ pub fn compute_and_store_execution_summary(trace: &Rc>) { .clone()); } } - let mut current_call_summary = - get_call_execution_summary(trace) + nested_calls_summaries.into_iter().sum(); + let mut current_call_summary = get_execution_summary_without_nested_calls(trace) + + nested_calls_summaries.into_iter().sum(); // vm_resources and gas_consumed of a call already contain the resources of its inner calls. current_call_summary.charged_resources.vm_resources = @@ -775,7 +775,7 @@ pub fn compute_and_store_execution_summary(trace: &Rc>) { } // Based on blockifier/src/execution/call_info.rs (summarize) -fn get_call_execution_summary(trace: &Rc>) -> ExecutionSummary { +fn get_execution_summary_without_nested_calls(trace: &Rc>) -> ExecutionSummary { let current_call = trace.borrow(); ExecutionSummary { charged_resources: ChargedResources {