Skip to content

Commit

Permalink
Additional code for CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
odesenfans committed Feb 13, 2024
1 parent 9dc3a6e commit de24338
Show file tree
Hide file tree
Showing 4 changed files with 302 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ description = "Rust SDK for the Starkware Stone prover and verifier."
build = "build.rs"

[dependencies]
bincode = "2.0.0-rc.3"
cairo-vm = { git = "https://github.com/Moonsong-Labs/cairo-vm", rev = "b2f69d230416129a84ad8237ccc13d088992f74b", features=["extensive_hints"] }
serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.108"
Expand Down
262 changes: 262 additions & 0 deletions src/cairo_vm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
use std::any::Any;
use std::collections::HashMap;

use cairo_vm::air_private_input::AirPrivateInput;
use cairo_vm::air_public_input::PublicInputError;
use cairo_vm::cairo_run::{
write_encoded_memory, write_encoded_trace, CairoRunConfig, EncodeTraceError,
};
use cairo_vm::hint_processor::builtin_hint_processor::bootloader::types::{
BootloaderConfig, BootloaderInput, PackedOutput, SimpleBootloaderInput, Task, TaskSpec,
};
use cairo_vm::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor;
use cairo_vm::hint_processor::hint_processor_definition::HintProcessor;
use cairo_vm::types::errors::cairo_pie_error::CairoPieError;
use cairo_vm::types::errors::program_errors::ProgramError;
use cairo_vm::types::program::Program;
use cairo_vm::vm::errors::cairo_run_errors::CairoRunError;
use cairo_vm::vm::errors::trace_errors::TraceError;
use cairo_vm::vm::errors::vm_exception::VmException;
use cairo_vm::vm::runners::cairo_pie::CairoPie;
use cairo_vm::vm::runners::cairo_runner::CairoRunner;
use cairo_vm::vm::security::verify_secure_runner;
use cairo_vm::vm::vm_core::VirtualMachine;
use cairo_vm::{any_box, Felt252};
use thiserror::Error;

use bincode::error::EncodeError;

use crate::models::{Layout, PublicInput};

// Copied from cairo_run.rs and adapted to support injecting the bootloader input.
// TODO: check if modifying CairoRunConfig to specify custom variables is accepted upstream.
pub fn cairo_run(
program: &Program,
cairo_run_config: &CairoRunConfig,
hint_executor: &mut dyn HintProcessor,
variables: HashMap<String, Box<dyn Any>>,
) -> Result<(CairoRunner, VirtualMachine), CairoRunError> {
let secure_run = cairo_run_config
.secure_run
.unwrap_or(!cairo_run_config.proof_mode);

let mut cairo_runner = CairoRunner::new(
program,
cairo_run_config.layout,
cairo_run_config.proof_mode,
)?;
for (key, value) in variables {
cairo_runner.exec_scopes.insert_box(&key, value);
}

let mut vm = VirtualMachine::new(cairo_run_config.trace_enabled);
let end = cairo_runner.initialize(&mut vm)?;
// check step calculation

cairo_runner
.run_until_pc(end, &mut vm, hint_executor)
.map_err(|err| VmException::from_vm_error(&cairo_runner, &vm, err))?;
cairo_runner.end_run(
cairo_run_config.disable_trace_padding,
false,
&mut vm,
hint_executor,
)?;

vm.verify_auto_deductions()?;
cairo_runner.read_return_values(&mut vm)?;
if cairo_run_config.proof_mode {
cairo_runner.finalize_segments(&mut vm)?;
}
if secure_run {
verify_secure_runner(&cairo_runner, true, None, &mut vm)?;
}
cairo_runner.relocate(&mut vm, cairo_run_config.relocate_mem)?;

Ok((cairo_runner, vm))
}

/// Run a Cairo program in proof mode.
///
/// * `program_content`: Compiled program content.
pub fn run_in_proof_mode(
program_content: &[u8],
layout: Layout,
) -> Result<(CairoRunner, VirtualMachine), CairoRunError> {
let proof_mode = true;

let cairo_run_config = CairoRunConfig {
entrypoint: "main",
trace_enabled: true,
relocate_mem: true,
layout: &layout.to_string(),
proof_mode,
secure_run: None,
disable_trace_padding: false,
};

let mut hint_processor = BuiltinHintProcessor::new_empty();

cairo_vm::cairo_run::cairo_run(program_content, &cairo_run_config, &mut hint_processor)
}

#[derive(thiserror::Error, Debug)]
pub enum BootloaderTaskError {
#[error("Failed to read program: {0}")]
Program(#[from] ProgramError),

#[error("Failed to read PIE: {0}")]
Pie(#[from] CairoPieError),
}

pub fn make_bootloader_tasks(
programs: &[Vec<u8>],
pies: &[Vec<u8>],
) -> Result<Vec<TaskSpec>, BootloaderTaskError> {
let program_tasks = programs.iter().map(|program_bytes| {
let program = Program::from_bytes(program_bytes, Some("main"));
program
.map(|program| TaskSpec {
task: Task::Program(program),
})
.map_err(BootloaderTaskError::Program)
});

let cairo_pie_tasks = pies.iter().map(|pie_bytes| {
let pie = CairoPie::from_bytes(pie_bytes);
pie.map(|pie| TaskSpec {
task: Task::Pie(pie),
})
.map_err(BootloaderTaskError::Pie)
});

program_tasks.chain(cairo_pie_tasks).collect()
}

pub struct ExecutionArtifacts {
pub public_input: PublicInput,
pub private_input: AirPrivateInput,
pub memory: Vec<u8>,
pub trace: Vec<u8>,
}

#[derive(Error, Debug)]
pub enum ExecutionError {
#[error(transparent)]
RunFailed(#[from] CairoRunError),
#[error(transparent)]
GeneratePublicInput(#[from] PublicInputError),
#[error(transparent)]
GenerateTrace(#[from] TraceError),
#[error(transparent)]
EncodeMemory(EncodeTraceError),
#[error(transparent)]
EncodeTrace(EncodeTraceError),
#[error(transparent)]
SerializePublicInput(#[from] serde_json::Error),
}

/// An in-memory writer for bincode encoding.
#[derive(Default)]
pub struct MemWriter {
pub buf: Vec<u8>,
}

impl MemWriter {
pub fn new() -> Self {
Self::default()
}
}

impl bincode::enc::write::Writer for MemWriter {
fn write(&mut self, bytes: &[u8]) -> Result<(), EncodeError> {
self.buf.extend_from_slice(bytes);
Ok(())
}
}

/// Extracts execution artifacts from the runner and VM (after execution).
///
/// * `cairo_runner` Cairo runner object.
/// * `vm`: Cairo VM object.
pub fn extract_execution_artifacts(
cairo_runner: CairoRunner,
vm: VirtualMachine,
) -> Result<ExecutionArtifacts, ExecutionError> {
let memory = &cairo_runner.relocated_memory;
let trace = cairo_runner
.relocated_trace
.as_ref()
.ok_or(ExecutionError::GenerateTrace(TraceError::TraceNotEnabled))?;

let mut memory_writer = MemWriter::new();
write_encoded_memory(memory, &mut memory_writer).map_err(ExecutionError::EncodeMemory)?;
let memory_raw = memory_writer.buf;

let mut trace_writer = MemWriter::new();
write_encoded_trace(trace, &mut trace_writer).map_err(ExecutionError::EncodeTrace)?;
let trace_raw = trace_writer.buf;

let cairo_vm_public_input = cairo_runner.get_air_public_input(&vm)?;
let public_input = PublicInput::try_from(cairo_vm_public_input)?;

let private_input = cairo_runner.get_air_private_input(&vm);

Ok(ExecutionArtifacts {
public_input,
private_input,
memory: memory_raw,
trace: trace_raw,
})
}

pub fn run_bootloader_in_proof_mode(
bootloader: &Program,
tasks: Vec<TaskSpec>,
) -> Result<ExecutionArtifacts, ExecutionError> {
let proof_mode = true;
let layout = "starknet_with_keccak";

let cairo_run_config = CairoRunConfig {
entrypoint: "main",
trace_enabled: true,
relocate_mem: true,
layout,
proof_mode,
secure_run: None,
disable_trace_padding: false,
};

let n_tasks = tasks.len();

let bootloader_input = BootloaderInput {
simple_bootloader_input: SimpleBootloaderInput {
fact_topologies_path: Some("/tmp/fact_topologies".into()),
single_page: false,
tasks,
},
bootloader_config: BootloaderConfig {
simple_bootloader_program_hash: Felt252::from(0),
supported_cairo_verifier_program_hashes: vec![],
},
packed_outputs: vec![PackedOutput::Plain(vec![]); n_tasks],
};

let mut hint_processor = BuiltinHintProcessor::new_empty();
let variables = HashMap::<String, Box<dyn Any>>::from([
("bootloader_input".to_string(), any_box!(bootloader_input)),
(
"bootloader_program".to_string(),
any_box!(bootloader.clone()),
),
]);

let (cairo_runner, vm) = cairo_run(
bootloader,
&cairo_run_config,
&mut hint_processor,
variables,
)?;

extract_execution_artifacts(cairo_runner, vm)
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod cairo_vm;
pub mod error;
pub mod fri;
pub(crate) mod json;
Expand Down
39 changes: 38 additions & 1 deletion src/models.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use cairo_vm::air_private_input::AirPrivateInputSerializable;
use stark_evm_adapter::annotation_parser::SplitProofs;
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::path::PathBuf;
use std::str::FromStr;

use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Serialize, Deserialize, Debug)]
pub struct CachedLdeConfig {
Expand Down Expand Up @@ -54,7 +57,7 @@ pub struct ProverParameters {
pub use_extension_field: bool,
}

#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)]
pub enum Layout {
#[serde(rename = "plain")]
Plain,
Expand All @@ -76,6 +79,23 @@ pub enum Layout {
StarknetWithKeccak,
}

impl FromStr for Layout {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_value::<Layout>(Value::String(s.to_string())).map_err(|e| e.to_string())
}
}

impl Display for Layout {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let value = serde_json::to_value(self).map_err(|_| std::fmt::Error)?;
let layout_str = value.as_str().expect("This is guaranteed to be a string");
// serde_json adds
write!(f, "{}", layout_str)
}
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct MemorySegmentAddresses {
pub begin_addr: u32,
Expand Down Expand Up @@ -160,6 +180,7 @@ pub struct ProofAnnotations {
#[cfg(test)]
mod tests {
use crate::test_utils::load_test_case_file;
use rstest::rstest;

use super::*;

Expand All @@ -185,4 +206,20 @@ mod tests {
// We don't check all fields, just ensure that we can deserialize the fixture
assert!(!parameters.use_extension_field);
}

#[rstest]
#[case("small", Layout::Small)]
#[case("starknet_with_keccak", Layout::StarknetWithKeccak)]
fn deserialize_layout(#[case] layout_str: String, #[case] expected: Layout) {
let layout = Layout::from_str(&layout_str).unwrap();
assert_eq!(layout, expected);
}

#[rstest]
#[case(Layout::Small, "small")]
#[case(Layout::StarknetWithKeccak, "starknet_with_keccak")]
fn serialize_layout(#[case] layout: Layout, #[case] expected: &str) {
let layout_str = layout.to_string();
assert_eq!(layout_str, expected);
}
}

0 comments on commit de24338

Please sign in to comment.