Skip to content

Commit

Permalink
feat: Merge all contracts into one ABI (#1033)
Browse files Browse the repository at this point in the history
* add contract function type and contract method

* when compiling contracts, we explicitly wrap the program as a ContractMethod along with a default FunctionType

* add a `save_contract_to_file` method

* add method to save and preprocess contracts

* Update crates/nargo/src/cli/compile_cmd.rs

Co-authored-by: Tom French <[email protected]>

* use `function` instead of `method` to describe a function in a contract.

* fix clippy

* Update crates/nargo/src/cli/fs/program.rs

Co-authored-by: Tom French <[email protected]>

* Update crates/noirc_driver/src/contract.rs

Co-authored-by: Tom French <[email protected]>

* fix: update remaining instance of variable name

* Format code

---------

Co-authored-by: Tom French <[email protected]>
Co-authored-by: jfecher <[email protected]>
Co-authored-by: Tom French <[email protected]>
  • Loading branch information
4 people authored Mar 27, 2023
1 parent 38bf571 commit 473428c
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 22 deletions.
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
/// 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,
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>(
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
}
Expand Down
37 changes: 36 additions & 1 deletion crates/noirc_driver/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, CompiledProgram>,
pub functions: BTreeMap<String, ContractFunction>,
}
9 changes: 6 additions & 3 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::ContractFunction;
use fm::FileType;
use iter_extended::{try_btree_map, try_vecmap};
use noirc_abi::FunctionSignature;
Expand Down Expand Up @@ -203,14 +204,16 @@ impl Driver {
) -> Result<CompiledContract, ReportedError> {
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<FuncId, ReportedError> {
Expand Down

0 comments on commit 473428c

Please sign in to comment.