Skip to content

Commit 7f07092

Browse files
starknet_os: os resources test - invoke tx constant factor
1 parent a20c3ab commit 7f07092

2 files changed

Lines changed: 137 additions & 7 deletions

File tree

crates/blockifier/resources/blockifier_versioned_constants_0_14_4.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -526,12 +526,12 @@
526526
},
527527
"InvokeFunction": {
528528
"constant": {
529-
"n_steps": 4348,
529+
"n_steps": 4779,
530530
"n_memory_holes": 0,
531531
"builtin_instance_counter": {
532-
"range_check_builtin": 90,
532+
"range_check_builtin": 110,
533533
"pedersen_builtin": 4,
534-
"poseidon_builtin": 12
534+
"poseidon_builtin": 11
535535
}
536536
},
537537
"calldata_factor": {

crates/starknet_os_flow_tests/src/os_resources_test.rs

Lines changed: 134 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::collections::{HashMap, HashSet};
2-
use std::sync::LazyLock;
2+
use std::sync::{Arc, LazyLock};
33

44
use blockifier::blockifier_versioned_constants::{
5+
CallDataFactor,
56
RawStepGasCost,
67
RawVersionedConstants,
78
ResourcesParams,
@@ -12,6 +13,7 @@ use blockifier::blockifier_versioned_constants::{
1213
use blockifier::context::BlockContext;
1314
use blockifier::execution::deprecated_syscalls::DeprecatedSyscallSelector as Selector;
1415
use blockifier::test_utils::dict_state_reader::DictStateReader;
16+
use blockifier::transaction::objects::ExecutionResourcesTraits;
1517
use blockifier_test_utils::cairo_versions::RunnableCairo1;
1618
use blockifier_test_utils::contracts::FeatureContract;
1719
use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
@@ -20,9 +22,9 @@ use indexmap::IndexMap;
2022
use starknet_api::block::StarknetVersion;
2123
use starknet_api::contract_class::SierraVersion;
2224
use starknet_api::core::{ClassHash, ContractAddress, EthAddress};
23-
use starknet_api::executable_transaction::InvokeTransaction;
25+
use starknet_api::executable_transaction::{InvokeTransaction, TransactionType};
2426
use starknet_api::test_utils::invoke::invoke_tx;
25-
use starknet_api::transaction::fields::ContractAddressSalt;
27+
use starknet_api::transaction::fields::{Calldata, ContractAddressSalt};
2628
use starknet_api::transaction::{L2ToL1Payload, MessageToL1};
2729
use starknet_api::versioned_constants_logic::VersionedConstantsTrait;
2830
use starknet_api::{calldata, declare_tx_args, invoke_tx_args};
@@ -180,7 +182,7 @@ async fn setup_test_builder(raw_vc: Option<&RawVersionedConstants>) -> OsResourc
180182
);
181183
test_builder.add_fund_address_tx_with_default_amount(os_resources_contract_address);
182184

183-
// Declare and deploy an instance of the stable contract.
185+
// Declare and deploy an instance of the stable contract. Also, fund it.
184186
let stable_contract_sierra = &DEPLOYABLE_FOR_RESOURCE_MEASUREMENT_CONTRACT_SIERRA;
185187
let stable_contract_casm = &DEPLOYABLE_FOR_RESOURCE_MEASUREMENT_CONTRACT_CASM;
186188
let stable_contract_class_hash = stable_contract_sierra.calculate_class_hash();
@@ -206,6 +208,7 @@ async fn setup_test_builder(raw_vc: Option<&RawVersionedConstants>) -> OsResourc
206208
deploy_from_zero,
207209
);
208210
test_builder.add_invoke_tx(deploy_tx, None, None);
211+
test_builder.add_fund_address_tx_with_default_amount(stable_contract_address);
209212

210213
// Move on to the next block, so the measurement txs are in their own block.
211214
test_builder.move_to_next_block();
@@ -218,6 +221,13 @@ async fn setup_test_builder(raw_vc: Option<&RawVersionedConstants>) -> OsResourc
218221
}
219222
}
220223

224+
/// Utility method to create dummy calldata to a cairo Span argument.
225+
fn span_calldata(n_elements: usize) -> Calldata {
226+
let mut calldata = vec![Felt::from(n_elements)];
227+
calldata.extend(vec![Felt::ZERO; n_elements]);
228+
Calldata(Arc::new(calldata))
229+
}
230+
221231
/// Regression test for the list of syscalls called during the fee transfer phase of a transaction.
222232
#[tokio::test]
223233
async fn test_fee_transfer_syscalls() {
@@ -485,3 +495,123 @@ async fn test_os_resources_regression() {
485495
expect_file![VersionedConstants::json_path(&version).unwrap()]
486496
.assert_eq(&raw_vc.to_string_pretty());
487497
}
498+
499+
/// Measures the per-transaction-type overhead of `execute_transaction_inner` in the OS and
500+
/// compares it against the versioned constants.
501+
///
502+
/// Methodology:
503+
/// - Run a minimal transaction of the given type through the full OS.
504+
/// - Compute: overhead = OS trace resources − blockifier business-logic resources
505+
/// (execute_call_info + validate_call_info).
506+
/// - The remainder is the pure OS scaffolding cost stored under `execute_txs_inner`.
507+
#[tokio::test]
508+
async fn test_execute_txs_inner_resources() {
509+
let version = StarknetVersion::LATEST;
510+
let mut raw_vc: RawVersionedConstants =
511+
serde_json::from_str(VersionedConstants::json_str(&version).unwrap()).unwrap();
512+
// TODO(Dori): Declare, DeployAccount, L1Handler.
513+
const N_TXS: usize = 2;
514+
515+
// For linear factor measurements, it's not enough to just add one more calldata element; the
516+
// increase is not the same per element. The linear scale is on average.
517+
const INVOKE_SCALING_FACTOR: usize = 2;
518+
const INVOKE_EXTRA_ARGS: usize = 10;
519+
520+
let OsResourcesTestSetup { stable_contract_address, mut test_builder, .. } =
521+
setup_test_builder(Some(&raw_vc)).await;
522+
523+
// Invoke.
524+
let invoke_args = invoke_tx_args! {
525+
sender_address: stable_contract_address,
526+
calldata: calldata![Felt::ZERO],
527+
resource_bounds: *NON_TRIVIAL_RESOURCE_BOUNDS,
528+
nonce: test_builder.next_nonce(stable_contract_address),
529+
};
530+
test_builder.add_invoke_tx(
531+
InvokeTransaction::create(invoke_tx(invoke_args), &test_builder.chain_id()).unwrap(),
532+
None,
533+
None,
534+
);
535+
// Invoke: scale-factor more calldata elements.
536+
let invoke_args = invoke_tx_args! {
537+
sender_address: stable_contract_address,
538+
calldata: span_calldata(INVOKE_EXTRA_ARGS),
539+
resource_bounds: *NON_TRIVIAL_RESOURCE_BOUNDS,
540+
nonce: test_builder.next_nonce(stable_contract_address),
541+
};
542+
test_builder.add_invoke_tx(
543+
InvokeTransaction::create(invoke_tx(invoke_args), &test_builder.chain_id()).unwrap(),
544+
None,
545+
None,
546+
);
547+
548+
// Execute the business logic and extract the business logic resources for each tx.
549+
let test_runner = test_builder.build().await;
550+
let business_logic_resources: [ExecutionResources; N_TXS] = test_runner
551+
.os_hints
552+
.os_input
553+
.os_block_inputs
554+
.last()
555+
.unwrap()
556+
.tx_execution_infos
557+
.iter()
558+
.map(|exec_info| {
559+
let mut business_logic_resources =
560+
[exec_info.execute_call_info.as_ref(), exec_info.validate_call_info.as_ref()]
561+
.into_iter()
562+
.flatten()
563+
.map(|ci| ci.resources.vm_resources.clone())
564+
.fold(ExecutionResources::default(), |acc, resources| &acc + &resources);
565+
// TODO(Dori): Consider supporting memory-hole counting in the OsLogger. Until then, we
566+
// cannot subtract inner calls with positive memory-hole counts from the OsLogger
567+
// resources.
568+
business_logic_resources.n_memory_holes = 0;
569+
business_logic_resources
570+
})
571+
.collect::<Vec<_>>()
572+
.try_into()
573+
.unwrap();
574+
575+
// Run the OS part.
576+
let test_output = test_runner.run();
577+
test_output.perform_default_validations();
578+
579+
// Fetch the OS resources for each tx.
580+
let [invoke_first, invoke_second]: [ExecutionResources; N_TXS] = test_output
581+
.runner_output
582+
.txs_trace
583+
.iter()
584+
.rev()
585+
.take(N_TXS)
586+
.rev()
587+
.map(|trace| trace.get_resources().unwrap().clone())
588+
.zip(business_logic_resources)
589+
.map(|(os_resources, business_logic_resources)| {
590+
(&os_resources - &business_logic_resources).filter_unused_builtins()
591+
})
592+
.collect::<Vec<_>>()
593+
.try_into()
594+
.unwrap();
595+
596+
// Update the VC with the measurements.
597+
// For transaction types with linear factors, the first call has one linear element (calldata
598+
// length, of zero), so one linear cost must be subtracted from the first measurement to get the
599+
// base cost.
600+
let invoke_linear_factor = (&(&invoke_second - &invoke_first).filter_unused_builtins()
601+
* INVOKE_SCALING_FACTOR)
602+
.div_ceil(INVOKE_EXTRA_ARGS);
603+
raw_vc.os_resources.execute_txs_inner.extend([(
604+
TransactionType::InvokeFunction,
605+
VariableResourceParams::WithFactor(ResourcesParams {
606+
constant: (&invoke_first - &invoke_linear_factor).filter_unused_builtins(),
607+
calldata_factor: VariableCallDataFactor::Scaled(CallDataFactor {
608+
resources: invoke_linear_factor,
609+
scaling_factor: INVOKE_SCALING_FACTOR,
610+
}),
611+
}),
612+
)]);
613+
614+
// Verify computation.
615+
expect_file![VersionedConstants::json_path(&version).unwrap()]
616+
.assert_eq(&raw_vc.to_string_pretty());
617+
}

0 commit comments

Comments
 (0)