Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
msrv = "1.41.0"
26 changes: 19 additions & 7 deletions src/bit_machine/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl BitMachine {
}

/// Push a new frame of given size onto the write frame stack
fn new_frame(&mut self, len: usize) {
pub(crate) fn new_frame(&mut self, len: usize) {
debug_assert!(
self.next_frame_start + len <= self.data.len() * 8,
"Data out of bounds: number of cells"
Expand All @@ -70,14 +70,14 @@ impl BitMachine {
}

/// Move the active write frame to the read frame stack
fn move_frame(&mut self) {
pub(crate) fn move_frame(&mut self) {
let mut _active_write_frame = self.write.pop().unwrap();
_active_write_frame.reset_cursor();
self.read.push(_active_write_frame);
}

/// Drop the active read frame
fn drop_frame(&mut self) {
pub(crate) 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);
Expand Down Expand Up @@ -243,7 +243,7 @@ impl BitMachine {
}

/// Write a value to the current write frame
fn write_value(&mut self, val: &Value) {
pub(crate) fn write_value(&mut self, val: &Value) {
// FIXME don't recurse
match *val {
Value::Unit => {}
Expand Down Expand Up @@ -274,9 +274,9 @@ impl BitMachine {
/// Execute the given program on the Bit Machine, using the given environment.
///
/// Make sure the Bit Machine has enough space by constructing it via [`Self::for_program()`].
pub fn exec<'a, J: Jet + std::fmt::Debug>(
pub fn exec<J: Jet + std::fmt::Debug>(
&mut self,
program: &'a RedeemNode<J>,
program: &RedeemNode<J>,
env: &J::Environment,
) -> Result<Value, ExecutionError> {
// Rust cannot use `J` from parent function
Expand Down Expand Up @@ -413,7 +413,7 @@ impl BitMachine {
RedeemNodeInner::Hidden(hash) => {
return Err(ExecutionError::ReachedPrunedBranch(*hash))
}
RedeemNodeInner::Jet(jet) => jet.exec()(self, env)?,
RedeemNodeInner::Jet(jet) => jet.exec(self, env)?,
RedeemNodeInner::Fail(left, right) => {
return Err(ExecutionError::ReachedFailNode(*left, *right))
}
Expand Down Expand Up @@ -480,3 +480,15 @@ impl From<JetFailed> for ExecutionError {
ExecutionError::JetFailed(jet_failed)
}
}

#[cfg(test)]
impl BitMachine {
/// Push a new frame of given size onto the write frame stack
/// without checking any assertions on IO. This is really handy when
/// we want to construct a BitMachine for testing purposes.
/// DO NOT USE THIS WITH REAL PROGRAMS. IT WILL FAIL.
pub(crate) fn new_frame_unchecked(&mut self, len: usize) {
self.write.push(Frame::new(self.next_frame_start, len));
self.next_frame_start += len;
}
}
2 changes: 1 addition & 1 deletion src/bit_machine/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#[allow(dead_code)]
pub mod exec;
mod frame;
pub(crate) mod frame;
4 changes: 2 additions & 2 deletions src/inference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,12 +342,12 @@ pub(crate) fn get_arrow<J: Jet>(
let pow2s = Variable::powers_of_two();
bind(
&arrow.source,
jet.source_ty().to_type(&pow2s),
jet.source_ty().to_variable_type(&pow2s),
"Cannot fail",
)?;
bind(
&arrow.target,
jet.target_ty().to_type(&pow2s),
jet.target_ty().to_variable_type(&pow2s),
"Cannot fail",
)?;
}
Expand Down
2 changes: 1 addition & 1 deletion src/jet/bitcoin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
//

mod environment;
#[allow(dead_code)]
mod exec;

use crate::jet::Bitcoin;
pub use environment::BitcoinEnv;
pub(crate) use exec::*;

impl std::fmt::Display for Bitcoin {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
Expand Down
222 changes: 222 additions & 0 deletions src/jet/elements/c_env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
//! High level APIs for creating C FFI compatible environment.
//!

use std::os::raw::{c_uchar, c_uint};

use elements::{
confidential,
encode::serialize,
secp256k1_zkp::{RangeProof, SurjectionProof},
taproot::ControlBlock,
};
use simplicity_sys::c_jets::c_env::{
c_set_rawInput, c_set_rawOutput, c_set_rawTapEnv, c_set_rawTransaction, c_set_txEnv,
elements_simplicity_mallocTapEnv, elements_simplicity_mallocTransaction, CElementsTxEnv,
CRawBuffer, CRawInput, CRawOutput, CRawTapEnv, CRawTransaction, CTapEnv, CTransaction,
RawInputData, RawOutputData, RawTransactionData,
};

use crate::merkle::cmr::Cmr;

use super::ElementsUtxo;

fn new_raw_output(out: &elements::TxOut, out_data: &RawOutputData) -> CRawOutput {
unsafe {
let mut raw_output = std::mem::MaybeUninit::<CRawOutput>::uninit();
c_set_rawOutput(
raw_output.as_mut_ptr(),
asset_ptr(out.asset, &out_data.asset),
value_ptr(out.value, &out_data.value),
nonce_ptr(out.nonce, &out_data.nonce),
&script_ptr(&out.script_pubkey),
&surjection_proof_ptr(&out_data.surjection_proof),
&range_proof_ptr(&out_data.range_proof),
);
raw_output.assume_init()
}
}

fn new_raw_input(
inp: &elements::TxIn,
in_utxo: &ElementsUtxo,
inp_data: &RawInputData,
) -> CRawInput {
unsafe {
let mut raw_input = std::mem::MaybeUninit::<CRawInput>::uninit();
c_set_rawInput(
raw_input.as_mut_ptr(),
opt_ptr(annex_ptr(&inp_data.annex).as_ref()), // FIXME: ACTUALLY STORE ANNEX
std::ptr::null(), // FIXME: ACTUALLY STORE PEGIN
&script_ptr(&inp.script_sig),
inp.previous_output.txid.as_ptr(),
inp.previous_output.vout as c_uint,
asset_ptr(in_utxo.asset, &inp_data.asset),
value_ptr(in_utxo.value, &inp_data.value),
&script_ptr(&in_utxo.script_pubkey),
inp.sequence as c_uint,
inp.asset_issuance.asset_blinding_nonce.as_ptr(), // FIXME: CHECK ASSET ISSUANCE IS NOT NULL. EASIER WITH NEW ELEMENTS VERSION.
inp.asset_issuance.asset_entropy.as_ptr(),
value_ptr(inp.asset_issuance.amount, &inp_data.issuance_amount),
value_ptr(
inp.asset_issuance.inflation_keys,
&inp_data.issuance_inflation_keys,
),
&range_proof_ptr(&inp_data.amount_range_proof),
&range_proof_ptr(&inp_data.inflation_keys_range_proof),
);
raw_input.assume_init()
}
}

fn new_tx_data(tx: &elements::Transaction, in_utxos: &[ElementsUtxo]) -> RawTransactionData {
let mut tx_data = RawTransactionData {
inputs: Vec::with_capacity(tx.input.len()),
outputs: Vec::with_capacity(tx.output.len()),
};
for (inp, in_utxo) in tx.input.iter().zip(in_utxos.iter()) {
let inp_data = RawInputData {
annex: None, // Actually store annex
issuance_amount: serialize(&inp.asset_issuance.amount),
issuance_inflation_keys: serialize(&inp.asset_issuance.inflation_keys),
amount_range_proof: serialize_rangeproof(&inp.witness.amount_rangeproof),
inflation_keys_range_proof: serialize_rangeproof(
&inp.witness.inflation_keys_rangeproof,
),
asset: serialize(&in_utxo.asset),
value: serialize(&in_utxo.value),
};
tx_data.inputs.push(inp_data);
}
for out in tx.output.iter() {
let out_data = RawOutputData {
asset: serialize(&out.asset),
value: serialize(&out.value),
nonce: serialize(&out.nonce),
surjection_proof: serialize_surjection_proof(&out.witness.surjection_proof),
range_proof: serialize_rangeproof(&out.witness.rangeproof),
};
tx_data.outputs.push(out_data);
}
tx_data
}

pub(super) fn new_tx(tx: &elements::Transaction, in_utxos: &[ElementsUtxo]) -> *mut CTransaction {
let mut raw_inputs = Vec::new();
let mut raw_outputs = Vec::new();
let tx_data = new_tx_data(tx, in_utxos);
for ((inp, in_utxo), inp_data) in tx
.input
.iter()
.zip(in_utxos.iter())
.zip(tx_data.inputs.iter())
{
let res = new_raw_input(inp, in_utxo, inp_data);
raw_inputs.push(res);
}
for (out, out_data) in tx.output.iter().zip(tx_data.outputs.iter()) {
raw_outputs.push(new_raw_output(out, out_data));
}
unsafe {
let mut raw_tx = std::mem::MaybeUninit::<CRawTransaction>::uninit();
c_set_rawTransaction(
raw_tx.as_mut_ptr(),
tx.version as c_uint,
raw_inputs.as_ptr(),
raw_inputs.len() as c_uint,
raw_outputs.as_ptr(),
raw_outputs.len() as c_uint,
tx.lock_time as c_uint,
);
let raw_tx = raw_tx.assume_init();
elements_simplicity_mallocTransaction(&raw_tx)
}
}

pub(super) fn new_tap_env(control_block: &ControlBlock, script_cmr: Cmr) -> *mut CTapEnv {
unsafe {
let mut raw_tap_env = std::mem::MaybeUninit::<CRawTapEnv>::uninit();
c_set_rawTapEnv(
raw_tap_env.as_mut_ptr(),
control_block.serialize().as_ptr(),
control_block.merkle_branch.as_inner().len() as c_uchar,
script_cmr.as_ref().as_ptr(),
);
let raw_tap_env = raw_tap_env.assume_init();
elements_simplicity_mallocTapEnv(&raw_tap_env)
}
}

pub(super) fn new_tx_env(
tx: *const CTransaction,
taproot: *const CTapEnv,
genesis_hash: elements::BlockHash,
ix: u32,
) -> CElementsTxEnv {
unsafe {
let mut tx_env = std::mem::MaybeUninit::<CElementsTxEnv>::uninit();
c_set_txEnv(tx_env.as_mut_ptr(), tx, taproot, genesis_hash.as_ptr(), ix);
tx_env.assume_init()
}
}

fn asset_ptr(asset: confidential::Asset, data: &[u8]) -> *const c_uchar {
if asset.is_null() {
std::ptr::null()
} else {
data.as_ptr()
}
}

fn value_ptr(value: confidential::Value, data: &[u8]) -> *const c_uchar {
if value.is_null() {
std::ptr::null()
} else {
data.as_ptr()
}
}

fn nonce_ptr(nonce: confidential::Nonce, data: &[u8]) -> *const c_uchar {
if nonce.is_null() {
std::ptr::null()
} else {
data.as_ptr()
}
}

fn opt_ptr<T>(t: Option<&T>) -> *const T {
if let Some(t) = t {
t
} else {
std::ptr::null()
}
}

fn script_ptr(script: &elements::Script) -> CRawBuffer {
CRawBuffer::new(script.as_bytes())
}

fn annex_ptr(annex: &Option<Vec<c_uchar>>) -> Option<CRawBuffer> {
annex.as_ref().map(|annex| CRawBuffer::new(annex))
}

fn surjection_proof_ptr(surjection_proof: &[c_uchar]) -> CRawBuffer {
CRawBuffer::new(surjection_proof)
}

fn range_proof_ptr(rangeproof: &[c_uchar]) -> CRawBuffer {
CRawBuffer::new(rangeproof)
}

fn serialize_rangeproof(rangeproof: &Option<Box<RangeProof>>) -> Vec<c_uchar> {
rangeproof
.as_ref()
.map(|x| x.serialize())
.unwrap_or_default()
}

fn serialize_surjection_proof(surjection_proof: &Option<Box<SurjectionProof>>) -> Vec<c_uchar> {
surjection_proof
.as_ref()
.map(|x| x.serialize())
.unwrap_or_default()
}
Loading