Skip to content

Commit

Permalink
Added get_unspent_gas function. (#7017)
Browse files Browse the repository at this point in the history
  • Loading branch information
orizi authored Jan 8, 2025
1 parent 536872b commit 03944ce
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 4 deletions.
18 changes: 18 additions & 0 deletions corelib/src/test/testing_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,21 @@ fn test_get_available_gas_with_gas_supply() {
fn test_assert_eq_path_requiring_inference() {
assert_eq!(Option::<u32>::None, Option::None);
}

#[inline(never)]
fn identity<T>(t: T) -> T {
t
}

#[test]
fn test_get_unspent_gas() {
let one = identity(1);
let two = identity(2);
let prev = crate::testing::get_unspent_gas();
let _three = identity(one + two);
let after = crate::testing::get_unspent_gas();
let expected_cost = 100 // `one + two`.
+ 300 // `identity(...)`.
+ 2300; // `get_unspent_gas()`.
assert_eq!(prev - after, expected_cost);
}
23 changes: 23 additions & 0 deletions corelib/src/testing.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,26 @@ use crate::gas::GasBuiltin;
/// }
/// ```
pub extern fn get_available_gas() -> u128 implicits(GasBuiltin) nopanic;

/// Returns the amount of gas available in the `GasBuiltin`, as well as the amount of gas unused in
/// the local wallet.
///
/// Useful for asserting that a certain amount of gas used.
/// Note: This function call costs exactly `2300` gas, so this may be ignored in calculations.
/// # Examples
///
/// ```
/// use core::testing::get_unspent_gas;
///
/// fn gas_heavy_function() {
/// // ... some gas-intensive code
/// }
///
/// fn test_gas_consumption() {
/// let gas_before = get_unspent_gas();
/// gas_heavy_function();
/// let gas_after = get_unspent_gas();
/// assert!(gas_after - gas_before < 100_000);
/// }
/// ```
pub extern fn get_unspent_gas() -> u128 implicits(GasBuiltin) nopanic;
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,12 @@ pub fn core_libfunc_ap_change<InfoProvider: InvocationApChangeInfoProvider>(
|token_type| info_provider.token_usages(token_type),
))]
}
GasConcreteLibfunc::GetAvailableGas(_) => vec![ApChange::Known(0)],
GasConcreteLibfunc::GetAvailableGas(_) => {
vec![ApChange::Known(0)]
}
GasConcreteLibfunc::GetUnspentGas(_) => {
vec![ApChange::Known(BuiltinCostsType::cost_computation_steps(false, |_| 2) + 1)]
}
GasConcreteLibfunc::BuiltinWithdrawGas(_) => {
let cost_computation_ap_change: usize =
BuiltinCostsType::cost_computation_steps(true, |token_type| {
Expand Down
9 changes: 8 additions & 1 deletion crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use cairo_lang_sierra::extensions::felt252_dict::{
};
use cairo_lang_sierra::extensions::function_call::SignatureAndFunctionConcreteLibfunc;
use cairo_lang_sierra::extensions::gas::GasConcreteLibfunc::{
BuiltinWithdrawGas, GetAvailableGas, GetBuiltinCosts, RedepositGas, WithdrawGas,
BuiltinWithdrawGas, GetAvailableGas, GetBuiltinCosts, GetUnspentGas, RedepositGas, WithdrawGas,
};
use cairo_lang_sierra::extensions::gas::{BuiltinCostsType, CostTokenType};
use cairo_lang_sierra::extensions::int::signed::{SintConcrete, SintTraits};
Expand Down Expand Up @@ -244,6 +244,13 @@ pub fn core_libfunc_cost(
],
RedepositGas(_) => vec![BranchCost::RedepositGas],
GetAvailableGas(_) => vec![ConstCost::default().into()],
GetUnspentGas(_) => vec![
ConstCost::steps(
BuiltinCostsType::cost_computation_steps(false, |_| 2).into_or_panic::<i32>()
+ 1,
)
.into(),
],
BuiltinWithdrawGas(_) => {
vec![
BranchCost::WithdrawGas(WithdrawGasBranchInfo {
Expand Down
43 changes: 43 additions & 0 deletions crates/cairo-lang-sierra-to-casm/src/invocations/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub fn build(
GasConcreteLibfunc::WithdrawGas(_) => build_withdraw_gas(builder),
GasConcreteLibfunc::RedepositGas(_) => build_redeposit_gas(builder),
GasConcreteLibfunc::GetAvailableGas(_) => misc::build_dup(builder),
GasConcreteLibfunc::GetUnspentGas(_) => build_get_unspent_gas(builder),
GasConcreteLibfunc::BuiltinWithdrawGas(_) => build_builtin_withdraw_gas(builder),
GasConcreteLibfunc::GetBuiltinCosts(_) => build_get_builtin_costs(builder),
}
Expand Down Expand Up @@ -139,6 +140,48 @@ fn build_redeposit_gas(
))
}

/// Handles the get_unspent_gas invocation.
fn build_get_unspent_gas(
builder: CompiledInvocationBuilder<'_>,
) -> Result<CompiledInvocation, InvocationError> {
let [gas_counter] = builder.try_get_single_cells()?;
let mut casm_builder = CasmBuilder::default();
add_input_variables! {casm_builder,
deref gas_counter;
};
let (pre_instructions, cost_builtin_ptr) = add_cost_builtin_ptr_fetch_code(&mut casm_builder);

let get_token_count = |token: CostTokenType| {
if let GasWallet::Value(wallet) = &builder.environment.gas_wallet {
wallet.get(&token).copied().unwrap_or_default()
} else {
0
}
};
casm_build_extend! {casm_builder,
tempvar builtin_cost = cost_builtin_ptr;
const const_count = get_token_count(CostTokenType::Const);
tempvar total_unspent = gas_counter + const_count;
};
let mut total_unspent = total_unspent;
for token_type in CostTokenType::iter_precost() {
let index = token_type.offset_in_builtin_costs();
casm_build_extend! {casm_builder,
tempvar single_cost = builtin_cost[index];
const count = get_token_count(*token_type);
tempvar multi_cost = single_cost * count;
tempvar updated_total_unspent = total_unspent + multi_cost;
};
total_unspent = updated_total_unspent;
}
Ok(builder.build_from_casm_builder_ex(
casm_builder,
[("Fallthrough", &[&[gas_counter], &[total_unspent]], None)],
Default::default(),
pre_instructions,
))
}

/// Handles the withdraw_gas invocation with the builtin costs argument.
fn build_builtin_withdraw_gas(
builder: CompiledInvocationBuilder<'_>,
Expand Down
29 changes: 29 additions & 0 deletions crates/cairo-lang-sierra/src/extensions/modules/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ define_libfunc_hierarchy! {
WithdrawGas(WithdrawGasLibfunc),
RedepositGas(RedepositGasLibfunc),
GetAvailableGas(GetAvailableGasLibfunc),
GetUnspentGas(GetUnspentGasLibfunc),
BuiltinWithdrawGas(BuiltinCostWithdrawGasLibfunc),
GetBuiltinCosts(BuiltinCostGetBuiltinCostsLibfunc),
}, GasConcreteLibfunc
Expand Down Expand Up @@ -128,6 +129,34 @@ impl NoGenericArgsGenericLibfunc for GetAvailableGasLibfunc {
}
}

/// Libfunc for returning the amount of available gas, including gas in local wallet.
#[derive(Default)]
pub struct GetUnspentGasLibfunc {}
impl NoGenericArgsGenericLibfunc for GetUnspentGasLibfunc {
const STR_ID: &'static str = "get_unspent_gas";

fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
) -> Result<LibfuncSignature, SpecializationError> {
let gas_builtin_type = context.get_concrete_type(GasBuiltinType::id(), &[])?;
Ok(LibfuncSignature::new_non_branch(
vec![gas_builtin_type.clone()],
vec![
OutputVarInfo {
ty: gas_builtin_type,
ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 0 },
},
OutputVarInfo {
ty: context.get_concrete_type(Uint128Type::id(), &[])?,
ref_info: OutputVarReferenceInfo::SimpleDerefs,
},
],
SierraApChange::Known { new_vars_only: false },
))
}
}

/// Represents different type of costs.
/// Note that if you add a type here you should update 'iter_precost'
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
Expand Down
4 changes: 3 additions & 1 deletion crates/cairo-lang-sierra/src/simulation/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ pub fn simulate<
(vec![CoreValue::GasBuiltin(gas_counter), CoreValue::Uint128(gas_counter as u128)], 0)
}
CoreConcreteLibfunc::Gas(
GasConcreteLibfunc::BuiltinWithdrawGas(_) | GasConcreteLibfunc::GetBuiltinCosts(_),
GasConcreteLibfunc::BuiltinWithdrawGas(_)
| GasConcreteLibfunc::GetBuiltinCosts(_)
| GasConcreteLibfunc::GetUnspentGas(_),
) => {
unimplemented!("Simulation of the builtin cost functionality is not implemented yet.")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use super::{

#[test]
fn all_list_includes_all_supported() {
let blocked_libfuncs = ["print", "cheatcode", "get_available_gas"];
let blocked_libfuncs = ["print", "cheatcode", "get_available_gas", "get_unspent_gas"];
pretty_assertions::assert_eq!(
lookup_allowed_libfuncs_list(ListSelector::ListName(BUILTIN_ALL_LIBFUNCS_LIST.to_string()))
.unwrap()
Expand Down

0 comments on commit 03944ce

Please sign in to comment.