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): gas cost enum #4633

Open
wants to merge 1 commit into
base: aviv/cherry_pick_deploy_call_data_factor
Choose a base branch
from
Open
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
7 changes: 4 additions & 3 deletions crates/blockifier/src/execution/native/syscall_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use crate::execution::syscalls::hint_processor::{SyscallExecutionError, OUT_OF_G
use crate::execution::syscalls::syscall_base::SyscallHandlerBase;
use crate::state::state_api::State;
use crate::transaction::objects::TransactionInfo;
use crate::versioned_constants::GasCosts;
use crate::versioned_constants::{GasCosts, SyscallGasCost};

pub const CALL_CONTRACT_SELECTOR_NAME: &str = "call_contract";
pub const LIBRARY_CALL_SELECTOR_NAME: &str = "library_call";
Expand Down Expand Up @@ -72,15 +72,16 @@ impl<'state> NativeSyscallHandler<'state> {
fn pre_execute_syscall(
&mut self,
remaining_gas: &mut u64,
syscall_gas_cost: u64,
syscall_gas_cost: SyscallGasCost,
) -> SyscallResult<()> {
if self.unrecoverable_error.is_some() {
// An unrecoverable error was found in a previous syscall, we return immediately to
// accelerate the end of the execution. The returned data is not important
return Err(vec![]);
}
// Refund `SYSCALL_BASE_GAS_COST` as it was pre-charged.
let required_gas = syscall_gas_cost - self.gas_costs().base.syscall_base_gas_cost;
let required_gas =
syscall_gas_cost.get_base_cost() - self.gas_costs().base.syscall_base_gas_cost;

if *remaining_gas < required_gas {
// Out of gas failure.
Expand Down
8 changes: 4 additions & 4 deletions crates/blockifier/src/execution/syscalls/hint_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ use crate::execution::syscalls::{
use crate::state::errors::StateError;
use crate::state::state_api::State;
use crate::transaction::objects::{CurrentTransactionInfo, TransactionInfo};
use crate::versioned_constants::GasCosts;
use crate::versioned_constants::{GasCosts, SyscallGasCost};

#[derive(Clone, Debug, Default)]
pub struct SyscallUsage {
Expand Down Expand Up @@ -480,7 +480,7 @@ impl<'a> SyscallHintProcessor<'a> {
&mut self,
vm: &mut VirtualMachine,
execute_callback: ExecuteCallback,
syscall_gas_cost: u64,
syscall_gas_cost: SyscallGasCost,
) -> HintExecutionResult
where
Request: SyscallRequest + std::fmt::Debug,
Expand All @@ -493,8 +493,8 @@ impl<'a> SyscallHintProcessor<'a> {
) -> SyscallResult<Response>,
{
// Refund `SYSCALL_BASE_GAS_COST` as it was pre-charged.
let required_gas =
syscall_gas_cost - self.base.context.gas_costs().base.syscall_base_gas_cost;
let required_gas = syscall_gas_cost.get_base_cost()
- self.base.context.gas_costs().base.syscall_base_gas_cost;

let SyscallRequestWrapper { gas_counter, request } =
SyscallRequestWrapper::<Request>::read(vm, &mut self.syscall_ptr)?;
Expand Down
3 changes: 2 additions & 1 deletion crates/blockifier/src/execution/syscalls/syscall_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ impl<'state> SyscallHandlerBase<'state> {
// TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion
// works.
let n_rounds_as_u64 = u64::try_from(n_rounds).expect("Failed to convert usize to u64.");
let gas_cost = n_rounds_as_u64 * self.context.gas_costs().syscalls.keccak_round_cost;
let gas_cost =
n_rounds_as_u64 * self.context.gas_costs().syscalls.keccak_round_cost.get_base_cost();

if gas_cost > *remaining_gas {
let out_of_gas_error = Felt::from_hex(OUT_OF_GAS_ERROR)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn test_out_of_gas(runnable_version: RunnableCairo1) {
// We hit the out of gas error right before executing the syscall.
let syscall_base_gas_cost = gas_costs.base.syscall_base_gas_cost;
let redeposit_gas = 300;
let syscall_required_gas = get_block_hash_gas_cost - syscall_base_gas_cost;
let syscall_required_gas = get_block_hash_gas_cost.get_base_cost() - syscall_base_gas_cost;
let call_info = entry_point_call.clone().execute_directly(&mut state).unwrap();
assert_eq!(
call_info.execution,
Expand Down
26 changes: 26 additions & 0 deletions crates/blockifier/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::collections::HashMap;

use cairo_vm::vm::runners::cairo_runner::ExecutionResources;

use crate::transaction::errors::NumericConversionError;
use crate::versioned_constants::GasCosts;

#[cfg(test)]
#[path = "utils_test.rs"]
Expand Down Expand Up @@ -60,3 +63,26 @@ pub fn usize_from_u64(val: u64) -> Result<usize, NumericConversionError> {
pub fn u64_from_usize(val: usize) -> u64 {
val.try_into().expect("Conversion from usize to u64 should not fail.")
}

pub fn get_gas_cost_from_vm_resources(
execution_resources: &ExecutionResources,
gas_costs: &GasCosts,
) -> u64 {
let n_steps = u64_from_usize(execution_resources.n_steps);
let n_memory_holes = u64_from_usize(execution_resources.n_memory_holes);
let total_builtin_gas_cost: u64 = execution_resources
.builtin_instance_counter
.iter()
.map(|(builtin, amount)| {
let builtin_cost = gas_costs
.builtins
.get_builtin_gas_cost(builtin)
.unwrap_or_else(|err| panic!("Failed to get gas cost: {}", err));
builtin_cost * u64_from_usize(*amount)
})
.sum();

n_steps * gas_costs.base.step_gas_cost
+ n_memory_holes * gas_costs.base.memory_hole_gas_cost
+ total_builtin_gas_cost
}
120 changes: 68 additions & 52 deletions crates/blockifier/src/versioned_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::execution::syscalls::hint_processor::SyscallUsageMap;
use crate::execution::syscalls::SyscallSelector;
use crate::fee::resources::StarknetResources;
use crate::transaction::transaction_types::TransactionType;
use crate::utils::u64_from_usize;
use crate::utils::get_gas_cost_from_vm_resources;

#[cfg(test)]
#[path = "versioned_constants_test.rs"]
Expand Down Expand Up @@ -381,34 +381,26 @@ impl VersionedConstants {
}

/// Calculates the syscall gas cost from the OS resources.
pub fn get_syscall_gas_cost(&self, syscall_selector: &SyscallSelector) -> u64 {
pub fn get_syscall_gas_cost(&self, syscall_selector: &SyscallSelector) -> SyscallGasCost {
let gas_costs = &self.os_constants.gas_costs;
let constant_execution_resources = &self
let execution_resources_params = &self
.os_resources
.execute_syscalls
.get(syscall_selector)
.expect("Fetching the execution resources of a syscall should not fail.")
.constant;
let n_steps = u64_from_usize(constant_execution_resources.n_steps);
let n_memory_holes = u64_from_usize(constant_execution_resources.n_memory_holes);
let total_builtin_gas_cost: u64 = constant_execution_resources
.builtin_instance_counter
.iter()
.map(|(builtin, amount)| {
let builtin_cost = gas_costs
.builtins
.get_builtin_gas_cost(builtin)
.unwrap_or_else(|err| panic!("Failed to get gas cost: {}", err));
builtin_cost * u64_from_usize(*amount)
})
.sum();
.expect("Fetching the execution resources of a syscall should not fail.");
// The minimum total cost is `syscall_base_gas_cost`, which is pre-charged by the compiler.
std::cmp::max(
n_steps * gas_costs.base.step_gas_cost
+ n_memory_holes * gas_costs.base.memory_hole_gas_cost
+ total_builtin_gas_cost,

let base_gas_cost = std::cmp::max(
gas_costs.base.syscall_base_gas_cost,
)
get_gas_cost_from_vm_resources(&execution_resources_params.constant, gas_costs),
);
if execution_resources_params.calldata_factor == ExecutionResources::default() {
return SyscallGasCost::Constant(base_gas_cost);
}
// Linear cost is not trivial.
let linear_gas_cost =
get_gas_cost_from_vm_resources(&execution_resources_params.calldata_factor, gas_costs);
SyscallGasCost::Linear { base_cost: base_gas_cost, linear_factor: linear_gas_cost }
}
}

Expand Down Expand Up @@ -707,37 +699,61 @@ impl<'de> Deserialize<'de> for OsResources {
}
}

#[derive(Deserialize, PartialEq, Debug, Clone, Copy, Serialize)]
pub enum SyscallGasCost {
Constant(u64),
Linear { base_cost: u64, linear_factor: u64 },
}

impl SyscallGasCost {
pub fn get_base_cost(&self) -> u64 {
match self {
SyscallGasCost::Constant(cost) => *cost,
SyscallGasCost::Linear { base_cost, .. } => *base_cost,
}
}
}

impl Default for SyscallGasCost {
fn default() -> Self {
SyscallGasCost::Constant(0)
}
}

#[cfg_attr(any(test, feature = "testing"), derive(Clone, Copy))]
#[derive(Debug, Default, Deserialize, PartialEq)]
pub struct SyscallGasCosts {
pub call_contract: u64,
pub deploy: u64,
pub get_block_hash: u64,
pub get_execution_info: u64,
pub library_call: u64,
pub replace_class: u64,
pub storage_read: u64,
pub storage_write: u64,
pub get_class_hash_at: u64,
pub emit_event: u64,
pub send_message_to_l1: u64,
pub secp256k1_add: u64,
pub secp256k1_get_point_from_x: u64,
pub secp256k1_get_xy: u64,
pub secp256k1_mul: u64,
pub secp256k1_new: u64,
pub secp256r1_add: u64,
pub secp256r1_get_point_from_x: u64,
pub secp256r1_get_xy: u64,
pub secp256r1_mul: u64,
pub secp256r1_new: u64,
pub keccak: u64,
pub keccak_round_cost: u64,
pub sha256_process_block: u64,
pub call_contract: SyscallGasCost,
pub deploy: SyscallGasCost,
pub get_block_hash: SyscallGasCost,
pub get_execution_info: SyscallGasCost,
pub library_call: SyscallGasCost,
pub replace_class: SyscallGasCost,
pub storage_read: SyscallGasCost,
pub storage_write: SyscallGasCost,
pub get_class_hash_at: SyscallGasCost,
pub emit_event: SyscallGasCost,
pub send_message_to_l1: SyscallGasCost,
pub secp256k1_add: SyscallGasCost,
pub secp256k1_get_point_from_x: SyscallGasCost,
pub secp256k1_get_xy: SyscallGasCost,
pub secp256k1_mul: SyscallGasCost,
pub secp256k1_new: SyscallGasCost,
pub secp256r1_add: SyscallGasCost,
pub secp256r1_get_point_from_x: SyscallGasCost,
pub secp256r1_get_xy: SyscallGasCost,
pub secp256r1_mul: SyscallGasCost,
pub secp256r1_new: SyscallGasCost,
pub keccak: SyscallGasCost,
pub keccak_round_cost: SyscallGasCost,
pub sha256_process_block: SyscallGasCost,
}

impl SyscallGasCosts {
pub fn get_syscall_gas_cost(&self, selector: &SyscallSelector) -> Result<u64, GasCostsError> {
pub fn get_syscall_gas_cost(
&self,
selector: &SyscallSelector,
) -> Result<SyscallGasCost, GasCostsError> {
let gas_cost = match *selector {
SyscallSelector::CallContract => self.call_contract,
SyscallSelector::Deploy => self.deploy,
Expand Down Expand Up @@ -1019,7 +1035,7 @@ impl OsConstantsRawJson {
&self,
base: &BaseGasCosts,
builtins: &BuiltinGasCosts,
) -> Result<IndexMap<String, u64>, OsConstantsSerdeError> {
) -> Result<IndexMap<String, SyscallGasCost>, OsConstantsSerdeError> {
let mut gas_costs = IndexMap::new();
let key = "syscall_gas_costs";
let syscalls: IndexMap<String, Value> = serde_json::from_value(
Expand Down Expand Up @@ -1126,7 +1142,7 @@ impl OsConstantsRawJson {
&self,
key: &str,
value: &Value,
syscalls: &mut IndexMap<String, u64>,
syscalls: &mut IndexMap<String, SyscallGasCost>,
base: &BaseGasCosts,
builtins: &BuiltinGasCosts,
) -> Result<(), OsConstantsSerdeError> {
Expand Down Expand Up @@ -1163,14 +1179,14 @@ impl OsConstantsRawJson {
})?;
cost += inner_value * factor;
}
syscalls.insert(key.to_string(), cost);
syscalls.insert(key.to_string(), SyscallGasCost::Constant(cost));
}
Value::Number(n) => {
cost = n.as_u64().ok_or_else(|| OsConstantsSerdeError::OutOfRange {
key: key.to_string(),
value: n.clone(),
})?;
syscalls.insert(key.to_string(), cost);
syscalls.insert(key.to_string(), SyscallGasCost::Constant(cost));
}
_ => return Err(OsConstantsSerdeError::UnhandledValueType(value.clone())),
}
Expand Down
17 changes: 14 additions & 3 deletions crates/blockifier/src/versioned_constants_test.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use assert_matches::assert_matches;
use glob::{glob, Paths};
use pretty_assertions::assert_eq;

Expand Down Expand Up @@ -189,15 +190,25 @@ fn test_syscall_gas_cost_calculation() {
let versioned_constants = VersionedConstants::latest_constants().clone();

assert_eq!(
versioned_constants.get_syscall_gas_cost(&SyscallSelector::CallContract),
versioned_constants.get_syscall_gas_cost(&SyscallSelector::CallContract).get_base_cost(),
EXPECTED_CALL_CONTRACT_GAS_COST
);
assert_eq!(
versioned_constants.get_syscall_gas_cost(&SyscallSelector::Secp256k1Mul),
versioned_constants.get_syscall_gas_cost(&SyscallSelector::Secp256k1Mul).get_base_cost(),
EXPECTED_SECP256K1MUL_GAS_COST
);
assert_eq!(
versioned_constants.get_syscall_gas_cost(&SyscallSelector::Sha256ProcessBlock),
versioned_constants
.get_syscall_gas_cost(&SyscallSelector::Sha256ProcessBlock)
.get_base_cost(),
EXPECTED_SHA256PROCESSBLOCK_GAS_COST
);
}

#[test]
fn test_call_data_factor_gas_cost_calculation() {
assert_matches!(
VersionedConstants::latest_constants().os_constants.gas_costs.syscalls.deploy,
SyscallGasCost::Linear { .. }
)
}
Loading