1use crate::{
4 cached::LazyCache,
5 error::{AccessError, MissingAccessArgError},
6 repeat::Repeat,
7 sets::encode_set,
8 types::convert::bool_from_word,
9 OpResult, Stack,
10};
11use essential_constraint_asm::Word;
12use essential_types::{
13 convert::{bytes_from_word, u8_32_from_word_4, word_4_from_u8_32},
14 solution::{Solution, SolutionData, SolutionDataIndex},
15 Key, Value,
16};
17use std::collections::HashSet;
18
19#[cfg(test)]
20mod dec_vars;
21#[cfg(test)]
22mod num_slots;
23#[cfg(test)]
24mod predicate_exists;
25#[cfg(test)]
26mod state;
27#[cfg(test)]
28mod test_utils;
29#[cfg(test)]
30mod tests;
31
32#[derive(Clone, Copy, Debug)]
34pub struct Access<'a> {
35 pub solution: SolutionAccess<'a>,
37 pub state_slots: StateSlots<'a>,
39}
40
41#[derive(Clone, Copy, Debug)]
43pub struct SolutionAccess<'a> {
44 pub data: &'a [SolutionData],
49 pub index: usize,
52 pub mutable_keys: &'a HashSet<&'a [Word]>,
54}
55
56#[derive(Clone, Copy, Debug)]
58pub struct StateSlots<'a> {
59 pub pre: &'a StateSlotSlice,
61 pub post: &'a StateSlotSlice,
63}
64
65pub type StateSlotSlice = [Vec<Word>];
67
68impl<'a> SolutionAccess<'a> {
69 pub fn new(
75 solution: &'a Solution,
76 predicate_index: SolutionDataIndex,
77 mutable_keys: &'a HashSet<&[Word]>,
78 ) -> Self {
79 Self {
80 data: &solution.data,
81 index: predicate_index.into(),
82 mutable_keys,
83 }
84 }
85
86 pub fn this_data(&self) -> &SolutionData {
90 self.data
91 .get(self.index)
92 .expect("predicate index out of range of solution data")
93 }
94}
95
96impl<'a> StateSlots<'a> {
97 pub const EMPTY: Self = Self {
99 pre: &[],
100 post: &[],
101 };
102}
103
104pub fn mut_keys(
113 solution: &Solution,
114 predicate_index: SolutionDataIndex,
115) -> impl Iterator<Item = &Key> {
116 solution.data[predicate_index as usize]
117 .state_mutations
118 .iter()
119 .map(|m| &m.key)
120}
121
122pub fn mut_keys_slices(
124 solution: &Solution,
125 predicate_index: SolutionDataIndex,
126) -> impl Iterator<Item = &[Word]> {
127 solution.data[predicate_index as usize]
128 .state_mutations
129 .iter()
130 .map(|m| m.key.as_ref())
131}
132
133pub fn mut_keys_set(solution: &Solution, predicate_index: SolutionDataIndex) -> HashSet<&[Word]> {
135 mut_keys_slices(solution, predicate_index).collect()
136}
137
138pub(crate) fn decision_var(this_decision_vars: &[Value], stack: &mut Stack) -> OpResult<()> {
140 let len = stack.pop().map_err(|_| MissingAccessArgError::DecVarLen)?;
141 let value_ix = stack
142 .pop()
143 .map_err(|_| MissingAccessArgError::DecVarValueIx)?;
144 let slot_ix = stack
145 .pop()
146 .map_err(|_| MissingAccessArgError::DecVarSlotIx)?;
147 let slot_ix =
148 usize::try_from(slot_ix).map_err(|_| AccessError::DecisionSlotIxOutOfBounds(slot_ix))?;
149 let range = range_from_start_len(value_ix, len).ok_or(AccessError::InvalidAccessRange)?;
150 let words = resolve_decision_var_range(this_decision_vars, slot_ix, range)?;
151 stack.extend(words.iter().copied())?;
152 Ok(())
153}
154
155pub(crate) fn decision_var_len(this_decision_vars: &[Value], stack: &mut Stack) -> OpResult<()> {
157 let slot_ix = stack
158 .pop()
159 .map_err(|_| MissingAccessArgError::DecVarSlotIx)?;
160 let slot_ix =
161 usize::try_from(slot_ix).map_err(|_| AccessError::DecisionSlotIxOutOfBounds(slot_ix))?;
162 let len = resolve_decision_var_len(this_decision_vars, slot_ix)?;
163 let w = Word::try_from(len).map_err(|_| AccessError::DecisionLengthTooLarge(len))?;
164 stack
165 .push(w)
166 .expect("Can't fail because 1 is popped and 1 is pushed");
167 Ok(())
168}
169
170pub(crate) fn push_mut_keys(solution: SolutionAccess, stack: &mut Stack) -> OpResult<()> {
172 encode_set(
173 solution.mutable_keys.iter().map(|k| k.iter().copied()),
174 stack,
175 )
176}
177
178pub(crate) fn state(slots: StateSlots, stack: &mut Stack) -> OpResult<()> {
180 let delta = stack.pop().map_err(|_| MissingAccessArgError::StateDelta)?;
181 let len = stack.pop().map_err(|_| MissingAccessArgError::StateLen)?;
182 let value_ix = stack
183 .pop()
184 .map_err(|_| MissingAccessArgError::StateValueIx)?;
185 let slot_ix = stack
186 .pop()
187 .map_err(|_| MissingAccessArgError::StateSlotIx)?;
188 let values = state_slot_value_range(slots, slot_ix, value_ix, len, delta)?;
189 stack.extend(values.iter().copied())?;
190 Ok(())
191}
192
193pub(crate) fn state_len(slots: StateSlots, stack: &mut Stack) -> OpResult<()> {
195 let delta = stack.pop().map_err(|_| MissingAccessArgError::StateDelta)?;
196 let slot_ix = stack
197 .pop()
198 .map_err(|_| MissingAccessArgError::StateSlotIx)?;
199 let slot = state_slot(slots, slot_ix, delta)?;
200 let len =
201 Word::try_from(slot.len()).map_err(|_| AccessError::StateValueTooLarge(slot.len()))?;
202 stack
203 .push(len)
204 .expect("Can't fail because 2 are popped and 1 is pushed");
205 Ok(())
206}
207
208pub(crate) fn this_address(data: &SolutionData, stack: &mut Stack) -> OpResult<()> {
210 let words = word_4_from_u8_32(data.predicate_to_solve.predicate.0);
211 stack.extend(words)?;
212 Ok(())
213}
214
215pub(crate) fn this_contract_address(data: &SolutionData, stack: &mut Stack) -> OpResult<()> {
217 let words = word_4_from_u8_32(data.predicate_to_solve.contract.0);
218 stack.extend(words)?;
219 Ok(())
220}
221
222pub(crate) fn repeat_counter(stack: &mut Stack, repeat: &Repeat) -> OpResult<()> {
223 let counter = repeat.counter()?;
224 Ok(stack.push(counter)?)
225}
226
227pub(crate) fn num_slots(
229 stack: &mut Stack,
230 state_slots: &StateSlots<'_>,
231 decision_variables: &[Value],
232) -> OpResult<()> {
233 const DEC_VAR_SLOTS: Word = 0;
234 const PRE_STATE_SLOTS: Word = 1;
235 const POST_STATE_SLOTS: Word = 2;
236
237 let which_slots = stack.pop()?;
238
239 match which_slots {
240 DEC_VAR_SLOTS => {
241 let num_slots = Word::try_from(decision_variables.len())
242 .map_err(|_| AccessError::SlotsLengthTooLarge(decision_variables.len()))?;
243 stack.push(num_slots)?;
244 }
245 PRE_STATE_SLOTS => {
246 let num_slots = Word::try_from(state_slots.pre.len())
247 .map_err(|_| AccessError::SlotsLengthTooLarge(state_slots.pre.len()))?;
248 stack.push(num_slots)?;
249 }
250 POST_STATE_SLOTS => {
251 let num_slots = Word::try_from(state_slots.post.len())
252 .map_err(|_| AccessError::SlotsLengthTooLarge(state_slots.post.len()))?;
253 stack.push(num_slots)?;
254 }
255 _ => return Err(AccessError::InvalidSlotType(which_slots).into()),
256 }
257 Ok(())
258}
259
260pub(crate) fn resolve_decision_var_range(
264 decision_variables: &[Value],
265 slot_ix: usize,
266 value_range_ix: core::ops::Range<usize>,
267) -> Result<&[Word], AccessError> {
268 decision_variables
269 .get(slot_ix)
270 .ok_or(AccessError::DecisionSlotIxOutOfBounds(slot_ix as Word))?
271 .get(value_range_ix.clone())
272 .ok_or(AccessError::DecisionValueRangeOutOfBounds(
273 value_range_ix.start as Word,
274 value_range_ix.end as Word,
275 ))
276}
277
278pub(crate) fn resolve_decision_var_len(
282 decision_variables: &[Value],
283 slot_ix: usize,
284) -> Result<usize, AccessError> {
285 decision_variables
286 .get(slot_ix)
287 .map(|slot| slot.len())
288 .ok_or(AccessError::DecisionSlotIxOutOfBounds(slot_ix as Word))
289}
290
291pub(crate) fn predicate_exists(
292 stack: &mut Stack,
293 data: &[SolutionData],
294 cache: &LazyCache,
295) -> OpResult<()> {
296 let hash = u8_32_from_word_4(stack.pop4()?);
297 let found = cache.get_dec_var_hashes(data).contains(&hash);
298 stack.push(found as Word)?;
299 Ok(())
300}
301
302pub(crate) fn init_predicate_exists(
303 data: &[SolutionData],
304) -> impl Iterator<Item = essential_types::Hash> + '_ {
305 data.iter().map(|d| {
306 let data = d
307 .decision_variables
308 .iter()
309 .flat_map(|slot| {
310 Some(slot.len() as Word)
311 .into_iter()
312 .chain(slot.iter().cloned())
313 })
314 .chain(word_4_from_u8_32(d.predicate_to_solve.contract.0))
315 .chain(word_4_from_u8_32(d.predicate_to_solve.predicate.0))
316 .flat_map(bytes_from_word)
317 .collect::<Vec<_>>();
318 sha256(&data)
319 })
320}
321
322fn sha256(bytes: &[u8]) -> [u8; 32] {
323 use sha2::{Digest, Sha256};
324 let mut hasher = Sha256::new();
325 hasher.update(bytes);
326 let result: [u8; 32] = hasher.finalize().into();
327 result
328}
329
330fn state_slot(slots: StateSlots, slot_ix: Word, delta: Word) -> OpResult<&Vec<Word>> {
331 let delta = bool_from_word(delta).ok_or(AccessError::InvalidStateSlotDelta(delta))?;
332 let slots = state_slots_from_delta(slots, delta);
333 let ix = usize::try_from(slot_ix).map_err(|_| AccessError::StateSlotIxOutOfBounds(slot_ix))?;
334 let slot = slots
335 .get(ix)
336 .ok_or(AccessError::StateSlotIxOutOfBounds(slot_ix))?;
337 Ok(slot)
338}
339
340fn state_slot_value_range(
341 slots: StateSlots,
342 slot_ix: Word,
343 value_ix: Word,
344 len: Word,
345 delta: Word,
346) -> OpResult<&[Word]> {
347 let delta = bool_from_word(delta).ok_or(AccessError::InvalidStateSlotDelta(delta))?;
348 let slots = state_slots_from_delta(slots, delta);
349 let slot_ix =
350 usize::try_from(slot_ix).map_err(|_| AccessError::StateSlotIxOutOfBounds(slot_ix))?;
351 let range = range_from_start_len(value_ix, len).ok_or(AccessError::InvalidAccessRange)?;
352 let values = slots
353 .get(slot_ix)
354 .ok_or(AccessError::StateSlotIxOutOfBounds(slot_ix as Word))?
355 .get(range.clone())
356 .ok_or(AccessError::StateValueRangeOutOfBounds(
357 range.start as Word,
358 range.end as Word,
359 ))?;
360 Ok(values)
361}
362
363fn range_from_start_len(start: Word, len: Word) -> Option<std::ops::Range<usize>> {
364 let start = usize::try_from(start).ok()?;
365 let len = usize::try_from(len).ok()?;
366 let end = start.checked_add(len)?;
367 Some(start..end)
368}
369
370fn state_slots_from_delta(slots: StateSlots, delta: bool) -> &StateSlotSlice {
371 if delta {
372 slots.post
373 } else {
374 slots.pre
375 }
376}