simplicityhl_core/trackers/
debug_tracker.rs

1//! Minimal tracker that collects dbg! logs into a map for assertions and inspection.
2
3use std::collections::HashMap;
4
5use simplicityhl::debug::DebugSymbols;
6use simplicityhl::either::Either;
7use simplicityhl::simplicity::Cmr;
8use simplicityhl::simplicity::bit_machine::ExecTracker;
9use simplicityhl::simplicity::ffi::ffi::UWORD;
10use simplicityhl::simplicity::jet::Elements;
11use simplicityhl::value::StructuralValue;
12
13/// Stores `dbg!` logs keyed by their label text.
14#[derive(Debug)]
15pub struct DebugTracker<'a> {
16    pub debug_symbols: &'a DebugSymbols,
17    pub debug_logs: HashMap<String, String>,
18}
19
20impl<'a> DebugTracker<'a> {
21    pub fn new(debug_symbols: &'a DebugSymbols) -> Self {
22        Self {
23            debug_symbols,
24            debug_logs: HashMap::new(),
25        }
26    }
27}
28
29impl<'a> ExecTracker<Elements> for DebugTracker<'a> {
30    fn track_left(&mut self, _: simplicityhl::simplicity::Ihr) {}
31
32    fn track_right(&mut self, _: simplicityhl::simplicity::Ihr) {}
33
34    fn track_jet_call(&mut self, _: &Elements, _: &[UWORD], _: &[UWORD], _: bool) {}
35
36    fn track_dbg_call(&mut self, cmr: &Cmr, value: simplicityhl::simplicity::Value) {
37        if let Some(tracked_call) = self.debug_symbols.get(cmr)
38            && let Some(Either::Right(debug_value)) =
39                tracked_call.map_value(&StructuralValue::from(value))
40        {
41            self.debug_logs.insert(
42                debug_value.text().to_string(),
43                debug_value.value().to_string(),
44            );
45        }
46    }
47
48    fn is_track_debug_enabled(&self) -> bool {
49        true
50    }
51}
52
53#[cfg(test)]
54mod test {
55    use super::*;
56
57    use anyhow::anyhow;
58
59    use std::sync::Arc;
60
61    use simplicityhl::simplicity::elements;
62    use simplicityhl::simplicity::elements::pset::PartiallySignedTransaction;
63    use simplicityhl::simplicity::elements::taproot::ControlBlock;
64    use simplicityhl::simplicity::hashes::Hash;
65
66    use simplicityhl::simplicity::jet::elements::ElementsEnv;
67    use simplicityhl::{Arguments, TemplateProgram, WitnessValues};
68
69    pub const MOCKED_PROGRAM_SOURCE: &str = r#"
70    fn main() {
71        let a: u64 = 1;
72        let b: u64 = dbg!(a);
73        assert!(true);
74    }
75    "#;
76
77    #[test]
78    fn test_debug_tracker() -> anyhow::Result<()> {
79        let program = TemplateProgram::new(MOCKED_PROGRAM_SOURCE).map_err(|e| anyhow!("{}", e))?;
80        let program = program
81            .instantiate(Arguments::default(), true)
82            .map_err(|e| anyhow!("{}", e))?;
83
84        let tx = PartiallySignedTransaction::new_v2();
85
86        let env = ElementsEnv::new(
87            Arc::new(tx.extract_tx()?),
88            vec![],
89            0,
90            Cmr::from_byte_array([0; 32]),
91            ControlBlock::from_slice(&[0xc0; 33])?,
92            None,
93            elements::BlockHash::all_zeros(),
94        );
95
96        let satisfied = program
97            .satisfy(WitnessValues::default())
98            .map_err(|e| anyhow!("{}", e))?;
99
100        let pruned = match satisfied.redeem().prune(&env) {
101            Ok(pruned) => pruned,
102            Err(e) => return Err(e.into()),
103        };
104        let mut mac = match simplicityhl::simplicity::BitMachine::for_program(&pruned) {
105            Ok(mac) => mac,
106            Err(e) => return Err(e.into()),
107        };
108
109        let mut tracker = DebugTracker::new(satisfied.debug_symbols());
110        match mac.exec_with_tracker(&pruned, &env, &mut tracker) {
111            Ok(_) => {}
112            Err(e) => return Err(e.into()),
113        };
114
115        let logs = tracker.debug_logs.iter().collect::<Vec<_>>();
116        assert_eq!(logs.len(), 1);
117        assert_eq!(logs[0].0, "a");
118        assert_eq!(logs[0].1, "1");
119
120        Ok(())
121    }
122}