diff --git a/crates/nargo/src/cli/compile_cmd.rs b/crates/nargo/src/cli/compile_cmd.rs index c4938686ed3..1e9bd6be37a 100644 --- a/crates/nargo/src/cli/compile_cmd.rs +++ b/crates/nargo/src/cli/compile_cmd.rs @@ -1,5 +1,5 @@ use acvm::ProofSystemCompiler; -use noirc_driver::{CompileOptions, CompiledProgram, Driver}; +use noirc_driver::{CompileOptions, CompiledContract, CompiledProgram, Driver}; use std::path::Path; use clap::Args; @@ -7,7 +7,7 @@ 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; @@ -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) @@ -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 +/// functions 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: &[CompiledContract], + 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_function) in &compiled_contract.functions { + // Create a name which uniquely identifies this contract function + // 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_function.function.circuit, + )?; + } + } + + Ok(()) +} + pub(crate) fn compile_circuit( program_dir: &Path, compile_options: &CompileOptions, diff --git a/crates/nargo/src/cli/fs/program.rs b/crates/nargo/src/cli/fs/program.rs index ce7999ed28e..b50ba4665b7 100644 --- a/crates/nargo/src/cli/fs/program.rs +++ b/crates/nargo/src/cli/fs/program.rs @@ -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}; @@ -11,11 +11,25 @@ pub(crate) fn save_program_to_file>( 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>( + 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, T: ?Sized + serde::Serialize>( + build_artifact: &T, + 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(build_artifact).unwrap(), &circuit_path); circuit_path } diff --git a/crates/noirc_driver/src/contract.rs b/crates/noirc_driver/src/contract.rs index 85e594e3b99..3f438355f3b 100644 --- a/crates/noirc_driver/src/contract.rs +++ b/crates/noirc_driver/src/contract.rs @@ -2,10 +2,45 @@ 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 constraints. + Unconstrained, +} +/// Each function in the contract will be compiled +/// as a separate noir program. +/// +/// A contract function unlike a regular Noir program +/// however can have addition properties. +/// One of these being a function type. +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ContractFunction { + 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, + pub functions: BTreeMap, } diff --git a/crates/noirc_driver/src/lib.rs b/crates/noirc_driver/src/lib.rs index 131a78095e7..789ea90ae10 100644 --- a/crates/noirc_driver/src/lib.rs +++ b/crates/noirc_driver/src/lib.rs @@ -4,6 +4,7 @@ use acvm::Language; use clap::Args; +use contract::ContractFunction; use fm::FileType; use iter_extended::{try_btree_map, try_vecmap}; use noirc_abi::FunctionSignature; @@ -203,14 +204,16 @@ impl Driver { ) -> Result { 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)) + let function = self.compile_no_check(options, *function)?; + let func_type = contract::ContractFunctionType::Secret; + // Note: currently we mark all of the contract methods as secret as we do not support public functions. + Ok((function_name, ContractFunction { func_type, function })) })?; 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 {