From ef5d214ea895e40c1818e3b54c71e56c6e0551f0 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 7 Jun 2024 12:50:49 -0300 Subject: [PATCH] Remove cairo1-run --- cairo1-run/Cargo.toml | 33 -- cairo1-run/Makefile | 33 -- cairo1-run/README.md | 31 -- cairo1-run/src/cairo_run.rs | 766 ------------------------------------ cairo1-run/src/main.rs | 722 --------------------------------- 5 files changed, 1585 deletions(-) delete mode 100644 cairo1-run/Cargo.toml delete mode 100644 cairo1-run/Makefile delete mode 100644 cairo1-run/README.md delete mode 100644 cairo1-run/src/cairo_run.rs delete mode 100644 cairo1-run/src/main.rs diff --git a/cairo1-run/Cargo.toml b/cairo1-run/Cargo.toml deleted file mode 100644 index 85b98e3927..0000000000 --- a/cairo1-run/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "cairo1-run" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true -keywords.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -cairo-vm = {workspace = true, features = ["std", "cairo-1-hints"]} - -cairo-lang-sierra-type-size = { version = "2.3.1", default-features = false } -cairo-lang-sierra-ap-change = { version = "2.3.1", default-features = false } -cairo-lang-sierra-gas = { version = "2.3.1", default-features = false } -cairo-lang-sierra-to-casm.workspace = true -cairo-lang-compiler.workspace = true -cairo-lang-sierra.workspace = true -cairo-lang-utils.workspace = true -cairo-lang-casm.workspace = true -itertools = "0.11.0" -clap = { version = "4.3.10", features = ["derive"] } -thiserror = { version = "1.0.40" } -bincode.workspace = true -assert_matches = "1.5.0" -rstest = "0.17.0" -mimalloc = { version = "0.1.37", default-features = false, optional = true } - -[features] -default = ["with_mimalloc"] -with_mimalloc = ["cairo-vm/with_mimalloc", "mimalloc"] -lambdaworks-felt = ["cairo-vm/lambdaworks-felt"] diff --git a/cairo1-run/Makefile b/cairo1-run/Makefile deleted file mode 100644 index 337cf3a53e..0000000000 --- a/cairo1-run/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -.PHONY: deps factorial fibonacci test clean run - -CAIRO_1_FOLDER=../cairo_programs/cairo-1-programs - -$(CAIRO_1_FOLDER)/%.trace: $(CAIRO_1_FOLDER)/%.cairo - cargo run --release $< --trace_file $@ --layout all_cairo - -$(CAIRO_1_FOLDER)/%.memory: $(CAIRO_1_FOLDER)/%.cairo - cargo run --release $< --memory_file $@ --layout all_cairo - -CAIRO_1_PROGRAMS=$(wildcard ../cairo_programs/cairo-1-programs/*.cairo) -TRACES:=$(patsubst $(CAIRO_1_FOLDER)/%.cairo, $(CAIRO_1_FOLDER)/%.trace, $(CAIRO_1_PROGRAMS)) -MEMORY:=$(patsubst $(CAIRO_1_FOLDER)/%.cairo, $(CAIRO_1_FOLDER)/%.memory, $(CAIRO_1_PROGRAMS)) - -deps: - git clone https://github.com/starkware-libs/cairo.git \ - && cd cairo \ - && git checkout v2.3.1 \ - && cd .. \ - && mv cairo/corelib/ . \ - && rm -rf cairo/ - -run: $(TRACES) $(MEMORY) - -test: - cargo test - -clean: - rm -rf corelib - rm -rf cairo - rm -rf ../cairo_programs/cairo-1-programs/*.memory - rm -rf ../cairo_programs/cairo-1-programs/*.trace - diff --git a/cairo1-run/README.md b/cairo1-run/README.md deleted file mode 100644 index 95be3b5ecb..0000000000 --- a/cairo1-run/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Cairo1-run - -A cairo-vm crate to run Cairo 1 Programs - -Once you are inside the `./cairo1-run` folder, use the CLI with the following commands - -To install the required dependencies(cairo corelib) run -```bash -make deps -``` - -Now that you have the dependencies necessary to run the tests, you can run: - -```bash -make test -``` - -To execute a cairo 1 program -```bash -cargo run ../cairo_programs/cairo-1-programs/fibonacci.cairo -``` - -Arguments to generate the trace and memory files -```bash -cargo run ../cairo_programs/cairo-1-programs/fibonacci.cairo --trace_file ../cairo_programs/cairo-1-programs/fibonacci.trace --memory_file ../cairo_programs/cairo-1-programs/fibonacci.memory -``` - -To execute all the cairo 1 programs inside `../cairo_programs/cairo-1-programs/` and generate the corresponding trace and the memory files -```bash -make run -``` diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs deleted file mode 100644 index 5dbda6c672..0000000000 --- a/cairo1-run/src/cairo_run.rs +++ /dev/null @@ -1,766 +0,0 @@ -use cairo_lang_casm::{casm, casm_extend, hints::Hint, instructions::Instruction}; -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::CairoProgram, - metadata::{calc_metadata, Metadata, MetadataComputationConfig, MetadataError}, -}; -use cairo_lang_sierra_type_size::get_type_size_map; -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}; - -#[derive(Debug)] -pub struct Cairo1RunConfig<'a> { - pub args: &'a [FuncArg], - 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, -} - -impl Default for Cairo1RunConfig<'_> { - fn default() -> Self { - Self { - args: Default::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( - 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 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, - )?; - - // 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, cairo_run_config.proof_mode)?; - - 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: &[FuncArg], -) -> 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: &[ConcreteTypeId], - 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(()) -} - -#[cfg(test)] -mod tests { - use std::path::Path; - - use super::*; - use cairo_lang_compiler::{compile_cairo_project_at_path, CompilerConfig}; - use cairo_vm::types::relocatable::Relocatable; - use rstest::rstest; - - fn compile_to_sierra(filename: &str) -> SierraProgram { - let compiler_config = CompilerConfig { - replace_ids: true, - ..CompilerConfig::default() - }; - - 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")] - #[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")] - fn check_append_ret_values_to_output_segment(#[case] filename: &str) { - // Compile to sierra - let sierra_program = compile_to_sierra(filename); - // Set proof_mode - let cairo_run_config = Cairo1RunConfig { - proof_mode: true, - layout: "all_cairo", - ..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 main_hash_panic_result(&sierra_program) { - 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, "{}", filename); - // 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()); - } -} diff --git a/cairo1-run/src/main.rs b/cairo1-run/src/main.rs deleted file mode 100644 index ce711ca554..0000000000 --- a/cairo1-run/src/main.rs +++ /dev/null @@ -1,722 +0,0 @@ -#![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::{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::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::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::{ - felt::Felt252, - serde::deserialize_program::ReferenceManager, - types::{program::Program, relocatable::MaybeRelocatable}, - vm::{ - runners::cairo_runner::{CairoRunner, RunResources}, - vm_core::VirtualMachine, - }, -}; -use clap::{CommandFactory, Parser, ValueHint}; -use itertools::{chain, Itertools}; -use std::borrow::Cow; -use std::io::BufWriter; -use std::io::Write; -use std::path::PathBuf; -use std::{collections::HashMap, io, path::Path}; -use thiserror::Error; - -#[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] -struct Args { - #[clap(value_parser, value_hint=ValueHint::FilePath)] - filename: PathBuf, - #[clap(long = "trace_file", value_parser)] - trace_file: Option, - #[structopt(long = "memory_file")] - memory_file: Option, - #[clap(long = "layout", default_value = "plain", value_parser=validate_layout)] - layout: String, -} - -fn validate_layout(value: &str) -> Result { - match value { - "plain" - | "small" - | "dex" - | "starknet" - | "starknet_with_keccak" - | "recursive_large_output" - | "all_cairo" - | "all_solidity" - | "dynamic" => Ok(value.to_string()), - _ => Err(format!("{value} is not a valid layout")), - } -} - -#[derive(Debug, Error)] -enum Error { - #[error("Invalid arguments")] - Cli(#[from] clap::Error), - #[error("Failed to interact with the file system")] - IO(#[from] std::io::Error), - #[error(transparent)] - EncodeTrace(#[from] EncodeTraceError), - #[error(transparent)] - VirtualMachine(#[from] VirtualMachineError), - #[error(transparent)] - Trace(#[from] TraceError), - #[error(transparent)] - PublicInput(#[from] PublicInputError), - #[error(transparent)] - Runner(#[from] RunnerError), - #[error(transparent)] - ProgramRegistry(#[from] Box), - #[error(transparent)] - Compilation(#[from] Box), - #[error("Failed to compile to sierra:\n {0}")] - SierraCompilation(String), - #[error(transparent)] - Metadata(#[from] MetadataError), - #[error(transparent)] - Program(#[from] ProgramError), - #[error(transparent)] - Memory(#[from] MemoryError), - #[error("Program panicked with {0:?}")] - RunPanic(Vec), - #[error("Function signature has no return types")] - NoRetTypesInSignature, - #[error("No size for concrete type id: {0}")] - NoTypeSizeForId(ConcreteTypeId), - #[error("Concrete type id has no debug name: {0}")] - TypeIdNoDebugName(ConcreteTypeId), - #[error("No info in sierra program registry for concrete type id: {0}")] - NoInfoForType(ConcreteTypeId), - #[error("Failed to extract return values from VM")] - FailedToExtractReturnValues, -} - -pub struct FileWriter { - buf_writer: io::BufWriter, - bytes_written: usize, -} - -impl Writer for FileWriter { - fn write(&mut self, bytes: &[u8]) -> Result<(), bincode::error::EncodeError> { - self.buf_writer - .write_all(bytes) - .map_err(|e| bincode::error::EncodeError::Io { - inner: e, - index: self.bytes_written, - })?; - - self.bytes_written += bytes.len(); - - Ok(()) - } -} - -impl FileWriter { - fn new(buf_writer: io::BufWriter) -> Self { - Self { - buf_writer, - bytes_written: 0, - } - } - - fn flush(&mut self) -> io::Result<()> { - self.buf_writer.flush() - } -} - -fn run(args: impl Iterator) -> Result, Error> { - let args = Args::try_parse_from(args)?; - - 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()))?) - .clone(); - - 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; - - // Entry code and footer are part of the whole instructions that are - // ran by the VM. - let (entry_code, builtins) = create_entry_code( - &sierra_program_registry, - &casm_program, - &type_sizes, - main_func, - initial_gas, - )?; - let footer = create_code_footer(); - - let check_gas_usage = true; - let metadata = calc_metadata(&sierra_program, Default::default(), false)?; - let casm_program = compile(&sierra_program, &metadata, check_gas_usage)?; - - let instructions = chain!( - entry_code.iter(), - casm_program.instructions.iter(), - 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(Felt252::from) - .map(MaybeRelocatable::from) - .collect(); - - let data_len = data.len(); - - let program = Program::new( - builtins, - data, - Some(0), - program_hints, - ReferenceManager { - references: Vec::new(), - }, - HashMap::new(), - vec![], - None, - )?; - - let mut runner = CairoRunner::new(&program, &args.layout, false)?; - let mut vm = VirtualMachine::new(args.trace_file.is_some()); - let end = runner.initialize(&mut vm)?; - - additional_initialization(&mut vm, data_len)?; - - runner.run_until_pc(end, &mut vm, &mut hint_processor)?; - runner.end_run(true, 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 teh 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().clone()).collect(), - )); - } else { - if return_values.len() < 3 { - return Err(Error::FailedToExtractReturnValues); - } - return_values = return_values[2..].to_vec() - } - } - - runner.relocate(&mut vm, true)?; - - if let Some(trace_path) = args.trace_file { - let relocated_trace = vm.get_relocated_trace()?; - let trace_file = std::fs::File::create(trace_path)?; - 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)?; - trace_writer.flush()?; - } - if let Some(memory_path) = args.memory_file { - let memory_file = std::fs::File::create(memory_path)?; - 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)?; - memory_writer.flush()?; - } - - Ok(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(()) -} - -fn main() -> Result<(), Error> { - match run(std::env::args()) { - Err(Error::Cli(err)) => err.exit(), - Ok(return_values) => { - if !return_values.is_empty() { - let return_values_string_list = - return_values.iter().map(|m| m.to_string()).join(", "); - println!("Return values : [{}]", return_values_string_list); - } - Ok(()) - } - Err(Error::RunPanic(panic_data)) => { - if !panic_data.is_empty() { - let panic_data_string_list = panic_data - .iter() - .map(|m| { - // Try to parse to utf8 string - let msg = String::from_utf8(m.to_be_bytes().to_vec()); - if let Ok(msg) = msg { - format!("{} ('{}')", m, msg) - } else { - m.to_string() - } - }) - .join(", "); - println!("Run panicked with: [{}]", panic_data_string_list); - } - Ok(()) - } - Err(err) => Err(err), - } -} - -#[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, -) -> 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); - // Load all vecs to memory. - let mut ap_offset: i16 = 0; - let after_vecs_offset = ap_offset; - 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 ty_size = type_sizes[ty]; - let generic_ty = &info.long_id.generic_id; - if let Some(offset) = builtin_offset.get(generic_ty) { - casm_extend! {ctx, - [ap + 0] = [fp - offset], ap++; - } - } else if generic_ty == &SystemType::ID { - casm_extend! {ctx, - %{ memory[ap + 0] = segments.add() %} - ap += 1; - } - } else if generic_ty == &GasBuiltinType::ID { - casm_extend! {ctx, - [ap + 0] = initial_gas, ap++; - } - } else if generic_ty == &SegmentArenaType::ID { - let offset = -ap_offset + after_vecs_offset; - casm_extend! {ctx, - [ap + 0] = [ap + offset] + 3, ap++; - } - // } else if let Some(Arg::Array(_)) = arg_iter.peek() { - // let values = extract_matches!(arg_iter.next().unwrap(), Arg::Array); - // let offset = -ap_offset + vecs.pop().unwrap(); - // expected_arguments_size += 1; - // casm_extend! {ctx, - // [ap + 0] = [ap + (offset)], ap++; - // [ap + 0] = [ap - 1] + (values.len()), ap++; - // } - // } else { - // let arg_size = ty_size; - // expected_arguments_size += arg_size as usize; - // for _ in 0..arg_size { - // if let Some(value) = arg_iter.next() { - // let value = extract_matches!(value, Arg::Value); - // casm_extend! {ctx, - // [ap + 0] = (value.to_bigint()), ap++; - // } - // } - // } - }; - ap_offset += ty_size; - } - // if expected_arguments_size != args.len() { - // return Err(RunnerError::ArgumentsSizeMismatch { - // expected: expected_arguments_size, - // actual: args.len(), - // }); - // } - 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, false).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(), - }, - }) - } -} - -fn get_function_builtins( - func: &Function, -) -> ( - 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); - } - builtins.reverse(); - (builtins, builtin_offset) -} - -#[cfg(test)] -mod tests { - #![allow(clippy::too_many_arguments)] - use super::*; - use assert_matches::assert_matches; - use cairo_vm::felt::felt_str; - use rstest::rstest; - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/fibonacci.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_fibonacci_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(89)]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/factorial.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_factorial_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(3628800)]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/array_get.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_array_get_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(3)]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/enum_flow.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_enum_flow_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(300)]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/enum_match.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_enum_match_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(10), MaybeRelocatable::from(felt_str!("3618502788666131213697322783095070105623107215331596699973092056135872020471"))]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/hello.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_hello_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(1), MaybeRelocatable::from(1234)]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/ops.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_ops_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(6)]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/print.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_print_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/recursion.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_recursion_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(felt_str!("1154076154663935037074198317650845438095734251249125412074882362667803016453"))]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/sample.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_sample_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(felt_str!("500000500000"))]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/poseidon.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_poseidon_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(felt_str!("1099385018355113290651252669115094675591288647745213771718157553170111442461"))]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/poseidon_pedersen.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_poseidon_pedersen_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(felt_str!("1036257840396636296853154602823055519264738423488122322497453114874087006398"))]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/pedersen_example.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_pedersen_example_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(felt_str!("1089549915800264549621536909767699778745926517555586332772759280702396009108"))]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/simple.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_simple_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(1)]); - } - - #[rstest] - #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/simple_struct.cairo", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo"].as_slice())] - fn test_run_simple_struct_ok(#[case] args: &[&str]) { - let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(res) if res == vec![MaybeRelocatable::from(100)]); - } -}