Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Merge all contracts into one ABI #1033

Merged
merged 12 commits into from
Mar 27, 2023
53 changes: 37 additions & 16 deletions crates/nargo/src/cli/compile_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use acvm::ProofSystemCompiler;
use noirc_driver::{CompileOptions, CompiledProgram, Driver};
use noirc_driver::{CompileOptions, CompiledContract, CompiledProgram, Driver};
use std::path::Path;

use clap::Args;

use crate::resolver::DependencyResolutionError;
use crate::{constants::TARGET_DIR, errors::CliError, resolver::Resolver};

use super::fs::program::save_program_to_file;
use super::fs::program::{save_contract_to_file, save_program_to_file};
use super::preprocess_cmd::preprocess_with_path;
use super::NargoConfig;

Expand All @@ -34,20 +34,7 @@ pub(crate) fn run(args: CompileCommand, config: NargoConfig) -> Result<(), CliEr
let compiled_contracts = driver
.compile_contracts(&args.compile_options)
.map_err(|_| CliError::CompilationError)?;

// Flatten each contract into a list of its functions, each being assigned a unique name.
let compiled_programs = compiled_contracts.into_iter().flat_map(|contract| {
let contract_id = format!("{}-{}", args.circuit_name, &contract.name);
contract.functions.into_iter().map(move |(function, program)| {
let program_name = format!("{}-{}", contract_id, function);
(program_name, program)
})
});

for (circuit_name, compiled_program) in compiled_programs {
save_and_preprocess_program(&compiled_program, &circuit_name, &circuit_dir)?
}
Ok(())
save_and_preprocess_contract(compiled_contracts, &args.circuit_name, &circuit_dir)
} else {
let program = compile_circuit(&config.program_dir, &args.compile_options)?;
save_and_preprocess_program(&program, &args.circuit_name, &circuit_dir)
Expand All @@ -70,6 +57,40 @@ fn save_and_preprocess_program(
Ok(())
}

/// Save a contract to disk along with proving and verification keys.
/// - The contract ABI is saved as one file, which contains all of the
/// methods defined in the contract.
/// - The proving and verification keys are namespaced since the file
/// could contain multiple contracts with the same name.
fn save_and_preprocess_contract(
compiled_contracts: Vec<CompiledContract>,
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
circuit_name: &str,
circuit_dir: &Path,
) -> Result<(), CliError> {
for compiled_contract in compiled_contracts {
// Unique identifier for a contract.
let contract_id = format!("{}-{}", circuit_name, &compiled_contract.name);

// Save contract ABI to file using the contract ID.
save_contract_to_file(&compiled_contract, &contract_id, circuit_dir);

for (function_name, contract_method) in compiled_contract.functions {
// Create a name which uniquely identifies this contract method
// over multiple contracts.
let uniquely_identifying_program_name = format!("{}-{}", contract_id, function_name);
// Each program in a contract is preprocessed
// Note: This can potentially be quite a long running process
preprocess_with_path(
&uniquely_identifying_program_name,
circuit_dir,
&contract_method.function.circuit,
)?;
}
}

Ok(())
}

pub(crate) fn compile_circuit(
program_dir: &Path,
compile_options: &CompileOptions,
Expand Down
18 changes: 16 additions & 2 deletions crates/nargo/src/cli/fs/program.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::{Path, PathBuf};

use acvm::{acir::circuit::Circuit, hash_constraint_system};
use noirc_driver::CompiledProgram;
use noirc_driver::{CompiledContract, CompiledProgram};

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

Expand All @@ -11,11 +11,25 @@ pub(crate) fn save_program_to_file<P: AsRef<Path>>(
compiled_program: &CompiledProgram,
circuit_name: &str,
circuit_dir: P,
) -> PathBuf {
save_build_artifact_to_file(compiled_program, circuit_name, circuit_dir)
}
pub(crate) fn save_contract_to_file<P: AsRef<Path>>(
compiled_contract: &CompiledContract,
circuit_name: &str,
circuit_dir: P,
) -> PathBuf {
save_build_artifact_to_file(compiled_contract, circuit_name, circuit_dir)
}
fn save_build_artifact_to_file<P: AsRef<Path>, T: ?Sized + serde::Serialize>(
strukt: &T,
jfecher marked this conversation as resolved.
Show resolved Hide resolved
circuit_name: &str,
circuit_dir: P,
) -> PathBuf {
create_named_dir(circuit_dir.as_ref(), "target");
let circuit_path = circuit_dir.as_ref().join(circuit_name).with_extension("json");

write_to_file(&serde_json::to_vec(compiled_program).unwrap(), &circuit_path);
write_to_file(&serde_json::to_vec(strukt).unwrap(), &circuit_path);

circuit_path
}
Expand Down
38 changes: 37 additions & 1 deletion crates/noirc_driver/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,46 @@ use std::collections::BTreeMap;

use crate::CompiledProgram;

/// ContractFunctionType describes the types
/// smart contract functions that are allowed.
///
/// Note:
/// - All Noir programs in the non-contract context
/// can be seen as `Secret`.
/// - It may be possible to have `unconstrained`
/// functions in regular Noir programs. For now
/// we leave it as a property of only contract functions.
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ContractFunctionType {
/// This function will be executed in a private
/// context.
Secret,
/// This function will be executed in a public
/// context.
Public,
// / A function which is non-deterministic
// / and does not require any constraint.
jfecher marked this conversation as resolved.
Show resolved Hide resolved
Unconstrained,
}
/// Each method in the contract , a contract method,
/// will be compiled as a separate noir program.
///
/// A contract method unlike a regular Noir program
/// however can have addition properties.
/// One of these being a function type.
#[derive(serde::Serialize, serde::Deserialize)]
pub struct ContractMethod {
pub func_type: ContractFunctionType,
pub function: CompiledProgram,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct CompiledContract {
/// The name of the contract.
pub name: String,
/// Each of the contract's functions are compiled into a separate `CompiledProgram`
/// stored in this `BTreeMap`.
pub functions: BTreeMap<String, CompiledProgram>,
#[serde(rename = "methods")]
pub functions: BTreeMap<String, ContractMethod>,
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
}
13 changes: 11 additions & 2 deletions crates/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use acvm::Language;
use clap::Args;
use contract::ContractMethod;
use fm::FileType;
use iter_extended::{try_btree_map, try_vecmap};
use noirc_abi::FunctionSignature;
Expand Down Expand Up @@ -204,13 +205,21 @@ impl Driver {
let functions = try_btree_map(&contract.functions, |function| {
let function_name = self.function_name(*function).to_owned();
let program = self.compile_no_check(options, *function)?;
Ok((function_name, program))
Ok((
function_name,
ContractMethod {
// Note: currently we mark all of the contract methods
// as secret as we do not support public functions.
func_type: contract::ContractFunctionType::Secret,
function: program,
},
))
})?;

Ok(CompiledContract { name: contract.name, functions })
}

/// Returns the FuncId of the 'main' funciton.
/// Returns the FuncId of the 'main' function.
/// - Expects check_crate to be called beforehand
/// - Panics if no main function is found
pub fn main_function(&self) -> Result<FuncId, ReportedError> {
Expand Down