Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(blockifier): update gas and vm resources computations #2153

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading