Skip to main content

miden_processor/host/advice/
mod.rs

1use alloc::{
2    collections::{VecDeque, btree_map::Entry},
3    vec::Vec,
4};
5
6use miden_core::{
7    Felt, Word,
8    advice::{AdviceInputs, AdviceMap},
9    crypto::merkle::{InnerNodeInfo, MerklePath, MerkleStore, NodeIndex},
10    precompile::PrecompileRequest,
11};
12
13mod errors;
14pub use errors::AdviceError;
15
16use crate::{host::AdviceMutation, processor::AdviceProviderInterface};
17
18// CONSTANTS
19// ================================================================================================
20
21/// Maximum number of elements allowed on the advice stack. Set to 2^17.
22const MAX_ADVICE_STACK_SIZE: usize = 1 << 17;
23
24// ADVICE PROVIDER
25// ================================================================================================
26
27/// An advice provider is a component through which the VM can request nondeterministic inputs from
28/// the host (i.e., result of a computation performed outside of the VM), as well as insert new data
29/// into the advice provider to be recovered by the host after the program has finished executing.
30///
31/// An advice provider consists of the following components:
32/// 1. Advice stack, which is a LIFO data structure. The processor can move the elements from the
33///    advice stack onto the operand stack, as well as push new elements onto the advice stack. The
34///    maximum number of elements that can be on the advice stack is 2^17.
35/// 2. Advice map, which is a key-value map where keys are words (4 field elements) and values are
36///    vectors of field elements. The processor can push the values from the map onto the advice
37///    stack, as well as insert new values into the map.
38/// 3. Merkle store, which contains structured data reducible to Merkle paths. The VM can request
39///    Merkle paths from the store, as well as mutate it by updating or merging nodes contained in
40///    the store.
41/// 4. Deferred precompile requests containing the calldata of any precompile requests made by the
42///    VM. The VM computes a commitment to the calldata of all the precompiles it requests. When
43///    verifying each call, this commitment must be recomputed and should match the one computed by
44///    the VM. After executing a program, the data in these requests can either
45///    - be included in the proof of the VM execution and verified natively alongside the VM proof,
46///      or,
47///    - used to produce a STARK proof using a precompile VM, which can be verified in the epilog of
48///      the program.
49#[derive(Debug, Clone, Default)]
50pub struct AdviceProvider {
51    stack: VecDeque<Felt>,
52    map: AdviceMap,
53    store: MerkleStore,
54    pc_requests: Vec<PrecompileRequest>,
55}
56
57impl AdviceProvider {
58    #[cfg(test)]
59    pub(crate) fn merkle_store(&self) -> &MerkleStore {
60        &self.store
61    }
62
63    /// Applies the mutations given in order to the `AdviceProvider`.
64    pub fn apply_mutations(
65        &mut self,
66        mutations: impl IntoIterator<Item = AdviceMutation>,
67    ) -> Result<(), AdviceError> {
68        mutations.into_iter().try_for_each(|mutation| self.apply_mutation(mutation))
69    }
70
71    fn apply_mutation(&mut self, mutation: AdviceMutation) -> Result<(), AdviceError> {
72        match mutation {
73            AdviceMutation::ExtendStack { values } => {
74                self.extend_stack(values)?;
75            },
76            AdviceMutation::ExtendMap { other } => {
77                self.extend_map(&other)?;
78            },
79            AdviceMutation::ExtendMerkleStore { infos } => {
80                self.extend_merkle_store(infos);
81            },
82            AdviceMutation::ExtendPrecompileRequests { data } => {
83                self.extend_precompile_requests(data);
84            },
85        }
86        Ok(())
87    }
88
89    // ADVICE STACK
90    // --------------------------------------------------------------------------------------------
91
92    /// Pops an element from the advice stack and returns it.
93    ///
94    /// # Errors
95    /// Returns an error if the advice stack is empty.
96    fn pop_stack(&mut self) -> Result<Felt, AdviceError> {
97        self.stack.pop_front().ok_or(AdviceError::StackReadFailed)
98    }
99
100    /// Pops a word (4 elements) from the advice stack and returns it.
101    ///
102    /// Note: a word is popped off the stack element-by-element. For example, a `[d, c, b, a, ...]`
103    /// stack (i.e., `d` is at the top of the stack) will yield `[d, c, b, a]`.
104    ///
105    /// # Errors
106    /// Returns an error if the advice stack does not contain a full word.
107    fn pop_stack_word(&mut self) -> Result<Word, AdviceError> {
108        if self.stack.len() < 4 {
109            return Err(AdviceError::StackReadFailed);
110        }
111
112        let w0 = self.stack.pop_front().expect("checked len");
113        let w1 = self.stack.pop_front().expect("checked len");
114        let w2 = self.stack.pop_front().expect("checked len");
115        let w3 = self.stack.pop_front().expect("checked len");
116
117        Ok(Word::new([w0, w1, w2, w3]))
118    }
119
120    /// Pops a double word (8 elements) from the advice stack and returns them.
121    ///
122    /// Note: words are popped off the stack element-by-element. For example, a
123    /// `[h, g, f, e, d, c, b, a, ...]` stack (i.e., `h` is at the top of the stack) will yield
124    /// two words: `[h, g, f,e ], [d, c, b, a]`.
125    ///
126    /// # Errors
127    /// Returns an error if the advice stack does not contain two words.
128    fn pop_stack_dword(&mut self) -> Result<[Word; 2], AdviceError> {
129        let word0 = self.pop_stack_word()?;
130        let word1 = self.pop_stack_word()?;
131
132        Ok([word0, word1])
133    }
134
135    /// Checks that pushing `count` elements would not exceed the advice stack size limit.
136    fn check_stack_capacity(&self, count: usize) -> Result<(), AdviceError> {
137        let resulting_size =
138            self.stack.len().checked_add(count).ok_or(AdviceError::StackSizeExceeded {
139                push_count: count,
140                max: MAX_ADVICE_STACK_SIZE,
141            })?;
142        if resulting_size > MAX_ADVICE_STACK_SIZE {
143            return Err(AdviceError::StackSizeExceeded {
144                push_count: count,
145                max: MAX_ADVICE_STACK_SIZE,
146            });
147        }
148        Ok(())
149    }
150
151    /// Pushes a single value onto the advice stack.
152    pub fn push_stack(&mut self, value: Felt) -> Result<(), AdviceError> {
153        self.check_stack_capacity(1)?;
154        self.stack.push_front(value);
155        Ok(())
156    }
157
158    /// Pushes a word (4 elements) onto the stack.
159    pub fn push_stack_word(&mut self, word: &Word) -> Result<(), AdviceError> {
160        self.check_stack_capacity(4)?;
161        for &value in word.iter().rev() {
162            self.stack.push_front(value);
163        }
164        Ok(())
165    }
166
167    /// Fetches a list of elements under the specified key from the advice map and pushes them onto
168    /// the advice stack.
169    ///
170    /// If `include_len` is set to true, this also pushes the number of elements onto the advice
171    /// stack.
172    ///
173    /// If `pad_to` is not equal to 0, the elements list obtained from the advice map will be padded
174    /// with zeros, increasing its length to the next multiple of `pad_to`.
175    ///
176    /// Note: this operation doesn't consume the map element so it can be called multiple times
177    /// for the same key.
178    ///
179    /// # Example
180    /// Given an advice stack `[a, b, c, ...]`, and a map `x |-> [d, e, f]`:
181    ///
182    /// A call `push_stack(AdviceSource::Map { key: x, include_len: false, pad_to: 0 })` will result
183    /// in advice stack: `[d, e, f, a, b, c, ...]`.
184    ///
185    /// A call `push_stack(AdviceSource::Map { key: x, include_len: true, pad_to: 0 })` will result
186    /// in advice stack: `[3, d, e, f, a, b, c, ...]`.
187    ///
188    /// A call `push_stack(AdviceSource::Map { key: x, include_len: true, pad_to: 4 })` will result
189    /// in advice stack: `[3, d, e, f, 0, a, b, c, ...]`.
190    ///
191    /// # Errors
192    /// Returns an error if the key was not found in the key-value map.
193    pub fn push_from_map(
194        &mut self,
195        key: Word,
196        include_len: bool,
197        pad_to: u8,
198    ) -> Result<(), AdviceError> {
199        let values = self.map.get(&key).ok_or(AdviceError::MapKeyNotFound { key })?;
200
201        // Calculate total elements to push including padding and optional length prefix
202        let num_pad_elements = if pad_to != 0 {
203            values.len().next_multiple_of(pad_to as usize) - values.len()
204        } else {
205            0
206        };
207        let total_push = values
208            .len()
209            .checked_add(num_pad_elements)
210            .and_then(|n| n.checked_add(if include_len { 1 } else { 0 }))
211            .ok_or(AdviceError::StackSizeExceeded {
212                push_count: usize::MAX,
213                max: MAX_ADVICE_STACK_SIZE,
214            })?;
215        self.check_stack_capacity(total_push)?;
216
217        // if pad_to was provided (not equal 0), push some zeros to the advice stack so that the
218        // final (padded) elements list length will be the next multiple of pad_to
219        for _ in 0..num_pad_elements {
220            self.stack.push_front(Felt::default());
221        }
222
223        // Treat map values as already canonical sequences of FELTs.
224        // The advice stack is LIFO; extend in reverse so that the first element of `values`
225        // becomes the first element returned by a subsequent `adv_push.*`.
226        for &value in values.iter().rev() {
227            self.stack.push_front(value);
228        }
229        if include_len {
230            self.stack.push_front(Felt::new(values.len() as u64));
231        }
232        Ok(())
233    }
234
235    /// Returns the current stack as a vector ordered from top (index 0) to bottom.
236    pub fn stack(&self) -> Vec<Felt> {
237        self.stack.iter().copied().collect()
238    }
239
240    /// Extends the stack with the given elements.
241    pub fn extend_stack<I>(&mut self, iter: I) -> Result<(), AdviceError>
242    where
243        I: IntoIterator<Item = Felt>,
244    {
245        let values: Vec<Felt> = iter.into_iter().collect();
246        self.check_stack_capacity(values.len())?;
247        for value in values.into_iter().rev() {
248            self.stack.push_front(value);
249        }
250        Ok(())
251    }
252
253    // ADVICE MAP
254    // --------------------------------------------------------------------------------------------
255
256    /// Returns true if the key has a corresponding value in the map.
257    pub fn contains_map_key(&self, key: &Word) -> bool {
258        self.map.contains_key(key)
259    }
260
261    /// Returns a reference to the value(s) associated with the specified key in the advice map.
262    pub fn get_mapped_values(&self, key: &Word) -> Option<&[Felt]> {
263        self.map.get(key).map(|value| value.as_ref())
264    }
265
266    /// Inserts the provided value into the advice map under the specified key.
267    ///
268    /// The values in the advice map can be moved onto the advice stack by invoking
269    /// the [AdviceProvider::push_from_map()] method.
270    ///
271    /// Returns an error if the specified key is already present in the advice map.
272    pub fn insert_into_map(&mut self, key: Word, values: Vec<Felt>) -> Result<(), AdviceError> {
273        match self.map.entry(key) {
274            Entry::Vacant(entry) => {
275                entry.insert(values.into());
276            },
277            Entry::Occupied(entry) => {
278                let existing_values = entry.get().as_ref();
279                if existing_values != values {
280                    return Err(AdviceError::MapKeyAlreadyPresent {
281                        key,
282                        prev_values: existing_values.to_vec(),
283                        new_values: values,
284                    });
285                }
286            },
287        }
288        Ok(())
289    }
290
291    /// Merges all entries from the given [`AdviceMap`] into the current advice map.
292    ///
293    /// Returns an error if any new entry already exists with the same key but a different value
294    /// than the one currently stored. The current map remains unchanged.
295    pub fn extend_map(&mut self, other: &AdviceMap) -> Result<(), AdviceError> {
296        self.map.merge(other).map_err(|((key, prev_values), new_values)| {
297            AdviceError::MapKeyAlreadyPresent {
298                key,
299                prev_values: prev_values.to_vec(),
300                new_values: new_values.to_vec(),
301            }
302        })
303    }
304
305    // MERKLE STORE
306    // --------------------------------------------------------------------------------------------
307
308    /// Returns a node at the specified depth and index in a Merkle tree with the given root.
309    ///
310    /// # Errors
311    /// Returns an error if:
312    /// - A Merkle tree for the specified root cannot be found in this advice provider.
313    /// - The specified depth is either zero or greater than the depth of the Merkle tree identified
314    ///   by the specified root.
315    /// - Value of the node at the specified depth and index is not known to this advice provider.
316    pub fn get_tree_node(&self, root: Word, depth: Felt, index: Felt) -> Result<Word, AdviceError> {
317        let index = NodeIndex::from_elements(&depth, &index)
318            .map_err(|_| AdviceError::InvalidMerkleTreeNodeIndex { depth, index })?;
319        self.store.get_node(root, index).map_err(AdviceError::MerkleStoreLookupFailed)
320    }
321
322    /// Returns true if a path to a node at the specified depth and index in a Merkle tree with the
323    /// specified root exists in this Merkle store.
324    ///
325    /// # Errors
326    /// Returns an error if accessing the Merkle store fails.
327    pub fn has_merkle_path(
328        &self,
329        root: Word,
330        depth: Felt,
331        index: Felt,
332    ) -> Result<bool, AdviceError> {
333        let index = NodeIndex::from_elements(&depth, &index)
334            .map_err(|_| AdviceError::InvalidMerkleTreeNodeIndex { depth, index })?;
335
336        Ok(self.store.has_path(root, index))
337    }
338
339    /// Returns a path to a node at the specified depth and index in a Merkle tree with the
340    /// specified root.
341    ///
342    /// # Errors
343    /// Returns an error if:
344    /// - A Merkle tree for the specified root cannot be found in this advice provider.
345    /// - The specified depth is either zero or greater than the depth of the Merkle tree identified
346    ///   by the specified root.
347    /// - Path to the node at the specified depth and index is not known to this advice provider.
348    pub fn get_merkle_path(
349        &self,
350        root: Word,
351        depth: Felt,
352        index: Felt,
353    ) -> Result<MerklePath, AdviceError> {
354        let index = NodeIndex::from_elements(&depth, &index)
355            .map_err(|_| AdviceError::InvalidMerkleTreeNodeIndex { depth, index })?;
356        self.store
357            .get_path(root, index)
358            .map(|value| value.path)
359            .map_err(AdviceError::MerkleStoreLookupFailed)
360    }
361
362    /// Updates a node at the specified depth and index in a Merkle tree with the specified root;
363    /// returns the Merkle path from the updated node to the new root, together with the new root.
364    ///
365    /// The tree is cloned prior to the update. Thus, the advice provider retains the original and
366    /// the updated tree.
367    ///
368    /// # Errors
369    /// Returns an error if:
370    /// - A Merkle tree for the specified root cannot be found in this advice provider.
371    /// - The specified depth is either zero or greater than the depth of the Merkle tree identified
372    ///   by the specified root.
373    /// - Path to the leaf at the specified index in the specified Merkle tree is not known to this
374    ///   advice provider.
375    pub fn update_merkle_node(
376        &mut self,
377        root: Word,
378        depth: Felt,
379        index: Felt,
380        value: Word,
381    ) -> Result<(MerklePath, Word), AdviceError> {
382        let node_index = NodeIndex::from_elements(&depth, &index)
383            .map_err(|_| AdviceError::InvalidMerkleTreeNodeIndex { depth, index })?;
384        self.store
385            .set_node(root, node_index, value)
386            .map(|root| (root.path, root.root))
387            .map_err(AdviceError::MerkleStoreUpdateFailed)
388    }
389
390    /// Creates a new Merkle tree in the advice provider by combining Merkle trees with the
391    /// specified roots. The root of the new tree is defined as `hash(left_root, right_root)`.
392    ///
393    /// After the operation, both the original trees and the new tree remains in the advice
394    /// provider (i.e., the input trees are not removed).
395    ///
396    /// It is not checked whether a Merkle tree for either of the specified roots can be found in
397    /// this advice provider.
398    pub fn merge_roots(&mut self, lhs: Word, rhs: Word) -> Result<Word, AdviceError> {
399        self.store.merge_roots(lhs, rhs).map_err(AdviceError::MerkleStoreMergeFailed)
400    }
401
402    /// Returns true if the Merkle root exists for the advice provider Merkle store.
403    pub fn has_merkle_root(&self, root: Word) -> bool {
404        self.store.get_node(root, NodeIndex::root()).is_ok()
405    }
406
407    /// Extends the [MerkleStore] with the given nodes.
408    pub fn extend_merkle_store<I>(&mut self, iter: I)
409    where
410        I: IntoIterator<Item = InnerNodeInfo>,
411    {
412        self.store.extend(iter);
413    }
414
415    // PRECOMPILE REQUESTS
416    // --------------------------------------------------------------------------------------------
417
418    /// Returns a reference to the precompile requests.
419    ///
420    /// Ordering is the same as the order in which requests are issued during execution. This
421    /// ordering is relied upon when recomputing the precompile sponge during verification.
422    pub fn precompile_requests(&self) -> &[PrecompileRequest] {
423        &self.pc_requests
424    }
425
426    /// Extends the precompile requests with the given entries.
427    pub fn extend_precompile_requests<I>(&mut self, iter: I)
428    where
429        I: IntoIterator<Item = PrecompileRequest>,
430    {
431        self.pc_requests.extend(iter);
432    }
433
434    /// Moves all accumulated precompile requests out of this provider, leaving it empty.
435    ///
436    /// Intended for proof packaging, where requests are serialized into the proof and no longer
437    /// needed in the provider after consumption.
438    pub fn take_precompile_requests(&mut self) -> Vec<PrecompileRequest> {
439        core::mem::take(&mut self.pc_requests)
440    }
441
442    // MUTATORS
443    // --------------------------------------------------------------------------------------------
444
445    /// Extends the contents of this instance with the contents of an `AdviceInputs`.
446    pub fn extend_from_inputs(&mut self, inputs: &AdviceInputs) -> Result<(), AdviceError> {
447        self.extend_stack(inputs.stack.iter().cloned())?;
448        self.extend_merkle_store(inputs.store.inner_nodes());
449        self.extend_map(&inputs.map)
450    }
451
452    /// Consumes `self` and return its parts (stack, map, store, precompile_requests).
453    ///
454    /// The returned stack vector is ordered from top (index 0) to bottom.
455    pub fn into_parts(self) -> (Vec<Felt>, AdviceMap, MerkleStore, Vec<PrecompileRequest>) {
456        (self.stack.into_iter().collect(), self.map, self.store, self.pc_requests)
457    }
458}
459
460impl From<AdviceInputs> for AdviceProvider {
461    fn from(inputs: AdviceInputs) -> Self {
462        let AdviceInputs { stack, map, store } = inputs;
463        Self {
464            stack: VecDeque::from(stack),
465            map,
466            store,
467            pc_requests: Vec::new(),
468        }
469    }
470}
471
472// ADVICE PROVIDER INTERFACE IMPLEMENTATION
473// ================================================================================================
474
475impl AdviceProviderInterface for AdviceProvider {
476    #[inline(always)]
477    fn pop_stack(&mut self) -> Result<Felt, AdviceError> {
478        self.pop_stack()
479    }
480
481    #[inline(always)]
482    fn pop_stack_word(&mut self) -> Result<Word, AdviceError> {
483        self.pop_stack_word()
484    }
485
486    #[inline(always)]
487    fn pop_stack_dword(&mut self) -> Result<[Word; 2], AdviceError> {
488        self.pop_stack_dword()
489    }
490
491    #[inline(always)]
492    fn get_merkle_path(
493        &self,
494        root: Word,
495        depth: Felt,
496        index: Felt,
497    ) -> Result<Option<MerklePath>, AdviceError> {
498        self.get_merkle_path(root, depth, index).map(Some)
499    }
500
501    #[inline(always)]
502    fn update_merkle_node(
503        &mut self,
504        root: Word,
505        depth: Felt,
506        index: Felt,
507        value: Word,
508    ) -> Result<Option<MerklePath>, AdviceError> {
509        self.update_merkle_node(root, depth, index, value).map(|(path, _)| Some(path))
510    }
511}