Skip to main content

miden_core/deferred/
mod.rs

1//! Content-addressed deferred computation for VM hints.
2//!
3//! Deferred events let programs commit opaque statements during execution and leave their
4//! semantic checks to installed [`Precompile`]s. The framework stores those commitments as a DAG
5//! of [`Node`]s and a deferred root commitment that verifies by evaluating every logged statement
6//! to TRUE.
7//!
8//! `miden-core` owns the data model, registry, state, and wire validation; the processor only
9//! provides system-event plumbing.
10
11mod node;
12mod precompile;
13mod precompile_registry;
14mod state;
15mod wire;
16
17use alloc::boxed::Box;
18
19pub use node::{DataChunk, Digest, Node, NodeType, Payload, TRUE_DIGEST, Tag};
20pub use precompile::{Precompile, precompile_id};
21pub use precompile_registry::PrecompileRegistry;
22pub use state::{DeferredContext, DeferredState};
23pub use wire::{DeferredStateWire, IntegrityError, TRUE_INDEX, WireEntry};
24
25use crate::Word;
26
27/// The deferred root committed in public inputs.
28pub type DeferredRoot = Digest;
29
30/// Fixed capacity word used to domain-separate deferred root folds.
31pub const DEFERRED_ROOT_DOMAIN: Word = Word::new(Tag::AND.as_word());
32
33/// Default maximum approximate number of field elements allowed in deferred state.
34pub const DEFAULT_MAX_DEFERRED_ELEMENTS: usize = 1 << 20;
35
36/// Folds a verified deferred statement into the rolling deferred root.
37pub fn fold_deferred_root(root: DeferredRoot, statement: Digest) -> DeferredRoot {
38    Node::and(root, statement).digest()
39}
40
41/// Small helper for tests and trace plumbing that need to advance a deferred root incrementally.
42#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
43pub struct DeferredRootTracker {
44    root: DeferredRoot,
45}
46
47impl DeferredRootTracker {
48    pub fn new() -> Self {
49        Self::default()
50    }
51
52    pub fn from_root(root: DeferredRoot) -> Self {
53        Self { root }
54    }
55
56    pub fn root(&self) -> DeferredRoot {
57        self.root
58    }
59
60    pub fn record_statement(&mut self, statement: Digest) {
61        self.root = fold_deferred_root(self.root, statement);
62    }
63}
64
65// ERROR
66// ================================================================================================
67
68/// Coarse deferred-framework failures shared by deferred state and precompile evaluation.
69#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
70pub enum DeferredError {
71    #[error("invalid or unknown deferred tag")]
72    InvalidTag,
73    #[error("referenced digest is not present in deferred state")]
74    MissingNode,
75    #[error("conflicting node definition for digest")]
76    ConflictingNode,
77    #[error("payload is not valid for the given tag")]
78    InvalidPayload,
79    #[error("equality assertion failed")]
80    AssertionFailed,
81    #[error("deferred insertion requires {num_elements} elements but only {max} remain")]
82    DeferredStateTooLarge { num_elements: usize, max: usize },
83    #[error("operation is not supported by this handler")]
84    Unsupported,
85    #[error("invalid deferred root transition: expected {expected:?}, got {actual:?}")]
86    InvalidDeferredRootTransition { expected: Digest, actual: Digest },
87}
88
89/// Errors produced while evaluating deferred nodes through precompiles.
90#[derive(Debug, Clone, thiserror::Error)]
91pub enum PrecompileError {
92    /// A referenced child digest is not present in `DeferredState.nodes`.
93    #[error("deferred DAG is missing a node referenced during evaluation")]
94    MissingNode,
95
96    /// A tag is unknown or its payload shape is invalid for the decoded node type.
97    #[error("node failed precompile validation")]
98    InvalidNode,
99
100    /// A precompile predicate evaluated to false.
101    #[error("deferred assertion failed: values disagree")]
102    AssertionFailed,
103
104    /// A framework-level error surfaced by a precompile evaluation.
105    #[error(transparent)]
106    Other(#[from] DeferredError),
107
108    /// Adds the owning precompile's name to a tag or evaluation failure.
109    ///
110    /// Registry construction errors are setup-time panics and are not represented here.
111    #[error("precompile `{name}`: {source}")]
112    Precompile {
113        name: &'static str,
114        source: Box<PrecompileError>,
115    },
116}
117
118impl PrecompileError {
119    /// Returns the underlying failure without registry attribution wrappers.
120    pub fn root(&self) -> &PrecompileError {
121        match self {
122            PrecompileError::Precompile { source, .. } => source.root(),
123            other => other,
124        }
125    }
126
127    pub(crate) fn with_precompile(name: &'static str, source: PrecompileError) -> Self {
128        Self::Precompile { name, source: Box::new(source) }
129    }
130}