1use acir::{
2 AcirField,
3 circuit::opcodes::{MemOp, MemOpKind},
4 native_types::{Witness, WitnessMap},
5};
6
7use super::{ErrorLocation, OpcodeResolutionError};
8use super::{insert_value, witness_to_value};
9
10type MemoryIndex = u32;
11
12pub(crate) struct MemoryOpSolver<F> {
14 pub(super) block_value: Vec<F>,
18}
19
20impl<F: AcirField> MemoryOpSolver<F> {
21 pub(crate) fn new(
23 init: &[Witness],
24 initial_witness: &WitnessMap<F>,
25 ) -> Result<Self, OpcodeResolutionError<F>> {
26 Ok(Self {
27 block_value: init
28 .iter()
29 .map(|witness| witness_to_value(initial_witness, *witness).copied())
30 .collect::<Result<Vec<_>, _>>()?,
31 })
32 }
33
34 pub(crate) fn len(&self) -> u32 {
35 u32::try_from(self.block_value.len()).expect("expected a length that fits into a u32")
36 }
37
38 pub(crate) fn index_from_field(
41 &self,
42 index: F,
43 ) -> Result<MemoryIndex, OpcodeResolutionError<F>> {
44 index.try_to_u32().ok_or_else({
45 || OpcodeResolutionError::IndexOutOfBounds {
46 opcode_location: ErrorLocation::Unresolved,
47 index,
48 array_size: self.len(),
49 payload: None,
50 }
51 })
52 }
53
54 pub(crate) fn write_memory_index(
57 &mut self,
58 index: MemoryIndex,
59 value: F,
60 ) -> Result<(), OpcodeResolutionError<F>> {
61 if index >= self.len() {
62 return Err(OpcodeResolutionError::IndexOutOfBounds {
63 opcode_location: ErrorLocation::Unresolved,
64 index: F::from(u128::from(index)),
65 array_size: self.len(),
66 payload: None,
67 });
68 }
69
70 self.block_value[index as usize] = value;
71 Ok(())
72 }
73
74 pub(crate) fn read_memory_index(
77 &self,
78 index: MemoryIndex,
79 ) -> Result<F, OpcodeResolutionError<F>> {
80 self.block_value.get(index as usize).copied().ok_or(
81 OpcodeResolutionError::IndexOutOfBounds {
82 opcode_location: ErrorLocation::Unresolved,
83 index: F::from(u128::from(index)),
84 array_size: self.len(),
85 payload: None,
86 },
87 )
88 }
89
90 pub(crate) fn solve_memory_op(
109 &mut self,
110 op: &MemOp,
111 initial_witness: &mut WitnessMap<F>,
112 ) -> Result<(), OpcodeResolutionError<F>> {
113 let index = *witness_to_value(initial_witness, op.index)?;
115 let memory_index = self.index_from_field(index)?;
116
117 match op.operation {
118 MemOpKind::Read => {
119 let value_in_array = self.read_memory_index(memory_index)?;
121 insert_value(&op.value, value_in_array, initial_witness)
122 }
123 MemOpKind::Write => {
124 let value_to_write = *witness_to_value(initial_witness, op.value)?;
126 self.write_memory_index(memory_index, value_to_write)
127 }
128 }
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use std::collections::BTreeMap;
135
136 use acir::{
137 FieldElement,
138 circuit::opcodes::MemOp,
139 native_types::{Witness, WitnessMap},
140 };
141
142 use super::MemoryOpSolver;
143
144 #[test]
145 fn test_solver() {
146 let mut initial_witness = WitnessMap::from(BTreeMap::from_iter([
147 (Witness(1), FieldElement::from(1u128)),
148 (Witness(2), FieldElement::from(1u128)),
149 (Witness(3), FieldElement::from(2u128)),
150 ]));
151
152 let init = vec![Witness(1), Witness(2)];
153 let trace = vec![
155 MemOp::write_to_mem_index(Witness(1), Witness(3)),
156 MemOp::read_at_mem_index(Witness(1), Witness(4)),
157 ];
158
159 let mut block_solver = MemoryOpSolver::new(&init, &initial_witness).unwrap();
160
161 for op in trace {
162 block_solver.solve_memory_op(&op, &mut initial_witness).unwrap();
163 }
164
165 assert_eq!(initial_witness[&Witness(4)], FieldElement::from(2u128));
166 }
167
168 #[test]
169 fn test_index_out_of_bounds() {
170 let mut initial_witness = WitnessMap::from(BTreeMap::from_iter([
171 (Witness(1), FieldElement::from(1u128)),
172 (Witness(2), FieldElement::from(1u128)),
173 (Witness(3), FieldElement::from(2u128)),
174 ]));
175
176 let init = vec![Witness(1), Witness(2)];
177 let invalid_trace = vec![
179 MemOp::write_to_mem_index(Witness(1), Witness(3)),
180 MemOp::read_at_mem_index(Witness(3), Witness(4)),
181 ];
182 let mut block_solver = MemoryOpSolver::new(&init, &initial_witness).unwrap();
183 let mut err = None;
184 for op in invalid_trace {
185 if err.is_none() {
186 err = block_solver.solve_memory_op(&op, &mut initial_witness).err();
187 }
188 }
189
190 assert!(matches!(
191 err,
192 Some(crate::pwg::OpcodeResolutionError::IndexOutOfBounds {
193 opcode_location: _,
194 index,
195 array_size: 2,
196 payload: None
197 }) if index == FieldElement::from(2u128)
198 ));
199 }
200}