diff --git a/simplicity-sys/depend/env.c b/simplicity-sys/depend/env.c index 62570ed6..55c10a44 100644 --- a/simplicity-sys/depend/env.c +++ b/simplicity-sys/depend/env.c @@ -25,49 +25,6 @@ const size_t rustsimplicity_0_5_c_alignof_rawElementsTransaction = alignof(rawEl const size_t rustsimplicity_0_5_c_alignof_rawElementsTapEnv = alignof(rawElementsTapEnv); const size_t rustsimplicity_0_5_c_alignof_txEnv = alignof(txEnv); -void rustsimplicity_0_5_c_set_rawElementsBuffer(rawElementsBuffer *result, const unsigned char *buf, unsigned int len) -{ - *result = (rawElementsBuffer){.buf = buf, .len = len}; -} - -void rustsimplicity_0_5_c_set_rawElementsOutput(rawElementsOutput *result, const unsigned char *asset, const unsigned char *value, const unsigned char *nonce, const rawElementsBuffer *scriptPubKey, - const rawElementsBuffer *surjectionProof, const rawElementsBuffer *rangeProof) -{ - *result = (rawElementsOutput){.asset = asset, .value = value, .nonce = nonce, .scriptPubKey = *scriptPubKey, .surjectionProof = *surjectionProof, .rangeProof = *rangeProof}; -} - -void rustsimplicity_0_5_c_set_rawElementsInput(rawElementsInput *result, const rawElementsBuffer *annex, const unsigned char *pegin, const rawElementsBuffer *scriptSig, - const unsigned char *prevTxid, unsigned int prevIx, - const unsigned char *asset, const unsigned char *value, const rawElementsBuffer *scriptPubKey, - unsigned int sequence, - const unsigned char *blindingNonce, const unsigned char *assetEntropy, const unsigned char *amount, const unsigned char *inflationKeys, - const rawElementsBuffer *amountRangePrf, const rawElementsBuffer *inflationKeysRangePrf) -{ - *result = (rawElementsInput){.annex = annex, .scriptSig = *scriptSig, .prevTxid = prevTxid, .pegin = pegin, .issuance = {.blindingNonce = blindingNonce, .assetEntropy = assetEntropy, .amount = amount, .inflationKeys = inflationKeys, .amountRangePrf = *amountRangePrf, .inflationKeysRangePrf = *inflationKeysRangePrf}, .txo = {.asset = asset, .value = value, .scriptPubKey = *scriptPubKey}, .prevIx = prevIx, .sequence = sequence}; -} - -void rustsimplicity_0_5_c_set_rawElementsTransaction(rawElementsTransaction *result, unsigned int version, - const unsigned char *txid, - const rawElementsInput *input, unsigned int numInputs, - const rawElementsOutput *output, unsigned int numOutputs, - unsigned int lockTime) -{ - *result = (rawElementsTransaction){ - .version = version, - .txid = txid, - .input = input, - .numInputs = numInputs, - .output = output, - .numOutputs = numOutputs, - .lockTime = lockTime, - }; -} - -void rustsimplicity_0_5_c_set_rawElementsTapEnv(rawElementsTapEnv *result, const unsigned char *controlBlock, unsigned char pathLen, const unsigned char *scriptCMR) -{ - *result = (rawElementsTapEnv){.controlBlock = controlBlock, .pathLen = pathLen, .scriptCMR = scriptCMR}; -} - void rustsimplicity_0_5_c_set_txEnv(txEnv *result, const elementsTransaction *tx, const elementsTapEnv *taproot, const unsigned char *genesisHash, unsigned int ix) { sha256_midstate genesis; diff --git a/simplicity-sys/src/c_jets/c_env.rs b/simplicity-sys/src/c_jets/c_env.rs deleted file mode 100644 index 42eec296..00000000 --- a/simplicity-sys/src/c_jets/c_env.rs +++ /dev/null @@ -1,296 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -use hashes::{sha256, Hash}; - -use crate::ffi::sha256::CSha256Midstate; -use crate::ffi::{c_size_t, c_uchar, c_uint, c_uint_fast32_t}; - -/// Documentation of RawInputData/RawOutputData/RawTapData/Raw. -/// -/// Data structure for holding data that CTransaction points to. -/// -/// Why do we this special data structure? -/// 1. We need to keep the data in memory until the CTransaction is dropped. -/// 2. The memory is Transaction is not saved in the same format as required by FFI. -/// We use more ergonomics in rust to allow better UX which interfere with the FFI. For example, -/// the Value is stored as Tagged Union, but we require it to be a slice of bytes in elements format. -/// 3. Allocating inside FFI functions does not work because the memory is freed after the function returns. -/// 4. We only create allocations for data fields that are stored differently from the -/// consensus serialization format. -#[derive(Debug)] -pub struct RawOutputData { - pub asset: Vec, - pub value: Vec, - pub nonce: Vec, - pub surjection_proof: Vec, - pub range_proof: Vec, -} - -#[derive(Debug)] -#[repr(C)] -pub struct CElementsRawBuffer { - pub ptr: *const c_uchar, - pub len: u32, -} - -/// Similar to [`RawOutputData`], for inputs. -#[derive(Debug)] -pub struct RawInputData { - pub annex: Option>, - // issuance - pub issuance_amount: Vec, - pub issuance_inflation_keys: Vec, - pub amount_range_proof: Vec, - pub inflation_keys_range_proof: Vec, - // spent txo - pub asset: Vec, - pub value: Vec, -} - -/// Similar to [`RawOutputData`], but for transaction -#[derive(Debug)] -pub struct RawTransactionData { - pub inputs: Vec, - pub outputs: Vec, -} - -#[derive(Debug)] -#[repr(C)] -pub struct CElementsRawOutput { - asset: *const c_uchar, - value: *const c_uchar, - nonce: *const c_uchar, - script_pubkey: CElementsRawBuffer, - surjection_proof: CElementsRawBuffer, - range_proof: CElementsRawBuffer, -} - -#[derive(Debug)] -#[repr(C)] -pub struct CElementsRawInput { - annex: *const CElementsRawBuffer, - prev_txid: *const c_uchar, - pegin: *const c_uchar, - // issuance - blinding_nonce: *const c_uchar, - asset_entropy: *const c_uchar, - amount: *const c_uchar, - inflation_keys: *const c_uchar, - amount_range_proof: CElementsRawBuffer, - inflation_keys_range_proof: CElementsRawBuffer, - // spent txo - asset: *const c_uchar, - value: *const c_uchar, - script_pubkey: CElementsRawBuffer, - // inputs - script_sig: CElementsRawBuffer, - prev_txout_index: u32, - sequence: u32, -} - -#[derive(Debug)] -#[repr(C)] -pub struct CElementsRawTransaction { - txid: *const c_uchar, - inputs: *const CElementsRawInput, - outputs: *const CElementsRawOutput, - version: u32, - locktime: u32, - n_inputs: u32, - n_outputs: u32, -} - -#[derive(Debug)] -#[repr(C)] -pub struct CElementsRawTapEnv { - control_block: *const c_uchar, - script_cmr: *const c_uchar, - branch_len: u8, -} - -#[derive(Debug)] -pub enum CTransaction {} - -#[derive(Debug)] -#[repr(C)] -pub struct CElementsTxEnv { - tx: *const CTransaction, - taproot: *const CTapEnv, - genesis_hash: CSha256Midstate, - sighash_all: CSha256Midstate, - ix: c_uint_fast32_t, -} - -#[derive(Debug)] -pub enum CTapEnv {} - -extern "C" { - #[link_name = "rustsimplicity_0_5_c_sizeof_rawElementsBuffer"] - pub static c_sizeof_rawElementsBuffer: c_size_t; - #[link_name = "rustsimplicity_0_5_c_sizeof_rawElementsOutput"] - pub static c_sizeof_rawElementsOutput: c_size_t; - #[link_name = "rustsimplicity_0_5_c_sizeof_rawElementsInput"] - pub static c_sizeof_rawElementsInput: c_size_t; - #[link_name = "rustsimplicity_0_5_c_sizeof_rawElementsTransaction"] - pub static c_sizeof_rawElementsTransaction: c_size_t; - #[link_name = "rustsimplicity_0_5_c_sizeof_rawElementsTapEnv"] - pub static c_sizeof_rawElementsTapEnv: c_size_t; - #[link_name = "rustsimplicity_0_5_c_sizeof_txEnv"] - pub static c_sizeof_txEnv: c_size_t; - - #[link_name = "rustsimplicity_0_5_c_alignof_rawElementsBuffer"] - pub static c_alignof_rawElementsBuffer: c_size_t; - #[link_name = "rustsimplicity_0_5_c_alignof_rawElementsOutput"] - pub static c_alignof_rawElementsOutput: c_size_t; - #[link_name = "rustsimplicity_0_5_c_alignof_rawElementsInput"] - pub static c_alignof_rawElementsInput: c_size_t; - #[link_name = "rustsimplicity_0_5_c_alignof_rawElementsTransaction"] - pub static c_alignof_rawElementsTransaction: c_size_t; - #[link_name = "rustsimplicity_0_5_c_alignof_rawElementsTapEnv"] - pub static c_alignof_rawElementsTapEnv: c_size_t; - #[link_name = "rustsimplicity_0_5_c_alignof_txEnv"] - pub static c_alignof_txEnv: c_size_t; - - #[link_name = "rustsimplicity_0_5_c_set_rawElementsBuffer"] - pub fn c_set_rawElementsBuffer(res: *mut CElementsRawBuffer, buf: *const c_uchar, len: c_uint); - #[link_name = "rustsimplicity_0_5_c_set_rawElementsOutput"] - pub fn c_set_rawElementsOutput( - res: *mut CElementsRawOutput, - asset: *const c_uchar, - value: *const c_uchar, - nonce: *const c_uchar, - scriptPubKey: *const CElementsRawBuffer, - surjectionProof: *const CElementsRawBuffer, - rangeProof: *const CElementsRawBuffer, - ); - #[link_name = "rustsimplicity_0_5_c_set_rawElementsInput"] - pub fn c_set_rawElementsInput( - result: *mut CElementsRawInput, - annex: *const CElementsRawBuffer, - pegin: *const c_uchar, - scriptSig: *const CElementsRawBuffer, - prevTxid: *const c_uchar, - prevIx: c_uint, - asset: *const c_uchar, - value: *const c_uchar, - scriptPubKey: *const CElementsRawBuffer, - sequence: c_uint, - blindingNonce: *const c_uchar, - assetEntropy: *const c_uchar, - amount: *const c_uchar, - inflationKeys: *const c_uchar, - amountRangePrf: *const CElementsRawBuffer, - inflationKeysRangePrf: *const CElementsRawBuffer, - ); - - #[link_name = "rustsimplicity_0_5_c_set_rawElementsTransaction"] - pub fn c_set_rawElementsTransaction( - result: *mut CElementsRawTransaction, - version: c_uint, - txid: *const c_uchar, - input: *const CElementsRawInput, - numInputs: c_uint, - output: *const CElementsRawOutput, - numOutputs: c_uint, - lockTime: c_uint, - ); - #[link_name = "rustsimplicity_0_5_c_set_rawElementsTapEnv"] - pub fn c_set_rawElementsTapEnv( - result: *mut CElementsRawTapEnv, - controlBlock: *const c_uchar, - pathLen: c_uchar, - scriptCMR: *const c_uchar, - ); - #[link_name = "rustsimplicity_0_5_c_set_txEnv"] - pub fn c_set_txEnv( - result: *mut CElementsTxEnv, - tx: *const CTransaction, - taproot: *const CTapEnv, - genesisHash: *const c_uchar, - ix: c_uint, - ); - #[link_name = "rustsimplicity_0_5_elements_mallocTapEnv"] - pub fn simplicity_elements_mallocTapEnv(rawEnv: *const CElementsRawTapEnv) -> *mut CTapEnv; - #[link_name = "rustsimplicity_0_5_elements_mallocTransaction"] - pub fn simplicity_elements_mallocTransaction( - rawTx: *const CElementsRawTransaction, - ) -> *mut CTransaction; - #[link_name = "rustsimplicity_0_5_c_free_transaction"] - pub fn c_free_transaction(tx: *mut CTransaction); - #[link_name = "rustsimplicity_0_5_c_free_tapEnv"] - pub fn c_free_tapEnv(env: *mut CTapEnv); -} -impl CElementsTxEnv { - pub fn sighash_all(&self) -> sha256::Hash { - let midstate: sha256::Midstate = self.sighash_all.into(); - sha256::Hash::from_byte_array(midstate.to_byte_array()) - } -} - -// Pointer must be manually free after dropping -impl Drop for CElementsTxEnv { - fn drop(&mut self) { - unsafe { - crate::alloc::rust_0_5_free(self.tx as *mut u8); - crate::alloc::rust_0_5_free(self.taproot as *mut u8); - } - } -} - -impl CElementsRawBuffer { - pub fn new(buf: &[c_uchar]) -> Self { - unsafe { - let mut raw_buffer = std::mem::MaybeUninit::::uninit(); - c_set_rawElementsBuffer(raw_buffer.as_mut_ptr(), buf.as_ptr(), buf.len() as c_uint); - raw_buffer.assume_init() - } - } -} - -#[cfg(test)] -mod tests { - use std::mem::{align_of, size_of}; - - use crate::c_jets::{c_env::*, frame_ffi::*}; - - #[test] - fn test_sizes() { - unsafe { - assert_eq!(size_of::(), c_sizeof_frameItem); - assert_eq!(size_of::(), c_sizeof_rawElementsBuffer); - assert_eq!(size_of::(), c_sizeof_rawElementsInput); - assert_eq!(size_of::(), c_sizeof_rawElementsOutput); - assert_eq!( - size_of::(), - c_sizeof_rawElementsTransaction - ); - assert_eq!(size_of::(), c_sizeof_rawElementsTapEnv); - assert_eq!(size_of::(), c_sizeof_txEnv); - } - } - - #[test] - fn test_aligns() { - unsafe { - assert_eq!(align_of::(), c_alignof_frameItem); - assert_eq!( - align_of::(), - c_alignof_rawElementsBuffer - ); - assert_eq!(align_of::(), c_alignof_rawElementsInput); - assert_eq!( - align_of::(), - c_alignof_rawElementsOutput - ); - assert_eq!( - align_of::(), - c_alignof_rawElementsTransaction - ); - assert_eq!( - align_of::(), - c_alignof_rawElementsTapEnv - ); - assert_eq!(align_of::(), c_alignof_txEnv); - } - } -} diff --git a/simplicity-sys/src/c_jets/c_env/elements.rs b/simplicity-sys/src/c_jets/c_env/elements.rs new file mode 100644 index 00000000..1adbccd8 --- /dev/null +++ b/simplicity-sys/src/c_jets/c_env/elements.rs @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: CC0-1.0 + +use hashes::{sha256, Hash}; + +use crate::ffi::sha256::CSha256Midstate; +use crate::ffi::{c_size_t, c_uchar, c_uint, c_uint_fast32_t}; + +#[derive(Debug)] +#[repr(C)] +pub struct CRawBuffer { + pub ptr: *const c_uchar, + pub len: u32, +} + +#[derive(Debug)] +#[repr(C)] +pub struct CRawOutput<'raw> { + pub asset: Option<&'raw [c_uchar; 33]>, + pub value: *const c_uchar, + pub nonce: Option<&'raw [c_uchar; 33]>, + pub script_pubkey: CRawBuffer, + pub surjection_proof: CRawBuffer, + pub range_proof: CRawBuffer, +} + +#[repr(C)] +pub struct CRawInputIssuance<'raw> { + pub blinding_nonce: Option<&'raw [c_uchar; 32]>, + pub asset_entropy: Option<&'raw [c_uchar; 32]>, + pub amount: *const c_uchar, + pub inflation_keys: *const c_uchar, + pub amount_range_proof: CRawBuffer, + pub inflation_keys_range_proof: CRawBuffer, +} + +impl<'raw> CRawInputIssuance<'raw> { + /// Constructs a raw input issuance structure corresponding to "no issuance". + pub fn no_issuance() -> Self { + Self { + blinding_nonce: None, + asset_entropy: None, + amount: core::ptr::null(), + inflation_keys: core::ptr::null(), + amount_range_proof: CRawBuffer::new(&[]), + inflation_keys_range_proof: CRawBuffer::new(&[]), + } + } +} + +#[repr(C)] +pub struct CRawInputTxo<'raw> { + pub asset: Option<&'raw [c_uchar; 33]>, + pub value: *const c_uchar, + pub script_pubkey: CRawBuffer, +} + +#[repr(C)] +pub struct CRawInput<'raw> { + pub annex: *const CRawBuffer, + pub prev_txid: &'raw [c_uchar; 32], + pub pegin: Option<&'raw [c_uchar; 32]>, + pub issuance: CRawInputIssuance<'raw>, + pub txo: CRawInputTxo<'raw>, + pub script_sig: CRawBuffer, + pub prev_txout_index: u32, + pub sequence: u32, +} + +#[derive(Debug)] +#[repr(C)] +pub struct CRawTransaction<'raw> { + pub txid: &'raw [c_uchar; 32], + pub inputs: *const CRawInput<'raw>, + pub outputs: *const CRawOutput<'raw>, + pub n_inputs: u32, + pub n_outputs: u32, + pub version: u32, + pub locktime: u32, +} + +#[derive(Debug)] +#[repr(C)] +pub struct CRawTapEnv { + pub control_block: *const c_uchar, + pub script_cmr: *const c_uchar, + pub branch_len: u8, +} + +#[repr(C)] +pub struct CTransaction { + _data: (), +} + +#[derive(Debug)] +#[repr(C)] +pub struct CTxEnv { + tx: *const CTransaction, + taproot: *const CTapEnv, + genesis_hash: CSha256Midstate, + sighash_all: CSha256Midstate, + ix: c_uint_fast32_t, +} + +#[repr(C)] +pub struct CTapEnv { + _data: (), +} + +extern "C" { + #[link_name = "rustsimplicity_0_5_c_sizeof_rawElementsBuffer"] + pub static c_sizeof_rawBuffer: c_size_t; + #[link_name = "rustsimplicity_0_5_c_sizeof_rawElementsOutput"] + pub static c_sizeof_rawOutput: c_size_t; + #[link_name = "rustsimplicity_0_5_c_sizeof_rawElementsInput"] + pub static c_sizeof_rawInput: c_size_t; + #[link_name = "rustsimplicity_0_5_c_sizeof_rawElementsTransaction"] + pub static c_sizeof_rawTransaction: c_size_t; + #[link_name = "rustsimplicity_0_5_c_sizeof_rawElementsTapEnv"] + pub static c_sizeof_rawTapEnv: c_size_t; + #[link_name = "rustsimplicity_0_5_c_sizeof_txEnv"] + pub static c_sizeof_txEnv: c_size_t; + + #[link_name = "rustsimplicity_0_5_c_alignof_rawElementsBuffer"] + pub static c_alignof_rawBuffer: c_size_t; + #[link_name = "rustsimplicity_0_5_c_alignof_rawElementsOutput"] + pub static c_alignof_rawOutput: c_size_t; + #[link_name = "rustsimplicity_0_5_c_alignof_rawElementsInput"] + pub static c_alignof_rawInput: c_size_t; + #[link_name = "rustsimplicity_0_5_c_alignof_rawElementsTransaction"] + pub static c_alignof_rawTransaction: c_size_t; + #[link_name = "rustsimplicity_0_5_c_alignof_rawElementsTapEnv"] + pub static c_alignof_rawTapEnv: c_size_t; + #[link_name = "rustsimplicity_0_5_c_alignof_txEnv"] + pub static c_alignof_txEnv: c_size_t; + + #[link_name = "rustsimplicity_0_5_c_set_txEnv"] + pub fn c_set_txEnv( + result: *mut CTxEnv, + tx: *const CTransaction, + taproot: *const CTapEnv, + genesisHash: *const c_uchar, + ix: c_uint, + ); + #[link_name = "rustsimplicity_0_5_elements_mallocTapEnv"] + pub fn simplicity_mallocTapEnv(rawEnv: *const CRawTapEnv) -> *mut CTapEnv; + #[link_name = "rustsimplicity_0_5_elements_mallocTransaction"] + pub fn simplicity_mallocTransaction(rawTx: *const CRawTransaction) -> *mut CTransaction; +} +impl CTxEnv { + pub fn sighash_all(&self) -> sha256::Hash { + let midstate: sha256::Midstate = self.sighash_all.into(); + sha256::Hash::from_byte_array(midstate.to_byte_array()) + } +} + +// Pointer must be manually free after dropping +impl Drop for CTxEnv { + fn drop(&mut self) { + unsafe { + crate::alloc::rust_0_5_free(self.tx as *mut u8); + crate::alloc::rust_0_5_free(self.taproot as *mut u8); + } + } +} + +impl CRawBuffer { + pub fn new(buf: &[c_uchar]) -> Self { + Self { + ptr: buf.as_ptr(), + len: buf.len().try_into().expect("sane buffer lengths"), + } + } +} + +#[cfg(test)] +mod tests { + use core::mem::{align_of, size_of}; + + use crate::c_jets::frame_ffi::{c_alignof_frameItem, c_sizeof_frameItem, CFrameItem}; + + use super::*; + + #[test] + fn test_sizes() { + unsafe { + assert_eq!(size_of::(), c_sizeof_frameItem); + assert_eq!(size_of::(), c_sizeof_rawBuffer); + assert_eq!(size_of::(), c_sizeof_rawInput); + assert_eq!(size_of::(), c_sizeof_rawOutput); + assert_eq!(size_of::(), c_sizeof_rawTransaction); + assert_eq!(size_of::(), c_sizeof_rawTapEnv); + assert_eq!(size_of::(), c_sizeof_txEnv); + } + } + + #[test] + fn test_aligns() { + unsafe { + assert_eq!(align_of::(), c_alignof_frameItem); + assert_eq!(align_of::(), c_alignof_rawBuffer); + assert_eq!(align_of::(), c_alignof_rawInput); + assert_eq!(align_of::(), c_alignof_rawOutput); + assert_eq!(align_of::(), c_alignof_rawTransaction); + assert_eq!(align_of::(), c_alignof_rawTapEnv); + assert_eq!(align_of::(), c_alignof_txEnv); + } + } +} diff --git a/simplicity-sys/src/c_jets/c_env/mod.rs b/simplicity-sys/src/c_jets/c_env/mod.rs new file mode 100644 index 00000000..fcca587c --- /dev/null +++ b/simplicity-sys/src/c_jets/c_env/mod.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: CC0-1.0 + +pub mod elements; diff --git a/simplicity-sys/src/c_jets/mod.rs b/simplicity-sys/src/c_jets/mod.rs index 6175bf4f..2cc7d864 100644 --- a/simplicity-sys/src/c_jets/mod.rs +++ b/simplicity-sys/src/c_jets/mod.rs @@ -12,18 +12,13 @@ pub mod frame_ffi; #[rustfmt::skip] pub mod jets_ffi; #[rustfmt::skip] pub mod jets_wrapper; -pub use c_env::{CElementsTxEnv, CTapEnv, CTransaction}; +pub use c_env::elements; pub use c_frame::{byte_width, uword_width}; pub use frame_ffi::CFrameItem; // The bindings use elements_ffi instead of jets_ffi. pub use jets_ffi as elements_ffi; -use crate::c_jets::c_env::{ - CElementsRawBuffer, CElementsRawInput, CElementsRawOutput, CElementsRawTapEnv, - CElementsRawTransaction, -}; - #[cfg(feature = "test-utils")] pub mod exec_ffi; @@ -41,25 +36,25 @@ pub fn sanity_checks() -> bool { } if std::mem::size_of::() != frame_ffi::c_sizeof_frameItem - || std::mem::size_of::() != c_env::c_sizeof_rawElementsBuffer - || std::mem::size_of::() != c_env::c_sizeof_rawElementsInput - || std::mem::size_of::() != c_env::c_sizeof_rawElementsOutput - || std::mem::size_of::() - != c_env::c_sizeof_rawElementsTransaction - || std::mem::size_of::() != c_env::c_sizeof_txEnv - || std::mem::size_of::() != c_env::c_sizeof_rawElementsTapEnv + || std::mem::size_of::() != c_env::elements::c_sizeof_rawBuffer + || std::mem::size_of::() != c_env::elements::c_sizeof_rawInput + || std::mem::size_of::() != c_env::elements::c_sizeof_rawOutput + || std::mem::size_of::() + != c_env::elements::c_sizeof_rawTransaction + || std::mem::size_of::() != c_env::elements::c_sizeof_txEnv + || std::mem::size_of::() != c_env::elements::c_sizeof_rawTapEnv { return false; } if std::mem::align_of::() != frame_ffi::c_alignof_frameItem - || std::mem::align_of::() != c_env::c_alignof_rawElementsBuffer - || std::mem::align_of::() != c_env::c_alignof_rawElementsInput - || std::mem::align_of::() != c_env::c_alignof_rawElementsOutput - || std::mem::align_of::() - != c_env::c_alignof_rawElementsTransaction - || std::mem::align_of::() != c_env::c_alignof_txEnv - || std::mem::align_of::() != c_env::c_alignof_rawElementsTapEnv + || std::mem::align_of::() != c_env::elements::c_alignof_rawBuffer + || std::mem::align_of::() != c_env::elements::c_alignof_rawInput + || std::mem::align_of::() != c_env::elements::c_alignof_rawOutput + || std::mem::align_of::() + != c_env::elements::c_alignof_rawTransaction + || std::mem::align_of::() != c_env::elements::c_alignof_txEnv + || std::mem::align_of::() != c_env::elements::c_alignof_rawTapEnv { return false; } diff --git a/simplicity-sys/src/lib.rs b/simplicity-sys/src/lib.rs index 2b84fa2d..8202a892 100644 --- a/simplicity-sys/src/lib.rs +++ b/simplicity-sys/src/lib.rs @@ -1,7 +1,11 @@ // SPDX-License-Identifier: CC0-1.0 pub mod c_jets; -pub use c_jets::{CElementsTxEnv, CFrameItem, CTapEnv, CTransaction}; +pub use c_jets::elements; +pub use c_jets::CFrameItem; + +// Temporary to keep the Haskell-generated code compiling +pub use c_jets::elements::CTxEnv as CElementsTxEnv; pub mod alloc; pub mod ffi; diff --git a/simplicity-sys/src/tests/ffi.rs b/simplicity-sys/src/tests/ffi.rs index cbfe1035..1acad2c7 100644 --- a/simplicity-sys/src/tests/ffi.rs +++ b/simplicity-sys/src/tests/ffi.rs @@ -376,7 +376,7 @@ pub mod elements { pub mod eval { use super::*; - use crate::c_jets::c_env::CElementsTxEnv; + use crate::c_jets::c_env::elements; use crate::ffi::UWORD; use crate::tests::ffi::dag::CDagNode; use crate::tests::ffi::ty::CType; @@ -399,7 +399,7 @@ pub mod eval { type_dag: *mut CType, len: c_size_t, budget: *const ubounded, - env: *const CElementsTxEnv, + env: *const elements::CTxEnv, ) -> SimplicityErr; /// Given a well-typed dag representing a Simplicity expression, @@ -435,7 +435,7 @@ pub mod eval { type_dag: *mut CType, len: c_size_t, budget: *const ubounded, - env: *const CElementsTxEnv, + env: *const elements::CTxEnv, ) -> SimplicityErr { simplicity_evalTCOExpression( CHECK_ALL, diff --git a/src/jet/elements/c_env.rs b/src/jet/elements/c_env.rs index 78d4a00b..c50ab0fb 100644 --- a/src/jet/elements/c_env.rs +++ b/src/jet/elements/c_env.rs @@ -3,8 +3,8 @@ //! High level APIs for creating C FFI compatible environment. //! -use elements::secp256k1_zkp::ffi::CPtr; -use std::os::raw::{c_uchar, c_uint}; +use hashes::Hash; +use std::os::raw::c_uchar; use elements::{ confidential, @@ -12,82 +12,98 @@ use elements::{ secp256k1_zkp::{RangeProof, SurjectionProof}, taproot::ControlBlock, }; -use simplicity_sys::c_jets::c_env::{ - c_set_rawElementsInput, c_set_rawElementsOutput, c_set_rawElementsTapEnv, - c_set_rawElementsTransaction, c_set_txEnv, simplicity_elements_mallocTapEnv, - simplicity_elements_mallocTransaction, CElementsRawBuffer, CElementsRawInput, - CElementsRawOutput, CElementsRawTapEnv, CElementsRawTransaction, CElementsTxEnv, CTapEnv, - CTransaction, RawInputData, RawOutputData, RawTransactionData, -}; +use simplicity_sys::c_jets::c_env::elements as c_elements; use crate::merkle::cmr::Cmr; use super::ElementsUtxo; -fn new_raw_output(out: &elements::TxOut, out_data: &RawOutputData) -> CElementsRawOutput { - unsafe { - let mut raw_output = std::mem::MaybeUninit::::uninit(); - c_set_rawElementsOutput( - 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() +/// Holds transaction output data which needs to be re-serialized before being +/// passed to the C FFI. +#[derive(Debug)] +struct RawOutputData { + pub asset: Option<[c_uchar; 33]>, + pub value: Vec, + pub nonce: Option<[c_uchar; 33]>, + pub surjection_proof: Vec, + pub range_proof: Vec, +} + +/// Holds transaction input data which needs to be re-serialized before being +/// passed to the C FFI. +#[derive(Debug)] +struct RawInputData { + #[allow(dead_code)] // see FIXME below + pub annex: Option>, + // pegin + pub genesis_hash: Option<[c_uchar; 32]>, + // issuance + pub issuance_amount: Vec, + pub issuance_inflation_keys: Vec, + pub amount_range_proof: Vec, + pub inflation_keys_range_proof: Vec, + // spent txo + pub asset: Option<[c_uchar; 33]>, + pub value: Vec, +} + +/// Holds transaction data which needs to be re-serialized before being +/// passed to the C FFI. +#[derive(Debug)] +struct RawTransactionData { + pub inputs: Vec, + pub outputs: Vec, +} + +fn new_raw_output<'raw>( + out: &elements::TxOut, + out_data: &'raw RawOutputData, +) -> c_elements::CRawOutput<'raw> { + c_elements::CRawOutput { + asset: out_data.asset.as_ref(), + value: value_ptr(out.value, &out_data.value), + nonce: out_data.nonce.as_ref(), + script_pubkey: c_elements::CRawBuffer::new(out.script_pubkey.as_bytes()), + surjection_proof: c_elements::CRawBuffer::new(&out_data.surjection_proof), + range_proof: c_elements::CRawBuffer::new(&out_data.range_proof), } } -fn new_raw_input( - inp: &elements::TxIn, - in_utxo: &ElementsUtxo, - inp_data: &RawInputData, -) -> CElementsRawInput { - unsafe { - let mut raw_input = std::mem::MaybeUninit::::uninit(); - - let (issue_nonce_ptr, issue_entropy_ptr, issue_amt_ptr, issue_infl_key_ptr) = - if inp.has_issuance() { - ( - inp.asset_issuance.asset_blinding_nonce.as_c_ptr(), - 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, - ), - ) - } else { - ( - std::ptr::null(), - std::ptr::null(), - std::ptr::null(), - std::ptr::null(), - ) - }; - c_set_rawElementsInput( - raw_input.as_mut_ptr(), - opt_ptr(annex_ptr(&inp_data.annex).as_ref()), - inp.pegin_data() - .map(|x| AsRef::<[u8]>::as_ref(&x.genesis_hash).as_ptr()) - .unwrap_or(std::ptr::null()), - &script_ptr(&inp.script_sig), - AsRef::<[u8]>::as_ref(&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.0 as c_uint, - issue_nonce_ptr, // FIXME: CHECK ASSET ISSUANCE IS NOT NULL. EASIER WITH NEW ELEMENTS VERSION. - issue_entropy_ptr, - issue_amt_ptr, - issue_infl_key_ptr, - &range_proof_ptr(&inp_data.amount_range_proof), - &range_proof_ptr(&inp_data.inflation_keys_range_proof), - ); - raw_input.assume_init() +fn new_raw_input<'raw>( + inp: &'raw elements::TxIn, + in_utxo: &'raw ElementsUtxo, + inp_data: &'raw RawInputData, +) -> c_elements::CRawInput<'raw> { + c_elements::CRawInput { + // FIXME actually pass the annex in; see https://github.com/BlockstreamResearch/simplicity/issues/311 for some difficulty here. + annex: core::ptr::null(), + prev_txid: inp.previous_output.txid.as_ref(), + pegin: inp_data.genesis_hash.as_ref(), + issuance: if inp.has_issuance() { + c_elements::CRawInputIssuance { + blinding_nonce: Some(inp.asset_issuance.asset_blinding_nonce.as_ref()), + asset_entropy: Some(&inp.asset_issuance.asset_entropy), + amount: value_ptr(inp.asset_issuance.amount, &inp_data.issuance_amount), + inflation_keys: value_ptr( + inp.asset_issuance.inflation_keys, + &inp_data.issuance_inflation_keys, + ), + amount_range_proof: c_elements::CRawBuffer::new(&inp_data.amount_range_proof), + inflation_keys_range_proof: c_elements::CRawBuffer::new( + &inp_data.inflation_keys_range_proof, + ), + } + } else { + c_elements::CRawInputIssuance::no_issuance() + }, + txo: c_elements::CRawInputTxo { + asset: inp_data.asset.as_ref(), + value: value_ptr(in_utxo.value, &inp_data.value), + script_pubkey: c_elements::CRawBuffer::new(in_utxo.script_pubkey.as_bytes()), + }, + script_sig: c_elements::CRawBuffer::new(inp.script_sig.as_bytes()), + prev_txout_index: inp.previous_output.vout, + sequence: inp.sequence.to_consensus_u32(), } } @@ -99,22 +115,25 @@ fn new_tx_data(tx: &elements::Transaction, in_utxos: &[ElementsUtxo]) -> RawTran for (inp, in_utxo) in tx.input.iter().zip(in_utxos.iter()) { let inp_data = RawInputData { annex: None, // Actually store annex + genesis_hash: inp + .pegin_data() + .map(|x| x.genesis_hash.to_raw_hash().to_byte_array()), 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), + asset: asset_array(&in_utxo.asset), value: serialize(&in_utxo.value), }; tx_data.inputs.push(inp_data); } for out in &tx.output { let out_data = RawOutputData { - asset: serialize(&out.asset), + asset: asset_array(&out.asset), value: serialize(&out.value), - nonce: serialize(&out.nonce), + nonce: nonce_array(&out.nonce), surjection_proof: serialize_surjection_proof(&out.witness.surjection_proof), range_proof: serialize_rangeproof(&out.witness.rangeproof), }; @@ -123,7 +142,10 @@ fn new_tx_data(tx: &elements::Transaction, in_utxos: &[ElementsUtxo]) -> RawTran tx_data } -pub(super) fn new_tx(tx: &elements::Transaction, in_utxos: &[ElementsUtxo]) -> *mut CTransaction { +pub(super) fn new_tx( + tx: &elements::Transaction, + in_utxos: &[ElementsUtxo], +) -> *mut c_elements::CTransaction { let mut raw_inputs = Vec::new(); let mut raw_outputs = Vec::new(); let txid = tx.txid(); @@ -140,47 +162,53 @@ pub(super) fn new_tx(tx: &elements::Transaction, in_utxos: &[ElementsUtxo]) -> * for (out, out_data) in tx.output.iter().zip(tx_data.outputs.iter()) { raw_outputs.push(new_raw_output(out, out_data)); } + + let c_raw_tx = c_elements::CRawTransaction { + txid: txid.as_raw_hash().as_byte_array(), + inputs: raw_inputs.as_ptr(), + outputs: raw_outputs.as_ptr(), + n_inputs: raw_inputs.len().try_into().expect("sane length"), + n_outputs: raw_outputs.len().try_into().expect("sane length"), + version: tx.version, + locktime: tx.lock_time.to_consensus_u32(), + }; unsafe { - let mut raw_tx = std::mem::MaybeUninit::::uninit(); - c_set_rawElementsTransaction( - raw_tx.as_mut_ptr(), - tx.version as c_uint, - AsRef::<[u8]>::as_ref(&txid).as_ptr(), - raw_inputs.as_ptr(), - raw_inputs.len() as c_uint, - raw_outputs.as_ptr(), - raw_outputs.len() as c_uint, - tx.lock_time.to_consensus_u32() as c_uint, - ); - let raw_tx = raw_tx.assume_init(); - simplicity_elements_mallocTransaction(&raw_tx) + // SAFETY: this is a FFI call and we constructed its argument correctly. + c_elements::simplicity_mallocTransaction(&c_raw_tx) } } -pub(super) fn new_tap_env(control_block: &ControlBlock, script_cmr: Cmr) -> *mut CTapEnv { +pub(super) fn new_tap_env( + control_block: &ControlBlock, + script_cmr: Cmr, +) -> *mut c_elements::CTapEnv { + let cb_ser = control_block.serialize(); + let raw_tap_env = c_elements::CRawTapEnv { + control_block: cb_ser.as_ptr(), + script_cmr: script_cmr.as_ref().as_ptr(), + branch_len: control_block + .merkle_branch + .as_inner() + .len() + .try_into() + .expect("sane length"), + }; + unsafe { - let mut raw_tap_env = std::mem::MaybeUninit::::uninit(); - let cb_ser = control_block.serialize(); - c_set_rawElementsTapEnv( - raw_tap_env.as_mut_ptr(), - cb_ser.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(); - simplicity_elements_mallocTapEnv(&raw_tap_env) + // SAFETY: this is a FFI call and we constructed its argument correctly. + c_elements::simplicity_mallocTapEnv(&raw_tap_env) } } pub(super) fn new_tx_env( - tx: *const CTransaction, - taproot: *const CTapEnv, + tx: *const c_elements::CTransaction, + taproot: *const c_elements::CTapEnv, genesis_hash: elements::BlockHash, ix: u32, -) -> CElementsTxEnv { +) -> c_elements::CTxEnv { unsafe { - let mut tx_env = std::mem::MaybeUninit::::uninit(); - c_set_txEnv( + let mut tx_env = std::mem::MaybeUninit::::uninit(); + c_elements::c_set_txEnv( tx_env.as_mut_ptr(), tx, taproot, @@ -191,54 +219,30 @@ pub(super) fn new_tx_env( } } -fn asset_ptr(asset: confidential::Asset, data: &[u8]) -> *const c_uchar { - if asset.is_null() { - std::ptr::null() - } else { - data.as_ptr() - } +fn asset_array(asset: &confidential::Asset) -> Option<[u8; 33]> { + (!asset.is_null()).then(|| { + serialize(asset) + .try_into() + .expect("non-null asset is 33 bytes") + }) } -fn value_ptr(value: confidential::Value, data: &[u8]) -> *const c_uchar { - if value.is_null() { - std::ptr::null() - } else { - data.as_ptr() - } +fn nonce_array(nonce: &confidential::Nonce) -> Option<[u8; 33]> { + (!nonce.is_null()).then(|| { + serialize(nonce) + .try_into() + .expect("non-null asset is 33 bytes") + }) } -fn nonce_ptr(nonce: confidential::Nonce, data: &[u8]) -> *const c_uchar { - if nonce.is_null() { +fn value_ptr(value: confidential::Value, data: &[u8]) -> *const c_uchar { + if value.is_null() { std::ptr::null() } else { data.as_ptr() } } -fn opt_ptr(t: Option<&T>) -> *const T { - if let Some(t) = t { - t - } else { - std::ptr::null() - } -} - -fn script_ptr(script: &elements::Script) -> CElementsRawBuffer { - CElementsRawBuffer::new(script.as_bytes()) -} - -fn annex_ptr(annex: &Option>) -> Option { - annex.as_ref().map(|annex| CElementsRawBuffer::new(annex)) -} - -fn surjection_proof_ptr(surjection_proof: &[c_uchar]) -> CElementsRawBuffer { - CElementsRawBuffer::new(surjection_proof) -} - -fn range_proof_ptr(rangeproof: &[c_uchar]) -> CElementsRawBuffer { - CElementsRawBuffer::new(rangeproof) -} - fn serialize_rangeproof(rangeproof: &Option>) -> Vec { rangeproof .as_ref() diff --git a/src/jet/elements/environment.rs b/src/jet/elements/environment.rs index 94ef1b3e..fc3a9433 100644 --- a/src/jet/elements/environment.rs +++ b/src/jet/elements/environment.rs @@ -3,7 +3,7 @@ use crate::merkle::cmr::Cmr; use elements::confidential; use elements::taproot::ControlBlock; -use simplicity_sys::c_jets::c_env::CElementsTxEnv; +use simplicity_sys::c_jets::c_env::elements as c_elements; use std::ops::Deref; use super::c_env; @@ -39,7 +39,7 @@ impl From for ElementsUtxo { #[derive(Debug)] pub struct ElementsEnv> { /// The CTxEnv struct - c_tx_env: CElementsTxEnv, + c_tx_env: c_elements::CTxEnv, /// The elements transaction tx: T, /// the current index of the input @@ -79,7 +79,7 @@ where } /// Obtains the FFI compatible CTxEnv from self - pub fn c_tx_env(&self) -> &CElementsTxEnv { + pub fn c_tx_env(&self) -> &c_elements::CTxEnv { &self.c_tx_env }