essential_vm/
access.rs

1//! Access operation implementations.
2
3use crate::{
4    cached::LazyCache,
5    error::{AccessError, MissingAccessArgError, OpResult},
6    repeat::Repeat,
7    types::{
8        convert::{bytes_from_word, u8_32_from_word_4, word_4_from_u8_32},
9        solution::{Solution, SolutionIndex},
10        Value, Word,
11    },
12    Stack,
13};
14use std::sync::Arc;
15
16#[cfg(test)]
17mod dec_vars;
18#[cfg(test)]
19mod predicate_exists;
20#[cfg(test)]
21mod test_utils;
22#[cfg(test)]
23mod tests;
24
25/// All necessary solution access required to check an individual predicate.
26#[derive(Clone, Debug)]
27pub struct Access {
28    /// The set of input data for each predicate being solved within the solution set.
29    ///
30    /// We require *all* solutions in order to handle checking predicate exists.
31    pub solutions: Arc<Vec<Solution>>,
32    /// Checking is performed for one solution at a time. This index refers to
33    /// the checked predicate's associated solution within the `SolutionSet` slice.
34    pub index: usize,
35}
36
37impl Access {
38    /// A shorthand for constructing a `SolutionAccess` instance for checking
39    /// the predicate at the given index within the given solution.
40    ///
41    /// This constructor assumes that the given mutable keys contract is correct
42    /// for this solution. It is not checked by this function for performance.
43    pub fn new(solutions: Arc<Vec<Solution>>, solution_index: SolutionIndex) -> Self {
44        Self {
45            solutions,
46            index: solution_index.into(),
47        }
48    }
49
50    /// The solution associated with the predicate currently being checked.
51    ///
52    /// **Panics** in the case that `self.index` is out of range of the `self.solutions` slice.
53    pub fn this_solution(&self) -> &Solution {
54        self.solutions
55            .get(self.index)
56            .expect("solution index out of range of solutions slice")
57    }
58}
59
60/// `Access::PredicateData` implementation.
61pub(crate) fn predicate_data(this_predicate_data: &[Value], stack: &mut Stack) -> OpResult<()> {
62    let len = stack
63        .pop()
64        .map_err(|_| AccessError::MissingArg(MissingAccessArgError::PredDataLen))?;
65    let value_ix = stack
66        .pop()
67        .map_err(|_| AccessError::MissingArg(MissingAccessArgError::PredDataValueIx))?;
68    let slot_ix = stack
69        .pop()
70        .map_err(|_| AccessError::MissingArg(MissingAccessArgError::PredDataSlotIx))?;
71    let slot_ix = usize::try_from(slot_ix)
72        .map_err(|_| AccessError::PredicateDataSlotIxOutOfBounds(slot_ix))?;
73    let range = range_from_start_len(value_ix, len).ok_or(AccessError::InvalidAccessRange)?;
74    let words = resolve_predicate_data_range(this_predicate_data, slot_ix, range)?;
75    stack.extend(words.iter().copied())?;
76    Ok(())
77}
78
79/// `Access::PredicateDataLen` implementation.
80pub(crate) fn predicate_data_len(
81    this_predicate_data: &[Value],
82    stack: &mut Stack,
83) -> Result<(), AccessError> {
84    let slot_ix = stack
85        .pop()
86        .map_err(|_| MissingAccessArgError::PredDataSlotIx)?;
87    let slot_ix = usize::try_from(slot_ix)
88        .map_err(|_| AccessError::PredicateDataSlotIxOutOfBounds(slot_ix))?;
89    let len = resolve_predicate_data_len(this_predicate_data, slot_ix)?;
90    let w = Word::try_from(len).map_err(|_| AccessError::PredicateDataValueTooLarge(len))?;
91    stack
92        .push(w)
93        .expect("Can't fail because 1 is popped and 1 is pushed");
94    Ok(())
95}
96
97/// `Access::ThisAddress` implementation.
98pub(crate) fn this_address(solution: &Solution, stack: &mut Stack) -> OpResult<()> {
99    let words = word_4_from_u8_32(solution.predicate_to_solve.predicate.0);
100    stack.extend(words)?;
101    Ok(())
102}
103
104/// `Access::ThisContractAddress` implementation.
105pub(crate) fn this_contract_address(solution: &Solution, stack: &mut Stack) -> OpResult<()> {
106    let words = word_4_from_u8_32(solution.predicate_to_solve.contract.0);
107    stack.extend(words)?;
108    Ok(())
109}
110
111pub(crate) fn repeat_counter(stack: &mut Stack, repeat: &Repeat) -> OpResult<()> {
112    let counter = repeat.counter()?;
113    Ok(stack.push(counter)?)
114}
115
116/// Implementation of the `Access::NumSlots` operation.
117pub(crate) fn predicate_data_slots(stack: &mut Stack, predicate_data: &[Value]) -> OpResult<()> {
118    let num_slots = Word::try_from(predicate_data.len())
119        .map_err(|_| AccessError::SlotsLengthTooLarge(predicate_data.len()))?;
120    stack.push(num_slots)?;
121    Ok(())
122}
123
124/// Resolve a range of words at a predicate data slot.
125///
126/// Errors if the solution or predicate data indices are out of bounds.
127pub(crate) fn resolve_predicate_data_range(
128    predicate_data: &[Value],
129    slot_ix: usize,
130    value_range_ix: core::ops::Range<usize>,
131) -> Result<&[Word], AccessError> {
132    predicate_data
133        .get(slot_ix)
134        .ok_or(AccessError::PredicateDataSlotIxOutOfBounds(slot_ix as Word))?
135        .get(value_range_ix.clone())
136        .ok_or(AccessError::PredicateDataValueRangeOutOfBounds(
137            value_range_ix.start as Word,
138            value_range_ix.end as Word,
139        ))
140}
141
142/// Resolve the length of predicate data slot.
143///
144/// Errors if the solution or predicate data indices are out of bounds.
145pub(crate) fn resolve_predicate_data_len(
146    predicate_data: &[Value],
147    slot_ix: usize,
148) -> Result<usize, AccessError> {
149    predicate_data
150        .get(slot_ix)
151        .map(|slot| slot.len())
152        .ok_or(AccessError::PredicateDataSlotIxOutOfBounds(slot_ix as Word))
153}
154
155pub(crate) fn predicate_exists(
156    stack: &mut Stack,
157    solutions: Arc<Vec<Solution>>,
158    cache: &LazyCache,
159) -> OpResult<()> {
160    let hash = u8_32_from_word_4(stack.pop4()?);
161    let found = cache.get_pred_data_hashes(solutions).contains(&hash);
162    stack.push(found as Word)?;
163    Ok(())
164}
165
166pub(crate) fn init_predicate_exists(solutions: Arc<Vec<Solution>>) -> Vec<essential_types::Hash> {
167    solutions
168        .iter()
169        .map(|d| {
170            let data = d
171                .predicate_data
172                .iter()
173                .flat_map(|slot| {
174                    Some(slot.len() as Word)
175                        .into_iter()
176                        .chain(slot.iter().cloned())
177                })
178                .chain(word_4_from_u8_32(d.predicate_to_solve.contract.0))
179                .chain(word_4_from_u8_32(d.predicate_to_solve.predicate.0))
180                .flat_map(bytes_from_word)
181                .collect::<Vec<_>>();
182            sha256(&data)
183        })
184        .collect()
185}
186
187fn sha256(bytes: &[u8]) -> [u8; 32] {
188    use sha2::{Digest, Sha256};
189    let mut hasher = Sha256::new();
190    hasher.update(bytes);
191    let result: [u8; 32] = hasher.finalize().into();
192    result
193}
194
195fn range_from_start_len(start: Word, len: Word) -> Option<std::ops::Range<usize>> {
196    let start = usize::try_from(start).ok()?;
197    let len = usize::try_from(len).ok()?;
198    let end = start.checked_add(len)?;
199    Some(start..end)
200}