Skip to content

Commit

Permalink
Implement air_private_input (#1552)
Browse files Browse the repository at this point in the history
* Implement get_air_private_input for RangeCheck;

* Implement air_private_input for Bitwise;

* Implement air_private_input for Hash

* Update proof_programs symlinks

* Add EcOp priv input variant

* Implement air_private_input for EcOp

* Implement air_private_input for Poseidon & Signature

* Implement air_private_input for Keccak

* Add AirPrivateInput serialization

* Remove unwrap

* Add targets to compare private inputs against python vm

* Add separate script to compare private inputs

* Ignore & Clean output files

* Fix target

* Fix + fmt

* Fetch absolute paths in cli and remove feature-gate

* Fix ecdsa private input

* Add no-std import

* Add Chaneglog entry

* Update README

* Add cli tests

* Add case to cli test

* Fix conditional

* Add no-std import

* fmt

* fix

* Remove unwraps

* Add tests so coverage doesnt sink

* Fix test

* Fix test

* Fix test

* Fix symlink

* Remove broken file

---------

Co-authored-by: Mario Rugiero <mario.rugiero@lambdaclass.com>
  • Loading branch information
fmoletta and Oppen authored Jan 15, 2024
1 parent 9fc9c6e commit 352e8a8
Show file tree
Hide file tree
Showing 22 changed files with 801 additions and 18 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
**/*.trace
**/*.memory
**/*.air_public_input
**/*.air_private_input
**/*.swp
bench/results
.python-version
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#### Upcoming Changes

* feat: Implement air_private_input [#1552](https://github.com/lambdaclass/cairo-vm/pull/1552)

* feat: Add `proof_mode` flag to `cairo1-run` [#1537] (https://github.com/lambdaclass/cairo-vm/pull/1537)
* The cairo1-run crate no longer compiles and executes in proof_mode by default
* Add flag `proof_mode` to cairo1-run crate. Activating this flag will enable proof_mode compilation and execution
Expand Down
20 changes: 13 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ endif
deps deps-macos cargo-deps build run check test clippy coverage benchmark \
compare_benchmarks_deps compare_benchmarks docs clean \
compare_vm_output compare_trace_memory compare_trace compare_memory \
compare_trace_memory_proof compare_all_proof compare_trace_proof compare_memory_proof compare_air_public_input \
compare_trace_memory_proof compare_all_proof compare_trace_proof compare_memory_proof compare_air_public_input compare_air_private_input\
cairo_bench_programs cairo_proof_programs cairo_test_programs cairo_1_test_contracts cairo_2_test_contracts \
cairo_trace cairo-vm_trace cairo_proof_trace cairo-vm_proof_trace \
fuzzer-deps fuzzer-run-cairo-compiled fuzzer-run-hint-diff build-cairo-lang hint-accountant \
Expand All @@ -37,10 +37,12 @@ COMPILED_PROOF_TESTS:=$(patsubst $(TEST_PROOF_DIR)/%.cairo, $(TEST_PROOF_DIR)/%.
CAIRO_MEM_PROOF:=$(patsubst $(TEST_PROOF_DIR)/%.json, $(TEST_PROOF_DIR)/%.memory, $(COMPILED_PROOF_TESTS))
CAIRO_TRACE_PROOF:=$(patsubst $(TEST_PROOF_DIR)/%.json, $(TEST_PROOF_DIR)/%.trace, $(COMPILED_PROOF_TESTS))
CAIRO_AIR_PUBLIC_INPUT:=$(patsubst $(TEST_PROOF_DIR)/%.json, $(TEST_PROOF_DIR)/%.air_public_input, $(COMPILED_PROOF_TESTS))
CAIRO_AIR_PRIVATE_INPUT:=$(patsubst $(TEST_PROOF_DIR)/%.json, $(TEST_PROOF_DIR)/%.air_private_input, $(COMPILED_PROOF_TESTS))

CAIRO_RS_MEM_PROOF:=$(patsubst $(TEST_PROOF_DIR)/%.json, $(TEST_PROOF_DIR)/%.rs.memory, $(COMPILED_PROOF_TESTS))
CAIRO_RS_TRACE_PROOF:=$(patsubst $(TEST_PROOF_DIR)/%.json, $(TEST_PROOF_DIR)/%.rs.trace, $(COMPILED_PROOF_TESTS))
CAIRO_RS_AIR_PUBLIC_INPUT:=$(patsubst $(TEST_PROOF_DIR)/%.json, $(TEST_PROOF_DIR)/%.rs.air_public_input, $(COMPILED_PROOF_TESTS))
CAIRO_RS_AIR_PRIVATE_INPUT:=$(patsubst $(TEST_PROOF_DIR)/%.json, $(TEST_PROOF_DIR)/%.rs.air_private_input, $(COMPILED_PROOF_TESTS))

PROOF_BENCH_DIR=cairo_programs/benchmarks
PROOF_BENCH_FILES:=$(wildcard $(PROOF_BENCH_DIR)/*.cairo)
Expand All @@ -49,11 +51,11 @@ PROOF_COMPILED_BENCHES:=$(patsubst $(PROOF_BENCH_DIR)/%.cairo, $(PROOF_BENCH_DIR
$(TEST_PROOF_DIR)/%.json: $(TEST_PROOF_DIR)/%.cairo
cairo-compile --cairo_path="$(TEST_PROOF_DIR):$(PROOF_BENCH_DIR)" $< --output $@ --proof_mode

$(TEST_PROOF_DIR)/%.rs.trace $(TEST_PROOF_DIR)/%.rs.memory $(TEST_PROOF_DIR)/%.rs.air_public_input: $(TEST_PROOF_DIR)/%.json $(RELBIN)
cargo llvm-cov run -p cairo-vm-cli --release --no-report -- --layout starknet_with_keccak --proof_mode $< --trace_file $@ --memory_file $(@D)/$(*F).rs.memory --air_public_input $(@D)/$(*F).rs.air_public_input
$(TEST_PROOF_DIR)/%.rs.trace $(TEST_PROOF_DIR)/%.rs.memory $(TEST_PROOF_DIR)/%.rs.air_public_input $(TEST_PROOF_DIR)/%.rs.air_private_input: $(TEST_PROOF_DIR)/%.json $(RELBIN)
cargo llvm-cov run -p cairo-vm-cli --release --no-report -- --layout starknet_with_keccak --proof_mode $< --trace_file $(@D)/$(*F).rs.trace --memory_file $(@D)/$(*F).rs.memory --air_public_input $(@D)/$(*F).rs.air_public_input --air_private_input $(@D)/$(*F).rs.air_private_input

$(TEST_PROOF_DIR)/%.trace $(TEST_PROOF_DIR)/%.memory $(TEST_PROOF_DIR)/%.air_public_input: $(TEST_PROOF_DIR)/%.json
cairo-run --layout starknet_with_keccak --proof_mode --program $< --trace_file $(@D)/$(*F).trace --air_public_input $(@D)/$(*F).air_public_input --memory_file $(@D)/$(*F).memory
$(TEST_PROOF_DIR)/%.trace $(TEST_PROOF_DIR)/%.memory $(TEST_PROOF_DIR)/%.air_public_input $(TEST_PROOF_DIR)/%.air_private_input: $(TEST_PROOF_DIR)/%.json
cairo-run --layout starknet_with_keccak --proof_mode --program $< --trace_file $(@D)/$(*F).trace --air_public_input $(@D)/$(*F).air_public_input --memory_file $(@D)/$(*F).memory --air_private_input $(@D)/$(*F).air_private_input

$(PROOF_BENCH_DIR)/%.json: $(PROOF_BENCH_DIR)/%.cairo
cairo-compile --cairo_path="$(TEST_PROOF_DIR):$(PROOF_BENCH_DIR)" $< --output $@ --proof_mode
Expand Down Expand Up @@ -289,8 +291,8 @@ compare_memory: $(CAIRO_RS_MEM) $(CAIRO_MEM)
compare_trace_memory_proof: $(COMPILED_PROOF_TESTS) $(CAIRO_RS_TRACE_PROOF) $(CAIRO_TRACE_PROOF) $(CAIRO_RS_MEM_PROOF) $(CAIRO_MEM_PROOF)
cd vm/src/tests; ./compare_vm_state.sh trace memory proof_mode

compare_all_proof: $(COMPILED_PROOF_TESTS) $(CAIRO_RS_TRACE_PROOF) $(CAIRO_TRACE_PROOF) $(CAIRO_RS_MEM_PROOF) $(CAIRO_MEM_PROOF) $(CAIRO_RS_AIR_PUBLIC_INPUT) $(CAIRO_AIR_PUBLIC_INPUT)
cd vm/src/tests; ./compare_vm_state.sh trace memory proof_mode air_public_input
compare_all_proof: $(COMPILED_PROOF_TESTS) $(CAIRO_RS_TRACE_PROOF) $(CAIRO_TRACE_PROOF) $(CAIRO_RS_MEM_PROOF) $(CAIRO_MEM_PROOF) $(CAIRO_RS_AIR_PUBLIC_INPUT) $(CAIRO_AIR_PUBLIC_INPUT) $(CAIRO_RS_AIR_PRIVATE_INPUT) $(CAIRO_AIR_PRIVATE_INPUT)
cd vm/src/tests; ./compare_vm_state.sh trace memory proof_mode air_public_input air_private_input

compare_trace_proof: $(CAIRO_RS_TRACE_PROOF) $(CAIRO_TRACE_PROOF)
cd vm/src/tests; ./compare_vm_state.sh trace proof_mode
Expand All @@ -301,6 +303,9 @@ compare_memory_proof: $(CAIRO_RS_MEM_PROOF) $(CAIRO_MEM_PROOF)
compare_air_public_input: $(CAIRO_RS_AIR_PUBLIC_INPUT) $(CAIRO_AIR_PUBLIC_INPUT)
cd vm/src/tests; ./compare_vm_state.sh memory proof_mode air_public_input

compare_air_private_input: $(CAIRO_RS_AIR_PRIVATE_INPUT) $(CAIRO_AIR_PRIVATE_INPUT)
cd vm/src/tests; ./compare_vm_state.sh memory proof_mode air_private_input

# Run with nightly enable the `doc_cfg` feature wich let us provide clear explaination about which parts of the code are behind a feature flag
docs:
RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --verbose --release --locked --no-deps --all-features --open
Expand All @@ -318,6 +323,7 @@ clean:
rm -f $(TEST_PROOF_DIR)/*.memory
rm -f $(TEST_PROOF_DIR)/*.trace
rm -f $(TEST_PROOF_DIR)/*.air_public_input
rm -f $(TEST_PROOF_DIR)/*.air_private_input
rm -rf cairo-vm-env
rm -rf cairo-vm-pypy-env
rm -rf cairo
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ The cairo-vm-cli supports the following optional arguments:

- `--air_public_input <AIR_PUBLIC_INPUT>`: Receives the name of a file and outputs the AIR public inputs into it. Can only be used if proof_mode is also enabled.

- `--air_private_input <AIR_PRIVATE_INPUT>`: Receives the name of a file and outputs the AIR private inputs into it. Can only be used if proof_mode, trace_file & memory_file are also enabled.

For example, to obtain the air public inputs from a fibonacci program run, we can run :

```bash
Expand Down
89 changes: 82 additions & 7 deletions cairo-vm-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ struct Args {
secure_run: Option<bool>,
#[clap(long = "air_public_input")]
air_public_input: Option<String>,
#[clap(long = "air_private_input")]
air_private_input: Option<String>,
}

fn validate_layout(value: &str) -> Result<String, String> {
Expand Down Expand Up @@ -119,6 +121,30 @@ fn run(args: impl Iterator<Item = String>) -> Result<(), Error> {
return Err(Error::Cli(error));
}

if args.air_private_input.is_some() && !args.proof_mode {
let error = Args::command().error(
clap::error::ErrorKind::ArgumentConflict,
"--air_private_input can only be used in proof_mode.",
);
return Err(Error::Cli(error));
}

if args.air_private_input.is_some() && args.trace_file.is_none() {
let error = Args::command().error(
clap::error::ErrorKind::ArgumentConflict,
"--trace_file must be set when --air_private_input is set.",
);
return Err(Error::Cli(error));
}

if args.air_private_input.is_some() && args.memory_file.is_none() {
let error = Args::command().error(
clap::error::ErrorKind::ArgumentConflict,
"--memory_file must be set when --air_private_input is set.",
);
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 {
Expand Down Expand Up @@ -148,7 +174,7 @@ fn run(args: impl Iterator<Item = String>) -> Result<(), Error> {
print!("{output_buffer}");
}

if let Some(trace_path) = args.trace_file {
if let Some(ref trace_path) = args.trace_file {
let relocated_trace = cairo_runner
.relocated_trace
.as_ref()
Expand All @@ -162,7 +188,7 @@ fn run(args: impl Iterator<Item = String>) -> Result<(), Error> {
trace_writer.flush()?;
}

if let Some(memory_path) = args.memory_file {
if let Some(ref 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));
Expand All @@ -176,6 +202,31 @@ fn run(args: impl Iterator<Item = String>) -> Result<(), Error> {
std::fs::write(file_path, json)?;
}

if let (Some(file_path), Some(ref trace_file), Some(ref memory_file)) =
(args.air_private_input, args.trace_file, args.memory_file)
{
// Get absolute paths of trace_file & memory_file
let trace_path = trace_file
.as_path()
.canonicalize()
.unwrap_or(trace_file.clone())
.to_string_lossy()
.to_string();
let memory_path = memory_file
.as_path()
.canonicalize()
.unwrap_or(memory_file.clone())
.to_string_lossy()
.to_string();

let json = cairo_runner
.get_air_private_input(&vm)
.to_serializable(trace_path, memory_path)
.serialize_json()
.map_err(PublicInputError::Serde)?;
std::fs::write(file_path, json)?;
}

Ok(())
}

Expand Down Expand Up @@ -212,6 +263,27 @@ mod tests {
assert_matches!(run(args), Err(Error::Cli(_)));
}

#[rstest]
#[case(["cairo-vm-cli", "../cairo_programs/fibonacci.json", "--air_private_input", "/dev/null", "--proof_mode", "--memory_file", "/dev/null"].as_slice())]
fn test_run_air_private_input_no_trace(#[case] args: &[&str]) {
let args = args.iter().cloned().map(String::from);
assert_matches!(run(args), Err(Error::Cli(_)));
}

#[rstest]
#[case(["cairo-vm-cli", "../cairo_programs/fibonacci.json", "--air_private_input", "/dev/null", "--proof_mode", "--trace_file", "/dev/null"].as_slice())]
fn test_run_air_private_input_no_memory(#[case] args: &[&str]) {
let args = args.iter().cloned().map(String::from);
assert_matches!(run(args), Err(Error::Cli(_)));
}

#[rstest]
#[case(["cairo-vm-cli", "../cairo_programs/fibonacci.json", "--air_private_input", "/dev/null", "--trace_file", "/dev/null", "--memory_file", "/dev/null"].as_slice())]
fn test_run_air_private_input_no_proof(#[case] args: &[&str]) {
let args = args.iter().cloned().map(String::from);
assert_matches!(run(args), Err(Error::Cli(_)));
}

#[rstest]
fn test_run_ok(
#[values(None,
Expand All @@ -234,16 +306,17 @@ mod tests {
#[values(false, true)] print_output: bool,
#[values(false, true)] entrypoint: bool,
#[values(false, true)] air_public_input: bool,
#[values(false, true)] air_private_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(),
]);
args.extend_from_slice(&["--air_public_input".to_string(), "/dev/null".to_string()]);
}
if air_private_input {
args.extend_from_slice(&["--air_private_input".to_string(), "/dev/null".to_string()]);
}
if proof_mode {
trace_file = true;
Expand All @@ -266,7 +339,9 @@ mod tests {
}

args.push("../cairo_programs/proof_programs/fibonacci.json".to_string());
if air_public_input && !proof_mode {
if air_public_input && !proof_mode
|| (air_private_input && (!proof_mode || !trace_file || !memory_file))
{
assert_matches!(run(args.into_iter()), Err(_));
} else {
assert_matches!(run(args.into_iter()), Ok(_));
Expand Down
2 changes: 1 addition & 1 deletion fuzzer/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

142 changes: 142 additions & 0 deletions vm/src/air_private_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use crate::{
stdlib::{
collections::HashMap,
prelude::{String, Vec},
},
vm::runners::builtin_runner::{
BITWISE_BUILTIN_NAME, EC_OP_BUILTIN_NAME, HASH_BUILTIN_NAME, KECCAK_BUILTIN_NAME,
POSEIDON_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME, SIGNATURE_BUILTIN_NAME,
},
};
use serde::{Deserialize, Serialize};

use crate::Felt252;

// Serializable format, matches the file output of the python implementation
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct AirPrivateInputSerializable {
trace_path: String,
memory_path: String,
pedersen: Vec<PrivateInput>,
range_check: Vec<PrivateInput>,
ecdsa: Vec<PrivateInput>,
bitwise: Vec<PrivateInput>,
ec_op: Vec<PrivateInput>,
keccak: Vec<PrivateInput>,
poseidon: Vec<PrivateInput>,
}

// Contains only builtin public inputs, useful for library users
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AirPrivateInput(pub HashMap<&'static str, Vec<PrivateInput>>);

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(untagged)]
pub enum PrivateInput {
Value(PrivateInputValue),
Pair(PrivateInputPair),
EcOp(PrivateInputEcOp),
PoseidonState(PrivateInputPoseidonState),
KeccakState(PrivateInputKeccakState),
Signature(PrivateInputSignature),
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct PrivateInputValue {
pub index: usize,
pub value: Felt252,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct PrivateInputPair {
pub index: usize,
pub x: Felt252,
pub y: Felt252,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct PrivateInputEcOp {
pub index: usize,
pub p_x: Felt252,
pub p_y: Felt252,
pub m: Felt252,
pub q_x: Felt252,
pub q_y: Felt252,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct PrivateInputPoseidonState {
pub index: usize,
pub input_s0: Felt252,
pub input_s1: Felt252,
pub input_s2: Felt252,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct PrivateInputKeccakState {
pub index: usize,
pub input_s0: Felt252,
pub input_s1: Felt252,
pub input_s2: Felt252,
pub input_s3: Felt252,
pub input_s4: Felt252,
pub input_s5: Felt252,
pub input_s6: Felt252,
pub input_s7: Felt252,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct PrivateInputSignature {
pub index: usize,
pub pubkey: Felt252,
pub msg: Felt252,
pub signature_input: SignatureInput,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct SignatureInput {
pub r: Felt252,
pub w: Felt252,
}

impl AirPrivateInput {
pub fn to_serializable(
&self,
trace_path: String,
memory_path: String,
) -> AirPrivateInputSerializable {
AirPrivateInputSerializable {
trace_path,
memory_path,
pedersen: self.0.get(HASH_BUILTIN_NAME).cloned().unwrap_or_default(),
range_check: self
.0
.get(RANGE_CHECK_BUILTIN_NAME)
.cloned()
.unwrap_or_default(),
ecdsa: self
.0
.get(SIGNATURE_BUILTIN_NAME)
.cloned()
.unwrap_or_default(),
bitwise: self
.0
.get(BITWISE_BUILTIN_NAME)
.cloned()
.unwrap_or_default(),
ec_op: self.0.get(EC_OP_BUILTIN_NAME).cloned().unwrap_or_default(),
keccak: self.0.get(KECCAK_BUILTIN_NAME).cloned().unwrap_or_default(),
poseidon: self
.0
.get(POSEIDON_BUILTIN_NAME)
.cloned()
.unwrap_or_default(),
}
}
}

impl AirPrivateInputSerializable {
pub fn serialize_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(&self)
}
}
Loading

0 comments on commit 352e8a8

Please sign in to comment.