From b85121356bebda7f18424d2cfed98da7e6f8058a Mon Sep 17 00:00:00 2001 From: Philippe Laferriere Date: Tue, 5 Nov 2024 14:30:18 -0500 Subject: [PATCH] fix: return error when 2 memory accesses at the same address in same clock cycle --- CHANGELOG.md | 1 + .../integration/operations/io_ops/mem_ops.rs | 24 ++++++ processor/src/chiplets/memory/mod.rs | 27 +++++-- processor/src/chiplets/memory/segment.rs | 73 ++++++++++++++----- processor/src/chiplets/memory/tests.rs | 50 ++++++------- processor/src/chiplets/mod.rs | 41 ++++++++--- processor/src/errors.rs | 10 +++ processor/src/operations/comb_ops.rs | 44 ++++++----- processor/src/operations/io_ops.rs | 10 +-- 9 files changed, 196 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d37a550c6..1509977ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ - Fixed the construction of the chiplets virtual table (#1514) (#1556) - Fixed the construction of the chiplets bus (#1516) (#1525) - Decorators are now allowed in empty basic blocks (#1466) +- Return an error if an instruction performs 2 memory accesses at the same memory address in the same cycle (#1561) ## 0.10.6 (2024-09-12) - `miden-processor` crate only diff --git a/miden/tests/integration/operations/io_ops/mem_ops.rs b/miden/tests/integration/operations/io_ops/mem_ops.rs index bb1f1469f..81542a86d 100644 --- a/miden/tests/integration/operations/io_ops/mem_ops.rs +++ b/miden/tests/integration/operations/io_ops/mem_ops.rs @@ -1,3 +1,6 @@ +use prover::ExecutionError; +use test_utils::expect_exec_error; + use super::{apply_permutation, build_op_test, build_test, Felt, ToElements, TRUNCATE_STACK_PROC}; // LOADING SINGLE ELEMENT ONTO THE STACK (MLOAD) @@ -228,3 +231,24 @@ fn read_after_write() { let test = build_op_test!("mem_storew.0 dropw mem_loadw.0", &[1, 2, 3, 4, 5, 6, 7, 8]); test.expect_stack(&[8, 7, 6, 5]); } + +// MISC +// ================================================================================================ + +/// Ensures that the processor returns an error when 2 memory operations occur in the same context, +/// at the same address, and in the same clock cycle (which is what RCOMBBASE does when `stack[13] = +/// stack[14] = 0`). +#[test] +fn mem_reads_same_clock_cycle() { + let asm_op = "begin rcomb_base end"; + + let test = build_test!(asm_op); + expect_exec_error!( + test, + ExecutionError::DuplicateMemoryAccess { + ctx: 0_u32.into(), + addr: 0, + clk: 1_u32.into() + } + ); +} diff --git a/processor/src/chiplets/memory/mod.rs b/processor/src/chiplets/memory/mod.rs index 78d20ffaf..76690130c 100644 --- a/processor/src/chiplets/memory/mod.rs +++ b/processor/src/chiplets/memory/mod.rs @@ -11,7 +11,7 @@ use super::{ utils::{split_element_u32_into_u16, split_u32_into_u16}, Felt, FieldElement, RangeChecker, TraceFragment, Word, EMPTY_WORD, ONE, }; -use crate::system::ContextId; +use crate::{system::ContextId, ExecutionError}; mod segment; use segment::MemorySegmentTrace; @@ -137,15 +137,32 @@ impl Memory { /// /// If the specified address hasn't been previously written to, four ZERO elements are /// returned. This effectively implies that memory is initialized to ZERO. - pub fn read(&mut self, ctx: ContextId, addr: u32, clk: RowIndex) -> Word { + /// + /// # Errors + /// - Returns an error if the same address is accessed more than once in the same clock cycle. + pub fn read( + &mut self, + ctx: ContextId, + addr: u32, + clk: RowIndex, + ) -> Result { self.num_trace_rows += 1; - self.trace.entry(ctx).or_default().read(addr, Felt::from(clk)) + self.trace.entry(ctx).or_default().read(ctx, addr, Felt::from(clk)) } /// Writes the provided word at the specified context/address. - pub fn write(&mut self, ctx: ContextId, addr: u32, clk: RowIndex, value: Word) { + /// + /// # Errors + /// - Returns an error if the same address is accessed more than once in the same clock cycle. + pub fn write( + &mut self, + ctx: ContextId, + addr: u32, + clk: RowIndex, + value: Word, + ) -> Result<(), ExecutionError> { self.num_trace_rows += 1; - self.trace.entry(ctx).or_default().write(addr, Felt::from(clk), value); + self.trace.entry(ctx).or_default().write(ctx, addr, Felt::from(clk), value) } // EXECUTION TRACE GENERATION diff --git a/processor/src/chiplets/memory/segment.rs b/processor/src/chiplets/memory/segment.rs index 60fc66747..4bee448fc 100644 --- a/processor/src/chiplets/memory/segment.rs +++ b/processor/src/chiplets/memory/segment.rs @@ -1,4 +1,7 @@ -use alloc::{collections::BTreeMap, vec::Vec}; +use alloc::{ + collections::{btree_map::Entry, BTreeMap}, + vec::Vec, +}; use miden_air::{ trace::chiplets::memory::{Selectors, MEMORY_COPY_READ, MEMORY_INIT_READ, MEMORY_WRITE}, @@ -6,6 +9,7 @@ use miden_air::{ }; use super::{Felt, Word, INIT_MEM_VALUE}; +use crate::{ContextId, ExecutionError}; // MEMORY SEGMENT TRACE // ================================================================================================ @@ -72,37 +76,66 @@ impl MemorySegmentTrace { /// /// If the specified address hasn't been previously written to, four ZERO elements are /// returned. This effectively implies that memory is initialized to ZERO. - pub fn read(&mut self, addr: u32, clk: Felt) -> Word { + /// + /// # Errors + /// - Returns an error if the same address is accessed more than once in the same clock cycle. + pub fn read(&mut self, ctx: ContextId, addr: u32, clk: Felt) -> Result { // look up the previous value in the appropriate address trace and add (clk, prev_value) // to it; if this is the first time we access this address, create address trace for it // with entry (clk, [ZERO, 4]). in both cases, return the last value in the address trace. - self.0 - .entry(addr) - .and_modify(|addr_trace| { - let last_value = addr_trace.last().expect("empty address trace").value(); - let access = MemorySegmentAccess::new(clk, MemoryOperation::CopyRead, last_value); - addr_trace.push(access); - }) - .or_insert_with(|| { + match self.0.entry(addr) { + Entry::Vacant(vacant_entry) => { let access = MemorySegmentAccess::new(clk, MemoryOperation::InitRead, INIT_MEM_VALUE); - vec![access] - }) - .last() - .expect("empty address trace") - .value() + vacant_entry.insert(vec![access]); + Ok(INIT_MEM_VALUE) + }, + Entry::Occupied(mut occupied_entry) => { + let addr_trace = occupied_entry.get_mut(); + if addr_trace.last().expect("empty address trace").clk() == clk { + Err(ExecutionError::DuplicateMemoryAccess { ctx, addr, clk }) + } else { + let last_value = addr_trace.last().expect("empty address trace").value(); + let access = + MemorySegmentAccess::new(clk, MemoryOperation::CopyRead, last_value); + addr_trace.push(access); + + Ok(last_value) + } + }, + } } /// Writes the provided word at the specified address. The memory access is assumed to happen /// at the provided clock cycle. - pub fn write(&mut self, addr: u32, clk: Felt, value: Word) { + /// + /// # Errors + /// - Returns an error if the same address is accessed more than once in the same clock cycle. + pub fn write( + &mut self, + ctx: ContextId, + addr: u32, + clk: Felt, + value: Word, + ) -> Result<(), ExecutionError> { // add a memory access to the appropriate address trace; if this is the first time // we access this address, initialize address trace. let access = MemorySegmentAccess::new(clk, MemoryOperation::Write, value); - self.0 - .entry(addr) - .and_modify(|addr_trace| addr_trace.push(access)) - .or_insert_with(|| vec![access]); + match self.0.entry(addr) { + Entry::Vacant(vacant_entry) => { + vacant_entry.insert(vec![access]); + Ok(()) + }, + Entry::Occupied(mut occupied_entry) => { + let addr_trace = occupied_entry.get_mut(); + if addr_trace.last().expect("empty address trace").clk() == clk { + Err(ExecutionError::DuplicateMemoryAccess { ctx, addr, clk }) + } else { + addr_trace.push(access); + Ok(()) + } + }, + } } // INNER VALUE ACCESSORS diff --git a/processor/src/chiplets/memory/tests.rs b/processor/src/chiplets/memory/tests.rs index 0bcdc40a1..5c169507f 100644 --- a/processor/src/chiplets/memory/tests.rs +++ b/processor/src/chiplets/memory/tests.rs @@ -28,27 +28,27 @@ fn mem_read() { // read a value from address 0; clk = 1 let addr0 = 0; - let value = mem.read(ContextId::root(), addr0, 1.into()); + let value = mem.read(ContextId::root(), addr0, 1.into()).unwrap(); assert_eq!(EMPTY_WORD, value); assert_eq!(1, mem.size()); assert_eq!(1, mem.trace_len()); // read a value from address 3; clk = 2 let addr3 = 3; - let value = mem.read(ContextId::root(), addr3, 2.into()); + let value = mem.read(ContextId::root(), addr3, 2.into()).unwrap(); assert_eq!(EMPTY_WORD, value); assert_eq!(2, mem.size()); assert_eq!(2, mem.trace_len()); // read a value from address 0 again; clk = 3 - let value = mem.read(ContextId::root(), addr0, 3.into()); + let value = mem.read(ContextId::root(), addr0, 3.into()).unwrap(); assert_eq!(EMPTY_WORD, value); assert_eq!(2, mem.size()); assert_eq!(3, mem.trace_len()); // read a value from address 2; clk = 4 let addr2 = 2; - let value = mem.read(ContextId::root(), addr2, 4.into()); + let value = mem.read(ContextId::root(), addr2, 4.into()).unwrap(); assert_eq!(EMPTY_WORD, value); assert_eq!(3, mem.size()); assert_eq!(4, mem.trace_len()); @@ -81,7 +81,7 @@ fn mem_write() { // write a value into address 0; clk = 1 let addr0 = 0; let value1 = [ONE, ZERO, ZERO, ZERO]; - mem.write(ContextId::root(), addr0, 1.into(), value1); + mem.write(ContextId::root(), addr0, 1.into(), value1).unwrap(); assert_eq!(value1, mem.get_value(ContextId::root(), addr0).unwrap()); assert_eq!(1, mem.size()); assert_eq!(1, mem.trace_len()); @@ -89,7 +89,7 @@ fn mem_write() { // write a value into address 2; clk = 2 let addr2 = 2; let value5 = [Felt::new(5), ZERO, ZERO, ZERO]; - mem.write(ContextId::root(), addr2, 2.into(), value5); + mem.write(ContextId::root(), addr2, 2.into(), value5).unwrap(); assert_eq!(value5, mem.get_value(ContextId::root(), addr2).unwrap()); assert_eq!(2, mem.size()); assert_eq!(2, mem.trace_len()); @@ -97,14 +97,14 @@ fn mem_write() { // write a value into address 1; clk = 3 let addr1 = 1; let value7 = [Felt::new(7), ZERO, ZERO, ZERO]; - mem.write(ContextId::root(), addr1, 3.into(), value7); + mem.write(ContextId::root(), addr1, 3.into(), value7).unwrap(); assert_eq!(value7, mem.get_value(ContextId::root(), addr1).unwrap()); assert_eq!(3, mem.size()); assert_eq!(3, mem.trace_len()); // write a value into address 0; clk = 4 let value9 = [Felt::new(9), ZERO, ZERO, ZERO]; - mem.write(ContextId::root(), addr0, 4.into(), value9); + mem.write(ContextId::root(), addr0, 4.into(), value9).unwrap(); assert_eq!(value7, mem.get_value(ContextId::root(), addr1).unwrap()); assert_eq!(3, mem.size()); assert_eq!(4, mem.trace_len()); @@ -137,35 +137,35 @@ fn mem_write_read() { // write 1 into address 5; clk = 1 let addr5 = 5; let value1 = [ONE, ZERO, ZERO, ZERO]; - mem.write(ContextId::root(), addr5, 1.into(), value1); + mem.write(ContextId::root(), addr5, 1.into(), value1).unwrap(); // write 4 into address 2; clk = 2 let addr2 = 2; let value4 = [Felt::new(4), ZERO, ZERO, ZERO]; - mem.write(ContextId::root(), addr2, 2.into(), value4); + mem.write(ContextId::root(), addr2, 2.into(), value4).unwrap(); // read a value from address 5; clk = 3 - mem.read(ContextId::root(), addr5, 3.into()); + mem.read(ContextId::root(), addr5, 3.into()).unwrap(); // write 2 into address 5; clk = 4 let value2 = [Felt::new(2), ZERO, ZERO, ZERO]; - mem.write(ContextId::root(), addr5, 4.into(), value2); + mem.write(ContextId::root(), addr5, 4.into(), value2).unwrap(); // read a value from address 2; clk = 5 - mem.read(ContextId::root(), addr2, 5.into()); + mem.read(ContextId::root(), addr2, 5.into()).unwrap(); // write 7 into address 2; clk = 6 let value7 = [Felt::new(7), ZERO, ZERO, ZERO]; - mem.write(ContextId::root(), addr2, 6.into(), value7); + mem.write(ContextId::root(), addr2, 6.into(), value7).unwrap(); // read a value from address 5; clk = 7 - mem.read(ContextId::root(), addr5, 7.into()); + mem.read(ContextId::root(), addr5, 7.into()).unwrap(); // read a value from address 2; clk = 8 - mem.read(ContextId::root(), addr2, 8.into()); + mem.read(ContextId::root(), addr2, 8.into()).unwrap(); // read a value from address 5; clk = 9 - mem.read(ContextId::root(), addr5, 9.into()); + mem.read(ContextId::root(), addr5, 9.into()).unwrap(); // check generated trace and memory data provided to the ChipletsBus; rows should be sorted by // address and then clock cycle @@ -208,33 +208,33 @@ fn mem_multi_context() { // write a value into ctx = ContextId::root(), addr = 0; clk = 1 let value1 = [ONE, ZERO, ZERO, ZERO]; - mem.write(ContextId::root(), 0, 1.into(), value1); + mem.write(ContextId::root(), 0, 1.into(), value1).unwrap(); assert_eq!(value1, mem.get_value(ContextId::root(), 0).unwrap()); assert_eq!(1, mem.size()); assert_eq!(1, mem.trace_len()); // write a value into ctx = 3, addr = 1; clk = 4 let value2 = [ZERO, ONE, ZERO, ZERO]; - mem.write(3.into(), 1, 4.into(), value2); + mem.write(3.into(), 1, 4.into(), value2).unwrap(); assert_eq!(value2, mem.get_value(3.into(), 1).unwrap()); assert_eq!(2, mem.size()); assert_eq!(2, mem.trace_len()); // read a value from ctx = 3, addr = 1; clk = 6 - let value = mem.read(3.into(), 1, 6.into()); + let value = mem.read(3.into(), 1, 6.into()).unwrap(); assert_eq!(value2, value); assert_eq!(2, mem.size()); assert_eq!(3, mem.trace_len()); // write a value into ctx = 3, addr = 0; clk = 7 let value3 = [ZERO, ZERO, ONE, ZERO]; - mem.write(3.into(), 0, 7.into(), value3); + mem.write(3.into(), 0, 7.into(), value3).unwrap(); assert_eq!(value3, mem.get_value(3.into(), 0).unwrap()); assert_eq!(3, mem.size()); assert_eq!(4, mem.trace_len()); // read a value from ctx = 0, addr = 0; clk = 9 - let value = mem.read(ContextId::root(), 0, 9.into()); + let value = mem.read(ContextId::root(), 0, 9.into()).unwrap(); assert_eq!(value1, value); assert_eq!(3, mem.size()); assert_eq!(5, mem.trace_len()); @@ -270,17 +270,17 @@ fn mem_get_state_at() { // Write 1 into (ctx = 0, addr = 5) at clk = 1. // This means that mem[5] = 1 at the beginning of clk = 2 let value1 = [ONE, ZERO, ZERO, ZERO]; - mem.write(ContextId::root(), 5, 1.into(), value1); + mem.write(ContextId::root(), 5, 1.into(), value1).unwrap(); // Write 4 into (ctx = 0, addr = 2) at clk = 2. // This means that mem[2] = 4 at the beginning of clk = 3 let value4 = [Felt::new(4), ZERO, ZERO, ZERO]; - mem.write(ContextId::root(), 2, 2.into(), value4); + mem.write(ContextId::root(), 2, 2.into(), value4).unwrap(); // write 7 into (ctx = 3, addr = 3) at clk = 4 // This means that mem[3] = 7 at the beginning of clk = 4 let value7 = [Felt::new(7), ZERO, ZERO, ZERO]; - mem.write(3.into(), 3, 4.into(), value7); + mem.write(3.into(), 3, 4.into(), value7).unwrap(); // Check memory state at clk = 2 assert_eq!(mem.get_state_at(ContextId::root(), 2.into()), vec![(5, value1)]); diff --git a/processor/src/chiplets/mod.rs b/processor/src/chiplets/mod.rs index b4a480724..a9fe8b777 100644 --- a/processor/src/chiplets/mod.rs +++ b/processor/src/chiplets/mod.rs @@ -290,7 +290,7 @@ impl Chiplets { /// /// If the specified address hasn't been previously written to, four ZERO elements are /// returned. This effectively implies that memory is initialized to ZERO. - pub fn read_mem(&mut self, ctx: ContextId, addr: u32) -> Word { + pub fn read_mem(&mut self, ctx: ContextId, addr: u32) -> Result { // read the word from memory self.memory.read(ctx, addr, self.clk) } @@ -300,35 +300,54 @@ impl Chiplets { /// /// If either of the accessed addresses hasn't been previously written to, ZERO elements are /// returned. This effectively implies that memory is initialized to ZERO. - pub fn read_mem_double(&mut self, ctx: ContextId, addr: u32) -> [Word; 2] { + pub fn read_mem_double( + &mut self, + ctx: ContextId, + addr: u32, + ) -> Result<[Word; 2], ExecutionError> { // read two words from memory: from addr and from addr + 1 let addr2 = addr + 1; - [self.memory.read(ctx, addr, self.clk), self.memory.read(ctx, addr2, self.clk)] + Ok([self.memory.read(ctx, addr, self.clk)?, self.memory.read(ctx, addr2, self.clk)?]) } /// Writes the provided word at the specified context/address. - pub fn write_mem(&mut self, ctx: ContextId, addr: u32, word: Word) { - self.memory.write(ctx, addr, self.clk, word); + pub fn write_mem( + &mut self, + ctx: ContextId, + addr: u32, + word: Word, + ) -> Result<(), ExecutionError> { + self.memory.write(ctx, addr, self.clk, word) } /// Writes the provided element into the specified context/address leaving the remaining 3 /// elements of the word previously stored at that address unchanged. - pub fn write_mem_element(&mut self, ctx: ContextId, addr: u32, value: Felt) -> Word { + pub fn write_mem_element( + &mut self, + ctx: ContextId, + addr: u32, + value: Felt, + ) -> Result { let old_word = self.memory.get_old_value(ctx, addr); let new_word = [value, old_word[1], old_word[2], old_word[3]]; - self.memory.write(ctx, addr, self.clk, new_word); + self.memory.write(ctx, addr, self.clk, new_word)?; - old_word + Ok(old_word) } /// Writes the two provided words to two consecutive addresses in memory in the specified /// context, starting at the specified address. - pub fn write_mem_double(&mut self, ctx: ContextId, addr: u32, words: [Word; 2]) { + pub fn write_mem_double( + &mut self, + ctx: ContextId, + addr: u32, + words: [Word; 2], + ) -> Result<(), ExecutionError> { let addr2 = addr + 1; // write two words to memory at addr and addr + 1 - self.memory.write(ctx, addr, self.clk, words[0]); - self.memory.write(ctx, addr2, self.clk, words[1]); + self.memory.write(ctx, addr, self.clk, words[0])?; + self.memory.write(ctx, addr2, self.clk, words[1]) } /// Returns a word located at the specified context/address, or None if the address hasn't diff --git a/processor/src/errors.rs b/processor/src/errors.rs index 230a37a1c..570d5fad5 100644 --- a/processor/src/errors.rs +++ b/processor/src/errors.rs @@ -16,6 +16,7 @@ use super::{ system::{FMP_MAX, FMP_MIN}, Digest, Felt, QuadFelt, Word, }; +use crate::ContextId; // EXECUTION ERROR // ================================================================================================ @@ -31,6 +32,11 @@ pub enum ExecutionError { decorator_id: DecoratorId, }, DivideByZero(RowIndex), + DuplicateMemoryAccess { + ctx: ContextId, + addr: u32, + clk: Felt, + }, DynamicNodeNotFound(Digest), EventError(String), Ext2InttError(Ext2InttError), @@ -110,6 +116,10 @@ impl Display for ExecutionError { write!(f, "Malformed MAST forest, decorator id {decorator_id} doesn't exist") }, DivideByZero(clk) => write!(f, "Division by zero at clock cycle {clk}"), + DuplicateMemoryAccess { ctx, addr, clk } => write!( + f, + "Memory address {addr} in context {ctx} accessed twice in clock cycle {clk}" + ), DynamicNodeNotFound(digest) => { let hex = to_hex(digest.as_bytes()); write!( diff --git a/processor/src/operations/comb_ops.rs b/processor/src/operations/comb_ops.rs index e0b68af19..f1ab1f8d4 100644 --- a/processor/src/operations/comb_ops.rs +++ b/processor/src/operations/comb_ops.rs @@ -64,10 +64,10 @@ where let [t7, t6, t5, t4, t3, t2, t1, t0] = self.get_trace_values(); // --- read the randomness from memory ---------------------------------------------------- - let alpha = self.get_randomness(); + let alpha = self.get_randomness()?; // --- read the OOD values from memory ---------------------------------------------------- - let [tz, tgz] = self.get_ood_values(); + let [tz, tgz] = self.get_ood_values()?; // --- read the accumulator values from stack --------------------------------------------- let [p, r] = self.read_accumulators(); @@ -125,22 +125,23 @@ where } /// Returns randomness. - fn get_randomness(&mut self) -> QuadFelt { + fn get_randomness(&mut self) -> Result { let ctx = self.system.ctx(); let addr = self.stack.get(14); - let word = self.chiplets.read_mem(ctx, addr.as_int() as u32); + let word = self.chiplets.read_mem(ctx, addr.as_int() as u32)?; let a0 = word[0]; let a1 = word[1]; - QuadFelt::new(a0, a1) + + Ok(QuadFelt::new(a0, a1)) } /// Returns the OOD values. - fn get_ood_values(&mut self) -> [QuadFelt; 2] { + fn get_ood_values(&mut self) -> Result<[QuadFelt; 2], ExecutionError> { let ctx = self.system.ctx(); let addr = self.stack.get(13); - let word = self.chiplets.read_mem(ctx, addr.as_int() as u32); + let word = self.chiplets.read_mem(ctx, addr.as_int() as u32)?; - [QuadFelt::new(word[0], word[1]), QuadFelt::new(word[2], word[3])] + Ok([QuadFelt::new(word[0], word[1]), QuadFelt::new(word[2], word[3])]) } /// Reads the accumulator values. @@ -202,18 +203,25 @@ mod tests { // --- setup memory ----------------------------------------------------------------------- let ctx = ContextId::root(); let tztgz = rand_array::(); - process.chiplets.write_mem( - ctx, - inputs[2].as_int().try_into().expect("Shouldn't fail by construction"), - tztgz, - ); + process + .chiplets + .write_mem( + ctx, + inputs[2].as_int().try_into().expect("Shouldn't fail by construction"), + tztgz, + ) + .unwrap(); let a = rand_array::(); - process.chiplets.write_mem( - ctx, - inputs[1].as_int().try_into().expect("Shouldn't fail by construction"), - a, - ); + process + .chiplets + .write_mem( + ctx, + inputs[1].as_int().try_into().expect("Shouldn't fail by construction"), + a, + ) + .unwrap(); + process.execute_op(Operation::Noop).unwrap(); // --- execute RCOMB1 operation ----------------------------------------------------------- process.execute_op(Operation::RCombBase).unwrap(); diff --git a/processor/src/operations/io_ops.rs b/processor/src/operations/io_ops.rs index d6765bb2a..b3c9d863b 100644 --- a/processor/src/operations/io_ops.rs +++ b/processor/src/operations/io_ops.rs @@ -90,7 +90,7 @@ where let addr = Self::get_valid_address(self.stack.get(12))?; // load two words from memory - let words = self.chiplets.read_mem_double(ctx, addr); + let words = self.chiplets.read_mem_double(ctx, addr)?; // replace the stack elements with the elements from memory (in stack order) for (i, &mem_value) in words.iter().flat_map(|word| word.iter()).rev().enumerate() { @@ -129,7 +129,7 @@ where let word = [self.stack.get(4), self.stack.get(3), self.stack.get(2), self.stack.get(1)]; // write the word to memory and get the previous word - self.chiplets.write_mem(ctx, addr, word); + self.chiplets.write_mem(ctx, addr, word)?; // reverse the order of the memory word & update the stack state for (i, &value) in word.iter().rev().enumerate() { @@ -160,7 +160,7 @@ where let value = self.stack.get(1); // write the value to the memory and get the previous word - let mut old_word = self.chiplets.write_mem_element(ctx, addr, value); + let mut old_word = self.chiplets.write_mem_element(ctx, addr, value)?; // put the retrieved word into stack order old_word.reverse(); @@ -192,7 +192,7 @@ where let words = self.host.borrow_mut().pop_adv_stack_dword(self)?; // write the words memory - self.chiplets.write_mem_double(ctx, addr, words); + self.chiplets.write_mem_double(ctx, addr, words)?; // replace the elements on the stack with the word elements (in stack order) for (i, &adv_value) in words.iter().flat_map(|word| word.iter()).rev().enumerate() { @@ -252,7 +252,7 @@ where pub(crate) fn read_mem_word(&mut self, addr: Felt) -> Result { let ctx = self.system.ctx(); let mem_addr = Self::get_valid_address(addr)?; - let word_at_addr = self.chiplets.read_mem(ctx, mem_addr); + let word_at_addr = self.chiplets.read_mem(ctx, mem_addr)?; Ok(word_at_addr) }