simplicity/
analysis.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use crate::jet::Jet;
4use std::{cmp, fmt};
5
6use crate::value::Word;
7#[cfg(feature = "elements")]
8use elements::encode::Encodable;
9#[cfg(feature = "elements")]
10use std::{convert::TryFrom, io};
11
12/// Copy of [`bitcoin::Weight`] that uses [`u32`] instead of [`u64`].
13///
14/// This struct is useful for conversions between [`bitcoin::Weight`]
15/// (which uses [`u64`]) and [`Cost`] (which uses [`u32`]).
16#[cfg(feature = "bitcoin")]
17#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
18struct U32Weight(u32);
19
20#[cfg(feature = "bitcoin")]
21impl std::ops::Sub for U32Weight {
22    type Output = Self;
23
24    fn sub(self, rhs: Self) -> Self::Output {
25        Self(self.0.saturating_sub(rhs.0))
26    }
27}
28
29#[cfg(feature = "bitcoin")]
30impl From<bitcoin::Weight> for U32Weight {
31    fn from(value: bitcoin::Weight) -> Self {
32        Self(u32::try_from(value.to_wu()).unwrap_or(u32::MAX))
33    }
34}
35
36#[cfg(feature = "bitcoin")]
37impl From<U32Weight> for bitcoin::Weight {
38    fn from(value: U32Weight) -> Self {
39        bitcoin::Weight::from_wu(u64::from(value.0))
40    }
41}
42
43/// CPU cost of a Simplicity expression.
44///
45/// The cost is measured in milli weight units
46/// and can be converted into weight units using the appropriate method.
47///
48/// Roughly speaking, the operational semantics of a combinator
49/// on the Bit Machine determine its cost.
50///
51/// First, every combinator has a fixed overhead cost.
52/// Frame allocations, copy and write operations cost proportional
53/// to the number of allocated or written bits.
54/// Frame moves / drops or cursor moves are one-step operations
55/// that are covered by the overhead.
56///
57/// The cost of a program is compared to its _budget_.
58/// A program is valid if it does not exceed its budget.
59///
60/// The budget is the size of the witness stack
61/// of the transaction input that includes the program.
62/// Users pay for their Simplicity programs in terms of fees
63/// which are based on transaction size, like normal Tapscript.
64///
65/// Programs that are CPU-heavy need to be padded
66/// so that the witness stack provides a large-enough budget.
67#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
68pub struct Cost(u32);
69
70impl Cost {
71    /// Overhead constant.
72    ///
73    /// Every combinator that is executed has this overhead added to its cost.
74    const OVERHEAD: Self = Cost(100);
75
76    /// Cost of combinators that are never executed.
77    ///
78    /// **This should only be used for `fail` nodes!**
79    const NEVER_EXECUTED: Self = Cost(0);
80
81    /// Maximum cost allowed by consensus.
82    ///
83    /// This is equal to the maximum budget that any program
84    /// can have inside a Taproot transaction:
85    /// 4 million weight units plus 50 free weight units for validation.
86    ///
87    /// This assumes a block that consists of a single transaction
88    /// which in turn consists of nothing but its witness stack.
89    ///
90    /// Transactions include other data besides the witness stack.
91    /// Also, transaction may have multiple inputs and
92    /// blocks usually include multiple transactions.
93    /// This means that the maximum budget is an unreachable upper bound.
94    pub const CONSENSUS_MAX: Self = Cost(4_000_050_000);
95
96    /// Return the cost of a type with the given bit width.
97    pub const fn of_type(bit_width: usize) -> Self {
98        // Cast safety: bit width cannot be more than 2^32 - 1
99        Cost(bit_width as u32)
100    }
101
102    /// Convert the given milli weight units into cost.
103    pub const fn from_milliweight(milliweight: u32) -> Self {
104        Cost(milliweight)
105    }
106
107    /// Return whether the cost is allowed by consensus.
108    ///
109    /// This means the cost is within the maximum budget
110    /// that any program inside a Taproot transaction can have.
111    pub fn is_consensus_valid(self) -> bool {
112        self <= Self::CONSENSUS_MAX
113    }
114
115    /// Return the budget of the given script witness of a transaction output.
116    ///
117    /// The script witness is passed as `&Vec<Vec<u8>>` in order to use
118    /// the consensus encoding implemented for this type.
119    #[cfg(feature = "elements")]
120    fn get_budget(script_witness: &Vec<Vec<u8>>) -> U32Weight {
121        let mut sink = io::sink();
122        let witness_stack_serialized_len = script_witness
123            .consensus_encode(&mut sink)
124            .expect("writing to sink never fails");
125        let budget = u32::try_from(witness_stack_serialized_len)
126            .expect("Serialized witness stack must be shorter than 2^32 elements")
127            .saturating_add(50);
128        U32Weight(budget)
129    }
130
131    /// Return whether the cost is within the budget of
132    /// the given script witness of a transaction input.
133    ///
134    /// The script witness is passed as `&Vec<Vec<u8>>` in order to use
135    /// the consensus encoding implemented for this type.
136    #[cfg(feature = "elements")]
137    pub fn is_budget_valid(self, script_witness: &Vec<Vec<u8>>) -> bool {
138        let budget = Self::get_budget(script_witness);
139        self.0 <= budget.0.saturating_mul(1000)
140    }
141
142    /// Return the annex bytes that are required as padding
143    /// so the transaction input has enough budget to cover the cost.
144    ///
145    /// The first annex byte is 0x50, as defined in BIP 341.
146    /// The following padding bytes are 0x00.
147    #[cfg(feature = "elements")]
148    pub fn get_padding(self, script_witness: &Vec<Vec<u8>>) -> Option<Vec<u8>> {
149        let weight = U32Weight::from(self);
150        let budget = Self::get_budget(script_witness);
151        if weight <= budget {
152            return None;
153        }
154
155        // Two bytes are automatically added to the encoded witness stack by adding the annex:
156        //
157        // 1. The encoded annex starts with the annex byte length
158        // 2. The first annex byte is always 0x50
159        //
160        // The remaining padding is done by adding (zero) bytes to the annex.
161        let required_padding = weight - budget - U32Weight(2);
162        let padding_len = required_padding.0 as usize; // cast safety: 32-bit machine or higher
163        let annex_bytes: Vec<u8> = std::iter::once(0x50)
164            .chain(std::iter::repeat(0x00).take(padding_len))
165            .collect();
166
167        Some(annex_bytes)
168    }
169}
170
171impl fmt::Display for Cost {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        fmt::Display::fmt(&self.0, f)
174    }
175}
176
177impl std::ops::Add for Cost {
178    type Output = Self;
179
180    fn add(self, rhs: Self) -> Self::Output {
181        Cost(self.0.saturating_add(rhs.0))
182    }
183}
184
185#[cfg(feature = "bitcoin")]
186impl From<U32Weight> for Cost {
187    fn from(value: U32Weight) -> Self {
188        Self(value.0.saturating_mul(1000))
189    }
190}
191
192#[cfg(feature = "bitcoin")]
193impl From<Cost> for U32Weight {
194    fn from(value: Cost) -> Self {
195        // Saturating addition to avoid panic at numeric bounds
196        // This results in a slightly different rounding for cost values close to u32::MAX.
197        // These values are strictly larger than CONSENSUS_MAX and are of no significance.
198        Self(value.0.saturating_add(999) / 1000)
199    }
200}
201
202#[cfg(feature = "bitcoin")]
203impl From<bitcoin::Weight> for Cost {
204    fn from(value: bitcoin::Weight) -> Self {
205        Self(U32Weight::from(value).0.saturating_mul(1000))
206    }
207}
208
209#[cfg(feature = "bitcoin")]
210impl From<Cost> for bitcoin::Weight {
211    fn from(value: Cost) -> Self {
212        bitcoin::Weight::from_wu(u64::from(U32Weight::from(value).0))
213    }
214}
215
216/// Bounds on the resources required by a node during execution on the Bit Machine
217#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
218pub struct NodeBounds {
219    /// Upper bound on the required number of cells (bits).
220    /// The root additionally requires the bit width of its source and target type (input, output)
221    pub extra_cells: usize,
222    /// Upper bound on the required number of frames (sum of read and write frames).
223    /// The root additionally requires two frames (input, output)
224    pub extra_frames: usize,
225    /// CPU cost
226    pub cost: Cost,
227}
228
229impl NodeBounds {
230    const NOP: Self = NodeBounds {
231        extra_cells: 0,
232        extra_frames: 0,
233        cost: Cost::OVERHEAD,
234    };
235    const NEVER_EXECUTED: Self = NodeBounds {
236        extra_cells: 0,
237        extra_frames: 0,
238        cost: Cost::NEVER_EXECUTED,
239    };
240
241    fn from_child(child: Self) -> Self {
242        NodeBounds {
243            extra_cells: child.extra_cells,
244            extra_frames: child.extra_frames,
245            cost: Cost::OVERHEAD + child.cost,
246        }
247    }
248
249    /// Node bounds for an `iden` node
250    pub fn iden(target_type: usize) -> NodeBounds {
251        NodeBounds {
252            extra_cells: 0,
253            extra_frames: 0,
254            cost: Cost::OVERHEAD + Cost::of_type(target_type),
255        }
256    }
257
258    /// Node bounds for a `unit` node
259    pub const fn unit() -> NodeBounds {
260        NodeBounds::NOP
261    }
262
263    /// Node bounds for an `injl` node
264    pub fn injl(child: Self) -> NodeBounds {
265        Self::from_child(child)
266    }
267
268    /// Node bounds for an `injr` node
269    pub fn injr(child: Self) -> NodeBounds {
270        Self::from_child(child)
271    }
272
273    /// Node bounds for a `take` node
274    pub fn take(child: Self) -> NodeBounds {
275        Self::from_child(child)
276    }
277
278    /// Node bounds for a `drop` node
279    pub fn drop(child: Self) -> NodeBounds {
280        Self::from_child(child)
281    }
282
283    /// Node bounds for a `comp` node
284    pub fn comp(left: Self, right: Self, mid_ty_bit_width: usize) -> NodeBounds {
285        NodeBounds {
286            extra_cells: mid_ty_bit_width + cmp::max(left.extra_cells, right.extra_cells),
287            extra_frames: 1 + cmp::max(left.extra_frames, right.extra_frames),
288            cost: Cost::OVERHEAD + Cost::of_type(mid_ty_bit_width) + left.cost + right.cost,
289        }
290    }
291
292    /// Node bounds for a `case` node
293    pub fn case(left: Self, right: Self) -> NodeBounds {
294        NodeBounds {
295            extra_cells: cmp::max(left.extra_cells, right.extra_cells),
296            extra_frames: cmp::max(left.extra_frames, right.extra_frames),
297            cost: Cost::OVERHEAD + cmp::max(left.cost, right.cost),
298        }
299    }
300
301    /// Node bounds for a `assertl` node
302    pub fn assertl(child: Self) -> NodeBounds {
303        Self::from_child(child)
304    }
305
306    /// Node bounds for a `assertr` node
307    pub fn assertr(child: Self) -> NodeBounds {
308        Self::from_child(child)
309    }
310
311    /// Node bounds for a `pair` node
312    pub fn pair(left: Self, right: Self) -> NodeBounds {
313        NodeBounds {
314            extra_cells: cmp::max(left.extra_cells, right.extra_cells),
315            extra_frames: cmp::max(left.extra_frames, right.extra_frames),
316            cost: Cost::OVERHEAD + left.cost + right.cost,
317        }
318    }
319
320    // disconnect, jet, witness, word
321    /// Node bounds for a `disconnect` node
322    pub fn disconnect(
323        left: Self,
324        right: Self,
325        left_target_b_bit_width: usize, // bit width of B in (b x C) target type
326        left_source_bit_width: usize,
327        left_target_bit_width: usize,
328    ) -> NodeBounds {
329        NodeBounds {
330            extra_cells: left_source_bit_width
331                + left_target_bit_width
332                + cmp::max(left.extra_cells, right.extra_cells),
333            extra_frames: 2 + cmp::max(left.extra_frames, right.extra_frames),
334            cost: Cost::OVERHEAD
335                + Cost::of_type(left_source_bit_width)
336                + Cost::of_type(left_source_bit_width)
337                + Cost::of_type(left_target_bit_width)
338                + Cost::of_type(left_target_b_bit_width)
339                + left.cost
340                + right.cost,
341        }
342    }
343
344    /// Node bounds for an arbitrary jet node
345    pub fn witness(target_ty_bit_width: usize) -> NodeBounds {
346        NodeBounds {
347            extra_cells: target_ty_bit_width,
348            extra_frames: 0,
349            cost: Cost::OVERHEAD + Cost::of_type(target_ty_bit_width),
350        }
351    }
352
353    /// Node bounds for an arbitrary jet node
354    pub fn jet<J: Jet>(jet: J) -> NodeBounds {
355        NodeBounds {
356            extra_cells: 0,
357            extra_frames: 0,
358            cost: Cost::OVERHEAD + jet.cost(),
359        }
360    }
361
362    /// Node bounds for an arbitrary constant word node
363    pub fn const_word(word: &Word) -> NodeBounds {
364        NodeBounds {
365            extra_cells: 0,
366            extra_frames: 0,
367            cost: Cost::OVERHEAD + Cost::of_type(word.len()),
368        }
369    }
370
371    /// Node bounds for a `fail` node.
372    ///
373    /// This is a bit of a silly constructor because if a `fail` node is actually
374    /// executed in the bit machine, it will fail instantly, while if it *isn't*
375    /// executed, it will fail the "no unexecuted nodes" check. But to analyze
376    /// arbitrary programs, we need it.
377    pub const fn fail() -> NodeBounds {
378        NodeBounds::NEVER_EXECUTED
379    }
380}
381
382/// Number of frames required for the input and output of a Simplicity expression
383pub(crate) const IO_EXTRA_FRAMES: usize = 2;
384
385#[cfg(test)]
386mod tests {
387    use super::*;
388    use simplicity_sys::ffi::bounded::cost_overhead;
389
390    #[test]
391    fn test_overhead() {
392        // Check that C overhead is same OVERHEAD
393        assert_eq!(Cost::OVERHEAD.0, cost_overhead());
394    }
395
396    #[test]
397    #[cfg(feature = "bitcoin")]
398    fn cost_to_weight() {
399        let test_vectors = vec![
400            (Cost::NEVER_EXECUTED, 0),
401            (Cost::from_milliweight(1), 1),
402            (Cost::from_milliweight(999), 1),
403            (Cost::from_milliweight(1_000), 1),
404            (Cost::from_milliweight(1_001), 2),
405            (Cost::from_milliweight(1_999), 2),
406            (Cost::from_milliweight(2_000), 2),
407            (Cost::CONSENSUS_MAX, 4_000_050),
408        ];
409
410        for (cost, expected_weight) in test_vectors {
411            let converted_cost = U32Weight::from(cost);
412            let expected_weight = U32Weight(expected_weight);
413            assert_eq!(converted_cost, expected_weight);
414        }
415    }
416
417    #[test]
418    #[cfg(feature = "elements")]
419    fn test_get_padding() {
420        // The budget of the empty witness stack is 51 WU:
421        //
422        // 1. 50 WU of free signature operations
423        // 2. 1 WU for the length byte of the witness stack
424        let empty = 51_000;
425
426        // The encoded annex starts with a length byte, so remove one padding byte from the annex
427        let test_vectors = vec![
428            (Cost::from_milliweight(0), vec![], None),
429            (Cost::from_milliweight(empty), vec![], None),
430            (Cost::from_milliweight(empty + 1), vec![], Some(1)),
431            (Cost::from_milliweight(empty + 2_000), vec![], Some(1)),
432            (Cost::from_milliweight(empty + 2_001), vec![], Some(2)),
433            (Cost::from_milliweight(empty + 3_000), vec![], Some(2)),
434            (Cost::from_milliweight(empty + 3_001), vec![], Some(3)),
435            (Cost::from_milliweight(empty + 4_000), vec![], Some(3)),
436            (Cost::from_milliweight(empty + 4_001), vec![], Some(4)),
437            (Cost::from_milliweight(empty + 50_000), vec![], Some(49)),
438        ];
439
440        for (cost, mut witness, maybe_padding) in test_vectors {
441            match maybe_padding {
442                None => {
443                    assert!(cost.is_budget_valid(&witness));
444                    assert!(cost.get_padding(&witness).is_none());
445                }
446                Some(expected_annex_len) => {
447                    assert!(!cost.is_budget_valid(&witness));
448
449                    let annex_bytes = cost.get_padding(&witness).expect("not enough budget");
450                    assert_eq!(expected_annex_len, annex_bytes.len());
451                    witness.extend(std::iter::once(annex_bytes));
452                    assert!(cost.is_budget_valid(&witness));
453
454                    witness.pop();
455                    assert!(!cost.is_budget_valid(&witness), "Padding must be minimal");
456                }
457            }
458        }
459    }
460}