Skip to content

Commit

Permalink
feat(blockifier): update gas and vm resources computations
Browse files Browse the repository at this point in the history
  • Loading branch information
TzahiTaub committed Dec 16, 2024
1 parent 3f75d13 commit 43545d9
Show file tree
Hide file tree
Showing 15 changed files with 322 additions and 205 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@
},
"validate_max_n_steps": 1000000,
"validate_max_sierra_gas": 10000000000,
"min_compiler_version_for_sierra_gas": "2.8.0",
"min_compiler_version_for_sierra_gas": "100.0.0",
"vm_resource_fee_cost": {
"builtins": {
"add_mod_builtin": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@
},
"validate_max_n_steps": 1000000,
"validate_max_sierra_gas": 10000000000,
"min_compiler_version_for_sierra_gas": "2.8.0",
"min_compiler_version_for_sierra_gas": "100.0.0",
"vm_resource_fee_cost": {
"builtins": {
"add_mod_builtin": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@
},
"validate_max_n_steps": 1000000,
"validate_max_sierra_gas": 10000000000,
"min_compiler_version_for_sierra_gas": "2.8.0",
"min_compiler_version_for_sierra_gas": "100.0.0",
"vm_resource_fee_cost": {
"builtins": {
"add_mod_builtin": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@
},
"validate_max_n_steps": 1000000,
"validate_max_sierra_gas": 10000000000,
"min_compiler_version_for_sierra_gas": "2.8.0",
"min_compiler_version_for_sierra_gas": "100.0.0",
"vm_resource_fee_cost": {
"builtins": {
"add_mod_builtin": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@
},
"validate_max_n_steps": 1000000,
"validate_max_sierra_gas": 10000000000,
"min_compiler_version_for_sierra_gas": "2.8.0",
"min_compiler_version_for_sierra_gas": "100.0.0",
"vm_resource_fee_cost": {
"builtins": {
"add_mod_builtin": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@
},
"validate_max_n_steps": 1000000,
"validate_max_sierra_gas": 10000000000,
"min_compiler_version_for_sierra_gas": "2.8.0",
"min_compiler_version_for_sierra_gas": "100.0.0",
"vm_resource_fee_cost": {
"builtins": {
"add_mod_builtin": [
Expand Down
4 changes: 4 additions & 0 deletions crates/blockifier/src/execution/call_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ impl ChargedResources {
pub fn from_execution_resources(resources: ExecutionResources) -> Self {
Self { vm_resources: resources, ..Default::default() }
}

pub fn from_gas(gas_for_fee: GasAmount) -> Self {
Self { gas_for_fee, ..Default::default() }
}
}

impl Add<&ChargedResources> for &ChargedResources {
Expand Down
70 changes: 34 additions & 36 deletions crates/blockifier/src/execution/entry_point_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use cairo_vm::vm::errors::cairo_run_errors::CairoRunError;
use cairo_vm::vm::errors::memory_errors::MemoryError;
use cairo_vm::vm::errors::vm_errors::VirtualMachineError;
use cairo_vm::vm::runners::builtin_runner::BuiltinRunner;
use cairo_vm::vm::runners::cairo_runner::{CairoArg, CairoRunner};
use cairo_vm::vm::runners::cairo_runner::{CairoArg, CairoRunner, ExecutionResources};
use cairo_vm::vm::security::verify_secure_runner;
use num_traits::{ToPrimitive, Zero};
use starknet_api::execution_resources::GasAmount;
Expand Down Expand Up @@ -360,32 +360,20 @@ fn maybe_fill_holes(
Ok(())
}

/// Calculates the total gas for fee in the current call + subtree.
#[allow(dead_code)]
fn to_gas_for_fee(
/// Calculates the gas consumed in the current call.
pub fn gas_consumed_without_inner_calls(
tracked_resource: &TrackedResource,
gas_consumed: u64,
inner_calls: &[CallInfo],
) -> GasAmount {
// The Sierra gas consumed in this specific call is `gas_consumed`
// (= total gas of self + subtree), minus the sum of all inner calls Sierra gas consumed.
// To compute the total Sierra gas to charge (of self + subtree), if the tracked resource is
// Sierra gas, we add this amount to the total gas to charge for in the subtree:
// gas_for_fee = gas_consumed - subtree_gas_consumed + subtree_gas_to_fee.
GasAmount(match tracked_resource {
// If the tracked resource is CairoSteps, then all tracked resources of all calls in
// the subtree are also CairoSteps. Thus, the total gas to charge in this subtree is zero.
TrackedResource::CairoSteps => 0,
TrackedResource::SierraGas => gas_consumed
.checked_sub(
inner_calls
.iter()
.map(|call| call.execution.gas_consumed - call.charged_resources.gas_for_fee.0)
.sum::<u64>(),
)
.expect("gas_for_fee unexpectedly underflowed."),
.checked_sub(inner_calls.iter().map(|call| call.execution.gas_consumed).sum::<u64>())
.expect("gas_consumed unexpectedly underflowed."),
})
}

pub fn finalize_execution(
mut runner: CairoRunner,
mut syscall_handler: SyscallHintProcessor<'_>,
Expand All @@ -410,30 +398,40 @@ pub fn finalize_execution(

let call_result = get_call_result(&runner, &syscall_handler, &tracked_resource)?;

// Take into account the resources of the current call, without inner calls.
// Has to happen after marking holes in segments as accessed.
let mut vm_resources_without_inner_calls = runner
.get_execution_resources()
.map_err(VirtualMachineError::RunnerError)?
.filter_unused_builtins();
let versioned_constants = syscall_handler.base.context.versioned_constants();
if versioned_constants.segment_arena_cells {
vm_resources_without_inner_calls
.builtin_instance_counter
.get_mut(&BuiltinName::segment_arena)
.map_or_else(|| {}, |val| *val *= SEGMENT_ARENA_BUILTIN_SIZE);
}
// Take into account the syscall resources of the current call.
vm_resources_without_inner_calls +=
&versioned_constants.get_additional_os_syscall_resources(&syscall_handler.syscall_counter);
let vm_resources_without_inner_calls = match tracked_resource {
TrackedResource::CairoSteps => {
// Take into account the resources of the current call, without inner calls.
// Has to happen after marking holes in segments as accessed.
let mut vm_resources_without_inner_calls = runner
.get_execution_resources()
.map_err(VirtualMachineError::RunnerError)?
.filter_unused_builtins();
let versioned_constants = syscall_handler.base.context.versioned_constants();
if versioned_constants.segment_arena_cells {
vm_resources_without_inner_calls
.builtin_instance_counter
.get_mut(&BuiltinName::segment_arena)
.map_or_else(|| {}, |val| *val *= SEGMENT_ARENA_BUILTIN_SIZE);
}
// Take into account the syscall resources of the current call.
vm_resources_without_inner_calls += &versioned_constants
.get_additional_os_syscall_resources(&syscall_handler.syscall_counter);
vm_resources_without_inner_calls
}
TrackedResource::SierraGas => ExecutionResources::default(),
};

syscall_handler.finalize();

let charged_resources_without_inner_calls = ChargedResources {
vm_resources: vm_resources_without_inner_calls,
// TODO(tzahi): Replace with a computed value.
gas_for_fee: GasAmount(0),
gas_for_fee: gas_consumed_without_inner_calls(
&tracked_resource,
call_result.gas_consumed,
&syscall_handler.base.inner_calls,
),
};

let charged_resources = &charged_resources_without_inner_calls
+ &CallInfo::summarize_charged_resources(syscall_handler.base.inner_calls.iter());

Expand Down
109 changes: 104 additions & 5 deletions crates/blockifier/src/execution/entry_point_execution_test.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
use std::sync::Arc;

use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
use rstest::rstest;
use starknet_api::abi::abi_utils::selector_from_name;
use starknet_api::execution_resources::GasAmount;
use starknet_api::transaction::fields::Calldata;

use crate::context::ChainInfo;
use crate::execution::call_info::{CallExecution, CallInfo, ChargedResources};
use crate::execution::contract_class::TrackedResource;
use crate::execution::entry_point_execution::to_gas_for_fee;
use crate::execution::entry_point::CallEntryPoint;
use crate::execution::entry_point_execution::gas_consumed_without_inner_calls;
use crate::test_utils::contracts::FeatureContract;
use crate::test_utils::initial_test_state::test_state;
use crate::test_utils::syscall::build_recurse_calldata;
use crate::test_utils::{
trivial_external_entry_point_new,
CairoVersion,
CompilerBasedVersion,
RunnableCairo1,
BALANCE,
};

#[test]
/// Verifies that every call from the inner most to the outer has the expected gas_for_fee for the
/// following topology (marked as TrackedResource(gas_consumed)):
// Gas(8) -> Gas(3) -> VM(2) -> VM(1)
// \ -> VM(4)
// Expected values are 2 -> 1 -> 0 -> 0.
// Expected values are 1 -> 1 -> 0 -> 0.
// \-> 0.
fn test_gas_for_fee() {
// First branch - 3 nested calls.
Expand All @@ -20,7 +38,7 @@ fn test_gas_for_fee() {
(TrackedResource::SierraGas, 3, 1),
] {
assert_eq!(
to_gas_for_fee(&tracked_resource, gas_consumed, &inner_calls).0,
gas_consumed_without_inner_calls(&tracked_resource, gas_consumed, &inner_calls).0,
expected_gas_for_fee
);
inner_calls = vec![CallInfo {
Expand All @@ -38,7 +56,10 @@ fn test_gas_for_fee() {
// Second branch - 1 call.
let (tracked_resource, gas_consumed, expected_gas_for_fee) =
(TrackedResource::CairoSteps, 4, 0);
assert_eq!(to_gas_for_fee(&tracked_resource, gas_consumed, &[]).0, expected_gas_for_fee);
assert_eq!(
gas_consumed_without_inner_calls(&tracked_resource, gas_consumed, &[]).0,
expected_gas_for_fee
);

inner_calls.push(CallInfo {
execution: CallExecution { gas_consumed, ..Default::default() },
Expand All @@ -51,5 +72,83 @@ fn test_gas_for_fee() {
});

// Outer call.
assert_eq!(to_gas_for_fee(&TrackedResource::SierraGas, 8, &inner_calls).0, 2);
assert_eq!(gas_consumed_without_inner_calls(&TrackedResource::SierraGas, 8, &inner_calls).0, 1);
}

/// Asserts that the charged resources of a call is consistent with the inner calls in its subtree.
fn assert_charged_resource_as_expected_rec(call_info: &CallInfo) {
let inner_calls = &call_info.inner_calls;
let mut children_vm_resources = ExecutionResources::default();
let mut children_gas = GasAmount(0);
for child_call_info in inner_calls.iter() {
let ChargedResources { gas_for_fee, vm_resources } = &child_call_info.charged_resources;
children_vm_resources += vm_resources;
children_gas += *gas_for_fee;
}

let ChargedResources { gas_for_fee, vm_resources } = &call_info.charged_resources;

match call_info.tracked_resource {
TrackedResource::SierraGas => {
assert_eq!(vm_resources, &children_vm_resources);
assert!(gas_for_fee > &children_gas)
}
TrackedResource::CairoSteps => {
assert_eq!(gas_for_fee, &children_gas);
assert!(vm_resources.n_steps > children_vm_resources.n_steps)
}
}

for child_call_info in inner_calls.iter() {
assert_charged_resource_as_expected_rec(child_call_info);
}
}

#[rstest]
fn test_charged_resources_computation(
#[values(
CompilerBasedVersion::CairoVersion(CairoVersion::Cairo0),
CompilerBasedVersion::OldCairo1
)]
third_contract_version: CompilerBasedVersion,
#[values(
CompilerBasedVersion::CairoVersion(CairoVersion::Cairo0),
CompilerBasedVersion::OldCairo1
)]
fourth_contract_version: CompilerBasedVersion,
#[values(
CompilerBasedVersion::CairoVersion(CairoVersion::Cairo0),
CompilerBasedVersion::OldCairo1
)]
second_branch_contract_version: CompilerBasedVersion,
) {
let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm));
let chain_info = &ChainInfo::create_for_testing();
let contracts = CompilerBasedVersion::iter().map(|version| version.get_test_contract());
let mut state = test_state(
chain_info,
BALANCE,
&contracts.map(|contract| (contract, 1)).collect::<Vec<_>>(),
);
let call_versions = [
CompilerBasedVersion::CairoVersion(CairoVersion::Cairo1(RunnableCairo1::Casm)),
CompilerBasedVersion::CairoVersion(CairoVersion::Cairo1(RunnableCairo1::Casm)),
third_contract_version,
fourth_contract_version,
];

let first_calldata = build_recurse_calldata(&call_versions);
let second_calldata = build_recurse_calldata(&[second_branch_contract_version]);
let outer_calldata = Calldata(Arc::new(
(*first_calldata.0).iter().copied().chain((*second_calldata.0).iter().copied()).collect(),
));
let call_contract_selector = selector_from_name("test_call_two_contracts");
let entry_point_call = CallEntryPoint {
entry_point_selector: call_contract_selector,
calldata: outer_calldata,
..trivial_external_entry_point_new(test_contract)
};
let call_info = entry_point_call.execute_directly(&mut state).unwrap();

assert_charged_resource_as_expected_rec(&call_info);
}
6 changes: 3 additions & 3 deletions crates/blockifier/src/execution/entry_point_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::state::cached_state::CachedState;
use crate::test_utils::contracts::FeatureContract;
use crate::test_utils::dict_state_reader::DictStateReader;
use crate::test_utils::initial_test_state::test_state;
use crate::test_utils::{trivial_external_entry_point_new, CairoVersion, RunnableCairo1, BALANCE};
use crate::test_utils::{trivial_external_entry_point_new, CairoVersion, BALANCE};
use crate::versioned_constants::VersionedConstants;

#[test]
Expand Down Expand Up @@ -513,8 +513,8 @@ fn test_storage_related_members() {
}

#[test]
fn test_cairo1_entry_point_segment_arena() {
let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm));
fn test_old_cairo1_entry_point_segment_arena() {
let test_contract = FeatureContract::CairoStepsTestContract;
let chain_info = &ChainInfo::create_for_testing();
let mut state = test_state(chain_info, BALANCE, &[(test_contract, 1)]);
let calldata = calldata![];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use cairo_native::execution_result::ContractExecutionResult;
use cairo_native::utils::BuiltinCosts;
use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
use stacker;
use starknet_api::execution_resources::GasAmount;

use crate::execution::call_info::{CallExecution, CallInfo, ChargedResources, Retdata};
use crate::execution::contract_class::TrackedResource;
Expand All @@ -11,6 +10,7 @@ use crate::execution::entry_point::{
EntryPointExecutionContext,
EntryPointExecutionResult,
};
use crate::execution::entry_point_execution::gas_consumed_without_inner_calls;
use crate::execution::errors::{EntryPointExecutionError, PostExecutionError};
use crate::execution::native::contract_class::NativeCompiledClassV1;
use crate::execution::native::syscall_handler::NativeSyscallHandler;
Expand Down Expand Up @@ -98,8 +98,11 @@ fn create_callinfo(

let charged_resources_without_inner_calls = ChargedResources {
vm_resources: ExecutionResources::default(),
// TODO(tzahi): Replace with a computed value.
gas_for_fee: GasAmount(0),
gas_for_fee: gas_consumed_without_inner_calls(
&TrackedResource::SierraGas,
gas_consumed,
&syscall_handler.base.inner_calls,
),
};
let charged_resources = &charged_resources_without_inner_calls
+ &CallInfo::summarize_charged_resources(syscall_handler.base.inner_calls.iter());
Expand Down
Loading

0 comments on commit 43545d9

Please sign in to comment.