diff --git a/src/bit_machine/frame.rs b/src/bit_machine/frame.rs index 53c9ba85..ba20e758 100644 --- a/src/bit_machine/frame.rs +++ b/src/bit_machine/frame.rs @@ -16,11 +16,11 @@ pub(super) struct Frame { /// Current position of the cursor. /// For read frames, this is the next bit which is to be read. /// For write frames, this is the next bit which is to be (over)written. - pub(super) cursor: usize, + cursor: usize, /// Start index of this frame in the referenced data. - pub(super) start: usize, - /// The total length of this frame. - pub(super) len: usize, + start: usize, + /// The total bit length of this frame. + len: usize, } impl Frame { @@ -33,6 +33,16 @@ impl Frame { } } + /// Return the start index of the frame inside the referenced data. + pub fn start(&self) -> usize { + self.start + } + + /// Return the bit width of the frame. + pub fn bit_width(&self) -> usize { + self.len + } + /// Reset the cursor to the start. pub(super) fn reset_cursor(&mut self) { self.cursor = self.start; diff --git a/src/bit_machine/mod.rs b/src/bit_machine/mod.rs index 1380a26c..e444d16c 100644 --- a/src/bit_machine/mod.rs +++ b/src/bit_machine/mod.rs @@ -8,15 +8,16 @@ mod frame; +use std::fmt; +use std::sync::Arc; +use std::{cmp, error}; + use crate::analysis; use crate::dag::{DagLike, NoSharing}; use crate::jet::{Jet, JetFailed}; use crate::node::{self, RedeemNode}; use crate::{Cmr, FailEntropy, Value}; use frame::Frame; -use std::fmt; -use std::sync::Arc; -use std::{cmp, error}; /// An execution context for a Simplicity program pub struct BitMachine { @@ -85,8 +86,8 @@ impl BitMachine { /// Drop the active read frame fn drop_frame(&mut self) { let active_read_frame = self.read.pop().unwrap(); - self.next_frame_start -= active_read_frame.len; - assert_eq!(self.next_frame_start, active_read_frame.start); + self.next_frame_start -= active_read_frame.bit_width(); + assert_eq!(self.next_frame_start, active_read_frame.start()); } /// Write a single bit to the active write frame @@ -177,6 +178,19 @@ impl BitMachine { } } + /// Return the bit width of the active read frame. + fn active_read_bit_width(&self) -> usize { + self.read.last().map(|frame| frame.bit_width()).unwrap_or(0) + } + + /// Return the bit width of the active write frame. + fn active_write_bit_width(&self) -> usize { + self.write + .last() + .map(|frame| frame.bit_width()) + .unwrap_or(0) + } + /// Add a read frame with some given value in it, as input to the /// program pub fn input(&mut self, input: &Value) { @@ -366,66 +380,104 @@ impl BitMachine { } fn exec_jet<J: Jet>(&mut self, jet: J, env: &J::Environment) -> Result<(), JetFailed> { - use simplicity_sys::c_jets::frame_ffi::{c_readBit, c_writeBit, CFrameItem}; - use simplicity_sys::c_jets::uword_width; - use simplicity_sys::ffi::UWORD; + use crate::ffi::c_jets::frame_ffi::{c_readBit, c_writeBit, CFrameItem}; + use crate::ffi::c_jets::uword_width; + use crate::ffi::ffi::UWORD; + + /// Create new C read frame that contains `bit_width` many bits from active read frame. + /// + /// Return C read frame together with underlying buffer. + /// + /// ## Safety + /// + /// The returned frame must outlive its buffer or there is a dangling pointer. + /// + /// ## Panics + /// + /// Active read frame has fewer bits than `bit_width`. + unsafe fn get_input_frame( + mac: &mut BitMachine, + bit_width: usize, + ) -> (CFrameItem, Vec<UWORD>) { + assert!(bit_width <= mac.active_read_bit_width()); + let uword_width = uword_width(bit_width); + let mut buffer = vec![0; uword_width]; + + // Copy bits from active read frame into input frame + let buffer_end = buffer.as_mut_ptr().add(uword_width); + let mut write_frame = CFrameItem::new_write(bit_width, buffer_end); + for _ in 0..bit_width { + let bit = mac.read_bit(); + c_writeBit(&mut write_frame, bit); + } + mac.back(bit_width); - // Sanity Check: This should never really fail, but still good to do - if !simplicity_sys::c_jets::sanity_checks() { - return Err(JetFailed); + // Convert input frame into read frame + let buffer_ptr = buffer.as_mut_ptr(); + let read_frame = CFrameItem::new_read(bit_width, buffer_ptr); + + (read_frame, buffer) } - let src_ty_bit_width = jet.source_ty().to_bit_width(); - let target_ty_bit_width = jet.target_ty().to_bit_width(); - - let a_frame_size = uword_width(src_ty_bit_width); - let b_frame_size = uword_width(target_ty_bit_width); - // a_frame_size + b_frame_size must be non-zero unless it is a unit to unit jet - if a_frame_size == 0 && b_frame_size == 0 { - return Ok(()); + + /// Create C write frame that is as wide as `bit_width`. + /// + /// Return C write frame together with underlying buffer. + /// + /// ## Safety + /// + /// The returned frame must outlive its buffer or there is a dangling pointer. + unsafe fn get_output_frame(bit_width: usize) -> (CFrameItem, Vec<UWORD>) { + let uword_width = uword_width(bit_width); + let mut buffer = vec![0; uword_width]; + + // Return output frame as write frame + let buffer_end = buffer.as_mut_ptr().add(uword_width); + let write_frame = CFrameItem::new_write(bit_width, buffer_end); + + (write_frame, buffer) } - let mut src_buf = vec![0 as UWORD; a_frame_size + b_frame_size]; - let src_ptr_end = unsafe { src_buf.as_mut_ptr().add(a_frame_size) }; // A frame write - let src_ptr = src_buf.as_mut_ptr(); // A read frame at ptr start - let dst_ptr_begin = unsafe { src_buf.as_mut_ptr().add(a_frame_size) }; // B read frame at ptr begin - let dst_ptr_end = unsafe { src_buf.as_mut_ptr().add(a_frame_size + b_frame_size) }; // B write frame at ptr end - - // For jet from type A -> B - // Jets execution: There is single buffer with a_frame_size + b_frame_size UWORDs - // ------[ A read frame ][ B write frame ]--- - // ^ src_ptr ^src_ptr_end(dst_ptr_begin) ^ dst_ptr_end - // 1. Write into C bitmachine using A write frame(= src_ptr_end) - // Precondition satisfied: src_ptr_end is one past the end of slice of UWORDs for A. - let mut a_frame = unsafe { CFrameItem::new_write(src_ty_bit_width, src_ptr_end) }; - for _ in 0..src_ty_bit_width { - let bit = self.read_bit(); - unsafe { - c_writeBit(&mut a_frame, bit); + + /// Write `bit_width` many bits from `buffer` into active write frame. + /// + /// ## Panics + /// + /// Active write frame has fewer bits than `bit_width`. + /// + /// Buffer has fewer than bits than `bit_width` (converted to UWORDs). + fn update_active_write_frame(mac: &mut BitMachine, bit_width: usize, buffer: &[UWORD]) { + assert!(bit_width <= mac.active_write_bit_width()); + assert!(uword_width(bit_width) <= buffer.len()); + let buffer_ptr = buffer.as_ptr(); + let mut read_frame = unsafe { CFrameItem::new_read(bit_width, buffer_ptr) }; + + for _ in 0..bit_width { + let bit = unsafe { c_readBit(&mut read_frame) }; + mac.write_bit(bit); } } - self.back(src_ty_bit_width); - - // 2. Execute the jet. src = A read frame, dst = B write frame - // Precondition satisfied: src_ptr is the start of slice of UWORDs of A. - let src_frame = unsafe { CFrameItem::new_read(src_ty_bit_width, src_ptr) }; - // Precondition satisfied: dst_ptr_end is one past the end of slice of UWORDs of B. - let mut dst_frame = unsafe { CFrameItem::new_write(target_ty_bit_width, dst_ptr_end) }; - let jet_fn = jet.c_jet_ptr(); - let c_env = jet.c_jet_env(env); - let res = jet_fn(&mut dst_frame, src_frame, c_env); - if !res { + // Sanity Check: This should never really fail, but still good to do + if !simplicity_sys::c_jets::sanity_checks() { return Err(JetFailed); } - // 3. Read the result from B read frame - // Precondition satisfied: dst_ptr_begin is the start of slice of UWORDs of B. - let mut b_frame = unsafe { CFrameItem::new_read(target_ty_bit_width, dst_ptr_begin) }; - // Read the value from b_frame - for _ in 0..target_ty_bit_width { - let bit = unsafe { c_readBit(&mut b_frame) }; - self.write_bit(bit); + let input_width = jet.source_ty().to_bit_width(); + let output_width = jet.target_ty().to_bit_width(); + // Input buffer is implicitly referenced by input read frame! + // Same goes for output buffer + let (input_read_frame, _input_buffer) = unsafe { get_input_frame(self, input_width) }; + let (mut output_write_frame, output_buffer) = unsafe { get_output_frame(output_width) }; + + let jet_fn = jet.c_jet_ptr(); + let c_env = jet.c_jet_env(env); + let success = jet_fn(&mut output_write_frame, input_read_frame, c_env); + + if !success { + Err(JetFailed) + } else { + update_active_write_frame(self, output_width, &output_buffer); + Ok(()) } - Ok(()) } } diff --git a/src/jet/elements/environment.rs b/src/jet/elements/environment.rs index a6a13a42..395b9e97 100644 --- a/src/jet/elements/environment.rs +++ b/src/jet/elements/environment.rs @@ -36,15 +36,6 @@ impl From<elements::TxOut> for ElementsUtxo { /// # Note /// The order of `utxos` must be same as of the order of inputs in the /// transaction. -// FIXME: -// Ideally the `Arc<elements::Transaction>` would be a generic -// `AsRef<elements::Transaction>` or something, but this is an associated type -// for the `Extension` trait, and Rust will not let us have generic parameters -// on associated types. (We could possibly add a parameter to the Extension -// trait itself, but that would be messy and layer-violating.) -// -// Similar story if we tried to use a &'a elements::Transaction rather than -// an Arc: we'd have a lifetime parameter <'a> that would cause us trouble. #[allow(dead_code)] pub struct ElementsEnv<T: Deref<Target = elements::Transaction>> { /// The CTxEnv struct diff --git a/src/value.rs b/src/value.rs index 280edb26..f3efb235 100644 --- a/src/value.rs +++ b/src/value.rs @@ -221,7 +221,7 @@ impl Value { /// Encode value as big-endian byte string. /// Fails if underlying bit string has length not divisible by 8 - pub fn try_to_bytes(&self) -> Result<Vec<u8>, &str> { + pub fn try_to_bytes(&self) -> Result<Vec<u8>, &'static str> { let (bytes, bit_length) = self.to_bytes_len(); if bit_length % 8 == 0 {