Skip to content

Commit

Permalink
Merge branch 'master' into phated/nix-ultra
Browse files Browse the repository at this point in the history
* master:
  feat(nargo)!: define preprocessed artifacts for programs/contracts (#1126)
  feat: import core logic in cli from `nargo` crate (#1142)
  chore: enforce `clippy::semicolon_if_nothing_returned` linting rule (#1139)
  chore: borrow instead of cloning witness vectors in IR gen (#1127)
  fix: compiler identifying imported functions as being part of a contract (#1112)
  feat: Add new `Vec` type to frontend (#1103)
  • Loading branch information
TomAFrench committed Apr 13, 2023
2 parents a5ad575 + 7528f59 commit c11e6e0
Show file tree
Hide file tree
Showing 75 changed files with 693 additions and 536 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/arena/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![forbid(unsafe_code)]
#![warn(unused_crate_dependencies, unused_extern_crates)]
#![warn(unreachable_pub)]
#![warn(clippy::semicolon_if_nothing_returned)]

// For now we use a wrapper around generational-arena
pub use generational_arena::{Arena, Index};
3 changes: 2 additions & 1 deletion crates/fm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![forbid(unsafe_code)]
#![warn(unused_crate_dependencies, unused_extern_crates)]
#![warn(unreachable_pub)]
#![warn(clippy::semicolon_if_nothing_returned)]

mod file_map;
mod file_reader;
Expand Down Expand Up @@ -136,7 +137,7 @@ mod tests {

let file_id = fm.add_file(&file_path, FileType::Normal).unwrap();

assert!(fm.path(file_id).ends_with("foo"))
assert!(fm.path(file_id).ends_with("foo"));
}
#[test]
fn path_resolve_sub_module() {
Expand Down
1 change: 1 addition & 0 deletions crates/iter-extended/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![forbid(unsafe_code)]
#![warn(unused_crate_dependencies, unused_extern_crates)]
#![warn(unreachable_pub)]
#![warn(clippy::semicolon_if_nothing_returned)]

use std::collections::BTreeMap;

Expand Down
4 changes: 4 additions & 0 deletions crates/nargo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ edition.workspace = true
rustc_version = "0.4.0"

[dependencies]
acvm.workspace = true
noirc_abi.workspace = true
noirc_driver.workspace = true
iter-extended.workspace = true
toml.workspace = true
serde.workspace = true
thiserror.workspace = true
41 changes: 41 additions & 0 deletions crates/nargo/src/artifacts/contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use acvm::acir::circuit::Circuit;
use noirc_abi::Abi;
use noirc_driver::ContractFunctionType;
use serde::{Deserialize, Serialize};

/// `PreprocessedContract` represents a Noir contract which has been preprocessed by a particular backend proving system.
///
/// This differs from a generic Noir contract artifact in that:
/// - The ACIR bytecode has had an optimization pass applied to tailor it for the backend.
/// - Proving and verification keys have been pregenerated based on this ACIR.
#[derive(Serialize, Deserialize)]
pub struct PreprocessedContract {
/// The name of the contract.
pub name: String,
/// The identifier of the proving backend which this contract has been compiled for.
pub backend: String,
/// Each of the contract's functions are compiled into a separate program stored in this `Vec`.
pub functions: Vec<PreprocessedContractFunction>,
}

/// Each function in the contract will be compiled as a separate noir program.
///
/// A contract function unlike a regular Noir program however can have additional properties.
/// One of these being a function type.
#[derive(Debug, Serialize, Deserialize)]
pub struct PreprocessedContractFunction {
pub name: String,

pub function_type: ContractFunctionType,

pub abi: Abi,

#[serde(
serialize_with = "super::serialize_circuit",
deserialize_with = "super::deserialize_circuit"
)]
pub bytecode: Circuit,

pub proving_key: Vec<u8>,
pub verification_key: Vec<u8>,
}
31 changes: 31 additions & 0 deletions crates/nargo/src/artifacts/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! This module defines the structure of Nargo's different compilation artifacts.
//!
//! These artifacts are intended to remain independent of any applications being built on top of Noir.
//! Should any projects require/desire a different artifact format, it's expected that they will write a transformer
//! to generate them using these artifacts as a starting point.
use acvm::acir::circuit::Circuit;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

pub mod contract;
pub mod program;

// TODO: move these down into ACVM.
fn serialize_circuit<S>(circuit: &Circuit, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut circuit_bytes: Vec<u8> = Vec::new();
circuit.write(&mut circuit_bytes).unwrap();

circuit_bytes.serialize(s)
}

fn deserialize_circuit<'de, D>(deserializer: D) -> Result<Circuit, D::Error>
where
D: Deserializer<'de>,
{
let circuit_bytes = Vec::<u8>::deserialize(deserializer)?;
let circuit = Circuit::read(&*circuit_bytes).unwrap();
Ok(circuit)
}
23 changes: 23 additions & 0 deletions crates/nargo/src/artifacts/program.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use acvm::acir::circuit::Circuit;
use noirc_abi::Abi;
use serde::{Deserialize, Serialize};

/// `PreprocessedProgram` represents a Noir program which has been preprocessed by a particular backend proving system.
///
/// This differs from a generic Noir program artifact in that:
/// - The ACIR bytecode has had an optimization pass applied to tailor it for the backend.
/// - Proving and verification keys have been pregenerated based on this ACIR.
#[derive(Serialize, Deserialize, Debug)]
pub struct PreprocessedProgram {
pub backend: String,
pub abi: Abi,

#[serde(
serialize_with = "super::serialize_circuit",
deserialize_with = "super::deserialize_circuit"
)]
pub bytecode: Circuit,

pub proving_key: Vec<u8>,
pub verification_key: Vec<u8>,
}
13 changes: 13 additions & 0 deletions crates/nargo/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use acvm::OpcodeResolutionError;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum NargoError {
/// Error while compiling Noir into ACIR.
#[error("Failed to compile circuit")]
CompilationError,

/// ACIR circuit solving error
#[error(transparent)]
SolvingError(#[from] OpcodeResolutionError),
}
6 changes: 6 additions & 0 deletions crates/nargo/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
#![forbid(unsafe_code)]
#![warn(unused_crate_dependencies, unused_extern_crates)]
#![warn(unreachable_pub)]
#![warn(clippy::semicolon_if_nothing_returned)]

//! Nargo is the package manager for Noir
//! This name was used because it sounds like `cargo` and
//! Noir Package Manager abbreviated is npm, which is already taken.
pub mod artifacts;
mod errors;
pub mod manifest;
pub mod ops;

pub use self::errors::NargoError;
10 changes: 10 additions & 0 deletions crates/nargo/src/ops/codegen_verifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use acvm::SmartContract;

use crate::NargoError;

pub fn codegen_verifier(
backend: &impl SmartContract,
verification_key: &[u8],
) -> Result<String, NargoError> {
Ok(backend.eth_contract_from_vk(verification_key))
}
20 changes: 20 additions & 0 deletions crates/nargo/src/ops/execute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use acvm::PartialWitnessGenerator;
use acvm::{acir::circuit::Circuit, pwg::block::Blocks};
use noirc_abi::WitnessMap;

use crate::NargoError;

pub fn execute_circuit(
backend: &impl PartialWitnessGenerator,
circuit: Circuit,
mut initial_witness: WitnessMap,
) -> Result<WitnessMap, NargoError> {
let mut blocks = Blocks::default();
let (unresolved_opcodes, oracles) =
backend.solve(&mut initial_witness, &mut blocks, circuit.opcodes)?;
if !unresolved_opcodes.is_empty() || !oracles.is_empty() {
todo!("Add oracle support to nargo execute")
}

Ok(initial_witness)
}
11 changes: 11 additions & 0 deletions crates/nargo/src/ops/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub use self::codegen_verifier::codegen_verifier;
pub use self::execute::execute_circuit;
pub use self::preprocess::{preprocess_contract, preprocess_program};
pub use self::prove::prove_execution;
pub use self::verify::verify_proof;

mod codegen_verifier;
mod execute;
mod preprocess;
mod prove;
mod verify;
60 changes: 60 additions & 0 deletions crates/nargo/src/ops/preprocess.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use acvm::ProofSystemCompiler;
use iter_extended::vecmap;
use noirc_driver::{CompiledContract, CompiledProgram};

use crate::{
artifacts::{
contract::{PreprocessedContract, PreprocessedContractFunction},
program::PreprocessedProgram,
},
NargoError,
};

// TODO: pull this from backend.
const BACKEND_IDENTIFIER: &str = "acvm-backend-barretenberg";

pub fn preprocess_program(
backend: &impl ProofSystemCompiler,
compiled_program: CompiledProgram,
) -> Result<PreprocessedProgram, NargoError> {
// TODO: currently `compiled_program`'s bytecode is already optimized for the backend.
// In future we'll need to apply those optimizations here.
let optimized_bytecode = compiled_program.circuit;
let (proving_key, verification_key) = backend.preprocess(&optimized_bytecode);

Ok(PreprocessedProgram {
backend: String::from(BACKEND_IDENTIFIER),
abi: compiled_program.abi,
bytecode: optimized_bytecode,
proving_key,
verification_key,
})
}

pub fn preprocess_contract(
backend: &impl ProofSystemCompiler,
compiled_contract: CompiledContract,
) -> Result<PreprocessedContract, NargoError> {
let preprocessed_contract_functions = vecmap(compiled_contract.functions, |func| {
// TODO: currently `func`'s bytecode is already optimized for the backend.
// In future we'll need to apply those optimizations here.
let optimized_bytecode = func.bytecode;
let (proving_key, verification_key) = backend.preprocess(&optimized_bytecode);

PreprocessedContractFunction {
name: func.name,
function_type: func.function_type,
abi: func.abi,

bytecode: optimized_bytecode,
proving_key,
verification_key,
}
});

Ok(PreprocessedContract {
name: compiled_contract.name,
backend: String::from(BACKEND_IDENTIFIER),
functions: preprocessed_contract_functions,
})
}
16 changes: 16 additions & 0 deletions crates/nargo/src/ops/prove.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use acvm::acir::circuit::Circuit;
use acvm::ProofSystemCompiler;
use noirc_abi::WitnessMap;

use crate::NargoError;

pub fn prove_execution(
backend: &impl ProofSystemCompiler,
circuit: &Circuit,
solved_witness: WitnessMap,
proving_key: &[u8],
) -> Result<Vec<u8>, NargoError> {
let proof = backend.prove_with_pk(circuit, solved_witness, proving_key);

Ok(proof)
}
17 changes: 17 additions & 0 deletions crates/nargo/src/ops/verify.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use acvm::acir::circuit::Circuit;
use acvm::ProofSystemCompiler;
use noirc_abi::WitnessMap;

use crate::NargoError;

pub fn verify_proof(
backend: &impl ProofSystemCompiler,
circuit: &Circuit,
proof: &[u8],
public_inputs: WitnessMap,
verification_key: &[u8],
) -> Result<bool, NargoError> {
let valid_proof = backend.verify_with_vk(proof, public_inputs, circuit, verification_key);

Ok(valid_proof)
}
20 changes: 6 additions & 14 deletions crates/nargo_cli/src/cli/codegen_verifier_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
};
use acvm::{ProofSystemCompiler, SmartContract};
use clap::Args;
use nargo::ops::{codegen_verifier, preprocess_program};
use noirc_driver::CompileOptions;

/// Generates a Solidity verifier smart contract for the program
Expand All @@ -23,30 +24,21 @@ pub(crate) struct CodegenVerifierCommand {
pub(crate) fn run(args: CodegenVerifierCommand, config: NargoConfig) -> Result<(), CliError> {
let backend = crate::backends::ConcreteBackend;

// Based on code in verify_cmd.rs
// TODO(blaine): Should this be a utility function?
let circuit_build_path = args
.circuit_name
.map(|circuit_name| config.program_dir.join(TARGET_DIR).join(circuit_name));

let verification_key = match circuit_build_path {
Some(circuit_build_path) => {
let compiled_program = read_program_from_file(&circuit_build_path)?;

let (_, verification_key) =
fetch_pk_and_vk(&compiled_program.circuit, circuit_build_path, false, true)?;
verification_key
}
let preprocessed_program = match circuit_build_path {
Some(circuit_build_path) => read_program_from_file(circuit_build_path)?,
None => {
let compiled_program =
compile_circuit(config.program_dir.as_ref(), &args.compile_options)?;

let (_, verification_key) = backend.preprocess(&compiled_program.circuit);
verification_key
compile_circuit(&backend, program_dir.as_ref(), &compile_options)?;
preprocess_program(&backend, compiled_program)?
}
};

let smart_contract_string = backend.eth_contract_from_vk(&verification_key);
let smart_contract_string = codegen_verifier(&backend, &preprocessed_program.verification_key)?;

let contract_dir = config.program_dir.join(CONTRACT_DIR);
create_named_dir(&contract_dir, "contract");
Expand Down
Loading

0 comments on commit c11e6e0

Please sign in to comment.