Skip to content

Conversation

@apoelstra
Copy link
Collaborator

Adds the ability for the execution tracker to track every visited node, to view the "input" (top read frame) of every node and the "output" (top write frame after execution) of every terminal node. Allows it to read this data in the form of a bit iterator, which is much easier to work with than the &[UWORD] that the old jet interface provided. Allows tracking "debug" nodes without special-purpose methods that enable/disable costly conversions.

Also adds a PruneTracker extension trait which can be used to introspect or manipulate the pruning process.

See #323 and #324 for motivation.

Fixes #324.

The tests call `iter.len()` a lot. The default implementation of this
method passes through to `iter.size_hint()` and also asserts that both
the low and the high estimates are equal. So these tests are pretty
thorough.
Returning a concrete type lets us get more trait methods rather than just
`Iterator`. In particular we should be able to get `ExactSizeIterator`
and `Fuse` which are important for efficiency.

We also want to name this type elsewhere, and even the existential one
was annoying to type, so we add an alias for it.
This whole type is pub(super). It should not have public methods.
There are three basic operations on the frame stack in the bit machine:
creating a new write frame, popping the write stack onto the read stack,
and dropping a read frame.

Our code uses shorthand like "new_frame" and "drop_frame" which makes
it easy to forget which stacks these operations are operating on. This
makes it harder to understand the code for somebody who doesn't currently
have the bit machine algorithm loaded into their head.

Rename to make stuff easier to follow.
Copy link
Collaborator Author

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On 6e34c44 successfully ran local tests

@apoelstra apoelstra mentioned this pull request Dec 7, 2025
uncomputable
uncomputable previously approved these changes Dec 7, 2025
Copy link
Collaborator

@uncomputable uncomputable left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gerau
Copy link

gerau commented Dec 8, 2025

This implementation is much better than mine!

But I also see a minor issue in bit_machine::exec_with_tracker --
because jet tracking does not happen inside exec_jet, and it returns early on error,
a failed jet will not be tracked by ExecTracker, whereas it was tracked in the previous version.

It would also be helpful to have a default tracker such as DebugTracker, which
could record basic information about jets and/or debug statements. This would give users
a default option for debugging and provide an example for implementing their own trackers.

@apoelstra
Copy link
Collaborator Author

This implementation is much better than mine!

But I also see a minor issue in bit_machine::exec_with_tracker -- because jet tracking does not happen inside exec_jet, and it returns early on error, a failed jet will not be tracked by ExecTracker, whereas it was tracked in the previous version.

Ah, great point. Let me what I can do to recover this. I agree it's important to at least notice if we enter a failed jet.

It would also be helpful to have a default tracker such as DebugTracker, which could record basic information about jets and/or debug statements. This would give users a default option for debugging and provide an example for implementing their own trackers.

Yeah, good idea.

@apoelstra
Copy link
Collaborator Author

apoelstra commented Dec 8, 2025

I think I will replace the output iterator with an enum

#[derive(Debug, PartialEq, Eq, Clone)]
enum NodeOutput {
    /// Node was not unit, iden, witness or jet, and thus has no currently-available output since its children have not yet run.
    NonTerminal,
    /// Node was a jet which "failed", i.e. aborted the program
    JetFailed,
    /// Node succeeded. This is its output frame.
    Success(FrameIter),
}

And I will defer the error return on exec_jet until after calling visit_node to ensure that we still call it.

This will also let me simplify all these comments which currently warn the user not to use the FrameIter that we're unconditionally providing.

@KyrylR
Copy link
Contributor

KyrylR commented Dec 9, 2025

I believe we can close this PR: #323 in favor of this one

I like the change too, the only part that is missing IMO is a "default" (basic) implementation of the tracker that the end dev can use

If we can add "default" tracker and mention it in the docs for the prune_with_tracker function, it would fully cover the initial need for opening the #323

Copy link
Collaborator Author

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On e22f8d9 successfully ran local tests; ready for review

@KyrylR
Copy link
Contributor

KyrylR commented Dec 10, 2025


[1365] exec injl 2^256 × (((2 × 2^256) + 2^256) × (2^32 × 2^256)) → 2
       input (((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))),(1((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))),((((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,0),(0,0)))),((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,1),(0,1))))),((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))))))

[1366] exec unit 2^256 × (((2 × 2^256) + 2^256) × (2^32 × 2^256)) → 1
       input (((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))),(1((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))),((((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,0),(0,0)))),((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,1),(0,1))))),((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))))))
      output ε

[1367] exec pair 2^256 × (((2 × 2^256) + 2^256) × (2^32 × 2^256)) → 2^512
       input (((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))),(1((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))),((((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,0),(0,0)))),((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,1),(0,1))))),((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))))))

[1368] exec take 2^256 × (((2 × 2^256) + 2^256) × (2^32 × 2^256)) → 2^256
       input (((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))),(1((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))),((((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,0),(0,0)))),((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,1),(0,1))))),((((((((0,1),(0,0)),((1,0),(0,1))),(((1,0),(0,1)),((1,0),(1,0)))),((((1,0),(0,0)),((0,0),(0,1))),(((1,0),(0,0)),((0,1),(0,1))))),(((((0,1),(0,0)),((0,1),(0,1))),(((1,1),(1,1)),((0,1),(1,0)))),((((1,0),(1,1)),((1,0),(1,0))),(((1,1),(1,0)),((0,0),(1,1)))))),((((((1,0),(0,1)),((1,1),(1,1))),(((1,1),(0,0)),((0,0),(0,0)))),((((0,0),(1,1)),((1,0),(1,1))),(((0,1),(1,0)),((0,0),(1,1))))),(((((0,1),(1,1)),((1,1),(1,1))),(((0,0),(1,0)),((1,0),(1,0)))),((((0,1),(0,0)),((1,1),(1,0))),(((0,0),(0,1)),((1,1),(1,0))))))),(((((((0,1),(1,0)),((0,1),(0,0))),(((1,1),(1,0)),((0,1),(0,1)))),((((1,0),(0,1)),((0,0),(0,0))),(((1,1),(0,0)),((1,0),(1,0))))),(((((1,1),(0,0)),((0,0),(0,1))),(((1,0),(1,1)),((1,1),(0,0)))),((((0,0),(1,1)),((1,0),(1,0))),(((0,1),(1,0)),((1,1),(1,1)))))),((((((0,1),(1,0)),((1,1),(0,1))),(((0,1),(1,1)),((0,0),(0,1)))),((((1,0),(1,0)),((1,0),(1,0))),(((0,1),(0,0)),((0,1),(0,0))))),(((((0,1),(0,0)),((0,0),(1,1))),(((0,1),(1,0)),((0,1),(0,1)))),((((0,1),(0,0)),((1,1),(0,0))),(((0,0),(0,1)),((0,1),(0,0)))))))))))

Debug output with &mut StderrTracker::default() is unreadable

@KyrylR
Copy link
Contributor

KyrylR commented Dec 10, 2025

Let me try a few things, so I can showcase what I would love to see

@KyrylR
Copy link
Contributor

KyrylR commented Dec 10, 2025

Now I understand why it looks like this...

All I wanted was to see the output as SimplicityHL statements, rather than the actual Bit Machine, because now it is more like assembly tracks rather than high-level call traces

Sorry for the confusion

@KyrylR
Copy link
Contributor

KyrylR commented Dec 10, 2025

[crates/simplicityhl-core/src/trackers/default_tracker.rs:179:29] &StructuralValue::from(input_val.clone()) = (0,(((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,0),(0,0)))),((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,1),(0,0))))))
[crates/simplicityhl-core/src/trackers/default_tracker.rs:190:33] &test = (0,(((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,0),(0,0)))),((((0,0),(0,0)),((0,0),(0,0))),(((0,0),(0,0)),((0,1),(0,0))))))
[crates/simplicityhl-core/src/trackers/default_tracker.rs:190:33] &inner_ty = u32
[crates/simplicityhl-core/src/trackers/default_tracker.rs:194:33] test1 = Some(
    (false, 4): (bool, u32),
)

What is a proper way to parse dbg statements?

[crates/simplicityhl-core/src/trackers/default_tracker.rs:161:17] self.debug_symbols.get(cmr) = Some(
    TrackedCall {
        text: "jet::num_inputs()",
        name: Debug(
            u32,
        ),
    },
)

This is what I receive from the debug symbols

Though for some reason it is not u32, but something like (bool, u32) when I am reading the input: FrameIter

Code:

if let Ok(input_val) =
    SimValue::from_padded_bits(&mut (input.clone()), &node.arrow().source)
{
	let inner_ty: Option<ResolvedType> = match tracked_call.name() {
	    TrackedCallName::Debug(ty) => Some(ty.clone()),
	    _ => None
	};
	
	if let Some(inner_ty) = inner_ty {
	    let test = StructuralValue::from(input_val.clone());
	
	    dbg!(&test, &inner_ty);
	
	    let test1 = Value::reconstruct(&test, &ResolvedType::parse_from_str(&format!("(bool, {})", &inner_ty.clone())).unwrap());
	
	    dbg!(test1);
	};
}

@KyrylR
Copy link
Contributor

KyrylR commented Dec 10, 2025

if let Some(tracked_call) = self.debug_symbols.get(cmr) {
    let mut input_frame = input.clone();
    input_frame.next();

    if let Ok(input_val) =
        SimValue::from_padded_bits(&mut input_frame, &node.arrow().target)
        && let Some(Either::Right(debug_value)) =
            tracked_call.map_value(&StructuralValue::from(input_val))
        {
            dbg!(debug_value);
        }
}

this is what I have to do, to be able to read statements wrapped into the dbg!(T) statement

This thing that I cannot understand is why I need to do this input_frame.next(), before calling the SimValue::from_padded_bits, as well as using &node.arrow().target instead of &node.arrow().source

@KyrylR
Copy link
Contributor

KyrylR commented Dec 10, 2025

Here is a PoC implementation of tracker that I could consider a default one for the SimplicityHL:

impl ExecTracker<Elements> for DefaultTracker<'_> {
    fn visit_node(&mut self, node: &RedeemNode<Elements>, input: FrameIter, output: NodeOutput) {
        self.inner.visit_node(node, input.clone(), output.clone());

        match node.inner() {
            Inner::Jet(jet) => {
                if let Some(sink) = self.jet_trace_sink.as_mut()
                    && let NodeOutput::Success(output_iter) = output
                {
                    let mut output_frame = output_iter.clone();
                    output_frame.next();

                    let parsed_result = Value::reconstruct(
                        &StructuralValue::from(
                            SimValue::from_padded_bits(
                                &mut (output_frame.clone()),
                                &node.arrow().target,
                            )
                            .expect("output from bit machine will always be well-formed"),
                        ),
                        &target_type(*jet)
                            .resolve(|_: &AliasName| -> Option<ResolvedType> { None })
                            .expect("target type of built-in jet always resolves"),
                    );

                    let mut input_frame = input.clone();
                    input_frame.next();

                    if let Ok(args) = parse_args_from_frame(*jet, &mut input_frame)
                        && let Some(result) = parsed_result
                    {
                        sink(*jet, &args, &result);
                    }
                }
            }
            // Handle debug calls (encoded as AssertL with CMR as key into debug symbols)
            Inner::AssertL(_, cmr) => {
                if let Some(sink) = self.debug_sink.as_mut()
                    && let Some(tracked_call) = self.debug_symbols.get(cmr)
                {
                    let mut input_frame = input.clone();
                    input_frame.next();

                    if let Ok(input_val) =
                        SimValue::from_padded_bits(&mut input_frame, &node.arrow().target)
                        && let Some(Either::Right(debug_value)) =
                            tracked_call.map_value(&StructuralValue::from(input_val))
                    {
                        sink(debug_value.text(), debug_value.value());
                    }
                }
            }
            _ => {}
        }
    }
}

fn parse_args_from_frame(jet: Elements, input_frame: &mut FrameIter) -> Result<Vec<Value>> {
    let jet_source_types = source_type(jet);
    if jet_source_types.is_empty() {
        return Ok(vec![]);
    }

    let arguments_blob = SimValue::from_padded_bits(input_frame, &jet.source_ty().to_final())
        .expect("input from bit machine will always be well-formed");

    let mut args = Vec::with_capacity(jet_source_types.len());
    collect_args(&arguments_blob.as_ref(), args.capacity(), &mut args)?;

    Ok(args
        .into_iter()
        .zip(jet_source_types.iter())
        .map(|(argument, aliased_type)| {
            Value::reconstruct(&argument.into(), &resolve_type(aliased_type))
                .expect("correct structure of the value of compiled program is guaranteed")
        })
        .collect())
}

/// Traverses a product and collects the arguments.
fn collect_args(node: &ValueRef, num_args: usize, args: &mut Vec<SimValue>) -> Result<()> {
    if num_args == 0 {
        return Ok(());
    }

    if num_args == 1 {
        args.push(node.to_value());

        Ok(())
    } else if let Some((left, right)) = node.as_product() {
        args.push(left.to_value());

        collect_args(&right, num_args - 1, args)
    } else {
        Err(anyhow!(
            "unexpected value structure while collecting arguments"
        ))
    }
}

fn resolve_type(aliased_type: &AliasedType) -> ResolvedType {
    aliased_type
        .resolve(|_: &AliasName| -> Option<ResolvedType> { None })
        .expect("target type of built-in jet always resolves")
}

@KyrylR
Copy link
Contributor

KyrylR commented Dec 10, 2025

Here is an example output:

NumInputs() = 3
Eq32(3, 3) = true
Verify(true) = ()
NumOutputs() = 7
Eq32(7, 7) = true
Verify(true) = ()
InputScriptHash(0) = Some(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9)
OutputScriptHash(0) = Some(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9)
Eq256(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9, 0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9) = true
Verify(true) = ()
InputScriptHash(1) = Some(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9)
OutputScriptHash(1) = Some(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9)
Eq256(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9, 0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9) = true
Verify(true) = ()
OutputScriptHash(0) = Some(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9)
OutputScriptHash(1) = Some(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9)
Eq256(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9, 0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9) = true
DBG: jet::eq_256(get_output_script_hash(0), get_output_script_hash(1)) = true
Verify(true) = ()
CurrentIndex() = 0
Le32(0, 1) = true
Verify(true) = ()
OutputScriptHash(0) = Some(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9)
OutputScriptHash(2) = Some(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9)
Eq256(0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9, 0x4d68e5c732d868fe921b8f39e3e525821eebaec11ea387c57d2e402c0f96fbd9) = true
Verify(true) = ()
OutputAmount(2) = Some((Right(0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14), Right(1000)))
DBG: get_output_explicit_asset_amount(2) = (0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14, 1000)
IssuanceAssetAmount(0) = Some(Some(Right(50)))
IssuanceAssetAmount(1) = Some(Some(Right(50)))
Eq64(50, 50) = true
Verify(true) = ()
DivMod64(1000, 20) = (100, 0)
Eq64(50, 50) = true
Verify(true) = ()
Eq64(0, 0) = true
Verify(true) = ()
DivMod64(1250, 25) = (100, 0)
Eq64(50, 50) = true
Verify(true) = ()
Eq64(0, 0) = true
Verify(true) = ()
OutputAmount(2) = Some((Right(0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14), Right(1000)))
DBG: get_output_explicit_asset_amount(index) = (0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14, 1000)
Eq256(0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14, 0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14) = true
Verify(true) = ()
Eq64(1000, 1000) = true
Verify(true) = ()
OutputAmount(3) = Some((Right(0x3c3fe87795809f74ed54005e0e50395fdc3b2862048641f439bd4ba6ca009502), Right(50)))
DBG: get_output_explicit_asset_amount(index) = (0x3c3fe87795809f74ed54005e0e50395fdc3b2862048641f439bd4ba6ca009502, 50)
Eq256(0x3c3fe87795809f74ed54005e0e50395fdc3b2862048641f439bd4ba6ca009502, 0x3c3fe87795809f74ed54005e0e50395fdc3b2862048641f439bd4ba6ca009502) = true
Verify(true) = ()
Eq64(50, 50) = true
Verify(true) = ()
OutputAmount(4) = Some((Right(0x1c01566a1e789e1a3f84c73b2c04352d30008eb30d905a0f22ba3e1a5a835b6f), Right(50)))
DBG: get_output_explicit_asset_amount(index) = (0x1c01566a1e789e1a3f84c73b2c04352d30008eb30d905a0f22ba3e1a5a835b6f, 50)
Eq256(0x1c01566a1e789e1a3f84c73b2c04352d30008eb30d905a0f22ba3e1a5a835b6f, 0x1c01566a1e789e1a3f84c73b2c04352d30008eb30d905a0f22ba3e1a5a835b6f) = true
Verify(true) = ()
Eq64(50, 50) = true
Verify(true) = ()
InputAsset(2) = Some(Right(0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14))
Eq256(0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14, 0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14) = true
Verify(true) = ()
OutputAsset(5) = Some(Right(0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14))
Eq256(0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14, 0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14) = true
Verify(true) = ()
OutputAsset(6) = Some(Right(0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14))
Eq256(0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14, 0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14) = true
Verify(true) = ()
Pruning unexecuted left child of IHR c2d923f05c3c9a8335ecb8fb7682c8688279e05bfdc73dc2c287ddb640c39230
Pruning unexecuted right child of IHR c2d923f05c3c9a8335ecb8fb7682c8688279e05bfdc73dc2c287ddb640c39230
Pruning unexecuted left child of IHR 0dcb0e5634be1aee18f9b8c94856ba9149850d6580be82ea73173434cf3a34c0
Pruning unexecuted right child of IHR 0dcb0e5634be1aee18f9b8c94856ba9149850d6580be82ea73173434cf3a34c0
Pruning unexecuted left child of IHR 0f6afdcf5ff04b3e9970df4cb79329dfb2b600f9d107befc13f61f0a576f1cf1
Pruning unexecuted right child of IHR 0f6afdcf5ff04b3e9970df4cb79329dfb2b600f9d107befc13f61f0a576f1cf1
Pruning unexecuted left child of IHR 4e5015d84fec815267ac9036461b10685d9766a643ebe482bb51a9dabaf6a528
Pruning unexecuted right child of IHR 4e5015d84fec815267ac9036461b10685d9766a643ebe482bb51a9dabaf6a528
Pruning unexecuted left child of IHR 0dcb0e5634be1aee18f9b8c94856ba9149850d6580be82ea73173434cf3a34c0
Pruning unexecuted right child of IHR 0dcb0e5634be1aee18f9b8c94856ba9149850d6580be82ea73173434cf3a34c0
Pruning unexecuted left child of IHR 0f6afdcf5ff04b3e9970df4cb79329dfb2b600f9d107befc13f61f0a576f1cf1
Pruning unexecuted right child of IHR 0f6afdcf5ff04b3e9970df4cb79329dfb2b600f9d107befc13f61f0a576f1cf1
Pruning unexecuted left child of IHR e56336e4a6942343d1149897523c9f4283d66d1f20c6eac5aa282b48116221d9
Pruning unexecuted right child of IHR e56336e4a6942343d1149897523c9f4283d66d1f20c6eac5aa282b48116221d9
Pruning unexecuted right child of IHR 3898dde1965ffc6992ba4e9de26a2c8f82d90904e6f5dcc34d4f49da0b6c1aa8
Pruning unexecuted left child of IHR 4e5015d84fec815267ac9036461b10685d9766a643ebe482bb51a9dabaf6a528
Pruning unexecuted right child of IHR 4e5015d84fec815267ac9036461b10685d9766a643ebe482bb51a9dabaf6a528
Pruning unexecuted left child of IHR 0dcb0e5634be1aee18f9b8c94856ba9149850d6580be82ea73173434cf3a34c0
Pruning unexecuted right child of IHR 0dcb0e5634be1aee18f9b8c94856ba9149850d6580be82ea73173434cf3a34c0
Pruning unexecuted left child of IHR 0f6afdcf5ff04b3e9970df4cb79329dfb2b600f9d107befc13f61f0a576f1cf1
Pruning unexecuted right child of IHR 0f6afdcf5ff04b3e9970df4cb79329dfb2b600f9d107befc13f61f0a576f1cf1
Pruning unexecuted left child of IHR 225a43a6291e944f652ef14d0c3c617bdf67b32bb5f615da48cffac6af5ce12a
Pruning unexecuted right child of IHR 225a43a6291e944f652ef14d0c3c617bdf67b32bb5f615da48cffac6af5ce12a
Pruning unexecuted left child of IHR 0dcb0e5634be1aee18f9b8c94856ba9149850d6580be82ea73173434cf3a34c0
Pruning unexecuted right child of IHR 0dcb0e5634be1aee18f9b8c94856ba9149850d6580be82ea73173434cf3a34c0
Pruning unexecuted left child of IHR 0f6afdcf5ff04b3e9970df4cb79329dfb2b600f9d107befc13f61f0a576f1cf1
Pruning unexecuted right child of IHR 0f6afdcf5ff04b3e9970df4cb79329dfb2b600f9d107befc13f61f0a576f1cf1
Pruning unexecuted left child of IHR 0f6afdcf5ff04b3e9970df4cb79329dfb2b600f9d107befc13f61f0a576f1cf1
Pruning unexecuted right child of IHR 0f6afdcf5ff04b3e9970df4cb79329dfb2b600f9d107befc13f61f0a576f1cf1
Pruning unexecuted left child of IHR 51b9abd31d5e118731d5d2f7b6ee42a7e9f5b7fdfca30d38c23260a367676229
Pruning unexecuted right child of IHR 51b9abd31d5e118731d5d2f7b6ee42a7e9f5b7fdfca30d38c23260a367676229
Pruning unexecuted right child of IHR 557cc9801e1af72b933fed9268cabc101cbd90b01a26a06f97fd7763a2018709

@apoelstra
Copy link
Collaborator Author

Okay, but this repo is rust-simplicity, not SimplicityHL.

@apoelstra
Copy link
Collaborator Author

I can certainly improve the Value display but that's a separate PR.

@KyrylR
Copy link
Contributor

KyrylR commented Dec 10, 2025

Okay, but this repo is rust-simplicity, not SimplicityHL.

Yeah, though, the thing that we are doing here is going to be used there, so this what I wanted to test

This thing that I cannot understand is why I need to do this input_frame.next(), before calling the SimValue::from_padded_bits, as well as using &node.arrow().target instead of &node.arrow().source

Do you have an idea why I should use input_frame.next()?

@apoelstra
Copy link
Collaborator Author

Yeah, though, the thing that we are doing here is going to be used there, so this what I wanted to test

Okay, but there's nothing I can do in this PR to do this. I can open a draft PR to SimplicityHL (or you can, using your above code).

The reason you need to advance a bit is because the AssertL combinator is actually a Case combinator, which takes a bit of input to decide which branch to take. But this bit is "meaningless" and always 0 because it's an assertion.

@uncomputable
Copy link
Collaborator

uncomputable commented Dec 10, 2025

Debug output with &mut StderrTracker::default() is unreadable

There is infrastructure in SimplicityHL to reconstruct and display values based on the low-level Simplicity units, sums and pairs.

Simplicity is not SimplicityHL, but I think we could group pairs of bits into bit / hex strings. We could go further and group buffer values (aka the sha2 context), too.

Comment on lines 3 to 8
//! Simplicity Execution
//!
//! Implementation of the Bit Machine, without TCO, as TCO precludes some
//! frame management optimizations which can be used to great benefit.
//!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy pasted from mod.rs? (it seems like it does not belong here)


/// An object which can be used to introspect the execution of the Bit Machine.
///
/// As an example of what you can do with this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a cut-off message

/// *pre* ordering. That is, for the program `comp iden unit` the nodes will be visited
/// in the order `comp`, `iden`, `unit`.
///
/// This method be used for logging, to track left or write accesses of the children of a
Copy link
Contributor

@KyrylR KyrylR Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// This method be used for logging, to track left or write accesses of the children of a
/// This method should be used for logging, to track left or write accesses of the children of a

Maybe can instead of should? (not sure)

Also left or write sounds weird

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, thanks. Will do can (and change write to right which I'm pretty sure is what I meant).

@KyrylR
Copy link
Contributor

KyrylR commented Dec 10, 2025

Overall I like the change, it also made a few things clearer for me, thanks

Comment on lines 153 to 157
) {
let input_val = Value::from_padded_bits(&mut input, &node.arrow().source)
.expect("input from bit machine will always be well-formed");
Copy link
Contributor

@KyrylR KyrylR Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
) {
let input_val = Value::from_padded_bits(&mut input, &node.arrow().source)
.expect("input from bit machine will always be well-formed");
) {
self.inner.visit_node(node, input.clone(), output.clone());
let input_val = Value::from_padded_bits(&mut input, &node.arrow().source)
.expect("input from bit machine will always be well-formed");

Is it ok that we do not have self.inner.visit_node(node, input.clone(), output.clone()); here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oo, no, it's not! Good catch.

Comment on lines 68 to 72
/// Returns true if the left branch of the of the `Case` node with the IHR `ihr` was taken.
fn contains_left(&self, ihr: Ihr) -> bool;

/// Returns true if the left branch of the of the `Case` node with the IHR `ihr` was taken.
fn contains_right(&self, ihr: Ihr) -> bool;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Returns true if the left branch of the of the `Case` node with the IHR `ihr` was taken.
fn contains_left(&self, ihr: Ihr) -> bool;
/// Returns true if the left branch of the of the `Case` node with the IHR `ihr` was taken.
fn contains_right(&self, ihr: Ihr) -> bool;
/// Returns true if the left branch of the of the `Case` node with the IHR `ihr` was taken.
fn contains_left(&self, ihr: Ihr) -> bool;
/// Returns true if the right branch of the of the `Case` node with the IHR `ihr` was taken.
fn contains_right(&self, ihr: Ihr) -> bool;

// decoding works.
let _input_val = Value::from_padded_bits(&mut input, &node.arrow().source);
if let NodeOutput::Success(mut output) = output {
let _output_val = Value::from_padded_bits(&mut output, &node.arrow().source);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let _output_val = Value::from_padded_bits(&mut output, &node.arrow().source);
let _output_val = Value::from_padded_bits(&mut output, &node.arrow().target);

Is there any reason to use .source instead of .target?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, it should be target.

I changed the let _output_val = X construct to X.expect(). Now the tests fail with source and pass with target. Good catch!

Re-export everything so there is no API change. This just tidies up the
code a little bit.
This eliminates the private `exec_prune` method which was weirdly-named
and no longer makes sense now that we're using a generic tracker rather
than a SetTracker.
This replaces the `ExecTracker` API with one which is able to detect
every node, not just cases and jets; which is able to read the input
value for every node (as a bit iterator which can be converted to a
value with Value::from_padded_bits) and the output value for terminal
nodes; and which can do all the existing things that the tracker can
do.

I suspect we want to add some examples or unit tests, in particular
around "debug nodes".

Fixes BlockstreamResearch#324
@apoelstra
Copy link
Collaborator Author

Addressed @KyrylR's comments.

NodeOutput::JetFailed => eprintln!(" JET FAILED"),
NodeOutput::Success(mut output) => {
let output_val = Value::from_padded_bits(&mut output, &node.arrow().target)
.expect("input from bit machine will always be well-formed");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.expect("input from bit machine will always be well-formed");
.expect("output from bit machine will always be well-formed");

Except that, everything else LGTM

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Outputting to stderr is not super useful but this can be used as a demo
of what the tracker is able to do.
Copy link
Collaborator Author

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On ed79edf successfully ran local tests

@KyrylR
Copy link
Contributor

KyrylR commented Dec 10, 2025

ACK 3bf7cef

Copy link
Collaborator Author

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On 3bf7cef successfully ran local tests

@apoelstra apoelstra merged commit 7c3cbb3 into BlockstreamResearch:master Dec 10, 2025
24 checks passed
@apoelstra apoelstra deleted the 2025-12/exectracker branch December 11, 2025 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New ExecTracker API

4 participants