Skip to content

Commit 4c9f06c

Browse files
committed
Merge #362: fix(bitmachine): fix frame data offsets send to tracker (fixes Execution reached a pruned branch)
9cd3446 fix(bit_machine): use cursor position instead of frame start for tracker read iterator (stringhandler) Pull request description: Fixes some bugs where the data sent from the bitmachine to trackers would not start at the current cursor, resulting in some random junk being seen by the tracker. This doesn't have an effect on full programs, but when pruning this seems to generate an incorrect pruning, as seen in #337. The following code now prunes and runs correctly: ```rust fn add(elt: u32, acc: u32) -> u32 { let (_, sum): (bool, u32) = jet::add_32(elt, acc); sum } fn main() { let list: List<u32, 4> = list![5]; let result: u32 = fold::<add, 4>(list, 0); assert!(jet::eq_32(result, 5)); } ``` Whereas previously it would fail on `Execution failed: Execution reached a pruned branch: fd6503b9fd020b8d4ed75deb14cc05bbcb91da392ba4b675f2b2345dabb1cde5` Some excerpts from StderrTracker: ``` // Before fix [ 45] exec iden 2^32 → 2^32 input 0x00000002 output 0x00000005 // After fix [ 45] exec iden 2^32 → 2^32 input 0x00000005 output 0x00000005 ``` ``` /// Before fix [ 46] exec iden (2^64? × 2^32?) × 1 → (2^64? × 2^32?) × 1 input ((L(ε),R(0x00000005)),ε) output ((L(ε),L(ε)),ε) // After fix [ 46] exec iden (2 × 2^32?) × 1 → (2 × 2^32?) × 1 input ((0b0,R(0x00000005)),ε) output ((0b0,R(0x00000005)),ε) ``` I haven't tried it on KyrylR 's specific SHRINCS test on that issue. NOTE: I have based this off an older commit so I could test it with SimplicityHL, which doesn't compile with the latest `rust-simplicity` NOTE: the unit test is written by Claude (Happy to remove it if needed) ACKs for top commit: apoelstra: ACK 9cd3446; successfully ran local tests Tree-SHA512: 606b75f928e253a7b9734bf6511c9663481cefd16fcc7b9d84b33927d2e06cb0f3caaa8971f46d772857f1991f272a56b554c3fbe69527c6803ca25ada5803ae
2 parents b57dba2 + 9cd3446 commit 4c9f06c

2 files changed

Lines changed: 42 additions & 10 deletions

File tree

src/bit_machine/frame.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,12 @@ impl Frame {
115115
}
116116
}
117117

118-
/// Extend the present frame with a read-only reference the the data
119-
/// and return the resulting struct.
120-
pub(super) fn as_bit_iter<'a>(
118+
/// Return an iterator over the frame's remaining bits, starting at the current cursor position.
119+
pub(super) fn as_bit_iter_from_cursor<'a>(
121120
&self,
122121
data: &'a [u8],
123122
) -> BitIter<core::iter::Copied<core::slice::Iter<'a, u8>>> {
124-
BitIter::byte_slice_window(data, self.start, self.start + self.len)
123+
BitIter::byte_slice_window(data, self.cursor, self.start + self.len)
125124
}
126125
}
127126

src/bit_machine/mod.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,11 @@ impl BitMachine {
277277
}
278278

279279
'main_loop: loop {
280-
// Make a copy of the input frame to give to the tracker.
280+
// Capture read and write frames before the node action, to give to the tracker.
281+
// The read frame cursor is where the node's input begins.
282+
// The write frame cursor is where the node's output begins.
281283
let input_frame = self.read.last().map(Frame::shallow_copy);
284+
let output_frame = self.write.last().map(Frame::shallow_copy);
282285
let mut jet_result = Ok(());
283286

284287
match ip.inner() {
@@ -391,16 +394,15 @@ impl BitMachine {
391394
// describes the Bit Machine "input" to the current node,
392395
// no matter the node.
393396
let read_iter = input_frame
394-
.map(|frame| frame.as_bit_iter(&self.data))
397+
.map(|frame| frame.as_bit_iter_from_cursor(&self.data))
395398
.unwrap_or(crate::BitIter::from([].iter().copied()));
396399
// See the docs on `tracker::NodeOutput` for more information about
397400
// this match.
398401
let output = match (ip.inner(), &jet_result) {
399402
(node::Inner::Unit | node::Inner::Iden | node::Inner::Witness(_), _)
400403
| (node::Inner::Jet(_), Ok(_)) => NodeOutput::Success(
401-
self.write
402-
.last()
403-
.map(|r| r.as_bit_iter(&self.data))
404+
output_frame
405+
.map(|frame| frame.as_bit_iter_from_cursor(&self.data))
404406
.unwrap_or(crate::BitIter::from([].iter().copied())),
405407
),
406408
(node::Inner::Jet(_), Err(_)) => NodeOutput::JetFailed,
@@ -430,7 +432,7 @@ impl BitMachine {
430432
let out_frame = self.write.last_mut().unwrap();
431433
out_frame.reset_cursor();
432434
let value = Value::from_padded_bits(
433-
&mut out_frame.as_bit_iter(&self.data),
435+
&mut out_frame.as_bit_iter_from_cursor(&self.data),
434436
&program.arrow().target,
435437
)
436438
.expect("Decode value of output frame");
@@ -669,6 +671,37 @@ mod tests {
669671
.exec(&prog, &env)
670672
}
671673

674+
#[test]
675+
#[cfg(feature = "human_encoding")]
676+
fn set_tracker_cursor_regression() {
677+
// When a `case` node executes inside another `case` or `drop`, the read frame cursor
678+
// is no longer at the frame's start. Before this fix, `exec_with_tracker` passed an
679+
// iterator starting at frame.start to the tracker, so `SetTracker` read the wrong bit
680+
// and recorded the wrong branch, leading to an assertl/assertr mismatch after pruning.
681+
//
682+
// Program: comp (pair (injl (injr unit)) unit) (case (case unit unit) unit)
683+
// Intermediate frame bits: [0=L-tag of outer, 1=R-tag of inner]
684+
// Outer case: peek bit 0 = 0 (LEFT), fwd(1), cursor moves to bit 1.
685+
// Inner case: should read bit 1 = 1 (RIGHT).
686+
// Bug: tracker received iterator from bit 0, read 0 (LEFT) → pruned wrong branch.
687+
use crate::human_encoding::Forest;
688+
use crate::types;
689+
use std::collections::HashMap;
690+
691+
types::Context::with_context(|ctx| {
692+
let s = "main := comp (pair (injl (injr unit)) unit) (case (case unit unit) unit)";
693+
let program = Forest::parse::<crate::jet::Core>(s)
694+
.expect("parse")
695+
.to_witness_node(&ctx, &HashMap::new())
696+
.expect("main root")
697+
.finalize_pruned(&CoreEnv::new())
698+
.expect("finalize and prune");
699+
let mut mac = BitMachine::for_program(&program).expect("for_program");
700+
mac.exec(&program, &CoreEnv::new())
701+
.expect("pruned execution should succeed");
702+
});
703+
}
704+
672705
#[test]
673706
#[cfg(feature = "elements")]
674707
fn crash_regression1() {

0 commit comments

Comments
 (0)