diff --git a/Cargo.toml b/Cargo.toml index f6dbe97..405ceb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,19 +10,18 @@ authors = ["Bartosz Nowak"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace.dependencies] -acir = "0.41.0" -acvm = "0.41.0" -acvm_blackbox_solver = "0.41.0" +acvm = "0.43.0" +acvm_blackbox_solver = "0.43.0" base64 = "0.22.0" bb_rs = {git = "https://github.com/Okm165/aztec-packages.git", branch = "v0.27.0"} bincode = "1.3.3" -bn254_blackbox_solver = "0.39.0" +bn254_blackbox_solver = "0.43.0" flate2 = "1.0.28" hex = "0.4.3" nargo = { path = "crates/nargo" } noir_rs = { path = "crates/noir_rs" } reqwest = { version = "0.12.1", features = ["blocking"] } -serde = "1.0.197" -thiserror = "1.0.58" +serde = "1" +thiserror = "1" tracing = "0.1" tracing-subscriber = "0.3" diff --git a/crates/nargo/Cargo.toml b/crates/nargo/Cargo.toml index d8d8d85..06e5e3a 100644 --- a/crates/nargo/Cargo.toml +++ b/crates/nargo/Cargo.toml @@ -7,13 +7,12 @@ authors.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -acir.workspace = true -acvm.workspace = true acvm_blackbox_solver.workspace = true -bn254_blackbox_solver.workspace = true +acvm.workspace = true base64.workspace = true bb_rs.workspace = true bincode.workspace = true +bn254_blackbox_solver.workspace = true flate2.workspace = true hex.workspace = true reqwest.workspace = true diff --git a/crates/nargo/src/errors.rs b/crates/nargo/src/errors.rs index 58ea849..1a44747 100644 --- a/crates/nargo/src/errors.rs +++ b/crates/nargo/src/errors.rs @@ -30,7 +30,9 @@ impl NargoError { ExecutionError::SolvingError(error) => match error { OpcodeResolutionError::IndexOutOfBounds { .. } | OpcodeResolutionError::OpcodeNotSolvable(_) - | OpcodeResolutionError::UnsatisfiedConstrain { .. } => None, + | OpcodeResolutionError::UnsatisfiedConstrain { .. } + | OpcodeResolutionError::AcirMainCallAttempted { .. } + | OpcodeResolutionError::AcirCallOutputsMismatch { .. } => None, OpcodeResolutionError::BrilligFunctionFailed { message, .. } => Some(message), OpcodeResolutionError::BlackBoxFunctionFailed(_, reason) => Some(reason), }, diff --git a/crates/nargo/src/ops/execute.rs b/crates/nargo/src/ops/execute.rs index a60d81d..0af4b7f 100644 --- a/crates/nargo/src/ops/execute.rs +++ b/crates/nargo/src/ops/execute.rs @@ -1,61 +1,123 @@ -use acir::{circuit::Circuit, native_types::WitnessMap}; -use acvm::{ - pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}, - BlackBoxFunctionSolver, -}; - use crate::errors::{ExecutionError, NargoError}; +use acvm::acir::circuit::Program; +use acvm::acir::native_types::WitnessStack; +use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ACVM}; +use acvm::BlackBoxFunctionSolver; +use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; -pub fn execute_circuit( - circuit: &Circuit, - initial_witness: WitnessMap, - blackbox_solver: &B, -) -> Result { - let mut acvm = ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness); - - // This message should be resolved by a nargo foreign call only when we have an unsatisfied assertion. - let assert_message: Option = None; - loop { - let solver_status = acvm.solve(); - - match solver_status { - ACVMStatus::Solved => break, - ACVMStatus::InProgress => { - unreachable!("Execution should not stop while in `InProgress` state.") - } - ACVMStatus::Failure(error) => { - let call_stack = match &error { - OpcodeResolutionError::UnsatisfiedConstrain { - opcode_location: ErrorLocation::Resolved(opcode_location), - } => Some(vec![*opcode_location]), - OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { - Some(call_stack.clone()) - } - _ => None, - }; - - return Err(NargoError::ExecutionError(match call_stack { - Some(call_stack) => { - // First check whether we have a runtime assertion message that should be resolved on an ACVM failure - // If we do not have a runtime assertion message, we should check whether the circuit has any hardcoded - // messages associated with a specific `OpcodeLocation`. - // Otherwise return the provided opcode resolution error. - if let Some(assert_message) = assert_message { - ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) - } else if let Some(assert_message) = circuit.get_assert_message( - *call_stack.last().expect("Call stacks should not be empty"), - ) { - ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) +struct ProgramExecutor<'a, B: BlackBoxFunctionSolver> { + functions: &'a [Circuit], + // This gets built as we run through the program looking at each function call + witness_stack: WitnessStack, + + blackbox_solver: &'a B, +} + +impl<'a, B: BlackBoxFunctionSolver> ProgramExecutor<'a, B> { + pub fn new(functions: &'a [Circuit], blackbox_solver: &'a B) -> Self { + ProgramExecutor { + functions, + witness_stack: WitnessStack::default(), + blackbox_solver, + } + } + + pub fn finalize(self) -> WitnessStack { + self.witness_stack + } + + pub fn execute_circuit( + &mut self, + circuit: &Circuit, + initial_witness: WitnessMap, + ) -> Result { + let mut acvm = ACVM::new(self.blackbox_solver, &circuit.opcodes, initial_witness); + + // This message should be resolved by a nargo foreign call only when we have an unsatisfied assertion. + let assert_message: Option = None; + loop { + let solver_status = acvm.solve(); + + match solver_status { + ACVMStatus::Solved => break, + ACVMStatus::InProgress => { + unreachable!("Execution should not stop while in `InProgress` state.") + } + ACVMStatus::Failure(error) => { + let call_stack = match &error { + OpcodeResolutionError::UnsatisfiedConstrain { + opcode_location: ErrorLocation::Resolved(opcode_location), + } => Some(vec![*opcode_location]), + OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { + Some(call_stack.clone()) + } + _ => None, + }; + + return Err(NargoError::ExecutionError(match call_stack { + Some(call_stack) => { + // First check whether we have a runtime assertion message that should be resolved on an ACVM failure + // If we do not have a runtime assertion message, we should check whether the circuit has any hardcoded + // messages associated with a specific `OpcodeLocation`. + // Otherwise return the provided opcode resolution error. + if let Some(assert_message) = assert_message { + ExecutionError::AssertionFailed( + assert_message.to_owned(), + call_stack, + ) + } else if let Some(assert_message) = circuit.get_assert_message( + *call_stack.last().expect("Call stacks should not be empty"), + ) { + ExecutionError::AssertionFailed( + assert_message.to_owned(), + call_stack, + ) + } else { + ExecutionError::SolvingError(error) + } + } + None => ExecutionError::SolvingError(error), + })); + } + ACVMStatus::RequiresForeignCall(_foreign_call) => {} + ACVMStatus::RequiresAcirCall(call_info) => { + let acir_to_call = &self.functions[call_info.id as usize]; + let initial_witness = call_info.initial_witness; + let call_solved_witness = + self.execute_circuit(acir_to_call, initial_witness)?; + let mut call_resolved_outputs = Vec::new(); + for return_witness_index in acir_to_call.return_values.indices() { + if let Some(return_value) = + call_solved_witness.get_index(return_witness_index) + { + call_resolved_outputs.push(*return_value); } else { - ExecutionError::SolvingError(error) + return Err(ExecutionError::SolvingError( + OpcodeNotSolvable::MissingAssignment(return_witness_index).into(), + ) + .into()); } } - None => ExecutionError::SolvingError(error), - })); + acvm.resolve_pending_acir_call(call_resolved_outputs); + self.witness_stack.push(call_info.id, call_solved_witness); + } } - ACVMStatus::RequiresForeignCall(_foreign_call) => {} } + + Ok(acvm.finalize()) } +} + +pub fn execute_program( + program: &Program, + initial_witness: WitnessMap, + blackbox_solver: &B, +) -> Result { + let main = &program.functions[0]; + + let mut executor = ProgramExecutor::new(&program.functions, blackbox_solver); + let main_witness = executor.execute_circuit(main, initial_witness)?; + executor.witness_stack.push(0, main_witness); - Ok(acvm.finalize()) + Ok(executor.finalize()) } diff --git a/crates/noir_rs/Cargo.toml b/crates/noir_rs/Cargo.toml index 87bb238..6e7f9e8 100644 --- a/crates/noir_rs/Cargo.toml +++ b/crates/noir_rs/Cargo.toml @@ -7,7 +7,6 @@ authors.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -acir.workspace = true acvm_blackbox_solver.workspace = true acvm.workspace = true base64.workspace = true diff --git a/crates/noir_rs/src/main.rs b/crates/noir_rs/src/main.rs index cd4c5ac..390c03b 100644 --- a/crates/noir_rs/src/main.rs +++ b/crates/noir_rs/src/main.rs @@ -1,5 +1,5 @@ -use acir::{ - native_types::{Witness, WitnessMap}, +use acvm::{ + acir::native_types::{Witness, WitnessMap}, FieldElement, }; use prove::prove; @@ -10,16 +10,17 @@ pub mod prove; pub mod srs; pub mod verify; -const BYTECODE: &str = "H4sIAAAAAAAA/7WUPQ7DIAyFTZNWHXsUm59gtlylqOT+J6iqqqmCiDfMW2CwzGc/mxkArnDWtJ/rfjpcvC/RFnL0RJsyB/QhL0xMgcPLsnOFPceUU8RE3hXaQnIb/lTnwj6RUeS66HHht2dG6KVpeol9Ik1m03j+n4WbwF/Htfd7FfdWrLV9t2V5CJwnD1ZFmBFmTgPyzqC7vCPqnvU9QhAGYkRPsVMGjuUxArP0kcAH+JIvC64FAAA="; +const BYTECODE: &str = "H4sIAAAAAAAA/7WUUQ6DIAyGqTK3192kFdDy5lVmhvc/wTKNmBEke7H8CaEJSfn4Wwpql17XTZ3Vxn2Ku8HB2jD2gQy9sPczO7RuHpiYHLt3z8YEtjz62Y/oyZpAi/NmwV1pLrwmAkGuRo4LN8+g4CVkXuI1kSQzZDW/x7gr8B96rKuJ8UeQ5WDYlPbkcZdOzruEBeRZSGW+5B48C/6caj8JwtRoorZCXq1kh0aNd2v5GqEqNEQNT/GiQP0+ERSY/w0w9QU2ntcLNgYAAA=="; fn main() { tracing_subscriber::fmt::init(); let mut initial_witness = WitnessMap::new(); - initial_witness.insert(Witness(1), FieldElement::zero()); - initial_witness.insert(Witness(2), FieldElement::one()); + initial_witness.insert(Witness(0), FieldElement::zero()); + initial_witness.insert(Witness(1), FieldElement::one()); let (proof, vk) = prove(String::from(BYTECODE), initial_witness).unwrap(); - let verdict = verify(String::from(BYTECODE), proof, vk).unwrap(); - info!("proof verification verdict: {}", verdict); + // let verdict = verify(String::from(BYTECODE), proof, vk).unwrap(); + // info!("proof verification verdict: {}", verdict); + todo!() } diff --git a/crates/noir_rs/src/prove.rs b/crates/noir_rs/src/prove.rs index 088870c..62f8592 100644 --- a/crates/noir_rs/src/prove.rs +++ b/crates/noir_rs/src/prove.rs @@ -1,6 +1,8 @@ -use std::io::Read; - -use acir::{circuit::Circuit, native_types::WitnessMap}; +use crate::srs::netsrs::NetSrs; +use acvm::acir::{ + circuit::Program, + native_types::{WitnessMap, WitnessStack}, +}; use base64::{engine::general_purpose, Engine}; use bb_rs::barretenberg_api::{ acir::{ @@ -11,52 +13,48 @@ use bb_rs::barretenberg_api::{ }; use bn254_blackbox_solver::Bn254BlackBoxSolver; use flate2::bufread::GzDecoder; -use nargo::ops::execute::execute_circuit; - -use crate::srs::netsrs::NetSrs; +use nargo::ops::execute::execute_program; +use std::io::Read; pub fn prove( circuit_bytecode: String, initial_witness: WitnessMap, ) -> Result<(Vec, Vec), String> { - let acir_buffer = general_purpose::STANDARD + let bytecode = general_purpose::STANDARD .decode(circuit_bytecode) .map_err(|e| e.to_string())?; - let circuit = Circuit::deserialize_circuit(&acir_buffer).map_err(|e| e.to_string())?; - - let mut decoder = GzDecoder::new(acir_buffer.as_slice()); - let mut acir_buffer_uncompressed = Vec::::new(); - decoder - .read_to_end(&mut acir_buffer_uncompressed) - .map_err(|e| e.to_string())?; - let blackbox_solver = Bn254BlackBoxSolver::new(); - - let solved_witness = - execute_circuit(&circuit, initial_witness, &blackbox_solver).map_err(|e| e.to_string())?; - let serialized_solved_witness = - bincode::serialize(&solved_witness).map_err(|e| e.to_string())?; - - let circuit_size = unsafe { get_circuit_sizes(&acir_buffer_uncompressed) }; - let log_value = (circuit_size.total as f64).log2().ceil() as u32; - let subgroup_size = 2u32.pow(log_value); - - let srs = NetSrs::new(subgroup_size + 1); - - Ok(unsafe { - init_srs(&srs.g1_data, srs.num_points, &srs.g2_data); - let mut acir_ptr = new_acir_composer(subgroup_size); - acir_init_proving_key(&mut acir_ptr, &acir_buffer_uncompressed); - let result = ( - acir_create_proof( - &mut acir_ptr, - &acir_buffer_uncompressed, - &serialized_solved_witness, - ), - acir_get_verification_key(&mut acir_ptr), - ); - delete_acir_composer(acir_ptr); - result - }) + let program: Program = Program::deserialize_program(&bytecode).unwrap(); + + let witness_stack = execute_program(&program, initial_witness, &blackbox_solver).unwrap(); + let _serialized_witnesses: Vec = witness_stack + .try_into() + .expect("could not serialize witness map"); + + let circuit = bincode::serialize(program.functions.first().unwrap()).unwrap(); + + let circuit_size = unsafe { get_circuit_sizes(&circuit) }; + + // let log_value = (circuit_size.total as f64).log2().ceil() as u32; + // let subgroup_size = 2u32.pow(log_value); + + // let srs = NetSrs::new(subgroup_size + 1); + + // Ok(unsafe { + // init_srs(&srs.g1_data, srs.num_points, &srs.g2_data); + // let mut acir_ptr = new_acir_composer(subgroup_size); + // acir_init_proving_key(&mut acir_ptr, &acir_buffer_uncompressed); + // let result = ( + // acir_create_proof( + // &mut acir_ptr, + // &acir_buffer_uncompressed, + // &serialized_solved_witness, + // ), + // acir_get_verification_key(&mut acir_ptr), + // ); + // delete_acir_composer(acir_ptr); + // result + // }) + todo!() }