Skip to content

Commit cd999e9

Browse files
feat(starknet_os): add EndpointArg and support parameters passed by value (#5024)
1 parent 314bb7d commit cd999e9

File tree

6 files changed

+418
-89
lines changed

6 files changed

+418
-89
lines changed

crates/starknet_committer_and_os_cli/src/os_cli/tests/python_tests.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,10 +197,9 @@ use cairo_vm::hint_processor::builtin_hint_processor::hint_code::{
197197
VM_EXIT_SCOPE,
198198
XS_SAFE_DIV,
199199
};
200-
use cairo_vm::types::relocatable::MaybeRelocatable;
201-
use cairo_vm::vm::runners::cairo_runner::CairoArg;
202200
use starknet_os::hints::enum_definition::{AggregatorHint, HintExtension, OsHint};
203201
use starknet_os::hints::types::HintEnum;
202+
use starknet_os::test_utils::cairo_runner::EndpointArg;
204203
use starknet_os::test_utils::errors::Cairo0EntryPointRunnerError;
205204
use starknet_os::test_utils::utils::run_cairo_function_and_check_result;
206205
use strum::IntoEnumIterator;
@@ -282,7 +281,7 @@ fn compare_os_hints(input: &str) -> OsPythonTestResult {
282281
fn test_cairo_function(
283282
program_str: &str,
284283
function_name: &str,
285-
explicit_args: &[CairoArg],
284+
explicit_args: &[EndpointArg],
286285
expected_retdata: &Retdata,
287286
) -> OsPythonTestResult {
288287
run_cairo_function_and_check_result(
@@ -304,7 +303,7 @@ fn run_dummy_cairo_function(input: &str) -> OsPythonTestResult {
304303
test_cairo_function(
305304
input,
306305
"dummy_function",
307-
&[MaybeRelocatable::from(param_1).into(), MaybeRelocatable::from(param_2).into()],
306+
&[param_1.into(), param_2.into()],
308307
&retdata![(789 + param_1).into(), param_1.into(), param_2.into()],
309308
)
310309
}
@@ -324,10 +323,10 @@ fn test_constants(input: &str) -> OsPythonTestResult {
324323
input,
325324
"test_constants",
326325
&[
327-
MaybeRelocatable::from(max_non_compressed_contract_address).into(),
328-
MaybeRelocatable::from(alias_counter_storage_key).into(),
329-
MaybeRelocatable::from(initial_available_alias).into(),
330-
MaybeRelocatable::from(alias_contract_address).into(),
326+
max_non_compressed_contract_address.into(),
327+
alias_counter_storage_key.into(),
328+
initial_available_alias.into(),
329+
alias_contract_address.into(),
331330
],
332331
&retdata![],
333332
)

crates/starknet_os/src/test_utils/cairo_runner.rs

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,106 @@ use cairo_vm::types::layout_name::LayoutName;
55
use cairo_vm::types::program::Program;
66
use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable};
77
use cairo_vm::vm::runners::cairo_runner::{CairoArg, CairoRunner};
8+
use starknet_types_core::felt::Felt;
89

910
use crate::test_utils::errors::{Cairo0EntryPointRunnerError, ExplicitArgError};
1011

1112
#[cfg(test)]
1213
#[path = "cairo_runner_test.rs"]
1314
mod test;
1415

16+
/// An arg passed by value (i.e., a felt, tuple, named tuple or struct).
17+
#[derive(Clone, Debug)]
18+
pub enum ValueArg {
19+
Single(Felt),
20+
Array(Vec<Felt>),
21+
Composed(Vec<EndpointArg>),
22+
}
23+
24+
/// An arg passed as a pointer. i.e., a pointer to a felt, tuple, named tuple or struct, or a
25+
/// pointer to a pointer.
26+
#[derive(Clone, Debug)]
27+
pub enum PointerArg {
28+
Array(Vec<Felt>),
29+
Composed(Vec<EndpointArg>),
30+
}
31+
32+
#[derive(Clone, Debug)]
33+
pub enum EndpointArg {
34+
Value(ValueArg),
35+
Pointer(PointerArg),
36+
}
37+
38+
impl From<i32> for EndpointArg {
39+
fn from(value: i32) -> Self {
40+
Self::Value(ValueArg::Single(value.into()))
41+
}
42+
}
43+
44+
impl EndpointArg {
45+
/// Converts an endpoint arg into a vector of cairo args.
46+
/// The cairo VM loads struct / tuple / named tuple parameters by adding each of their fields
47+
/// to the stack. This is why a single endpoint arg can be converted into multiple cairo args -
48+
/// an arg of type Struct {a: felt, b: felt} will be converted into a vector of two cairo args
49+
/// of type felt.
50+
fn to_cairo_arg_vec(endpoint_arg: &EndpointArg) -> Vec<CairoArg> {
51+
match endpoint_arg {
52+
EndpointArg::Value(value_arg) => match value_arg {
53+
ValueArg::Single(felt) => {
54+
vec![CairoArg::Single(MaybeRelocatable::Int(*felt))]
55+
}
56+
ValueArg::Array(felts) => felts
57+
.iter()
58+
.map(|felt| CairoArg::Single(MaybeRelocatable::Int(*felt)))
59+
.collect(),
60+
ValueArg::Composed(endpoint_args) => {
61+
endpoint_args.iter().flat_map(Self::to_cairo_arg_vec).collect()
62+
}
63+
},
64+
EndpointArg::Pointer(pointer_arg) => match pointer_arg {
65+
PointerArg::Array(felts) => vec![CairoArg::Array(
66+
felts.iter().map(|felt| MaybeRelocatable::Int(*felt)).collect(),
67+
)],
68+
PointerArg::Composed(endpoint_args) => vec![CairoArg::Composed(
69+
endpoint_args.iter().flat_map(Self::to_cairo_arg_vec).collect(),
70+
)],
71+
},
72+
}
73+
}
74+
}
75+
1576
/// Performs basic validations on the cairo arg. Assumes the arg is not a builtin.
1677
/// A successful result from this function does NOT guarantee that the arguments are valid.
1778
fn perform_basic_validations_on_cairo_arg(
1879
index: usize,
1980
expected_arg: &Member,
20-
actual_arg: &CairoArg,
81+
actual_arg: &EndpointArg,
2182
) -> Result<(), Cairo0EntryPointRunnerError> {
22-
if matches!(actual_arg, CairoArg::Single(MaybeRelocatable::RelocatableValue(_))) {
23-
Err(ExplicitArgError::SingleRelocatableParam { index, actual_arg: actual_arg.clone() })?
24-
}
25-
let actual_arg_is_felt = matches!(actual_arg, CairoArg::Single(MaybeRelocatable::Int(_)));
26-
let actual_arg_is_single = matches!(actual_arg, CairoArg::Single(_));
83+
let actual_arg_is_felt = matches!(actual_arg, EndpointArg::Value(ValueArg::Single(_)));
84+
let actual_arg_is_pointer = matches!(actual_arg, EndpointArg::Pointer(_));
85+
let actual_arg_is_struct_or_tuple = !actual_arg_is_felt && !actual_arg_is_pointer;
2786

2887
let expected_arg_is_pointer = expected_arg.cairo_type.ends_with("*");
2988
let expected_arg_is_felt = expected_arg.cairo_type == "felt";
89+
let expected_arg_is_struct_or_tuple = !expected_arg_is_felt && !expected_arg_is_pointer;
3090

31-
if expected_arg_is_felt != actual_arg_is_felt || expected_arg_is_pointer == actual_arg_is_single
91+
if expected_arg_is_felt != actual_arg_is_felt
92+
|| expected_arg_is_pointer != actual_arg_is_pointer
93+
|| expected_arg_is_struct_or_tuple != actual_arg_is_struct_or_tuple
3294
{
3395
Err(ExplicitArgError::Mismatch {
3496
index,
3597
expected: expected_arg.clone(),
3698
actual: actual_arg.clone(),
3799
})?;
38100
};
39-
// expected arg is tuple / named tuple / struct.
40-
if !expected_arg_is_felt && !expected_arg_is_pointer {
41-
// TODO(Amos): Load tuple / named tuple / struct parameters to stack and remove this error.
42-
Err(ExplicitArgError::UnsupportedArgType { index, expected_arg: expected_arg.clone() })?;
43-
};
44101
Ok(())
45102
}
46103

47104
/// Performs basic validations on the explicit arguments. A successful result from this function
48105
/// does NOT guarantee that the arguments are valid.
49106
fn perform_basic_validations_on_explicit_args(
50-
explicit_args: &[CairoArg],
107+
explicit_args: &[EndpointArg],
51108
program: &Program,
52109
entrypoint: &str,
53110
) -> Result<(), Cairo0EntryPointRunnerError> {
@@ -82,7 +139,7 @@ pub fn run_cairo_0_entry_point(
82139
program: &Program,
83140
entrypoint: &str,
84141
n_expected_return_values: usize,
85-
explicit_args: &[CairoArg],
142+
explicit_args: &[EndpointArg],
86143
mut hint_processor: impl HintProcessor,
87144
) -> Result<Retdata, Cairo0EntryPointRunnerError> {
88145
// TODO(Amos): Perform complete validations.
@@ -97,7 +154,9 @@ pub fn run_cairo_0_entry_point(
97154
let program_base: Option<Relocatable> = None;
98155
cairo_runner.initialize_segments(program_base);
99156

100-
let entrypoint_args: Vec<&CairoArg> = explicit_args.iter().collect();
157+
let entrypoint_args: Vec<CairoArg> =
158+
explicit_args.iter().flat_map(EndpointArg::to_cairo_arg_vec).collect();
159+
let entrypoint_args: Vec<&CairoArg> = entrypoint_args.iter().collect();
101160
let verify_secure = true;
102161
let program_segment_size: Option<usize> = None;
103162
// TODO(Amos): Pass implicit args to the cairo runner.
Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use blockifier::retdata;
2-
use cairo_vm::types::relocatable::MaybeRelocatable;
3-
use cairo_vm::vm::runners::cairo_runner::CairoArg;
42

3+
use crate::test_utils::cairo_runner::{EndpointArg, PointerArg, ValueArg};
54
use crate::test_utils::errors::Cairo0EntryPointRunnerError;
65
use crate::test_utils::utils::run_cairo_function_and_check_result;
76

@@ -15,38 +14,90 @@ use crate::test_utils::utils::run_cairo_function_and_check_result;
1514
/// b: felt,
1615
/// }
1716
///
18-
/// func dummy_function(number: felt, array: felt*, tuple: felt*, simple_struct: SimpleStruct*,
19-
/// compound_struct: CompoundStruct*) -> (res1: felt, res2: felt, res3: felt) {
17+
/// func pass_felt_and_pointers(number: felt, array: felt*, tuple: felt*, simple_struct:
18+
/// SimpleStruct*, compound_struct: CompoundStruct*) -> (res1: felt, res2: felt, res3: felt) {
2019
/// let res1 = number + array[0];
2120
/// let res2 = tuple[0] + tuple[1];
2221
/// let res3 = simple_struct.a + compound_struct.simple_struct.b;
2322
/// return (res1=res1, res2=res2, res3=res3);
2423
/// }
24+
///
25+
/// func pass_structs_and_tuples(tuple: (felt, felt), named_tuple: (a: felt, b:felt), simple_struct:
26+
/// SimpleStruct, compound_struct: CompoundStruct) -> (res1: felt, res2: felt, res3: felt) {
27+
/// let res1 = tuple[0] + tuple[1];
28+
/// let res2 = named_tuple.a + named_tuple.b;
29+
/// let res3 = simple_struct.a + compound_struct.simple_struct.b;
30+
/// return (res1=res1, res2=res2, res3=res3);
31+
/// }
2532
const COMPILED_DUMMY_FUNCTION: &str = include_str!("compiled_dummy_function.json");
2633

2734
#[test]
28-
fn test_cairo0_function_runner() -> Result<(), Cairo0EntryPointRunnerError> {
35+
fn test_felt_and_pointers() -> Result<(), Cairo0EntryPointRunnerError> {
2936
let number = 2;
3037
let (first_array_val, second_array_val) = (3, 4);
3138
let (first_tuple_val, second_tuple_val) = (5, 6);
3239
let (first_simple_struct_val, second_simple_struct_val) = (7, 8);
3340
let compound_struct_val = 9;
34-
let array = CairoArg::Array(vec![first_array_val.into(), second_array_val.into()]);
35-
let tuple = CairoArg::Array(vec![first_tuple_val.into(), second_tuple_val.into()]);
36-
let simple_struct =
37-
CairoArg::Array(vec![first_simple_struct_val.into(), second_simple_struct_val.into()]);
38-
let compound_struct = CairoArg::Composed(vec![
39-
MaybeRelocatable::from(compound_struct_val).into(),
41+
let array = EndpointArg::Pointer(PointerArg::Array(vec![
42+
first_array_val.into(),
43+
second_array_val.into(),
44+
]));
45+
let tuple = EndpointArg::Pointer(PointerArg::Array(vec![
46+
first_tuple_val.into(),
47+
second_tuple_val.into(),
48+
]));
49+
let simple_struct = EndpointArg::Pointer(PointerArg::Array(vec![
50+
first_simple_struct_val.into(),
51+
second_simple_struct_val.into(),
52+
]));
53+
let compound_struct = EndpointArg::Pointer(PointerArg::Composed(vec![
54+
compound_struct_val.into(),
4055
simple_struct.clone(),
41-
]);
56+
]));
4257
run_cairo_function_and_check_result(
4358
COMPILED_DUMMY_FUNCTION,
44-
"dummy_function",
45-
&[MaybeRelocatable::from(number).into(), array, tuple, simple_struct, compound_struct],
59+
"pass_felt_and_pointers",
60+
&[number.into(), array, tuple, simple_struct, compound_struct],
4661
&retdata![
4762
(number + first_array_val).into(),
4863
(first_tuple_val + second_tuple_val).into(),
4964
(first_simple_struct_val + second_simple_struct_val).into()
5065
],
5166
)
5267
}
68+
69+
#[test]
70+
fn test_tuples_and_structs() -> Result<(), Cairo0EntryPointRunnerError> {
71+
let (first_tuple_val, second_tuple_val) = (3, 4);
72+
let (first_named_tuple_val, second_named_tuple_val) = (5, 6);
73+
let (first_simple_struct_val, second_simple_struct_val) = (7, 8);
74+
let compound_struct_val = 9;
75+
let tuple =
76+
EndpointArg::Value(ValueArg::Array(vec![first_tuple_val.into(), second_tuple_val.into()]));
77+
let named_tuple = EndpointArg::Value(ValueArg::Array(vec![
78+
first_named_tuple_val.into(),
79+
second_named_tuple_val.into(),
80+
]));
81+
let simple_struct = EndpointArg::Value(ValueArg::Array(vec![
82+
first_simple_struct_val.into(),
83+
second_simple_struct_val.into(),
84+
]));
85+
let simple_struct_pointer = EndpointArg::Pointer(PointerArg::Array(vec![
86+
first_simple_struct_val.into(),
87+
second_simple_struct_val.into(),
88+
]));
89+
let compound_struct = EndpointArg::Value(ValueArg::Composed(vec![
90+
compound_struct_val.into(),
91+
simple_struct_pointer,
92+
]));
93+
run_cairo_function_and_check_result(
94+
COMPILED_DUMMY_FUNCTION,
95+
"pass_structs_and_tuples",
96+
&[tuple, named_tuple, simple_struct, compound_struct],
97+
&retdata![
98+
(first_tuple_val + second_tuple_val).into(),
99+
(first_named_tuple_val + second_named_tuple_val).into(),
100+
(first_simple_struct_val + second_simple_struct_val).into()
101+
],
102+
)
103+
}

0 commit comments

Comments
 (0)