simplicity/bit_machine/
limits.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Bit Machine Resource Limits
4//!
5//! Implementation of the Bit Machine, without TCO, as TCO precludes some
6//! frame management optimizations which can be used to great benefit.
7//!
8
9use core::fmt;
10use std::error;
11
12/// The maximum number of cells needed by the bit machine.
13///
14/// This roughly corresponds to the maximum size of any type used by a
15/// program, in bits. In a blockchain context this should be limited by
16/// the transaction's weight budget.
17///
18/// The limit here is an absolute limit enforced by the library to avoid
19/// unbounded allocations.
20// This value must be less than usize::MAX / 2 to avoid panics in this module.
21const MAX_CELLS: usize = 2 * 1024 * 1024 * 1024 - 1;
22
23/// The maximum number of frames needed by the bit machine.
24///
25/// This roughly corresponds to the maximum depth of nested `comp` and
26/// `disconnect` combinators. In a blockchain context this should be
27/// limited by the transaction's weight budget.
28///
29/// The limit here is an absolute limit enforced by the library to avoid
30/// unbounded allocations.
31// This value must be less than usize::MAX / 2 to avoid panics in this module.
32const MAX_FRAMES: usize = 1024 * 1024;
33
34#[non_exhaustive]
35#[derive(Clone, Debug)]
36pub enum LimitError {
37    MaxCellsExceeded {
38        /// The number of cells needed by the program.
39        got: usize,
40        /// The maximum allowed number of cells.
41        max: usize,
42        /// A description of which cell count exceeded the limit.
43        bound: &'static str,
44    },
45    MaxFramesExceeded {
46        /// The number of frames needed by the program.
47        got: usize,
48        /// The maximum allowed number of frames.
49        max: usize,
50        /// A description of which frame count exceeded the limit.
51        bound: &'static str,
52    },
53}
54
55impl fmt::Display for LimitError {
56    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57        let (limit, got, max, bound) = match self {
58            LimitError::MaxCellsExceeded { got, max, bound } => ("cells", got, max, bound),
59            LimitError::MaxFramesExceeded { got, max, bound } => ("frames", got, max, bound),
60        };
61        write!(
62            f,
63            "maximum number of {} exceeded (needed {}, maximum {}) (bound: {})",
64            limit, got, max, bound,
65        )
66    }
67}
68impl error::Error for LimitError {}
69
70impl LimitError {
71    fn check_max_cells(got: usize, bound: &'static str) -> Result<(), Self> {
72        if got > MAX_CELLS {
73            Err(Self::MaxCellsExceeded {
74                got,
75                max: MAX_CELLS,
76                bound,
77            })
78        } else {
79            Ok(())
80        }
81    }
82
83    fn check_max_frames(got: usize, bound: &'static str) -> Result<(), Self> {
84        if got > MAX_FRAMES {
85            Err(Self::MaxFramesExceeded {
86                got,
87                max: MAX_CELLS,
88                bound,
89            })
90        } else {
91            Ok(())
92        }
93    }
94
95    /// Helper function to check every value and sum for being within bounds.
96    pub(super) fn check_program<J: crate::jet::Jet>(
97        program: &crate::RedeemNode<J>,
98    ) -> Result<(), Self> {
99        let source_ty_width = program.arrow().source.bit_width();
100        let target_ty_width = program.arrow().target.bit_width();
101        let bounds = program.bounds();
102
103        Self::check_max_cells(source_ty_width, "source type width")?;
104        Self::check_max_cells(target_ty_width, "target type width")?;
105        Self::check_max_cells(bounds.extra_cells, "extra cells")?;
106        Self::check_max_cells(
107            source_ty_width + target_ty_width,
108            "source + target type widths",
109        )?;
110        Self::check_max_cells(
111            source_ty_width + target_ty_width + bounds.extra_cells,
112            "source + target type widths + extra cells",
113        )?;
114
115        Self::check_max_frames(bounds.extra_frames, "extra frames")?;
116        Self::check_max_frames(
117            bounds.extra_frames + crate::analysis::IO_EXTRA_FRAMES,
118            "extra frames + fixed overhead",
119        )?;
120        Ok(())
121    }
122}