diff --git a/Cargo.lock b/Cargo.lock index 76a9d98e23c..35a5d364d8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "acir" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b9e150d258755881f17bcad6ef79c86374d4c1f1aecc518e9f99edc3d4306e" +checksum = "73be19aa7187beec9846bfbf1a14ffe6e2734d93beec2cdee484ee9a73f0c3b2" dependencies = [ "acir_field", "bincode", @@ -18,9 +18,9 @@ dependencies = [ [[package]] name = "acir_field" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "425f312fa4a91b4f3dc663fe75b7b29cf282a26dfe90fabd132fa5730ca96c9f" +checksum = "2789ab2ac86557428d542e6777a9b8f6f4538f15c1a771a9949d3d848e544286" dependencies = [ "ark-bn254", "ark-ff", @@ -32,9 +32,9 @@ dependencies = [ [[package]] name = "acvm" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4cfe40d3dbe00978a4d749887e79a54e967ad684d40a2986dd69d07408eebe" +checksum = "7c8a26353bf5b2f71bad4fb66a3577bba079e4fa532526991fb97495f34a5746" dependencies = [ "acir", "acvm_blackbox_solver", @@ -65,9 +65,9 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b802948382c6c44cbf44b76853842460879a1f4a7484bd5bb1e24b17101865d9" +checksum = "26e6fa3c9a3ce14707362eec74f7293ee91f144669a617624ae503cd2260d782" dependencies = [ "acir", "blake2", @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "acvm_stdlib" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75f5f501fa7d57724a4eb5f31150e8cba3d5e6547ecf0f28fa719a00d0ecbab" +checksum = "a97848b040795cccc1dec010256d34fc4e767bbc26af4080d784b7b3392c522f" dependencies = [ "acir", ] @@ -494,9 +494,9 @@ dependencies = [ [[package]] name = "brillig" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b230e0ea0fc806ea633106519eebdb3a0e03acce2627dfd0adc97bf55ac584c" +checksum = "551dbdf6d26e0f0ebdd89cd4f2134360ef79f83e05f12f19766fab7dcbf62ea6" dependencies = [ "acir_field", "serde", @@ -504,9 +504,9 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f47a4fd7a4c507222f17185718bd62e704dcd0f1f5e1555c60923f2324d232" +checksum = "5de41cbba0e6b35fa4963470f117a809c32d2818d92d67810b1bd5cc31fdfa6f" dependencies = [ "acir", "acvm_blackbox_solver", diff --git a/Cargo.toml b/Cargo.toml index 63fe20ad72d..53d85122c86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ edition = "2021" rust-version = "1.66" [workspace.dependencies] -acvm = "0.24.1" +acvm = "0.25.0" arena = { path = "crates/arena" } fm = { path = "crates/fm" } iter-extended = { path = "crates/iter-extended" } diff --git a/crates/nargo/src/errors.rs b/crates/nargo/src/errors.rs index 8ffae3bf643..ed7d4f4707a 100644 --- a/crates/nargo/src/errors.rs +++ b/crates/nargo/src/errors.rs @@ -20,7 +20,7 @@ pub enum NargoError { #[derive(Debug, Error)] pub enum ExecutionError { #[error("Failed assertion: '{}'", .0)] - AssertionFailed(String, OpcodeLocation), + AssertionFailed(String, Vec), #[error(transparent)] SolvingError(#[from] OpcodeResolutionError), diff --git a/crates/nargo/src/ops/execute.rs b/crates/nargo/src/ops/execute.rs index 74b744efb4b..33f41ebe819 100644 --- a/crates/nargo/src/ops/execute.rs +++ b/crates/nargo/src/ops/execute.rs @@ -33,17 +33,28 @@ pub fn execute_circuit( unreachable!("Execution should not stop while in `InProgress` state.") } ACVMStatus::Failure(error) => { - return Err(NargoError::ExecutionError(match error { + let call_stack = match &error { OpcodeResolutionError::UnsatisfiedConstrain { opcode_location: ErrorLocation::Resolved(opcode_location), - } => match get_assert_message(&opcode_location) { - Some(assert_message) => { - ExecutionError::AssertionFailed(assert_message, 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) => { + if let Some(assert_message) = get_assert_message( + call_stack.last().expect("Call stacks should not be empty"), + ) { + ExecutionError::AssertionFailed(assert_message, call_stack) + } else { + ExecutionError::SolvingError(error) } - None => ExecutionError::SolvingError(error), - }, - _ => ExecutionError::SolvingError(error), - })) + } + None => ExecutionError::SolvingError(error), + })); } ACVMStatus::RequiresForeignCall(foreign_call) => { let foreign_call_result = ForeignCall::execute(&foreign_call, show_output)?; diff --git a/crates/nargo_cli/src/cli/execute_cmd.rs b/crates/nargo_cli/src/cli/execute_cmd.rs index 1035840c65d..90edb2dd298 100644 --- a/crates/nargo_cli/src/cli/execute_cmd.rs +++ b/crates/nargo_cli/src/cli/execute_cmd.rs @@ -95,14 +95,20 @@ fn execute_package( /// If the location has been resolved we return the contained [OpcodeLocation]. fn extract_opcode_error_from_nargo_error( nargo_err: &NargoError, -) -> Option<(OpcodeLocation, &ExecutionError)> { +) -> Option<(Vec, &ExecutionError)> { let execution_error = match nargo_err { NargoError::ExecutionError(err) => err, _ => return None, }; match execution_error { - ExecutionError::AssertionFailed(_, location) => Some((*location, execution_error)), + ExecutionError::SolvingError(OpcodeResolutionError::BrilligFunctionFailed { + call_stack, + .. + }) + | ExecutionError::AssertionFailed(_, call_stack) => { + Some((call_stack.clone(), execution_error)) + } ExecutionError::SolvingError(OpcodeResolutionError::IndexOutOfBounds { opcode_location: error_location, .. @@ -113,54 +119,61 @@ fn extract_opcode_error_from_nargo_error( ErrorLocation::Unresolved => { unreachable!("Cannot resolve index for unsatisfied constraint") } - ErrorLocation::Resolved(opcode_location) => Some((*opcode_location, execution_error)), + ErrorLocation::Resolved(opcode_location) => { + Some((vec![*opcode_location], execution_error)) + } }, _ => None, } } -/// Resolve an [OpcodeLocation] using debug information generated during compilation -/// to determine an opcode's call stack. Then report the error using the resolved -/// call stack and any other relevant error information returned from the ACVM. -fn report_error_with_opcode_location( - opcode_err_info: Option<(OpcodeLocation, &ExecutionError)>, +/// Resolve the vector of [OpcodeLocation] that caused an execution error using the debug information +/// generated during compilation to determine the complete call stack for an error. Then report the error using +/// the resolved call stack and any other relevant error information returned from the ACVM. +fn report_error_with_opcode_locations( + opcode_err_info: Option<(Vec, &ExecutionError)>, debug: &DebugInfo, context: &Context, ) { - if let Some((opcode_location, opcode_err)) = opcode_err_info { - if let Some(locations) = debug.opcode_location(&opcode_location) { - // The location of the error itself will be the location at the top - // of the call stack (the last item in the Vec). - if let Some(location) = locations.last() { - let message = match opcode_err { - ExecutionError::AssertionFailed(message, _) => { - format!("Assertion failed: '{message}'") - } - ExecutionError::SolvingError(OpcodeResolutionError::IndexOutOfBounds { - index, - array_size, - .. - }) => { - format!( + if let Some((opcode_locations, opcode_err)) = opcode_err_info { + let source_locations: Vec<_> = opcode_locations + .iter() + .flat_map(|opcode_location| { + let locations = debug.opcode_location(opcode_location); + locations.unwrap_or_default() + }) + .collect(); + // The location of the error itself will be the location at the top + // of the call stack (the last item in the Vec). + if let Some(location) = source_locations.last() { + let message = match opcode_err { + ExecutionError::AssertionFailed(message, _) => { + format!("Assertion failed: '{message}'") + } + ExecutionError::SolvingError(OpcodeResolutionError::IndexOutOfBounds { + index, + array_size, + .. + }) => { + format!( "Index out of bounds, array has size {array_size:?}, but index was {index:?}" ) - } - ExecutionError::SolvingError(OpcodeResolutionError::UnsatisfiedConstrain { - .. - }) => "Failed constraint".into(), - _ => { - // All other errors that do not have corresponding opcode locations - // should not be reported in this method. - // If an error with an opcode location is not handled in this match statement - // the basic message attached to the original error from the ACVM should be reported. - return; - } - }; - CustomDiagnostic::simple_error(message, String::new(), location.span) - .in_file(location.file) - .with_call_stack(locations) - .report(&context.file_manager, false); - } + } + ExecutionError::SolvingError(OpcodeResolutionError::UnsatisfiedConstrain { + .. + }) => "Failed constraint".into(), + _ => { + // All other errors that do not have corresponding opcode locations + // should not be reported in this method. + // If an error with an opcode location is not handled in this match statement + // the basic message attached to the original error from the ACVM should be reported. + return; + } + }; + CustomDiagnostic::simple_error(message, String::new(), location.span) + .in_file(location.file) + .with_call_stack(source_locations) + .report(&context.file_manager, false); } } } @@ -184,7 +197,7 @@ pub(crate) fn execute_program( Err(err) => { if let Some((debug, context)) = debug_data { let opcode_err_info = extract_opcode_error_from_nargo_error(&err); - report_error_with_opcode_location(opcode_err_info, &debug, &context); + report_error_with_opcode_locations(opcode_err_info, &debug, &context); } Err(crate::errors::CliError::NargoError(err))