From 2d0a5e447b02b11426ad80b64fba817dfce38e44 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:02:51 +0100 Subject: [PATCH] feat: pull `Language` and `Opcode` support from backend (#2563) --- Cargo.lock | 2 + crates/acvm_backend_barretenberg/Cargo.toml | 2 + crates/acvm_backend_barretenberg/src/bb.rs | 6 +- .../acvm_backend_barretenberg/src/cli/info.rs | 94 +++++++++++++++++++ .../acvm_backend_barretenberg/src/cli/mod.rs | 2 + .../src/proof_system.rs | 36 ++----- .../mock_backend/src/info_cmd.rs | 38 ++++++++ .../test-binaries/mock_backend/src/main.rs | 3 + .../src/cli/backend_cmd/uninstall_cmd.rs | 6 +- crates/nargo_cli/src/cli/compile_cmd.rs | 7 +- crates/nargo_cli/src/cli/info_cmd.rs | 6 +- 11 files changed, 163 insertions(+), 39 deletions(-) create mode 100644 crates/acvm_backend_barretenberg/src/cli/info.rs create mode 100644 crates/acvm_backend_barretenberg/test-binaries/mock_backend/src/info_cmd.rs diff --git a/Cargo.lock b/Cargo.lock index 0011a4685d5..c1bbafd63b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,8 @@ dependencies = [ "dirs", "flate2", "reqwest", + "serde", + "serde_json", "serial_test", "tar", "tempfile", diff --git a/crates/acvm_backend_barretenberg/Cargo.toml b/crates/acvm_backend_barretenberg/Cargo.toml index 58162ea4015..67f6c2b83a9 100644 --- a/crates/acvm_backend_barretenberg/Cargo.toml +++ b/crates/acvm_backend_barretenberg/Cargo.toml @@ -13,6 +13,8 @@ license = "MIT OR Apache-2.0" acvm.workspace = true dirs.workspace = true thiserror.workspace = true +serde.workspace = true +serde_json.workspace = true tempfile = "3.6.0" diff --git a/crates/acvm_backend_barretenberg/src/bb.rs b/crates/acvm_backend_barretenberg/src/bb.rs index 703ee5174c9..3270fd096a2 100644 --- a/crates/acvm_backend_barretenberg/src/bb.rs +++ b/crates/acvm_backend_barretenberg/src/bb.rs @@ -8,7 +8,11 @@ const TAG: &str = formatcp!("barretenberg-v{}", VERSION); const API_URL: &str = formatcp!("https://github.com/{}/{}/releases/download/{}", USERNAME, REPO, TAG); -pub(crate) fn get_bb_download_url() -> String { +pub(super) fn get_bb_download_url() -> String { + if let Ok(path) = std::env::var("BB_BINARY_URL") { + return path; + } + let target_os = env!("TARGET_OS"); let target_arch = env!("TARGET_ARCH"); diff --git a/crates/acvm_backend_barretenberg/src/cli/info.rs b/crates/acvm_backend_barretenberg/src/cli/info.rs new file mode 100644 index 00000000000..a39122567d9 --- /dev/null +++ b/crates/acvm_backend_barretenberg/src/cli/info.rs @@ -0,0 +1,94 @@ +use acvm::acir::circuit::opcodes::Opcode; +use acvm::Language; +use serde::Deserialize; +use std::collections::HashSet; +use std::path::{Path, PathBuf}; + +use crate::BackendError; + +pub(crate) struct InfoCommand { + pub(crate) crs_path: PathBuf, +} + +#[derive(Deserialize)] +struct InfoResponse { + language: LanguageResponse, + opcodes_supported: Vec, + black_box_functions_supported: Vec, +} + +#[derive(Deserialize)] +struct LanguageResponse { + name: String, + width: Option, +} + +impl InfoCommand { + pub(crate) fn run( + self, + binary_path: &Path, + ) -> Result<(Language, Box bool>), BackendError> { + let mut command = std::process::Command::new(binary_path); + + command.arg("info").arg("-c").arg(self.crs_path).arg("-o").arg("-"); + + let output = command.output().expect("Failed to execute command"); + + if !output.status.success() { + return Err(BackendError(String::from_utf8(output.stderr).unwrap())); + } + + let backend_info: InfoResponse = + serde_json::from_slice(&output.stdout).expect("Backend should return valid json"); + let language: Language = match backend_info.language.name.as_str() { + "PLONK-CSAT" => { + let width = backend_info.language.width.unwrap(); + Language::PLONKCSat { width } + } + "R1CS" => Language::R1CS, + _ => panic!("Unknown langauge"), + }; + + let opcodes_set: HashSet = backend_info.opcodes_supported.into_iter().collect(); + let black_box_functions_set: HashSet = + backend_info.black_box_functions_supported.into_iter().collect(); + + let is_opcode_supported = move |opcode: &Opcode| -> bool { + match opcode { + Opcode::Arithmetic(_) => opcodes_set.contains("arithmetic"), + Opcode::Directive(_) => opcodes_set.contains("directive"), + Opcode::Brillig(_) => opcodes_set.contains("brillig"), + Opcode::MemoryInit { .. } => opcodes_set.contains("memory_init"), + Opcode::MemoryOp { .. } => opcodes_set.contains("memory_op"), + Opcode::BlackBoxFuncCall(func) => { + black_box_functions_set.contains(func.get_black_box_func().name()) + } + } + }; + + Ok((language, Box::new(is_opcode_supported))) + } +} + +#[test] +#[serial_test::serial] +fn info_command() { + use acvm::acir::circuit::black_box_functions::BlackBoxFunc; + use acvm::acir::circuit::opcodes::{BlackBoxFuncCall, Opcode}; + + use acvm::acir::native_types::Expression; + + let backend = crate::get_mock_backend(); + let crs_path = backend.backend_directory(); + + let (language, is_opcode_supported) = + InfoCommand { crs_path }.run(&backend.binary_path()).unwrap(); + + assert!(matches!(language, Language::PLONKCSat { width: 3 })); + assert!(is_opcode_supported(&Opcode::Arithmetic(Expression::default()))); + + assert!(!is_opcode_supported(&Opcode::BlackBoxFuncCall( + #[allow(deprecated)] + BlackBoxFuncCall::dummy(BlackBoxFunc::Keccak256) + ))); +} diff --git a/crates/acvm_backend_barretenberg/src/cli/mod.rs b/crates/acvm_backend_barretenberg/src/cli/mod.rs index daad2f9b639..f34738acb62 100644 --- a/crates/acvm_backend_barretenberg/src/cli/mod.rs +++ b/crates/acvm_backend_barretenberg/src/cli/mod.rs @@ -2,12 +2,14 @@ mod contract; mod gates; +mod info; mod prove; mod verify; mod write_vk; pub(crate) use contract::ContractCommand; pub(crate) use gates::GatesCommand; +pub(crate) use info::InfoCommand; pub(crate) use prove::ProveCommand; pub(crate) use verify::VerifyCommand; pub(crate) use write_vk::WriteVkCommand; diff --git a/crates/acvm_backend_barretenberg/src/proof_system.rs b/crates/acvm_backend_barretenberg/src/proof_system.rs index 2bc2774c498..a53513fa49e 100644 --- a/crates/acvm_backend_barretenberg/src/proof_system.rs +++ b/crates/acvm_backend_barretenberg/src/proof_system.rs @@ -3,19 +3,15 @@ use std::io::{Read, Write}; use std::path::Path; use acvm::acir::circuit::Opcode; -use acvm::acir::{circuit::Circuit, native_types::WitnessMap, BlackBoxFunc}; +use acvm::acir::{circuit::Circuit, native_types::WitnessMap}; use acvm::FieldElement; use acvm::Language; use tempfile::tempdir; -use crate::cli::{GatesCommand, ProveCommand, VerifyCommand, WriteVkCommand}; +use crate::cli::{GatesCommand, InfoCommand, ProveCommand, VerifyCommand, WriteVkCommand}; use crate::{assert_binary_exists, Backend, BackendError}; impl Backend { - pub fn np_language(&self) -> Language { - Language::PLONKCSat { width: 3 } - } - pub fn get_exact_circuit_size(&self, circuit: &Circuit) -> Result { let temp_directory = tempdir().expect("could not create a temporary directory"); let temp_directory = temp_directory.path().to_path_buf(); @@ -30,29 +26,11 @@ impl Backend { .run(&binary_path) } - pub fn supports_opcode(&self, opcode: &Opcode) -> bool { - match opcode { - Opcode::Arithmetic(_) => true, - Opcode::Directive(_) => true, - Opcode::Brillig(_) => true, - Opcode::MemoryInit { .. } => true, - Opcode::MemoryOp { .. } => true, - Opcode::BlackBoxFuncCall(func) => match func.get_black_box_func() { - BlackBoxFunc::AND - | BlackBoxFunc::XOR - | BlackBoxFunc::RANGE - | BlackBoxFunc::SHA256 - | BlackBoxFunc::Blake2s - | BlackBoxFunc::Keccak256 - | BlackBoxFunc::SchnorrVerify - | BlackBoxFunc::Pedersen - | BlackBoxFunc::HashToField128Security - | BlackBoxFunc::EcdsaSecp256k1 - | BlackBoxFunc::EcdsaSecp256r1 - | BlackBoxFunc::FixedBaseScalarMul - | BlackBoxFunc::RecursiveAggregation => true, - }, - } + pub fn get_backend_info( + &self, + ) -> Result<(Language, Box bool>), BackendError> { + let binary_path = assert_binary_exists(self); + InfoCommand { crs_path: self.crs_directory() }.run(&binary_path) } pub fn prove( diff --git a/crates/acvm_backend_barretenberg/test-binaries/mock_backend/src/info_cmd.rs b/crates/acvm_backend_barretenberg/test-binaries/mock_backend/src/info_cmd.rs new file mode 100644 index 00000000000..e3314c10911 --- /dev/null +++ b/crates/acvm_backend_barretenberg/test-binaries/mock_backend/src/info_cmd.rs @@ -0,0 +1,38 @@ +use clap::Args; +use std::io::Write; +use std::path::PathBuf; + +const INFO_RESPONSE: &str = r#"{ + "language": { + "name": "PLONK-CSAT", + "width": 3 + }, + "opcodes_supported": ["arithmetic", "directive", "brillig", "memory_init", "memory_op"], + "black_box_functions_supported": [ + "and", + "xor", + "range", + "sha256", + "blake2s", + "schnorr_verify", + "pedersen", + "hash_to_field_128_security", + "ecdsa_secp256k1", + "ecdsa_secp256r1", + "fixed_base_scalar_mul", + "recursive_aggregation" + ] +}"#; + +#[derive(Debug, Clone, Args)] +pub(crate) struct InfoCommand { + #[clap(short = 'c')] + pub(crate) crs_path: Option, + + #[clap(short = 'o')] + pub(crate) info_path: Option, +} + +pub(crate) fn run(_args: InfoCommand) { + std::io::stdout().write_all(INFO_RESPONSE.as_bytes()).unwrap(); +} diff --git a/crates/acvm_backend_barretenberg/test-binaries/mock_backend/src/main.rs b/crates/acvm_backend_barretenberg/test-binaries/mock_backend/src/main.rs index 74ea82d28f8..ef8819af94b 100644 --- a/crates/acvm_backend_barretenberg/test-binaries/mock_backend/src/main.rs +++ b/crates/acvm_backend_barretenberg/test-binaries/mock_backend/src/main.rs @@ -7,6 +7,7 @@ use clap::{Parser, Subcommand}; mod contract_cmd; mod gates_cmd; +mod info_cmd; mod prove_cmd; mod verify_cmd; mod write_vk_cmd; @@ -20,6 +21,7 @@ struct BackendCli { #[derive(Subcommand, Clone, Debug)] enum BackendCommand { + Info(info_cmd::InfoCommand), Contract(contract_cmd::ContractCommand), Gates(gates_cmd::GatesCommand), Prove(prove_cmd::ProveCommand), @@ -32,6 +34,7 @@ fn main() { let BackendCli { command } = BackendCli::parse(); match command { + BackendCommand::Info(args) => info_cmd::run(args), BackendCommand::Contract(args) => contract_cmd::run(args), BackendCommand::Gates(args) => gates_cmd::run(args), BackendCommand::Prove(args) => prove_cmd::run(args), diff --git a/crates/nargo_cli/src/cli/backend_cmd/uninstall_cmd.rs b/crates/nargo_cli/src/cli/backend_cmd/uninstall_cmd.rs index aa261639b2a..e46b7cf468a 100644 --- a/crates/nargo_cli/src/cli/backend_cmd/uninstall_cmd.rs +++ b/crates/nargo_cli/src/cli/backend_cmd/uninstall_cmd.rs @@ -41,14 +41,14 @@ pub(crate) fn run(args: UninstallCommand) -> Result<(), CliError> { }; if let Some(backend) = new_active_backend { - set_active_backend(backend) + set_active_backend(backend); } else { // We've deleted the last backend. Clear the active backend file to be recreated once we install a new one. - clear_active_backend() + clear_active_backend(); } } - std::fs::remove_dir_all(&backends_directory().join(args.backend)) + std::fs::remove_dir_all(backends_directory().join(args.backend)) .expect("backend directory should be deleted"); Ok(()) diff --git a/crates/nargo_cli/src/cli/compile_cmd.rs b/crates/nargo_cli/src/cli/compile_cmd.rs index dca8c3a89d8..6313f87de7e 100644 --- a/crates/nargo_cli/src/cli/compile_cmd.rs +++ b/crates/nargo_cli/src/cli/compile_cmd.rs @@ -120,10 +120,9 @@ pub(super) fn optimize_circuit( backend: &Backend, circuit: Circuit, ) -> Result<(Circuit, AcirTransformationMap), CliError> { - let result = acvm::compiler::compile(circuit, backend.np_language(), |opcode| { - backend.supports_opcode(opcode) - }) - .map_err(|_| NargoError::CompilationError)?; + let (np_language, is_opcode_supported) = backend.get_backend_info()?; + let result = acvm::compiler::compile(circuit, np_language, is_opcode_supported) + .map_err(|_| NargoError::CompilationError)?; Ok(result) } diff --git a/crates/nargo_cli/src/cli/info_cmd.rs b/crates/nargo_cli/src/cli/info_cmd.rs index efd97a1ab02..26a2efc8ca2 100644 --- a/crates/nargo_cli/src/cli/info_cmd.rs +++ b/crates/nargo_cli/src/cli/info_cmd.rs @@ -158,10 +158,11 @@ fn count_opcodes_and_gates_in_program( compile_options: &CompileOptions, ) -> Result { let (_, compiled_program) = compile_package(backend, package, compile_options)?; + let (language, _) = backend.get_backend_info()?; Ok(ProgramInfo { name: package.name.to_string(), - language: backend.np_language(), + language, acir_opcodes: compiled_program.circuit.opcodes.len(), circuit_size: backend.get_exact_circuit_size(&compiled_program.circuit)?, }) @@ -173,6 +174,7 @@ fn count_opcodes_and_gates_in_contracts( compile_options: &CompileOptions, ) -> Result, CliError> { let (_, contracts) = compile_contracts(backend, package, compile_options)?; + let (language, _) = backend.get_backend_info()?; try_vecmap(contracts, |contract| { let functions = try_vecmap(contract.functions, |function| -> Result<_, BackendError> { @@ -183,6 +185,6 @@ fn count_opcodes_and_gates_in_contracts( }) })?; - Ok(ContractInfo { name: contract.name, language: backend.np_language(), functions }) + Ok(ContractInfo { name: contract.name, language, functions }) }) }