Skip to main content

miden_node_store/state/view/
scoped.rs

1//! Proof-of-validation block-number types issued by [`StateView`](super::StateView).
2//!
3//! Tip-scoped database queries take these types instead of raw block numbers, so a query whose
4//! bound was not validated against a state view's tip is not expressible: the only constructors
5//! live on [`StateView`](super::StateView), which checks the bound against its pinned snapshot.
6//!
7//! Chain tips are monotonic, so a value issued by an older view remains valid for any later
8//! state — holding one across view lifetimes is sound, if pointless.
9
10use std::ops::{Deref, RangeInclusive};
11
12use miden_protocol::block::BlockNumber;
13
14/// A block number proven to be at or below the issuing view's chain tip.
15#[derive(Debug, Clone, Copy)]
16pub struct ScopedBlockNum(BlockNumber);
17
18impl ScopedBlockNum {
19    /// Issued by [`StateView`](super::StateView) after validating the bound against its tip.
20    pub(super) fn new(block_num: BlockNumber) -> Self {
21        Self(block_num)
22    }
23
24    /// Constructs a scoped block number without validation.
25    ///
26    /// Test-only: lets database tests exercise scoped queries without a running state.
27    #[cfg(test)]
28    pub(crate) fn new_unchecked(block_num: BlockNumber) -> Self {
29        Self(block_num)
30    }
31
32    /// Derives a scoped range ending at this validated block number.
33    ///
34    /// Sound for any `start` because only a range's upper bound carries the proof obligation.
35    /// A `start` beyond this block number would however produce an empty range, which the range
36    /// queries reject as an invalid block range. Used for paginating over a validated bound in
37    /// sub-ranges.
38    pub(crate) fn range_from(self, start: BlockNumber) -> ScopedBlockRange {
39        debug_assert!(start <= self.0, "derived range start {start} exceeds its end {}", self.0);
40        ScopedBlockRange(start..=self.0)
41    }
42}
43
44/// The validated block number is read by dereferencing (`*scoped`); [`DerefMut`] is deliberately
45/// not implemented, as mutating the inner value would invalidate the proof.
46///
47/// [`DerefMut`]: std::ops::DerefMut
48impl Deref for ScopedBlockNum {
49    type Target = BlockNumber;
50
51    fn deref(&self) -> &BlockNumber {
52        &self.0
53    }
54}
55
56/// A block range whose upper bound is proven to be at or below the issuing view's chain tip.
57#[derive(Debug, Clone)]
58pub struct ScopedBlockRange(RangeInclusive<BlockNumber>);
59
60impl ScopedBlockRange {
61    /// Issued by [`StateView`](super::StateView) after validating the range against its tip.
62    pub(super) fn new(range: RangeInclusive<BlockNumber>) -> Self {
63        Self(range)
64    }
65
66    /// Returns the start of the validated range.
67    pub(crate) fn start(&self) -> BlockNumber {
68        *self.0.start()
69    }
70
71    /// Returns the end of the validated range.
72    pub(crate) fn end(&self) -> BlockNumber {
73        *self.0.end()
74    }
75
76    /// Returns the end of the validated range as a scoped block number.
77    ///
78    /// Sound because the range's upper bound is exactly what its proof covers.
79    pub(crate) fn scoped_end(&self) -> ScopedBlockNum {
80        ScopedBlockNum(*self.0.end())
81    }
82
83    /// Returns the validated range.
84    pub(crate) fn into_inner(self) -> RangeInclusive<BlockNumber> {
85        self.0
86    }
87}