From 39d3a52329725671007d97b06a4a7e2e6dc66a25 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 12:07:12 -0300 Subject: [PATCH 01/14] Create a cairo_run for cairo1-run crate + Refactor cairo1 execution into different functions --- cairo1-run/src/cairo_run.rs | 698 ++++++++++++++++++++++++++++++++++++ cairo1-run/src/main.rs | 623 ++------------------------------ 2 files changed, 719 insertions(+), 602 deletions(-) create mode 100644 cairo1-run/src/cairo_run.rs diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs new file mode 100644 index 0000000000..5bdfc9d072 --- /dev/null +++ b/cairo1-run/src/cairo_run.rs @@ -0,0 +1,698 @@ +#![allow(unused_imports)] +use bincode::enc::write::Writer; +use cairo_lang_casm::casm; +use cairo_lang_casm::casm_extend; +use cairo_lang_casm::hints::Hint; +use cairo_lang_casm::instructions::Instruction; +use cairo_lang_compiler::db; +use cairo_lang_compiler::{compile_cairo_project_at_path, CompilerConfig}; +use cairo_lang_sierra::extensions::bitwise::BitwiseType; +use cairo_lang_sierra::extensions::core::{CoreLibfunc, CoreType}; +use cairo_lang_sierra::extensions::ec::EcOpType; +use cairo_lang_sierra::extensions::gas::GasBuiltinType; +use cairo_lang_sierra::extensions::pedersen::PedersenType; +use cairo_lang_sierra::extensions::poseidon::PoseidonType; +use cairo_lang_sierra::extensions::range_check::RangeCheckType; +use cairo_lang_sierra::extensions::segment_arena::SegmentArenaType; +use cairo_lang_sierra::extensions::starknet::syscalls::SystemType; +use cairo_lang_sierra::extensions::ConcreteType; +use cairo_lang_sierra::extensions::NamedType; +use cairo_lang_sierra::ids::ConcreteTypeId; +use cairo_lang_sierra::program::Function; +use cairo_lang_sierra::program::Program as SierraProgram; +use cairo_lang_sierra::program_registry::{ProgramRegistry, ProgramRegistryError}; +use cairo_lang_sierra::{extensions::gas::CostTokenType, ProgramParser}; +use cairo_lang_sierra_ap_change::calc_ap_changes; +use cairo_lang_sierra_gas::gas_info::GasInfo; +use cairo_lang_sierra_to_casm::compiler::CairoProgram; +use cairo_lang_sierra_to_casm::compiler::CompilationError; +use cairo_lang_sierra_to_casm::metadata::Metadata; +use cairo_lang_sierra_to_casm::metadata::MetadataComputationConfig; +use cairo_lang_sierra_to_casm::metadata::MetadataError; +use cairo_lang_sierra_to_casm::{compiler::compile, metadata::calc_metadata}; +use cairo_lang_sierra_type_size::get_type_size_map; +use cairo_lang_utils::extract_matches; +use cairo_lang_utils::ordered_hash_map::OrderedHashMap; +use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; +use cairo_vm::air_public_input::PublicInputError; +use cairo_vm::cairo_run; +use cairo_vm::cairo_run::EncodeTraceError; +use cairo_vm::hint_processor::cairo_1_hint_processor::hint_processor::Cairo1HintProcessor; +use cairo_vm::serde::deserialize_program::BuiltinName; +use cairo_vm::serde::deserialize_program::{ApTracking, FlowTrackingData, HintParams}; +use cairo_vm::types::errors::program_errors::ProgramError; +use cairo_vm::types::relocatable::Relocatable; +use cairo_vm::vm::decoding::decoder::decode_instruction; +use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; +use cairo_vm::vm::errors::memory_errors::MemoryError; +use cairo_vm::vm::errors::runner_errors::RunnerError; +use cairo_vm::vm::errors::trace_errors::TraceError; +use cairo_vm::vm::errors::vm_errors::VirtualMachineError; +use cairo_vm::vm::runners::builtin_runner::{ + BITWISE_BUILTIN_NAME, EC_OP_BUILTIN_NAME, HASH_BUILTIN_NAME, OUTPUT_BUILTIN_NAME, + POSEIDON_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME, SIGNATURE_BUILTIN_NAME, +}; +use cairo_vm::vm::runners::cairo_runner::CairoArg; +use cairo_vm::vm::runners::cairo_runner::RunnerMode; +use cairo_vm::vm::vm_memory::memory::Memory; +use cairo_vm::{ + serde::deserialize_program::ReferenceManager, + types::{program::Program, relocatable::MaybeRelocatable}, + vm::{ + runners::cairo_runner::{CairoRunner, RunResources}, + vm_core::VirtualMachine, + }, + Felt252, +}; +use clap::{CommandFactory, Parser, ValueHint}; +use itertools::{chain, Itertools}; +use std::borrow::Cow; +use std::io::BufWriter; +use std::io::Write; +use std::iter::Peekable; +use std::path::PathBuf; +use std::slice::Iter; +use std::{collections::HashMap, io, path::Path}; + +use crate::Error; +use crate::FuncArg; +use crate::FuncArgs; + +#[derive(Default, Debug)] +pub struct Cairo1RunConfig<'a> { + pub args: FuncArgs, + pub trace_enabled: bool, + pub relocate_mem: bool, + pub layout: &'a str, + pub proof_mode: bool, + // Should be true if either air_public_input or cairo_pie_output are needed + // Sets builtins stop_ptr by calling `final_stack` on each builtin + pub finalize_builtins: bool, +} + +// Runs a Cairo 1 program +// Returns the runner & VM after execution + the return values +pub fn cairo_run_program<'a>( + sierra_program: &'a SierraProgram, + cairo_run_config: Cairo1RunConfig, +) -> Result<(CairoRunner, VirtualMachine, Vec), Error> { + let metadata = create_metadata(&sierra_program, Some(Default::default()))?; + let sierra_program_registry = ProgramRegistry::::new(&sierra_program)?; + let type_sizes = + get_type_size_map(&sierra_program, &sierra_program_registry).unwrap_or_default(); + let casm_program = + cairo_lang_sierra_to_casm::compiler::compile(&sierra_program, &metadata, true)?; + + let main_func = find_function(&sierra_program, "::main")?; + + let initial_gas = 9999999999999_usize; + + // 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( + &sierra_program_registry, + &casm_program, + &type_sizes, + main_func, + initial_gas, + cairo_run_config.proof_mode, + &cairo_run_config.args.0, + )?; + + // Fetch return type data + let return_type_id = main_func + .signature + .ret_types + .last() + .ok_or(Error::NoRetTypesInSignature)?; + let return_type_size = type_sizes + .get(return_type_id) + .cloned() + .ok_or_else(|| Error::NoTypeSizeForId(return_type_id.clone()))?; + + // This footer is used by lib funcs + let libfunc_footer = create_code_footer(); + + // Header used to initiate the infinite loop after executing the program + // Also appends return values to output segment + let proof_mode_header = if cairo_run_config.proof_mode { + create_proof_mode_header(builtins.len() as i16, return_type_size) + } else { + casm! {}.instructions + }; + + // This is the program we are actually running/proving + // With (embedded proof mode), cairo1 header and the libfunc footer + let instructions = chain!( + proof_mode_header.iter(), + entry_code.iter(), + casm_program.instructions.iter(), + libfunc_footer.iter(), + ); + + let (processor_hints, program_hints) = build_hints_vec(instructions.clone()); + + let mut hint_processor = Cairo1HintProcessor::new(&processor_hints, RunResources::default()); + + let data: Vec = instructions + .flat_map(|inst| inst.assemble().encode()) + .map(|x| Felt252::from(&x)) + .map(MaybeRelocatable::from) + .collect(); + + let data_len = data.len(); + + let program = if cairo_run_config.proof_mode { + Program::new_for_proof( + builtins, + data, + 0, + // Proof mode is on top + // jmp rel 0 is on PC == 2 + 2, + program_hints, + ReferenceManager { + references: Vec::new(), + }, + HashMap::new(), + vec![], + None, + )? + } else { + Program::new( + builtins, + data, + Some(0), + program_hints, + ReferenceManager { + references: Vec::new(), + }, + HashMap::new(), + vec![], + None, + )? + }; + + let runner_mode = if cairo_run_config.proof_mode { + RunnerMode::ProofModeCairo1 + } else { + RunnerMode::ExecutionMode + }; + + let mut runner = CairoRunner::new_v2(&program, &cairo_run_config.layout, runner_mode)?; + let mut vm = VirtualMachine::new(cairo_run_config.trace_enabled); + let end = runner.initialize(&mut vm)?; + + additional_initialization(&mut vm, data_len)?; + + // Run it until the end / infinite loop in proof_mode + runner.run_until_pc(end, &mut vm, &mut hint_processor)?; + if cairo_run_config.proof_mode { + // As we will be inserting the return values into the output segment after running the main program (right before the infinite loop) the computed size for the output builtin will be 0 + // We need to manually set the segment size for the output builtin's segment so memory hole counting doesn't fail due to having a higher accessed address count than the segment's size + vm.segments + .segment_sizes + .insert(2, return_type_size as usize); + } + runner.end_run(false, false, &mut vm, &mut hint_processor)?; + + // Fetch return values + let return_values = fetch_return_values(return_type_size, return_type_id, &vm)?; + + // Set stop pointers for builtins so we can obtain the air public input + if cairo_run_config.finalize_builtins { + finalize_builtins( + cairo_run_config.proof_mode, + &main_func.signature.ret_types, + &type_sizes, + &mut vm, + )?; + + // Build execution public memory + if cairo_run_config.proof_mode { + // As the output builtin is not used by the program we need to compute it's stop ptr manually + vm.set_output_stop_ptr_offset(return_type_size as usize); + + runner.finalize_segments(&mut vm)?; + } + } + + runner.relocate(&mut vm, true)?; + + Ok((runner, vm, return_values)) +} + +fn additional_initialization(vm: &mut VirtualMachine, data_len: usize) -> Result<(), Error> { + // Create the builtin cost segment + let builtin_cost_segment = vm.add_memory_segment(); + for token_type in CostTokenType::iter_precost() { + vm.insert_value( + (builtin_cost_segment + (token_type.offset_in_builtin_costs() as usize)) + .map_err(VirtualMachineError::Math)?, + Felt252::default(), + )? + } + // Put a pointer to the builtin cost segment at the end of the program (after the + // additional `ret` statement). + vm.insert_value( + (vm.get_pc() + data_len).map_err(VirtualMachineError::Math)?, + builtin_cost_segment, + )?; + + Ok(()) +} + +#[allow(clippy::type_complexity)] +fn build_hints_vec<'b>( + instructions: impl Iterator, +) -> (Vec<(usize, Vec)>, HashMap>) { + let mut hints: Vec<(usize, Vec)> = Vec::new(); + let mut program_hints: HashMap> = HashMap::new(); + + let mut hint_offset = 0; + + for instruction in instructions { + if !instruction.hints.is_empty() { + hints.push((hint_offset, instruction.hints.clone())); + program_hints.insert( + hint_offset, + vec![HintParams { + code: hint_offset.to_string(), + accessible_scopes: Vec::new(), + flow_tracking_data: FlowTrackingData { + ap_tracking: ApTracking::default(), + reference_ids: HashMap::new(), + }, + }], + ); + } + hint_offset += instruction.body.op_size(); + } + (hints, program_hints) +} + +/// Finds first function ending with `name_suffix`. +fn find_function<'a>( + sierra_program: &'a SierraProgram, + name_suffix: &'a str, +) -> Result<&'a Function, RunnerError> { + sierra_program + .funcs + .iter() + .find(|f| { + if let Some(name) = &f.id.debug_name { + name.ends_with(name_suffix) + } else { + false + } + }) + .ok_or_else(|| RunnerError::MissingMain) +} + +/// Creates a list of instructions that will be appended to the program's bytecode. +fn create_code_footer() -> Vec { + casm! { + // Add a `ret` instruction used in libfuncs that retrieve the current value of the `fp` + // and `pc` registers. + ret; + } + .instructions +} + +// Create proof_mode specific instructions +// Including the "canonical" proof mode instructions (the ones added by the compiler in cairo 0) +// wich call the firt program instruction and then initiate an infinite loop. +// And also appending the return values to the output builtin's memory segment +fn create_proof_mode_header(builtin_count: i16, return_type_size: i16) -> Vec { + // As the output builtin is not used by cairo 1 (we forced it for this purpose), it's segment is always empty + // so we can start writing values directly from it's base, which is located relative to the fp before the other builtin's bases + let output_fp_offset: i16 = -(builtin_count + 2); // The 2 here represents the return_fp & end segments + + // The pc offset where the original program should start + // Without this header it should start at 0, but we add 2 for each call and jump instruction (as both of them use immediate values) + // and also 1 for each instruction added to copy each return value into the output segment + let program_start_offset: i16 = 4 + return_type_size; + + let mut ctx = casm! {}; + casm_extend! {ctx, + call rel program_start_offset; // Begin program execution by calling the first instruction in the original program + }; + // Append each return value to the output segment + for (i, j) in (1..return_type_size + 1).rev().enumerate() { + casm_extend! {ctx, + // [ap -j] is where each return value is located in memory + // [[fp + output_fp_offet] + 0] is the base of the output segment + [ap - j] = [[fp + output_fp_offset] + i as i16]; + }; + } + casm_extend! {ctx, + jmp rel 0; // Infinite loop + }; + ctx.instructions +} + +/// Returns the instructions to add to the beginning of the code to successfully call the main +/// function, as well as the builtins required to execute the program. +fn create_entry_code( + sierra_program_registry: &ProgramRegistry, + casm_program: &CairoProgram, + type_sizes: &UnorderedHashMap, + func: &Function, + initial_gas: usize, + proof_mode: bool, + args: &Vec, +) -> Result<(Vec, Vec), Error> { + let mut ctx = casm! {}; + // The builtins in the formatting expected by the runner. + let (builtins, builtin_offset) = get_function_builtins(func, proof_mode); + + // Load all vecs to memory. + // Load all array args content to memory. + let mut array_args_data = vec![]; + let mut ap_offset: i16 = 0; + for arg in args { + let FuncArg::Array(values) = arg else { continue }; + array_args_data.push(ap_offset); + casm_extend! {ctx, + %{ memory[ap + 0] = segments.add() %} + ap += 1; + } + for (i, v) in values.iter().enumerate() { + let arr_at = (i + 1) as i16; + casm_extend! {ctx, + [ap + 0] = (v.to_bigint()); + [ap + 0] = [[ap - arr_at] + (i as i16)], ap++; + }; + } + ap_offset += (1 + values.len()) as i16; + } + let mut array_args_data_iter = array_args_data.iter(); + let after_arrays_data_offset = ap_offset; + let mut arg_iter = args.iter().enumerate(); + let mut param_index = 0; + let mut expected_arguments_size = 0; + if func.signature.param_types.iter().any(|ty| { + get_info(sierra_program_registry, ty) + .map(|x| x.long_id.generic_id == SegmentArenaType::ID) + .unwrap_or_default() + }) { + casm_extend! {ctx, + // SegmentArena segment. + %{ memory[ap + 0] = segments.add() %} + // Infos segment. + %{ memory[ap + 1] = segments.add() %} + ap += 2; + [ap + 0] = 0, ap++; + // Write Infos segment, n_constructed (0), and n_destructed (0) to the segment. + [ap - 2] = [[ap - 3]]; + [ap - 1] = [[ap - 3] + 1]; + [ap - 1] = [[ap - 3] + 2]; + } + ap_offset += 3; + } + for ty in func.signature.param_types.iter() { + let info = get_info(sierra_program_registry, ty) + .ok_or_else(|| Error::NoInfoForType(ty.clone()))?; + let generic_ty = &info.long_id.generic_id; + if let Some(offset) = builtin_offset.get(generic_ty) { + let mut offset = *offset; + if proof_mode { + // Everything is off by 2 due to the proof mode header + offset += 2; + } + casm_extend! {ctx, + [ap + 0] = [fp - offset], ap++; + } + ap_offset += 1; + } else if generic_ty == &SystemType::ID { + casm_extend! {ctx, + %{ memory[ap + 0] = segments.add() %} + ap += 1; + } + ap_offset += 1; + } else if generic_ty == &GasBuiltinType::ID { + casm_extend! {ctx, + [ap + 0] = initial_gas, ap++; + } + ap_offset += 1; + } else if generic_ty == &SegmentArenaType::ID { + let offset = -ap_offset + after_arrays_data_offset; + casm_extend! {ctx, + [ap + 0] = [ap + offset] + 3, ap++; + } + ap_offset += 1; + } else { + let ty_size = type_sizes[ty]; + let param_ap_offset_end = ap_offset + ty_size; + expected_arguments_size += ty_size; + while ap_offset < param_ap_offset_end { + let Some((arg_index, arg)) = arg_iter.next() else { + break; + }; + match arg { + FuncArg::Single(value) => { + casm_extend! {ctx, + [ap + 0] = (value.to_bigint()), ap++; + } + ap_offset += 1; + } + FuncArg::Array(values) => { + let offset = -ap_offset + array_args_data_iter.next().unwrap(); + casm_extend! {ctx, + [ap + 0] = [ap + (offset)], ap++; + [ap + 0] = [ap - 1] + (values.len()), ap++; + } + ap_offset += 2; + if ap_offset > param_ap_offset_end { + return Err(Error::ArgumentUnaligned { + param_index, + arg_index, + }); + } + } + } + } + param_index += 1; + }; + } + let actual_args_size = args + .iter() + .map(|arg| match arg { + FuncArg::Single(_) => 1, + FuncArg::Array(_) => 2, + }) + .sum::(); + if expected_arguments_size != actual_args_size { + return Err(Error::ArgumentsSizeMismatch { + expected: expected_arguments_size, + actual: actual_args_size, + }); + } + + let before_final_call = ctx.current_code_offset; + let final_call_size = 3; + let offset = final_call_size + + casm_program.debug_info.sierra_statement_info[func.entry_point.0].code_offset; + + casm_extend! {ctx, + call rel offset; + ret; + } + assert_eq!(before_final_call + final_call_size, ctx.current_code_offset); + + Ok((ctx.instructions, builtins)) +} + +fn get_info<'a>( + sierra_program_registry: &'a ProgramRegistry, + ty: &'a cairo_lang_sierra::ids::ConcreteTypeId, +) -> Option<&'a cairo_lang_sierra::extensions::types::TypeInfo> { + sierra_program_registry + .get_type(ty) + .ok() + .map(|ctc| ctc.info()) +} + +/// Creates the metadata required for a Sierra program lowering to casm. +fn create_metadata( + sierra_program: &cairo_lang_sierra::program::Program, + metadata_config: Option, +) -> Result { + if let Some(metadata_config) = metadata_config { + calc_metadata(sierra_program, metadata_config).map_err(|err| match err { + MetadataError::ApChangeError(_) => VirtualMachineError::Unexpected, + MetadataError::CostError(_) => VirtualMachineError::Unexpected, + }) + } else { + Ok(Metadata { + ap_change_info: calc_ap_changes(sierra_program, |_, _| 0) + .map_err(|_| VirtualMachineError::Unexpected)?, + gas_info: GasInfo { + variable_values: Default::default(), + function_costs: Default::default(), + }, + }) + } +} + +/// Type representing the Output builtin. +#[derive(Default)] +pub struct OutputType {} +impl cairo_lang_sierra::extensions::NoGenericArgsGenericType for OutputType { + const ID: cairo_lang_sierra::ids::GenericTypeId = + cairo_lang_sierra::ids::GenericTypeId::new_inline("Output"); + const STORABLE: bool = true; + const DUPLICATABLE: bool = false; + const DROPPABLE: bool = false; + const ZERO_SIZED: bool = false; +} + +fn get_function_builtins( + func: &Function, + proof_mode: bool, +) -> ( + Vec, + HashMap, +) { + let entry_params = &func.signature.param_types; + let mut builtins = Vec::new(); + let mut builtin_offset: HashMap = HashMap::new(); + let mut current_offset = 3; + // Fetch builtins from the entry_params in the standard order + if entry_params + .iter() + .any(|ti| ti.debug_name == Some("Poseidon".into())) + { + builtins.push(BuiltinName::poseidon); + builtin_offset.insert(PoseidonType::ID, current_offset); + current_offset += 1; + } + if entry_params + .iter() + .any(|ti| ti.debug_name == Some("EcOp".into())) + { + builtins.push(BuiltinName::ec_op); + builtin_offset.insert(EcOpType::ID, current_offset); + current_offset += 1 + } + if entry_params + .iter() + .any(|ti| ti.debug_name == Some("Bitwise".into())) + { + builtins.push(BuiltinName::bitwise); + builtin_offset.insert(BitwiseType::ID, current_offset); + current_offset += 1; + } + if entry_params + .iter() + .any(|ti| ti.debug_name == Some("RangeCheck".into())) + { + builtins.push(BuiltinName::range_check); + builtin_offset.insert(RangeCheckType::ID, current_offset); + current_offset += 1; + } + if entry_params + .iter() + .any(|ti| ti.debug_name == Some("Pedersen".into())) + { + builtins.push(BuiltinName::pedersen); + builtin_offset.insert(PedersenType::ID, current_offset); + current_offset += 1; + } + // Force an output builtin so that we can write the program output into it's segment + if proof_mode { + builtins.push(BuiltinName::output); + builtin_offset.insert(OutputType::ID, current_offset); + } + builtins.reverse(); + (builtins, builtin_offset) +} + +fn fetch_return_values( + return_type_size: i16, + return_type_id: &ConcreteTypeId, + vm: &VirtualMachine, +) -> Result, Error> { + let mut return_values = vm.get_return_values(return_type_size as usize)?; + // Check if this result is a Panic result + if return_type_id + .debug_name + .as_ref() + .ok_or_else(|| Error::TypeIdNoDebugName(return_type_id.clone()))? + .starts_with("core::panics::PanicResult::") + { + // Check the failure flag (aka first return value) + if return_values.first() != Some(&MaybeRelocatable::from(0)) { + // In case of failure, extract the error from the return values (aka last two values) + let panic_data_end = return_values + .last() + .ok_or(Error::FailedToExtractReturnValues)? + .get_relocatable() + .ok_or(Error::FailedToExtractReturnValues)?; + let panic_data_start = return_values + .get(return_values.len() - 2) + .ok_or(Error::FailedToExtractReturnValues)? + .get_relocatable() + .ok_or(Error::FailedToExtractReturnValues)?; + let panic_data = vm.get_integer_range( + panic_data_start, + (panic_data_end - panic_data_start).map_err(VirtualMachineError::Math)?, + )?; + return Err(Error::RunPanic( + panic_data.iter().map(|c| *c.as_ref()).collect(), + )); + } else { + if return_values.len() < 3 { + return Err(Error::FailedToExtractReturnValues); + } + return_values = return_values[2..].to_vec() + } + } + Ok(return_values) +} + +// Calculates builtins' final_stack setting each stop_ptr +// Calling this function is a must if either air_public_input or cairo_pie are needed +fn finalize_builtins( + proof_mode: bool, + main_ret_types: &Vec, + type_sizes: &UnorderedHashMap, + vm: &mut VirtualMachine, +) -> Result<(), Error> { + // Set stop pointers for builtins so we can obtain the air public input + // Cairo 1 programs have other return values aside from the used builtin's final pointers, so we need to hand-pick them + let ret_types_sizes = main_ret_types + .iter() + .map(|id| type_sizes.get(id).cloned().unwrap_or_default()); + let ret_types_and_sizes = main_ret_types.iter().zip(ret_types_sizes.clone()); + + let full_ret_types_size: i16 = ret_types_sizes.sum(); + let mut stack_pointer = (vm.get_ap() - (full_ret_types_size as usize).saturating_sub(1)) + .map_err(VirtualMachineError::Math)?; + + // Calculate the stack_ptr for each return builtin in the return values + let mut builtin_name_to_stack_pointer = HashMap::new(); + for (id, size) in ret_types_and_sizes { + if let Some(ref name) = id.debug_name { + let builtin_name = match &*name.to_string() { + "RangeCheck" => RANGE_CHECK_BUILTIN_NAME, + "Poseidon" => POSEIDON_BUILTIN_NAME, + "EcOp" => EC_OP_BUILTIN_NAME, + "Bitwise" => BITWISE_BUILTIN_NAME, + "Pedersen" => HASH_BUILTIN_NAME, + "Output" => OUTPUT_BUILTIN_NAME, + "Ecdsa" => SIGNATURE_BUILTIN_NAME, + _ => { + stack_pointer.offset += size as usize; + continue; + } + }; + builtin_name_to_stack_pointer.insert(builtin_name, stack_pointer); + } + stack_pointer.offset += size as usize; + } + + // Set stop pointer for each builtin + vm.builtins_final_stack_from_stack_pointer_dict(&builtin_name_to_stack_pointer, proof_mode)?; + Ok(()) +} diff --git a/cairo1-run/src/main.rs b/cairo1-run/src/main.rs index 6c0214d7f7..7f2bb8a7ec 100644 --- a/cairo1-run/src/main.rs +++ b/cairo1-run/src/main.rs @@ -34,8 +34,8 @@ use cairo_lang_sierra_type_size::get_type_size_map; use cairo_lang_utils::extract_matches; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; +use cairo_run::Cairo1RunConfig; use cairo_vm::air_public_input::PublicInputError; -use cairo_vm::cairo_run; use cairo_vm::cairo_run::EncodeTraceError; use cairo_vm::hint_processor::cairo_1_hint_processor::hint_processor::Cairo1HintProcessor; use cairo_vm::serde::deserialize_program::BuiltinName; @@ -75,6 +75,8 @@ use std::slice::Iter; use std::{collections::HashMap, io, path::Path}; use thiserror::Error; +pub mod cairo_run; + #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] struct Args { @@ -111,13 +113,13 @@ struct Args { } #[derive(Debug, Clone)] -enum FuncArg { +pub enum FuncArg { Array(Vec), Single(Felt252), } #[derive(Debug, Clone, Default)] -struct FuncArgs(Vec); +pub struct FuncArgs(Vec); fn process_args(value: &str) -> Result { if value.is_empty() { @@ -170,7 +172,7 @@ fn validate_layout(value: &str) -> Result { } #[derive(Debug, Error)] -enum Error { +pub enum Error { #[error("Invalid arguments")] Cli(#[from] clap::Error), #[error("Failed to interact with the file system")] @@ -254,217 +256,25 @@ impl FileWriter { fn run(args: impl Iterator) -> Result, Error> { let args = Args::try_parse_from(args)?; + let cairo_run_config = Cairo1RunConfig { + proof_mode: args.proof_mode, + relocate_mem: args.memory_file.is_some() || args.air_public_input.is_some(), + layout: &args.layout, + trace_enabled: args.trace_file.is_some() || args.air_public_input.is_some(), + args: args.args, + finalize_builtins: args.air_private_input.is_some() || args.cairo_pie_output.is_some(), + }; + let compiler_config = CompilerConfig { replace_ids: true, ..CompilerConfig::default() }; + let sierra_program = compile_cairo_project_at_path(&args.filename, compiler_config) .map_err(|err| Error::SierraCompilation(err.to_string()))?; - let metadata_config = Some(Default::default()); - - let gas_usage_check = metadata_config.is_some(); - let metadata = create_metadata(&sierra_program, metadata_config)?; - let sierra_program_registry = ProgramRegistry::::new(&sierra_program)?; - let type_sizes = - get_type_size_map(&sierra_program, &sierra_program_registry).unwrap_or_default(); - let casm_program = - cairo_lang_sierra_to_casm::compiler::compile(&sierra_program, &metadata, gas_usage_check)?; - - let main_func = find_function(&sierra_program, "::main")?; - - let initial_gas = 9999999999999_usize; - - // 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( - &sierra_program_registry, - &casm_program, - &type_sizes, - main_func, - initial_gas, - args.proof_mode, - &args.args.0, - )?; - - // Get the user program instructions - let program_instructions = casm_program.instructions.iter(); - - // Fetch return type data - let return_type_id = main_func - .signature - .ret_types - .last() - .ok_or(Error::NoRetTypesInSignature)?; - let return_type_size = type_sizes - .get(return_type_id) - .cloned() - .ok_or_else(|| Error::NoTypeSizeForId(return_type_id.clone()))?; - - // This footer is used by lib funcs - let libfunc_footer = create_code_footer(); - - let proof_mode_header = if args.proof_mode { - println!("Compiling with proof mode and running ..."); - - // This information can be useful for the users using the prover. - println!("Builtins used: {:?}", builtins); - - // Create proof_mode specific instructions - // Including the "canonical" proof mode instructions (the ones added by the compiler in cairo 0) - // wich call the firt program instruction and then initiate an infinite loop. - // And also appending the return values to the output builtin's memory segment - - // As the output builtin is not used by cairo 1 (we forced it for this purpose), it's segment is always empty - // so we can start writing values directly from it's base, which is located relative to the fp before the other builtin's bases - let output_fp_offset: i16 = -(builtins.len() as i16 + 2); // The 2 here represents the return_fp & end segments - - // The pc offset where the original program should start - // Without this header it should start at 0, but we add 2 for each call and jump instruction (as both of them use immediate values) - // and also 1 for each instruction added to copy each return value into the output segment - let program_start_offset: i16 = 4 + return_type_size; - - let mut ctx = casm! {}; - casm_extend! {ctx, - call rel program_start_offset; // Begin program execution by calling the first instruction in the original program - }; - // Append each return value to the output segment - for (i, j) in (1..return_type_size + 1).rev().enumerate() { - casm_extend! {ctx, - // [ap -j] is where each return value is located in memory - // [[fp + output_fp_offet] + 0] is the base of the output segment - [ap - j] = [[fp + output_fp_offset] + i as i16]; - }; - } - casm_extend! {ctx, - jmp rel 0; // Infinite loop - }; - ctx.instructions - } else { - casm! {}.instructions - }; - - // This is the program we are actually running/proving - // With (embedded proof mode), cairo1 header and the libfunc footer - let instructions = chain!( - proof_mode_header.iter(), - entry_code.iter(), - program_instructions, - libfunc_footer.iter(), - ); - - let (processor_hints, program_hints) = build_hints_vec(instructions.clone()); - - let mut hint_processor = Cairo1HintProcessor::new(&processor_hints, RunResources::default()); - - let data: Vec = instructions - .flat_map(|inst| inst.assemble().encode()) - .map(|x| Felt252::from(&x)) - .map(MaybeRelocatable::from) - .collect(); - - let data_len = data.len(); - - let program = if args.proof_mode { - Program::new_for_proof( - builtins, - data, - 0, - // Proof mode is on top - // jmp rel 0 is on PC == 2 - 2, - program_hints, - ReferenceManager { - references: Vec::new(), - }, - HashMap::new(), - vec![], - None, - )? - } else { - Program::new( - builtins, - data, - Some(0), - program_hints, - ReferenceManager { - references: Vec::new(), - }, - HashMap::new(), - vec![], - None, - )? - }; - - let runner_mode = if args.proof_mode { - RunnerMode::ProofModeCairo1 - } else { - RunnerMode::ExecutionMode - }; - - let mut runner = CairoRunner::new_v2(&program, &args.layout, runner_mode)?; - let mut vm = VirtualMachine::new(args.trace_file.is_some() || args.air_public_input.is_some()); - let end = runner.initialize(&mut vm)?; - - additional_initialization(&mut vm, data_len)?; - - // Run it until the end/ infinite loop in proof_mode - runner.run_until_pc(end, &mut vm, &mut hint_processor)?; - if args.proof_mode { - // As we will be inserting the return values into the output segment after running the main program (right before the infinite loop) the computed size for the output builtin will be 0 - // We need to manually set the segment size for the output builtin's segment so memory hole counting doesn't fail due to having a higher accessed address count than the segment's size - vm.segments - .segment_sizes - .insert(2, return_type_size as usize); - } - runner.end_run(false, false, &mut vm, &mut hint_processor)?; - - // Fetch return type data - let return_type_id = main_func - .signature - .ret_types - .last() - .ok_or(Error::NoRetTypesInSignature)?; - let return_type_size = type_sizes - .get(return_type_id) - .cloned() - .ok_or_else(|| Error::NoTypeSizeForId(return_type_id.clone()))?; - - let mut return_values = vm.get_return_values(return_type_size as usize)?; - // Check if this result is a Panic result - if return_type_id - .debug_name - .as_ref() - .ok_or_else(|| Error::TypeIdNoDebugName(return_type_id.clone()))? - .starts_with("core::panics::PanicResult::") - { - // Check the failure flag (aka first return value) - if return_values.first() != Some(&MaybeRelocatable::from(0)) { - // In case of failure, extract the error from the return values (aka last two values) - let panic_data_end = return_values - .last() - .ok_or(Error::FailedToExtractReturnValues)? - .get_relocatable() - .ok_or(Error::FailedToExtractReturnValues)?; - let panic_data_start = return_values - .get(return_values.len() - 2) - .ok_or(Error::FailedToExtractReturnValues)? - .get_relocatable() - .ok_or(Error::FailedToExtractReturnValues)?; - let panic_data = vm.get_integer_range( - panic_data_start, - (panic_data_end - panic_data_start).map_err(VirtualMachineError::Math)?, - )?; - return Err(Error::RunPanic( - panic_data.iter().map(|c| *c.as_ref()).collect(), - )); - } else { - if return_values.len() < 3 { - return Err(Error::FailedToExtractReturnValues); - } - return_values = return_values[2..].to_vec() - } - } + let (runner, vm, return_values) = + cairo_run::cairo_run_program(&sierra_program, cairo_run_config)?; let output_string = if args.print_output { Some(serialize_output(&vm, &return_values)) @@ -472,63 +282,6 @@ fn run(args: impl Iterator) -> Result, Error> { None }; - // Set stop pointers for builtins so we can obtain the air public input - if args.air_public_input.is_some() || args.cairo_pie_output.is_some() { - // Cairo 1 programs have other return values aside from the used builtin's final pointers, so we need to hand-pick them - let ret_types_sizes = main_func - .signature - .ret_types - .iter() - .map(|id| type_sizes.get(id).cloned().unwrap_or_default()); - let ret_types_and_sizes = main_func - .signature - .ret_types - .iter() - .zip(ret_types_sizes.clone()); - - let full_ret_types_size: i16 = ret_types_sizes.sum(); - let mut stack_pointer = (vm.get_ap() - (full_ret_types_size as usize).saturating_sub(1)) - .map_err(VirtualMachineError::Math)?; - - // Calculate the stack_ptr for each return builtin in the return values - let mut builtin_name_to_stack_pointer = HashMap::new(); - for (id, size) in ret_types_and_sizes { - if let Some(ref name) = id.debug_name { - let builtin_name = match &*name.to_string() { - "RangeCheck" => RANGE_CHECK_BUILTIN_NAME, - "Poseidon" => POSEIDON_BUILTIN_NAME, - "EcOp" => EC_OP_BUILTIN_NAME, - "Bitwise" => BITWISE_BUILTIN_NAME, - "Pedersen" => HASH_BUILTIN_NAME, - "Output" => OUTPUT_BUILTIN_NAME, - "Ecdsa" => SIGNATURE_BUILTIN_NAME, - _ => { - stack_pointer.offset += size as usize; - continue; - } - }; - builtin_name_to_stack_pointer.insert(builtin_name, stack_pointer); - } - stack_pointer.offset += size as usize; - } - - // Set stop pointer for each builtin - vm.builtins_final_stack_from_stack_pointer_dict( - &builtin_name_to_stack_pointer, - args.proof_mode, - )?; - - // Build execution public memory - if args.proof_mode { - // As the output builtin is not used by the program we need to compute it's stop ptr manually - vm.set_output_stop_ptr_offset(return_type_size as usize); - - runner.finalize_segments(&mut vm)?; - } - } - - runner.relocate(&mut vm, true)?; - if let Some(file_path) = args.air_public_input { let json = runner.get_air_public_input(&vm)?.serialize_json()?; std::fs::write(file_path, json)?; @@ -573,7 +326,7 @@ fn run(args: impl Iterator) -> Result, Error> { let mut trace_writer = FileWriter::new(io::BufWriter::with_capacity(3 * 1024 * 1024, trace_file)); - cairo_run::write_encoded_trace(&relocated_trace, &mut trace_writer)?; + cairo_vm::cairo_run::write_encoded_trace(&relocated_trace, &mut trace_writer)?; trace_writer.flush()?; } if let Some(memory_path) = args.memory_file { @@ -581,33 +334,13 @@ fn run(args: impl Iterator) -> Result, Error> { let mut memory_writer = FileWriter::new(io::BufWriter::with_capacity(5 * 1024 * 1024, memory_file)); - cairo_run::write_encoded_memory(&runner.relocated_memory, &mut memory_writer)?; + cairo_vm::cairo_run::write_encoded_memory(&runner.relocated_memory, &mut memory_writer)?; memory_writer.flush()?; } Ok(output_string) } -fn additional_initialization(vm: &mut VirtualMachine, data_len: usize) -> Result<(), Error> { - // Create the builtin cost segment - let builtin_cost_segment = vm.add_memory_segment(); - for token_type in CostTokenType::iter_precost() { - vm.insert_value( - (builtin_cost_segment + (token_type.offset_in_builtin_costs() as usize)) - .map_err(VirtualMachineError::Math)?, - Felt252::default(), - )? - } - // Put a pointer to the builtin cost segment at the end of the program (after the - // additional `ret` statement). - vm.insert_value( - (vm.get_pc() + data_len).map_err(VirtualMachineError::Math)?, - builtin_cost_segment, - )?; - - Ok(()) -} - fn main() -> Result<(), Error> { match run(std::env::args()) { Err(Error::Cli(err)) => err.exit(), @@ -639,321 +372,7 @@ fn main() -> Result<(), Error> { } } -#[allow(clippy::type_complexity)] -fn build_hints_vec<'b>( - instructions: impl Iterator, -) -> (Vec<(usize, Vec)>, HashMap>) { - let mut hints: Vec<(usize, Vec)> = Vec::new(); - let mut program_hints: HashMap> = HashMap::new(); - - let mut hint_offset = 0; - - for instruction in instructions { - if !instruction.hints.is_empty() { - hints.push((hint_offset, instruction.hints.clone())); - program_hints.insert( - hint_offset, - vec![HintParams { - code: hint_offset.to_string(), - accessible_scopes: Vec::new(), - flow_tracking_data: FlowTrackingData { - ap_tracking: ApTracking::default(), - reference_ids: HashMap::new(), - }, - }], - ); - } - hint_offset += instruction.body.op_size(); - } - (hints, program_hints) -} - -/// Finds first function ending with `name_suffix`. -fn find_function<'a>( - sierra_program: &'a SierraProgram, - name_suffix: &'a str, -) -> Result<&'a Function, RunnerError> { - sierra_program - .funcs - .iter() - .find(|f| { - if let Some(name) = &f.id.debug_name { - name.ends_with(name_suffix) - } else { - false - } - }) - .ok_or_else(|| RunnerError::MissingMain) -} - -/// Creates a list of instructions that will be appended to the program's bytecode. -fn create_code_footer() -> Vec { - casm! { - // Add a `ret` instruction used in libfuncs that retrieve the current value of the `fp` - // and `pc` registers. - ret; - } - .instructions -} - -/// Returns the instructions to add to the beginning of the code to successfully call the main -/// function, as well as the builtins required to execute the program. -fn create_entry_code( - sierra_program_registry: &ProgramRegistry, - casm_program: &CairoProgram, - type_sizes: &UnorderedHashMap, - func: &Function, - initial_gas: usize, - proof_mode: bool, - args: &Vec, -) -> Result<(Vec, Vec), Error> { - let mut ctx = casm! {}; - // The builtins in the formatting expected by the runner. - let (builtins, builtin_offset) = get_function_builtins(func, proof_mode); - - // Load all vecs to memory. - // Load all array args content to memory. - let mut array_args_data = vec![]; - let mut ap_offset: i16 = 0; - for arg in args { - let FuncArg::Array(values) = arg else { continue }; - array_args_data.push(ap_offset); - casm_extend! {ctx, - %{ memory[ap + 0] = segments.add() %} - ap += 1; - } - for (i, v) in values.iter().enumerate() { - let arr_at = (i + 1) as i16; - casm_extend! {ctx, - [ap + 0] = (v.to_bigint()); - [ap + 0] = [[ap - arr_at] + (i as i16)], ap++; - }; - } - ap_offset += (1 + values.len()) as i16; - } - let mut array_args_data_iter = array_args_data.iter(); - let after_arrays_data_offset = ap_offset; - let mut arg_iter = args.iter().enumerate(); - let mut param_index = 0; - let mut expected_arguments_size = 0; - if func.signature.param_types.iter().any(|ty| { - get_info(sierra_program_registry, ty) - .map(|x| x.long_id.generic_id == SegmentArenaType::ID) - .unwrap_or_default() - }) { - casm_extend! {ctx, - // SegmentArena segment. - %{ memory[ap + 0] = segments.add() %} - // Infos segment. - %{ memory[ap + 1] = segments.add() %} - ap += 2; - [ap + 0] = 0, ap++; - // Write Infos segment, n_constructed (0), and n_destructed (0) to the segment. - [ap - 2] = [[ap - 3]]; - [ap - 1] = [[ap - 3] + 1]; - [ap - 1] = [[ap - 3] + 2]; - } - ap_offset += 3; - } - for ty in func.signature.param_types.iter() { - let info = get_info(sierra_program_registry, ty) - .ok_or_else(|| Error::NoInfoForType(ty.clone()))?; - let generic_ty = &info.long_id.generic_id; - if let Some(offset) = builtin_offset.get(generic_ty) { - let mut offset = *offset; - if proof_mode { - // Everything is off by 2 due to the proof mode header - offset += 2; - } - casm_extend! {ctx, - [ap + 0] = [fp - offset], ap++; - } - ap_offset += 1; - } else if generic_ty == &SystemType::ID { - casm_extend! {ctx, - %{ memory[ap + 0] = segments.add() %} - ap += 1; - } - ap_offset += 1; - } else if generic_ty == &GasBuiltinType::ID { - casm_extend! {ctx, - [ap + 0] = initial_gas, ap++; - } - ap_offset += 1; - } else if generic_ty == &SegmentArenaType::ID { - let offset = -ap_offset + after_arrays_data_offset; - casm_extend! {ctx, - [ap + 0] = [ap + offset] + 3, ap++; - } - ap_offset += 1; - } else { - let ty_size = type_sizes[ty]; - let param_ap_offset_end = ap_offset + ty_size; - expected_arguments_size += ty_size; - while ap_offset < param_ap_offset_end { - let Some((arg_index, arg)) = arg_iter.next() else { - break; - }; - match arg { - FuncArg::Single(value) => { - casm_extend! {ctx, - [ap + 0] = (value.to_bigint()), ap++; - } - ap_offset += 1; - } - FuncArg::Array(values) => { - let offset = -ap_offset + array_args_data_iter.next().unwrap(); - casm_extend! {ctx, - [ap + 0] = [ap + (offset)], ap++; - [ap + 0] = [ap - 1] + (values.len()), ap++; - } - ap_offset += 2; - if ap_offset > param_ap_offset_end { - return Err(Error::ArgumentUnaligned { - param_index, - arg_index, - }); - } - } - } - } - param_index += 1; - }; - } - let actual_args_size = args - .iter() - .map(|arg| match arg { - FuncArg::Single(_) => 1, - FuncArg::Array(_) => 2, - }) - .sum::(); - if expected_arguments_size != actual_args_size { - return Err(Error::ArgumentsSizeMismatch { - expected: expected_arguments_size, - actual: actual_args_size, - }); - } - - let before_final_call = ctx.current_code_offset; - let final_call_size = 3; - let offset = final_call_size - + casm_program.debug_info.sierra_statement_info[func.entry_point.0].code_offset; - - casm_extend! {ctx, - call rel offset; - ret; - } - assert_eq!(before_final_call + final_call_size, ctx.current_code_offset); - - Ok((ctx.instructions, builtins)) -} - -fn get_info<'a>( - sierra_program_registry: &'a ProgramRegistry, - ty: &'a cairo_lang_sierra::ids::ConcreteTypeId, -) -> Option<&'a cairo_lang_sierra::extensions::types::TypeInfo> { - sierra_program_registry - .get_type(ty) - .ok() - .map(|ctc| ctc.info()) -} - -/// Creates the metadata required for a Sierra program lowering to casm. -fn create_metadata( - sierra_program: &cairo_lang_sierra::program::Program, - metadata_config: Option, -) -> Result { - if let Some(metadata_config) = metadata_config { - calc_metadata(sierra_program, metadata_config).map_err(|err| match err { - MetadataError::ApChangeError(_) => VirtualMachineError::Unexpected, - MetadataError::CostError(_) => VirtualMachineError::Unexpected, - }) - } else { - Ok(Metadata { - ap_change_info: calc_ap_changes(sierra_program, |_, _| 0) - .map_err(|_| VirtualMachineError::Unexpected)?, - gas_info: GasInfo { - variable_values: Default::default(), - function_costs: Default::default(), - }, - }) - } -} - -/// Type representing the Output builtin. -#[derive(Default)] -pub struct OutputType {} -impl cairo_lang_sierra::extensions::NoGenericArgsGenericType for OutputType { - const ID: cairo_lang_sierra::ids::GenericTypeId = - cairo_lang_sierra::ids::GenericTypeId::new_inline("Output"); - const STORABLE: bool = true; - const DUPLICATABLE: bool = false; - const DROPPABLE: bool = false; - const ZERO_SIZED: bool = false; -} - -fn get_function_builtins( - func: &Function, - proof_mode: bool, -) -> ( - Vec, - HashMap, -) { - let entry_params = &func.signature.param_types; - let mut builtins = Vec::new(); - let mut builtin_offset: HashMap = HashMap::new(); - let mut current_offset = 3; - // Fetch builtins from the entry_params in the standard order - if entry_params - .iter() - .any(|ti| ti.debug_name == Some("Poseidon".into())) - { - builtins.push(BuiltinName::poseidon); - builtin_offset.insert(PoseidonType::ID, current_offset); - current_offset += 1; - } - if entry_params - .iter() - .any(|ti| ti.debug_name == Some("EcOp".into())) - { - builtins.push(BuiltinName::ec_op); - builtin_offset.insert(EcOpType::ID, current_offset); - current_offset += 1 - } - if entry_params - .iter() - .any(|ti| ti.debug_name == Some("Bitwise".into())) - { - builtins.push(BuiltinName::bitwise); - builtin_offset.insert(BitwiseType::ID, current_offset); - current_offset += 1; - } - if entry_params - .iter() - .any(|ti| ti.debug_name == Some("RangeCheck".into())) - { - builtins.push(BuiltinName::range_check); - builtin_offset.insert(RangeCheckType::ID, current_offset); - current_offset += 1; - } - if entry_params - .iter() - .any(|ti| ti.debug_name == Some("Pedersen".into())) - { - builtins.push(BuiltinName::pedersen); - builtin_offset.insert(PedersenType::ID, current_offset); - current_offset += 1; - } - // Force an output builtin so that we can write the program output into it's segment - if proof_mode { - builtins.push(BuiltinName::output); - builtin_offset.insert(OutputType::ID, current_offset); - } - builtins.reverse(); - (builtins, builtin_offset) -} - -fn serialize_output(vm: &VirtualMachine, return_values: &[MaybeRelocatable]) -> String { +pub fn serialize_output(vm: &VirtualMachine, return_values: &[MaybeRelocatable]) -> String { let mut output_string = String::new(); let mut return_values_iter: Peekable> = return_values.iter().peekable(); serialize_output_inner(&mut return_values_iter, &mut output_string, vm); From 2181a6821b45151bef6a999a3f16d5c603b9a478 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 12:16:47 -0300 Subject: [PATCH 02/14] Clippy --- cairo1-run/src/cairo_run.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 5bdfc9d072..0b2b0643eb 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -92,18 +92,18 @@ pub struct Cairo1RunConfig<'a> { // Runs a Cairo 1 program // Returns the runner & VM after execution + the return values -pub fn cairo_run_program<'a>( - sierra_program: &'a SierraProgram, +pub fn cairo_run_program( + sierra_program: &SierraProgram, cairo_run_config: Cairo1RunConfig, ) -> Result<(CairoRunner, VirtualMachine, Vec), Error> { - let metadata = create_metadata(&sierra_program, Some(Default::default()))?; - let sierra_program_registry = ProgramRegistry::::new(&sierra_program)?; + let metadata = create_metadata(sierra_program, Some(Default::default()))?; + let sierra_program_registry = ProgramRegistry::::new(sierra_program)?; let type_sizes = - get_type_size_map(&sierra_program, &sierra_program_registry).unwrap_or_default(); + get_type_size_map(sierra_program, &sierra_program_registry).unwrap_or_default(); let casm_program = - cairo_lang_sierra_to_casm::compiler::compile(&sierra_program, &metadata, true)?; + cairo_lang_sierra_to_casm::compiler::compile(sierra_program, &metadata, true)?; - let main_func = find_function(&sierra_program, "::main")?; + let main_func = find_function(sierra_program, "::main")?; let initial_gas = 9999999999999_usize; @@ -199,7 +199,7 @@ pub fn cairo_run_program<'a>( RunnerMode::ExecutionMode }; - let mut runner = CairoRunner::new_v2(&program, &cairo_run_config.layout, runner_mode)?; + let mut runner = CairoRunner::new_v2(&program, cairo_run_config.layout, runner_mode)?; let mut vm = VirtualMachine::new(cairo_run_config.trace_enabled); let end = runner.initialize(&mut vm)?; @@ -655,7 +655,7 @@ fn fetch_return_values( // Calling this function is a must if either air_public_input or cairo_pie are needed fn finalize_builtins( proof_mode: bool, - main_ret_types: &Vec, + main_ret_types: &[ConcreteTypeId], type_sizes: &UnorderedHashMap, vm: &mut VirtualMachine, ) -> Result<(), Error> { From 62adb27a373156cb0c51357b04aba620c240737b Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 12:47:00 -0300 Subject: [PATCH 03/14] Test first draft --- cairo1-run/src/cairo_run.rs | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 0b2b0643eb..94d50a1be1 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -696,3 +696,52 @@ fn finalize_builtins( vm.builtins_final_stack_from_stack_pointer_dict(&builtin_name_to_stack_pointer, proof_mode)?; Ok(()) } + +mod tests { + #![allow(clippy::too_many_arguments)] + use super::*; + use rstest::rstest; + + fn compile_to_sierra<'a>(filename: &'a str) -> SierraProgram { + let compiler_config = CompilerConfig { + replace_ids: true, + ..CompilerConfig::default() + }; + + compile_cairo_project_at_path(Path::new(filename), compiler_config).unwrap() + } + + #[rstest] + #[case(("../cairo_programs/cairo-1-programs/struct_span_return.cairo", false))] + fn check_append_ret_values_to_output_segment(#[case] args: (&str, bool)) { + let (filename, may_panic) = args; + // Compile to sierra + let sierra_program = compile_to_sierra(filename); + // Set proof_mode + let cairo_run_config = Cairo1RunConfig { + proof_mode: true, + ..Default::default() + }; + // Run program + let (_, vm, return_values) = cairo_run_program(&sierra_program, cairo_run_config).unwrap(); + // 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 may_panic { + let mut rv = vec![Felt252::ZERO.into(), Felt252::ZERO.into()]; + rv.extend_from_slice(&return_values); + rv + } else { + return_values + }; + // Check that the output segment contains the return values + // The output builtin will always be the first builtin, so we know it's segment is 2 + let output_builtin_segment = vm + .get_continuous_range((2, 0).into(), return_values.len()) + .unwrap(); + assert_eq!(output_builtin_segment, return_values); + // Just for consistency, we will check that there are no values in the output segment aside from the return values + assert!(vm + .get_maybe(&Relocatable::from((2_isize, return_values.len()))) + .is_none()); + } +} From 01114043ba6847508ee2f2d8fed5a6c244835799 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 12:53:40 -0300 Subject: [PATCH 04/14] Merge imports + Impl Default for Cairo1RunConfig --- cairo1-run/src/cairo_run.rs | 97 ++++++++++--------------------------- 1 file changed, 25 insertions(+), 72 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 0b2b0643eb..13c5d5f85c 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -1,84 +1,24 @@ #![allow(unused_imports)] use bincode::enc::write::Writer; -use cairo_lang_casm::casm; -use cairo_lang_casm::casm_extend; -use cairo_lang_casm::hints::Hint; -use cairo_lang_casm::instructions::Instruction; -use cairo_lang_compiler::db; -use cairo_lang_compiler::{compile_cairo_project_at_path, CompilerConfig}; -use cairo_lang_sierra::extensions::bitwise::BitwiseType; -use cairo_lang_sierra::extensions::core::{CoreLibfunc, CoreType}; -use cairo_lang_sierra::extensions::ec::EcOpType; -use cairo_lang_sierra::extensions::gas::GasBuiltinType; -use cairo_lang_sierra::extensions::pedersen::PedersenType; -use cairo_lang_sierra::extensions::poseidon::PoseidonType; -use cairo_lang_sierra::extensions::range_check::RangeCheckType; -use cairo_lang_sierra::extensions::segment_arena::SegmentArenaType; -use cairo_lang_sierra::extensions::starknet::syscalls::SystemType; -use cairo_lang_sierra::extensions::ConcreteType; -use cairo_lang_sierra::extensions::NamedType; -use cairo_lang_sierra::ids::ConcreteTypeId; -use cairo_lang_sierra::program::Function; -use cairo_lang_sierra::program::Program as SierraProgram; -use cairo_lang_sierra::program_registry::{ProgramRegistry, ProgramRegistryError}; -use cairo_lang_sierra::{extensions::gas::CostTokenType, ProgramParser}; +use cairo_lang_casm::{casm, casm_extend, hints::Hint, instructions::Instruction}; +use cairo_lang_compiler::{compile_cairo_project_at_path, db, CompilerConfig}; +use cairo_lang_sierra::{extensions::{bitwise::BitwiseType, core::{CoreLibfunc, CoreType}, ec::EcOpType, gas::{CostTokenType, GasBuiltinType}, pedersen::PedersenType, poseidon::PoseidonType, range_check::RangeCheckType, segment_arena::SegmentArenaType, starknet::syscalls::SystemType, ConcreteType, NamedType}, ids::ConcreteTypeId, program::{Function, Program as SierraProgram}, program_registry::{ProgramRegistry, ProgramRegistryError}, ProgramParser}; use cairo_lang_sierra_ap_change::calc_ap_changes; use cairo_lang_sierra_gas::gas_info::GasInfo; -use cairo_lang_sierra_to_casm::compiler::CairoProgram; -use cairo_lang_sierra_to_casm::compiler::CompilationError; -use cairo_lang_sierra_to_casm::metadata::Metadata; -use cairo_lang_sierra_to_casm::metadata::MetadataComputationConfig; -use cairo_lang_sierra_to_casm::metadata::MetadataError; -use cairo_lang_sierra_to_casm::{compiler::compile, metadata::calc_metadata}; +use cairo_lang_sierra_to_casm::{compiler::{compile, CairoProgram, CompilationError}, metadata::{calc_metadata, Metadata, MetadataComputationConfig, MetadataError}}; use cairo_lang_sierra_type_size::get_type_size_map; -use cairo_lang_utils::extract_matches; -use cairo_lang_utils::ordered_hash_map::OrderedHashMap; -use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; -use cairo_vm::air_public_input::PublicInputError; -use cairo_vm::cairo_run; -use cairo_vm::cairo_run::EncodeTraceError; -use cairo_vm::hint_processor::cairo_1_hint_processor::hint_processor::Cairo1HintProcessor; -use cairo_vm::serde::deserialize_program::BuiltinName; -use cairo_vm::serde::deserialize_program::{ApTracking, FlowTrackingData, HintParams}; -use cairo_vm::types::errors::program_errors::ProgramError; -use cairo_vm::types::relocatable::Relocatable; -use cairo_vm::vm::decoding::decoder::decode_instruction; -use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; -use cairo_vm::vm::errors::memory_errors::MemoryError; -use cairo_vm::vm::errors::runner_errors::RunnerError; -use cairo_vm::vm::errors::trace_errors::TraceError; -use cairo_vm::vm::errors::vm_errors::VirtualMachineError; -use cairo_vm::vm::runners::builtin_runner::{ +use cairo_lang_utils::{extract_matches, ordered_hash_map::OrderedHashMap, unordered_hash_map::UnorderedHashMap}; +use cairo_vm::{air_public_input::PublicInputError, cairo_run::{self, EncodeTraceError}, hint_processor::cairo_1_hint_processor::hint_processor::Cairo1HintProcessor, serde::deserialize_program::{ApTracking, BuiltinName, FlowTrackingData, HintParams, ReferenceManager}, types::{errors::program_errors::ProgramError, program::Program, relocatable::{MaybeRelocatable, Relocatable}}, vm::{decoding::decoder::decode_instruction, errors::{cairo_run_errors::CairoRunError, memory_errors::MemoryError, runner_errors::RunnerError, trace_errors::TraceError, vm_errors::VirtualMachineError}, runners::{builtin_runner::{ BITWISE_BUILTIN_NAME, EC_OP_BUILTIN_NAME, HASH_BUILTIN_NAME, OUTPUT_BUILTIN_NAME, POSEIDON_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME, SIGNATURE_BUILTIN_NAME, -}; -use cairo_vm::vm::runners::cairo_runner::CairoArg; -use cairo_vm::vm::runners::cairo_runner::RunnerMode; -use cairo_vm::vm::vm_memory::memory::Memory; -use cairo_vm::{ - serde::deserialize_program::ReferenceManager, - types::{program::Program, relocatable::MaybeRelocatable}, - vm::{ - runners::cairo_runner::{CairoRunner, RunResources}, - vm_core::VirtualMachine, - }, - Felt252, -}; +}, cairo_runner::{CairoArg, CairoRunner, RunResources, RunnerMode}}, vm_core::VirtualMachine, vm_memory::memory::Memory}, Felt252}; use clap::{CommandFactory, Parser, ValueHint}; use itertools::{chain, Itertools}; -use std::borrow::Cow; -use std::io::BufWriter; -use std::io::Write; -use std::iter::Peekable; -use std::path::PathBuf; -use std::slice::Iter; -use std::{collections::HashMap, io, path::Path}; - -use crate::Error; -use crate::FuncArg; -use crate::FuncArgs; - -#[derive(Default, Debug)] +use std::{borrow::Cow, collections::HashMap, default, io::{self, BufWriter, Write}, iter::Peekable, path::{Path, PathBuf}, slice::Iter}; + +use crate::{Error, FuncArg, FuncArgs}; + +#[derive(Debug)] pub struct Cairo1RunConfig<'a> { pub args: FuncArgs, pub trace_enabled: bool, @@ -90,6 +30,19 @@ pub struct Cairo1RunConfig<'a> { pub finalize_builtins: bool, } +impl Default for Cairo1RunConfig<'_> { + fn default() -> Self { + Self { + args: FuncArgs::default(), + trace_enabled: false, + relocate_mem: false, + layout: "plain", + proof_mode: false, + finalize_builtins: false, + } + } +} + // Runs a Cairo 1 program // Returns the runner & VM after execution + the return values pub fn cairo_run_program( From 917021328482fe21f625c22ae8562cc57308e28c Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 13:00:29 -0300 Subject: [PATCH 05/14] Remove allow unused imports --- cairo1-run/src/cairo_run.rs | 56 +++++++++++++++++++------ cairo1-run/src/main.rs | 83 ++++++++----------------------------- 2 files changed, 60 insertions(+), 79 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 13c5d5f85c..e60a78f7b0 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -1,20 +1,50 @@ -#![allow(unused_imports)] -use bincode::enc::write::Writer; use cairo_lang_casm::{casm, casm_extend, hints::Hint, instructions::Instruction}; -use cairo_lang_compiler::{compile_cairo_project_at_path, db, CompilerConfig}; -use cairo_lang_sierra::{extensions::{bitwise::BitwiseType, core::{CoreLibfunc, CoreType}, ec::EcOpType, gas::{CostTokenType, GasBuiltinType}, pedersen::PedersenType, poseidon::PoseidonType, range_check::RangeCheckType, segment_arena::SegmentArenaType, starknet::syscalls::SystemType, ConcreteType, NamedType}, ids::ConcreteTypeId, program::{Function, Program as SierraProgram}, program_registry::{ProgramRegistry, ProgramRegistryError}, ProgramParser}; +use cairo_lang_sierra::{ + extensions::{ + bitwise::BitwiseType, + core::{CoreLibfunc, CoreType}, + ec::EcOpType, + gas::{CostTokenType, GasBuiltinType}, + pedersen::PedersenType, + poseidon::PoseidonType, + range_check::RangeCheckType, + segment_arena::SegmentArenaType, + starknet::syscalls::SystemType, + ConcreteType, NamedType, + }, + ids::ConcreteTypeId, + program::{Function, Program as SierraProgram}, + program_registry::ProgramRegistry, +}; use cairo_lang_sierra_ap_change::calc_ap_changes; use cairo_lang_sierra_gas::gas_info::GasInfo; -use cairo_lang_sierra_to_casm::{compiler::{compile, CairoProgram, CompilationError}, metadata::{calc_metadata, Metadata, MetadataComputationConfig, MetadataError}}; +use cairo_lang_sierra_to_casm::{ + compiler::CairoProgram, + metadata::{calc_metadata, Metadata, MetadataComputationConfig, MetadataError}, +}; use cairo_lang_sierra_type_size::get_type_size_map; -use cairo_lang_utils::{extract_matches, ordered_hash_map::OrderedHashMap, unordered_hash_map::UnorderedHashMap}; -use cairo_vm::{air_public_input::PublicInputError, cairo_run::{self, EncodeTraceError}, hint_processor::cairo_1_hint_processor::hint_processor::Cairo1HintProcessor, serde::deserialize_program::{ApTracking, BuiltinName, FlowTrackingData, HintParams, ReferenceManager}, types::{errors::program_errors::ProgramError, program::Program, relocatable::{MaybeRelocatable, Relocatable}}, vm::{decoding::decoder::decode_instruction, errors::{cairo_run_errors::CairoRunError, memory_errors::MemoryError, runner_errors::RunnerError, trace_errors::TraceError, vm_errors::VirtualMachineError}, runners::{builtin_runner::{ - BITWISE_BUILTIN_NAME, EC_OP_BUILTIN_NAME, HASH_BUILTIN_NAME, OUTPUT_BUILTIN_NAME, - POSEIDON_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME, SIGNATURE_BUILTIN_NAME, -}, cairo_runner::{CairoArg, CairoRunner, RunResources, RunnerMode}}, vm_core::VirtualMachine, vm_memory::memory::Memory}, Felt252}; -use clap::{CommandFactory, Parser, ValueHint}; -use itertools::{chain, Itertools}; -use std::{borrow::Cow, collections::HashMap, default, io::{self, BufWriter, Write}, iter::Peekable, path::{Path, PathBuf}, slice::Iter}; +use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; +use cairo_vm::{ + hint_processor::cairo_1_hint_processor::hint_processor::Cairo1HintProcessor, + serde::deserialize_program::{ + ApTracking, BuiltinName, FlowTrackingData, HintParams, ReferenceManager, + }, + types::{program::Program, relocatable::MaybeRelocatable}, + vm::{ + errors::{runner_errors::RunnerError, vm_errors::VirtualMachineError}, + runners::{ + builtin_runner::{ + BITWISE_BUILTIN_NAME, EC_OP_BUILTIN_NAME, HASH_BUILTIN_NAME, OUTPUT_BUILTIN_NAME, + POSEIDON_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME, SIGNATURE_BUILTIN_NAME, + }, + cairo_runner::{CairoRunner, RunResources, RunnerMode}, + }, + vm_core::VirtualMachine, + }, + Felt252, +}; +use itertools::chain; +use std::collections::HashMap; use crate::{Error, FuncArg, FuncArgs}; diff --git a/cairo1-run/src/main.rs b/cairo1-run/src/main.rs index 7f2bb8a7ec..2d10625f55 100644 --- a/cairo1-run/src/main.rs +++ b/cairo1-run/src/main.rs @@ -1,78 +1,29 @@ -#![allow(unused_imports)] use bincode::enc::write::Writer; -use cairo_lang_casm::casm; -use cairo_lang_casm::casm_extend; -use cairo_lang_casm::hints::Hint; -use cairo_lang_casm::instructions::Instruction; -use cairo_lang_compiler::db; use cairo_lang_compiler::{compile_cairo_project_at_path, CompilerConfig}; -use cairo_lang_sierra::extensions::bitwise::BitwiseType; -use cairo_lang_sierra::extensions::core::{CoreLibfunc, CoreType}; -use cairo_lang_sierra::extensions::ec::EcOpType; -use cairo_lang_sierra::extensions::gas::GasBuiltinType; -use cairo_lang_sierra::extensions::pedersen::PedersenType; -use cairo_lang_sierra::extensions::poseidon::PoseidonType; -use cairo_lang_sierra::extensions::range_check::RangeCheckType; -use cairo_lang_sierra::extensions::segment_arena::SegmentArenaType; -use cairo_lang_sierra::extensions::starknet::syscalls::SystemType; -use cairo_lang_sierra::extensions::ConcreteType; -use cairo_lang_sierra::extensions::NamedType; -use cairo_lang_sierra::ids::ConcreteTypeId; -use cairo_lang_sierra::program::Function; -use cairo_lang_sierra::program::Program as SierraProgram; -use cairo_lang_sierra::program_registry::{ProgramRegistry, ProgramRegistryError}; -use cairo_lang_sierra::{extensions::gas::CostTokenType, ProgramParser}; -use cairo_lang_sierra_ap_change::calc_ap_changes; -use cairo_lang_sierra_gas::gas_info::GasInfo; -use cairo_lang_sierra_to_casm::compiler::CairoProgram; -use cairo_lang_sierra_to_casm::compiler::CompilationError; -use cairo_lang_sierra_to_casm::metadata::Metadata; -use cairo_lang_sierra_to_casm::metadata::MetadataComputationConfig; -use cairo_lang_sierra_to_casm::metadata::MetadataError; -use cairo_lang_sierra_to_casm::{compiler::compile, metadata::calc_metadata}; -use cairo_lang_sierra_type_size::get_type_size_map; -use cairo_lang_utils::extract_matches; -use cairo_lang_utils::ordered_hash_map::OrderedHashMap; -use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; +use cairo_lang_sierra::{ids::ConcreteTypeId, program_registry::ProgramRegistryError}; +use cairo_lang_sierra_to_casm::{compiler::CompilationError, metadata::MetadataError}; use cairo_run::Cairo1RunConfig; -use cairo_vm::air_public_input::PublicInputError; -use cairo_vm::cairo_run::EncodeTraceError; -use cairo_vm::hint_processor::cairo_1_hint_processor::hint_processor::Cairo1HintProcessor; -use cairo_vm::serde::deserialize_program::BuiltinName; -use cairo_vm::serde::deserialize_program::{ApTracking, FlowTrackingData, HintParams}; -use cairo_vm::types::errors::program_errors::ProgramError; -use cairo_vm::types::relocatable::Relocatable; -use cairo_vm::vm::decoding::decoder::decode_instruction; -use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; -use cairo_vm::vm::errors::memory_errors::MemoryError; -use cairo_vm::vm::errors::runner_errors::RunnerError; -use cairo_vm::vm::errors::trace_errors::TraceError; -use cairo_vm::vm::errors::vm_errors::VirtualMachineError; -use cairo_vm::vm::runners::builtin_runner::{ - BITWISE_BUILTIN_NAME, EC_OP_BUILTIN_NAME, HASH_BUILTIN_NAME, OUTPUT_BUILTIN_NAME, - POSEIDON_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME, SIGNATURE_BUILTIN_NAME, -}; -use cairo_vm::vm::runners::cairo_runner::CairoArg; -use cairo_vm::vm::runners::cairo_runner::RunnerMode; -use cairo_vm::vm::vm_memory::memory::Memory; use cairo_vm::{ - serde::deserialize_program::ReferenceManager, - types::{program::Program, relocatable::MaybeRelocatable}, + air_public_input::PublicInputError, + cairo_run::EncodeTraceError, + types::{errors::program_errors::ProgramError, relocatable::MaybeRelocatable}, vm::{ - runners::cairo_runner::{CairoRunner, RunResources}, + errors::{ + memory_errors::MemoryError, runner_errors::RunnerError, trace_errors::TraceError, + vm_errors::VirtualMachineError, + }, vm_core::VirtualMachine, }, Felt252, }; -use clap::{CommandFactory, Parser, ValueHint}; -use itertools::{chain, Itertools}; -use std::borrow::Cow; -use std::io::BufWriter; -use std::io::Write; -use std::iter::Peekable; -use std::path::PathBuf; -use std::slice::Iter; -use std::{collections::HashMap, io, path::Path}; +use clap::{Parser, ValueHint}; +use itertools::Itertools; +use std::{ + io::{self, Write}, + iter::Peekable, + path::PathBuf, + slice::Iter, +}; use thiserror::Error; pub mod cairo_run; From 28afdd83453aaf3c4627adeee81f18917b7cd3b9 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 13:02:58 -0300 Subject: [PATCH 06/14] Fix test --- cairo1-run/src/cairo_run.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 8a8277c532..a692ec1961 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -682,8 +682,12 @@ fn finalize_builtins( mod tests { #![allow(clippy::too_many_arguments)] + use std::path::Path; + use super::*; + use cairo_lang_compiler::{compile_cairo_project_at_path, CompilerConfig}; use rstest::rstest; + use cairo_vm::types::relocatable::Relocatable; fn compile_to_sierra<'a>(filename: &'a str) -> SierraProgram { let compiler_config = CompilerConfig { @@ -703,6 +707,7 @@ mod tests { // Set proof_mode let cairo_run_config = Cairo1RunConfig { proof_mode: true, + layout: "all_cairo", ..Default::default() }; // Run program From 0072cd6efb226b6e11e8f2aa3cff2961166834c9 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 13:12:44 -0300 Subject: [PATCH 07/14] Add a case for each test file --- cairo1-run/src/cairo_run.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index a692ec1961..128ccd5e88 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -686,8 +686,8 @@ mod tests { use super::*; use cairo_lang_compiler::{compile_cairo_project_at_path, CompilerConfig}; - use rstest::rstest; use cairo_vm::types::relocatable::Relocatable; + use rstest::rstest; fn compile_to_sierra<'a>(filename: &'a str) -> SierraProgram { let compiler_config = CompilerConfig { @@ -699,6 +699,23 @@ mod tests { } #[rstest] + #[case(("../cairo_programs/cairo-1-programs/array_append.cairo", false))] + #[case(("../cairo_programs/cairo-1-programs/array_get.cairo", true))] + #[case(("../cairo_programs/cairo-1-programs/array.cairo", false))] + #[case(("../cairo_programs/cairo-1-programs/dictionaries.cairo", false))] + #[case(("../cairo_programs/cairo-1-programs/enum_flow.cairo", false))] + #[case(("../cairo_programs/cairo-1-programs/enum_match.cairo", false))] + #[case(("../cairo_programs/cairo-1-programs/factorial.cairo", true))] + #[case(("../cairo_programs/cairo-1-programs/fibonacci.cairo", true))] + #[case(("../cairo_programs/cairo-1-programs/hello.cairo", false))] + #[case(("../cairo_programs/cairo-1-programs/pedersen_example.cairo", false))] + #[case(("../cairo_programs/cairo-1-programs/poseidon.cairo", true))] + #[case(("../cairo_programs/cairo-1-programs/print.cairo", false))] + #[case(("../cairo_programs/cairo-1-programs/array_append.cairo", false))] + #[case(("../cairo_programs/cairo-1-programs/recursion.cairo", true))] + #[case(("../cairo_programs/cairo-1-programs/sample.cairo", true))] + #[case(("../cairo_programs/cairo-1-programs/simple_struct.cairo", false))] + #[case(("../cairo_programs/cairo-1-programs/simple.cairo", false))] #[case(("../cairo_programs/cairo-1-programs/struct_span_return.cairo", false))] fn check_append_ret_values_to_output_segment(#[case] args: (&str, bool)) { let (filename, may_panic) = args; @@ -726,7 +743,7 @@ mod tests { let output_builtin_segment = vm .get_continuous_range((2, 0).into(), return_values.len()) .unwrap(); - assert_eq!(output_builtin_segment, return_values); + assert_eq!(output_builtin_segment, return_values, "{}", filename); // Just for consistency, we will check that there are no values in the output segment aside from the return values assert!(vm .get_maybe(&Relocatable::from((2_isize, return_values.len()))) From e9227c4063417bed53a74592a697b69967d32377 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 13:20:08 -0300 Subject: [PATCH 08/14] Improve test --- cairo1-run/src/cairo_run.rs | 55 +++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 128ccd5e88..c2922ce903 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -698,27 +698,40 @@ mod tests { compile_cairo_project_at_path(Path::new(filename), compiler_config).unwrap() } + fn main_hash_panic_result(sierra_program: &SierraProgram) -> bool { + let main_func = find_function(sierra_program, "::main").unwrap(); + main_func + .signature + .ret_types + .last() + .and_then(|rt| { + rt.debug_name + .as_ref() + .map(|n| n.as_ref().starts_with("core::panics::PanicResult::")) + }) + .unwrap_or_default() + } + #[rstest] - #[case(("../cairo_programs/cairo-1-programs/array_append.cairo", false))] - #[case(("../cairo_programs/cairo-1-programs/array_get.cairo", true))] - #[case(("../cairo_programs/cairo-1-programs/array.cairo", false))] - #[case(("../cairo_programs/cairo-1-programs/dictionaries.cairo", false))] - #[case(("../cairo_programs/cairo-1-programs/enum_flow.cairo", false))] - #[case(("../cairo_programs/cairo-1-programs/enum_match.cairo", false))] - #[case(("../cairo_programs/cairo-1-programs/factorial.cairo", true))] - #[case(("../cairo_programs/cairo-1-programs/fibonacci.cairo", true))] - #[case(("../cairo_programs/cairo-1-programs/hello.cairo", false))] - #[case(("../cairo_programs/cairo-1-programs/pedersen_example.cairo", false))] - #[case(("../cairo_programs/cairo-1-programs/poseidon.cairo", true))] - #[case(("../cairo_programs/cairo-1-programs/print.cairo", false))] - #[case(("../cairo_programs/cairo-1-programs/array_append.cairo", false))] - #[case(("../cairo_programs/cairo-1-programs/recursion.cairo", true))] - #[case(("../cairo_programs/cairo-1-programs/sample.cairo", true))] - #[case(("../cairo_programs/cairo-1-programs/simple_struct.cairo", false))] - #[case(("../cairo_programs/cairo-1-programs/simple.cairo", false))] - #[case(("../cairo_programs/cairo-1-programs/struct_span_return.cairo", false))] - fn check_append_ret_values_to_output_segment(#[case] args: (&str, bool)) { - let (filename, may_panic) = args; + #[case("../cairo_programs/cairo-1-programs/array_append.cairo")] + #[case("../cairo_programs/cairo-1-programs/array_get.cairo")] + #[case("../cairo_programs/cairo-1-programs/array.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")] + fn check_append_ret_values_to_output_segment(#[case] filename: &str) { // Compile to sierra let sierra_program = compile_to_sierra(filename); // Set proof_mode @@ -731,7 +744,7 @@ mod tests { let (_, vm, return_values) = cairo_run_program(&sierra_program, cairo_run_config).unwrap(); // 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 may_panic { + let return_values = if main_hash_panic_result(&sierra_program) { let mut rv = vec![Felt252::ZERO.into(), Felt252::ZERO.into()]; rv.extend_from_slice(&return_values); rv From c7e214d7034647efc01bc60844ce7539ea3fb375 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 13:24:11 -0300 Subject: [PATCH 09/14] Clippy --- cairo1-run/src/cairo_run.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index c2922ce903..21ccdee057 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -680,8 +680,8 @@ fn finalize_builtins( Ok(()) } +#[cfg(test)] mod tests { - #![allow(clippy::too_many_arguments)] use std::path::Path; use super::*; @@ -689,7 +689,7 @@ mod tests { use cairo_vm::types::relocatable::Relocatable; use rstest::rstest; - fn compile_to_sierra<'a>(filename: &'a str) -> SierraProgram { + fn compile_to_sierra(filename: &str) -> SierraProgram { let compiler_config = CompilerConfig { replace_ids: true, ..CompilerConfig::default() From 2dcbf677486cf3343a5e4bfa340db9140301c43c Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 13:30:15 -0300 Subject: [PATCH 10/14] Add changelog entry --- CHANGELOG.md | 4 ++++ cairo1-run/src/main.rs | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebbe3dfca1..c3c3a078a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ #### Upcoming Changes +* refactor: Refactor `cairo1-run` crate [#1601](https://github.com/lambdaclass/cairo-vm/pull/1601) + * Add function `cairo_run_program` & struct `Cairo1RunConfig` in `cairo1-run::cairo_run` module. + * Function `serialize_output` in crate `cairo1-run` is now public. + * feat: Append return values to the output segment when running cairo1-run in proof_mode [#1597](https://github.com/lambdaclass/cairo-vm/pull/1597) * Add instructions to the proof_mode header to copy return values to the output segment before initiating the infinite loop * Output builtin is now always included when running cairo 1 programs in proof_mode diff --git a/cairo1-run/src/main.rs b/cairo1-run/src/main.rs index 2d10625f55..8973d5fabc 100644 --- a/cairo1-run/src/main.rs +++ b/cairo1-run/src/main.rs @@ -367,7 +367,6 @@ pub fn serialize_output(vm: &VirtualMachine, return_values: &[MaybeRelocatable]) #[cfg(test)] mod tests { - #![allow(clippy::too_many_arguments)] use super::*; use assert_matches::assert_matches; use rstest::rstest; From 96b72af53d18bdeabd13591b3aef9a2690cbf770 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 13:36:25 -0300 Subject: [PATCH 11/14] Use FuncArg slice instead of FuncArgs wrapper in public api --- cairo1-run/src/cairo_run.rs | 10 +++++----- cairo1-run/src/main.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index e60a78f7b0..73555bc7f7 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -46,11 +46,11 @@ use cairo_vm::{ use itertools::chain; use std::collections::HashMap; -use crate::{Error, FuncArg, FuncArgs}; +use crate::{Error, FuncArg}; #[derive(Debug)] pub struct Cairo1RunConfig<'a> { - pub args: FuncArgs, + pub args: &'a [FuncArg], pub trace_enabled: bool, pub relocate_mem: bool, pub layout: &'a str, @@ -63,7 +63,7 @@ pub struct Cairo1RunConfig<'a> { impl Default for Cairo1RunConfig<'_> { fn default() -> Self { Self { - args: FuncArgs::default(), + args: Default::default(), trace_enabled: false, relocate_mem: false, layout: "plain", @@ -99,7 +99,7 @@ pub fn cairo_run_program( main_func, initial_gas, cairo_run_config.proof_mode, - &cairo_run_config.args.0, + cairo_run_config.args, )?; // Fetch return type data @@ -343,7 +343,7 @@ fn create_entry_code( func: &Function, initial_gas: usize, proof_mode: bool, - args: &Vec, + args: &[FuncArg], ) -> Result<(Vec, Vec), Error> { let mut ctx = casm! {}; // The builtins in the formatting expected by the runner. diff --git a/cairo1-run/src/main.rs b/cairo1-run/src/main.rs index 8973d5fabc..a04df32369 100644 --- a/cairo1-run/src/main.rs +++ b/cairo1-run/src/main.rs @@ -70,7 +70,7 @@ pub enum FuncArg { } #[derive(Debug, Clone, Default)] -pub struct FuncArgs(Vec); +struct FuncArgs(Vec); fn process_args(value: &str) -> Result { if value.is_empty() { @@ -212,7 +212,7 @@ fn run(args: impl Iterator) -> Result, Error> { relocate_mem: args.memory_file.is_some() || args.air_public_input.is_some(), layout: &args.layout, trace_enabled: args.trace_file.is_some() || args.air_public_input.is_some(), - args: args.args, + args: &args.args.0, finalize_builtins: args.air_private_input.is_some() || args.cairo_pie_output.is_some(), }; From 4007848180972b4bd3ce7bc982d24b4332d82000 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 13:36:43 -0300 Subject: [PATCH 12/14] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3c3a078a1..b31024d77c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ * refactor: Refactor `cairo1-run` crate [#1601](https://github.com/lambdaclass/cairo-vm/pull/1601) * Add function `cairo_run_program` & struct `Cairo1RunConfig` in `cairo1-run::cairo_run` module. - * Function `serialize_output` in crate `cairo1-run` is now public. + * Function `serialize_output` & structs `FuncArg` and `Error` in crate `cairo1-run` are now public. * feat: Append return values to the output segment when running cairo1-run in proof_mode [#1597](https://github.com/lambdaclass/cairo-vm/pull/1597) * Add instructions to the proof_mode header to copy return values to the output segment before initiating the infinite loop From 6a06dd4c0e83479c8dd63d3720e1dfafb5b7231c Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 13:39:13 -0300 Subject: [PATCH 13/14] remove test case for ghost file --- cairo1-run/src/cairo_run.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 4bad6dbbaa..fc04095b3f 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -715,7 +715,6 @@ 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/array.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")] From 1079913f57221b8c49a5c80d952dbc0559602f29 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 6 Feb 2024 13:40:52 -0300 Subject: [PATCH 14/14] Fix misleading comment --- cairo1-run/src/cairo_run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index fc04095b3f..eccd039fb3 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -756,7 +756,7 @@ mod tests { .get_continuous_range((2, 0).into(), return_values.len()) .unwrap(); assert_eq!(output_builtin_segment, return_values, "{}", filename); - // Just for consistency, we will check that there are no values in the output segment aside from the return values + // Just for consistency, we will check that there are no values in the output segment after the return values assert!(vm .get_maybe(&Relocatable::from((2_isize, return_values.len()))) .is_none());