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

chore!: Restrict packages to contain at most a single contract #2668

Merged
merged 2 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

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

34 changes: 21 additions & 13 deletions compiler/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@
let compiled_program = compile_no_check(context, options, main)?;

if options.print_acir {
println!("Compiled ACIR for main (unoptimized):");

Check warning on line 164 in compiler/noirc_driver/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (unoptimized)
println!("{}", compiled_program.circuit);
}

Expand All @@ -169,20 +169,27 @@
}

/// Run the frontend to check the crate for errors then compile all contracts if there were none
pub fn compile_contracts(
pub fn compile_contract(
context: &mut Context,
crate_id: CrateId,
options: &CompileOptions,
) -> CompilationResult<Vec<CompiledContract>> {
) -> CompilationResult<CompiledContract> {
let (_, warnings) = check_crate(context, crate_id, options.deny_warnings)?;

// TODO: We probably want to error if contracts is empty
let contracts = context.get_all_contracts(&crate_id);

let mut compiled_contracts = vec![];
let mut errors = warnings;

if contracts.len() > 1 {
let err = CustomDiagnostic::from_message("Packages are limited to a single contract")
.in_file(FileId::default());
return Err(vec![err]);
};

for contract in contracts {
match compile_contract(context, contract, options) {
match compile_contract_inner(context, contract, options) {
Ok(contract) => compiled_contracts.push(contract),
Err(mut more_errors) => errors.append(&mut more_errors),
}
Expand All @@ -191,19 +198,20 @@
if has_errors(&errors, options.deny_warnings) {
Err(errors)
} else {
assert_eq!(compiled_contracts.len(), 1);
let compiled_contract = compiled_contracts.remove(0);

if options.print_acir {
for compiled_contract in &compiled_contracts {
for contract_function in &compiled_contract.functions {
println!(
"Compiled ACIR for {}::{} (unoptimized):",
compiled_contract.name, contract_function.name
);
println!("{}", contract_function.bytecode);
}
for contract_function in &compiled_contract.functions {
println!(
"Compiled ACIR for {}::{} (unoptimized):",

Check warning on line 207 in compiler/noirc_driver/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (unoptimized)
compiled_contract.name, contract_function.name
);
println!("{}", contract_function.bytecode);
}
}
// errors here is either empty or contains only warnings
Ok((compiled_contracts, errors))
Ok((compiled_contract, errors))
}
}

Expand All @@ -217,7 +225,7 @@
}

/// Compile all of the functions associated with a Noir contract.
fn compile_contract(
fn compile_contract_inner(
context: &Context,
contract: Contract,
options: &CompileOptions,
Expand Down
1 change: 0 additions & 1 deletion compiler/wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ crate-type = ["cdylib"]
[dependencies]
acvm.workspace = true
fm.workspace = true
iter-extended.workspace = true
nargo.workspace = true
noirc_driver.workspace = true
noirc_frontend.workspace = true
Expand Down
19 changes: 8 additions & 11 deletions compiler/wasm/src/compile.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use fm::FileManager;
use gloo_utils::format::JsValueSerdeExt;
use iter_extended::try_vecmap;
use log::debug;
use noirc_driver::{
add_dep, compile_contracts, compile_main, prepare_crate, prepare_dependency, CompileOptions,
add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, CompileOptions,
};
use noirc_frontend::{graph::CrateGraph, hir::Context};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -116,17 +115,15 @@ pub fn compile(args: JsValue) -> JsValue {
let is_opcode_supported = acvm::pwg::default_is_opcode_supported(np_language);

if options.contracts {
let compiled_contracts =
compile_contracts(&mut context, crate_id, &options.compile_options)
.expect("Contract compilation failed")
.0;
let compiled_contract = compile_contract(&mut context, crate_id, &options.compile_options)
.expect("Contract compilation failed")
.0;

let optimized_contracts = try_vecmap(compiled_contracts, |contract| {
nargo::ops::optimize_contract(contract, np_language, &is_opcode_supported)
})
.expect("Contract optimization failed");
let optimized_contract =
nargo::ops::optimize_contract(compiled_contract, np_language, &is_opcode_supported)
.expect("Contract optimization failed");

<JsValue as JsValueSerdeExt>::from_serde(&optimized_contracts).unwrap()
<JsValue as JsValueSerdeExt>::from_serde(&optimized_contract).unwrap()
} else {
let compiled_program = compile_main(&mut context, crate_id, &options.compile_options)
.expect("Compilation failed")
Expand Down
111 changes: 48 additions & 63 deletions tooling/nargo_cli/src/cli/compile_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::path::Path;
use acvm::acir::circuit::Opcode;
use acvm::Language;
use fm::FileManager;
use iter_extended::{try_vecmap, vecmap};
use iter_extended::vecmap;
use nargo::artifacts::contract::PreprocessedContract;
use nargo::artifacts::contract::PreprocessedContractFunction;
use nargo::artifacts::debug::DebugArtifact;
Expand Down Expand Up @@ -78,11 +78,11 @@ pub(crate) fn run(
compile_program(package, &args.compile_options, np_language, &is_opcode_supported)
})
.collect();
let contract_results: Vec<(FileManager, CompilationResult<Vec<CompiledContract>>)> =
let contract_results: Vec<(FileManager, CompilationResult<CompiledContract>)> =
contract_packages
.par_iter()
.map(|package| {
compile_contracts(package, &args.compile_options, np_language, &is_opcode_supported)
compile_contract(package, &args.compile_options, np_language, &is_opcode_supported)
})
.collect();

Expand All @@ -93,7 +93,7 @@ pub(crate) fn run(
report_errors(compilation_result, &file_manager, args.compile_options.deny_warnings)
})
.collect::<Result<_, _>>()?;
let compiled_contracts: Vec<Vec<CompiledContract>> = contract_results
let compiled_contracts: Vec<CompiledContract> = contract_results
.into_iter()
.map(|(file_manager, compilation_result)| {
report_errors(compilation_result, &file_manager, args.compile_options.deny_warnings)
Expand All @@ -104,8 +104,8 @@ pub(crate) fn run(
for (package, program) in binary_packages.into_iter().zip(compiled_programs) {
save_program(program, package, &circuit_dir, args.output_debug);
}
for (package, compiled_contracts) in contract_packages.into_iter().zip(compiled_contracts) {
save_contracts(compiled_contracts, package, &circuit_dir, args.output_debug);
for (package, compiled_contract) in contract_packages.into_iter().zip(compiled_contracts) {
save_contract(compiled_contract, package, &circuit_dir, args.output_debug);
}

Ok(())
Expand Down Expand Up @@ -134,12 +134,12 @@ pub(crate) fn compile_contract_package(
compile_options: &CompileOptions,
np_language: Language,
is_opcode_supported: &impl Fn(&Opcode) -> bool,
) -> Result<Vec<CompiledContract>, CliError> {
) -> Result<CompiledContract, CliError> {
let (file_manager, compilation_result) =
compile_contracts(package, compile_options, np_language, &is_opcode_supported);
let contracts_with_debug_artifacts =
compile_contract(package, compile_options, np_language, &is_opcode_supported);
let contract_and_debug_artifact =
report_errors(compilation_result, &file_manager, compile_options.deny_warnings)?;
Ok(contracts_with_debug_artifacts)
Ok(contract_and_debug_artifact)
}

fn compile_program(
Expand All @@ -166,27 +166,26 @@ fn compile_program(
(context.file_manager, Ok((optimized_program, warnings)))
}

fn compile_contracts(
fn compile_contract(
package: &Package,
compile_options: &CompileOptions,
np_language: Language,
is_opcode_supported: &impl Fn(&Opcode) -> bool,
) -> (FileManager, CompilationResult<Vec<CompiledContract>>) {
) -> (FileManager, CompilationResult<CompiledContract>) {
let (mut context, crate_id) = prepare_package(package);
let (contracts, warnings) =
match noirc_driver::compile_contracts(&mut context, crate_id, compile_options) {
let (contract, warnings) =
match noirc_driver::compile_contract(&mut context, crate_id, compile_options) {
Ok(contracts_and_warnings) => contracts_and_warnings,
Err(errors) => {
return (context.file_manager, Err(errors));
}
};

let optimized_contracts = try_vecmap(contracts, |contract| {
let optimized_contract =
nargo::ops::optimize_contract(contract, np_language, &is_opcode_supported)
})
.expect("Backend does not support an opcode that is in the IR");
.expect("Backend does not support an opcode that is in the IR");

(context.file_manager, Ok((optimized_contracts, warnings)))
(context.file_manager, Ok((optimized_contract, warnings)))
}

fn save_program(
Expand All @@ -211,8 +210,8 @@ fn save_program(
}
}

fn save_contracts(
contracts: Vec<CompiledContract>,
fn save_contract(
contract: CompiledContract,
package: &Package,
circuit_dir: &Path,
output_debug: bool,
Expand All @@ -221,51 +220,37 @@ fn save_contracts(
// As can be seen here, It seems like a leaky abstraction where ContractFunctions (essentially CompiledPrograms)
// are compiled via nargo-core and then the PreprocessedContract is constructed here.
// This is due to EACH function needing it's own CRS, PKey, and VKey from the backend.
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
let preprocessed_contracts: Vec<(PreprocessedContract, DebugArtifact)> =
vecmap(contracts, |contract| {
let debug_artifact = DebugArtifact {
debug_symbols: contract
.functions
.iter()
.map(|function| function.debug.clone())
.collect(),
file_map: contract.file_map,
};

let preprocessed_functions =
vecmap(contract.functions, |func| PreprocessedContractFunction {
name: func.name,
function_type: func.function_type,
is_internal: func.is_internal,
abi: func.abi,

bytecode: func.bytecode,
});

(
PreprocessedContract {
name: contract.name,
backend: String::from(BACKEND_IDENTIFIER),
functions: preprocessed_functions,
},
debug_artifact,
)
});

for (contract, debug_artifact) in preprocessed_contracts {
save_contract_to_file(
&contract,
&format!("{}-{}", package.name, contract.name),
let debug_artifact = DebugArtifact {
debug_symbols: contract.functions.iter().map(|function| function.debug.clone()).collect(),
file_map: contract.file_map,
};

let preprocessed_functions = vecmap(contract.functions, |func| PreprocessedContractFunction {
name: func.name,
function_type: func.function_type,
is_internal: func.is_internal,
abi: func.abi,
bytecode: func.bytecode,
});

let preprocessed_contract = PreprocessedContract {
name: contract.name,
backend: String::from(BACKEND_IDENTIFIER),
functions: preprocessed_functions,
};

save_contract_to_file(
&preprocessed_contract,
&format!("{}-{}", package.name, preprocessed_contract.name),
circuit_dir,
);

if output_debug {
save_debug_artifact_to_file(
&debug_artifact,
&format!("{}-{}", package.name, preprocessed_contract.name),
circuit_dir,
);

if output_debug {
save_debug_artifact_to_file(
&debug_artifact,
&format!("{}-{}", package.name, contract.name),
circuit_dir,
);
}
}
}

Expand Down
24 changes: 11 additions & 13 deletions tooling/nargo_cli/src/cli/info_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub(crate) fn run(
np_language,
&is_opcode_supported,
)?;
info_report.contracts.extend(contract_info);
info_report.contracts.push(contract_info);
} else {
let program_info = count_opcodes_and_gates_in_program(
backend,
Expand Down Expand Up @@ -192,20 +192,18 @@ fn count_opcodes_and_gates_in_contracts(
compile_options: &CompileOptions,
np_language: Language,
is_opcode_supported: &impl Fn(&Opcode) -> bool,
) -> Result<Vec<ContractInfo>, CliError> {
let contracts =
) -> Result<ContractInfo, CliError> {
let contract =
compile_contract_package(package, compile_options, np_language, &is_opcode_supported)?;
let (language, _) = backend.get_backend_info()?;

try_vecmap(contracts, |contract| {
let functions = try_vecmap(contract.functions, |function| -> Result<_, BackendError> {
Ok(FunctionInfo {
name: function.name,
acir_opcodes: function.bytecode.opcodes.len(),
circuit_size: backend.get_exact_circuit_size(&function.bytecode)?,
})
})?;
let functions = try_vecmap(contract.functions, |function| -> Result<_, BackendError> {
Ok(FunctionInfo {
name: function.name,
acir_opcodes: function.bytecode.opcodes.len(),
circuit_size: backend.get_exact_circuit_size(&function.bytecode)?,
})
})?;

Ok(ContractInfo { name: contract.name, language, functions })
})
Ok(ContractInfo { name: contract.name, language, functions })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "multiple_contracts"
type = "contract"
authors = [""]
compiler_version = "0.9.0"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
contract Foo {}


contract Bar {}