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