diff --git a/CHANGELOG.md b/CHANGELOG.md index 081779efb5..ae01a73480 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ #### Upcoming Changes +* feat(BREAKING): Serialize `Array` return value into output segment in cairo1-run crate: + * Checks that only `PanicResult>` or `Array` can be returned by the program when running with either `--proof_mode` or `--append_return_values`. + * Serializes return values into the output segment under the previous conditions following the format: + * `PanicResult>` -> `[panic_flag, array_len, arr[0], arr[1],.., arr[n]]` + * `` -> `[array_len, arr[0], arr[1],.., arr[n]]` + * feat: Handle `BoundedInt` variant in `serialize_output`, `cairo1-run` crate [#1768](https://github.com/lambdaclass/cairo-vm/pull/1768) * fix: make `OutputBuiltinState` public [#1769](https://github.com/lambdaclass/cairo-vm/pull/1769) diff --git a/cairo1-run/README.md b/cairo1-run/README.md index 9654e91747..c1095dcbf4 100644 --- a/cairo1-run/README.md +++ b/cairo1-run/README.md @@ -61,7 +61,7 @@ The cairo1-run cli supports the following optional arguments: * `--memory_file `: Receives the name of a file and outputs the relocated memory into it -* `--proof_mode`: Runs the program in proof_mode +* `--proof_mode`: Runs the program in proof_mode. Only allows `Array` as return value. * `--air_public_input `: Receives the name of a file and outputs the AIR public inputs into it. Can only be used if proof_mode is also enabled. @@ -69,7 +69,7 @@ The cairo1-run cli supports the following optional arguments: * `--cairo_pie_output `: Receives the name of a file and outputs the Cairo PIE into it. Can only be used if proof_mode, is not enabled. -* `--append_return_values`: Adds extra instructions to the program in order to append the return values to the output builtin's segment. This is the default behaviour for proof_mode. +* `--append_return_values`: Adds extra instructions to the program in order to append the return values to the output builtin's segment. This is the default behaviour for proof_mode. Only allows `Array` as return value. # Running scarb projects diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 1016a7ffc7..8fd85f8dbe 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -110,6 +110,8 @@ impl Default for Cairo1RunConfig<'_> { // Runs a Cairo 1 program // Returns the runner after execution + the return values + the serialized return values (if serialize_output is enabled) +// The return values will contain the memory values just as they appear in the VM, after removing the PanicResult enum (if present). +// Except if either the flag append_return_values or proof_mode are enabled, in which case the return values will consist of its serialized form: [array_len, array[0], array[1], ..., array[array_len -1]] pub fn cairo_run_program( sierra_program: &SierraProgram, cairo_run_config: Cairo1RunConfig, @@ -130,6 +132,16 @@ pub fn cairo_run_program( let initial_gas = 9999999999999_usize; + // Fetch return type data + + let return_type_id = main_func.signature.ret_types.last(); + + if (cairo_run_config.proof_mode || cairo_run_config.append_return_values) + && !check_only_array_felt_return_type(return_type_id, &sierra_program_registry) + { + return Err(Error::IlegalReturnValue); + }; + // Modified entry code to be compatible with custom cairo1 Proof Mode. // This adds code that's needed for dictionaries, adjusts ap for builtin pointers, adds initial gas for the gas builtin if needed, and sets up other necessary code for cairo1 let (entry_code, builtins) = create_entry_code( @@ -141,9 +153,6 @@ pub fn cairo_run_program( &cairo_run_config, )?; - // Fetch return type data - - let return_type_id = main_func.signature.ret_types.last(); let return_type_size = return_type_id .and_then(|id| type_sizes.get(id).cloned()) .unwrap_or_default(); @@ -238,13 +247,25 @@ pub fn cairo_run_program( )?; let serialized_output = if cairo_run_config.serialize_output { - Some(serialize_output( - &return_values, - &mut runner.vm, - return_type_id, - &sierra_program_registry, - &type_sizes, - )) + if cairo_run_config.append_return_values || cairo_run_config.proof_mode { + // The return value is already serialized, so we can just print the array values + let mut output_string = String::from("["); + // Skip array_len + for elem in return_values[1..].iter() { + maybe_add_whitespace(&mut output_string); + output_string.push_str(&elem.to_string()); + } + output_string.push(']'); + Some(output_string) + } else { + Some(serialize_output( + &return_values, + &mut runner.vm, + return_type_id, + &sierra_program_registry, + &type_sizes, + )) + } } else { None }; @@ -382,9 +403,9 @@ After the entry_code (up until calling main) has been ran by the VM: builtin_base_1 return_fp return_pc - (*1+2+3) gap (for output_builtin final ptr) - (*1+2) gap (for builtin_0 final ptr) - (*1+2) gap (for builtin_1 final ptr) + (*1) gap (for output_builtin final ptr) + (*1) gap (for builtin_0 final ptr) + (*1) gap (for builtin_1 final ptr) (*2) segment_arena_ptr (*2) infos_ptr (*2) 0 @@ -416,20 +437,17 @@ fn load_arguments( // This AP correction represents the memory slots taken up by the values created by `create_entry_code`: // These include: // * The builtin bases (not including output) + // * (Only if the output builtin is added) A gap for each builtin's final pointer // * The segment arena values (if present), including: // * segment_arena_ptr // * info_segment_ptr // * 0 - // * (Only if the output builtin is added) A gap for each builtin's final pointer let mut ap_offset = runner.get_program().builtins_len(); if append_output { - ap_offset -= 1; + ap_offset += runner.get_program().builtins_len() - 1; } if got_segment_arena { ap_offset += 3; - if append_output { - ap_offset += runner.get_program().builtins_len(); - } } for arg in cairo_run_config.args { match arg { @@ -497,8 +515,9 @@ fn create_entry_code( .map(|x| x.long_id.generic_id == SegmentArenaType::ID) .unwrap_or_default() }); - if got_segment_arena { - // Allocating local vars to save the builtins for the validations. + if copy_to_output_builtin { + // Leave a gap to write the builtin final pointers + // We write them on a fixed cells relative to the starting FP pointer so we don't lose them after serializing outputs for _ in 0..builtins.len() { casm_build_extend!(ctx, tempvar _local;); } @@ -582,70 +601,117 @@ fn create_entry_code( *var = ctx.add_var(CellExpression::Deref(deref!([ap - offset]))); } - if copy_to_output_builtin { - let output_ptr = output_ptr.unwrap(); - let outputs = (1..(return_type_size + 1)) - .rev() - .map(|i| ctx.add_var(CellExpression::Deref(deref!([ap - i])))) - .collect_vec(); - for output in outputs { - casm_build_extend!(ctx, assert output = *(output_ptr++);); - } - } // Helper to get a variable for a given builtin. // Fails for builtins that will never be present. let get_var = |name: &BuiltinName| match name { - BuiltinName::output => output_ptr.unwrap(), BuiltinName::range_check => builtin_vars[&RangeCheckType::ID], BuiltinName::pedersen => builtin_vars[&PedersenType::ID], BuiltinName::bitwise => builtin_vars[&BitwiseType::ID], BuiltinName::ec_op => builtin_vars[&EcOpType::ID], BuiltinName::poseidon => builtin_vars[&PoseidonType::ID], BuiltinName::segment_arena => builtin_vars[&SegmentArenaType::ID], - BuiltinName::keccak - | BuiltinName::ecdsa - | BuiltinName::range_check96 - | BuiltinName::add_mod - | BuiltinName::mul_mod => unreachable!(), + _ => unreachable!(), }; - if copy_to_output_builtin && got_segment_arena { + if copy_to_output_builtin { // Copying the final builtins into a local variables. for (i, builtin) in builtins.iter().enumerate() { + // Skip output_ptr as we still haven't written into it and this will lead to the wrong size being written + if matches!(builtin, BuiltinName::output) { + continue; + } let var = get_var(builtin); let local = ctx.add_var(CellExpression::Deref(deref!([fp + i.to_i16().unwrap()]))); casm_build_extend!(ctx, assert local = var;); } - let segment_arena_ptr = get_var(&BuiltinName::segment_arena); - // Validating the segment arena's segments are one after the other. + // Deserialize return values into output segment + let output_ptr = output_ptr.unwrap(); + let outputs = (1..(return_type_size + 1)) + .rev() + .map(|i| ctx.add_var(CellExpression::Deref(deref!([ap - i])))) + .collect_vec(); + let (array_start_ptr, array_end_ptr) = if is_panic_result(signature.ret_types.last()) { + // Write panic flag value + let panic_flag = outputs[0]; + casm_build_extend! {ctx, + assert panic_flag = *(output_ptr++); + }; + // If the run did panic, these will point to the panic data + (outputs[1], outputs[2]) + } else { + (outputs[0], outputs[1]) + }; casm_build_extend! {ctx, - tempvar n_segments = segment_arena_ptr[-2]; - tempvar n_finalized = segment_arena_ptr[-1]; - assert n_segments = n_finalized; - jump STILL_LEFT_PRE if n_segments != 0; - rescope{}; - jump DONE_VALIDATION; - STILL_LEFT_PRE: - const one = 1; - tempvar infos = segment_arena_ptr[-3]; - tempvar remaining_segments = n_segments - one; - rescope{infos = infos, remaining_segments = remaining_segments}; - LOOP_START: - jump STILL_LEFT_LOOP if remaining_segments != 0; - rescope{}; - jump DONE_VALIDATION; - STILL_LEFT_LOOP: + // Calculate size of array and write it into the output segment + tempvar array_size = array_end_ptr - array_start_ptr; + assert array_size = *(output_ptr++); + // Create loop variables + tempvar remaining_elements = array_size; + tempvar array_ptr = array_start_ptr; + tempvar write_ptr = output_ptr; + // Enter copying loop + rescope{remaining_elements = remaining_elements, array_ptr = array_ptr, write_ptr = write_ptr}; + jump CopyArray if remaining_elements != 0; + jump End; + + // Main Loop + CopyArray: + #{steps = 0;} + // Write array value into output segment + tempvar val = *(array_ptr++); + assert val = *(write_ptr++); const one = 1; - const three = 3; - tempvar prev_end = infos[1]; - tempvar curr_start = infos[3]; - assert curr_start = prev_end + one; - tempvar next_infos = infos + three; - tempvar next_remaining_segments = remaining_segments - one; - rescope{infos = next_infos, remaining_segments = next_remaining_segments}; - #{ steps = 0; } - jump LOOP_START; - DONE_VALIDATION: + // Create loop variables + tempvar new_remaining_elements = remaining_elements - one; + tempvar new_array_ptr = array_ptr; + tempvar new_write_ptr = write_ptr; + // Continue the loop + rescope{remaining_elements = new_remaining_elements, array_ptr = new_array_ptr, write_ptr = new_write_ptr}; + jump CopyArray if remaining_elements != 0; + + End: }; + // After we are done writing into the output segment, we can write the final output_ptr into locals: + // The last instruction will write the final output ptr so we can find it in [ap - 1] + let output_ptr = ctx.add_var(CellExpression::Deref(deref!([ap - 1]))); + let local = ctx.add_var(CellExpression::Deref(deref!([fp]))); + casm_build_extend!(ctx, assert local = output_ptr;); + + if got_segment_arena { + // We re-scoped when serializing the output so we have to create a var for the segment arena + // len(builtins) + len(builtins - output) + segment_arena_ptr + info_segment + 0 + let off = 2 * builtins.len() + 2; + let segment_arena_ptr = ctx.add_var(CellExpression::Deref(deref!([fp + off as i16]))); + // Validating the segment arena's segments are one after the other. + casm_build_extend! {ctx, + tempvar n_segments = segment_arena_ptr[-2]; + tempvar n_finalized = segment_arena_ptr[-1]; + assert n_segments = n_finalized; + jump STILL_LEFT_PRE if n_segments != 0; + rescope{}; + jump DONE_VALIDATION; + STILL_LEFT_PRE: + const one = 1; + tempvar infos = segment_arena_ptr[-3]; + tempvar remaining_segments = n_segments - one; + rescope{infos = infos, remaining_segments = remaining_segments}; + LOOP_START: + jump STILL_LEFT_LOOP if remaining_segments != 0; + rescope{}; + jump DONE_VALIDATION; + STILL_LEFT_LOOP: + const one = 1; + const three = 3; + tempvar prev_end = infos[1]; + tempvar curr_start = infos[3]; + assert curr_start = prev_end + one; + tempvar next_infos = infos + three; + tempvar next_remaining_segments = remaining_segments - one; + rescope{infos = next_infos, remaining_segments = next_remaining_segments}; + #{ steps = 0; } + jump LOOP_START; + DONE_VALIDATION: + }; + } // Copying the final builtins from locals into the top of the stack. for i in 0..builtins.len().to_i16().unwrap() { let local = ctx.add_var(CellExpression::Deref(deref!([fp + i]))); @@ -738,35 +804,86 @@ fn get_function_builtins( (builtins, builtin_offset) } -// Returns the size of the T type in PanicResult::Ok(T) if applicable -// Returns None if the return_type_id is not a PanicResult -fn result_inner_type_size( +// Checks that the return type is either an Array or a PanicResult> type +fn check_only_array_felt_return_type( return_type_id: Option<&ConcreteTypeId>, sierra_program_registry: &ProgramRegistry, - type_sizes: &UnorderedHashMap, -) -> Option { - if return_type_id - .and_then(|id| { +) -> bool { + if return_type_id.is_none() { + return false; + }; + // Unwrap PanicResult (if appicable) + let return_type = + if let Some(return_type) = result_inner_type(return_type_id, sierra_program_registry) { + return_type + } else { + return_type_id.unwrap() + }; + let return_type = sierra_program_registry.get_type(return_type).unwrap(); + // Check that the resulting type is an Array + match return_type { + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Array(info) => { + let inner_ty = sierra_program_registry.get_type(&info.ty).unwrap(); + matches!( + inner_ty, + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Felt252(_) + ) + } + _ => false, + } +} + +fn is_panic_result(return_type_id: Option<&ConcreteTypeId>) -> bool { + return_type_id + .map(|id| { id.debug_name .as_ref() - .map(|name| name.starts_with("core::panics::PanicResult::")) + .is_some_and(|name| name.starts_with("core::panics::PanicResult::")) }) .unwrap_or_default() - { +} + +// Returns the T type in PanicResult::Ok(T) if applicable +// Returns None if the return_type_id is not a PanicResult +fn result_inner_type<'a>( + return_type_id: Option<&'a ConcreteTypeId>, + sierra_program_registry: &'a ProgramRegistry, +) -> Option<&'a ConcreteTypeId> { + if is_panic_result(return_type_id) { let return_type_info = get_info(sierra_program_registry, return_type_id.as_ref().unwrap()).unwrap(); // We already know info.long_id.generic_args[0] contains the Panic variant let inner_args = &return_type_info.long_id.generic_args[1]; - let inner_type = match inner_args { - GenericArg::Type(type_id) => type_id, - _ => unreachable!(), + let inner_type = { + let inner_type = match inner_args { + GenericArg::Type(type_id) => type_id, + _ => unreachable!(), + }; + // The inner type contains a single-element tuple so we need to get rid of it too + let inner_type_info = get_info(sierra_program_registry, inner_type).unwrap(); + match &inner_type_info.long_id.generic_args[1] { + GenericArg::Type(type_id) => type_id, + _ => unreachable!(), + } }; - type_sizes.get(inner_type).copied() + + Some(inner_type) } else { None } } +// Returns the size of the T type in PanicResult::Ok(T) if applicable +// Returns None if the return_type_id is not a PanicResult +fn result_inner_type_size( + return_type_id: Option<&ConcreteTypeId>, + sierra_program_registry: &ProgramRegistry, + type_sizes: &UnorderedHashMap, +) -> Option { + result_inner_type(return_type_id, sierra_program_registry) + .and_then(|ty| type_sizes.get(ty).copied()) +} + fn fetch_return_values( return_type_size: i16, result_inner_type_size: Option, @@ -774,18 +891,37 @@ fn fetch_return_values( builtin_count: i16, fetch_from_output: bool, ) -> Result, Error> { - let mut return_values = if fetch_from_output { - let output_builtin_end = vm - .get_relocatable((vm.get_ap() + (-builtin_count as i32)).unwrap()) - .unwrap(); - let output_builtin_base = (output_builtin_end + (-return_type_size as i32)).unwrap(); - vm.get_continuous_range(output_builtin_base, return_type_size.into_or_panic())? - } else { - vm.get_continuous_range( - (vm.get_ap() - (return_type_size + builtin_count) as usize).unwrap(), - return_type_size as usize, - )? - }; + if fetch_from_output { + // In this case we will find the serialized return value in the format: + // [*panic_flag, array_len, array[0], array[1],..., array[array_len-1]] + // *: If the return value is a PanicResult + + // Output Builtin will always be on segment 2 + let return_values = + vm.get_continuous_range((2, 0).into(), vm.get_segment_size(2).unwrap())?; + // This means that the return value is not a PanicResult + return if result_inner_type_size.is_none() { + Ok(return_values) + // The return value is a PanicResult so we need to check the panic_flag + } else { + // PanicResult::Err + if return_values.first() != Some(&MaybeRelocatable::from(0)) { + return Err(Error::RunPanic( + return_values[1..] + .iter() + .map(|mr| mr.get_int().unwrap_or_default()) + .collect_vec(), + )); + // PanicResult::Ok + } else { + Ok(return_values[1..].to_vec()) + } + }; + } + let mut return_values = vm.get_continuous_range( + (vm.get_ap() - (return_type_size + builtin_count) as usize).unwrap(), + return_type_size as usize, + )?; // Handle PanicResult (we already checked if the type is a PanicResult when fetching the inner type size) if let Some(inner_type_size) = result_inner_type_size { // Check the failure flag (aka first return value) @@ -1279,23 +1415,23 @@ mod tests { } #[rstest] - #[case("../cairo_programs/cairo-1-programs/array_append.cairo")] - #[case("../cairo_programs/cairo-1-programs/array_get.cairo")] - #[case("../cairo_programs/cairo-1-programs/dictionaries.cairo")] - #[case("../cairo_programs/cairo-1-programs/enum_flow.cairo")] - #[case("../cairo_programs/cairo-1-programs/enum_match.cairo")] - #[case("../cairo_programs/cairo-1-programs/factorial.cairo")] - #[case("../cairo_programs/cairo-1-programs/fibonacci.cairo")] - #[case("../cairo_programs/cairo-1-programs/hello.cairo")] - #[case("../cairo_programs/cairo-1-programs/pedersen_example.cairo")] - #[case("../cairo_programs/cairo-1-programs/poseidon.cairo")] - #[case("../cairo_programs/cairo-1-programs/print.cairo")] - #[case("../cairo_programs/cairo-1-programs/array_append.cairo")] - #[case("../cairo_programs/cairo-1-programs/recursion.cairo")] - #[case("../cairo_programs/cairo-1-programs/sample.cairo")] - #[case("../cairo_programs/cairo-1-programs/simple_struct.cairo")] - #[case("../cairo_programs/cairo-1-programs/simple.cairo")] - #[case("../cairo_programs/cairo-1-programs/struct_span_return.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/array_append.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/array_get.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/dictionaries.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/enum_flow.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/enum_match.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/factorial.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/fibonacci.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/hello.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/pedersen_example.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/poseidon.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/print.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/array_append.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/recursion.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/sample.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/simple_struct.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/simple.cairo")] + #[case("../cairo_programs/cairo-1-programs/serialized_output/struct_span_return.cairo")] fn check_append_ret_values_to_output_segment( #[case] filename: &str, #[values(true, false)] proof_mode: bool, @@ -1316,7 +1452,7 @@ mod tests { // When the return type is a PanicResult, we remove the panic wrapper when returning the ret values // And handle the panics returning an error, so we need to add it here let return_values = if main_hash_panic_result(&sierra_program) { - let mut rv = vec![Felt252::ZERO.into(), Felt252::ZERO.into()]; + let mut rv = vec![Felt252::ZERO.into()]; rv.extend_from_slice(&return_values); rv } else { diff --git a/cairo1-run/src/error.rs b/cairo1-run/src/error.rs index 7629b18001..f3a716cb1f 100644 --- a/cairo1-run/src/error.rs +++ b/cairo1-run/src/error.rs @@ -59,4 +59,6 @@ pub enum Error { param_index: usize, arg_index: usize, }, + #[error("Only programs returning `Array` can be currently proven. Try serializing the final values before returning them")] + IlegalReturnValue, } diff --git a/cairo1-run/src/main.rs b/cairo1-run/src/main.rs index 0657eaa38e..5e9ab9839b 100644 --- a/cairo1-run/src/main.rs +++ b/cairo1-run/src/main.rs @@ -282,67 +282,104 @@ mod tests { #[case( "ecdsa_recover.cairo", "3490001189944926769628658346285649224182856084131963744896357527096042836716", + "[3490001189944926769628658346285649224182856084131963744896357527096042836716]", None )] - #[case("tensor_new.cairo", "[1 2] [1 false 1 true]", None)] - #[case("bytes31_ret.cairo", "123", None)] - #[case("null_ret.cairo", "null", None)] - #[case("felt_dict_squash.cairo", "{66675: [4 5 6] 66676: [1 2 3]}", None)] - #[case("dict_with_struct.cairo", "{0: 1 true 1: 1 false 2: 1 true}", None)] - #[case("nullable_box_vec.cairo", "{0: 10 1: 20 2: 30} 3", None)] - #[case("array_integer_tuple.cairo", "[1] 1", None)] - #[case("felt_dict.cairo", "{66675: [8 9 10 11] 66676: [1 2 3]}", None)] - #[case("felt_span.cairo", "[8 9 10 11]", None)] - #[case("nullable_dict.cairo", "", None)] - #[case("struct_span_return.cairo", "[[4 3] [2 1]]", None)] - #[case("null_ret.cairo", "null", None)] - #[case("with_input/tensor.cairo", "1", Some("[2 2] [1 2 3 4]"))] - #[case("with_input/array_input_sum.cairo", "12", Some("2 [1 2 3 4] 0 [9 8]"))] - #[case("with_input/array_length.cairo", "5", Some("[1 2 3 4] [1]"))] - #[case("with_input/array_length.cairo", "4", Some("[1 2 3 4] []"))] - #[case("with_input/branching.cairo", "0", Some("17"))] - #[case("with_input/branching.cairo", "1", Some("0"))] - #[case("dictionaries.cairo", "1024", None)] - #[case("simple_struct.cairo", "100", None)] - #[case("simple.cairo", "true", None)] + #[case( + "tensor_new.cairo", + "[1 2] [1 false 1 true]", // Struct { span [1 2] span [struct {1 false} struct {1 true}]} + "[2 1 2 2 1 0 1 1]", // len: 2 [1 2] len 2: [{1 0} {1 0}] + None + )] + #[case("bytes31_ret.cairo", "123", "[123]", None)] + #[case("null_ret.cairo", "null", "[]", None)] + #[case( + "felt_dict_squash.cairo", + "{66675: [4 5 6] 66676: [1 2 3]}", + "[66675 3 4 5 6 66676 3 1 2 3]", + None + )] + #[case( + "dict_with_struct.cairo", + "{0: 1 true 1: 1 false 2: 1 true}", + "[0 1 1 1 1 0 2 1 1]", + None + )] + #[case( + "nullable_box_vec.cairo", + "{0: 10 1: 20 2: 30} 3", + "[0 10 1 20 2 30 3]", + None + )] + #[case("array_integer_tuple.cairo", "[1] 1", "[1 1 1]", None)] + #[case( + "felt_dict.cairo", + "{66675: [8 9 10 11] 66676: [1 2 3]}", + "[66675 4 8 9 10 11 66676 3 1 2 3]", + None + )] + #[case("felt_span.cairo", "[8 9 10 11]", "[4 8 9 10 11]", None)] + #[case("nullable_dict.cairo", "", "[]", None)] + #[case("struct_span_return.cairo", "[[4 3] [2 1]]", "[2 2 4 3 2 2 1]", None)] + #[case("null_ret.cairo", "null", "[]", None)] + #[case("with_input/tensor.cairo", "1", "[1]", Some("[2 2] [1 2 3 4]"))] + #[case( + "with_input/array_input_sum.cairo", + "12", + "[12]", + Some("2 [1 2 3 4] 0 [9 8]") + )] + #[case("with_input/array_length.cairo", "5", "[5]", Some("[1 2 3 4] [1]"))] + #[case("with_input/array_length.cairo", "4", "[4]", Some("[1 2 3 4] []"))] + #[case("with_input/branching.cairo", "0", "[0]", Some("17"))] + #[case("with_input/branching.cairo", "1", "[1]", Some("0"))] + #[case("dictionaries.cairo", "1024", "[1024]", None)] + #[case("simple_struct.cairo", "100", "[100]", None)] + #[case("simple.cairo", "true", "[1]", None)] #[case( "pedersen_example.cairo", "1089549915800264549621536909767699778745926517555586332772759280702396009108", + "[1089549915800264549621536909767699778745926517555586332772759280702396009108]", None )] #[case( "poseidon_pedersen.cairo", "1036257840396636296853154602823055519264738423488122322497453114874087006398", + "[1036257840396636296853154602823055519264738423488122322497453114874087006398]", None )] #[case( "poseidon.cairo", "1099385018355113290651252669115094675591288647745213771718157553170111442461", + "[1099385018355113290651252669115094675591288647745213771718157553170111442461]", None )] - #[case("sample.cairo", "5050", None)] + #[case("sample.cairo", "5050", "[5050]", None)] #[case( "recursion.cairo", "1154076154663935037074198317650845438095734251249125412074882362667803016453", + "[1154076154663935037074198317650845438095734251249125412074882362667803016453]", None )] - #[case("print.cairo", "", None)] - #[case("ops.cairo", "6", None)] - #[case("hello.cairo", "1234", None)] + #[case("print.cairo", "", "[]", None)] + #[case("ops.cairo", "6", "[6]", None)] + #[case("hello.cairo", "1234", "[1 1234]", None)] #[case( "enum_match.cairo", "10 3618502788666131213697322783095070105623107215331596699973092056135872020471", + "[10 3618502788666131213697322783095070105623107215331596699973092056135872020471]", None )] - #[case("enum_flow.cairo", "300", None)] - #[case("array_get.cairo", "3", None)] - #[case("bitwise.cairo", "11772", None)] - #[case("factorial.cairo", "3628800", None)] - #[case("fibonacci.cairo", "89", None)] + #[case("enum_flow.cairo", "300", "[300]", None)] + #[case("array_get.cairo", "3", "[3]", None)] + #[case("bitwise.cairo", "11772", "[11772]", None)] + #[case("factorial.cairo", "3628800", "[3628800]", None)] + #[case("fibonacci.cairo", "89", "[89]", None)] fn test_run_progarm( #[case] program: &str, #[case] expected_output: &str, + #[case] expected_serialized_output: &str, #[case] inputs: Option<&str>, #[values( &["--cairo_pie_output", "/dev/null"], // Non proof-mode @@ -361,7 +398,18 @@ mod tests { "all_cairo", ]; let mut args = vec!["cairo1-run"]; - let filename = format!("../cairo_programs/cairo-1-programs/{}", program); + let has_serialized_output = extra_flags + .iter() + .any(|flag| flag == &"--append_return_values" || flag == &"--proof_mode"); + let filename = if has_serialized_output { + format!( + "../cairo_programs/cairo-1-programs/serialized_output/{}", + program + ) + } else { + format!("../cairo_programs/cairo-1-programs/{}", program) + }; + args.push(&filename); args.extend_from_slice(common_flags); args.extend_from_slice(extra_flags); @@ -369,12 +417,17 @@ mod tests { args.extend_from_slice(&["--args", inputs]) } let args = args.iter().cloned().map(String::from); + let expected_output = if has_serialized_output { + expected_serialized_output + } else { + expected_output + }; assert_matches!(run(args), Ok(Some(res)) if res == expected_output, "Program {} failed with flags {}", program, extra_flags.concat()); } #[rstest] #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/with_input/branching.cairo", "--layout", "all_cairo", "--cairo_pie_output", "/dev/null"].as_slice())] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/with_input/branching.cairo", "--layout", "all_cairo", "--proof_mode"].as_slice())] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/serialized_output/with_input/branching.cairo", "--layout", "all_cairo", "--proof_mode"].as_slice())] fn test_run_branching_no_args(#[case] args: &[&str]) { let args = args.iter().cloned().map(String::from); assert_matches!(run(args), Err(Error::ArgumentsSizeMismatch { expected, actual }) if expected == 1 && actual == 0); @@ -382,7 +435,7 @@ mod tests { #[rstest] #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/with_input/branching.cairo", "--layout", "all_cairo","--args", "1 2 3"].as_slice())] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/with_input/branching.cairo", "--layout", "all_cairo", "--proof_mode", "--args", "1 2 3"].as_slice())] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/serialized_output/with_input/branching.cairo", "--layout", "all_cairo", "--proof_mode", "--args", "1 2 3"].as_slice())] fn test_run_branching_too_many_args(#[case] args: &[&str]) { let args = args.iter().cloned().map(String::from); assert_matches!(run(args), Err(Error::ArgumentsSizeMismatch { expected, actual }) if expected == 1 && actual == 3); diff --git a/cairo_programs/cairo-1-programs/dict_with_struct.cairo b/cairo_programs/cairo-1-programs/dict_with_struct.cairo index c085198888..bdb19157b8 100644 --- a/cairo_programs/cairo-1-programs/dict_with_struct.cairo +++ b/cairo_programs/cairo-1-programs/dict_with_struct.cairo @@ -23,6 +23,6 @@ fn main() -> SquashedFelt252Dict> { d.squash() } -// TODO: remove this temporary fixed once fixed in cairo +// TODO: remove this temporary fix once fixed in cairo #[inline(never)] fn identity(t: T) -> T { t } diff --git a/cairo_programs/cairo-1-programs/nullable_box_vec.cairo b/cairo_programs/cairo-1-programs/nullable_box_vec.cairo index a26704bfd3..7143e37099 100644 --- a/cairo_programs/cairo-1-programs/nullable_box_vec.cairo +++ b/cairo_programs/cairo-1-programs/nullable_box_vec.cairo @@ -18,6 +18,6 @@ fn main() -> NullableVec { } } -// TODO: remove this temporary fixed once fixed in cairo +// TODO: remove this temporary fix once fixed in cairo #[inline(never)] fn identity(t: T) -> T { t } diff --git a/cairo_programs/cairo-1-programs/serialized_output/array_append.cairo b/cairo_programs/cairo-1-programs/serialized_output/array_append.cairo new file mode 100644 index 0000000000..5829942616 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/array_append.cairo @@ -0,0 +1,11 @@ +use array::ArrayTrait; + +fn main() -> Array { + let mut numbers = ArrayTrait::new(); + numbers.append(4_u32); + numbers.append(2_u32); + let _x = numbers.pop_front(); + let mut output: Array = ArrayTrait::new(); + numbers.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/array_get.cairo b/cairo_programs/cairo-1-programs/serialized_output/array_get.cairo new file mode 100644 index 0000000000..3eb164c951 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/array_get.cairo @@ -0,0 +1,13 @@ +use array::ArrayTrait; + +fn main() -> Array { + let mut numbers = ArrayTrait::new(); + numbers.append(4_u32); + numbers.append(3_u32); + numbers.append(2_u32); + numbers.append(1_u32); + let res = *numbers.at(1); + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/array_integer_tuple.cairo b/cairo_programs/cairo-1-programs/serialized_output/array_integer_tuple.cairo new file mode 100644 index 0000000000..2c843c2853 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/array_integer_tuple.cairo @@ -0,0 +1,12 @@ +use core::array::ArrayTrait; + + +fn main() -> Array { + let mut numbers = ArrayTrait::new(); + numbers.append(1); + + let res = (numbers, 1); + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/bitwise.cairo b/cairo_programs/cairo-1-programs/serialized_output/bitwise.cairo new file mode 100644 index 0000000000..8a15dce87b --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/bitwise.cairo @@ -0,0 +1,13 @@ +fn main() -> Array { + let a = 1234_u128; + let b = 5678_u128; + + let c0 = a & b; + let c1 = a ^ b; + let c2 = a | b; + + let c3 = c0 + c1 + c2; + let mut output: Array = ArrayTrait::new(); + c3.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/bytes31_ret.cairo b/cairo_programs/cairo-1-programs/serialized_output/bytes31_ret.cairo new file mode 100644 index 0000000000..fb7256eadc --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/bytes31_ret.cairo @@ -0,0 +1,7 @@ +fn main() -> Array { + let a: u128 = 123; + let b: bytes31 = a.into(); + let mut output: Array = ArrayTrait::new(); + b.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/dict_with_struct.cairo b/cairo_programs/cairo-1-programs/serialized_output/dict_with_struct.cairo new file mode 100644 index 0000000000..f7f3cca3f8 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/dict_with_struct.cairo @@ -0,0 +1,45 @@ +use core::nullable::{nullable_from_box, match_nullable, FromNullableResult}; + + +#[derive(Drop, Copy, Serde)] +struct FP16x16 { + mag: u32, + sign: bool +} + +fn main() -> Array { + // Create the dictionary + let mut d: Felt252Dict> = Default::default(); + + let box_a = BoxTrait::new(identity(FP16x16 { mag: 1, sign: false })); + let box_b = BoxTrait::new(identity(FP16x16 { mag: 1, sign: true })); + let box_c = BoxTrait::new(identity(FP16x16 { mag: 1, sign: true })); + + // Insert it as a `Span` + d.insert(0, nullable_from_box(box_c)); + d.insert(1, nullable_from_box(box_a)); + d.insert(2, nullable_from_box(box_b)); + + // We can't implement Serde for a Felt252Dict due to mutability requirements + // So we will serialize the dict explicitely + let mut output: Array = ArrayTrait::new(); + // Serialize entry 0 + 0.serialize(ref output); + let array_0 = d.get(0).deref(); + array_0.serialize(ref output); + // Serialize entry 1 + 1.serialize(ref output); + let array_1 = d.get(1).deref(); + array_1.serialize(ref output); + // Serialize entry 2 + 2.serialize(ref output); + let array_2 = d.get(2).deref(); + array_2.serialize(ref output); + // Squash after serializing + d.squash(); + output +} + +// TODO: remove this temporary fix once fixed in cairo +#[inline(never)] +fn identity(t: T) -> T { t } diff --git a/cairo_programs/cairo-1-programs/serialized_output/dictionaries.cairo b/cairo_programs/cairo-1-programs/serialized_output/dictionaries.cairo new file mode 100644 index 0000000000..e5cb83c840 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/dictionaries.cairo @@ -0,0 +1,19 @@ +use dict::Felt252DictTrait; + +fn main() -> Array { + let mut dict_u8 = felt252_dict_new::(); + let mut dict_felt = felt252_dict_new::(); + let _dict_felt2 = felt252_dict_new::(); + + dict_u8.insert(10, 110); + dict_u8.insert(10, 110); + + let _val10 = dict_u8[10]; // 110 + let _val11 = dict_felt[11]; // 0 + dict_felt.insert(11, 1024); + let res = dict_felt[11]; // 1024 + + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/ecdsa_recover.cairo b/cairo_programs/cairo-1-programs/serialized_output/ecdsa_recover.cairo new file mode 100644 index 0000000000..93b6448722 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/ecdsa_recover.cairo @@ -0,0 +1,10 @@ + +fn main() -> Array { + let message_hash: felt252 = 0x503f4bea29baee10b22a7f10bdc82dda071c977c1f25b8f3973d34e6b03b2c; + let signature_r: felt252 = 0xbe96d72eb4f94078192c2e84d5230cde2a70f4b45c8797e2c907acff5060bb; + let signature_s: felt252 = 0x677ae6bba6daf00d2631fab14c8acf24be6579f9d9e98f67aa7f2770e57a1f5; + let res = core::ecdsa::recover_public_key(:message_hash, :signature_r, :signature_s, y_parity: false).unwrap(); + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/enum_flow.cairo b/cairo_programs/cairo-1-programs/serialized_output/enum_flow.cairo new file mode 100644 index 0000000000..b4fefc74f1 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/enum_flow.cairo @@ -0,0 +1,62 @@ +enum MyEnumShort { + a: felt252, + b: felt252 +} +enum MyEnumLong { + a: felt252, + b: felt252, + c: felt252 +} +enum MyEnumGeneric { + a: T, + b: S, + c: T +} + +impl MyEnumGenericDrop of Drop>; + +fn main() -> Array { + let es0 = MyEnumShort::a(10); + match_short(es0); + let es1 = MyEnumShort::b(11); + match_short(es1); + let el0 = MyEnumLong::a(20); + match_long(el0); + let el1 = MyEnumLong::b(21); + match_long(el1); + let el2 = MyEnumLong::c(22); + match_long(el2); + let _eg1: MyEnumGeneric<(), felt252> = MyEnumGeneric::<(), felt252>::a(30); + let _eg2: MyEnumGeneric<(), felt252> = MyEnumGeneric::<(), felt252>::b(()); + let _eg3: MyEnumGeneric<(), felt252> = MyEnumGeneric::<(), felt252>::c(32); + + let res = 300; + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} + +fn match_short(e: MyEnumShort) -> felt252 { + match e { + MyEnumShort::a(x) => { + x + }, + MyEnumShort::b(x) => { + x + }, + } +} + +fn match_long(e: MyEnumLong) -> felt252 { + match e { + MyEnumLong::a(x) => { + x + }, + MyEnumLong::b(x) => { + x + }, + MyEnumLong::c(x) => { + x + }, + } +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/enum_match.cairo b/cairo_programs/cairo-1-programs/serialized_output/enum_match.cairo new file mode 100644 index 0000000000..9ac1f2bee1 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/enum_match.cairo @@ -0,0 +1,22 @@ +enum MyEnum { + A: felt252, + B: (felt252, felt252), +} + +fn get_value(e: MyEnum) -> felt252 { + match e { + MyEnum::A(a) => a, + MyEnum::B((x,y)) => x - y, + } +} + +fn main() -> Array { + let res = ( + get_value(MyEnum::A(10)), + get_value(MyEnum::B((20, 30))), + ); + + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/factorial.cairo b/cairo_programs/cairo-1-programs/serialized_output/factorial.cairo new file mode 100644 index 0000000000..2e07282981 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/factorial.cairo @@ -0,0 +1,17 @@ +use core::felt252; + +fn main() -> Array { + let n = 10; + let result = factorial(n); + + let mut output: Array = ArrayTrait::new(); + result.serialize(ref output); + output +} + +fn factorial(n: felt252) -> felt252 { + match n { + 0 => 1, + _ => n * factorial(n - 1), + } +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/felt_dict.cairo b/cairo_programs/cairo-1-programs/serialized_output/felt_dict.cairo new file mode 100644 index 0000000000..997e0ed880 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/felt_dict.cairo @@ -0,0 +1,29 @@ +use core::nullable::{nullable_from_box, match_nullable, FromNullableResult}; + +fn main() -> Array { + // Create the dictionary + let mut d: Felt252Dict>> = Default::default(); + + // Create the array to insert + let a = array![8, 9, 10, 11]; + let b = array![1, 2, 3]; + + // Insert it as a `Span` + d.insert(66675, nullable_from_box(BoxTrait::new(a.span()))); + d.insert(66676, nullable_from_box(BoxTrait::new(b.span()))); + + // We can't implement Serde for a Felt252Dict due to mutability requirements + // So we will serialize the dict explicitely + let mut output: Array = ArrayTrait::new(); + // Serialize entry A + let key_a = 66675; + key_a.serialize(ref output); + let array_a = d.get(key_a).deref(); + array_a.serialize(ref output); + // Serialize entry B + let key_b = 66676; + key_b.serialize(ref output); + let array_b = d.get(key_b).deref(); + array_b.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/felt_dict_squash.cairo b/cairo_programs/cairo-1-programs/serialized_output/felt_dict_squash.cairo new file mode 100644 index 0000000000..a51d68e8dc --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/felt_dict_squash.cairo @@ -0,0 +1,34 @@ +use core::nullable::{nullable_from_box, match_nullable, FromNullableResult}; +use core::dict::Felt252DictEntry; + +fn main() -> Array { + // Create the dictionary + let mut d: Felt252Dict>> = Default::default(); + + // Create the array to insert + let a = array![8, 9, 10, 11]; + let b = array![1, 2, 3]; + let c = array![4, 5, 6]; + + // Insert it as a `Span` + d.insert(66675, nullable_from_box(BoxTrait::new(a.span()))); + d.insert(66676, nullable_from_box(BoxTrait::new(b.span()))); + d.insert(66675, nullable_from_box(BoxTrait::new(c.span()))); + + // We can't implement Serde for a Felt252Dict due to mutability requirements + // So we will serialize the dict explicitely + let mut output: Array = ArrayTrait::new(); + // Serialize entry A + let key_a = 66675; + key_a.serialize(ref output); + let array_a = d.get(key_a).deref(); + array_a.serialize(ref output); + // Serialize entry B + let key_b = 66676; + key_b.serialize(ref output); + let array_b = d.get(key_b).deref(); + array_b.serialize(ref output); + // Squash after serializing + d.squash(); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/felt_span.cairo b/cairo_programs/cairo-1-programs/serialized_output/felt_span.cairo new file mode 100644 index 0000000000..12e83abcc6 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/felt_span.cairo @@ -0,0 +1,11 @@ +use core::nullable::{nullable_from_box, match_nullable, FromNullableResult}; + +fn main() -> Array { + let a = array![8, 9, 10, 11]; + let _res = nullable_from_box(BoxTrait::new(a.span())); + + let mut output: Array = ArrayTrait::new(); + // Nullable doesn't implement Serde, so we serialize the array instead + a.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/fibonacci.cairo b/cairo_programs/cairo-1-programs/serialized_output/fibonacci.cairo new file mode 100644 index 0000000000..3b55898999 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/fibonacci.cairo @@ -0,0 +1,17 @@ +use core::felt252; + +fn main() -> Array { + let n = 10; + let result = fib(1, 1, n); + + let mut output: Array = ArrayTrait::new(); + result.serialize(ref output); + output +} + +fn fib(a: felt252, b: felt252, n: felt252) -> felt252 { + match n { + 0 => a, + _ => fib(b, a + b, n - 1), + } +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/hello.cairo b/cairo_programs/cairo-1-programs/serialized_output/hello.cairo new file mode 100644 index 0000000000..6e6605a690 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/hello.cairo @@ -0,0 +1,15 @@ +use core::serde::Serde; +use core::option::OptionTrait; + +#[derive(Drop, Serde)] +enum MyEnum { + A: (), + B: felt252, +} + +fn main() -> Array { + let res = MyEnum::B(1234); + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/null_ret.cairo b/cairo_programs/cairo-1-programs/serialized_output/null_ret.cairo new file mode 100644 index 0000000000..10d60e809e --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/null_ret.cairo @@ -0,0 +1,7 @@ +fn main() -> Array { + let _res: Nullable = null(); + let mut output: Array = ArrayTrait::new(); + // Nullable doesn't implement Serde + ().serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/nullable_box_vec.cairo b/cairo_programs/cairo-1-programs/serialized_output/nullable_box_vec.cairo new file mode 100644 index 0000000000..0bb7aebf84 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/nullable_box_vec.cairo @@ -0,0 +1,43 @@ +#[derive(Destruct)] +struct NullableVec { + items: Felt252Dict>>, + len: usize, +} + +fn main() -> Array { + let mut d: Felt252Dict>> = Default::default(); + + // Populate the dictionary + d.insert(0, nullable_from_box(BoxTrait::new(BoxTrait::new(identity(10))))); + d.insert(1, nullable_from_box(BoxTrait::new(BoxTrait::new(identity(20))))); + d.insert(2, nullable_from_box(BoxTrait::new(BoxTrait::new(identity(30))))); + + // Return NullableVec + let mut res = NullableVec { + items: d, + len: 3, + }; + + let mut output: Array = ArrayTrait::new(); + // Custom Serialization + // Serialize items field + // Serialize entry 0 + 0.serialize(ref output); + let val0 = res.items.get(0).deref().unbox(); + val0.serialize(ref output); + // Serialize entry 1 + 1.serialize(ref output); + let val1 = res.items.get(1).deref().unbox(); + val1.serialize(ref output); + // Serialize entry 2 + 2.serialize(ref output); + let val2 = res.items.get(2).deref().unbox(); + val2.serialize(ref output); + // Serialize len field + res.len.serialize(ref output); + output +} + +// TODO: remove this temporary fix once fixed in cairo +#[inline(never)] +fn identity(t: T) -> T { t } diff --git a/cairo_programs/cairo-1-programs/serialized_output/nullable_dict.cairo b/cairo_programs/cairo-1-programs/serialized_output/nullable_dict.cairo new file mode 100644 index 0000000000..b07b7bff4f --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/nullable_dict.cairo @@ -0,0 +1,18 @@ +use core::nullable::{nullable_from_box, match_nullable, FromNullableResult}; + +fn main() -> Array{ + // Create the dictionary + let mut d: Felt252Dict>> = Default::default(); + + // Create the array to insert + let a = array![8, 9, 10]; + + // Insert it as a `Span` + d.insert(0, nullable_from_box(BoxTrait::new(a.span()))); + + let mut output: Array = ArrayTrait::new(); + // Felt252Dict doesn't implement Serde + ().serialize(ref output); + output + +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/ops.cairo b/cairo_programs/cairo-1-programs/serialized_output/ops.cairo new file mode 100644 index 0000000000..6bcd30007d --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/ops.cairo @@ -0,0 +1,6 @@ +fn main() -> Array { + let res = 2_u32 + 4_u32; + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/pedersen_example.cairo b/cairo_programs/cairo-1-programs/serialized_output/pedersen_example.cairo new file mode 100644 index 0000000000..a9fb07874b --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/pedersen_example.cairo @@ -0,0 +1,8 @@ +use core::pedersen::pedersen; + +fn main() -> Array { + let res = pedersen(1, 0); + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/poseidon.cairo b/cairo_programs/cairo-1-programs/serialized_output/poseidon.cairo new file mode 100644 index 0000000000..db6f67d2f6 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/poseidon.cairo @@ -0,0 +1,42 @@ +use core::felt252; +use array::ArrayTrait; +use array::SpanTrait; +use core::hash::HashStateTrait; +use core::poseidon::{hades_permutation, HashState}; + + +fn main() -> Array { + let mut data: Array = ArrayTrait::new(); + data.append(1); + data.append(2); + data.append(3); + data.append(4); + + let res = poseidon_hash_span(data.span()); + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} + +// Modified version of poseidon_hash_span that doesn't require builtin gas costs +pub fn poseidon_hash_span(mut span: Span) -> felt252 { + _poseidon_hash_span_inner((0, 0, 0), ref span) +} + +/// Helper function for poseidon_hash_span. +fn _poseidon_hash_span_inner( + state: (felt252, felt252, felt252), + ref span: Span +) -> felt252 { + let (s0, s1, s2) = state; + let x = *match span.pop_front() { + Option::Some(x) => x, + Option::None => { return HashState { s0, s1, s2, odd: false }.finalize(); }, + }; + let y = *match span.pop_front() { + Option::Some(y) => y, + Option::None => { return HashState { s0: s0 + x, s1, s2, odd: true }.finalize(); }, + }; + let next_state = hades_permutation(s0 + x, s1 + y, s2); + _poseidon_hash_span_inner(next_state, ref span) +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/poseidon_pedersen.cairo b/cairo_programs/cairo-1-programs/serialized_output/poseidon_pedersen.cairo new file mode 100644 index 0000000000..825e677162 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/poseidon_pedersen.cairo @@ -0,0 +1,43 @@ +use core::felt252; +use array::ArrayTrait; +use array::SpanTrait; +use core::pedersen::pedersen; +use core::hash::HashStateTrait; +use core::poseidon::{hades_permutation, HashState}; + +fn main() -> Array { + let mut data: Array = ArrayTrait::new(); + data.append(1); + data.append(2); + data.append(3); + data.append(4); + + let res = poseidon_hash_span(data.span()); + let res = pedersen(res, 0); + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} + +// Modified version of poseidon_hash_span that doesn't require builtin gas costs +pub fn poseidon_hash_span(mut span: Span) -> felt252 { + _poseidon_hash_span_inner((0, 0, 0), ref span) +} + +/// Helper function for poseidon_hash_span. +fn _poseidon_hash_span_inner( + state: (felt252, felt252, felt252), + ref span: Span +) -> felt252 { + let (s0, s1, s2) = state; + let x = *match span.pop_front() { + Option::Some(x) => x, + Option::None => { return HashState { s0, s1, s2, odd: false }.finalize(); }, + }; + let y = *match span.pop_front() { + Option::Some(y) => y, + Option::None => { return HashState { s0: s0 + x, s1, s2, odd: true }.finalize(); }, + }; + let next_state = hades_permutation(s0 + x, s1 + y, s2); + _poseidon_hash_span_inner(next_state, ref span) +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/print.cairo b/cairo_programs/cairo-1-programs/serialized_output/print.cairo new file mode 100644 index 0000000000..1f6fdc5409 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/print.cairo @@ -0,0 +1,10 @@ +use core::debug::PrintTrait; + +fn main() -> Array { + 'Hello, world!'.print(); + 1234.print(); + + let mut output: Array = ArrayTrait::new(); + ().serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/recursion.cairo b/cairo_programs/cairo-1-programs/serialized_output/recursion.cairo new file mode 100644 index 0000000000..1b98be9e75 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/recursion.cairo @@ -0,0 +1,18 @@ +fn factorial(mut n: felt252) -> felt252 { + let mut value = n; + loop { + if n == 0 || n == 1 || n == 2 { + break value; + } + + n -= 1; + value *= n; + } +} + +fn main() -> Array { + let res = factorial(1000); + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/sample.cairo b/cairo_programs/cairo-1-programs/serialized_output/sample.cairo new file mode 100644 index 0000000000..44f640ec10 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/sample.cairo @@ -0,0 +1,15 @@ +// This function is NOT considered tail recursive and will not be optimized +// because the state is not empty (it needs `i`). +fn inner(i: felt252) -> felt252 { + match i { + 0 => 0, + _ => i + inner(i - 1), + } +} + +fn main() -> Array { + let res = inner(100); + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/serialize_felt.cairo b/cairo_programs/cairo-1-programs/serialized_output/serialize_felt.cairo new file mode 100644 index 0000000000..0926a630ac --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/serialize_felt.cairo @@ -0,0 +1,11 @@ +use core::felt252; +use array::ArrayTrait; +use core::Serde; + + +fn main() -> Array { + let mut output: Array = ArrayTrait::new(); + let a : u32 = 10 - 2; + a.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/simple.cairo b/cairo_programs/cairo-1-programs/serialized_output/simple.cairo new file mode 100644 index 0000000000..f337162921 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/simple.cairo @@ -0,0 +1,6 @@ +fn main() -> Array { + let res = true; + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/simple_struct.cairo b/cairo_programs/cairo-1-programs/serialized_output/simple_struct.cairo new file mode 100644 index 0000000000..d1481a1a97 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/simple_struct.cairo @@ -0,0 +1,13 @@ +#[derive(Drop, Serde)] +struct Hello { + a: felt252 +} + +fn main() -> Array { + let res = Hello { + a: 100 + }; + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/struct_span_return.cairo b/cairo_programs/cairo-1-programs/serialized_output/struct_span_return.cairo new file mode 100644 index 0000000000..a77b47e246 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/struct_span_return.cairo @@ -0,0 +1,19 @@ +use core::array::SpanTrait; +use core::array::ArrayTrait; + + +fn main() -> Array { + let mut numbers = ArrayTrait::new(); + let mut numbers_a = ArrayTrait::new(); + let mut numbers_b = ArrayTrait::new(); + numbers_a.append(4_u32); + numbers_a.append(3_u32); + numbers_b.append(2_u32); + numbers_b.append(1_u32); + numbers.append(numbers_a); + numbers.append(numbers_b); + + let mut output: Array = ArrayTrait::new(); + numbers.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/tensor_new.cairo b/cairo_programs/cairo-1-programs/serialized_output/tensor_new.cairo new file mode 100644 index 0000000000..7a3e761574 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/tensor_new.cairo @@ -0,0 +1,86 @@ +// Custom Serde impl +use core::array::{array_new, serialize_array_helper}; + +impl TensorFP16x16Serde of Serde> { + fn serialize(self: @Tensor, ref output: Array) { + // Serialize shape + (*self).shape.len().serialize(ref output); + serialize_array_helper((*self).shape, ref output); + // Serialize data + (*self).data.len().serialize(ref output); + serialize_array_helper((*self).data, ref output); + } + + fn deserialize(ref serialized: Span) -> Option> { + Option::None // Unused + } +} + +// FP16x16 +#[derive(Serde, Copy, Drop)] +struct FP16x16 { + mag: u32, + sign: bool +} + +trait FixedTrait { + fn new(mag: MAG, sign: bool) -> T; +} + +impl FP16x16Impl of FixedTrait { + fn new(mag: u32, sign: bool) -> FP16x16 { + FP16x16 { mag: mag, sign: sign } + } +} + +//Tensor +#[derive(Copy, Drop)] +struct Tensor { + shape: Span, + data: Span, +} + +trait TensorTrait { + fn new(shape: Span, data: Span) -> Tensor; +} + +impl FP16x16Tensor of TensorTrait { + fn new(shape: Span, data: Span) -> Tensor { + new_tensor(shape, data) + } +} + +fn new_tensor(shape: Span, data: Span) -> Tensor { + check_shape::(shape, data); + Tensor:: { shape, data } +} + +fn check_shape(shape: Span, data: Span) { + assert(len_from_shape(shape) == data.len(), 'wrong tensor shape'); +} + +fn len_from_shape(mut shape: Span) -> usize { + let mut result: usize = 1; + + loop { + match shape.pop_front() { + Option::Some(item) => { result *= *item; }, + Option::None => { break; } + }; + }; + + result +} + +fn main() -> Array { + let res: Tensor = TensorTrait::new( + array![1, 2].span(), + array![ + FixedTrait::new(1, false), + FixedTrait::new(1, true) + ].span() + ); + let mut output = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/with_input/array_input_sum.cairo b/cairo_programs/cairo-1-programs/serialized_output/with_input/array_input_sum.cairo new file mode 100644 index 0000000000..44e71731bf --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/with_input/array_input_sum.cairo @@ -0,0 +1,9 @@ +use array::ArrayTrait; + +fn main(index_a: u32, array_a: Array, index_b: u32, array_b: Array) -> Array { + let res = *array_a.at(index_a) + *array_b.at(index_b); + + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/with_input/array_length.cairo b/cairo_programs/cairo-1-programs/serialized_output/with_input/array_length.cairo new file mode 100644 index 0000000000..0400c14ae8 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/with_input/array_length.cairo @@ -0,0 +1,8 @@ +use array::ArrayTrait; + +fn main(array_a: Array, array_b: Array) -> Array { + let res = array_a.len() + array_b.len(); + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/with_input/branching.cairo b/cairo_programs/cairo-1-programs/serialized_output/with_input/branching.cairo new file mode 100644 index 0000000000..3844787ae4 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/with_input/branching.cairo @@ -0,0 +1,11 @@ +fn main(argc: u32) -> Array { + let res = if argc == 0 { + 1_u8 + } else { + 0_u8 + }; + + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/with_input/tensor.cairo b/cairo_programs/cairo-1-programs/serialized_output/with_input/tensor.cairo new file mode 100644 index 0000000000..d613159110 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/with_input/tensor.cairo @@ -0,0 +1,13 @@ +#[derive(Copy, Drop)] +struct Tensor { + shape: Span, + data: Span +} + +fn main(tensor: Tensor) -> Array { + let res = *tensor.data.at(0); + + let mut output: Array = ArrayTrait::new(); + res.serialize(ref output); + output +}