From 01fcea479b03cfef10ae5a0d99337c9b04c0578f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Est=C3=A9fano=20Bargas?= Date: Wed, 26 Jul 2023 17:09:13 -0300 Subject: [PATCH] Add `--air_public_input` to runner (#1268) * Add air_public_input arg * Added get_public_memory_addresses() * Added get_memory_segment_addresses * Added write_air_public_input skeleton * Added air_public_input.rs * Added fixme for unwrap * Fix get_memory_segment_addresses() * Finished implementation * Fixed relocated memory issue * Fixed trace issue * Removed rc limits unwrap * Refactor * Added changelog entry * Remove memory addresses structs * Fix changelog * Refactor get_public_memory_addresses * Fixed relocation issue on builtin addresses * Replaced get_trace() with get_relocated_trace() * Use references on new() * Removed trace indexing * Remove allow unused from air public input * Fix all builtins stop_ptr * Handle write result * Removed indexing * Remove indexing * Partial fix cli error * Fixed error formatting * Added get_air_public_input() to cairo runner * Fix clippy * Change to clearer code * Update vm/src/air_public_input.rs Co-authored-by: fmoletta <99273364+fmoletta@users.noreply.github.com> * Update changelog * Update vm/src/air_public_input.rs Co-authored-by: fmoletta <99273364+fmoletta@users.noreply.github.com> * Reorder to avoid cloning * Fix main test * Custom ser for mem value * Another fix for main test * Return NoStopPtr err instead of using Option * Make get_layout private * Clone page_id * Return err if rc values are missing * Added new MemorySegmentAddresses * Fix changelog * Fix test main * Fix wasm build * Fix clippy * Fix nostd again * Dummy commit * Revert "Dummy commit" This reverts commit 6b01c6628f1d6b28325827d8d219d17a2790897b. * New main test * Added air_public_input test, changed main * Allow too many args in test * Try deny deny * Fix clippy * Fix clap * Fix argument * Changed run rstest --------- Co-authored-by: fmoletta <99273364+fmoletta@users.noreply.github.com> Co-authored-by: Pedro Fontana Co-authored-by: Mario Rugiero --- CHANGELOG.md | 3 + cairo-vm-cli/src/main.rs | 62 +++++--- vm/src/air_public_input.rs | 140 ++++++++++++++++++ vm/src/lib.rs | 1 + .../bitwise_instance_def.rs | 4 +- .../builtins_instance_def.rs | 3 +- .../instance_definitions/cpu_instance_def.rs | 4 +- .../diluted_pool_instance_def.rs | 4 +- .../ec_op_instance_def.rs | 4 +- .../ecdsa_instance_def.rs | 4 +- .../keccak_instance_def.rs | 3 +- .../pedersen_instance_def.rs | 3 +- .../poseidon_instance_def.rs | 4 +- .../range_check_instance_def.rs | 3 +- vm/src/types/layout.rs | 6 +- vm/src/vm/errors/memory_errors.rs | 2 + vm/src/vm/errors/vm_errors.rs | 2 + vm/src/vm/runners/builtin_runner/bitwise.rs | 3 +- vm/src/vm/runners/builtin_runner/ec_op.rs | 3 +- vm/src/vm/runners/builtin_runner/hash.rs | 3 +- vm/src/vm/runners/builtin_runner/keccak.rs | 3 +- vm/src/vm/runners/builtin_runner/output.rs | 3 +- vm/src/vm/runners/builtin_runner/poseidon.rs | 3 +- .../vm/runners/builtin_runner/range_check.rs | 3 +- vm/src/vm/runners/builtin_runner/signature.rs | 3 +- vm/src/vm/runners/cairo_runner.rs | 34 ++++- vm/src/vm/vm_core.rs | 44 ++++++ vm/src/vm/vm_memory/memory_segments.rs | 19 +++ 28 files changed, 326 insertions(+), 47 deletions(-) create mode 100644 vm/src/air_public_input.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0211c6e04f..e0f37b8863 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ## Cairo-VM Changelog #### Upcoming Changes +* feat: implement the `--air_public_input` flag to the runner for outputting public inputs into a file [#1268](https://github.com/lambdaclass/cairo-rs/pull/1268) + +* fix: CLI errors bad formatting and handling * fix: return error when a parsed hint's PC is invalid [#1340](https://github.com/lambdaclass/cairo-vm/pull/1340) diff --git a/cairo-vm-cli/src/main.rs b/cairo-vm-cli/src/main.rs index 552f4df39f..06a51c713e 100644 --- a/cairo-vm-cli/src/main.rs +++ b/cairo-vm-cli/src/main.rs @@ -1,12 +1,13 @@ #![deny(warnings)] #![forbid(unsafe_code)] use bincode::enc::write::Writer; +use cairo_vm::air_public_input::PublicInputError; use cairo_vm::cairo_run::{self, EncodeTraceError}; use cairo_vm::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; use cairo_vm::vm::errors::trace_errors::TraceError; use cairo_vm::vm::errors::vm_errors::VirtualMachineError; -use clap::{Parser, ValueHint}; +use clap::{CommandFactory, Parser, ValueHint}; use std::io::{self, Write}; use std::path::PathBuf; use thiserror::Error; @@ -37,6 +38,8 @@ struct Args { proof_mode: bool, #[structopt(long = "secure_run")] secure_run: Option, + #[clap(long = "air_public_input")] + air_public_input: Option, } fn validate_layout(value: &str) -> Result { @@ -68,6 +71,8 @@ enum Error { VirtualMachine(#[from] VirtualMachineError), #[error(transparent)] Trace(#[from] TraceError), + #[error(transparent)] + PublicInput(#[from] PublicInputError), } struct FileWriter { @@ -104,20 +109,22 @@ impl FileWriter { } fn run(args: impl Iterator) -> Result<(), Error> { - let args = Args::try_parse_from(args); - let args = match args { - Ok(args) => args, - Err(error) => { - eprintln!("{error}"); - return Err(Error::Cli(error)); - } - }; - let trace_enabled = args.trace_file.is_some(); + let args = Args::try_parse_from(args)?; + + if args.air_public_input.is_some() && !args.proof_mode { + let error = Args::command().error( + clap::error::ErrorKind::ArgumentConflict, + "--air_public_input can only be used in proof_mode.", + ); + return Err(Error::Cli(error)); + } + + let trace_enabled = args.trace_file.is_some() || args.air_public_input.is_some(); let mut hint_executor = BuiltinHintProcessor::new_empty(); let cairo_run_config = cairo_run::CairoRunConfig { entrypoint: &args.entrypoint, trace_enabled, - relocate_mem: args.memory_file.is_some(), + relocate_mem: args.memory_file.is_some() || args.air_public_input.is_some(), layout: &args.layout, proof_mode: args.proof_mode, secure_run: args.secure_run, @@ -160,21 +167,28 @@ fn run(args: impl Iterator) -> Result<(), Error> { memory_writer.flush()?; } + if let Some(file_path) = args.air_public_input { + let json = cairo_runner.get_air_public_input(&vm)?.serialize_json()?; + std::fs::write(file_path, json)?; + } + Ok(()) } fn main() -> Result<(), Error> { + #[cfg(test)] + return Ok(()); + + #[cfg(not(test))] match run(std::env::args()) { - Ok(()) => Ok(()), - Err(Error::Cli(_)) => { - Ok(()) // Exit with code 0 to avoid printing CLI error message - } - Err(error) => Err(error), + Err(Error::Cli(err)) => err.exit(), + other => other, } } #[cfg(test)] mod tests { + #![allow(clippy::too_many_arguments)] use super::*; use assert_matches::assert_matches; use rstest::rstest; @@ -215,11 +229,18 @@ mod tests { #[values(false, true)] secure_run: bool, #[values(false, true)] print_output: bool, #[values(false, true)] entrypoint: bool, + #[values(false, true)] air_public_input: bool, ) { let mut args = vec!["cairo-vm-cli".to_string()]; if let Some(layout) = layout { args.extend_from_slice(&["--layout".to_string(), layout.to_string()]); } + if air_public_input { + args.extend_from_slice(&[ + "--air_public_input".to_string(), + "air_input.pub".to_string(), + ]); + } if proof_mode { trace_file = true; args.extend_from_slice(&["--proof_mode".to_string()]); @@ -239,8 +260,13 @@ mod tests { if print_output { args.extend_from_slice(&["--print_output".to_string()]); } + args.push("../cairo_programs/proof_programs/fibonacci.json".to_string()); - assert_matches!(run(args.into_iter()), Ok(_)); + if air_public_input && !proof_mode { + assert_matches!(run(args.into_iter()), Err(_)); + } else { + assert_matches!(run(args.into_iter()), Ok(_)); + } } #[test] @@ -266,7 +292,7 @@ mod tests { //to fool Codecov. #[test] fn test_main() { - assert!(main().is_ok()); + main().unwrap(); } #[test] diff --git a/vm/src/air_public_input.rs b/vm/src/air_public_input.rs new file mode 100644 index 0000000000..75e76e58ea --- /dev/null +++ b/vm/src/air_public_input.rs @@ -0,0 +1,140 @@ +use felt::Felt252; +use serde::Serialize; +use thiserror_no_std::Error; + +use crate::{ + stdlib::{ + collections::HashMap, + prelude::{String, Vec}, + }, + types::layout::CairoLayout, + vm::{ + errors::{trace_errors::TraceError, vm_errors::VirtualMachineError}, + trace::trace_entry::TraceEntry, + }, +}; + +#[derive(Serialize, Debug)] +pub struct PublicMemoryEntry { + address: usize, + page: usize, + #[serde(serialize_with = "mem_value_serde::serialize")] + value: Option, +} + +mod mem_value_serde { + use super::*; + use serde::Serializer; + + pub(crate) fn serialize( + value: &Option, + serializer: S, + ) -> Result { + if let Some(value) = value { + serializer.serialize_str(&format!("0x{}", value.to_str_radix(16))) + } else { + serializer.serialize_none() + } + } +} + +#[derive(Serialize, Debug)] +struct MemorySegmentAddresses { + begin_addr: usize, + stop_addr: usize, +} + +impl From<(usize, usize)> for MemorySegmentAddresses { + fn from(addresses: (usize, usize)) -> Self { + let (begin_addr, stop_addr) = addresses; + MemorySegmentAddresses { + begin_addr, + stop_addr, + } + } +} + +#[derive(Serialize, Debug)] +pub struct PublicInput<'a> { + layout: &'a str, + layout_params: Option<&'a CairoLayout>, + rc_min: isize, + rc_max: isize, + n_steps: usize, + memory_segments: HashMap<&'a str, MemorySegmentAddresses>, + public_memory: Vec, +} + +impl<'a> PublicInput<'a> { + pub fn new( + memory: &[Option], + layout: &'a str, + dyn_layout_params: Option<&'a CairoLayout>, + public_memory_addresses: &[(usize, usize)], + memory_segment_addresses: HashMap<&'static str, (usize, usize)>, + trace: &[TraceEntry], + rc_limits: (isize, isize), + ) -> Result { + let memory_entry = + |addresses: &(usize, usize)| -> Result { + let (address, page) = addresses; + Ok(PublicMemoryEntry { + address: *address, + page: *page, + value: memory + .get(*address) + .ok_or(PublicInputError::MemoryNotFound(*address))? + .clone(), + }) + }; + let public_memory = public_memory_addresses + .iter() + .map(memory_entry) + .collect::, _>>()?; + + let (rc_min, rc_max) = rc_limits; + + let trace_first = trace.first().ok_or(PublicInputError::EmptyTrace)?; + let trace_last = trace.last().ok_or(PublicInputError::EmptyTrace)?; + + Ok(PublicInput { + layout, + layout_params: dyn_layout_params, + rc_min, + rc_max, + n_steps: trace.len(), + memory_segments: { + let mut memory_segment_addresses = memory_segment_addresses + .into_iter() + .map(|(n, s)| (n, s.into())) + .collect::>(); + + memory_segment_addresses.insert("program", (trace_first.pc, trace_last.pc).into()); + memory_segment_addresses + .insert("execution", (trace_first.ap, trace_last.ap).into()); + memory_segment_addresses + }, + public_memory, + }) + } + + pub fn serialize_json(&self) -> Result { + serde_json::to_string_pretty(&self).map_err(PublicInputError::from) + } +} + +#[derive(Debug, Error)] +pub enum PublicInputError { + #[error("The trace slice provided is empty")] + EmptyTrace, + #[error("The provided memory doesn't contain public address {0}")] + MemoryNotFound(usize), + #[error("Range check values are missing")] + NoRangeCheckLimits, + #[error("Failed to (de)serialize data")] + Serde(#[from] serde_json::Error), + #[error(transparent)] + VirtualMachine(#[from] VirtualMachineError), + #[error(transparent)] + Trace(#[from] TraceError), +} diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 23205cc8d2..1d60dfaebf 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -54,6 +54,7 @@ mod stdlib { } pub extern crate felt; +pub mod air_public_input; pub mod cairo_run; pub mod hint_processor; pub mod math_utils; diff --git a/vm/src/types/instance_definitions/bitwise_instance_def.rs b/vm/src/types/instance_definitions/bitwise_instance_def.rs index a0d68637fe..e59c219fd3 100644 --- a/vm/src/types/instance_definitions/bitwise_instance_def.rs +++ b/vm/src/types/instance_definitions/bitwise_instance_def.rs @@ -1,7 +1,9 @@ +use serde::Serialize; + pub(crate) const CELLS_PER_BITWISE: u32 = 5; pub(crate) const INPUT_CELLS_PER_BITWISE: u32 = 2; -#[derive(Clone, Debug, PartialEq)] +#[derive(Serialize, Clone, Debug, PartialEq)] pub(crate) struct BitwiseInstanceDef { pub(crate) ratio: Option, pub(crate) total_n_bits: u32, diff --git a/vm/src/types/instance_definitions/builtins_instance_def.rs b/vm/src/types/instance_definitions/builtins_instance_def.rs index 6765be1633..019a926b8f 100644 --- a/vm/src/types/instance_definitions/builtins_instance_def.rs +++ b/vm/src/types/instance_definitions/builtins_instance_def.rs @@ -4,8 +4,9 @@ use super::{ pedersen_instance_def::PedersenInstanceDef, poseidon_instance_def::PoseidonInstanceDef, range_check_instance_def::RangeCheckInstanceDef, }; +use serde::Serialize; -#[derive(Debug, PartialEq)] +#[derive(Serialize, Debug, PartialEq)] pub(crate) struct BuiltinsInstanceDef { pub(crate) output: bool, pub(crate) pedersen: Option, diff --git a/vm/src/types/instance_definitions/cpu_instance_def.rs b/vm/src/types/instance_definitions/cpu_instance_def.rs index 0bee2496ea..19d22643a1 100644 --- a/vm/src/types/instance_definitions/cpu_instance_def.rs +++ b/vm/src/types/instance_definitions/cpu_instance_def.rs @@ -1,4 +1,6 @@ -#[derive(Debug, PartialEq)] +use serde::Serialize; + +#[derive(Serialize, Debug, PartialEq)] pub(crate) struct CpuInstanceDef { pub(crate) _safe_call: bool, } diff --git a/vm/src/types/instance_definitions/diluted_pool_instance_def.rs b/vm/src/types/instance_definitions/diluted_pool_instance_def.rs index 8138c573bd..24e1a51ba6 100644 --- a/vm/src/types/instance_definitions/diluted_pool_instance_def.rs +++ b/vm/src/types/instance_definitions/diluted_pool_instance_def.rs @@ -1,4 +1,6 @@ -#[derive(Debug, PartialEq)] +use serde::Serialize; + +#[derive(Serialize, Debug, PartialEq)] pub(crate) struct DilutedPoolInstanceDef { pub(crate) units_per_step: u32, pub(crate) spacing: u32, diff --git a/vm/src/types/instance_definitions/ec_op_instance_def.rs b/vm/src/types/instance_definitions/ec_op_instance_def.rs index ef0dc08f6c..75e8039dba 100644 --- a/vm/src/types/instance_definitions/ec_op_instance_def.rs +++ b/vm/src/types/instance_definitions/ec_op_instance_def.rs @@ -1,7 +1,9 @@ +use serde::Serialize; + pub(crate) const CELLS_PER_EC_OP: u32 = 7; pub(crate) const INPUT_CELLS_PER_EC_OP: u32 = 5; -#[derive(Clone, Debug, PartialEq)] +#[derive(Serialize, Clone, Debug, PartialEq)] pub(crate) struct EcOpInstanceDef { pub(crate) ratio: Option, pub(crate) scalar_height: u32, diff --git a/vm/src/types/instance_definitions/ecdsa_instance_def.rs b/vm/src/types/instance_definitions/ecdsa_instance_def.rs index 15e7360567..518e5f5288 100644 --- a/vm/src/types/instance_definitions/ecdsa_instance_def.rs +++ b/vm/src/types/instance_definitions/ecdsa_instance_def.rs @@ -1,7 +1,9 @@ +use serde::Serialize; + pub(crate) const CELLS_PER_SIGNATURE: u32 = 2; pub(crate) const _INPUTCELLS_PER_SIGNATURE: u32 = 2; -#[derive(Debug, PartialEq)] +#[derive(Serialize, Debug, PartialEq)] pub(crate) struct EcdsaInstanceDef { pub(crate) ratio: Option, pub(crate) _repetitions: u32, diff --git a/vm/src/types/instance_definitions/keccak_instance_def.rs b/vm/src/types/instance_definitions/keccak_instance_def.rs index 7ed8bf9bd5..655c361fcc 100644 --- a/vm/src/types/instance_definitions/keccak_instance_def.rs +++ b/vm/src/types/instance_definitions/keccak_instance_def.rs @@ -1,6 +1,7 @@ use crate::stdlib::prelude::*; +use serde::Serialize; -#[derive(Clone, Debug, PartialEq)] +#[derive(Serialize, Clone, Debug, PartialEq)] pub(crate) struct KeccakInstanceDef { pub(crate) ratio: Option, pub(crate) _state_rep: Vec, diff --git a/vm/src/types/instance_definitions/pedersen_instance_def.rs b/vm/src/types/instance_definitions/pedersen_instance_def.rs index cca3a89a57..d6273f35f1 100644 --- a/vm/src/types/instance_definitions/pedersen_instance_def.rs +++ b/vm/src/types/instance_definitions/pedersen_instance_def.rs @@ -1,9 +1,10 @@ use num_bigint::{BigInt, Sign}; +use serde::Serialize; pub(crate) const CELLS_PER_HASH: u32 = 3; pub(crate) const INPUT_CELLS_PER_HASH: u32 = 2; -#[derive(Debug, PartialEq)] +#[derive(Serialize, Debug, PartialEq)] pub(crate) struct PedersenInstanceDef { pub(crate) ratio: Option, pub(crate) _repetitions: u32, diff --git a/vm/src/types/instance_definitions/poseidon_instance_def.rs b/vm/src/types/instance_definitions/poseidon_instance_def.rs index 20b0636075..fa478c4c3d 100644 --- a/vm/src/types/instance_definitions/poseidon_instance_def.rs +++ b/vm/src/types/instance_definitions/poseidon_instance_def.rs @@ -1,7 +1,9 @@ +use serde::Serialize; + pub(crate) const CELLS_PER_POSEIDON: u32 = 6; pub(crate) const INPUT_CELLS_PER_POSEIDON: u32 = 3; -#[derive(Clone, Debug, PartialEq)] +#[derive(Serialize, Clone, Debug, PartialEq)] pub(crate) struct PoseidonInstanceDef { pub(crate) ratio: Option, } diff --git a/vm/src/types/instance_definitions/range_check_instance_def.rs b/vm/src/types/instance_definitions/range_check_instance_def.rs index 7ed48f7901..ad46df54c6 100644 --- a/vm/src/types/instance_definitions/range_check_instance_def.rs +++ b/vm/src/types/instance_definitions/range_check_instance_def.rs @@ -1,6 +1,7 @@ +use serde::Serialize; pub(crate) const CELLS_PER_RANGE_CHECK: u32 = 1; -#[derive(Debug, PartialEq)] +#[derive(Serialize, Debug, PartialEq)] pub(crate) struct RangeCheckInstanceDef { pub(crate) ratio: Option, pub(crate) n_parts: u32, diff --git a/vm/src/types/layout.rs b/vm/src/types/layout.rs index 060730d5fd..82eba1bece 100644 --- a/vm/src/types/layout.rs +++ b/vm/src/types/layout.rs @@ -5,8 +5,10 @@ use super::instance_definitions::{ diluted_pool_instance_def::DilutedPoolInstanceDef, }; -#[derive(Debug)] -pub(crate) struct CairoLayout { +use serde::Serialize; + +#[derive(Serialize, Debug)] +pub struct CairoLayout { pub(crate) _name: String, pub(crate) _cpu_component_step: u32, pub(crate) rc_units: u32, diff --git a/vm/src/vm/errors/memory_errors.rs b/vm/src/vm/errors/memory_errors.rs index e63c0fb8bd..5757ea1f77 100644 --- a/vm/src/vm/errors/memory_errors.rs +++ b/vm/src/vm/errors/memory_errors.rs @@ -96,6 +96,8 @@ pub enum MemoryError { InvalidUsedSizeSegmentArena, #[error("Vector capacity exceeded")] VecCapacityExceeded, + #[error("Memory wasn't relocated")] + UnrelocatedMemory, } #[derive(Debug, PartialEq, Eq, Error)] diff --git a/vm/src/vm/errors/vm_errors.rs b/vm/src/vm/errors/vm_errors.rs index ba9f8a0941..1eed2153cb 100644 --- a/vm/src/vm/errors/vm_errors.rs +++ b/vm/src/vm/errors/vm_errors.rs @@ -125,6 +125,8 @@ pub enum VirtualMachineError { MissingAccessedAddresses, #[error("Failed to write the output builtin content")] FailedToWriteOutput, + #[error("Failed to find index {0} in the vm's relocation table")] + RelocationNotFound(usize), } #[cfg(test)] diff --git a/vm/src/vm/runners/builtin_runner/bitwise.rs b/vm/src/vm/runners/builtin_runner/bitwise.rs index d09347bac2..1ea7d58bb1 100644 --- a/vm/src/vm/runners/builtin_runner/bitwise.rs +++ b/vm/src/vm/runners/builtin_runner/bitwise.rs @@ -166,8 +166,7 @@ impl BitwiseBuiltinRunner { self.stop_ptr = Some(stop_ptr); Ok(stop_pointer_addr) } else { - let stop_ptr = self.base; - self.stop_ptr = Some(stop_ptr); + self.stop_ptr = Some(0); Ok(pointer) } } diff --git a/vm/src/vm/runners/builtin_runner/ec_op.rs b/vm/src/vm/runners/builtin_runner/ec_op.rs index 65e753c7e2..672ada96bc 100644 --- a/vm/src/vm/runners/builtin_runner/ec_op.rs +++ b/vm/src/vm/runners/builtin_runner/ec_op.rs @@ -251,8 +251,7 @@ impl EcOpBuiltinRunner { self.stop_ptr = Some(stop_ptr); Ok(stop_pointer_addr) } else { - let stop_ptr = self.base; - self.stop_ptr = Some(stop_ptr); + self.stop_ptr = Some(0); Ok(pointer) } } diff --git a/vm/src/vm/runners/builtin_runner/hash.rs b/vm/src/vm/runners/builtin_runner/hash.rs index 0ebd19f9ff..3f8e667044 100644 --- a/vm/src/vm/runners/builtin_runner/hash.rs +++ b/vm/src/vm/runners/builtin_runner/hash.rs @@ -172,8 +172,7 @@ impl HashBuiltinRunner { self.stop_ptr = Some(stop_ptr); Ok(stop_pointer_addr) } else { - let stop_ptr = self.base; - self.stop_ptr = Some(stop_ptr); + self.stop_ptr = Some(0); Ok(pointer) } } diff --git a/vm/src/vm/runners/builtin_runner/keccak.rs b/vm/src/vm/runners/builtin_runner/keccak.rs index 0b8d2b834f..355b59a8ab 100644 --- a/vm/src/vm/runners/builtin_runner/keccak.rs +++ b/vm/src/vm/runners/builtin_runner/keccak.rs @@ -178,8 +178,7 @@ impl KeccakBuiltinRunner { self.stop_ptr = Some(stop_ptr); Ok(stop_pointer_addr) } else { - let stop_ptr = self.base; - self.stop_ptr = Some(stop_ptr); + self.stop_ptr = Some(0); Ok(pointer) } } diff --git a/vm/src/vm/runners/builtin_runner/output.rs b/vm/src/vm/runners/builtin_runner/output.rs index ac0cd7f6a1..e558de95eb 100644 --- a/vm/src/vm/runners/builtin_runner/output.rs +++ b/vm/src/vm/runners/builtin_runner/output.rs @@ -102,8 +102,7 @@ impl OutputBuiltinRunner { self.stop_ptr = Some(stop_ptr); Ok(stop_pointer_addr) } else { - let stop_ptr = self.base; - self.stop_ptr = Some(stop_ptr); + self.stop_ptr = Some(0); Ok(pointer) } } diff --git a/vm/src/vm/runners/builtin_runner/poseidon.rs b/vm/src/vm/runners/builtin_runner/poseidon.rs index 9988c5e2c9..894627d622 100644 --- a/vm/src/vm/runners/builtin_runner/poseidon.rs +++ b/vm/src/vm/runners/builtin_runner/poseidon.rs @@ -158,8 +158,7 @@ impl PoseidonBuiltinRunner { self.stop_ptr = Some(stop_ptr); Ok(stop_pointer_addr) } else { - let stop_ptr = self.base; - self.stop_ptr = Some(stop_ptr); + self.stop_ptr = Some(0); Ok(pointer) } } diff --git a/vm/src/vm/runners/builtin_runner/range_check.rs b/vm/src/vm/runners/builtin_runner/range_check.rs index 6188a3b05d..4ef0c23ade 100644 --- a/vm/src/vm/runners/builtin_runner/range_check.rs +++ b/vm/src/vm/runners/builtin_runner/range_check.rs @@ -188,8 +188,7 @@ impl RangeCheckBuiltinRunner { self.stop_ptr = Some(stop_ptr); Ok(stop_pointer_addr) } else { - let stop_ptr = self.base; - self.stop_ptr = Some(stop_ptr); + self.stop_ptr = Some(0); Ok(pointer) } } diff --git a/vm/src/vm/runners/builtin_runner/signature.rs b/vm/src/vm/runners/builtin_runner/signature.rs index 9db3045727..2c39084322 100644 --- a/vm/src/vm/runners/builtin_runner/signature.rs +++ b/vm/src/vm/runners/builtin_runner/signature.rs @@ -209,8 +209,7 @@ impl SignatureBuiltinRunner { self.stop_ptr = Some(stop_ptr); Ok(stop_pointer_addr) } else { - let stop_ptr = self.base; - self.stop_ptr = Some(stop_ptr); + self.stop_ptr = Some(0); Ok(pointer) } } diff --git a/vm/src/vm/runners/cairo_runner.rs b/vm/src/vm/runners/cairo_runner.rs index 5a6fd66320..a4811a5a78 100644 --- a/vm/src/vm/runners/cairo_runner.rs +++ b/vm/src/vm/runners/cairo_runner.rs @@ -1,4 +1,5 @@ use crate::{ + air_public_input::{PublicInput, PublicInputError}, stdlib::{ any::Any, collections::{HashMap, HashSet}, @@ -797,12 +798,16 @@ impl CairoRunner { .segments .relocate_segments() .expect("compute_effective_sizes called but relocate_memory still returned error"); + if relocate_mem { if let Err(memory_error) = self.relocate_memory(vm, &relocation_table) { return Err(TraceError::MemoryError(memory_error)); } } - vm.relocate_trace(&relocation_table) + + vm.relocate_trace(&relocation_table)?; + vm.relocation_table = Some(relocation_table); + Ok(()) } // Returns a map from builtin base's segment index to stop_ptr offset @@ -1111,6 +1116,33 @@ impl CairoRunner { pub fn get_program(&self) -> &Program { &self.program } + + /// Return CairoRunner.layout + fn get_layout(&self) -> &CairoLayout { + &self.layout + } + + pub fn get_air_public_input( + &self, + vm: &VirtualMachine, + ) -> Result { + let layout_name = self.get_layout()._name.as_str(); + let dyn_layout = match layout_name { + "dynamic" => Some(self.get_layout()), + _ => None, + }; + + PublicInput::new( + &self.relocated_memory, + layout_name, + dyn_layout, + &vm.get_public_memory_addresses()?, + vm.get_memory_segment_addresses()?, + vm.get_relocated_trace()?, + self.get_perm_range_check_limits(vm) + .ok_or(PublicInputError::NoRangeCheckLimits)?, + ) + } } #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index 99d92c9696..c1c8aea0a7 100644 --- a/vm/src/vm/vm_core.rs +++ b/vm/src/vm/vm_core.rs @@ -27,6 +27,7 @@ use core::cmp::Ordering; use felt::Felt252; use num_traits::{ToPrimitive, Zero}; +use super::errors::runner_errors::RunnerError; use super::errors::trace_errors::TraceError; use super::runners::builtin_runner::OUTPUT_BUILTIN_NAME; @@ -85,6 +86,7 @@ pub struct VirtualMachine { instruction_cache: Vec>, #[cfg(feature = "hooks")] pub(crate) hooks: crate::vm::hooks::Hooks, + pub(crate) relocation_table: Option>, } impl VirtualMachine { @@ -114,6 +116,7 @@ impl VirtualMachine { instruction_cache: Vec::new(), #[cfg(feature = "hooks")] hooks: Default::default(), + relocation_table: None, } } @@ -1002,6 +1005,46 @@ impl VirtualMachine { Err(TraceError::TraceNotRelocated) } } + + /// Returns a list of addresses of memory cells that constitute the public memory. + pub fn get_public_memory_addresses(&self) -> Result, VirtualMachineError> { + if let Some(relocation_table) = &self.relocation_table { + Ok(self.segments.get_public_memory_addresses(relocation_table)) + } else { + Err(MemoryError::UnrelocatedMemory.into()) + } + } + + pub fn get_memory_segment_addresses( + &self, + ) -> Result, VirtualMachineError> { + let relocation_table = self + .relocation_table + .as_ref() + .ok_or(MemoryError::UnrelocatedMemory)?; + + let relocate = |segment: (usize, usize)| -> Result<(usize, usize), VirtualMachineError> { + let (index, stop_ptr_offset) = segment; + let base = relocation_table + .get(index) + .ok_or(VirtualMachineError::RelocationNotFound(index))?; + Ok((*base, base + stop_ptr_offset)) + }; + + self.builtin_runners + .iter() + .map(|builtin| -> Result<_, VirtualMachineError> { + let addresses = + if let (base, Some(stop_ptr)) = builtin.get_memory_segment_addresses() { + (base, stop_ptr) + } else { + return Err(RunnerError::NoStopPointer(Box::new(builtin.name())).into()); + }; + + Ok((builtin.name(), relocate(addresses)?)) + }) + .collect() + } } pub struct VirtualMachineBuilder { @@ -1097,6 +1140,7 @@ impl VirtualMachineBuilder { instruction_cache: Vec::new(), #[cfg(feature = "hooks")] hooks: self.hooks, + relocation_table: None, } } } diff --git a/vm/src/vm/vm_memory/memory_segments.rs b/vm/src/vm/vm_memory/memory_segments.rs index da089ce5f8..4b60e02f44 100644 --- a/vm/src/vm/vm_memory/memory_segments.rs +++ b/vm/src/vm/vm_memory/memory_segments.rs @@ -213,6 +213,25 @@ impl MemorySegmentManager { Ok(memory_holes) } + /// Returns a list of addresses of memory cells that constitute the public memory. + /// segment_offsets is the result of self.relocate_segments() + pub fn get_public_memory_addresses(&self, segment_offsets: &[usize]) -> Vec<(usize, usize)> { + let len = self.num_segments().min(self.public_memory_offsets.len()); + let mut addresses = Vec::with_capacity(len); + + for (offsets, segment_start) in self + .public_memory_offsets + .values() + .zip(segment_offsets.iter()) + .take(len) + { + for (offset, page_id) in offsets.iter() { + addresses.push((segment_start + offset, *page_id)); + } + } + addresses + } + // Writes the following information for the given segment: // * size - The size of the segment (to be used in relocate_segments). // * public_memory - A list of offsets for memory cells that will be considered as public