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}