Skip to content
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
17 changes: 0 additions & 17 deletions corelib.patch
Original file line number Diff line number Diff line change
Expand Up @@ -1028,23 +1028,6 @@ diff --color=auto -crN cairo2/corelib/src/test.cairo cairo2/corelib_1/src/test.c
*** cairo2/corelib/src/test.cairo Mon Aug 25 09:50:36 2025
--- cairo2/corelib_1/src/test.cairo Wed Sep 3 12:26:58 2025
***************
*** 12,18 ****
mod ec_test;
mod felt_test;
mod fmt_test;
! mod gas_reserve_test;
mod hash_test;
mod integer_test;
mod iter_test;
--- 12,18 ----
mod ec_test;
mod felt_test;
mod fmt_test;
! // mod gas_reserve_test;
mod hash_test;
mod integer_test;
mod iter_test;
***************
*** 24,30 ****
mod option_test;
mod plugins_test;
Expand Down
2 changes: 1 addition & 1 deletion debug_utils/sierra-emu/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,9 @@ impl Value {
StarknetTypeConcrete::Sha256StateHandle(_) => matches!(self, Self::Struct { .. }),
},
CoreTypeConcrete::IntRange(_) => matches!(self, Self::IntRange { .. }),
CoreTypeConcrete::GasReserve(_) => matches!(self, Self::U128(_)),
CoreTypeConcrete::Blake(_) => todo!(),
CoreTypeConcrete::QM31(_) => todo!(),
CoreTypeConcrete::GasReserve(_) => todo!(),
};

if !res {
Expand Down
3 changes: 2 additions & 1 deletion debug_utils/sierra-emu/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ mod felt252_dict;
mod felt252_dict_entry;
mod function_call;
mod gas;
mod gas_reserve;
mod int;
mod int_range;
mod jump;
Expand Down Expand Up @@ -520,12 +521,12 @@ fn eval<'a>(
EvalAction::NormalBranch(0, smallvec![value])
}
CoreConcreteLibfunc::IntRange(selector) => self::int_range::eval(registry, selector, args),
CoreConcreteLibfunc::GasReserve(selector) => self::gas_reserve::eval(selector, args),
CoreConcreteLibfunc::Blake(_) => todo!(),
CoreConcreteLibfunc::QM31(_) => todo!(),
CoreConcreteLibfunc::Felt252SquashedDict(_) => todo!(),
CoreConcreteLibfunc::Trace(_) => todo!(),
CoreConcreteLibfunc::UnsafePanic(_) => todo!(),
CoreConcreteLibfunc::DummyFunctionCall(_) => todo!(),
CoreConcreteLibfunc::GasReserve(_) => todo!(),
}
}
39 changes: 39 additions & 0 deletions debug_utils/sierra-emu/src/vm/gas_reserve.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use cairo_lang_sierra::extensions::gas_reserve::GasReserveConcreteLibfunc;
use smallvec::smallvec;

use crate::{vm::EvalAction, Value};

pub fn eval(selector: &GasReserveConcreteLibfunc, args: Vec<Value>) -> EvalAction {
match selector {
GasReserveConcreteLibfunc::Create(_) => eval_gas_reserve_create(args),
GasReserveConcreteLibfunc::Utilize(_) => eval_gas_reserve_utilize(args),
}
}

fn eval_gas_reserve_create(args: Vec<Value>) -> EvalAction {
let [range_check @ Value::Unit, Value::U64(gas), Value::U128(amount)]: [Value; 3] =
args.try_into().unwrap()
else {
panic!()
};

if amount <= gas.into() {
let spare_gas = gas - amount as u64;
EvalAction::NormalBranch(
0,
smallvec![range_check, Value::U64(spare_gas), Value::U128(amount)],
)
} else {
EvalAction::NormalBranch(1, smallvec![range_check, Value::U64(gas)])
}
}

fn eval_gas_reserve_utilize(args: Vec<Value>) -> EvalAction {
let [Value::U64(gas), Value::U128(amount)]: [Value; 2] = args.try_into().unwrap() else {
panic!()
};

let updated_gas = gas + amount as u64;

EvalAction::NormalBranch(0, smallvec![Value::U64(updated_gas)])
}
5 changes: 4 additions & 1 deletion src/libfuncs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ mod felt252_dict;
mod felt252_dict_entry;
mod function_call;
mod gas;
mod gas_reserve;
mod int;
mod int_range;
mod mem;
Expand Down Expand Up @@ -255,9 +256,11 @@ impl LibfuncBuilder for CoreConcreteLibfunc {
metadata,
&info.signature.param_signatures,
),
Self::GasReserve(selector) => self::gas_reserve::build(
context, registry, entry, location, helper, metadata, selector,
),
Self::QM31(_) => native_panic!("Implement QM31 libfunc"),
Self::UnsafePanic(_) => native_panic!("Implement unsafe_panic libfunc"),
Self::GasReserve(_) => native_panic!("Implement gas_reserve libfunc"),
Self::DummyFunctionCall(_) => native_panic!("Implement dummy_function_call libfunc"),
}
}
Expand Down
206 changes: 206 additions & 0 deletions src/libfuncs/gas_reserve.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
use crate::libfuncs::LibfuncHelper;
use crate::{error::Result, metadata::MetadataStorage};
use cairo_lang_sierra::extensions::lib_func::SignatureOnlyConcreteLibfunc;
use cairo_lang_sierra::{
extensions::{
core::{CoreLibfunc, CoreType},
gas_reserve::GasReserveConcreteLibfunc,
},
program_registry::ProgramRegistry,
};
use melior::dialect::arith::{self, CmpiPredicate};
use melior::helpers::{ArithBlockExt, BuiltinBlockExt};
use melior::ir::r#type::IntegerType;
use melior::{
ir::{Block, Location},
Context,
};

pub fn build<'ctx, 'this>(
context: &'ctx Context,
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
entry: &'this Block<'ctx>,
location: Location<'ctx>,
helper: &LibfuncHelper<'ctx, 'this>,
metadata: &mut MetadataStorage,
selector: &GasReserveConcreteLibfunc,
) -> Result<()> {
match selector {
GasReserveConcreteLibfunc::Create(info) => {
build_gas_reserve_create(context, registry, entry, location, helper, metadata, info)
}
GasReserveConcreteLibfunc::Utilize(info) => {
build_gas_reserve_utilize(context, registry, entry, location, helper, metadata, info)
}
}
}

/// Generate MLIR operations for the `gas_reserve_create` libfunc.
///
/// # Cairo Signature
///
/// ```cairo
/// pub extern fn gas_reserve_create(
/// amount: u128,
/// ) -> Option<GasReserve> implicits(RangeCheck, GasBuiltin) nopanic;
/// ```
fn build_gas_reserve_create<'ctx, 'this>(
context: &'ctx Context,
_registry: &ProgramRegistry<CoreType, CoreLibfunc>,
entry: &'this Block<'ctx>,
location: Location<'ctx>,
helper: &LibfuncHelper<'ctx, 'this>,
_metadata: &mut MetadataStorage,
_info: &SignatureOnlyConcreteLibfunc,
) -> Result<()> {
let range_check = super::increment_builtin_counter(context, entry, location, entry.arg(0)?)?;
let current_gas = entry.arg(1)?; // u64
let amount = entry.arg(2)?; // u128

let amount_ty = IntegerType::new(context, 128).into();
let current_gas_128 = entry.append_op_result(arith::extui(current_gas, amount_ty, location))?;
let enough_gas = entry.cmpi(
context,
CmpiPredicate::Uge,
current_gas_128,
amount,
location,
)?;

let gas_builtin_ty = IntegerType::new(context, 64).into();
let spare_gas_128 = entry.append_op_result(arith::subi(current_gas_128, amount, location))?;
let spare_gas =
entry.append_op_result(arith::trunci(spare_gas_128, gas_builtin_ty, location))?;

helper.cond_br(
context,
entry,
enough_gas,
[0, 1],
[
&[range_check, spare_gas, amount],
&[range_check, current_gas],
],
location,
)
}

/// Generate MLIR operations for the `gas_reserve_utilize` libfunc.
///
/// # Cairo Signature
///
/// ```cairo
/// pub extern fn gas_reserve_utilize(reserve: GasReserve) implicits(GasBuiltin) nopanic;
/// ```
fn build_gas_reserve_utilize<'ctx, 'this>(
context: &'ctx Context,
_registry: &ProgramRegistry<CoreType, CoreLibfunc>,
entry: &'this Block<'ctx>,
location: Location<'ctx>,
helper: &LibfuncHelper<'ctx, 'this>,
_metadata: &mut MetadataStorage,
_info: &SignatureOnlyConcreteLibfunc,
) -> Result<()> {
let current_gas = entry.arg(0)?; // u64
let gas_reserve_128 = entry.arg(1)?; // u128

let gas_reserve = entry.append_op_result(arith::trunci(
gas_reserve_128,
IntegerType::new(context, 64).into(),
location,
))?;
let updated_gas = entry.append_op_result(arith::addi(current_gas, gas_reserve, location))?;

helper.br(entry, 0, &[updated_gas], location)
}

#[cfg(test)]
mod test {
use crate::{load_cairo, utils::testing::run_program, Value};
use proptest::proptest;
const GAS_OVERFLOW: u128 = u64::MAX as u128 + 1;

proptest! {
#[test]
fn run_utilize(gas_quant: u64) { // The quantity cant be bigger than an u64. Otherwise it would panic
let program = load_cairo!(
use core::gas::{GasReserve, gas_reserve_create, gas_reserve_utilize};

fn run_test(amount: u128) -> u128 {
let initial_gas = core::testing::get_available_gas();
let reserve = gas_reserve_create(amount).unwrap();
gas_reserve_utilize(reserve);
let final_gas = core::testing::get_available_gas();

initial_gas - final_gas
}
);

let result = run_program(&program, "run_test", &[Value::Uint128(gas_quant as u128)]).return_value;
if let Value::Enum { tag, value, .. } = result {
if let Value::Struct { fields, .. } = *value {
assert_eq!(tag, 0);
assert_eq!(fields[0], Value::Uint128(0));
}
}
}

#[test]
fn run_create_success(gas_quant: u64) {
let program = load_cairo!(
use core::gas::{GasReserve, gas_reserve_create, gas_reserve_utilize};

fn run_test_1(amount: u128) -> Option<GasReserve> {
gas_reserve_create(amount)
}

fn run_test_2(amount: u128) -> u128 {
let initial_gas = core::testing::get_available_gas();
let reserve = gas_reserve_create(amount).unwrap();
let final_gas = core::testing::get_available_gas();
gas_reserve_utilize(reserve);

initial_gas - final_gas
}
);

let result = run_program(&program, "run_test_1", &[Value::Uint128(gas_quant as u128)]).return_value;
if let Value::Enum { tag, value, .. } = result {
assert_eq!(tag, 0);
assert_eq!(value, Box::new(Value::Uint128(gas_quant as u128)))
}

let result = run_program(&program, "run_test_2", &[Value::Uint128(gas_quant as u128)]).return_value;
if let Value::Enum { tag, value, .. } = result {
if let Value::Struct { fields, .. } = *value {
assert_eq!(tag, 0);
assert_eq!(fields[0], Value::Uint128(gas_quant as u128));
}
}
}

/// Checks that the reserve creation fails when using amounts above u64.
/// Gas is represented as a u64, but reserve amounts can be u128, so when
/// trying to create a reserve that exceeds the max gas amount it should fail.
#[test]
fn run_create_fail(gas_quant in GAS_OVERFLOW..u128::MAX ) {
let program = load_cairo!(
use core::gas::{GasReserve, gas_reserve_create, gas_reserve_utilize};

fn run_test(amount: u128) -> u128 {
let initial_gas = core::testing::get_available_gas();
let reserve = gas_reserve_create(amount).unwrap(); // It should panic here
gas_reserve_utilize(reserve);
let final_gas = core::testing::get_available_gas();

initial_gas - final_gas
}
);

let result = run_program(&program, "run_test", &[Value::Uint128(gas_quant)]).return_value;
if let Value::Enum { tag, .. } = result {
assert_eq!(tag, 1);
}
}
}
}
Loading
Loading