Skip to content

Commit

Permalink
chore: move nargo I/O into separate module (#896)
Browse files Browse the repository at this point in the history
* chore: move IO logic into separate module

* chore: remove preprocessing from io module

* chore: replace if-lets with match
  • Loading branch information
TomAFrench authored Feb 22, 2023
1 parent 811b346 commit 8cafa34
Show file tree
Hide file tree
Showing 14 changed files with 303 additions and 258 deletions.
3 changes: 2 additions & 1 deletion crates/nargo/src/cli/check_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use std::{
path::{Path, PathBuf},
};

use super::{add_std_lib, write_to_file, NargoConfig};
use super::fs::write_to_file;
use super::{add_std_lib, NargoConfig};
use crate::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE};

/// Checks the constraint system for errors
Expand Down
49 changes: 4 additions & 45 deletions crates/nargo/src/cli/compile_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
use acvm::acir::circuit::Circuit;
use acvm::ProofSystemCompiler;
use acvm::{acir::circuit::Circuit, hash_constraint_system};
use std::path::{Path, PathBuf};

use clap::Args;

use crate::{
constants::{ACIR_EXT, PK_EXT, TARGET_DIR, VK_EXT},
errors::CliError,
resolver::Resolver,
};
use crate::{constants::TARGET_DIR, errors::CliError, resolver::Resolver};

use super::{add_std_lib, create_named_dir, write_to_file, NargoConfig};
use super::fs::{acir::save_acir_to_dir, keys::save_key_to_dir};
use super::{add_std_lib, NargoConfig};

/// Compile the program and its secret execution trace into ACIR format
#[derive(Debug, Clone, Args)]
Expand Down Expand Up @@ -81,41 +78,3 @@ fn preprocess_with_path<P: AsRef<Path>>(

Ok((pk_path, vk_path))
}

fn save_acir_to_dir<P: AsRef<Path>>(
circuit: &Circuit,
circuit_name: &str,
circuit_dir: P,
) -> PathBuf {
let mut circuit_path = create_named_dir(circuit_dir.as_ref(), "target");
circuit_path.push(circuit_name);

let mut serialized = Vec::new();
circuit.write(&mut serialized).expect("could not serialize circuit");

circuit_path.set_extension(ACIR_EXT);
write_to_file(serialized.as_slice(), &circuit_path);

// Save a checksum of the circuit to compare against during proving and verification
let acir_hash = hash_constraint_system(circuit);
circuit_path.set_extension(ACIR_EXT.to_owned() + ".sha256");
write_to_file(hex::encode(acir_hash).as_bytes(), &circuit_path);

circuit_path
}

fn save_key_to_dir<P: AsRef<Path>>(
key: Vec<u8>,
key_name: &str,
key_dir: P,
is_proving_key: bool,
) -> Result<PathBuf, CliError> {
let mut key_path = create_named_dir(key_dir.as_ref(), key_name);
key_path.push(key_name);
let extension = if is_proving_key { PK_EXT } else { VK_EXT };
key_path.set_extension(extension);

write_to_file(hex::encode(key).as_bytes(), &key_path);

Ok(key_path)
}
3 changes: 2 additions & 1 deletion crates/nargo/src/cli/contract_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{create_named_dir, write_to_file, NargoConfig};
use super::fs::{create_named_dir, write_to_file};
use super::NargoConfig;
use crate::{cli::compile_cmd::compile_circuit, constants::CONTRACT_DIR, errors::CliError};
use acvm::SmartContract;
use clap::Args;
Expand Down
23 changes: 3 additions & 20 deletions crates/nargo/src/cli/execute_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
use std::path::{Path, PathBuf};
use std::path::Path;

use acvm::acir::native_types::Witness;
use acvm::PartialWitnessGenerator;
use clap::Args;
use noirc_abi::input_parser::{Format, InputValue};
use noirc_abi::{InputMap, WitnessMap};
use noirc_driver::CompiledProgram;

use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir};
use super::NargoConfig;
use super::{create_named_dir, read_inputs_from_file, write_to_file};
use crate::{
cli::compile_cmd::compile_circuit,
constants::{PROVER_INPUT_FILE, TARGET_DIR, WITNESS_EXT},
constants::{PROVER_INPUT_FILE, TARGET_DIR},
errors::CliError,
};

Expand Down Expand Up @@ -83,19 +82,3 @@ pub(crate) fn execute_program(

Ok(solved_witness)
}

pub(crate) fn save_witness_to_dir<P: AsRef<Path>>(
witness: WitnessMap,
witness_name: &str,
witness_dir: P,
) -> Result<PathBuf, CliError> {
let mut witness_path = create_named_dir(witness_dir.as_ref(), "witness");
witness_path.push(witness_name);
witness_path.set_extension(WITNESS_EXT);

let buf = Witness::to_bytes(&witness);

write_to_file(buf.as_slice(), &witness_path);

Ok(witness_path)
}
29 changes: 29 additions & 0 deletions crates/nargo/src/cli/fs/acir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::path::{Path, PathBuf};

use acvm::{acir::circuit::Circuit, hash_constraint_system};

use crate::constants::ACIR_EXT;

use super::{create_named_dir, write_to_file};

pub(crate) fn save_acir_to_dir<P: AsRef<Path>>(
circuit: &Circuit,
circuit_name: &str,
circuit_dir: P,
) -> PathBuf {
let mut circuit_path = create_named_dir(circuit_dir.as_ref(), "target");
circuit_path.push(circuit_name);

let mut serialized = Vec::new();
circuit.write(&mut serialized).expect("could not serialize circuit");

circuit_path.set_extension(ACIR_EXT);
write_to_file(serialized.as_slice(), &circuit_path);

// Save a checksum of the circuit to compare against during proving and verification
let acir_hash = hash_constraint_system(circuit);
circuit_path.set_extension(ACIR_EXT.to_owned() + ".sha256");
write_to_file(hex::encode(acir_hash).as_bytes(), &circuit_path);

circuit_path
}
75 changes: 75 additions & 0 deletions crates/nargo/src/cli/fs/inputs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use noirc_abi::{
input_parser::{Format, InputValue},
Abi, InputMap, MAIN_RETURN_NAME,
};
use std::{collections::BTreeMap, path::Path};

use crate::errors::CliError;

use super::write_to_file;

/// Returns the circuit's parameters and its return value, if one exists.
/// # Examples
///
/// ```ignore
/// let (input_map, return_value): (InputMap, Option<InputValue>) =
/// read_inputs_from_file(path, "Verifier", Format::Toml, &abi)?;
/// ```
pub fn read_inputs_from_file<P: AsRef<Path>>(
path: P,
file_name: &str,
format: Format,
abi: &Abi,
) -> Result<(InputMap, Option<InputValue>), CliError> {
if abi.is_empty() {
return Ok((BTreeMap::new(), None));
}

let file_path = {
let mut dir_path = path.as_ref().to_path_buf();
dir_path.push(file_name);
dir_path.set_extension(format.ext());
dir_path
};
if !file_path.exists() {
return Err(CliError::MissingTomlFile(file_name.to_owned(), file_path));
}

let input_string = std::fs::read_to_string(file_path).unwrap();
let mut input_map = format.parse(&input_string, abi)?;
let return_value = input_map.remove(MAIN_RETURN_NAME);

Ok((input_map, return_value))
}

pub fn write_inputs_to_file<P: AsRef<Path>>(
input_map: &InputMap,
return_value: &Option<InputValue>,
path: P,
file_name: &str,
format: Format,
) -> Result<(), CliError> {
let file_path = {
let mut dir_path = path.as_ref().to_path_buf();
dir_path.push(file_name);
dir_path.set_extension(format.ext());
dir_path
};

// We must insert the return value into the `InputMap` in order for it to be written to file.
let serialized_output = match return_value {
// Parameters and return values are kept separate except for when they're being written to file.
// As a result, we don't want to modify the original map and must clone it before insertion.
Some(return_value) => {
let mut input_map = input_map.clone();
input_map.insert(MAIN_RETURN_NAME.to_owned(), return_value.clone());
format.serialize(&input_map)?
}
// If no return value exists, then we can serialize the original map directly.
None => format.serialize(input_map)?,
};

write_to_file(serialized_output.as_bytes(), &file_path);

Ok(())
}
68 changes: 68 additions & 0 deletions crates/nargo/src/cli/fs/keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::path::{Path, PathBuf};

use acvm::{acir::circuit::Circuit, hash_constraint_system};

use crate::{
constants::{ACIR_EXT, PK_EXT, VK_EXT},
errors::CliError,
};

use super::{create_named_dir, load_hex_data, write_to_file};

pub(crate) fn save_key_to_dir<P: AsRef<Path>>(
key: Vec<u8>,
key_name: &str,
key_dir: P,
is_proving_key: bool,
) -> Result<PathBuf, CliError> {
let mut key_path = create_named_dir(key_dir.as_ref(), key_name);
key_path.push(key_name);
let extension = if is_proving_key { PK_EXT } else { VK_EXT };
key_path.set_extension(extension);

write_to_file(hex::encode(key).as_bytes(), &key_path);

Ok(key_path)
}

pub(crate) fn fetch_pk_and_vk<P: AsRef<Path>>(
circuit: &Circuit,
circuit_build_path: P,
prove_circuit: bool,
check_proof: bool,
) -> Result<(Vec<u8>, Vec<u8>), CliError> {
let mut acir_hash_path = PathBuf::new();
acir_hash_path.push(circuit_build_path.as_ref());
acir_hash_path.set_extension(ACIR_EXT.to_owned() + ".sha256");
let expected_acir_hash = load_hex_data(acir_hash_path.clone())?;

let new_acir_hash = hash_constraint_system(circuit);

if new_acir_hash[..] != expected_acir_hash {
return Err(CliError::MismatchedAcir(acir_hash_path));
}

// This flag exists to avoid an unnecessary read of the proving key during verification
// as this method is used by both `nargo prove` and `nargo verify`
let proving_key = if prove_circuit {
let mut proving_key_path = PathBuf::new();
proving_key_path.push(circuit_build_path.as_ref());
proving_key_path.set_extension(PK_EXT);
load_hex_data(proving_key_path)?
} else {
// We can return an empty Vec here as `prove_circuit` should only be false when running `nargo verify`
vec![]
};

let verification_key = if check_proof {
let mut verification_key_path = PathBuf::new();
verification_key_path.push(circuit_build_path);
verification_key_path.set_extension(VK_EXT);
load_hex_data(verification_key_path)?
} else {
// We can return an empty Vec here as the verification key is used only is `check_proof` is true
vec![]
};

Ok((proving_key, verification_key))
}
47 changes: 47 additions & 0 deletions crates/nargo/src/cli/fs/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::{
fs::File,
io::Write,
path::{Path, PathBuf},
};

use crate::errors::CliError;

pub mod acir;
pub mod inputs;
pub mod keys;
pub mod proof;
pub mod witness;

fn create_dir<P: AsRef<Path>>(dir_path: P) -> Result<PathBuf, std::io::Error> {
let mut dir = std::path::PathBuf::new();
dir.push(dir_path);
std::fs::create_dir_all(&dir)?;
Ok(dir)
}

pub(crate) fn create_named_dir(named_dir: &Path, name: &str) -> PathBuf {
create_dir(named_dir).unwrap_or_else(|_| panic!("could not create the `{name}` directory"))
}

pub(crate) fn write_to_file(bytes: &[u8], path: &Path) -> String {
let display = path.display();

let mut file = match File::create(path) {
Err(why) => panic!("couldn't create {display}: {why}"),
Ok(file) => file,
};

match file.write_all(bytes) {
Err(why) => panic!("couldn't write to {display}: {why}"),
Ok(_) => display.to_string(),
}
}

pub fn load_hex_data<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, CliError> {
let hex_data: Vec<_> =
std::fs::read(&path).map_err(|_| CliError::PathNotValid(path.as_ref().to_path_buf()))?;

let raw_bytes = hex::decode(hex_data).map_err(CliError::HexArtifactNotValid)?;

Ok(raw_bytes)
}
19 changes: 19 additions & 0 deletions crates/nargo/src/cli/fs/proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use std::path::{Path, PathBuf};

use crate::{constants::PROOF_EXT, errors::CliError};

use super::{create_named_dir, write_to_file};

pub(crate) fn save_proof_to_dir<P: AsRef<Path>>(
proof: &[u8],
proof_name: &str,
proof_dir: P,
) -> Result<PathBuf, CliError> {
let mut proof_path = create_named_dir(proof_dir.as_ref(), "proof");
proof_path.push(proof_name);
proof_path.set_extension(PROOF_EXT);

write_to_file(hex::encode(proof).as_bytes(), &proof_path);

Ok(proof_path)
}
23 changes: 23 additions & 0 deletions crates/nargo/src/cli/fs/witness.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use std::path::{Path, PathBuf};

use acvm::acir::native_types::Witness;
use noirc_abi::WitnessMap;

use super::{create_named_dir, write_to_file};
use crate::{constants::WITNESS_EXT, errors::CliError};

pub(crate) fn save_witness_to_dir<P: AsRef<Path>>(
witness: WitnessMap,
witness_name: &str,
witness_dir: P,
) -> Result<PathBuf, CliError> {
let mut witness_path = create_named_dir(witness_dir.as_ref(), "witness");
witness_path.push(witness_name);
witness_path.set_extension(WITNESS_EXT);

let buf = Witness::to_bytes(&witness);

write_to_file(buf.as_slice(), &witness_path);

Ok(witness_path)
}
Loading

0 comments on commit 8cafa34

Please sign in to comment.