diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e19b68ef3..4e83f70278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Updated Winterfell dependency to v0.7 (#1121). - Added methods `StackOutputs::get_stack_item()` and `StackOutputs::get_stack_word()` (#1155). - Added [Tracing](https://crates.io/crates/tracing) logger to the VM (#1139). +- Added `on_assert_failed()` method to the Host trait (#1197). #### CLI - Introduced the `!use` command for the Miden REPL (#1162). diff --git a/air/src/constraints/stack/op_flags/mod.rs b/air/src/constraints/stack/op_flags/mod.rs index fc9882c2ce..741d83a871 100644 --- a/air/src/constraints/stack/op_flags/mod.rs +++ b/air/src/constraints/stack/op_flags/mod.rs @@ -624,7 +624,7 @@ impl OpFlags { /// Operation Flag of ASSERT operation. #[inline(always)] pub fn assert(&self) -> E { - self.degree7_op_flags[get_op_index(Operation::Assert(ZERO).op_code())] + self.degree7_op_flags[get_op_index(Operation::Assert(0).op_code())] } /// Operation Flag of EQ operation. diff --git a/air/src/constraints/stack/system_ops/tests.rs b/air/src/constraints/stack/system_ops/tests.rs index 166a41b48a..cd4790999f 100644 --- a/air/src/constraints/stack/system_ops/tests.rs +++ b/air/src/constraints/stack/system_ops/tests.rs @@ -102,7 +102,7 @@ pub fn get_fmpupdate_test_frame(a: u64) -> EvaluationFrame { /// returns an EvaluationFrame for testing. pub fn get_assert_test_frame() -> EvaluationFrame { // frame initialized with a fmpupdate operation using it's unique opcode. - let mut frame = generate_evaluation_frame(Operation::Assert(ZERO).op_code() as usize); + let mut frame = generate_evaluation_frame(Operation::Assert(0).op_code() as usize); // Set the output. The top element in the current frame of the stack should be ONE. frame.current_mut()[STACK_TRACE_OFFSET] = ONE; diff --git a/assembly/src/assembler/instruction/ext2_ops.rs b/assembly/src/assembler/instruction/ext2_ops.rs index a5642035d7..20286440f9 100644 --- a/assembly/src/assembler/instruction/ext2_ops.rs +++ b/assembly/src/assembler/instruction/ext2_ops.rs @@ -1,4 +1,4 @@ -use super::{AssemblyError, CodeBlock, Operation::*, SpanBuilder, ZERO}; +use super::{AssemblyError, CodeBlock, Operation::*, SpanBuilder}; use vm_core::AdviceInjector::Ext2Inv; /// Given a stack in the following initial configuration [b1, b0, a1, a0, ...] where a = (a0, a1) @@ -60,9 +60,9 @@ pub fn ext2_div(span: &mut SpanBuilder) -> Result, AssemblyErr Ext2Mul, // [b1', b0', 0, 1, a1, a0, ...] MovUp2, // [0, b1', b0', 1, a1, a0, ...] Eqz, // [1, b1', b0', 1, a1, a0, ...] - Assert(ZERO), // [b1', b0', 1, a1, a0, ...] + Assert(0), // [b1', b0', 1, a1, a0, ...] MovUp2, // [1, b1', b0', a1, a0, ...] - Assert(ZERO), // [b1', b0', a1, a0, ...] + Assert(0), // [b1', b0', a1, a0, ...] Ext2Mul, // [b1', b0', a1*b1', a0*b0', ...] Drop, // [b0', a1*b1', a0*b0'...] Drop // [a1*b1', a0*b0'...] @@ -120,9 +120,9 @@ pub fn ext2_inv(span: &mut SpanBuilder) -> Result, AssemblyErr Ext2Mul, // [a1', a0', 0, 1, ...] MovUp2, // [0, a1', a0', 1, ...] Eqz, // [1, a1', a0', 1, ...] - Assert(ZERO), // [a1', a0', 1, ...] + Assert(0), // [a1', a0', 1, ...] MovUp2, // [1, a1', a0', ...] - Assert(ZERO), // [a1', a0', ...] + Assert(0), // [a1', a0', ...] ]; span.add_ops(ops) } diff --git a/assembly/src/assembler/instruction/field_ops.rs b/assembly/src/assembler/instruction/field_ops.rs index 0619c1596d..61dcfecb60 100644 --- a/assembly/src/assembler/instruction/field_ops.rs +++ b/assembly/src/assembler/instruction/field_ops.rs @@ -13,7 +13,7 @@ const TWO: Felt = Felt::new(2); /// Asserts that the top two words in the stack are equal. /// /// VM cycles: 11 cycles -pub fn assertw(span: &mut SpanBuilder, err_code: Felt) -> Result, AssemblyError> { +pub fn assertw(span: &mut SpanBuilder, err_code: u32) -> Result, AssemblyError> { span.add_ops([ MovUp4, Eq, @@ -122,7 +122,7 @@ pub fn append_pow2_op(span: &mut SpanBuilder) { // drop the top two elements bit and exp value of the latest bit. span.push_ops([Drop, Drop]); // taking `b` to the top and asserting if it's equal to ZERO after all the right shifts. - span.push_ops([Swap, Eqz, Assert(ZERO)]); + span.push_ops([Swap, Eqz, Assert(0)]); } // EXPONENTIATION OPERATION @@ -151,7 +151,7 @@ pub fn exp(span: &mut SpanBuilder, num_pow_bits: u8) -> Result span.push_ops([Drop, Drop]); // taking `b` to the top and asserting if it's equal to ZERO after all the right shifts. - span.push_ops([Swap, Eqz, Assert(ZERO)]); + span.push_ops([Swap, Eqz, Assert(0)]); Ok(None) } diff --git a/assembly/src/assembler/instruction/mod.rs b/assembly/src/assembler/instruction/mod.rs index 7fa4f2412c..08a6936fb3 100644 --- a/assembly/src/assembler/instruction/mod.rs +++ b/assembly/src/assembler/instruction/mod.rs @@ -37,20 +37,14 @@ impl Assembler { } let result = match instruction { - Instruction::Assert => span.add_op(Assert(ZERO)), - Instruction::AssertWithError(err_code) => span.add_op(Assert(Felt::from(*err_code))), - Instruction::AssertEq => span.add_ops([Eq, Assert(ZERO)]), - Instruction::AssertEqWithError(err_code) => { - span.add_ops([Eq, Assert(Felt::from(*err_code))]) - } - Instruction::AssertEqw => field_ops::assertw(span, ZERO), - Instruction::AssertEqwWithError(err_code) => { - field_ops::assertw(span, Felt::from(*err_code)) - } - Instruction::Assertz => span.add_ops([Eqz, Assert(ZERO)]), - Instruction::AssertzWithError(err_code) => { - span.add_ops([Eqz, Assert(Felt::from(*err_code))]) - } + Instruction::Assert => span.add_op(Assert(0)), + Instruction::AssertWithError(err_code) => span.add_op(Assert(*err_code)), + Instruction::AssertEq => span.add_ops([Eq, Assert(0)]), + Instruction::AssertEqWithError(err_code) => span.add_ops([Eq, Assert(*err_code)]), + Instruction::AssertEqw => field_ops::assertw(span, 0), + Instruction::AssertEqwWithError(err_code) => field_ops::assertw(span, *err_code), + Instruction::Assertz => span.add_ops([Eqz, Assert(0)]), + Instruction::AssertzWithError(err_code) => span.add_ops([Eqz, Assert(*err_code)]), Instruction::Add => span.add_op(Add), Instruction::AddImm(imm) => field_ops::add_imm(span, *imm), diff --git a/core/src/operations/mod.rs b/core/src/operations/mod.rs index fb18a59626..4ccb0f2247 100644 --- a/core/src/operations/mod.rs +++ b/core/src/operations/mod.rs @@ -22,7 +22,7 @@ pub enum Operation { /// /// The internal value specifies an error code associated with the error in case when the /// execution fails. - Assert(Felt), + Assert(u32), /// Pops an element off the stack, adds the current value of the `fmp` register to it, and /// pushes the result back onto the stack. diff --git a/miden/tests/integration/operations/sys_ops.rs b/miden/tests/integration/operations/sys_ops.rs index fe34b513e9..0107fd9422 100644 --- a/miden/tests/integration/operations/sys_ops.rs +++ b/miden/tests/integration/operations/sys_ops.rs @@ -19,7 +19,7 @@ fn assert_with_code() { test.expect_stack(&[]); // triggered assertion captures both the VM cycle and error code - let expected_err = "FailedAssertion(1, 123)"; + let expected_err = "FailedAssertion { clk: 1, err_code: 123, err_msg: None }"; let test = build_op_test!(asm_op, &[0]); test.expect_error(TestError::ExecutionError(&expected_err)); } diff --git a/processor/src/errors.rs b/processor/src/errors.rs index 5c817ba0bd..410a0abea2 100644 --- a/processor/src/errors.rs +++ b/processor/src/errors.rs @@ -28,15 +28,27 @@ pub enum ExecutionError { DivideByZero(u32), EventError(String), Ext2InttError(Ext2InttError), - FailedAssertion(u32, Felt), + FailedAssertion { + clk: u32, + err_code: u32, + err_msg: Option, + }, InvalidFmpValue(Felt, Felt), InvalidFriDomainSegment(u64), InvalidFriLayerFolding(QuadFelt, QuadFelt), - InvalidMemoryRange { start_addr: u64, end_addr: u64 }, + InvalidMemoryRange { + start_addr: u64, + end_addr: u64, + }, InvalidStackDepthOnReturn(usize), InvalidStackWordOffset(usize), - InvalidTreeDepth { depth: Felt }, - InvalidTreeNodeIndex { depth: Felt, value: Felt }, + InvalidTreeDepth { + depth: Felt, + }, + InvalidTreeNodeIndex { + depth: Felt, + value: Felt, + }, MemoryAddressOutOfBounds(u64), MerkleStoreMergeFailed(MerkleError), MerkleStoreLookupFailed(MerkleError), @@ -90,8 +102,19 @@ impl Display for ExecutionError { DivideByZero(clk) => write!(f, "Division by zero at clock cycle {clk}"), EventError(error) => write!(f, "Failed to process event - {error}"), Ext2InttError(err) => write!(f, "Failed to execute Ext2Intt operation: {err}"), - FailedAssertion(clk, err_code) => { - write!(f, "Assertion failed at clock cycle {clk} with error code {err_code}") + FailedAssertion { + clk, + err_code, + err_msg, + } => { + if let Some(err_msg) = err_msg { + write!( + f, + "Assertion failed at clock cycle {clk} with error code {err_code}: {err_msg}" + ) + } else { + write!(f, "Assertion failed at clock cycle {clk} with error code {err_code}") + } } InvalidFmpValue(old, new) => { write!(f, "Updating FMP register from {old} to {new} failed because {new} is outside of {FMP_MIN}..{FMP_MAX}") diff --git a/processor/src/host/mod.rs b/processor/src/host/mod.rs index 7939239d60..82c459a31b 100644 --- a/processor/src/host/mod.rs +++ b/processor/src/host/mod.rs @@ -82,6 +82,15 @@ pub trait Host { Ok(HostResponse::None) } + /// Handles the failure of the assertion instruction. + fn on_assert_failed(&mut self, process: &S, err_code: u32) -> ExecutionError { + ExecutionError::FailedAssertion { + clk: process.clk(), + err_code, + err_msg: None, + } + } + /// Pops an element from the advice stack and returns it. /// /// # Errors @@ -172,6 +181,10 @@ where ) -> Result { H::on_event(self, process, event_id) } + + fn on_assert_failed(&mut self, process: &S, err_code: u32) -> ExecutionError { + H::on_assert_failed(self, process, err_code) + } } // HOST RESPONSE diff --git a/processor/src/operations/sys_ops.rs b/processor/src/operations/sys_ops.rs index ba62fdb4c8..2541580cce 100644 --- a/processor/src/operations/sys_ops.rs +++ b/processor/src/operations/sys_ops.rs @@ -17,9 +17,9 @@ where /// /// # Errors /// Returns an error if the popped value is not ONE. - pub(super) fn op_assert(&mut self, err_code: Felt) -> Result<(), ExecutionError> { + pub(super) fn op_assert(&mut self, err_code: u32) -> Result<(), ExecutionError> { if self.stack.get(0) != ONE { - return Err(ExecutionError::FailedAssertion(self.system.clk(), err_code)); + return Err(self.host.borrow_mut().on_assert_failed(self, err_code)); } self.stack.shift_left(1); Ok(()) @@ -128,7 +128,7 @@ mod tests { process.execute_op(Operation::Swap).unwrap(); process.execute_op(Operation::Drop).unwrap(); - assert!(process.execute_op(Operation::Assert(ZERO)).is_ok()); + assert!(process.execute_op(Operation::Assert(0)).is_ok()); } #[test]