simplicity/bit_machine/
tracker.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Bit Machine Tracker
4//!
5//! This module provides traits for adding "trackers" to the bit machine execution
6//! and pruning algorithms, which can provide debugging output or control which
7//! branches are pruned. It also provides a couple example/utility trackers.
8//!
9//! It is a private module but all types and traits are re-exported above.
10
11use std::collections::HashSet;
12
13use crate::jet::Jet;
14use crate::node::Inner;
15use crate::{Ihr, RedeemNode, Value};
16
17/// Write frame of a terminal (childless) Simplicity program node.
18///
19/// When a terminal node of a program is encountered in the Bit Machine, it
20/// has a well-defined "output": the contents of the topmost write frame in
21/// the machine. In particular, for `witness` nodes this will be the witness
22/// data, for jets it will be the result of the jet, and so on.
23///
24/// For non-terminal nodes, the Bit Machine typically does some setup, then
25/// executes the nodes' children, then does some teardown. So at no point is
26/// there a well-defined "output" we can provide.
27#[derive(Debug, Clone)]
28pub enum NodeOutput<'m> {
29    /// Non-terminal node, which has no output.
30    NonTerminal,
31    /// Node was a jet which failed, i.e. aborted the program, and therefore
32    /// has no output.
33    JetFailed,
34    /// Node succeeded. This is its output frame.
35    Success(super::FrameIter<'m>),
36}
37
38/// An object which can be used to introspect the execution of the Bit Machine.
39///
40/// If this tracker records accesses to the left and right children of `Case` nodes, you
41/// may want to also implement [`PruneTracker`] so that this data can be used by
42/// [`RedeemNode::prune_with_tracker`] to prune the program. The most straightforward
43/// way to do this is to embed a [`SetTracker`] in your tracker and forward all the trait
44/// methods to that.
45pub trait ExecTracker<J: Jet> {
46    /// Called immediately after a specific node of the program is executed, but before
47    /// its children are executed.
48    ///
49    /// More precisely, this iterates through the through the Simplicity program tree in
50    /// *pre* ordering. That is, for the program `comp iden unit` the nodes will be visited
51    /// in the order `comp`, `iden`, `unit`.
52    ///
53    /// This method can be used for logging, to track left or right accesses of the children of a
54    /// `Case` node (to do this, call `input.peek_bit()`; false means left and true means right),
55    /// to extract debug information (which may be embedded in the hidden CMR in `AssertL`
56    /// and `AssertR` nodes, depending how the program was constructed), and so on.
57    ///
58    /// The provided arguments are:
59    ///   * `node` is the node which was just visited.
60    ///   * `input` is an iterator over the read frame when the node's execution began
61    ///   * for terminal nodes (`witness`, `unit`, `iden` and jets), `output` is an iterator
62    ///     the write frame after the node has executed. See [`NodeOutput`] for more information.
63    fn visit_node(&mut self, _node: &RedeemNode<J>, _input: super::FrameIter, _output: NodeOutput) {
64    }
65}
66
67pub trait PruneTracker<J: Jet>: ExecTracker<J> {
68    /// Returns true if the left branch of the of the `Case` node with the IHR `ihr` was taken.
69    fn contains_left(&self, ihr: Ihr) -> bool;
70
71    /// Returns true if the right branch of the of the `Case` node with the IHR `ihr` was taken.
72    fn contains_right(&self, ihr: Ihr) -> bool;
73}
74
75/// Tracker of executed left and right branches for each case node.
76#[derive(Clone, Debug, Default)]
77pub struct SetTracker {
78    left: HashSet<Ihr>,
79    right: HashSet<Ihr>,
80}
81
82impl<J: Jet> ExecTracker<J> for SetTracker {
83    fn visit_node<'d>(
84        &mut self,
85        node: &RedeemNode<J>,
86        mut input: super::FrameIter,
87        _output: NodeOutput,
88    ) {
89        match (node.inner(), input.next()) {
90            (Inner::AssertL(..) | Inner::Case(..), Some(false)) => {
91                self.left.insert(node.ihr());
92            }
93            (Inner::AssertR(..) | Inner::Case(..), Some(true)) => {
94                self.right.insert(node.ihr());
95            }
96            _ => {}
97        }
98    }
99}
100
101impl<J: Jet> PruneTracker<J> for SetTracker {
102    fn contains_left(&self, ihr: Ihr) -> bool {
103        self.left.contains(&ihr)
104    }
105
106    fn contains_right(&self, ihr: Ihr) -> bool {
107        self.right.contains(&ihr)
108    }
109}
110
111/// Tracker that does not do anything (noop).
112#[derive(Copy, Clone, Debug)]
113pub struct NoTracker;
114
115impl<J: Jet> ExecTracker<J> for NoTracker {
116    fn visit_node<'d>(
117        &mut self,
118        node: &RedeemNode<J>,
119        mut input: super::FrameIter,
120        output: NodeOutput,
121    ) {
122        if cfg!(test) {
123            // In unit tests, attempt to decode values from the frames, confirming that
124            // decoding works.
125            Value::from_padded_bits(&mut input, &node.arrow().source)
126                .expect("decoding input should work");
127            if let NodeOutput::Success(mut output) = output {
128                Value::from_padded_bits(&mut output, &node.arrow().target)
129                    .expect("decoding output should work");
130            }
131        }
132    }
133}
134
135/// Tracker that just outputs all its activity to stderr.
136#[derive(Clone, Debug, Default)]
137pub struct StderrTracker {
138    exec_count: usize,
139    inner: SetTracker,
140}
141
142impl StderrTracker {
143    /// Constructs a new empty [`StderrTracker`], ready for use.
144    pub fn new() -> Self {
145        Self::default()
146    }
147}
148
149impl<J: Jet> ExecTracker<J> for StderrTracker {
150    fn visit_node(&mut self, node: &RedeemNode<J>, input: super::FrameIter, output: NodeOutput) {
151        let input_val = Value::from_padded_bits(&mut input.clone(), &node.arrow().source)
152            .expect("input from bit machine will always be well-formed");
153        eprintln!(
154            "[{:4}] exec {:10} {}",
155            self.exec_count,
156            node.inner(),
157            node.arrow()
158        );
159        eprintln!("       input {input_val}");
160        match output.clone() {
161            NodeOutput::NonTerminal => { /* don't bother describing non-terminal output */ }
162            NodeOutput::JetFailed => eprintln!("    JET FAILED"),
163            NodeOutput::Success(mut output) => {
164                let output_val = Value::from_padded_bits(&mut output, &node.arrow().target)
165                    .expect("output from bit machine will always be well-formed");
166                eprintln!("       output {output_val}");
167            }
168        }
169
170        if let crate::node::Inner::AssertL(_, cmr) = node.inner() {
171            // SimplicityHL, when compiling in "debug mode", tags nodes by inserting
172            // synthetic AssertL nodes where the "cmr" is actually a key into a lookup
173            // table of debug information. An implementation of ExecTracker within
174            // the compiler itself might do a lookup here to output more useful
175            // information to the user.
176            eprintln!("      [debug] assertL CMR {cmr}");
177        }
178
179        ExecTracker::<J>::visit_node(&mut self.inner, node, input, output);
180        self.exec_count += 1;
181        eprintln!();
182    }
183}
184
185impl<J: Jet> PruneTracker<J> for StderrTracker {
186    fn contains_left(&self, ihr: Ihr) -> bool {
187        if PruneTracker::<J>::contains_left(&self.inner, ihr) {
188            true
189        } else {
190            eprintln!("Pruning unexecuted left child of IHR {ihr}");
191            false
192        }
193    }
194
195    fn contains_right(&self, ihr: Ihr) -> bool {
196        if PruneTracker::<J>::contains_right(&self.inner, ihr) {
197            true
198        } else {
199            eprintln!("Pruning unexecuted right child of IHR {ihr}");
200            false
201        }
202    }
203}