Skip to content

Commit 3137945

Browse files
committed
tracker: use generic JetHL instead of hardcoding Elements
1 parent 16fae19 commit 3137945

4 files changed

Lines changed: 105 additions & 18 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Unreleased
22

33
* Remove ModuleProgram and the parsing of Arguments and Witnesses from the core compiler [#323](https://github.com/BlockstreamResearch/SimplicityHL/pull/323)
4+
* Deprecate the `DefaultTracker::new` [#355](https://github.com/BlockstreamResearch/SimplicityHL/pull/355)
45

56
# 0.6.0-rc.0 - 2026-04-24
67

src/ast.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,8 @@ pub trait JetHinter: std::fmt::Debug + Send + Sync {
504504
fn parse_jet(&self, name: &str) -> Option<Box<dyn JetHL>>;
505505
/// Constructs an instance of the `verify` jet.
506506
fn construct_verify(&self) -> Box<dyn JetHL>;
507+
/// Converts a runtime Simplicity jet back into this hinter's high-level jet.
508+
fn conjure(&self, jet: &dyn Jet) -> Option<Box<dyn JetHL>>;
507509

508510
/// Clones the `JetHinter` into a boxed trait object.
509511
fn clone_box(&self) -> Box<dyn JetHinter>;
@@ -531,6 +533,12 @@ macro_rules! impl_jet_hinter {
531533
Box::new($jet_type::Verify)
532534
}
533535

536+
fn conjure(&self, jet: &dyn Jet) -> Option<Box<dyn JetHL>> {
537+
jet.as_any()
538+
.downcast_ref::<$jet_type>()
539+
.map(|jet| Box::new(*jet) as Box<dyn JetHL>)
540+
}
541+
534542
fn clone_box(&self) -> Box<dyn JetHinter> {
535543
Box::new(Self)
536544
}

src/tracker.rs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use simplicity::bit_machine::{ExecTracker, FrameIter, NodeOutput, PruneTracker, SetTracker};
2-
use simplicity::jet::{Elements, Jet};
32
use simplicity::node::Inner;
43
use simplicity::{Ihr, RedeemNode, Value as SimValue};
54

65
use crate::array::Unfolder;
6+
use crate::ast::{ElementsJetHinter, JetHinter};
77
use crate::debug::{DebugSymbols, TrackedCallName};
88
use crate::either::Either;
9-
use crate::jet::{source_type, target_type};
9+
use crate::jet::{source_type, target_type, JetHL};
1010
use crate::str::AliasName;
1111
use crate::types::AliasedType;
1212
use crate::value::StructuralValue;
@@ -22,7 +22,7 @@ type DebugSink<'a> = Box<dyn FnMut(&str, &Value) + 'a>;
2222
///
2323
/// Arguments are: the jet that was executed, its input arguments (if successfully parsed),
2424
/// and the result (`None` if the jet failed).
25-
type JetTraceSink<'a> = Box<dyn FnMut(Elements, Option<&[Value]>, Option<Value>) + 'a>;
25+
type JetTraceSink<'a> = Box<dyn FnMut(&dyn JetHL, Option<&[Value]>, Option<Value>) + 'a>;
2626

2727
/// Callback signature for receiving warnings during execution.
2828
type WarningSink<'a> = Box<dyn Fn(&str) + 'a>;
@@ -43,7 +43,7 @@ fn default_debug_sink(label: &str, value: &Value) {
4343
}
4444

4545
/// Default jet trace sink that prints jet calls to stderr.
46-
fn default_jet_trace_sink(jet: Elements, args: Option<&[Value]>, result: Option<Value>) {
46+
fn default_jet_trace_sink(jet: &dyn JetHL, args: Option<&[Value]>, result: Option<Value>) {
4747
print!("{jet:?}(");
4848
if let Some(args) = args {
4949
for (i, arg) in args.iter().enumerate() {
@@ -85,6 +85,7 @@ fn default_warning_sink(message: &str) {
8585
/// ```
8686
pub struct DefaultTracker<'a> {
8787
debug_symbols: &'a DebugSymbols,
88+
jet_hinter: Box<dyn JetHinter>,
8889
debug_sink: Option<DebugSink<'a>>,
8990
jet_trace_sink: Option<JetTraceSink<'a>>,
9091
warning_sink: Option<WarningSink<'a>>,
@@ -93,9 +94,20 @@ pub struct DefaultTracker<'a> {
9394

9495
impl<'a> DefaultTracker<'a> {
9596
/// Creates a new tracker bound to the given debug symbol table.
97+
///
98+
/// This constructor is deprecated in favor of more flexible tracker setup.
99+
/// The deprecation is necessary to show the direction in which the SimplicityHL is moving
100+
/// (i.e. different targets and support for possible custom Jets)
101+
#[deprecated(since = "0.6.0", note = "Please use `build` instead")]
96102
pub fn new(debug_symbols: &'a DebugSymbols) -> Self {
103+
Self::build(debug_symbols, Box::new(ElementsJetHinter::new()))
104+
}
105+
106+
/// Creates a new tracker bound to a custom jet family.
107+
pub fn build(debug_symbols: &'a DebugSymbols, jet_hinter: Box<dyn JetHinter>) -> Self {
97108
Self {
98109
debug_symbols,
110+
jet_hinter,
99111
debug_sink: None,
100112
jet_trace_sink: None,
101113
warning_sink: None,
@@ -120,7 +132,7 @@ impl<'a> DefaultTracker<'a> {
120132
/// Enables forwarding of jet call traces to the provided sink.
121133
pub fn with_jet_trace_sink<F>(mut self, sink: F) -> Self
122134
where
123-
F: FnMut(Elements, Option<&[Value]>, Option<Value>) + 'a,
135+
F: FnMut(&dyn JetHL, Option<&[Value]>, Option<Value>) + 'a,
124136
{
125137
self.jet_trace_sink = Some(Box::new(sink));
126138
self
@@ -175,7 +187,7 @@ impl<'a> DefaultTracker<'a> {
175187
fn handle_jet(
176188
&mut self,
177189
node: &RedeemNode,
178-
jet: Elements,
190+
jet: &dyn JetHL,
179191
input: &FrameIter,
180192
output: &NodeOutput,
181193
) {
@@ -208,11 +220,11 @@ impl<'a> DefaultTracker<'a> {
208220
}
209221

210222
/// Parses the result of a jet execution from the output frame.
211-
fn parse_jet_result(node: &RedeemNode, jet: Elements, output: &NodeOutput) -> Option<Value> {
223+
fn parse_jet_result(node: &RedeemNode, jet: &dyn JetHL, output: &NodeOutput) -> Option<Value> {
212224
match output.clone() {
213225
NodeOutput::Success(mut output_frame) => {
214226
let target_ty = &node.arrow().target;
215-
let jet_target_ty = resolve_jet_type(&target_type(&jet));
227+
let jet_target_ty = resolve_jet_type(&target_type(jet));
216228

217229
let output_value = SimValue::from_padded_bits(&mut output_frame, target_ty)
218230
.expect("output from bit machine is always well-formed");
@@ -303,10 +315,10 @@ impl ExecTracker for DefaultTracker<'_> {
303315
fn visit_node(&mut self, node: &RedeemNode, input: FrameIter, output: NodeOutput) {
304316
match node.inner() {
305317
Inner::Jet(jet) => {
306-
if let Some(&elements_jet) = jet.as_any().downcast_ref::<Elements>() {
307-
self.handle_jet(node, elements_jet, &input, &output);
318+
if let Some(jet) = self.jet_hinter.conjure(jet.as_ref()) {
319+
self.handle_jet(node, jet.as_ref(), &input, &output);
308320
} else {
309-
panic!("Expected an Elements jet, found a different type of jet: {jet:?}");
321+
self.warn(&format!("Unexpected jet type for tracker: {jet:?}"));
310322
}
311323
}
312324
Inner::AssertL(_, cmr) => self.handle_debug(node, &input, cmr),
@@ -318,8 +330,8 @@ impl ExecTracker for DefaultTracker<'_> {
318330
}
319331

320332
/// Parses jet input arguments from the bit machine's read frame.
321-
fn parse_jet_arguments(jet: Elements, input_frame: &mut FrameIter) -> Result<Vec<Value>, String> {
322-
let source_types = source_type(&jet);
333+
fn parse_jet_arguments(jet: &dyn JetHL, input_frame: &mut FrameIter) -> Result<Vec<Value>, String> {
334+
let source_types = source_type(jet);
323335
if source_types.is_empty() {
324336
return Ok(vec![]);
325337
}
@@ -389,7 +401,7 @@ mod tests {
389401
type DebugStore = Rc<RefCell<HashMap<String, String>>>;
390402
type JetStore = Rc<RefCell<HashMap<String, (Option<Vec<String>>, Option<String>)>>>;
391403

392-
fn create_test_tracker(
404+
fn create_test_elements_tracker(
393405
debug_symbols: &DebugSymbols,
394406
) -> (DefaultTracker<'_>, DebugStore, JetStore) {
395407
let debug_store: DebugStore = Rc::default();
@@ -398,7 +410,7 @@ mod tests {
398410
let debug_clone = debug_store.clone();
399411
let jet_clone = jet_store.clone();
400412

401-
let tracker = DefaultTracker::new(debug_symbols)
413+
let tracker = DefaultTracker::build(debug_symbols, Box::new(ElementsJetHinter::new()))
402414
.with_debug_sink(move |label, value| {
403415
debug_clone
404416
.borrow_mut()
@@ -444,7 +456,8 @@ mod tests {
444456
let program = program.instantiate(Arguments::default(), true).unwrap();
445457
let satisfied = program.satisfy(WitnessValues::default()).unwrap();
446458

447-
let (mut tracker, debug_store, jet_store) = create_test_tracker(&satisfied.debug_symbols);
459+
let (mut tracker, debug_store, jet_store) =
460+
create_test_elements_tracker(&satisfied.debug_symbols);
448461
let env = create_test_env();
449462

450463
let _ = satisfied
@@ -514,7 +527,7 @@ mod tests {
514527
let program = program.instantiate(Arguments::default(), true).unwrap();
515528
let satisfied = program.satisfy(WitnessValues::default()).unwrap();
516529

517-
let (mut tracker, _, jet_store) = create_test_tracker(&satisfied.debug_symbols);
530+
let (mut tracker, _, jet_store) = create_test_elements_tracker(&satisfied.debug_symbols);
518531

519532
let _ = satisfied.redeem().prune_with_tracker(&env, &mut tracker);
520533

@@ -570,7 +583,7 @@ mod tests {
570583
let program = program.instantiate(Arguments::default(), true).unwrap();
571584
let satisfied = program.satisfy(WitnessValues::default()).unwrap();
572585

573-
let (mut tracker, _, jet_store) = create_test_tracker(&satisfied.debug_symbols);
586+
let (mut tracker, _, jet_store) = create_test_elements_tracker(&satisfied.debug_symbols);
574587

575588
let _ = satisfied.redeem().prune_with_tracker(&env, &mut tracker);
576589

tests/core_tracker.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use std::cell::RefCell;
2+
use std::rc::Rc;
3+
4+
use simplicityhl::ast::CoreJetHinter;
5+
use simplicityhl::simplicity::jet::CoreEnv;
6+
use simplicityhl::tracker::DefaultTracker;
7+
use simplicityhl::{Arguments, TemplateProgram, WitnessValues};
8+
9+
const CORE_PROGRAM: &str = r#"fn main() {
10+
let (_, sum): (bool, u32) = jet::add_32(10, 20);
11+
assert!(jet::eq_32(sum, 30));
12+
}"#;
13+
14+
fn satisfied_core_program() -> simplicityhl::SatisfiedProgram {
15+
TemplateProgram::new(CORE_PROGRAM, Box::new(CoreJetHinter::new()))
16+
.unwrap()
17+
.instantiate(Arguments::default(), true)
18+
.unwrap()
19+
.satisfy(WitnessValues::default())
20+
.unwrap()
21+
}
22+
23+
#[test]
24+
fn core_program_prunes_without_tracker() {
25+
let satisfied = satisfied_core_program();
26+
satisfied.redeem().prune(&CoreEnv::new()).unwrap();
27+
}
28+
29+
#[test]
30+
fn core_program_should_not_panic_with_core_tracker() {
31+
let satisfied = satisfied_core_program();
32+
let mut tracker = DefaultTracker::build(
33+
satisfied.debug_symbols(),
34+
Box::new(CoreJetHinter::new()),
35+
);
36+
37+
satisfied
38+
.redeem()
39+
.prune_with_tracker(&CoreEnv::new(), &mut tracker)
40+
.unwrap();
41+
}
42+
43+
#[test]
44+
fn core_program_traces_core_jets() {
45+
let satisfied = satisfied_core_program();
46+
let traced_jets = Rc::new(RefCell::new(Vec::new()));
47+
let traced_jets_clone = traced_jets.clone();
48+
let mut tracker = DefaultTracker::build(
49+
satisfied.debug_symbols(),
50+
Box::new(CoreJetHinter::new()),
51+
)
52+
.with_jet_trace_sink(move |jet, _args, _result| {
53+
traced_jets_clone.borrow_mut().push(jet.to_string());
54+
});
55+
56+
satisfied
57+
.redeem()
58+
.prune_with_tracker(&CoreEnv::new(), &mut tracker)
59+
.unwrap();
60+
61+
assert!(
62+
traced_jets.borrow().iter().any(|jet| jet == "add_32"),
63+
"expected add_32 to be traced as a Core jet"
64+
);
65+
}

0 commit comments

Comments
 (0)