From 3382d8ace73f43c20f639d3e0038160177c9d687 Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Tue, 17 Feb 2026 16:35:02 +0200 Subject: [PATCH 01/14] wip: proc macro for implementing custom extension --- Cargo.lock | 35 +++ Cargo.toml | 2 +- core/Cargo.toml | 2 + core/src/jets/custom.rs | 6 + core/src/jets/mod.rs | 1 + jet_plugins/Cargo.toml | 13 + jet_plugins/src/helpers.rs | 363 +++++++++++++++++++++++++++ jet_plugins/src/lib.rs | 489 +++++++++++++++++++++++++++++++++++++ 8 files changed, 910 insertions(+), 1 deletion(-) create mode 100644 core/src/jets/custom.rs create mode 100644 jet_plugins/Cargo.toml create mode 100644 jet_plugins/src/helpers.rs create mode 100644 jet_plugins/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 9981881..d56bfb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -414,9 +414,11 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" name = "core" version = "0.1.0" dependencies = [ + "dlopen2", "hal-simplicity", "hex 0.4.3", "hex-literal", + "jet_plugins", "thiserror", ] @@ -482,6 +484,29 @@ dependencies = [ "syn", ] +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "elements" version = "0.25.2" @@ -1084,6 +1109,16 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" +[[package]] +name = "jet_plugins" +version = "0.1.0" +dependencies = [ + "bitcoin_hashes", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "jobserver" version = "0.1.34" diff --git a/Cargo.toml b/Cargo.toml index 77df24f..799cfeb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["cli", "core", "service"] +members = ["cli", "core", "jet_plugins", "service"] [workspace.dependencies] hal-simplicity = { path = "../hal-unchained" } diff --git a/core/Cargo.toml b/core/Cargo.toml index 6b7a6bc..24d9f88 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -13,3 +13,5 @@ hex-literal = "1.1.0" hal-simplicity = { workspace = true } thiserror = { workspace = true } hex = { workspace = true } +dlopen2 = "0.8.2" +jet_plugins = { path = "../jet_plugins" } diff --git a/core/src/jets/custom.rs b/core/src/jets/custom.rs new file mode 100644 index 0000000..019e4d3 --- /dev/null +++ b/core/src/jets/custom.rs @@ -0,0 +1,6 @@ +use jet_plugins::register_jets; +// File for testing +register_jets!( + "custom_jet1" => custom_jet1, b"h", b"h"; + "custom_jet2" => custom_jet2, b"h", b"h"; +); diff --git a/core/src/jets/mod.rs b/core/src/jets/mod.rs index fa0d9a4..da6a895 100644 --- a/core/src/jets/mod.rs +++ b/core/src/jets/mod.rs @@ -1,4 +1,5 @@ pub mod bitcoin; +mod custom; pub mod elements; pub mod environments; pub mod exec; diff --git a/jet_plugins/Cargo.toml b/jet_plugins/Cargo.toml new file mode 100644 index 0000000..0ea27c8 --- /dev/null +++ b/jet_plugins/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "jet_plugins" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "2.0", features = ["full"] } +quote = "1.0" +proc-macro2 = "1.0" +bitcoin_hashes = "0.14.1" diff --git a/jet_plugins/src/helpers.rs b/jet_plugins/src/helpers.rs new file mode 100644 index 0000000..199fe46 --- /dev/null +++ b/jet_plugins/src/helpers.rs @@ -0,0 +1,363 @@ +use bitcoin_hashes::{Hash, HashEngine, sha256}; +use quote::quote; +use std::{fs::create_dir, mem::MaybeUninit}; + +const SIMPLICITY_TAG_PREFIX: &[u8] = b"Simplicity\x1fCommitment\x1f"; +const JETIV: sha256::Midstate = sha256::Midstate([ + 0x95, 0x32, 0xee, 0x28, 0xcd, 0xca, 0x69, 0xde, 0xc8, 0xa0, 0xa2, 0x18, 0xb7, 0x9b, 0xe3, 0x62, + 0xf7, 0x40, 0xce, 0xaf, 0x64, 0x7f, 0x15, 0xb3, 0x8a, 0xed, 0x91, 0x68, 0x16, 0x3f, 0x92, 0x1b, +]); +pub const ENCODE_PREFIX: &[u8] = &[1, 1, 1, 1]; +pub type JetCodeBits = u16; +pub const JET_ENC_BITLEN: usize = 8 * std::mem::size_of::(); +// `Jet::encode` uses `write_bits_be` which is bounded to u64 +const CHECK_CODE_BITS_TYPE: () = { + let _ = 0 as JetCodeBits; + if std::mem::size_of::() > std::mem::size_of::() { + panic!("JetCodeBits type should not exceed u64") + } +}; + +// Warning: The CMRs generated here does not follow the proper Simplicity specification. +// +// TODO(ivanlele): Build valid Simplicity in Haskell from which we can extract the true CMRs. +// Taken from core::utils +#[allow(unused)] +pub fn cmr(name: &str) -> [u8; 32] { + let name = SIMPLICITY_TAG_PREFIX + .iter() + .chain(name.as_bytes().iter()) + .copied() + .collect::>(); + + let right_state = sha256::Hash::hash(&name).as_byte_array().to_owned(); + + let mut engine = sha256::HashEngine::from_midstate(JETIV, 0); + engine.input(&right_state); + + right_state +} + +/// Helper structure for converting jet bit encoding to `decode_bits!` macro input format +/// By construction ensures that token and left/right can not be Some() simultaneously +pub struct JetDecodeTree { + left: Option>, + right: Option>, + token: Option, +} + +// Stores bit pattern starting from most significant bit to be able to input patterns in BE order i.e. +// 0b111 -> +// 0 => {} +// 1 => { +// 0 => {} +// 1 => { +// 0 => {} +// 1 => { +// Ident +// } +// } +// } +#[derive(Clone)] +pub struct JetBranchCode { + pub bits: JetCodeBits, + pub token: proc_macro2::Ident, +} + +impl JetBranchCode { + // Shifts number's MSB to type MSB + pub fn new(bits: JetCodeBits, len: usize, token: proc_macro2::Ident) -> Self { + Self { + bits: bits << (JET_ENC_BITLEN - len), + token, + } + } + + /// Hashes identifier string and takes `JET_ENC_BITLEN - ENCODE_PREFIX.len()` bits of that hash + /// as jet's encoding alongside with `ENCODE_PREFIX` + pub fn from_ident_fixed(token: proc_macro2::Ident) -> Self { + let mut bits = 0 as JetCodeBits; + let mut cursor = 1; + + for &bit in ENCODE_PREFIX { + if bit == 1 { + bits |= cursor; + } + cursor <<= 1; + } + + let mut token_branch = [0 as u8; std::mem::size_of::()]; + token_branch.copy_from_slice( + &sha256::Hash::hash(token.to_string().as_bytes()).to_byte_array() + [0..std::mem::size_of::()], + ); + let mut token_bits = JetCodeBits::from_le_bytes(token_branch); + + for _ in 0..(JET_ENC_BITLEN - ENCODE_PREFIX.len()) { + if token_bits & 1 == 1 { + bits |= cursor; + } + cursor <<= 1; + token_bits >>= 1; + } + + Self { + bits: bits.reverse_bits(), + token, + } + } +} + +impl JetDecodeTree { + fn new() -> Self { + Self { + left: None, + right: None, + token: None, + } + } + /// Constructs `JetDecodeTree` from branches. + /// ## Panics + /// Panics if some branches bit patterns collide or some bit pattern is prefix of another + pub fn from_branches(branches: Vec) -> Self { + // check for pattern collision + for i in 0..branches.len() { + for j in (i + 1)..branches.len() { + assert!( + branches[i].bits != branches[j].bits, + "Idents {}, {} collide", + branches[i].token.to_string(), + branches[j].token.to_string() + ) + } + } + + let mut res = Self::new(); + + for JetBranchCode { bits, token } in branches { + let mut curr = &mut res; + let mut cursor = bits.reverse_bits(); + + while cursor != 0 { + if curr.token.is_some() { + panic!( + "Existing branch is a prefix of the new branch {} being added", + bits + ); + } + let bit = (cursor & 1) == 1; + + match bit { + false => { + if curr.left.is_none() { + curr.left = Some(Box::new(JetDecodeTree::new())); + } + curr = curr.left.as_mut().unwrap(); + } + true => { + if curr.right.is_none() { + curr.right = Some(Box::new(JetDecodeTree::new())); + } + curr = curr.right.as_mut().unwrap(); + } + } + cursor >>= 1; + } + + if curr.left.is_some() || curr.right.is_some() { + panic!("{} branch is prefix of some other branch", bits); + } + curr.token = Some(token) + } + + res + } +} + +impl Into for JetDecodeTree { + /// Panics if branch somehow contains branch as child and `Ident` value simultaneously + fn into(self) -> proc_macro2::TokenStream { + let (left_branch, right_branch, token) = (self.left, self.right, self.token); + + let (left, right, val) = match (left_branch, right_branch, token) { + (Some(left_branch), Some(right_branch), _) => { + (Self::into(*left_branch), Self::into(*right_branch), None) + } + (Some(left_branch), None, _) => (Self::into(*left_branch), quote! {}, None), + (None, Some(right_branch), _) => (quote! {}, Self::into(*right_branch), None), + (None, None, Some(ident)) => (quote! {}, quote! {}, Some(ident)), + _ => unreachable!("Non null ident implifies null left/right branches by construction"), + }; + + if let Some(ident) = val { + return quote! {Self::#ident}; + } + quote! { + 0 => { + #left + }, + 1 => { + #right + } + } + } +} + +pub fn snake_to_pascal_case(s: &str) -> String { + s.split('_') + .map(|word| { + let mut chars = word.chars(); + match chars.next() { + None => String::new(), + Some(f) => f.to_uppercase().collect::() + chars.as_str(), + } + }) + .collect() +} +pub fn pascal_to_snake_case(s: &str) -> String { + let mut snake = String::new(); + let chars = s.chars().collect::>(); + + chars.windows(2).for_each(|pair| { + if let [curr, next] = pair { + snake.extend(pair[0].to_lowercase()); + if curr.is_lowercase() && !next.is_lowercase() { + snake.push('_'); + } + } + }); + + if let Some(c) = chars.last() { + snake.extend(c.to_lowercase()); + } + + snake +} + +// Currently hardcoded inside `CustomExtension::ALL` impl, maybe worth to move it out there +struct AllVariantsBuilder { + data: [MaybeUninit; LEN], + len: usize, +} + +impl AllVariantsBuilder { + const fn new() -> Self { + Self { + data: [MaybeUninit::uninit(); LEN], + len: 0, + } + } + + const fn push(&mut self, item: Enum) { + assert!(self.len < self.data.len()); + + self.data[self.len].write(item); + self.len += 1; + } + + const fn finalize(self) -> [Enum; LEN] { + assert!(self.len == LEN); + + let ptr = &self.data as *const [MaybeUninit; LEN] as *const [Enum; LEN]; + let res = unsafe { std::ptr::read(ptr) }; + + std::mem::forget(self.data); + + res + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_to_pascal_case() { + let snake = "valid_str"; + let single = "single"; + let empty = ""; + + assert_eq!(snake_to_pascal_case(&snake), "ValidStr"); + assert_eq!(snake_to_pascal_case(&single), "Single"); + assert_eq!(snake_to_pascal_case(&empty), ""); + } + + #[test] + fn test_to_snake_case() { + let pascal = "PascalCase"; + let single = "Single"; + let acronym = "PAScalCase"; + let empty = ""; + + assert_eq!(pascal_to_snake_case(pascal), "pascal_case"); + assert_eq!(pascal_to_snake_case(single), "single"); + assert_eq!(pascal_to_snake_case(acronym), "pascal_case"); + assert_eq!(pascal_to_snake_case(empty), ""); + } + + fn format_arms(ts: proc_macro2::TokenStream) -> String { + let mut lines: Vec = Vec::new(); + let mut indent = 0usize; + let mut line: Vec<&str> = Vec::new(); + + let s = ts.to_string(); + + let flush = |line: &mut Vec<&str>, indent: usize, lines: &mut Vec| { + if !line.is_empty() { + lines.push(format!("{}{}", " ".repeat(indent), line.join(" "))); + line.clear(); + } + }; + + for token in s.split_whitespace() { + match token { + "{" => { + line.push("{"); + flush(&mut line, indent, &mut lines); + indent += 1; + } + "}" => { + flush(&mut line, indent, &mut lines); + indent = indent.saturating_sub(1); + lines.push(format!("{}}}", " ".repeat(indent))); + } + "," => { + flush(&mut line, indent, &mut lines); + } + t => line.push(t), + } + } + + flush(&mut line, indent, &mut lines); + lines.join("\n") + } + + fn depr_test_decode_tree() { + let branches = vec![ + JetBranchCode::new(0b11101, 5, quote::format_ident!("Ident1")), + JetBranchCode::new(0b1111, 4, quote::format_ident!("Ident2")), + ]; + + let tree = JetDecodeTree::from_branches(branches); + let tree_tokens: proc_macro2::TokenStream = tree.into(); + + let formatted = format_arms(tree_tokens); + + println!("{}", formatted); + } + + #[test] + fn test_decode_tree() { + let branches = vec![ + JetBranchCode::from_ident_fixed(quote::format_ident!("custom_jet1")), + JetBranchCode::from_ident_fixed(quote::format_ident!("custom_jet2")), + ]; + + let tree = JetDecodeTree::from_branches(branches); + let tree_tokens: proc_macro2::TokenStream = tree.into(); + + let formatted = format_arms(tree_tokens); + + println!("{}", formatted); + } +} diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs new file mode 100644 index 0000000..cdfe041 --- /dev/null +++ b/jet_plugins/src/lib.rs @@ -0,0 +1,489 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{ + Ident, LitByteStr, LitStr, Path, Token, + parse::{Parse, ParseStream}, + parse_macro_input, parse2, + punctuated::Punctuated, +}; + +mod helpers; +use helpers::snake_to_pascal_case; + +use crate::helpers::{ENCODE_PREFIX, JET_ENC_BITLEN, JetBranchCode, JetDecodeTree}; + +// TODO: get rid of hardcoded import +const STRUCT_EXTENSION_NAME: &str = "JetExtension"; +const ELEMENTS_ENUM_PATH: &str = "hal_simplicity::simplicity::jet::Elements"; +const JET_TRAIT_PATH: &str = "hal_simplicity::simplicity::jet::Jet"; +const UNCHAINED_ENV_PATH: &str = "hal_simplicity::simplicity::environments::ElementsUnchainedEnv"; +const CMR_BY_PATH: &str = "hal_simplicity::simplicity::Cmr"; +const TYPE_NAME_BY_PATH: &str = "hal_simplicity::simplicity::jet::type_name::TypeName"; + +struct StaticTokenInfo {} + +impl StaticTokenInfo { + fn enum_ident() -> proc_macro2::Ident { + quote::format_ident!("{}", STRUCT_EXTENSION_NAME) + } + + fn elements_enum_by_path() -> syn::Path { + syn::parse_str(ELEMENTS_ENUM_PATH).expect("Failed to find elements enum by given path") + } + + fn jet_trait_by_path() -> syn::Path { + syn::parse_str(JET_TRAIT_PATH).expect("Failed to find Jet trait by given path") + } + + fn unchained_env_by_path() -> syn::Path { + syn::parse_str(UNCHAINED_ENV_PATH).expect("Failed to find UnchainedEnv by given path") + } + + fn cmr_by_path() -> syn::Path { + syn::parse_str(CMR_BY_PATH).expect("Failed to find Cmr by given path") + } + + fn type_name_by_path() -> syn::Path { + syn::parse_str(TYPE_NAME_BY_PATH).expect("Failed to find TypeName by given path") + } +} + +#[proc_macro] +pub fn register_jets(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse2(input.into()).expect("Failed to parse JetsInput"); + + let names = build_custom_fields(&input); + + let self_impl = self_impl_full(&names); + let jet_trait_impl = jet_trait_full(&names, &input); + + quote! { + #self_impl + #jet_trait_impl + } + .into() +} + +fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2::TokenStream { + let associated_types = impl_associated_types(); + let c_jet_env = impl_c_jet_env(); + let cmr = impl_cmr(names); + + let src_ty = impl_src_trg_ty(names, &build_source_tys(jets), "source"); + let trgt_ty = impl_src_trg_ty(names, &build_source_tys(jets), "target"); + + let jet_codes = build_jet_codes(names); + + let jet_encode = impl_jet_encode(names, &jet_codes); + let jet_decode = impl_jet_decode(jet_codes); + + let c_jet_ptr = impl_c_jet_ptr(); + let cost = impl_cost(); + + let jet_trait_path = StaticTokenInfo::jet_trait_by_path(); + let enum_ident = StaticTokenInfo::enum_ident(); + quote! { + // maybe consider moving it somewhere + macro_rules! decode_bits { + ($bits:ident, {}) => { + Err(::simplicity::decode::Error::InvalidJet.into()) + }; + ($bits:ident, {$jet:expr}) => { + Ok($jet) + }; + ($bits:ident, { 0 => $false_branch:tt, 1 => $true_branch:tt }) => { + match $bits.next() { + None => Err(::simplicity::decode::Error::EndOfStream.into()), + Some(false) => decode_bits!($bits, $false_branch), + Some(true) => decode_bits!($bits, $true_branch), + } + }; + } + impl #jet_trait_path for #enum_ident { + #associated_types + #c_jet_env + #cmr + #src_ty + #trgt_ty + #jet_encode + #jet_decode + #c_jet_ptr + #cost + } + } +} + +fn self_impl_full(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { + let definition = impl_enum_definition(&names); + let all_impl = impl_all_constant(&names); + + quote! { + #definition + #all_impl + } +} + +fn impl_cost() -> proc_macro2::TokenStream { + quote! { + fn cost(&self) -> Cost { + todo!() + } + } +} + +fn impl_c_jet_ptr() -> proc_macro2::TokenStream { + quote! { + fn c_jet_ptr(&self) -> &dyn Fn(&mut CFrameItem, CFrameItem, &Self::CJetEnvironment) -> bool { + todo!() + } + } +} + +fn impl_jet_decode(codes: Vec) -> proc_macro2::TokenStream { + let custom_decode_tree: proc_macro2::TokenStream = JetDecodeTree::from_branches(codes).into(); + + quote! { + /// # Safety + /// + /// Due to the lack of a `Clone` bound on `I`, the underlying implementation uses + /// `ptr::read` to create bitwise copies of the iterator. This is unsafe and may cause + /// undefined behavior if `I` contains types that manage unique resources. + /// This works correctly for common slice-based iterators like `Copied>`. + /// + /// See for details. + fn decode>(bits: &mut ::simplicity::BitIter) -> Result { + let (mut elements_iter, mut custom_iter) = + unsafe { (std::ptr::read(bits), std::ptr::read(bits)) }; + + let bits_read = bits.n_total_read(); + + let try_elements = Elements::decode(&mut elements_iter); + + if let Ok(jet) = try_elements { + for _ in 0..(elements_iter.n_total_read() - bits_read) { + bits.next(); + } + + std::mem::forget(elements_iter); + std::mem::forget(custom_iter); + + return Ok(Self::Elements(jet)); + } + + let custom_iter_ref = &mut custom_iter; + let try_custom = decode_bits!(custom_iter_ref, { + #custom_decode_tree + }); + + if try_custom.is_ok() { + for _ in 0..(custom_iter.n_total_read() - bits_read) { + bits.next(); + } + } + + std::mem::forget(elements_iter); + std::mem::forget(custom_iter); + + try_custom + } + + } +} + +fn impl_jet_encode( + variants: &[proc_macro2::Ident], + codes: &[JetBranchCode], +) -> proc_macro2::TokenStream { + let code_len = JET_ENC_BITLEN; + let codes_bits = codes.iter().map(|code| code.bits); + quote! { + fn encode(&self, w: &mut ::simplicity::BitWriter) -> std::io::Result { + if let Self::Elements(inner_jet) = self { + return inner_jet.encode(w); + } + + let (n, #code_len) = match self { + #(Self::#variants => (#codes_bits, #code_len), )* + _ => unreachable!(), + }; + + w.write_bits_be(n, len) + } + } +} + +fn build_jet_codes(variants: &[proc_macro2::Ident]) -> Vec { + variants + .iter() + .map(|ident| JetBranchCode::from_ident_fixed(ident.clone())) + .collect() +} + +fn impl_src_trg_ty( + variants: &[proc_macro2::Ident], + types: &[Vec], + mode: &str, +) -> proc_macro2::TokenStream { + let type_name_path = StaticTokenInfo::type_name_by_path(); + let source_or_target_ty = match mode { + "source" => quote::format_ident!("source_ty"), + "target" => quote::format_ident!("target_ty"), + _ => unreachable!(), + }; + quote! { + fn #source_or_target_ty(&self) -> #type_name_path { + if let Self::Elements(inner_jet) = self { + return inner_jet.#source_or_target_ty(); + } + + let name = match self { + #(Self::#variants => [#(#types,)*],)* + _ => unreachable!(), + }; + + #type_name_path(name) + } + } +} + +fn impl_cmr(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { + let cmr_by_path = StaticTokenInfo::cmr_by_path(); + let cmrs = variants + .iter() + .map(|ident| { + let ident_str = ident.to_string(); + helpers::cmr(&helpers::pascal_to_snake_case(&ident_str)) + }) + .collect::>(); + + quote! { + fn cmr(&self) -> #cmr_by_path { + if let Self::Elements(inner_jet) = self { + return inner_jet.cmr(); + } + + let bytes = match self { + #(Self::#variants => [#(#cmrs,)*],)* + _ => unreachable!(), + }; + + Cmr::from_byte_array(bytes) + } + } +} + +fn impl_c_jet_env() -> proc_macro2::TokenStream { + quote! { + fn c_jet_env(env: &Self::Environment) -> &Self::CJetEnvironment { + // For the time being, we are goint to use the initial environment for unchained jets, + // as we are going to implement them in rust. + env + } + } +} + +fn impl_associated_types() -> proc_macro2::TokenStream { + let unchained_env = StaticTokenInfo::unchained_env_by_path(); + quote! { + type Environment = #unchained_env; + type CJetEnvironment = #unchained_env; + } +} + +fn impl_all_constant(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let elements_enum_by_path = StaticTokenInfo::elements_enum_by_path(); + let custom_jets_num = variants.len(); + quote! { + impl #enum_ident { + + const ALL_JETS_NUM: usize = #elements_enum_by_path::ALL.len() + #custom_jets_num; + pub const ALL: [Self; Self::ALL_JETS_NUM] = Self::build_all_variants(); + + const fn build_all_variants() -> [Self; Self::ALL_JETS_NUM] { + + // consider moving it outside of macro + struct AllVariantsBuilder { + data: [std::mem::MaybeUninit; LEN], + len: usize, + } + + impl AllVariantsBuilder { + const fn new() -> Self { + Self { + data: [std::mem::MaybeUninit::uninit(); LEN], + len: 0, + } + } + + const fn push(&mut self, item: Enum) { + assert!(self.len < self.data.len()); + + self.data[self.len].write(item); + self.len += 1; + } + + const fn finalize(self) -> [Enum; LEN] { + assert!(self.len == LEN); + + let ptr = &self.data as *const [std::mem::MaybeUninit; LEN] as *const [Enum; LEN]; + let res = unsafe { std::ptr::read(ptr) }; + + std::mem::forget(self.data); + + res + } + } + + let mut builder = AllVariantsBuilder::new(); + + let mut i = 0; + + while i < #elements_enum_by_path::ALL.len() { + builder.push(#enum_ident::Elements(#elements_enum_by_path::ALL[i])); + i += 1; + } + + #(builder.push(#enum_ident::#variants);)* + builder.finalize() + } + } + } +} + +fn impl_enum_definition(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let elements_enum_by_path = StaticTokenInfo::elements_enum_by_path(); + quote! { + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] + pub enum #enum_ident { + Elements(#elements_enum_by_path), + #( #variants, )* + } + } +} + +fn build_target_tys(jets: &JetsInput) -> Vec> { + jets.0.iter().map(|jet| jet.target_type.value()).collect() +} + +fn build_source_tys(jets: &JetsInput) -> Vec> { + jets.0.iter().map(|jet| jet.source_type.value()).collect() +} + +fn build_custom_fields(jets: &JetsInput) -> Vec { + jets.0 + .iter() + .map(|jet| quote::format_ident!("{}", snake_to_pascal_case(&jet.name.value()))) + .collect::>() +} + +struct JetsInput(Punctuated); + +impl Parse for JetsInput { + fn parse(input: ParseStream) -> syn::Result { + Ok(JetsInput(input.parse_terminated(JetDef::parse, Token![;])?)) + } +} + +struct JetDef { + name: LitStr, + _arrow: Token![=>], + func: Ident, + _comma1: Token![,], + source_type: LitByteStr, + _comma2: Token![,], + target_type: LitByteStr, +} + +impl Parse for JetDef { + fn parse(input: ParseStream) -> syn::Result { + Ok(JetDef { + // TODO: guarantee that its non-empty and starts from letter + name: input.parse()?, + _arrow: input.parse()?, + func: input.parse()?, + _comma1: input.parse()?, + source_type: input.parse()?, + _comma2: input.parse()?, + target_type: input.parse()?, + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn test_extenstion_def_builder() { + let input = quote! { + "custom_jet1" => custom_jet1, b"h",b"h"; + "custom_jet2" => custom_jet2, b"h",b"h"; + }; + + let parsed: JetsInput = parse2(input).expect("Failed to parse JetsInput"); + let variants_names = build_custom_fields(&parsed); + let expanded = impl_enum_definition(&variants_names); + + println!("{}", expanded.to_string()); + } + + #[test] + fn test_cmr_impl() { + let input = quote! { + "custom_jet1" => custom_jet1, b"h",b"h"; + "custom_jet2" => custom_jet2, b"h",b"h"; + }; + + let parsed: JetsInput = parse2(input).expect("Failed to parse JetsInput"); + let variants_names = build_custom_fields(&parsed); + let cmrs = impl_cmr(&variants_names); + + println!("{}", cmrs.to_string()); + } + + #[test] + fn test_source_impl() { + let input = quote! { + "custom_jet1" => custom_jet1, b"h",b"h"; + "custom_jet2" => custom_jet2, b"h",b"h"; + }; + + let parsed: JetsInput = parse2(input).expect("Failed to parse JetsInput"); + let variants_names = build_custom_fields(&parsed); + let source_tys = build_source_tys(&parsed); + let tys = impl_src_trg_ty(&variants_names, &source_tys, "source"); + + println!("{}", tys.to_string()); + } + + #[test] + fn test_encode_impl() { + let input = quote! { + "custom_jet1" => custom_jet1, b"h",b"h"; + "custom_jet2" => custom_jet2, b"h",b"h"; + }; + + let parsed: JetsInput = parse2(input).expect("Failed to parse JetsInput"); + let variants_names = build_custom_fields(&parsed); + let codes = build_jet_codes(&variants_names); + let encode_impl = impl_jet_encode(&variants_names, &codes); + + println!("{}", encode_impl.to_string()); + } + + #[test] + fn test_decode_impl() { + let input = quote! { + "custom_jet1" => custom_jet1, b"h",b"h"; + "custom_jet2" => custom_jet2, b"h",b"h"; + }; + + let parsed: JetsInput = parse2(input).expect("Failed to parse JetsInput"); + let variants_names = build_custom_fields(&parsed); + let codes = build_jet_codes(&variants_names); + let decode_impl = impl_jet_decode(codes); + + println!("{}", decode_impl.to_string()); + } +} From f022aabcc60498f951eb75486489e141091d477d Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Wed, 18 Feb 2026 12:33:02 +0200 Subject: [PATCH 02/14] fix imports --- jet_plugins/src/helpers.rs | 2 +- jet_plugins/src/lib.rs | 160 ++++++++++++++++++++++++++++++------- 2 files changed, 133 insertions(+), 29 deletions(-) diff --git a/jet_plugins/src/helpers.rs b/jet_plugins/src/helpers.rs index 199fe46..9c4323d 100644 --- a/jet_plugins/src/helpers.rs +++ b/jet_plugins/src/helpers.rs @@ -1,6 +1,6 @@ use bitcoin_hashes::{Hash, HashEngine, sha256}; use quote::quote; -use std::{fs::create_dir, mem::MaybeUninit}; +use std::mem::MaybeUninit; const SIMPLICITY_TAG_PREFIX: &[u8] = b"Simplicity\x1fCommitment\x1f"; const JETIV: sha256::Midstate = sha256::Midstate([ diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index cdfe041..fe815de 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -10,15 +10,26 @@ use syn::{ mod helpers; use helpers::snake_to_pascal_case; -use crate::helpers::{ENCODE_PREFIX, JET_ENC_BITLEN, JetBranchCode, JetDecodeTree}; +use crate::helpers::{ + ENCODE_PREFIX, JET_ENC_BITLEN, JetBranchCode, JetDecodeTree, pascal_to_snake_case, +}; // TODO: get rid of hardcoded import const STRUCT_EXTENSION_NAME: &str = "JetExtension"; const ELEMENTS_ENUM_PATH: &str = "hal_simplicity::simplicity::jet::Elements"; const JET_TRAIT_PATH: &str = "hal_simplicity::simplicity::jet::Jet"; -const UNCHAINED_ENV_PATH: &str = "hal_simplicity::simplicity::environments::ElementsUnchainedEnv"; -const CMR_BY_PATH: &str = "hal_simplicity::simplicity::Cmr"; -const TYPE_NAME_BY_PATH: &str = "hal_simplicity::simplicity::jet::type_name::TypeName"; +const UNCHAINED_ENV_PATH: &str = "crate::jets::environments::ElementsUnchainedEnv"; +const BIT_WRITER_PATH: &str = "hal_simplicity::simplicity::BitWriter"; +const BIT_ITER_PATH: &str = "hal_simplicity::simplicity::BitIter"; +const CMR_PATH: &str = "hal_simplicity::simplicity::Cmr"; +const COST_PATH: &str = "hal_simplicity::simplicity::Cost"; +const INVALID_JET_ERR: &str = "hal_simplicity::simplicity::decode::Error::InvalidJet"; +const END_OF_STREAM_ERR: &str = "hal_simplicity::simplicity::decode::Error::EndOfStream"; +const DECODE_ERR_TY: &str = "hal_simplicity::simplicity::decode::Error"; + +const TYPE_NAME_PATH: &str = "hal_simplicity::simplicity::jet::type_name::TypeName"; +const CFRAME_ITEM_PATH: &str = "hal_simplicity::simplicity::ffi::CFrameItem"; +const SIMPLICITY_ERROR_TY: &str = "hal_simplicity::simplicity::Error"; struct StaticTokenInfo {} @@ -27,24 +38,59 @@ impl StaticTokenInfo { quote::format_ident!("{}", STRUCT_EXTENSION_NAME) } - fn elements_enum_by_path() -> syn::Path { + fn elements_enum_path() -> syn::Path { syn::parse_str(ELEMENTS_ENUM_PATH).expect("Failed to find elements enum by given path") } - fn jet_trait_by_path() -> syn::Path { + fn jet_trait_path() -> syn::Path { syn::parse_str(JET_TRAIT_PATH).expect("Failed to find Jet trait by given path") } - fn unchained_env_by_path() -> syn::Path { + fn unchained_env_path() -> syn::Path { syn::parse_str(UNCHAINED_ENV_PATH).expect("Failed to find UnchainedEnv by given path") } - fn cmr_by_path() -> syn::Path { - syn::parse_str(CMR_BY_PATH).expect("Failed to find Cmr by given path") + fn bit_iter_path() -> syn::Path { + syn::parse_str(BIT_ITER_PATH).expect("Failed to find BitIter by given path") + } + + fn bit_writer_path() -> syn::Path { + syn::parse_str(BIT_WRITER_PATH).expect("Failed to find BitWriter by given path") + } + + fn invalid_jet_err() -> syn::Path { + syn::parse_str(INVALID_JET_ERR) + .expect("Failed to find simplicity::decode::Error::InvalidJet by given path") + } + + fn end_of_stream_err() -> syn::Path { + syn::parse_str(END_OF_STREAM_ERR) + .expect("Failed to find simplicity::decode::Error::EndOfStream by given path") + } + + fn decode_err() -> syn::Path { + syn::parse_str(DECODE_ERR_TY) + .expect("Failed to find simplicity::decode::Error by given path") + } + + fn cmr_path() -> syn::Path { + syn::parse_str(CMR_PATH).expect("Failed to find Cmr by given path") + } + + fn cost_path() -> syn::Path { + syn::parse_str(COST_PATH).expect("Failed to find Cost by given path") + } + + fn type_name_path() -> syn::Path { + syn::parse_str(TYPE_NAME_PATH).expect("Failed to find TypeName by given path") } - fn type_name_by_path() -> syn::Path { - syn::parse_str(TYPE_NAME_BY_PATH).expect("Failed to find TypeName by given path") + fn cframe_item_path() -> syn::Path { + syn::parse_str(CFRAME_ITEM_PATH).expect("Failed to find CFrame by given path") + } + + fn simplicity_error_ty() -> syn::Path { + syn::parse_str(SIMPLICITY_ERROR_TY).expect("Failed to find simplicity::Error by given path") } } @@ -56,14 +102,61 @@ pub fn register_jets(input: proc_macro::TokenStream) -> proc_macro::TokenStream let self_impl = self_impl_full(&names); let jet_trait_impl = jet_trait_full(&names, &input); + let fmt_impl = fmt_trait_full(&names); + let from_str_impl = from_str_trait_full(&names); quote! { #self_impl #jet_trait_impl + #fmt_impl + #from_str_impl } .into() } +// TODO: settle on namings +fn from_str_trait_full(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let snake_case_names = names + .iter() + .map(|name| pascal_to_snake_case(&name.to_string())); + let simpl_err_ty = StaticTokenInfo::simplicity_error_ty(); + let elements_enum = StaticTokenInfo::elements_enum_path(); + quote! { + impl std::str::FromStr for #enum_ident { + type Err = #simpl_err_ty; + + fn from_str(s: &str) -> Result { + match s { + #(#snake_case_names => Ok(Self::#names), )* + _ => { + let inner_jet = s.parse::<#elements_enum>()?; + Ok(Self::Elements(inner_jet)) + } + } + } + } + } +} + +// TODO: settle on namings +fn fmt_trait_full(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let snake_case_names = names + .iter() + .map(|name| pascal_to_snake_case(&name.to_string())); + quote! { + impl std::fmt::Display for #enum_ident { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Elements(inner_jet) => f.write_str(&inner_jet.to_string()), + #(Self::#names => f.write_str(#snake_case_names), )* + } + } + } + } +} + fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2::TokenStream { let associated_types = impl_associated_types(); let c_jet_env = impl_c_jet_env(); @@ -80,20 +173,24 @@ fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2 let c_jet_ptr = impl_c_jet_ptr(); let cost = impl_cost(); - let jet_trait_path = StaticTokenInfo::jet_trait_by_path(); + let jet_trait_path = StaticTokenInfo::jet_trait_path(); let enum_ident = StaticTokenInfo::enum_ident(); + + let invalid_jet_err = StaticTokenInfo::invalid_jet_err(); + let end_of_stream_err = StaticTokenInfo::end_of_stream_err(); + quote! { // maybe consider moving it somewhere macro_rules! decode_bits { ($bits:ident, {}) => { - Err(::simplicity::decode::Error::InvalidJet.into()) + Err(#invalid_jet_err.into()) }; ($bits:ident, {$jet:expr}) => { Ok($jet) }; ($bits:ident, { 0 => $false_branch:tt, 1 => $true_branch:tt }) => { match $bits.next() { - None => Err(::simplicity::decode::Error::EndOfStream.into()), + None => Err(#end_of_stream_err.into()), Some(false) => decode_bits!($bits, $false_branch), Some(true) => decode_bits!($bits, $true_branch), } @@ -124,16 +221,18 @@ fn self_impl_full(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { } fn impl_cost() -> proc_macro2::TokenStream { + let cost_path = StaticTokenInfo::cost_path(); quote! { - fn cost(&self) -> Cost { + fn cost(&self) -> #cost_path { todo!() } } } fn impl_c_jet_ptr() -> proc_macro2::TokenStream { + let c_frame_path = StaticTokenInfo::cframe_item_path(); quote! { - fn c_jet_ptr(&self) -> &dyn Fn(&mut CFrameItem, CFrameItem, &Self::CJetEnvironment) -> bool { + fn c_jet_ptr(&self) -> &dyn Fn(&mut #c_frame_path, #c_frame_path, &Self::CJetEnvironment) -> bool { todo!() } } @@ -141,6 +240,9 @@ fn impl_c_jet_ptr() -> proc_macro2::TokenStream { fn impl_jet_decode(codes: Vec) -> proc_macro2::TokenStream { let custom_decode_tree: proc_macro2::TokenStream = JetDecodeTree::from_branches(codes).into(); + let bit_iter = StaticTokenInfo::bit_iter_path(); + let element_enum = StaticTokenInfo::elements_enum_path(); + let decode_err = StaticTokenInfo::decode_err(); quote! { /// # Safety @@ -151,13 +253,13 @@ fn impl_jet_decode(codes: Vec) -> proc_macro2::TokenStream { /// This works correctly for common slice-based iterators like `Copied>`. /// /// See for details. - fn decode>(bits: &mut ::simplicity::BitIter) -> Result { + fn decode>(bits: &mut #bit_iter) -> Result { let (mut elements_iter, mut custom_iter) = unsafe { (std::ptr::read(bits), std::ptr::read(bits)) }; let bits_read = bits.n_total_read(); - let try_elements = Elements::decode(&mut elements_iter); + let try_elements = #element_enum::decode(&mut elements_iter); if let Ok(jet) = try_elements { for _ in 0..(elements_iter.n_total_read() - bits_read) { @@ -196,18 +298,20 @@ fn impl_jet_encode( ) -> proc_macro2::TokenStream { let code_len = JET_ENC_BITLEN; let codes_bits = codes.iter().map(|code| code.bits); + let bit_writer = StaticTokenInfo::bit_writer_path(); + quote! { - fn encode(&self, w: &mut ::simplicity::BitWriter) -> std::io::Result { + fn encode(&self, w: &mut #bit_writer) -> std::io::Result { if let Self::Elements(inner_jet) = self { return inner_jet.encode(w); } - let (n, #code_len) = match self { + let (n, len) = match self { #(Self::#variants => (#codes_bits, #code_len), )* _ => unreachable!(), }; - w.write_bits_be(n, len) + w.write_bits_be(n as u64, len) } } } @@ -224,7 +328,7 @@ fn impl_src_trg_ty( types: &[Vec], mode: &str, ) -> proc_macro2::TokenStream { - let type_name_path = StaticTokenInfo::type_name_by_path(); + let type_name_path = StaticTokenInfo::type_name_path(); let source_or_target_ty = match mode { "source" => quote::format_ident!("source_ty"), "target" => quote::format_ident!("target_ty"), @@ -237,7 +341,7 @@ fn impl_src_trg_ty( } let name = match self { - #(Self::#variants => [#(#types,)*],)* + #(Self::#variants => &[#(#types,)*],)* _ => unreachable!(), }; @@ -247,7 +351,7 @@ fn impl_src_trg_ty( } fn impl_cmr(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { - let cmr_by_path = StaticTokenInfo::cmr_by_path(); + let cmr_by_path = StaticTokenInfo::cmr_path(); let cmrs = variants .iter() .map(|ident| { @@ -267,7 +371,7 @@ fn impl_cmr(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { _ => unreachable!(), }; - Cmr::from_byte_array(bytes) + #cmr_by_path::from_byte_array(bytes) } } } @@ -283,7 +387,7 @@ fn impl_c_jet_env() -> proc_macro2::TokenStream { } fn impl_associated_types() -> proc_macro2::TokenStream { - let unchained_env = StaticTokenInfo::unchained_env_by_path(); + let unchained_env = StaticTokenInfo::unchained_env_path(); quote! { type Environment = #unchained_env; type CJetEnvironment = #unchained_env; @@ -292,7 +396,7 @@ fn impl_associated_types() -> proc_macro2::TokenStream { fn impl_all_constant(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); - let elements_enum_by_path = StaticTokenInfo::elements_enum_by_path(); + let elements_enum_by_path = StaticTokenInfo::elements_enum_path(); let custom_jets_num = variants.len(); quote! { impl #enum_ident { @@ -353,7 +457,7 @@ fn impl_all_constant(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStrea fn impl_enum_definition(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); - let elements_enum_by_path = StaticTokenInfo::elements_enum_by_path(); + let elements_enum_by_path = StaticTokenInfo::elements_enum_path(); quote! { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub enum #enum_ident { From a39f5c8a65f35b57975b7ee53997dd95680795a6 Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Thu, 19 Feb 2026 15:39:42 +0200 Subject: [PATCH 03/14] implement `c_jet_ptr` method --- core/src/jets/custom.rs | 11 + jet_plugins/src/elements_idents.rs | 473 +++++++++++++++++++++++++++++ jet_plugins/src/lib.rs | 58 +++- 3 files changed, 537 insertions(+), 5 deletions(-) create mode 100644 jet_plugins/src/elements_idents.rs diff --git a/core/src/jets/custom.rs b/core/src/jets/custom.rs index 019e4d3..70f9bbd 100644 --- a/core/src/jets/custom.rs +++ b/core/src/jets/custom.rs @@ -1,4 +1,15 @@ +use hal_simplicity::simplicity::ffi::CFrameItem; use jet_plugins::register_jets; + +use crate::jets::environments::ElementsUnchainedEnv; + +pub fn custom_jet1(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { + false +} + +pub fn custom_jet2(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { + false +} // File for testing register_jets!( "custom_jet1" => custom_jet1, b"h", b"h"; diff --git a/jet_plugins/src/elements_idents.rs b/jet_plugins/src/elements_idents.rs new file mode 100644 index 0000000..7b2a75c --- /dev/null +++ b/jet_plugins/src/elements_idents.rs @@ -0,0 +1,473 @@ +pub(crate) const ELEMENTS_FIELDS: [&str; 471] = [ + "Add16", + "Add32", + "Add64", + "Add8", + "All16", + "All32", + "All64", + "All8", + "And1", + "And16", + "And32", + "And64", + "And8", + "AnnexHash", + "AssetAmountHash", + "Bip0340Verify", + "BuildTapbranch", + "BuildTapleafSimplicity", + "BuildTaptweak", + "CalculateAsset", + "CalculateConfidentialToken", + "CalculateExplicitToken", + "CalculateIssuanceEntropy", + "Ch1", + "Ch16", + "Ch32", + "Ch64", + "Ch8", + "CheckLockDistance", + "CheckLockDuration", + "CheckLockHeight", + "CheckLockTime", + "CheckSigVerify", + "Complement1", + "Complement16", + "Complement32", + "Complement64", + "Complement8", + "CurrentAmount", + "CurrentAnnexHash", + "CurrentAsset", + "CurrentIndex", + "CurrentIssuanceAssetAmount", + "CurrentIssuanceAssetProof", + "CurrentIssuanceTokenAmount", + "CurrentIssuanceTokenProof", + "CurrentNewIssuanceContract", + "CurrentPegin", + "CurrentPrevOutpoint", + "CurrentReissuanceBlinding", + "CurrentReissuanceEntropy", + "CurrentScriptHash", + "CurrentScriptSigHash", + "CurrentSequence", + "Decompress", + "Decrement16", + "Decrement32", + "Decrement64", + "Decrement8", + "DivMod128_64", + "DivMod16", + "DivMod32", + "DivMod64", + "DivMod8", + "Divide16", + "Divide32", + "Divide64", + "Divide8", + "Divides16", + "Divides32", + "Divides64", + "Divides8", + "Eq1", + "Eq16", + "Eq256", + "Eq32", + "Eq64", + "Eq8", + "FeAdd", + "FeInvert", + "FeIsOdd", + "FeIsZero", + "FeMultiply", + "FeMultiplyBeta", + "FeNegate", + "FeNormalize", + "FeSquare", + "FeSquareRoot", + "FullAdd16", + "FullAdd32", + "FullAdd64", + "FullAdd8", + "FullDecrement16", + "FullDecrement32", + "FullDecrement64", + "FullDecrement8", + "FullIncrement16", + "FullIncrement32", + "FullIncrement64", + "FullIncrement8", + "FullLeftShift16_1", + "FullLeftShift16_2", + "FullLeftShift16_4", + "FullLeftShift16_8", + "FullLeftShift32_1", + "FullLeftShift32_16", + "FullLeftShift32_2", + "FullLeftShift32_4", + "FullLeftShift32_8", + "FullLeftShift64_1", + "FullLeftShift64_16", + "FullLeftShift64_2", + "FullLeftShift64_32", + "FullLeftShift64_4", + "FullLeftShift64_8", + "FullLeftShift8_1", + "FullLeftShift8_2", + "FullLeftShift8_4", + "FullMultiply16", + "FullMultiply32", + "FullMultiply64", + "FullMultiply8", + "FullRightShift16_1", + "FullRightShift16_2", + "FullRightShift16_4", + "FullRightShift16_8", + "FullRightShift32_1", + "FullRightShift32_16", + "FullRightShift32_2", + "FullRightShift32_4", + "FullRightShift32_8", + "FullRightShift64_1", + "FullRightShift64_16", + "FullRightShift64_2", + "FullRightShift64_32", + "FullRightShift64_4", + "FullRightShift64_8", + "FullRightShift8_1", + "FullRightShift8_2", + "FullRightShift8_4", + "FullSubtract16", + "FullSubtract32", + "FullSubtract64", + "FullSubtract8", + "GeIsOnCurve", + "GeNegate", + "GejAdd", + "GejDouble", + "GejEquiv", + "GejGeAdd", + "GejGeAddEx", + "GejGeEquiv", + "GejInfinity", + "GejIsInfinity", + "GejIsOnCurve", + "GejNegate", + "GejNormalize", + "GejRescale", + "GejXEquiv", + "GejYIsOdd", + "Generate", + "GenesisBlockHash", + "HashToCurve", + "High1", + "High16", + "High32", + "High64", + "High8", + "Increment16", + "Increment32", + "Increment64", + "Increment8", + "InputAmount", + "InputAmountsHash", + "InputAnnexHash", + "InputAnnexesHash", + "InputAsset", + "InputHash", + "InputOutpointsHash", + "InputPegin", + "InputPrevOutpoint", + "InputScriptHash", + "InputScriptSigHash", + "InputScriptSigsHash", + "InputScriptsHash", + "InputSequence", + "InputSequencesHash", + "InputUtxoHash", + "InputUtxosHash", + "InputsHash", + "InternalKey", + "IsOne16", + "IsOne32", + "IsOne64", + "IsOne8", + "IsZero16", + "IsZero32", + "IsZero64", + "IsZero8", + "Issuance", + "IssuanceAsset", + "IssuanceAssetAmount", + "IssuanceAssetAmountsHash", + "IssuanceAssetProof", + "IssuanceBlindingEntropyHash", + "IssuanceEntropy", + "IssuanceHash", + "IssuanceRangeProofsHash", + "IssuanceToken", + "IssuanceTokenAmount", + "IssuanceTokenAmountsHash", + "IssuanceTokenProof", + "IssuancesHash", + "LbtcAsset", + "Le16", + "Le32", + "Le64", + "Le8", + "LeftExtend16_32", + "LeftExtend16_64", + "LeftExtend1_16", + "LeftExtend1_32", + "LeftExtend1_64", + "LeftExtend1_8", + "LeftExtend32_64", + "LeftExtend8_16", + "LeftExtend8_32", + "LeftExtend8_64", + "LeftPadHigh16_32", + "LeftPadHigh16_64", + "LeftPadHigh1_16", + "LeftPadHigh1_32", + "LeftPadHigh1_64", + "LeftPadHigh1_8", + "LeftPadHigh32_64", + "LeftPadHigh8_16", + "LeftPadHigh8_32", + "LeftPadHigh8_64", + "LeftPadLow16_32", + "LeftPadLow16_64", + "LeftPadLow1_16", + "LeftPadLow1_32", + "LeftPadLow1_64", + "LeftPadLow1_8", + "LeftPadLow32_64", + "LeftPadLow8_16", + "LeftPadLow8_32", + "LeftPadLow8_64", + "LeftRotate16", + "LeftRotate32", + "LeftRotate64", + "LeftRotate8", + "LeftShift16", + "LeftShift32", + "LeftShift64", + "LeftShift8", + "LeftShiftWith16", + "LeftShiftWith32", + "LeftShiftWith64", + "LeftShiftWith8", + "Leftmost16_1", + "Leftmost16_2", + "Leftmost16_4", + "Leftmost16_8", + "Leftmost32_1", + "Leftmost32_16", + "Leftmost32_2", + "Leftmost32_4", + "Leftmost32_8", + "Leftmost64_1", + "Leftmost64_16", + "Leftmost64_2", + "Leftmost64_32", + "Leftmost64_4", + "Leftmost64_8", + "Leftmost8_1", + "Leftmost8_2", + "Leftmost8_4", + "LinearCombination1", + "LinearVerify1", + "LockTime", + "Low1", + "Low16", + "Low32", + "Low64", + "Low8", + "Lt16", + "Lt32", + "Lt64", + "Lt8", + "Maj1", + "Maj16", + "Maj32", + "Maj64", + "Maj8", + "Max16", + "Max32", + "Max64", + "Max8", + "Median16", + "Median32", + "Median64", + "Median8", + "Min16", + "Min32", + "Min64", + "Min8", + "Modulo16", + "Modulo32", + "Modulo64", + "Modulo8", + "Multiply16", + "Multiply32", + "Multiply64", + "Multiply8", + "Negate16", + "Negate32", + "Negate64", + "Negate8", + "NewIssuanceContract", + "NonceHash", + "NumInputs", + "NumOutputs", + "One16", + "One32", + "One64", + "One8", + "Or1", + "Or16", + "Or32", + "Or64", + "Or8", + "OutpointHash", + "OutputAmount", + "OutputAmountsHash", + "OutputAsset", + "OutputHash", + "OutputIsFee", + "OutputNonce", + "OutputNoncesHash", + "OutputNullDatum", + "OutputRangeProof", + "OutputRangeProofsHash", + "OutputScriptHash", + "OutputScriptsHash", + "OutputSurjectionProof", + "OutputSurjectionProofsHash", + "OutputsHash", + "ParseLock", + "ParseSequence", + "PointVerify1", + "ReissuanceBlinding", + "ReissuanceEntropy", + "RightExtend16_32", + "RightExtend16_64", + "RightExtend32_64", + "RightExtend8_16", + "RightExtend8_32", + "RightExtend8_64", + "RightPadHigh16_32", + "RightPadHigh16_64", + "RightPadHigh1_16", + "RightPadHigh1_32", + "RightPadHigh1_64", + "RightPadHigh1_8", + "RightPadHigh32_64", + "RightPadHigh8_16", + "RightPadHigh8_32", + "RightPadHigh8_64", + "RightPadLow16_32", + "RightPadLow16_64", + "RightPadLow1_16", + "RightPadLow1_32", + "RightPadLow1_64", + "RightPadLow1_8", + "RightPadLow32_64", + "RightPadLow8_16", + "RightPadLow8_32", + "RightPadLow8_64", + "RightRotate16", + "RightRotate32", + "RightRotate64", + "RightRotate8", + "RightShift16", + "RightShift32", + "RightShift64", + "RightShift8", + "RightShiftWith16", + "RightShiftWith32", + "RightShiftWith64", + "RightShiftWith8", + "Rightmost16_1", + "Rightmost16_2", + "Rightmost16_4", + "Rightmost16_8", + "Rightmost32_1", + "Rightmost32_16", + "Rightmost32_2", + "Rightmost32_4", + "Rightmost32_8", + "Rightmost64_1", + "Rightmost64_16", + "Rightmost64_2", + "Rightmost64_32", + "Rightmost64_4", + "Rightmost64_8", + "Rightmost8_1", + "Rightmost8_2", + "Rightmost8_4", + "ScalarAdd", + "ScalarInvert", + "ScalarIsZero", + "ScalarMultiply", + "ScalarMultiplyLambda", + "ScalarNegate", + "ScalarNormalize", + "ScalarSquare", + "Scale", + "ScriptCMR", + "Sha256Block", + "Sha256Ctx8Add1", + "Sha256Ctx8Add128", + "Sha256Ctx8Add16", + "Sha256Ctx8Add2", + "Sha256Ctx8Add256", + "Sha256Ctx8Add32", + "Sha256Ctx8Add4", + "Sha256Ctx8Add512", + "Sha256Ctx8Add64", + "Sha256Ctx8Add8", + "Sha256Ctx8AddBuffer511", + "Sha256Ctx8Finalize", + "Sha256Ctx8Init", + "Sha256Iv", + "SigAllHash", + "Some1", + "Some16", + "Some32", + "Some64", + "Some8", + "Subtract16", + "Subtract32", + "Subtract64", + "Subtract8", + "Swu", + "TapEnvHash", + "TapdataInit", + "TapleafHash", + "TapleafVersion", + "Tappath", + "TappathHash", + "TotalFee", + "TransactionId", + "TxHash", + "TxIsFinal", + "TxLockDistance", + "TxLockDuration", + "TxLockHeight", + "TxLockTime", + "Verify", + "Version", + "Xor1", + "Xor16", + "Xor32", + "Xor64", + "Xor8", + "XorXor1", + "XorXor16", + "XorXor32", + "XorXor64", + "XorXor8", +]; diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index fe815de..2471516 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -7,6 +7,7 @@ use syn::{ punctuated::Punctuated, }; +mod elements_idents; mod helpers; use helpers::snake_to_pascal_case; @@ -163,14 +164,14 @@ fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2 let cmr = impl_cmr(names); let src_ty = impl_src_trg_ty(names, &build_source_tys(jets), "source"); - let trgt_ty = impl_src_trg_ty(names, &build_source_tys(jets), "target"); + let trgt_ty = impl_src_trg_ty(names, &&build_target_tys(jets), "target"); let jet_codes = build_jet_codes(names); let jet_encode = impl_jet_encode(names, &jet_codes); let jet_decode = impl_jet_decode(jet_codes); - let c_jet_ptr = impl_c_jet_ptr(); + let c_jet_ptr = impl_c_jet_ptr(names, jets); let cost = impl_cost(); let jet_trait_path = StaticTokenInfo::jet_trait_path(); @@ -213,10 +214,12 @@ fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2 fn self_impl_full(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { let definition = impl_enum_definition(&names); let all_impl = impl_all_constant(&names); + let c_jet_ptr_shims = c_jet_ptr_shims(); quote! { #definition #all_impl + #c_jet_ptr_shims } } @@ -229,11 +232,16 @@ fn impl_cost() -> proc_macro2::TokenStream { } } -fn impl_c_jet_ptr() -> proc_macro2::TokenStream { +fn impl_c_jet_ptr(names: &[proc_macro2::Ident], inputs: &JetsInput) -> proc_macro2::TokenStream { let c_frame_path = StaticTokenInfo::cframe_item_path(); + let funcs = inputs.0.iter().map(|jet| jet.func.clone()); + quote! { fn c_jet_ptr(&self) -> &dyn Fn(&mut #c_frame_path, #c_frame_path, &Self::CJetEnvironment) -> bool { - todo!() + match self { + Self::Elements(inner) => Self::elements_c_jet_ptr_wrapper(self), + #(Self::#names => &#funcs, )* + } } } } @@ -455,6 +463,46 @@ fn impl_all_constant(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStrea } } +// TODO: move this wrappers to some place to avoid reimplementing them for each extension + +// seems it's impossible to wrap `c_jet_ptr` via closure so we should generate a lot of +// boilerplate wrappers +fn c_jet_ptr_shims() -> proc_macro2::TokenStream { + let idents = elements_idents::ELEMENTS_FIELDS + .iter() + .map(|name| quote::format_ident!("{}", name)) + .collect::>(); + + let idents_snake = elements_idents::ELEMENTS_FIELDS + .iter() + .map(|name| quote::format_ident!("{}", pascal_to_snake_case(name))) + .collect::>(); + + let elements_enum = StaticTokenInfo::elements_enum_path(); + let c_frame_item = StaticTokenInfo::cframe_item_path(); + let unchained_env = StaticTokenInfo::unchained_env_path(); + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_trait = StaticTokenInfo::jet_trait_path(); + + quote! { + + impl #enum_ident { + #( + fn #idents_snake(frame: &mut #c_frame_item, arg: #c_frame_item, env: &#unchained_env) -> bool { + <#elements_enum as #jet_trait>::c_jet_ptr(&#elements_enum::#idents)(frame, arg, env.env.c_tx_env()) + } + )* + + fn elements_c_jet_ptr_wrapper(&self) -> &'static dyn Fn(&mut #c_frame_item, #c_frame_item, &#unchained_env) -> bool { + match self { + #( Self::Elements(#elements_enum::#idents) => &Self::#idents_snake), *, + _ => unreachable!() + } + } + } + } +} + fn impl_enum_definition(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); let elements_enum_by_path = StaticTokenInfo::elements_enum_path(); @@ -493,7 +541,7 @@ impl Parse for JetsInput { struct JetDef { name: LitStr, _arrow: Token![=>], - func: Ident, + pub func: Ident, _comma1: Token![,], source_type: LitByteStr, _comma2: Token![,], From 8d90717bad97259f082cd4c13c262d3366b991b7 Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Tue, 24 Feb 2026 17:29:58 +0200 Subject: [PATCH 04/14] wip: wrapper for trait dll Currently, `decode` for custom jets does not working. Probably bug in encoding tree pasted into `decode_bits!` --- Cargo.lock | 12 +- Cargo.toml | 2 +- core/Cargo.toml | 1 - core/src/jets/jet_dyn.rs | 132 ++++++++++++++++ core/src/jets/mod.rs | 3 +- jet_plugins/Cargo.toml | 1 + jet_plugins/src/c_wrappers.rs | 147 ++++++++++++++++++ jet_plugins/src/helpers.rs | 6 +- jet_plugins/src/lib.rs | 53 ++++++- plugin_test/Cargo.toml | 12 ++ .../jets/custom.rs => plugin_test/src/lib.rs | 3 +- plugin_test/src/main.rs | 3 + service/Cargo.toml | 1 + service/src/dll_loader.rs | 61 ++++++++ service/src/main.rs | 1 + 15 files changed, 425 insertions(+), 13 deletions(-) create mode 100644 core/src/jets/jet_dyn.rs create mode 100644 jet_plugins/src/c_wrappers.rs create mode 100644 plugin_test/Cargo.toml rename core/src/jets/custom.rs => plugin_test/src/lib.rs (84%) create mode 100644 plugin_test/src/main.rs create mode 100644 service/src/dll_loader.rs diff --git a/Cargo.lock b/Cargo.lock index d56bfb9..c869747 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,7 +418,6 @@ dependencies = [ "hal-simplicity", "hex 0.4.3", "hex-literal", - "jet_plugins", "thiserror", ] @@ -1114,6 +1113,7 @@ name = "jet_plugins" version = "0.1.0" dependencies = [ "bitcoin_hashes", + "core", "proc-macro2", "quote", "syn", @@ -1349,6 +1349,15 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plugin_test" +version = "0.1.0" +dependencies = [ + "core", + "hal-simplicity", + "jet_plugins", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1726,6 +1735,7 @@ dependencies = [ "base64 0.22.1", "clap 4.5.53", "core", + "dlopen2", "hal-simplicity", "hex 0.4.3", "log", diff --git a/Cargo.toml b/Cargo.toml index 799cfeb..841d062 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["cli", "core", "jet_plugins", "service"] +members = ["cli", "core", "jet_plugins", "plugin_test", "service"] [workspace.dependencies] hal-simplicity = { path = "../hal-unchained" } diff --git a/core/Cargo.toml b/core/Cargo.toml index 24d9f88..4651449 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,4 +14,3 @@ hal-simplicity = { workspace = true } thiserror = { workspace = true } hex = { workspace = true } dlopen2 = "0.8.2" -jet_plugins = { path = "../jet_plugins" } diff --git a/core/src/jets/jet_dyn.rs b/core/src/jets/jet_dyn.rs new file mode 100644 index 0000000..cecf8ec --- /dev/null +++ b/core/src/jets/jet_dyn.rs @@ -0,0 +1,132 @@ +use dlopen2::wrapper::{Container, WrapperApi}; +use hal_simplicity::simplicity::{ + BitIter, BitWriter, Cmr, + jet::{Jet, type_name::TypeName}, +}; +use std::{ + ffi::CString, + io::{IoSlice, Write}, + str::FromStr, +}; + +use crate::jets::environments::ElementsUnchainedEnv; + +#[repr(C)] +pub struct JetSelfHandle(()); + +#[repr(C)] +pub struct CmrHandle(()); + +#[repr(C)] +pub struct TypeNameHandle(()); + +#[repr(C)] +pub struct BitWriterHandle<'a> { + pub _writer: BitWriter<&'a mut Vec>, +} + +/// Extremely unsafe solution caused by lack of tools on BitWriter API side +/// TODO: raise issue to provide API for safe solution +pub struct BitIterMirror> { + iter: I, + cached_byte: u8, + read_bits: usize, + total_read: usize, +} + +impl Into for BitIterMirror +where + I: Iterator, +{ + fn into(self) -> BitIterHandle { + let boxed_iter: Box> = Box::new(self.iter); + + let mirror_with_boxed = BitIterMirror { + iter: boxed_iter, + cached_byte: self.cached_byte, + read_bits: self.read_bits, + total_read: self.total_read, + }; + + let _iter: BitIter>> = + unsafe { std::mem::transmute(mirror_with_boxed) }; + + BitIterHandle { _iter } + } +} + +#[repr(C)] +pub struct BitIterHandle { + pub _iter: BitIter>>, +} + +#[derive(WrapperApi)] +pub struct CustomJetApi { + _get_jet: extern "C" fn(name: *const std::ffi::c_char) -> *const JetSelfHandle, + _cmr: extern "C" fn(_self: *const JetSelfHandle) -> *const CmrHandle, + _source_ty: extern "C" fn(_self: *const JetSelfHandle) -> *const TypeNameHandle, + _target_ty: extern "C" fn(_self: *const JetSelfHandle) -> *const TypeNameHandle, + _encode: extern "C" fn(_self: *const JetSelfHandle, w: *mut BitWriterHandle) -> *const (), + _decode: extern "C" fn(w: *mut BitIterHandle) -> *const (), +} + +impl CustomJetApi { + pub fn get_jet(&self, name: &str) -> *const JetSelfHandle { + let c_str = CString::from_str(name).unwrap(); + (self._get_jet)(c_str.as_ptr()) + } + + pub fn cmr(&self, jet: *const JetSelfHandle) -> Cmr { + *unsafe { Box::from_raw((self._cmr)(jet) as *mut Cmr) } + } + + pub fn source_ty(&self, jet: *const JetSelfHandle) -> TypeName { + *unsafe { Box::from_raw((self._source_ty)(jet) as *mut TypeName) } + } + + pub fn target_ty(&self, jet: *const JetSelfHandle) -> TypeName { + *unsafe { Box::from_raw((self._target_ty)(jet) as *mut TypeName) } + } + + pub fn encode( + &self, + jet: *const JetSelfHandle, + w: &mut BitWriter, + ) -> std::io::Result { + let mut buffer = Vec::new(); + let mut _writer = BitWriter::new(&mut buffer); + + let mut handle = BitWriterHandle { _writer }; + + let res = unsafe { + *Box::from_raw((self._encode)(jet, &mut handle) as *mut std::io::Result) + }; + + handle._writer.flush_all()?; + println!("intermidiate buffer: {:?}", buffer); + + w.write_vectored(&[IoSlice::new(&buffer)])?; + + res + } + + pub fn decode>( + &self, + bits: &mut BitIter, + ) -> Result<*const JetSelfHandle, hal_simplicity::simplicity::decode::Error> { + let wrapper: &mut BitIterMirror = unsafe { std::mem::transmute(bits) }; + let mut _dyn: BitIterHandle = + unsafe { std::ptr::read(wrapper as *mut BitIterMirror) }.into(); + + let res = unsafe { + *Box::from_raw((self._decode)(&mut _dyn) + as *mut Result< + *const JetSelfHandle, + hal_simplicity::simplicity::decode::Error, + >) + }; + std::mem::forget(_dyn); + + res + } +} diff --git a/core/src/jets/mod.rs b/core/src/jets/mod.rs index da6a895..13c5a98 100644 --- a/core/src/jets/mod.rs +++ b/core/src/jets/mod.rs @@ -1,8 +1,7 @@ pub mod bitcoin; -mod custom; pub mod elements; pub mod environments; pub mod exec; - +pub mod jet_dyn; #[cfg(test)] mod tests; diff --git a/jet_plugins/Cargo.toml b/jet_plugins/Cargo.toml index 0ea27c8..154ad3b 100644 --- a/jet_plugins/Cargo.toml +++ b/jet_plugins/Cargo.toml @@ -11,3 +11,4 @@ syn = { version = "2.0", features = ["full"] } quote = "1.0" proc-macro2 = "1.0" bitcoin_hashes = "0.14.1" +core = { path = "../core" } diff --git a/jet_plugins/src/c_wrappers.rs b/jet_plugins/src/c_wrappers.rs new file mode 100644 index 0000000..342c99d --- /dev/null +++ b/jet_plugins/src/c_wrappers.rs @@ -0,0 +1,147 @@ +use std::io::Write; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{ + Ident, LitByteStr, LitStr, Path, Token, + parse::{Parse, ParseStream}, + parse_macro_input, parse2, + punctuated::Punctuated, +}; + +use crate::{StaticTokenInfo, elements_idents, pascal_to_snake_case}; + +pub(crate) fn decode_c_wrapper() -> proc_macro2::TokenStream { + let bititer_handle = StaticTokenInfo::bititer_handle(); + let jet_self = StaticTokenInfo::jet_self_handle(); + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_trait = StaticTokenInfo::jet_trait_path(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _decode(w: *mut #bititer_handle) -> *const () + { + let bit_iter = unsafe { + &mut (*w)._iter + }; + let decode_res = <#enum_ident as #jet_trait>::decode(bit_iter); + + let res = Box::new(match decode_res { + Ok(jet) => { + let boxed = Box::new(jet); + Ok(Box::into_raw(boxed) as *const #jet_self) + } + Err(err) => Err(err) + }); + + Box::into_raw(res) as *const () + + } + } +} + +pub(crate) fn encode_c_wrapper() -> proc_macro2::TokenStream { + let bitwriter_handle = StaticTokenInfo::bitwriter_handle(); + let jet_self = StaticTokenInfo::jet_self_handle(); + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_trait = StaticTokenInfo::jet_trait_path(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _encode(_self: *const #jet_self, w: *mut #bitwriter_handle) -> *const () { + let jet_ref = unsafe { + &*(_self as *const #enum_ident) + }; + let bit_writer = unsafe { + &mut (*w)._writer + }; + let boxed = Box::new(<#enum_ident as #jet_trait>::encode(jet_ref, bit_writer)); + Box::into_raw(boxed) as *const () + } + } +} + +pub(crate) fn src_trg_ty_c_wrapper(mode: &str) -> proc_macro2::TokenStream { + let src_trg_ty = quote::format_ident!("{}", { + match mode { + "source" | "target" => format!("{}_ty", mode), + _ => unreachable!(), + } + }); + + let fn_name = quote::format_ident!("{}", { + match mode { + "source" | "target" => format!("_{}_ty", mode), + _ => unreachable!(), + } + }); + + let typename_handle = StaticTokenInfo::typename_handle(); + let jet_self = StaticTokenInfo::jet_self_handle(); + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_trait = StaticTokenInfo::jet_trait_path(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn #fn_name(_self: *const #jet_self) -> *const #typename_handle { + let jet_ref = unsafe { + &*(_self as *const #enum_ident) + }; + let boxed = Box::new(<#enum_ident as #jet_trait>::#src_trg_ty(jet_ref)); + Box::into_raw(boxed) as *const #typename_handle + } + } +} + +pub(crate) fn cmr_c_wrapper() -> proc_macro2::TokenStream { + let cmr_handle = StaticTokenInfo::cmr_handle(); + let jet_self = StaticTokenInfo::jet_self_handle(); + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_trait = StaticTokenInfo::jet_trait_path(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _cmr(_self: *const #jet_self) -> *const #cmr_handle { + let jet_ref = unsafe { + &*(_self as *const #enum_ident) + }; + let boxed = Box::new(<#enum_ident as #jet_trait>::cmr(jet_ref)); + Box::into_raw(boxed) as *const #cmr_handle + } + } +} + +pub(crate) fn jet_getter_dispatch(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let elements_enum = StaticTokenInfo::elements_enum_path(); + let jet_self_handle = StaticTokenInfo::jet_self_handle(); + let elements_strs = elements_idents::ELEMENTS_FIELDS; + let elements_idents = elements_idents::ELEMENTS_FIELDS + .iter() + .map(|name| quote::format_ident!("{}", name)); + let custom_idents = names.iter().map(|name| name.to_string()); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _get_jet(name: *const std::ffi::c_char) -> *const #jet_self_handle { + if name.is_null() { + return panic!("name null ptr"); + } + + let c_str = unsafe { std::ffi::CStr::from_ptr(name) }; + + let rust_str = match c_str.to_str() { + Ok(rust_str) => rust_str, + Err(_) => panic!("invalid str"), + }; + + let boxed = Box::new(match rust_str { + #(#elements_strs => #enum_ident::Elements(#elements_enum::#elements_idents)), *, + #(#custom_idents => #enum_ident::#names), *, + _ => unreachable!("Jet does not exists") + }); + + Box::into_raw(boxed) as *const #jet_self_handle + } + } +} diff --git a/jet_plugins/src/helpers.rs b/jet_plugins/src/helpers.rs index 9c4323d..dd6ec08 100644 --- a/jet_plugins/src/helpers.rs +++ b/jet_plugins/src/helpers.rs @@ -332,6 +332,7 @@ mod test { lines.join("\n") } + #[test] fn depr_test_decode_tree() { let branches = vec![ JetBranchCode::new(0b11101, 5, quote::format_ident!("Ident1")), @@ -349,9 +350,10 @@ mod test { #[test] fn test_decode_tree() { let branches = vec![ - JetBranchCode::from_ident_fixed(quote::format_ident!("custom_jet1")), - JetBranchCode::from_ident_fixed(quote::format_ident!("custom_jet2")), + JetBranchCode::from_ident_fixed(quote::format_ident!("CustomJet1")), + JetBranchCode::from_ident_fixed(quote::format_ident!("CustomJet2")), ]; + println!("jet1 bits {:b} {}", branches[0].bits, branches[0].bits); let tree = JetDecodeTree::from_branches(branches); let tree_tokens: proc_macro2::TokenStream = tree.into(); diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index 2471516..adeed85 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -7,19 +7,25 @@ use syn::{ punctuated::Punctuated, }; +mod c_wrappers; mod elements_idents; mod helpers; use helpers::snake_to_pascal_case; -use crate::helpers::{ - ENCODE_PREFIX, JET_ENC_BITLEN, JetBranchCode, JetDecodeTree, pascal_to_snake_case, +use crate::{ + c_wrappers::{ + cmr_c_wrapper, decode_c_wrapper, encode_c_wrapper, jet_getter_dispatch, + src_trg_ty_c_wrapper, + }, + helpers::{ENCODE_PREFIX, JET_ENC_BITLEN, JetBranchCode, JetDecodeTree, pascal_to_snake_case}, }; // TODO: get rid of hardcoded import const STRUCT_EXTENSION_NAME: &str = "JetExtension"; const ELEMENTS_ENUM_PATH: &str = "hal_simplicity::simplicity::jet::Elements"; const JET_TRAIT_PATH: &str = "hal_simplicity::simplicity::jet::Jet"; -const UNCHAINED_ENV_PATH: &str = "crate::jets::environments::ElementsUnchainedEnv"; +const UNCHAINED_ENV_PATH: &str = + "simplicity_unchained_core::jets::environments::ElementsUnchainedEnv"; const BIT_WRITER_PATH: &str = "hal_simplicity::simplicity::BitWriter"; const BIT_ITER_PATH: &str = "hal_simplicity::simplicity::BitIter"; const CMR_PATH: &str = "hal_simplicity::simplicity::Cmr"; @@ -32,7 +38,13 @@ const TYPE_NAME_PATH: &str = "hal_simplicity::simplicity::jet::type_name::TypeNa const CFRAME_ITEM_PATH: &str = "hal_simplicity::simplicity::ffi::CFrameItem"; const SIMPLICITY_ERROR_TY: &str = "hal_simplicity::simplicity::Error"; -struct StaticTokenInfo {} +const JET_SELF_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::JetSelfHandle"; +const CMR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CmrHandle"; +const TYPENAME_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::TypeNameHandle"; +const BIT_WRITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitWriterHandle"; +const BIT_ITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitIterHandle"; + +pub(crate) struct StaticTokenInfo {} impl StaticTokenInfo { fn enum_ident() -> proc_macro2::Ident { @@ -93,6 +105,26 @@ impl StaticTokenInfo { fn simplicity_error_ty() -> syn::Path { syn::parse_str(SIMPLICITY_ERROR_TY).expect("Failed to find simplicity::Error by given path") } + + fn jet_self_handle() -> syn::Path { + syn::parse_str(JET_SELF_HANDLE).expect("Failed to find JetSelfHandle by given path") + } + + fn cmr_handle() -> syn::Path { + syn::parse_str(CMR_HANDLE).expect("Failed to find CmrHandle by given path") + } + + fn typename_handle() -> syn::Path { + syn::parse_str(TYPENAME_HANDLE).expect("Failed to find TypeNameHandle by given path") + } + + fn bitwriter_handle() -> syn::Path { + syn::parse_str(BIT_WRITER_HANDLE).expect("Failed to find BitWriterHandle by given path") + } + + fn bititer_handle() -> syn::Path { + syn::parse_str(BIT_ITER_HANDLE).expect("Failed to find BitIterHandle by given path") + } } #[proc_macro] @@ -105,12 +137,24 @@ pub fn register_jets(input: proc_macro::TokenStream) -> proc_macro::TokenStream let jet_trait_impl = jet_trait_full(&names, &input); let fmt_impl = fmt_trait_full(&names); let from_str_impl = from_str_trait_full(&names); + let c_jet_getters = jet_getter_dispatch(&names); + let cmr_c_wrapper = cmr_c_wrapper(); + let src_ty_c_wrapper = src_trg_ty_c_wrapper("source"); + let trg_ty_c_wrapper = src_trg_ty_c_wrapper("target"); + let encode_c_wrapper = encode_c_wrapper(); + let decode_c_wrapper = decode_c_wrapper(); quote! { #self_impl #jet_trait_impl #fmt_impl #from_str_impl + #c_jet_getters + #cmr_c_wrapper + #src_ty_c_wrapper + #trg_ty_c_wrapper + #encode_c_wrapper + #decode_c_wrapper } .into() } @@ -409,6 +453,7 @@ fn impl_all_constant(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStrea quote! { impl #enum_ident { + // TODO: reference local elements instead of dep const ALL_JETS_NUM: usize = #elements_enum_by_path::ALL.len() + #custom_jets_num; pub const ALL: [Self; Self::ALL_JETS_NUM] = Self::build_all_variants(); diff --git a/plugin_test/Cargo.toml b/plugin_test/Cargo.toml new file mode 100644 index 0000000..15dc30d --- /dev/null +++ b/plugin_test/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "plugin_test" +version = "0.1.0" +edition = "2024" + +[dependencies] +hal-simplicity = { workspace = true } +core = { path = "../core" } +jet_plugins = { path = "../jet_plugins" } + +[lib] +crate-type = ["cdylib"] diff --git a/core/src/jets/custom.rs b/plugin_test/src/lib.rs similarity index 84% rename from core/src/jets/custom.rs rename to plugin_test/src/lib.rs index 70f9bbd..4006501 100644 --- a/core/src/jets/custom.rs +++ b/plugin_test/src/lib.rs @@ -1,7 +1,7 @@ use hal_simplicity::simplicity::ffi::CFrameItem; use jet_plugins::register_jets; -use crate::jets::environments::ElementsUnchainedEnv; +use simplicity_unchained_core::jets::environments::ElementsUnchainedEnv; pub fn custom_jet1(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { false @@ -10,7 +10,6 @@ pub fn custom_jet1(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchain pub fn custom_jet2(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { false } -// File for testing register_jets!( "custom_jet1" => custom_jet1, b"h", b"h"; "custom_jet2" => custom_jet2, b"h", b"h"; diff --git a/plugin_test/src/main.rs b/plugin_test/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/plugin_test/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/service/Cargo.toml b/service/Cargo.toml index 1cc1f0f..145bbed 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -20,3 +20,4 @@ validator = { version = "0.20.0", features = ["derive"] } hal-simplicity = { workspace = true } core = { path = "../core" } +dlopen2 = "0.8.2" diff --git a/service/src/dll_loader.rs b/service/src/dll_loader.rs new file mode 100644 index 0000000..30be380 --- /dev/null +++ b/service/src/dll_loader.rs @@ -0,0 +1,61 @@ +#[cfg(test)] +mod test { + use dlopen2::wrapper::Container; + use hal_simplicity::simplicity::{BitIter, BitWriter, elements::WriteExt}; + use simplicity_unchained_core::jets::jet_dyn::CustomJetApi; + + #[test] + fn test_dll_cmr() { + let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } + .expect("Failed to load lib"); + let jet = cont.get_jet("CustomJet1"); + let cmr = cont.cmr(jet).to_byte_array(); + println!("{:?}", cmr); + } + + #[test] + fn test_dll_encode() { + let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } + .expect("Failed to load lib"); + + let custom_jet_1 = cont.get_jet("CustomJet1"); + let lock_time = cont.get_jet("LockTime"); + + let mut buffer = Vec::new(); + let mut w = BitWriter::new(&mut buffer); + + let res1 = cont.encode(custom_jet_1, &mut w); + let res2 = cont.encode(lock_time, &mut w); + + assert!(res1.is_ok_and(|x| x == 16)); + assert!(res2.is_ok_and(|x| x == 13)); + + w.flush_all(); + + println!("{:?}", buffer); + } + + #[test] + fn test_dll_decode() { + let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } + .expect("Failed to load lib"); + + let custom_jet_1 = cont.get_jet("CustomJet1"); + + let mut buffer = Vec::new(); + let mut w = BitWriter::new(&mut buffer); + + let res1 = cont.encode(custom_jet_1, &mut w); + + w.flush_all(); + println!("{:?}", buffer); + + let mut bit_iter = BitIter::from(buffer); + let decoded = cont.decode(&mut bit_iter); + + assert!(decoded.is_ok(), "{}", decoded.err().unwrap()); + + let cmr = cont.cmr(decoded.unwrap()).to_byte_array(); + println!("{:02x?}", cmr); + } +} diff --git a/service/src/main.rs b/service/src/main.rs index 06f4f80..70ffd77 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -1,5 +1,6 @@ mod cli; mod config; +mod dll_loader; mod handlers; mod validation; From 223e237ac18a415235a70d8fa6cfe657ae731264 Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Wed, 25 Feb 2026 13:15:21 +0200 Subject: [PATCH 05/14] fix decoding FFI --- core/src/jets/jet_dyn.rs | 73 ++++++++++++------------------- jet_plugins/src/c_wrappers.rs | 18 +++++--- jet_plugins/src/helpers.rs | 11 +++-- jet_plugins/src/lib.rs | 5 +++ service/src/dll_loader.rs | 82 +++++++++++++++++++++++++++++------ 5 files changed, 122 insertions(+), 67 deletions(-) diff --git a/core/src/jets/jet_dyn.rs b/core/src/jets/jet_dyn.rs index cecf8ec..cf66a3b 100644 --- a/core/src/jets/jet_dyn.rs +++ b/core/src/jets/jet_dyn.rs @@ -1,6 +1,6 @@ use dlopen2::wrapper::{Container, WrapperApi}; use hal_simplicity::simplicity::{ - BitIter, BitWriter, Cmr, + BitCollector, BitIter, BitWriter, Cmr, jet::{Jet, type_name::TypeName}, }; use std::{ @@ -9,8 +9,6 @@ use std::{ str::FromStr, }; -use crate::jets::environments::ElementsUnchainedEnv; - #[repr(C)] pub struct JetSelfHandle(()); @@ -25,39 +23,15 @@ pub struct BitWriterHandle<'a> { pub _writer: BitWriter<&'a mut Vec>, } -/// Extremely unsafe solution caused by lack of tools on BitWriter API side -/// TODO: raise issue to provide API for safe solution -pub struct BitIterMirror> { - iter: I, - cached_byte: u8, - read_bits: usize, - total_read: usize, -} - -impl Into for BitIterMirror -where - I: Iterator, -{ - fn into(self) -> BitIterHandle { - let boxed_iter: Box> = Box::new(self.iter); - - let mirror_with_boxed = BitIterMirror { - iter: boxed_iter, - cached_byte: self.cached_byte, - read_bits: self.read_bits, - total_read: self.total_read, - }; - - let _iter: BitIter>> = - unsafe { std::mem::transmute(mirror_with_boxed) }; - - BitIterHandle { _iter } - } +#[repr(C)] +pub struct BitIterHandle { + pub data: Vec, } #[repr(C)] -pub struct BitIterHandle { - pub _iter: BitIter>>, +pub struct DecodeFFIRes { + pub decoded: *const (), + pub n_total_read: usize, } #[derive(WrapperApi)] @@ -67,7 +41,7 @@ pub struct CustomJetApi { _source_ty: extern "C" fn(_self: *const JetSelfHandle) -> *const TypeNameHandle, _target_ty: extern "C" fn(_self: *const JetSelfHandle) -> *const TypeNameHandle, _encode: extern "C" fn(_self: *const JetSelfHandle, w: *mut BitWriterHandle) -> *const (), - _decode: extern "C" fn(w: *mut BitIterHandle) -> *const (), + _decode: extern "C" fn(w: *const BitIterHandle) -> DecodeFFIRes, } impl CustomJetApi { @@ -103,29 +77,38 @@ impl CustomJetApi { }; handle._writer.flush_all()?; - println!("intermidiate buffer: {:?}", buffer); w.write_vectored(&[IoSlice::new(&buffer)])?; res } - pub fn decode>( + /// Underlying code inside `Jet` trait implementor macro is unsafe and relies on + /// `I` of `BitIter` being clonable via `std::ptr::read`. + /// Need to ask for normal `Clone` + /// + pub fn decode + Clone>( &self, bits: &mut BitIter, ) -> Result<*const JetSelfHandle, hal_simplicity::simplicity::decode::Error> { - let wrapper: &mut BitIterMirror = unsafe { std::mem::transmute(bits) }; - let mut _dyn: BitIterHandle = - unsafe { std::ptr::read(wrapper as *mut BitIterMirror) }.into(); + let (data, _) = bits.clone().collect_bits(); + let bit_iter_handle = BitIterHandle { data }; + + let DecodeFFIRes { + decoded, + n_total_read, + } = (self._decode)(&bit_iter_handle); + + for _ in 0..n_total_read { + bits.next(); + } let res = unsafe { - *Box::from_raw((self._decode)(&mut _dyn) - as *mut Result< - *const JetSelfHandle, - hal_simplicity::simplicity::decode::Error, - >) + *Box::from_raw( + decoded + as *mut Result<*const JetSelfHandle, hal_simplicity::simplicity::decode::Error>, + ) }; - std::mem::forget(_dyn); res } diff --git a/jet_plugins/src/c_wrappers.rs b/jet_plugins/src/c_wrappers.rs index 342c99d..8601675 100644 --- a/jet_plugins/src/c_wrappers.rs +++ b/jet_plugins/src/c_wrappers.rs @@ -16,15 +16,17 @@ pub(crate) fn decode_c_wrapper() -> proc_macro2::TokenStream { let jet_self = StaticTokenInfo::jet_self_handle(); let enum_ident = StaticTokenInfo::enum_ident(); let jet_trait = StaticTokenInfo::jet_trait_path(); + let bit_iter = StaticTokenInfo::bit_iter_path(); + let decode_ffi_res = StaticTokenInfo::decode_ffi_res(); quote! { #[unsafe(no_mangle)] - pub extern "C" fn _decode(w: *mut #bititer_handle) -> *const () + pub extern "C" fn _decode(w: *const #bititer_handle) -> #decode_ffi_res { - let bit_iter = unsafe { - &mut (*w)._iter + let mut bit_iter = unsafe { + #bit_iter::from((*w).data.iter().copied()) }; - let decode_res = <#enum_ident as #jet_trait>::decode(bit_iter); + let decode_res = <#enum_ident as #jet_trait>::decode(&mut bit_iter); let res = Box::new(match decode_res { Ok(jet) => { @@ -34,7 +36,13 @@ pub(crate) fn decode_c_wrapper() -> proc_macro2::TokenStream { Err(err) => Err(err) }); - Box::into_raw(res) as *const () + let n_total_read = bit_iter.n_total_read(); + + // (*Result<*const JetSelfHandle, simplicity::decode::Error>, usize) + #decode_ffi_res { + decoded: Box::into_raw(res) as *const (), + n_total_read + } } } diff --git a/jet_plugins/src/helpers.rs b/jet_plugins/src/helpers.rs index dd6ec08..6e3e330 100644 --- a/jet_plugins/src/helpers.rs +++ b/jet_plugins/src/helpers.rs @@ -61,6 +61,7 @@ pub struct JetDecodeTree { #[derive(Clone)] pub struct JetBranchCode { pub bits: JetCodeBits, + pub len: usize, pub token: proc_macro2::Ident, } @@ -69,6 +70,7 @@ impl JetBranchCode { pub fn new(bits: JetCodeBits, len: usize, token: proc_macro2::Ident) -> Self { Self { bits: bits << (JET_ENC_BITLEN - len), + len, token, } } @@ -103,6 +105,7 @@ impl JetBranchCode { Self { bits: bits.reverse_bits(), + len: JET_ENC_BITLEN, token, } } @@ -134,14 +137,14 @@ impl JetDecodeTree { let mut res = Self::new(); - for JetBranchCode { bits, token } in branches { + for JetBranchCode { bits, len, token } in branches { let mut curr = &mut res; let mut cursor = bits.reverse_bits(); - while cursor != 0 { + for _ in 0..len { if curr.token.is_some() { panic!( - "Existing branch is a prefix of the new branch {} being added", + "Existing branch is a prefix of the new branch {:b} being added", bits ); } @@ -165,7 +168,7 @@ impl JetDecodeTree { } if curr.left.is_some() || curr.right.is_some() { - panic!("{} branch is prefix of some other branch", bits); + panic!("{:b} branch is prefix of some other branch", bits); } curr.token = Some(token) } diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index adeed85..81e21f7 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -43,6 +43,7 @@ const CMR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CmrHandle"; const TYPENAME_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::TypeNameHandle"; const BIT_WRITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitWriterHandle"; const BIT_ITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitIterHandle"; +const DECODE_FFI_RES: &str = "simplicity_unchained_core::jets::jet_dyn::DecodeFFIRes"; pub(crate) struct StaticTokenInfo {} @@ -125,6 +126,10 @@ impl StaticTokenInfo { fn bititer_handle() -> syn::Path { syn::parse_str(BIT_ITER_HANDLE).expect("Failed to find BitIterHandle by given path") } + + fn decode_ffi_res() -> syn::Path { + syn::parse_str(DECODE_FFI_RES).expect("Failed to find DecodeFFIRes by given path") + } } #[proc_macro] diff --git a/service/src/dll_loader.rs b/service/src/dll_loader.rs index 30be380..8411f20 100644 --- a/service/src/dll_loader.rs +++ b/service/src/dll_loader.rs @@ -1,16 +1,33 @@ #[cfg(test)] mod test { use dlopen2::wrapper::Container; - use hal_simplicity::simplicity::{BitIter, BitWriter, elements::WriteExt}; + use hal_simplicity::simplicity::{ + BitIter, BitWriter, + jet::{Elements, Jet}, + }; use simplicity_unchained_core::jets::jet_dyn::CustomJetApi; #[test] fn test_dll_cmr() { let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } .expect("Failed to load lib"); - let jet = cont.get_jet("CustomJet1"); - let cmr = cont.cmr(jet).to_byte_array(); - println!("{:?}", cmr); + + let custom_jet = cont.get_jet("CustomJet1"); + let custom_cmr = cont.cmr(custom_jet).to_byte_array(); + + // Cmr is hardcoded by macro. This val taken from result of `cargo expand` + assert_eq!( + custom_cmr, + [ + 19u8, 49u8, 128u8, 215u8, 32u8, 93u8, 0u8, 222u8, 164u8, 160u8, 157u8, 60u8, 148u8, + 109u8, 185u8, 179u8, 223u8, 110u8, 137u8, 180u8, 168u8, 155u8, 16u8, 44u8, 115u8, + 17u8, 194u8, 244u8, 4u8, 102u8, 49u8, 128u8, + ] + ); + + let elements_jet = cont.get_jet("Add16"); + let elements_cmr = cont.cmr(elements_jet); + assert_eq!(elements_cmr, Elements::Add16.cmr()); } #[test] @@ -30,9 +47,30 @@ mod test { assert!(res1.is_ok_and(|x| x == 16)); assert!(res2.is_ok_and(|x| x == 13)); - w.flush_all(); + w.flush_all().unwrap(); + + let mut bit_iter = BitIter::from(buffer); - println!("{:?}", buffer); + let mut read_code = |read_bits: u8| -> Option { + let mut res = 0; + let mut cursor = 1 << (read_bits - 1); + + for _ in 0..read_bits { + match bit_iter.next() { + None => return None, + Some(true) => res |= cursor, + Some(false) => {} + } + cursor >>= 1; + } + Some(res) + }; + + let custom_jet_code = read_code(16); + let elements_jet_code = read_code(13); + + assert!(custom_jet_code.is_some_and(|code| code == 65465)); + assert!(elements_jet_code.is_some_and(|code| code == 7218)); } #[test] @@ -41,21 +79,39 @@ mod test { .expect("Failed to load lib"); let custom_jet_1 = cont.get_jet("CustomJet1"); + let elements_jet = cont.get_jet("Add16"); let mut buffer = Vec::new(); let mut w = BitWriter::new(&mut buffer); - let res1 = cont.encode(custom_jet_1, &mut w); + let custom_encoding = cont.encode(custom_jet_1, &mut w); + assert!(custom_encoding.is_ok()); + let elements_encoding = cont.encode(elements_jet, &mut w); + assert!(elements_encoding.is_ok()); - w.flush_all(); - println!("{:?}", buffer); + w.flush_all().unwrap(); let mut bit_iter = BitIter::from(buffer); - let decoded = cont.decode(&mut bit_iter); - assert!(decoded.is_ok(), "{}", decoded.err().unwrap()); + let custom_decoded = cont.decode(&mut bit_iter); + assert!(custom_decoded.is_ok()); + + let custom_cmr = cont.cmr(custom_decoded.unwrap()).to_byte_array(); + + // Cmr is hardcoded by macro. This val taken from result of `cargo expand` + assert_eq!( + custom_cmr, + [ + 19u8, 49u8, 128u8, 215u8, 32u8, 93u8, 0u8, 222u8, 164u8, 160u8, 157u8, 60u8, 148u8, + 109u8, 185u8, 179u8, 223u8, 110u8, 137u8, 180u8, 168u8, 155u8, 16u8, 44u8, 115u8, + 17u8, 194u8, 244u8, 4u8, 102u8, 49u8, 128u8, + ] + ); + + let elements_decoded = cont.decode(&mut bit_iter); + assert!(elements_decoded.is_ok()); - let cmr = cont.cmr(decoded.unwrap()).to_byte_array(); - println!("{:02x?}", cmr); + let elements_cmr = cont.cmr(elements_jet); + assert_eq!(elements_cmr, Elements::Add16.cmr()); } } From d6613bc72d7c44d79c0430cedc59a544826fea7f Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Wed, 25 Feb 2026 14:26:09 +0200 Subject: [PATCH 06/14] add free for `JetSelfHandle` and dll `c_jet_ptr --- core/src/jets/jet_dyn.rs | 24 ++++++++++++++++++- jet_plugins/src/c_wrappers.rs | 32 +++++++++++++++++++++++++ jet_plugins/src/lib.rs | 39 +++++++++++++++++++++++++++++-- service/src/dll_loader.rs | 44 ++++++++++++++++++++++++++++++----- 4 files changed, 130 insertions(+), 9 deletions(-) diff --git a/core/src/jets/jet_dyn.rs b/core/src/jets/jet_dyn.rs index cf66a3b..23cf495 100644 --- a/core/src/jets/jet_dyn.rs +++ b/core/src/jets/jet_dyn.rs @@ -1,6 +1,7 @@ use dlopen2::wrapper::{Container, WrapperApi}; use hal_simplicity::simplicity::{ BitCollector, BitIter, BitWriter, Cmr, + ffi::CFrameItem, jet::{Jet, type_name::TypeName}, }; use std::{ @@ -9,6 +10,8 @@ use std::{ str::FromStr, }; +use crate::jets::environments::ElementsUnchainedEnv; + #[repr(C)] pub struct JetSelfHandle(()); @@ -37,11 +40,14 @@ pub struct DecodeFFIRes { #[derive(WrapperApi)] pub struct CustomJetApi { _get_jet: extern "C" fn(name: *const std::ffi::c_char) -> *const JetSelfHandle, + _free_jet: extern "C" fn(_self: *const JetSelfHandle), _cmr: extern "C" fn(_self: *const JetSelfHandle) -> *const CmrHandle, _source_ty: extern "C" fn(_self: *const JetSelfHandle) -> *const TypeNameHandle, _target_ty: extern "C" fn(_self: *const JetSelfHandle) -> *const TypeNameHandle, - _encode: extern "C" fn(_self: *const JetSelfHandle, w: *mut BitWriterHandle) -> *const (), + _encode: extern "C" fn(_self: *const JetSelfHandle, w: *mut BitWriterHandle) -> *const (), // *std::io::Result _decode: extern "C" fn(w: *const BitIterHandle) -> DecodeFFIRes, + _c_jet_ptr: extern "C" fn(_self: *const JetSelfHandle) -> *const (), + // (*Result<*const JetSelfHandle, simplicity::decode::Error>, usize) } impl CustomJetApi { @@ -50,6 +56,10 @@ impl CustomJetApi { (self._get_jet)(c_str.as_ptr()) } + pub fn free_jet(&self, jet: *const JetSelfHandle) { + (self._free_jet)(jet) + } + pub fn cmr(&self, jet: *const JetSelfHandle) -> Cmr { *unsafe { Box::from_raw((self._cmr)(jet) as *mut Cmr) } } @@ -112,4 +122,16 @@ impl CustomJetApi { res } + + pub fn c_jet_ptr( + &self, + jet: *const JetSelfHandle, + ) -> Box bool> { + unsafe { + *Box::from_raw((self._c_jet_ptr)(jet) + as *mut Box< + dyn Fn(&mut CFrameItem, CFrameItem, &ElementsUnchainedEnv) -> bool, + >) + } + } } diff --git a/jet_plugins/src/c_wrappers.rs b/jet_plugins/src/c_wrappers.rs index 8601675..81473ad 100644 --- a/jet_plugins/src/c_wrappers.rs +++ b/jet_plugins/src/c_wrappers.rs @@ -11,6 +11,23 @@ use syn::{ use crate::{StaticTokenInfo, elements_idents, pascal_to_snake_case}; +pub(crate) fn c_jet_ptr_wrapper() -> proc_macro2::TokenStream { + let jet_self = StaticTokenInfo::jet_self_handle(); + let enum_ident = StaticTokenInfo::enum_ident(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _c_jet_ptr(_self: *const #jet_self) -> *const () { + let jet_ref = unsafe { + &*(_self as *const #enum_ident) + }; + let boxed = Box::new(<#enum_ident>::c_jet_ptr_boxed(jet_ref)); + // * Box bool> + Box::into_raw(boxed) as *const () + } + } +} + pub(crate) fn decode_c_wrapper() -> proc_macro2::TokenStream { let bititer_handle = StaticTokenInfo::bititer_handle(); let jet_self = StaticTokenInfo::jet_self_handle(); @@ -64,6 +81,7 @@ pub(crate) fn encode_c_wrapper() -> proc_macro2::TokenStream { &mut (*w)._writer }; let boxed = Box::new(<#enum_ident as #jet_trait>::encode(jet_ref, bit_writer)); + // *std::io::Result Box::into_raw(boxed) as *const () } } @@ -119,6 +137,20 @@ pub(crate) fn cmr_c_wrapper() -> proc_macro2::TokenStream { } } +pub(crate) fn free_jet_handler() -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_self_handle = StaticTokenInfo::jet_self_handle(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _free_jet(_self: *const #jet_self_handle) { + if !_self.is_null() { + let _ = unsafe { Box::from_raw(_self as *mut #enum_ident) }; + } + } + } +} + pub(crate) fn jet_getter_dispatch(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); let elements_enum = StaticTokenInfo::elements_enum_path(); diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index 81e21f7..91fe44e 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -14,8 +14,8 @@ use helpers::snake_to_pascal_case; use crate::{ c_wrappers::{ - cmr_c_wrapper, decode_c_wrapper, encode_c_wrapper, jet_getter_dispatch, - src_trg_ty_c_wrapper, + c_jet_ptr_wrapper, cmr_c_wrapper, decode_c_wrapper, encode_c_wrapper, free_jet_handler, + jet_getter_dispatch, src_trg_ty_c_wrapper, }, helpers::{ENCODE_PREFIX, JET_ENC_BITLEN, JetBranchCode, JetDecodeTree, pascal_to_snake_case}, }; @@ -142,12 +142,15 @@ pub fn register_jets(input: proc_macro::TokenStream) -> proc_macro::TokenStream let jet_trait_impl = jet_trait_full(&names, &input); let fmt_impl = fmt_trait_full(&names); let from_str_impl = from_str_trait_full(&names); + let c_jet_getters = jet_getter_dispatch(&names); + let jet_handle_free = free_jet_handler(); let cmr_c_wrapper = cmr_c_wrapper(); let src_ty_c_wrapper = src_trg_ty_c_wrapper("source"); let trg_ty_c_wrapper = src_trg_ty_c_wrapper("target"); let encode_c_wrapper = encode_c_wrapper(); let decode_c_wrapper = decode_c_wrapper(); + let c_jet_ptr_wrapper = c_jet_ptr_wrapper(); quote! { #self_impl @@ -155,11 +158,13 @@ pub fn register_jets(input: proc_macro::TokenStream) -> proc_macro::TokenStream #fmt_impl #from_str_impl #c_jet_getters + #jet_handle_free #cmr_c_wrapper #src_ty_c_wrapper #trg_ty_c_wrapper #encode_c_wrapper #decode_c_wrapper + #c_jet_ptr_wrapper } .into() } @@ -221,6 +226,7 @@ fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2 let jet_decode = impl_jet_decode(jet_codes); let c_jet_ptr = impl_c_jet_ptr(names, jets); + let c_jet_ptr_boxed = impl_c_jet_ptr_boxed(names, jets); let cost = impl_cost(); let jet_trait_path = StaticTokenInfo::jet_trait_path(); @@ -257,6 +263,10 @@ fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2 #c_jet_ptr #cost } + impl #enum_ident { + #c_jet_ptr_boxed + } + } } @@ -295,6 +305,24 @@ fn impl_c_jet_ptr(names: &[proc_macro2::Ident], inputs: &JetsInput) -> proc_macr } } +fn impl_c_jet_ptr_boxed( + names: &[proc_macro2::Ident], + inputs: &JetsInput, +) -> proc_macro2::TokenStream { + let c_frame_path = StaticTokenInfo::cframe_item_path(); + let funcs = inputs.0.iter().map(|jet| jet.func.clone()); + let unchained_env = StaticTokenInfo::unchained_env_path(); + + quote! { + pub fn c_jet_ptr_boxed(&self) -> Box bool> { + match self { + Self::Elements(inner) => Self::elements_c_jet_ptr_wrapper_boxed(self), + #(Self::#names => Box::new(#funcs), )* + } + } + } +} + fn impl_jet_decode(codes: Vec) -> proc_macro2::TokenStream { let custom_decode_tree: proc_macro2::TokenStream = JetDecodeTree::from_branches(codes).into(); let bit_iter = StaticTokenInfo::bit_iter_path(); @@ -549,6 +577,13 @@ fn c_jet_ptr_shims() -> proc_macro2::TokenStream { _ => unreachable!() } } + + fn elements_c_jet_ptr_wrapper_boxed(&self) -> Box bool> { + match self { + #( Self::Elements(#elements_enum::#idents) => Box::new(Self::#idents_snake)), *, + _ => unreachable!() + } + } } } } diff --git a/service/src/dll_loader.rs b/service/src/dll_loader.rs index 8411f20..0989953 100644 --- a/service/src/dll_loader.rs +++ b/service/src/dll_loader.rs @@ -7,6 +7,18 @@ mod test { }; use simplicity_unchained_core::jets::jet_dyn::CustomJetApi; + #[test] + fn test_dll_jet_free() { + let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } + .expect("Failed to load lib"); + + let custom_jet = cont.get_jet("CustomJet1"); + let elements_jet = cont.get_jet("Add16"); + + cont.free_jet(custom_jet); + cont.free_jet(elements_jet); + } + #[test] fn test_dll_cmr() { let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } @@ -28,6 +40,9 @@ mod test { let elements_jet = cont.get_jet("Add16"); let elements_cmr = cont.cmr(elements_jet); assert_eq!(elements_cmr, Elements::Add16.cmr()); + + cont.free_jet(custom_jet); + cont.free_jet(elements_jet); } #[test] @@ -35,14 +50,14 @@ mod test { let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } .expect("Failed to load lib"); - let custom_jet_1 = cont.get_jet("CustomJet1"); - let lock_time = cont.get_jet("LockTime"); + let custom_jet = cont.get_jet("CustomJet1"); + let elements_jet = cont.get_jet("LockTime"); let mut buffer = Vec::new(); let mut w = BitWriter::new(&mut buffer); - let res1 = cont.encode(custom_jet_1, &mut w); - let res2 = cont.encode(lock_time, &mut w); + let res1 = cont.encode(custom_jet, &mut w); + let res2 = cont.encode(elements_jet, &mut w); assert!(res1.is_ok_and(|x| x == 16)); assert!(res2.is_ok_and(|x| x == 13)); @@ -71,6 +86,9 @@ mod test { assert!(custom_jet_code.is_some_and(|code| code == 65465)); assert!(elements_jet_code.is_some_and(|code| code == 7218)); + + cont.free_jet(custom_jet); + cont.free_jet(elements_jet); } #[test] @@ -78,13 +96,13 @@ mod test { let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } .expect("Failed to load lib"); - let custom_jet_1 = cont.get_jet("CustomJet1"); + let custom_jet = cont.get_jet("CustomJet1"); let elements_jet = cont.get_jet("Add16"); let mut buffer = Vec::new(); let mut w = BitWriter::new(&mut buffer); - let custom_encoding = cont.encode(custom_jet_1, &mut w); + let custom_encoding = cont.encode(custom_jet, &mut w); assert!(custom_encoding.is_ok()); let elements_encoding = cont.encode(elements_jet, &mut w); assert!(elements_encoding.is_ok()); @@ -113,5 +131,19 @@ mod test { let elements_cmr = cont.cmr(elements_jet); assert_eq!(elements_cmr, Elements::Add16.cmr()); + + cont.free_jet(custom_jet); + cont.free_jet(elements_jet); + } + #[test] + fn test_dll_c_jet_ptr() { + let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } + .expect("Failed to load lib"); + + let custom_jet = cont.get_jet("CustomJet1"); + + let _ = cont.c_jet_ptr(custom_jet); + + cont.free_jet(custom_jet); } } From e75d41f7e74c2431e11f1bad89f885693224b611 Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Thu, 26 Feb 2026 17:58:53 +0200 Subject: [PATCH 07/14] partially implement `Jet` for FFI wrapper and misc Changed handle for FFI jet value since `Jet` trait requires it to be `Copy`. It involves storing index of jet's val inside `ALL` array. `decode` func is mocked for now since I think it worth just paste original Elements decode tree inside and then implement it via fallback if `Clone` will become available. `c_jet_ptr` is also mocked because Box can't be used and I have not figured out how to mitigate this issue. --- core/src/jets/jet_dyn.rs | 188 +++++++++++++++++++++++++--------- core/src/lib.rs | 5 + jet_plugins/src/c_wrappers.rs | 180 +++++++++++++++++++++----------- jet_plugins/src/helpers.rs | 36 +------ jet_plugins/src/lib.rs | 160 +++++++++++++++++++++++------ service/src/custom_jet.rs | 135 ++++++++++++++++++++++++ service/src/dll_loader.rs | 149 --------------------------- service/src/main.rs | 2 +- 8 files changed, 531 insertions(+), 324 deletions(-) create mode 100644 service/src/custom_jet.rs delete mode 100644 service/src/dll_loader.rs diff --git a/core/src/jets/jet_dyn.rs b/core/src/jets/jet_dyn.rs index 23cf495..b17b559 100644 --- a/core/src/jets/jet_dyn.rs +++ b/core/src/jets/jet_dyn.rs @@ -1,17 +1,21 @@ -use dlopen2::wrapper::{Container, WrapperApi}; +use dlopen2::wrapper::WrapperApi; use hal_simplicity::simplicity::{ - BitCollector, BitIter, BitWriter, Cmr, - ffi::CFrameItem, - jet::{Jet, type_name::TypeName}, + BitCollector, BitIter, BitWriter, Cmr, Cost, ffi::CFrameItem, jet::type_name::TypeName, }; use std::{ - ffi::CString, + fmt::Formatter, + hash::Hasher, io::{IoSlice, Write}, - str::FromStr, }; use crate::jets::environments::ElementsUnchainedEnv; +#[repr(C)] +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct CCustomJet { + pub index: usize, +} + #[repr(C)] pub struct JetSelfHandle(()); @@ -21,6 +25,81 @@ pub struct CmrHandle(()); #[repr(C)] pub struct TypeNameHandle(()); +#[repr(C)] +pub struct CostHandle(()); + +#[repr(C)] +pub struct HasherHandle<'a> { + pub _hasher: &'a mut dyn Hasher, +} + +#[repr(C)] +pub struct FmtHandle<'a, 'b> { + pub _formatter: &'a mut core::fmt::Formatter<'b>, +} + +#[repr(C)] +pub struct StrHandle<'a> { + pub _str: &'a dyn AsRef, +} + +#[repr(i8)] +pub enum COrdering { + Less = -1, + Equal = 0, + Greater = 1, +} + +impl From for COrdering { + fn from(o: std::cmp::Ordering) -> Self { + match o { + std::cmp::Ordering::Less => COrdering::Less, + std::cmp::Ordering::Equal => COrdering::Equal, + std::cmp::Ordering::Greater => COrdering::Greater, + } + } +} + +impl From for std::cmp::Ordering { + fn from(o: COrdering) -> Self { + match o { + COrdering::Less => std::cmp::Ordering::Less, + COrdering::Equal => std::cmp::Ordering::Equal, + COrdering::Greater => std::cmp::Ordering::Greater, + } + } +} + +#[repr(i8)] +pub enum COptionOrdering { + None = i8::MIN, + Less = -1, + Equal = 0, + Greater = 1, +} + +impl From> for COptionOrdering { + fn from(o: Option) -> Self { + match o { + None => COptionOrdering::None, + Some(std::cmp::Ordering::Less) => COptionOrdering::Less, + Some(std::cmp::Ordering::Equal) => COptionOrdering::Equal, + Some(std::cmp::Ordering::Greater) => COptionOrdering::Greater, + } + } +} + +impl From for Option { + fn from(o: COptionOrdering) -> Self { + match o { + COptionOrdering::None => None, + COptionOrdering::Less => Some(std::cmp::Ordering::Less), + COptionOrdering::Equal => Some(std::cmp::Ordering::Equal), + COptionOrdering::Greater => Some(std::cmp::Ordering::Greater), + } + } +} + #[repr(C)] pub struct BitWriterHandle<'a> { pub _writer: BitWriter<&'a mut Vec>, @@ -39,42 +118,74 @@ pub struct DecodeFFIRes { #[derive(WrapperApi)] pub struct CustomJetApi { - _get_jet: extern "C" fn(name: *const std::ffi::c_char) -> *const JetSelfHandle, - _free_jet: extern "C" fn(_self: *const JetSelfHandle), - _cmr: extern "C" fn(_self: *const JetSelfHandle) -> *const CmrHandle, - _source_ty: extern "C" fn(_self: *const JetSelfHandle) -> *const TypeNameHandle, - _target_ty: extern "C" fn(_self: *const JetSelfHandle) -> *const TypeNameHandle, - _encode: extern "C" fn(_self: *const JetSelfHandle, w: *mut BitWriterHandle) -> *const (), // *std::io::Result + _cmp: extern "C" fn(_self: CCustomJet, other: CCustomJet) -> COrdering, + _partial_cmp: extern "C" fn(_self: CCustomJet, other: CCustomJet) -> COptionOrdering, + _hash: extern "C" fn(_self: CCustomJet, hasher_handle: *mut HasherHandle), + _debug_fmt: extern "C" fn(_self: CCustomJet, fmt_handle: *mut FmtHandle) -> *const (), // *const std::fmt::Result + _display_fmt: extern "C" fn(_self: CCustomJet, fmt_handle: *mut FmtHandle) -> *const (), // *const std::fmt::Result + _from_str: extern "C" fn(name: *const StrHandle) -> *const (), // *const Result + _cmr: extern "C" fn(_self: CCustomJet) -> *const CmrHandle, + _source_ty: extern "C" fn(_self: CCustomJet) -> *const TypeNameHandle, + _target_ty: extern "C" fn(_self: CCustomJet) -> *const TypeNameHandle, + _encode: extern "C" fn(_self: CCustomJet, w: *mut BitWriterHandle) -> *const (), // *std::io::Result _decode: extern "C" fn(w: *const BitIterHandle) -> DecodeFFIRes, - _c_jet_ptr: extern "C" fn(_self: *const JetSelfHandle) -> *const (), - // (*Result<*const JetSelfHandle, simplicity::decode::Error>, usize) + _c_jet_ptr: extern "C" fn(_self: CCustomJet) -> *const (), // (*const Result, usize) + _cost: extern "C" fn(_self: CCustomJet) -> *const CostHandle, } impl CustomJetApi { - pub fn get_jet(&self, name: &str) -> *const JetSelfHandle { - let c_str = CString::from_str(name).unwrap(); - (self._get_jet)(c_str.as_ptr()) + pub fn from_str(&self, name: &str) -> Result { + let str_handle = StrHandle { _str: &name }; + unsafe { + *Box::from_raw((self._from_str)(&str_handle) + as *mut Result) + } } - pub fn free_jet(&self, jet: *const JetSelfHandle) { - (self._free_jet)(jet) + pub fn cmp(&self, lhs: CCustomJet, rhs: CCustomJet) -> std::cmp::Ordering { + (self._cmp)(lhs, rhs).into() } - pub fn cmr(&self, jet: *const JetSelfHandle) -> Cmr { + pub fn partial_cmp(&self, lhs: CCustomJet, rhs: CCustomJet) -> Option { + (self._partial_cmp)(lhs, rhs).into() + } + + pub fn hash(&self, jet: CCustomJet, hasher: &mut H) { + let mut hasher_handle = HasherHandle { _hasher: hasher }; + (self._hash)(jet, &mut hasher_handle) + } + + pub fn debug_fmt(&self, jet: CCustomJet, formatter: &mut Formatter) -> std::fmt::Result { + let mut fmt_handle = FmtHandle { + _formatter: formatter, + }; + unsafe { *Box::from_raw((self._debug_fmt(jet, &mut fmt_handle)) as *mut std::fmt::Result) } + } + + pub fn display_fmt(&self, jet: CCustomJet, formatter: &mut Formatter) -> std::fmt::Result { + let mut fmt_handle = FmtHandle { + _formatter: formatter, + }; + unsafe { + *Box::from_raw((self._display_fmt(jet, &mut fmt_handle)) as *mut std::fmt::Result) + } + } + + pub fn cmr(&self, jet: CCustomJet) -> Cmr { *unsafe { Box::from_raw((self._cmr)(jet) as *mut Cmr) } } - pub fn source_ty(&self, jet: *const JetSelfHandle) -> TypeName { + pub fn source_ty(&self, jet: CCustomJet) -> TypeName { *unsafe { Box::from_raw((self._source_ty)(jet) as *mut TypeName) } } - pub fn target_ty(&self, jet: *const JetSelfHandle) -> TypeName { + pub fn target_ty(&self, jet: CCustomJet) -> TypeName { *unsafe { Box::from_raw((self._target_ty)(jet) as *mut TypeName) } } pub fn encode( &self, - jet: *const JetSelfHandle, + jet: CCustomJet, w: &mut BitWriter, ) -> std::io::Result { let mut buffer = Vec::new(); @@ -97,35 +208,16 @@ impl CustomJetApi { /// `I` of `BitIter` being clonable via `std::ptr::read`. /// Need to ask for normal `Clone` /// - pub fn decode + Clone>( + pub fn decode>( &self, bits: &mut BitIter, - ) -> Result<*const JetSelfHandle, hal_simplicity::simplicity::decode::Error> { - let (data, _) = bits.clone().collect_bits(); - let bit_iter_handle = BitIterHandle { data }; - - let DecodeFFIRes { - decoded, - n_total_read, - } = (self._decode)(&bit_iter_handle); - - for _ in 0..n_total_read { - bits.next(); - } - - let res = unsafe { - *Box::from_raw( - decoded - as *mut Result<*const JetSelfHandle, hal_simplicity::simplicity::decode::Error>, - ) - }; - - res + ) -> Result { + todo!() } pub fn c_jet_ptr( &self, - jet: *const JetSelfHandle, + jet: CCustomJet, ) -> Box bool> { unsafe { *Box::from_raw((self._c_jet_ptr)(jet) @@ -134,4 +226,8 @@ impl CustomJetApi { >) } } + + pub fn cost(&self, jet: CCustomJet) -> Cost { + unsafe { *Box::from_raw(self._cost(jet) as *mut Cost) } + } } diff --git a/core/src/lib.rs b/core/src/lib.rs index c470803..6cb4cff 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -61,3 +61,8 @@ impl FromStr for BitcoinNetwork { } } } + +#[doc(hidden)] +pub mod __simplicity { + pub use hal_simplicity::simplicity; +} diff --git a/jet_plugins/src/c_wrappers.rs b/jet_plugins/src/c_wrappers.rs index 81473ad..cdc0a82 100644 --- a/jet_plugins/src/c_wrappers.rs +++ b/jet_plugins/src/c_wrappers.rs @@ -1,15 +1,22 @@ -use std::io::Write; - -use proc_macro::TokenStream; +use crate::StaticTokenInfo; use quote::quote; -use syn::{ - Ident, LitByteStr, LitStr, Path, Token, - parse::{Parse, ParseStream}, - parse_macro_input, parse2, - punctuated::Punctuated, -}; -use crate::{StaticTokenInfo, elements_idents, pascal_to_snake_case}; +pub(crate) fn cost_c_wrapper() -> proc_macro2::TokenStream { + let jet_self = StaticTokenInfo::jet_self_handle(); + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_trait = StaticTokenInfo::jet_trait_path(); + let cost_handle = StaticTokenInfo::cost_handle(); + + quote! { + pub extern "C" fn _cost(_self: #jet_self) -> *const #cost_handle { + let jet = #enum_ident::variant_from_index(_self.index); + let boxed = Box::new( + <#enum_ident as #jet_trait>::cost(&jet) + ); + Box::into_raw(boxed) as *const #cost_handle + } + } +} pub(crate) fn c_jet_ptr_wrapper() -> proc_macro2::TokenStream { let jet_self = StaticTokenInfo::jet_self_handle(); @@ -17,11 +24,9 @@ pub(crate) fn c_jet_ptr_wrapper() -> proc_macro2::TokenStream { quote! { #[unsafe(no_mangle)] - pub extern "C" fn _c_jet_ptr(_self: *const #jet_self) -> *const () { - let jet_ref = unsafe { - &*(_self as *const #enum_ident) - }; - let boxed = Box::new(<#enum_ident>::c_jet_ptr_boxed(jet_ref)); + pub extern "C" fn _c_jet_ptr(_self: #jet_self) -> *const () { + let jet = #enum_ident::variant_from_index(_self.index); + let boxed = Box::new(<#enum_ident>::c_jet_ptr_boxed(&jet)); // * Box bool> Box::into_raw(boxed) as *const () } @@ -47,15 +52,17 @@ pub(crate) fn decode_c_wrapper() -> proc_macro2::TokenStream { let res = Box::new(match decode_res { Ok(jet) => { - let boxed = Box::new(jet); - Ok(Box::into_raw(boxed) as *const #jet_self) + let index = #enum_ident::variant_to_index(&jet); + Ok(#jet_self { + index + }) } Err(err) => Err(err) }); let n_total_read = bit_iter.n_total_read(); - // (*Result<*const JetSelfHandle, simplicity::decode::Error>, usize) + // (*Result, usize) #decode_ffi_res { decoded: Box::into_raw(res) as *const (), n_total_read @@ -73,14 +80,12 @@ pub(crate) fn encode_c_wrapper() -> proc_macro2::TokenStream { quote! { #[unsafe(no_mangle)] - pub extern "C" fn _encode(_self: *const #jet_self, w: *mut #bitwriter_handle) -> *const () { - let jet_ref = unsafe { - &*(_self as *const #enum_ident) - }; + pub extern "C" fn _encode(_self: #jet_self, w: *mut #bitwriter_handle) -> *const () { + let jet = #enum_ident::variant_from_index(_self.index); let bit_writer = unsafe { &mut (*w)._writer }; - let boxed = Box::new(<#enum_ident as #jet_trait>::encode(jet_ref, bit_writer)); + let boxed = Box::new(<#enum_ident as #jet_trait>::encode(&jet, bit_writer)); // *std::io::Result Box::into_raw(boxed) as *const () } @@ -109,11 +114,9 @@ pub(crate) fn src_trg_ty_c_wrapper(mode: &str) -> proc_macro2::TokenStream { quote! { #[unsafe(no_mangle)] - pub extern "C" fn #fn_name(_self: *const #jet_self) -> *const #typename_handle { - let jet_ref = unsafe { - &*(_self as *const #enum_ident) - }; - let boxed = Box::new(<#enum_ident as #jet_trait>::#src_trg_ty(jet_ref)); + pub extern "C" fn #fn_name(_self: #jet_self) -> *const #typename_handle { + let jet = #enum_ident::variant_from_index(_self.index); + let boxed = Box::new(<#enum_ident as #jet_trait>::#src_trg_ty(&jet)); Box::into_raw(boxed) as *const #typename_handle } } @@ -127,61 +130,116 @@ pub(crate) fn cmr_c_wrapper() -> proc_macro2::TokenStream { quote! { #[unsafe(no_mangle)] - pub extern "C" fn _cmr(_self: *const #jet_self) -> *const #cmr_handle { - let jet_ref = unsafe { - &*(_self as *const #enum_ident) - }; - let boxed = Box::new(<#enum_ident as #jet_trait>::cmr(jet_ref)); + pub extern "C" fn _cmr(_self: #jet_self) -> *const #cmr_handle { + let jet = #enum_ident::variant_from_index(_self.index); + let boxed = Box::new(<#enum_ident as #jet_trait>::cmr(&jet)); Box::into_raw(boxed) as *const #cmr_handle } } } -pub(crate) fn free_jet_handler() -> proc_macro2::TokenStream { +pub(crate) fn from_str_c_wrapper() -> proc_macro2::TokenStream { + let str_handle = StaticTokenInfo::str_handle(); let enum_ident = StaticTokenInfo::enum_ident(); - let jet_self_handle = StaticTokenInfo::jet_self_handle(); + let jet_self = StaticTokenInfo::jet_self_handle(); quote! { #[unsafe(no_mangle)] - pub extern "C" fn _free_jet(_self: *const #jet_self_handle) { - if !_self.is_null() { - let _ = unsafe { Box::from_raw(_self as *mut #enum_ident) }; + pub extern "C" fn _from_str(name: *const #str_handle) -> *const () { + if name.is_null() { + panic!("null ptr at name"); } + let _str = unsafe { (*name)._str }; + let parsing_res = <#enum_ident as std::str::FromStr>::from_str(_str.as_ref()); + let boxed = Box::new( + match parsing_res { + Ok(jet) => { + let index = #enum_ident::variant_to_index(&jet); + Ok(#jet_self { + index + }) + }, + Err(err) => Err(err) + } + ); + Box::into_raw(boxed) as *const () } } } -pub(crate) fn jet_getter_dispatch(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { +pub(crate) fn display_fmt_c_wrapper() -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); - let elements_enum = StaticTokenInfo::elements_enum_path(); - let jet_self_handle = StaticTokenInfo::jet_self_handle(); - let elements_strs = elements_idents::ELEMENTS_FIELDS; - let elements_idents = elements_idents::ELEMENTS_FIELDS - .iter() - .map(|name| quote::format_ident!("{}", name)); - let custom_idents = names.iter().map(|name| name.to_string()); + let jet_self = StaticTokenInfo::jet_self_handle(); + let fmt_handle = StaticTokenInfo::fmt_handle(); quote! { #[unsafe(no_mangle)] - pub extern "C" fn _get_jet(name: *const std::ffi::c_char) -> *const #jet_self_handle { - if name.is_null() { - return panic!("name null ptr"); - } + pub extern "C" fn _display_fmt(_self: #jet_self, fmt_handle: *mut #fmt_handle) -> *const () { + let jet = #enum_ident::variant_from_index(_self.index); + let formatter = unsafe { &mut (*fmt_handle)._formatter }; + let boxed = Box::new(<#enum_ident as std::fmt::Display>::fmt(&jet, formatter)); + Box::into_raw(boxed) as *const () + } + } +} - let c_str = unsafe { std::ffi::CStr::from_ptr(name) }; +pub(crate) fn debug_fmt_c_wrapper() -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_self = StaticTokenInfo::jet_self_handle(); + let fmt_handle = StaticTokenInfo::fmt_handle(); - let rust_str = match c_str.to_str() { - Ok(rust_str) => rust_str, - Err(_) => panic!("invalid str"), - }; + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _debug_fmt(_self: #jet_self, fmt_handle: *mut #fmt_handle) -> *const () { + let jet = #enum_ident::variant_from_index(_self.index); + let formatter = unsafe { &mut (*fmt_handle)._formatter }; + let boxed = Box::new(<#enum_ident as core::fmt::Debug>::fmt(&jet, formatter)); + Box::into_raw(boxed) as *const () + } + } +} - let boxed = Box::new(match rust_str { - #(#elements_strs => #enum_ident::Elements(#elements_enum::#elements_idents)), *, - #(#custom_idents => #enum_ident::#names), *, - _ => unreachable!("Jet does not exists") - }); +pub(crate) fn hash_c_wrapper() -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_self = StaticTokenInfo::jet_self_handle(); + let hasher_handle = StaticTokenInfo::hasher_handle(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _hash(_self: #jet_self, hasher_handle: *mut #hasher_handle) { + let jet = #enum_ident::variant_from_index(_self.index); + let hasher = unsafe { &mut (*hasher_handle)._hasher }; + <#enum_ident as std::hash::Hash>::hash(&jet, hasher) + } + } +} - Box::into_raw(boxed) as *const #jet_self_handle +pub(crate) fn partial_cmp_c_wrapper() -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_self = StaticTokenInfo::jet_self_handle(); + let c_option_ordering = StaticTokenInfo::c_option_ordering(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _partial_cmp(_self: #jet_self, other: #jet_self) -> #c_option_ordering { + let lhs = #enum_ident::variant_from_index(_self.index); + let rhs = #enum_ident::variant_from_index(other.index); + #c_option_ordering::from(lhs.partial_cmp(&rhs)) + } + } +} + +pub(crate) fn cmp_c_wrapper() -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_self = StaticTokenInfo::jet_self_handle(); + let c_ordering = StaticTokenInfo::c_ordering(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _cmp(_self: #jet_self, other: #jet_self) -> #c_ordering { + let lhs = #enum_ident::variant_from_index(_self.index); + let rhs = #enum_ident::variant_from_index(other.index); + #c_ordering::from(lhs.cmp(&rhs)) } } } diff --git a/jet_plugins/src/helpers.rs b/jet_plugins/src/helpers.rs index 6e3e330..778f818 100644 --- a/jet_plugins/src/helpers.rs +++ b/jet_plugins/src/helpers.rs @@ -1,6 +1,5 @@ use bitcoin_hashes::{Hash, HashEngine, sha256}; use quote::quote; -use std::mem::MaybeUninit; const SIMPLICITY_TAG_PREFIX: &[u8] = b"Simplicity\x1fCommitment\x1f"; const JETIV: sha256::Midstate = sha256::Midstate([ @@ -11,7 +10,7 @@ pub const ENCODE_PREFIX: &[u8] = &[1, 1, 1, 1]; pub type JetCodeBits = u16; pub const JET_ENC_BITLEN: usize = 8 * std::mem::size_of::(); // `Jet::encode` uses `write_bits_be` which is bounded to u64 -const CHECK_CODE_BITS_TYPE: () = { +const _: () = { let _ = 0 as JetCodeBits; if std::mem::size_of::() > std::mem::size_of::() { panic!("JetCodeBits type should not exceed u64") @@ -237,39 +236,6 @@ pub fn pascal_to_snake_case(s: &str) -> String { snake } -// Currently hardcoded inside `CustomExtension::ALL` impl, maybe worth to move it out there -struct AllVariantsBuilder { - data: [MaybeUninit; LEN], - len: usize, -} - -impl AllVariantsBuilder { - const fn new() -> Self { - Self { - data: [MaybeUninit::uninit(); LEN], - len: 0, - } - } - - const fn push(&mut self, item: Enum) { - assert!(self.len < self.data.len()); - - self.data[self.len].write(item); - self.len += 1; - } - - const fn finalize(self) -> [Enum; LEN] { - assert!(self.len == LEN); - - let ptr = &self.data as *const [MaybeUninit; LEN] as *const [Enum; LEN]; - let res = unsafe { std::ptr::read(ptr) }; - - std::mem::forget(self.data); - - res - } -} - #[cfg(test)] mod test { use super::*; diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index 91fe44e..0160295 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -1,9 +1,8 @@ -use proc_macro::TokenStream; use quote::quote; use syn::{ - Ident, LitByteStr, LitStr, Path, Token, + Ident, LitByteStr, LitStr, Token, parse::{Parse, ParseStream}, - parse_macro_input, parse2, + parse2, punctuated::Punctuated, }; @@ -14,36 +13,50 @@ use helpers::snake_to_pascal_case; use crate::{ c_wrappers::{ - c_jet_ptr_wrapper, cmr_c_wrapper, decode_c_wrapper, encode_c_wrapper, free_jet_handler, - jet_getter_dispatch, src_trg_ty_c_wrapper, + c_jet_ptr_wrapper, cmp_c_wrapper, cmr_c_wrapper, cost_c_wrapper, debug_fmt_c_wrapper, + decode_c_wrapper, display_fmt_c_wrapper, encode_c_wrapper, from_str_c_wrapper, + hash_c_wrapper, partial_cmp_c_wrapper, src_trg_ty_c_wrapper, }, - helpers::{ENCODE_PREFIX, JET_ENC_BITLEN, JetBranchCode, JetDecodeTree, pascal_to_snake_case}, + helpers::{JET_ENC_BITLEN, JetBranchCode, JetDecodeTree, pascal_to_snake_case}, }; -// TODO: get rid of hardcoded import const STRUCT_EXTENSION_NAME: &str = "JetExtension"; -const ELEMENTS_ENUM_PATH: &str = "hal_simplicity::simplicity::jet::Elements"; -const JET_TRAIT_PATH: &str = "hal_simplicity::simplicity::jet::Jet"; +// Elements stuff +const ELEMENTS_ENUM_PATH: &str = + "simplicity_unchained_core::__simplicity::simplicity::jet::Elements"; +const JET_TRAIT_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::jet::Jet"; const UNCHAINED_ENV_PATH: &str = "simplicity_unchained_core::jets::environments::ElementsUnchainedEnv"; -const BIT_WRITER_PATH: &str = "hal_simplicity::simplicity::BitWriter"; -const BIT_ITER_PATH: &str = "hal_simplicity::simplicity::BitIter"; -const CMR_PATH: &str = "hal_simplicity::simplicity::Cmr"; -const COST_PATH: &str = "hal_simplicity::simplicity::Cost"; -const INVALID_JET_ERR: &str = "hal_simplicity::simplicity::decode::Error::InvalidJet"; -const END_OF_STREAM_ERR: &str = "hal_simplicity::simplicity::decode::Error::EndOfStream"; -const DECODE_ERR_TY: &str = "hal_simplicity::simplicity::decode::Error"; - -const TYPE_NAME_PATH: &str = "hal_simplicity::simplicity::jet::type_name::TypeName"; -const CFRAME_ITEM_PATH: &str = "hal_simplicity::simplicity::ffi::CFrameItem"; -const SIMPLICITY_ERROR_TY: &str = "hal_simplicity::simplicity::Error"; - -const JET_SELF_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::JetSelfHandle"; +const BIT_WRITER_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::BitWriter"; +const BIT_ITER_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::BitIter"; +const CMR_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::Cmr"; +const COST_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::Cost"; +const INVALID_JET_ERR: &str = + "simplicity_unchained_core::__simplicity::simplicity::decode::Error::InvalidJet"; +const END_OF_STREAM_ERR: &str = + "simplicity_unchained_core::__simplicity::simplicity::decode::Error::EndOfStream"; +const DECODE_ERR_TY: &str = "simplicity_unchained_core::__simplicity::simplicity::decode::Error"; +const TYPE_NAME_PATH: &str = + "simplicity_unchained_core::__simplicity::simplicity::jet::type_name::TypeName"; +const CFRAME_ITEM_PATH: &str = + "simplicity_unchained_core::__simplicity::simplicity::ffi::CFrameItem"; +const SIMPLICITY_ERROR_TY: &str = "simplicity_unchained_core::__simplicity::simplicity::Error"; + +// C FFI stuff +const JET_SELF_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CCustomJet"; const CMR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CmrHandle"; const TYPENAME_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::TypeNameHandle"; const BIT_WRITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitWriterHandle"; const BIT_ITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitIterHandle"; const DECODE_FFI_RES: &str = "simplicity_unchained_core::jets::jet_dyn::DecodeFFIRes"; +const C_ORDERING: &str = "simplicity_unchained_core::jets::jet_dyn::COrdering"; +const C_OPTION_ORDERING: &str = "simplicity_unchained_core::jets::jet_dyn::COptionOrdering"; +const HASHER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::HasherHandle"; +const FMT_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::FmtHandle"; +const STR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::StrHandle"; +const COST_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CostHandle"; + +const CUSTOM_JET_COST: u32 = 1000; pub(crate) struct StaticTokenInfo {} @@ -130,8 +143,60 @@ impl StaticTokenInfo { fn decode_ffi_res() -> syn::Path { syn::parse_str(DECODE_FFI_RES).expect("Failed to find DecodeFFIRes by given path") } + + fn c_ordering() -> syn::Path { + syn::parse_str(C_ORDERING).expect("Failed to find COrdering by given path") + } + + fn c_option_ordering() -> syn::Path { + syn::parse_str(C_OPTION_ORDERING).expect("Failed to find COptionOrdering by given path") + } + + fn hasher_handle() -> syn::Path { + syn::parse_str(HASHER_HANDLE).expect("Failed to find HasherHandle by given path") + } + + fn fmt_handle() -> syn::Path { + syn::parse_str(FMT_HANDLE).expect("Failed to find FmtHandle by given path") + } + + fn str_handle() -> syn::Path { + syn::parse_str(STR_HANDLE).expect("Failed to find StrHandle by given path") + } + + fn cost_handle() -> syn::Path { + syn::parse_str(COST_HANDLE).expect("Failed to find CostHandle by given path") + } } +/// Implements `Jet` trait and C FFI compatible with `simplicity_unchained_core::jets::jet_dyn` interface. +/// ## Arguments +/// - `name: literal` - name of jet in snake case; +/// - `function: Fn(CFrameItem, CFrameItem, &ElementsUnchainedEnv)`; +/// +/// see `simplicity::jet::type_name` +/// +/// - `source_type: &[u8]` +/// - `target_type: &[u8]` +/// ## Usage +/// ```rust +/// use jet_plugins::register_jets; +/// use simplicity_unchained_core::jets::environments::ElementsUnchainedEnv; +/// use simplicity_unchained_core::__simplicity::simplicity::ffi::CFrameItem; +/// +/// fn custom_jet_1(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { +/// false +/// } +/// +/// fn custom_jet_2(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { +/// false +/// } +/// +/// register_jets![ +/// "custom_jet_1" => custom_jet_1, b"h",b"h"; +/// "custom_jet_2" => custom_jet_2, b"h",b"h"; +/// ]; +/// ``` #[proc_macro] pub fn register_jets(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse2(input.into()).expect("Failed to parse JetsInput"); @@ -143,30 +208,49 @@ pub fn register_jets(input: proc_macro::TokenStream) -> proc_macro::TokenStream let fmt_impl = fmt_trait_full(&names); let from_str_impl = from_str_trait_full(&names); - let c_jet_getters = jet_getter_dispatch(&names); - let jet_handle_free = free_jet_handler(); + let c_ffi = impl_c_ffi(&names); + + quote! { + #self_impl + #jet_trait_impl + #fmt_impl + #from_str_impl + #c_ffi + } + .into() +} + +fn impl_c_ffi(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { + let cmp = cmp_c_wrapper(); + let partial_cmp = partial_cmp_c_wrapper(); + let hash = hash_c_wrapper(); + let debug_fmt = debug_fmt_c_wrapper(); + let display_fmt = display_fmt_c_wrapper(); + let cmr_c_wrapper = cmr_c_wrapper(); let src_ty_c_wrapper = src_trg_ty_c_wrapper("source"); let trg_ty_c_wrapper = src_trg_ty_c_wrapper("target"); let encode_c_wrapper = encode_c_wrapper(); let decode_c_wrapper = decode_c_wrapper(); + let cost_c_wrapper = cost_c_wrapper(); let c_jet_ptr_wrapper = c_jet_ptr_wrapper(); + let from_str = from_str_c_wrapper(); quote! { - #self_impl - #jet_trait_impl - #fmt_impl - #from_str_impl - #c_jet_getters - #jet_handle_free + #cmp + #partial_cmp + #hash + #debug_fmt + #display_fmt #cmr_c_wrapper #src_ty_c_wrapper #trg_ty_c_wrapper #encode_c_wrapper #decode_c_wrapper + #cost_c_wrapper #c_jet_ptr_wrapper + #from_str } - .into() } // TODO: settle on namings @@ -286,7 +370,10 @@ fn impl_cost() -> proc_macro2::TokenStream { let cost_path = StaticTokenInfo::cost_path(); quote! { fn cost(&self) -> #cost_path { - todo!() + match self { + Self::Elements(inner_jet) => inner_jet.cost(), + _ => #cost_path::from_milliweight(#CUSTOM_JET_COST) + } } } } @@ -537,6 +624,15 @@ fn impl_all_constant(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStrea #(builder.push(#enum_ident::#variants);)* builder.finalize() } + + /// Returns index of `self` inside `Self::ALL` array + pub fn variant_to_index(&self) -> usize { + Self::ALL.iter().position(|x| x == self).expect("ALL must contain all enum's variants") + } + + pub fn variant_from_index(idx: usize) -> Self { + Self::ALL[idx] + } } } } diff --git a/service/src/custom_jet.rs b/service/src/custom_jet.rs new file mode 100644 index 0000000..cb0badd --- /dev/null +++ b/service/src/custom_jet.rs @@ -0,0 +1,135 @@ +use dlopen2::wrapper::Container; +use hal_simplicity::simplicity::jet::Jet; +use simplicity_unchained_core::jets::{ + environments::ElementsUnchainedEnv, + jet_dyn::{CCustomJet, CustomJetApi, JetSelfHandle}, +}; +use std::sync::LazyLock; + +// TODO: make env var +static JET_DLL: LazyLock> = LazyLock::new(|| { + unsafe { Container::::load("../target/debug/libplugin_test.so") } + .expect("Failed to load lib") +}); + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct CustomJet { + _jet: CCustomJet, +} + +impl std::str::FromStr for CustomJet { + type Err = hal_simplicity::simplicity::Error; + + fn from_str(s: &str) -> Result { + match JET_DLL.from_str(s) { + Ok(_jet) => Ok(Self { _jet }), + Err(err) => Err(err), + } + } +} + +impl PartialOrd for CustomJet { + fn partial_cmp(&self, other: &Self) -> Option { + JET_DLL.partial_cmp(self._jet, other._jet) + } +} + +impl Ord for CustomJet { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + JET_DLL.cmp(self._jet, other._jet) + } +} + +impl core::hash::Hash for CustomJet { + fn hash(&self, state: &mut H) { + JET_DLL.hash(self._jet, state); + } +} + +impl std::fmt::Debug for CustomJet { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + JET_DLL.debug_fmt(self._jet, f) + } +} + +impl std::fmt::Display for CustomJet { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + JET_DLL.display_fmt(self._jet, f) + } +} + +impl Jet for CustomJet { + type Environment = ElementsUnchainedEnv; + type CJetEnvironment = ElementsUnchainedEnv; + + fn c_jet_env(env: &Self::Environment) -> &Self::CJetEnvironment { + env + } + + fn cmr(&self) -> hal_simplicity::simplicity::Cmr { + JET_DLL.cmr(self._jet) + } + + fn source_ty(&self) -> hal_simplicity::simplicity::jet::type_name::TypeName { + JET_DLL.source_ty(self._jet) + } + + fn target_ty(&self) -> hal_simplicity::simplicity::jet::type_name::TypeName { + JET_DLL.target_ty(self._jet) + } + + fn encode( + &self, + w: &mut hal_simplicity::simplicity::BitWriter, + ) -> std::io::Result { + JET_DLL.encode(self._jet, w) + } + + fn decode>( + bits: &mut hal_simplicity::simplicity::BitIter, + ) -> Result { + JET_DLL.decode(bits).map(|_jet| Self { _jet }) + } + + fn c_jet_ptr( + &self, + ) -> &dyn Fn( + &mut hal_simplicity::simplicity::ffi::CFrameItem, + hal_simplicity::simplicity::ffi::CFrameItem, + &Self::CJetEnvironment, + ) -> bool { + todo!() + } + + fn cost(&self) -> hal_simplicity::simplicity::Cost { + JET_DLL.cost(self._jet) + } +} + +#[cfg(test)] +mod test { + use std::{ + hash::{Hash, Hasher}, + str::FromStr, + }; + + use super::*; + + #[test] + fn test_dll_from_str() { + let jet = CustomJet::from_str("custom_jet_1"); + + assert!(jet.is_ok()); + assert_eq!(jet.unwrap().to_string(), "custom_jet_1") + } + + #[test] + fn test_dll_hash() { + let jet = CustomJet::from_str("custom_jet_1").expect("Failed to load jet"); + + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + jet.hash(&mut hasher); + + let _ = hasher.finish(); + } +} diff --git a/service/src/dll_loader.rs b/service/src/dll_loader.rs deleted file mode 100644 index 0989953..0000000 --- a/service/src/dll_loader.rs +++ /dev/null @@ -1,149 +0,0 @@ -#[cfg(test)] -mod test { - use dlopen2::wrapper::Container; - use hal_simplicity::simplicity::{ - BitIter, BitWriter, - jet::{Elements, Jet}, - }; - use simplicity_unchained_core::jets::jet_dyn::CustomJetApi; - - #[test] - fn test_dll_jet_free() { - let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } - .expect("Failed to load lib"); - - let custom_jet = cont.get_jet("CustomJet1"); - let elements_jet = cont.get_jet("Add16"); - - cont.free_jet(custom_jet); - cont.free_jet(elements_jet); - } - - #[test] - fn test_dll_cmr() { - let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } - .expect("Failed to load lib"); - - let custom_jet = cont.get_jet("CustomJet1"); - let custom_cmr = cont.cmr(custom_jet).to_byte_array(); - - // Cmr is hardcoded by macro. This val taken from result of `cargo expand` - assert_eq!( - custom_cmr, - [ - 19u8, 49u8, 128u8, 215u8, 32u8, 93u8, 0u8, 222u8, 164u8, 160u8, 157u8, 60u8, 148u8, - 109u8, 185u8, 179u8, 223u8, 110u8, 137u8, 180u8, 168u8, 155u8, 16u8, 44u8, 115u8, - 17u8, 194u8, 244u8, 4u8, 102u8, 49u8, 128u8, - ] - ); - - let elements_jet = cont.get_jet("Add16"); - let elements_cmr = cont.cmr(elements_jet); - assert_eq!(elements_cmr, Elements::Add16.cmr()); - - cont.free_jet(custom_jet); - cont.free_jet(elements_jet); - } - - #[test] - fn test_dll_encode() { - let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } - .expect("Failed to load lib"); - - let custom_jet = cont.get_jet("CustomJet1"); - let elements_jet = cont.get_jet("LockTime"); - - let mut buffer = Vec::new(); - let mut w = BitWriter::new(&mut buffer); - - let res1 = cont.encode(custom_jet, &mut w); - let res2 = cont.encode(elements_jet, &mut w); - - assert!(res1.is_ok_and(|x| x == 16)); - assert!(res2.is_ok_and(|x| x == 13)); - - w.flush_all().unwrap(); - - let mut bit_iter = BitIter::from(buffer); - - let mut read_code = |read_bits: u8| -> Option { - let mut res = 0; - let mut cursor = 1 << (read_bits - 1); - - for _ in 0..read_bits { - match bit_iter.next() { - None => return None, - Some(true) => res |= cursor, - Some(false) => {} - } - cursor >>= 1; - } - Some(res) - }; - - let custom_jet_code = read_code(16); - let elements_jet_code = read_code(13); - - assert!(custom_jet_code.is_some_and(|code| code == 65465)); - assert!(elements_jet_code.is_some_and(|code| code == 7218)); - - cont.free_jet(custom_jet); - cont.free_jet(elements_jet); - } - - #[test] - fn test_dll_decode() { - let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } - .expect("Failed to load lib"); - - let custom_jet = cont.get_jet("CustomJet1"); - let elements_jet = cont.get_jet("Add16"); - - let mut buffer = Vec::new(); - let mut w = BitWriter::new(&mut buffer); - - let custom_encoding = cont.encode(custom_jet, &mut w); - assert!(custom_encoding.is_ok()); - let elements_encoding = cont.encode(elements_jet, &mut w); - assert!(elements_encoding.is_ok()); - - w.flush_all().unwrap(); - - let mut bit_iter = BitIter::from(buffer); - - let custom_decoded = cont.decode(&mut bit_iter); - assert!(custom_decoded.is_ok()); - - let custom_cmr = cont.cmr(custom_decoded.unwrap()).to_byte_array(); - - // Cmr is hardcoded by macro. This val taken from result of `cargo expand` - assert_eq!( - custom_cmr, - [ - 19u8, 49u8, 128u8, 215u8, 32u8, 93u8, 0u8, 222u8, 164u8, 160u8, 157u8, 60u8, 148u8, - 109u8, 185u8, 179u8, 223u8, 110u8, 137u8, 180u8, 168u8, 155u8, 16u8, 44u8, 115u8, - 17u8, 194u8, 244u8, 4u8, 102u8, 49u8, 128u8, - ] - ); - - let elements_decoded = cont.decode(&mut bit_iter); - assert!(elements_decoded.is_ok()); - - let elements_cmr = cont.cmr(elements_jet); - assert_eq!(elements_cmr, Elements::Add16.cmr()); - - cont.free_jet(custom_jet); - cont.free_jet(elements_jet); - } - #[test] - fn test_dll_c_jet_ptr() { - let cont = unsafe { Container::::load("../target/debug/libplugin_test.so") } - .expect("Failed to load lib"); - - let custom_jet = cont.get_jet("CustomJet1"); - - let _ = cont.c_jet_ptr(custom_jet); - - cont.free_jet(custom_jet); - } -} diff --git a/service/src/main.rs b/service/src/main.rs index 70ffd77..6bac61f 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -1,6 +1,6 @@ mod cli; mod config; -mod dll_loader; +mod custom_jet; mod handlers; mod validation; From 31fdd3e7bda944a8aa10daf53238983268ebd7e3 Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Fri, 27 Feb 2026 12:54:10 +0200 Subject: [PATCH 08/14] fix bug in `encode` dll wrapper and `decode` impl Moving `BitIter` via FFI was too complicated, so I implemented auxiliar function `decode_bool_iter`, which takes `&mut dyn Iterator `. It also stores entire encoding tree, so is safe now. We can reimplement fallback strategy if `Clone` on `BitIter` would become available. Custom jet codes now store 20 bits (16 bits of hash + 4 bits of prefix) --- core/src/jets/jet_dyn.rs | 63 +- jet_plugins/src/c_wrappers.rs | 21 +- jet_plugins/src/elements_decode_tree.rs | 2666 +++++++++++++++++++++++ jet_plugins/src/helpers.rs | 3 +- jet_plugins/src/lib.rs | 75 +- service/src/custom_jet.rs | 77 + 6 files changed, 2814 insertions(+), 91 deletions(-) create mode 100644 jet_plugins/src/elements_decode_tree.rs diff --git a/core/src/jets/jet_dyn.rs b/core/src/jets/jet_dyn.rs index b17b559..b0c6108 100644 --- a/core/src/jets/jet_dyn.rs +++ b/core/src/jets/jet_dyn.rs @@ -1,12 +1,8 @@ use dlopen2::wrapper::WrapperApi; use hal_simplicity::simplicity::{ - BitCollector, BitIter, BitWriter, Cmr, Cost, ffi::CFrameItem, jet::type_name::TypeName, -}; -use std::{ - fmt::Formatter, - hash::Hasher, - io::{IoSlice, Write}, + BitIter, BitWriter, Cmr, Cost, ffi::CFrameItem, jet::type_name::TypeName, }; +use std::{fmt::Formatter, hash::Hasher, io::Write}; use crate::jets::environments::ElementsUnchainedEnv; @@ -101,19 +97,13 @@ impl From for Option { } #[repr(C)] -pub struct BitWriterHandle<'a> { - pub _writer: BitWriter<&'a mut Vec>, -} - -#[repr(C)] -pub struct BitIterHandle { - pub data: Vec, +pub struct BitIterHandle<'a> { + pub _iter: &'a mut dyn Iterator, } #[repr(C)] -pub struct DecodeFFIRes { - pub decoded: *const (), - pub n_total_read: usize, +pub struct BitWriterHandle<'a> { + pub _writer: BitWriter<&'a mut Vec>, } #[derive(WrapperApi)] @@ -128,8 +118,8 @@ pub struct CustomJetApi { _source_ty: extern "C" fn(_self: CCustomJet) -> *const TypeNameHandle, _target_ty: extern "C" fn(_self: CCustomJet) -> *const TypeNameHandle, _encode: extern "C" fn(_self: CCustomJet, w: *mut BitWriterHandle) -> *const (), // *std::io::Result - _decode: extern "C" fn(w: *const BitIterHandle) -> DecodeFFIRes, - _c_jet_ptr: extern "C" fn(_self: CCustomJet) -> *const (), // (*const Result, usize) + _decode: extern "C" fn(w: *mut BitIterHandle) -> *const (), // (*const Result, usize) + _c_jet_ptr: extern "C" fn(_self: CCustomJet) -> *const (), // * Box bool> _cost: extern "C" fn(_self: CCustomJet) -> *const CostHandle, } @@ -197,22 +187,39 @@ impl CustomJetApi { *Box::from_raw((self._encode)(jet, &mut handle) as *mut std::io::Result) }; - handle._writer.flush_all()?; - - w.write_vectored(&[IoSlice::new(&buffer)])?; - - res + match res { + Ok(bits_written) => { + handle._writer.flush_all()?; + + let mut bit_iter = BitIter::from(buffer); + for _ in 0..bits_written { + if let Some(bit) = bit_iter.next() { + w.write_bit(bit)?; + } + } + Ok(bits_written) + } + Err(err) => Err(err), + } } - /// Underlying code inside `Jet` trait implementor macro is unsafe and relies on - /// `I` of `BitIter` being clonable via `std::ptr::read`. - /// Need to ask for normal `Clone` - /// pub fn decode>( &self, bits: &mut BitIter, ) -> Result { - todo!() + let mut handle = BitIterHandle { + _iter: bits as &mut dyn Iterator, + }; + + let decoded = (self._decode)(&mut handle); + + let res = unsafe { + *Box::from_raw( + decoded as *mut Result, + ) + }; + + res } pub fn c_jet_ptr( diff --git a/jet_plugins/src/c_wrappers.rs b/jet_plugins/src/c_wrappers.rs index cdc0a82..e2681a9 100644 --- a/jet_plugins/src/c_wrappers.rs +++ b/jet_plugins/src/c_wrappers.rs @@ -8,6 +8,7 @@ pub(crate) fn cost_c_wrapper() -> proc_macro2::TokenStream { let cost_handle = StaticTokenInfo::cost_handle(); quote! { + #[unsafe(no_mangle)] pub extern "C" fn _cost(_self: #jet_self) -> *const #cost_handle { let jet = #enum_ident::variant_from_index(_self.index); let boxed = Box::new( @@ -37,18 +38,13 @@ pub(crate) fn decode_c_wrapper() -> proc_macro2::TokenStream { let bititer_handle = StaticTokenInfo::bititer_handle(); let jet_self = StaticTokenInfo::jet_self_handle(); let enum_ident = StaticTokenInfo::enum_ident(); - let jet_trait = StaticTokenInfo::jet_trait_path(); - let bit_iter = StaticTokenInfo::bit_iter_path(); - let decode_ffi_res = StaticTokenInfo::decode_ffi_res(); quote! { #[unsafe(no_mangle)] - pub extern "C" fn _decode(w: *const #bititer_handle) -> #decode_ffi_res + pub extern "C" fn _decode(w: *mut #bititer_handle) -> *const () { - let mut bit_iter = unsafe { - #bit_iter::from((*w).data.iter().copied()) - }; - let decode_res = <#enum_ident as #jet_trait>::decode(&mut bit_iter); + let bit_iter: &mut dyn Iterator = unsafe { &mut *(*w)._iter }; + let decode_res = #enum_ident::decode_bool_iter(bit_iter); let res = Box::new(match decode_res { Ok(jet) => { @@ -60,13 +56,8 @@ pub(crate) fn decode_c_wrapper() -> proc_macro2::TokenStream { Err(err) => Err(err) }); - let n_total_read = bit_iter.n_total_read(); - - // (*Result, usize) - #decode_ffi_res { - decoded: Box::into_raw(res) as *const (), - n_total_read - } + // *Result + Box::into_raw(res) as *const () } } diff --git a/jet_plugins/src/elements_decode_tree.rs b/jet_plugins/src/elements_decode_tree.rs new file mode 100644 index 0000000..8249eb4 --- /dev/null +++ b/jet_plugins/src/elements_decode_tree.rs @@ -0,0 +1,2666 @@ +use crate::StaticTokenInfo; +use quote::quote; + +pub(crate) fn hardcoded_decode_tree( + custom_branches: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let elements_enum = StaticTokenInfo::elements_enum_path(); + quote! { + decode_bits!(bits, { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Verify)}, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Low1)}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Low8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Low16)}, + 1 => {#enum_ident::Elements(#elements_enum::Low32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Low64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::High1)}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::High8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::High16)}, + 1 => {#enum_ident::Elements(#elements_enum::High32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::High64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Complement1)}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Complement8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Complement16)}, + 1 => {#enum_ident::Elements(#elements_enum::Complement32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Complement64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::And1)}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::And8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::And16)}, + 1 => {#enum_ident::Elements(#elements_enum::And32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::And64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Or1)}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Or8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Or16)}, + 1 => {#enum_ident::Elements(#elements_enum::Or32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Or64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Xor1)}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Xor8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Xor16)}, + 1 => {#enum_ident::Elements(#elements_enum::Xor32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Xor64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Maj1)}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Maj8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Maj16)}, + 1 => {#enum_ident::Elements(#elements_enum::Maj32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Maj64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::XorXor1)}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::XorXor8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::XorXor16)}, + 1 => {#enum_ident::Elements(#elements_enum::XorXor32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::XorXor64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Ch1)}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Ch8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Ch16)}, + 1 => {#enum_ident::Elements(#elements_enum::Ch32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Ch64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Some1)}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Some8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Some16)}, + 1 => {#enum_ident::Elements(#elements_enum::Some32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Some64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::All8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::All16)}, + 1 => {#enum_ident::Elements(#elements_enum::All32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::All64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Eq1)}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Eq8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Eq16)}, + 1 => {#enum_ident::Elements(#elements_enum::Eq32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Eq64)}, + 1 => {} + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Eq256)}, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift8_1)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift16_1)}, + 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift32_1)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_1)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift8_2)}, + 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift16_2)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift32_2)}, + 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_2)} + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift8_4)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift16_4)}, + 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift32_4)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_4)}, + 1 => {} + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift16_8)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift32_8)}, + 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_8)} + }, + 1 => {} + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift32_16)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_16)}, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_32)}, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::FullRightShift8_1)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift16_1)}, + 1 => {#enum_ident::Elements(#elements_enum::FullRightShift32_1)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift64_1)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift8_2)}, + 1 => {#enum_ident::Elements(#elements_enum::FullRightShift16_2)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift32_2)}, + 1 => {#enum_ident::Elements(#elements_enum::FullRightShift64_2)} + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift8_4)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift16_4)}, + 1 => {#enum_ident::Elements(#elements_enum::FullRightShift32_4)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift64_4)}, + 1 => {} + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift16_8)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift32_8)}, + 1 => {#enum_ident::Elements(#elements_enum::FullRightShift64_8)} + }, + 1 => {} + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift32_16)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift64_16)}, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullRightShift64_32)}, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Leftmost8_1)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost16_1)}, + 1 => {#enum_ident::Elements(#elements_enum::Leftmost32_1)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost64_1)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost8_2)}, + 1 => {#enum_ident::Elements(#elements_enum::Leftmost16_2)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost32_2)}, + 1 => {#enum_ident::Elements(#elements_enum::Leftmost64_2)} + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost8_4)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost16_4)}, + 1 => {#enum_ident::Elements(#elements_enum::Leftmost32_4)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost64_4)}, + 1 => {} + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost16_8)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost32_8)}, + 1 => {#enum_ident::Elements(#elements_enum::Leftmost64_8)} + }, + 1 => {} + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost32_16)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost64_16)}, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Leftmost64_32)}, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Rightmost8_1)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost16_1)}, + 1 => {#enum_ident::Elements(#elements_enum::Rightmost32_1)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost64_1)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost8_2)}, + 1 => {#enum_ident::Elements(#elements_enum::Rightmost16_2)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost32_2)}, + 1 => {#enum_ident::Elements(#elements_enum::Rightmost64_2)} + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost8_4)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost16_4)}, + 1 => {#enum_ident::Elements(#elements_enum::Rightmost32_4)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost64_4)}, + 1 => {} + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost16_8)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost32_8)}, + 1 => {#enum_ident::Elements(#elements_enum::Rightmost64_8)} + }, + 1 => {} + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost32_16)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost64_16)}, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Rightmost64_32)}, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::LeftPadLow1_8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow1_16)}, + 1 => {#enum_ident::Elements(#elements_enum::LeftPadLow1_32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow1_64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow8_16)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow8_32)}, + 1 => {#enum_ident::Elements(#elements_enum::LeftPadLow8_64)} + }, + 1 => {} + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow16_32)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow16_64)}, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow32_64)}, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::LeftPadHigh1_8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh1_16)}, + 1 => {#enum_ident::Elements(#elements_enum::LeftPadHigh1_32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh1_64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh8_16)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh8_32)}, + 1 => {#enum_ident::Elements(#elements_enum::LeftPadHigh8_64)} + }, + 1 => {} + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh16_32)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh16_64)}, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh32_64)}, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::LeftExtend1_8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftExtend1_16)}, + 1 => {#enum_ident::Elements(#elements_enum::LeftExtend1_32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftExtend1_64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftExtend8_16)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftExtend8_32)}, + 1 => {#enum_ident::Elements(#elements_enum::LeftExtend8_64)} + }, + 1 => {} + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftExtend16_32)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftExtend16_64)}, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftExtend32_64)}, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::RightPadLow1_8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadLow1_16)}, + 1 => {#enum_ident::Elements(#elements_enum::RightPadLow1_32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadLow1_64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadLow8_16)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadLow8_32)}, + 1 => {#enum_ident::Elements(#elements_enum::RightPadLow8_64)} + }, + 1 => {} + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadLow16_32)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadLow16_64)}, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadLow32_64)}, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::RightPadHigh1_8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh1_16)}, + 1 => {#enum_ident::Elements(#elements_enum::RightPadHigh1_32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh1_64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh8_16)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh8_32)}, + 1 => {#enum_ident::Elements(#elements_enum::RightPadHigh8_64)} + }, + 1 => {} + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh16_32)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh16_64)}, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh32_64)}, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightExtend8_16)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightExtend8_32)}, + 1 => {#enum_ident::Elements(#elements_enum::RightExtend8_64)} + }, + 1 => {} + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::RightExtend16_32)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightExtend16_64)}, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightExtend32_64)}, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::LeftShiftWith8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftShiftWith16)}, + 1 => {#enum_ident::Elements(#elements_enum::LeftShiftWith32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftShiftWith64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::RightShiftWith8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightShiftWith16)}, + 1 => {#enum_ident::Elements(#elements_enum::RightShiftWith32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::RightShiftWith64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::LeftShift8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftShift16)}, + 1 => {#enum_ident::Elements(#elements_enum::LeftShift32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftShift64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::RightShift8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightShift16)}, + 1 => {#enum_ident::Elements(#elements_enum::RightShift32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::RightShift64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::LeftRotate8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftRotate16)}, + 1 => {#enum_ident::Elements(#elements_enum::LeftRotate32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::LeftRotate64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::RightRotate8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::RightRotate16)}, + 1 => {#enum_ident::Elements(#elements_enum::RightRotate32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::RightRotate64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => {} + } + } + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::One8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::One16)}, + 1 => {#enum_ident::Elements(#elements_enum::One32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::One64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::FullAdd8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullAdd16)}, + 1 => {#enum_ident::Elements(#elements_enum::FullAdd32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FullAdd64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Add8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Add16)}, + 1 => {#enum_ident::Elements(#elements_enum::Add32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Add64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::FullIncrement8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullIncrement16)}, + 1 => {#enum_ident::Elements(#elements_enum::FullIncrement32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FullIncrement64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Increment8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Increment16)}, + 1 => {#enum_ident::Elements(#elements_enum::Increment32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Increment64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::FullSubtract8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullSubtract16)}, + 1 => {#enum_ident::Elements(#elements_enum::FullSubtract32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FullSubtract64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Subtract8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Subtract16)}, + 1 => {#enum_ident::Elements(#elements_enum::Subtract32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Subtract64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Negate8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Negate16)}, + 1 => {#enum_ident::Elements(#elements_enum::Negate32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Negate64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::FullDecrement8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullDecrement16)}, + 1 => {#enum_ident::Elements(#elements_enum::FullDecrement32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FullDecrement64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Decrement8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Decrement16)}, + 1 => {#enum_ident::Elements(#elements_enum::Decrement32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Decrement64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::FullMultiply8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FullMultiply16)}, + 1 => {#enum_ident::Elements(#elements_enum::FullMultiply32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FullMultiply64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Multiply8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Multiply16)}, + 1 => {#enum_ident::Elements(#elements_enum::Multiply32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Multiply64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::IsZero8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::IsZero16)}, + 1 => {#enum_ident::Elements(#elements_enum::IsZero32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::IsZero64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::IsOne8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::IsOne16)}, + 1 => {#enum_ident::Elements(#elements_enum::IsOne32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::IsOne64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Le8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Le16)}, + 1 => {#enum_ident::Elements(#elements_enum::Le32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Le64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Lt8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Lt16)}, + 1 => {#enum_ident::Elements(#elements_enum::Lt32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Lt64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Min8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Min16)}, + 1 => {#enum_ident::Elements(#elements_enum::Min32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Min64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Max8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Max16)}, + 1 => {#enum_ident::Elements(#elements_enum::Max32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Max64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Median8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Median16)}, + 1 => {#enum_ident::Elements(#elements_enum::Median32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Median64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::DivMod128_64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::DivMod8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::DivMod16)}, + 1 => {#enum_ident::Elements(#elements_enum::DivMod32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::DivMod64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Divide8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Divide16)}, + 1 => {#enum_ident::Elements(#elements_enum::Divide32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Divide64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Modulo8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Modulo16)}, + 1 => {#enum_ident::Elements(#elements_enum::Modulo32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Modulo64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::Divides8)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Divides16)}, + 1 => {#enum_ident::Elements(#elements_enum::Divides32)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Divides64)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Sha256Block)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Sha256Iv)}, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add1)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add2)}, + 1 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add4)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add8)}, + 1 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add16)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add32)}, + 1 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add64)} + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add128)}, + 1 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add256)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add512)}, + 1 => {} + } + }, + 1 => {} + } + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8AddBuffer511)}, + 1 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Finalize)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Init)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => {} + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::PointVerify1)}, + 1 => {} + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Decompress)}, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::LinearVerify1)}, + 1 => {} + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::LinearCombination1)}, + 1 => {} + }, + 1 => {#enum_ident::Elements(#elements_enum::Scale)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Generate)}, + 1 => {#enum_ident::Elements(#elements_enum::GejInfinity)} + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::GejNormalize)}, + 1 => {#enum_ident::Elements(#elements_enum::GejNegate)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::GeNegate)}, + 1 => {#enum_ident::Elements(#elements_enum::GejDouble)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::GejAdd)}, + 1 => {#enum_ident::Elements(#elements_enum::GejGeAddEx)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::GejGeAdd)}, + 1 => {#enum_ident::Elements(#elements_enum::GejRescale)} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::GejIsInfinity)}, + 1 => {#enum_ident::Elements(#elements_enum::GejEquiv)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::GejGeEquiv)}, + 1 => {#enum_ident::Elements(#elements_enum::GejXEquiv)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::GejYIsOdd)}, + 1 => {#enum_ident::Elements(#elements_enum::GejIsOnCurve)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::GeIsOnCurve)}, + 1 => {#enum_ident::Elements(#elements_enum::ScalarNormalize)} + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::ScalarNegate)}, + 1 => {#enum_ident::Elements(#elements_enum::ScalarAdd)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::ScalarSquare)}, + 1 => {#enum_ident::Elements(#elements_enum::ScalarMultiply)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::ScalarMultiplyLambda)}, + 1 => {#enum_ident::Elements(#elements_enum::ScalarInvert)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::ScalarIsZero)}, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {}, + 1 => { + 0 => {}, + 1 => {#enum_ident::Elements(#elements_enum::FeNormalize)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FeNegate)}, + 1 => {#enum_ident::Elements(#elements_enum::FeAdd)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FeSquare)}, + 1 => {#enum_ident::Elements(#elements_enum::FeMultiply)} + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FeMultiplyBeta)}, + 1 => {#enum_ident::Elements(#elements_enum::FeInvert)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::FeSquareRoot)}, + 1 => {#enum_ident::Elements(#elements_enum::FeIsZero)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::FeIsOdd)}, + 1 => {} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::HashToCurve)}, + 1 => {#enum_ident::Elements(#elements_enum::Swu)} + } + } + } + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::CheckSigVerify)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Bip0340Verify)}, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => {}, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::ParseLock)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::ParseSequence)}, + 1 => {#enum_ident::Elements(#elements_enum::TapdataInit)} + }, + 1 => {} + } + } + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::SigAllHash)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::TxHash)}, + 1 => {#enum_ident::Elements(#elements_enum::TapEnvHash)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::OutputsHash)}, + 1 => {#enum_ident::Elements(#elements_enum::InputsHash)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::IssuancesHash)}, + 1 => {#enum_ident::Elements(#elements_enum::InputUtxosHash)} + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::OutputHash)}, + 1 => {#enum_ident::Elements(#elements_enum::OutputAmountsHash)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::OutputScriptsHash)}, + 1 => {#enum_ident::Elements(#elements_enum::OutputNoncesHash)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::OutputRangeProofsHash)}, + 1 => {#enum_ident::Elements(#elements_enum::OutputSurjectionProofsHash)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::InputHash)}, + 1 => {#enum_ident::Elements(#elements_enum::InputOutpointsHash)} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::InputSequencesHash)}, + 1 => {#enum_ident::Elements(#elements_enum::InputAnnexesHash)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::InputScriptSigsHash)}, + 1 => {#enum_ident::Elements(#elements_enum::IssuanceHash)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::IssuanceAssetAmountsHash)}, + 1 => {#enum_ident::Elements(#elements_enum::IssuanceTokenAmountsHash)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::IssuanceRangeProofsHash)}, + 1 => {#enum_ident::Elements(#elements_enum::IssuanceBlindingEntropyHash)} + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::InputUtxoHash)}, + 1 => {#enum_ident::Elements(#elements_enum::InputAmountsHash)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::InputScriptsHash)}, + 1 => {#enum_ident::Elements(#elements_enum::TapleafHash)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::TappathHash)}, + 1 => {#enum_ident::Elements(#elements_enum::OutpointHash)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::AssetAmountHash)}, + 1 => {#enum_ident::Elements(#elements_enum::NonceHash)} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::AnnexHash)}, + 1 => {#enum_ident::Elements(#elements_enum::BuildTapleafSimplicity)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::BuildTapbranch)}, + 1 => {#enum_ident::Elements(#elements_enum::BuildTaptweak)} + } + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::CheckLockHeight)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::CheckLockTime)}, + 1 => {#enum_ident::Elements(#elements_enum::CheckLockDistance)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::CheckLockDuration)}, + 1 => {#enum_ident::Elements(#elements_enum::TxLockHeight)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::TxLockTime)}, + 1 => {#enum_ident::Elements(#elements_enum::TxLockDistance)} + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::TxLockDuration)}, + 1 => {#enum_ident::Elements(#elements_enum::TxIsFinal)} + }, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + } + } + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::Issuance)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::IssuanceAsset)}, + 1 => {#enum_ident::Elements(#elements_enum::IssuanceToken)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::IssuanceEntropy)}, + 1 => {#enum_ident::Elements(#elements_enum::CalculateIssuanceEntropy)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::CalculateAsset)}, + 1 => {#enum_ident::Elements(#elements_enum::CalculateExplicitToken)} + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::CalculateConfidentialToken)}, + 1 => {#enum_ident::Elements(#elements_enum::LbtcAsset)} + }, + 1 => {} + }, + 1 => {} + } + }, + 1 => {} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::ScriptCMR)}, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::InternalKey)}, + 1 => {#enum_ident::Elements(#elements_enum::CurrentIndex)} + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::NumInputs)}, + 1 => {#enum_ident::Elements(#elements_enum::NumOutputs)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::LockTime)}, + 1 => {#enum_ident::Elements(#elements_enum::OutputAsset)} + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::OutputAmount)}, + 1 => {#enum_ident::Elements(#elements_enum::OutputNonce)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::OutputScriptHash)}, + 1 => {#enum_ident::Elements(#elements_enum::OutputNullDatum)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::OutputIsFee)}, + 1 => {#enum_ident::Elements(#elements_enum::OutputSurjectionProof)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::OutputRangeProof)}, + 1 => {#enum_ident::Elements(#elements_enum::TotalFee)} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::CurrentPegin)}, + 1 => {#enum_ident::Elements(#elements_enum::CurrentPrevOutpoint)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::CurrentAsset)}, + 1 => {#enum_ident::Elements(#elements_enum::CurrentAmount)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::CurrentScriptHash)}, + 1 => {#enum_ident::Elements(#elements_enum::CurrentSequence)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::CurrentAnnexHash)}, + 1 => {#enum_ident::Elements(#elements_enum::CurrentScriptSigHash)} + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::CurrentReissuanceBlinding)}, + 1 => {#enum_ident::Elements(#elements_enum::CurrentNewIssuanceContract)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::CurrentReissuanceEntropy)}, + 1 => {#enum_ident::Elements(#elements_enum::CurrentIssuanceAssetAmount)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::CurrentIssuanceTokenAmount)}, + 1 => {#enum_ident::Elements(#elements_enum::CurrentIssuanceAssetProof)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::CurrentIssuanceTokenProof)}, + 1 => {#enum_ident::Elements(#elements_enum::InputPegin)} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::InputPrevOutpoint)}, + 1 => {#enum_ident::Elements(#elements_enum::InputAsset)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::InputAmount)}, + 1 => {#enum_ident::Elements(#elements_enum::InputScriptHash)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::InputSequence)}, + 1 => {#enum_ident::Elements(#elements_enum::InputAnnexHash)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::InputScriptSigHash)}, + 1 => {#enum_ident::Elements(#elements_enum::ReissuanceBlinding)} + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::NewIssuanceContract)}, + 1 => {#enum_ident::Elements(#elements_enum::ReissuanceEntropy)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::IssuanceAssetAmount)}, + 1 => {#enum_ident::Elements(#elements_enum::IssuanceTokenAmount)} + } + }, + 1 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::IssuanceAssetProof)}, + 1 => {#enum_ident::Elements(#elements_enum::IssuanceTokenProof)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::TapleafVersion)}, + 1 => {#enum_ident::Elements(#elements_enum::Tappath)} + } + } + } + }, + 1 => { + 0 => { + 0 => { + 0 => { + 0 => {#enum_ident::Elements(#elements_enum::Version)}, + 1 => {#enum_ident::Elements(#elements_enum::GenesisBlockHash)} + }, + 1 => { + 0 => {#enum_ident::Elements(#elements_enum::TransactionId)}, + 1 => {} + } + }, + 1 => {} + }, + 1 => {} + } + } + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + } + } + } + }, + 1 => {} + }, + 1 => {} + }, + 1 => {} + }, + 1 => { + #custom_branches + } + } + } + } + }) + } +} diff --git a/jet_plugins/src/helpers.rs b/jet_plugins/src/helpers.rs index 778f818..409cbd8 100644 --- a/jet_plugins/src/helpers.rs +++ b/jet_plugins/src/helpers.rs @@ -6,7 +6,8 @@ const JETIV: sha256::Midstate = sha256::Midstate([ 0x95, 0x32, 0xee, 0x28, 0xcd, 0xca, 0x69, 0xde, 0xc8, 0xa0, 0xa2, 0x18, 0xb7, 0x9b, 0xe3, 0x62, 0xf7, 0x40, 0xce, 0xaf, 0x64, 0x7f, 0x15, 0xb3, 0x8a, 0xed, 0x91, 0x68, 0x16, 0x3f, 0x92, 0x1b, ]); -pub const ENCODE_PREFIX: &[u8] = &[1, 1, 1, 1]; +/// Prefix is empty because we use entire hardcoded tree right now. Could be useful if tree was implemented as fallback. +pub const ENCODE_PREFIX: &[u8] = &[]; pub type JetCodeBits = u16; pub const JET_ENC_BITLEN: usize = 8 * std::mem::size_of::(); // `Jet::encode` uses `write_bits_be` which is bounded to u64 diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index 0160295..7f3f3e8 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -1,3 +1,4 @@ +#![recursion_limit = "256"] use quote::quote; use syn::{ Ident, LitByteStr, LitStr, Token, @@ -7,6 +8,7 @@ use syn::{ }; mod c_wrappers; +mod elements_decode_tree; mod elements_idents; mod helpers; use helpers::snake_to_pascal_case; @@ -17,6 +19,7 @@ use crate::{ decode_c_wrapper, display_fmt_c_wrapper, encode_c_wrapper, from_str_c_wrapper, hash_c_wrapper, partial_cmp_c_wrapper, src_trg_ty_c_wrapper, }, + elements_decode_tree::hardcoded_decode_tree, helpers::{JET_ENC_BITLEN, JetBranchCode, JetDecodeTree, pascal_to_snake_case}, }; @@ -208,7 +211,7 @@ pub fn register_jets(input: proc_macro::TokenStream) -> proc_macro::TokenStream let fmt_impl = fmt_trait_full(&names); let from_str_impl = from_str_trait_full(&names); - let c_ffi = impl_c_ffi(&names); + let c_ffi = impl_c_ffi(); quote! { #self_impl @@ -220,7 +223,7 @@ pub fn register_jets(input: proc_macro::TokenStream) -> proc_macro::TokenStream .into() } -fn impl_c_ffi(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { +fn impl_c_ffi() -> proc_macro2::TokenStream { let cmp = cmp_c_wrapper(); let partial_cmp = partial_cmp_c_wrapper(); let hash = hash_c_wrapper(); @@ -307,7 +310,8 @@ fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2 let jet_codes = build_jet_codes(names); let jet_encode = impl_jet_encode(names, &jet_codes); - let jet_decode = impl_jet_decode(jet_codes); + let jet_decode_bool_iter = impl_jet_decode_bool_iter(jet_codes); + let jet_decode = impl_jet_decode(); let c_jet_ptr = impl_c_jet_ptr(names, jets); let c_jet_ptr_boxed = impl_c_jet_ptr_boxed(names, jets); @@ -336,6 +340,9 @@ fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2 } }; } + impl #enum_ident { + #jet_decode_bool_iter + } impl #jet_trait_path for #enum_ident { #associated_types #c_jet_env @@ -410,55 +417,27 @@ fn impl_c_jet_ptr_boxed( } } -fn impl_jet_decode(codes: Vec) -> proc_macro2::TokenStream { - let custom_decode_tree: proc_macro2::TokenStream = JetDecodeTree::from_branches(codes).into(); +fn impl_jet_decode() -> proc_macro2::TokenStream { let bit_iter = StaticTokenInfo::bit_iter_path(); - let element_enum = StaticTokenInfo::elements_enum_path(); let decode_err = StaticTokenInfo::decode_err(); quote! { - /// # Safety - /// - /// Due to the lack of a `Clone` bound on `I`, the underlying implementation uses - /// `ptr::read` to create bitwise copies of the iterator. This is unsafe and may cause - /// undefined behavior if `I` contains types that manage unique resources. - /// This works correctly for common slice-based iterators like `Copied>`. - /// - /// See for details. fn decode>(bits: &mut #bit_iter) -> Result { - let (mut elements_iter, mut custom_iter) = - unsafe { (std::ptr::read(bits), std::ptr::read(bits)) }; - - let bits_read = bits.n_total_read(); - - let try_elements = #element_enum::decode(&mut elements_iter); - - if let Ok(jet) = try_elements { - for _ in 0..(elements_iter.n_total_read() - bits_read) { - bits.next(); - } - - std::mem::forget(elements_iter); - std::mem::forget(custom_iter); - - return Ok(Self::Elements(jet)); - } - - let custom_iter_ref = &mut custom_iter; - let try_custom = decode_bits!(custom_iter_ref, { - #custom_decode_tree - }); + let dyn_iter: &mut dyn Iterator = bits; + Self::decode_bool_iter(dyn_iter) + } - if try_custom.is_ok() { - for _ in 0..(custom_iter.n_total_read() - bits_read) { - bits.next(); - } - } + } +} - std::mem::forget(elements_iter); - std::mem::forget(custom_iter); +fn impl_jet_decode_bool_iter(codes: Vec) -> proc_macro2::TokenStream { + let custom_decode_tree: proc_macro2::TokenStream = JetDecodeTree::from_branches(codes).into(); + let decode_err = StaticTokenInfo::decode_err(); + let entire_tree = hardcoded_decode_tree(custom_decode_tree); - try_custom + quote! { + pub fn decode_bool_iter<>(bits: &mut dyn Iterator) -> Result { + #entire_tree } } @@ -468,8 +447,10 @@ fn impl_jet_encode( variants: &[proc_macro2::Ident], codes: &[JetBranchCode], ) -> proc_macro2::TokenStream { - let code_len = JET_ENC_BITLEN; - let codes_bits = codes.iter().map(|code| code.bits); + let code_len = JET_ENC_BITLEN + 4; // prefix bits are hardcoded + let codes_bits = codes + .iter() + .map(|code| (code.bits as u64) | (15 << JET_ENC_BITLEN)); let bit_writer = StaticTokenInfo::bit_writer_path(); quote! { @@ -815,7 +796,7 @@ mod test { let parsed: JetsInput = parse2(input).expect("Failed to parse JetsInput"); let variants_names = build_custom_fields(&parsed); let codes = build_jet_codes(&variants_names); - let decode_impl = impl_jet_decode(codes); + let decode_impl = impl_jet_decode(); println!("{}", decode_impl.to_string()); } diff --git a/service/src/custom_jet.rs b/service/src/custom_jet.rs index cb0badd..4aaf80f 100644 --- a/service/src/custom_jet.rs +++ b/service/src/custom_jet.rs @@ -113,6 +113,8 @@ mod test { str::FromStr, }; + use hal_simplicity::simplicity::{BitIter, BitWriter}; + use super::*; #[test] @@ -132,4 +134,79 @@ mod test { let _ = hasher.finish(); } + + #[test] + fn test_dll_encode() { + let custom_jet = CustomJet::from_str("custom_jet_1").expect("Failed to load jet"); + let elements_jet = CustomJet::from_str("input_asset").expect("Failed to load jet"); + + let mut buffer = Vec::new(); + let mut w = BitWriter::new(&mut buffer); + + let res1 = custom_jet.encode(&mut w); + let res2 = elements_jet.encode(&mut w); + + assert!(res1.is_ok()); + assert!(res2.is_ok()); + + assert_eq!(res1.unwrap(), 20); + assert_eq!(res2.unwrap(), 19); + + w.flush_all().unwrap(); + + let mut bit_iter = BitIter::from(buffer); + + let mut read_code = |read_bits: u8| -> Option { + let mut res = 0; + let mut cursor = 1 << (read_bits - 1); + + for _ in 0..read_bits { + match bit_iter.next() { + None => return None, + Some(true) => res |= cursor, + Some(false) => {} + } + cursor >>= 1; + } + Some(res) + }; + + let custom_jet_code = read_code(20); + let elements_jet_code = read_code(19); + + assert!(custom_jet_code.is_some()); + assert!(elements_jet_code.is_some()); + + assert_eq!(custom_jet_code.unwrap(), 1047454); + assert_eq!(elements_jet_code.unwrap(), 462369); + } + + #[test] + fn test_dll_decode() { + let custom_jet = CustomJet::from_str("custom_jet_1").expect("Failed to load jet"); + let elements_jet = CustomJet::from_str("input_asset").expect("Failed to load jet"); + + let mut buffer = Vec::new(); + let mut w = BitWriter::new(&mut buffer); + + let res1 = custom_jet.encode(&mut w); + let res2 = elements_jet.encode(&mut w); + + assert!(res1.is_ok()); + assert!(res2.is_ok()); + + w.flush_all().unwrap(); + + let mut bit_iter = BitIter::from(buffer); + + let custom_decoded = CustomJet::decode(&mut bit_iter); + assert!(custom_decoded.is_ok()); + + assert_eq!(custom_decoded.unwrap().to_string(), "custom_jet_1"); + + let elements_decoded = CustomJet::decode(&mut bit_iter); + assert!(elements_decoded.is_ok()); + + assert_eq!(elements_decoded.unwrap().to_string(), "input_asset"); + } } From 2b74261cbbf886f6d13a3077b29cd4ffd53269f2 Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Sun, 1 Mar 2026 16:49:28 +0200 Subject: [PATCH 09/14] fix c_jet_ptr dll wrapper --- Cargo.lock | 6 +++ Cargo.toml | 2 +- core/src/jets/jet_dyn.rs | 20 +++++++++- jet_plugins/src/c_wrappers.rs | 19 +++++++++ jet_plugins/src/lib.rs | 23 +++++++++-- service/src/custom_jet.rs | 75 ++++++++++++++++++++++++++--------- 6 files changed, 121 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c869747..0b80348 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -723,6 +723,8 @@ dependencies = [ [[package]] name = "hal-simplicity" version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a540615bef9124e283bbd087e007af922c9f6cfac210412bda4d1c34b2a74d" dependencies = [ "clap 2.33.3", "elements", @@ -1773,6 +1775,8 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "simplicity-lang" version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e57bd4d84853974a212eab24ed89da54f49fbccf5e33e93bcd29f0a6591cd5" dependencies = [ "bitcoin", "bitcoin_hashes", @@ -1790,6 +1794,8 @@ dependencies = [ [[package]] name = "simplicity-sys" version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bcb4e5bfc15080d67e0ce2c17d1c31bfb7521d65c86ea26ed0de72d5119d119" dependencies = [ "bitcoin_hashes", "cc", diff --git a/Cargo.toml b/Cargo.toml index 841d062..de5a73b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["cli", "core", "jet_plugins", "plugin_test", "service"] [workspace.dependencies] -hal-simplicity = { path = "../hal-unchained" } +hal-simplicity = "0.2.0" clap = { version = "4.5.53", features = ["derive"] } hex = "0.4" serde = { version = "1.0", features = ["derive"] } diff --git a/core/src/jets/jet_dyn.rs b/core/src/jets/jet_dyn.rs index b0c6108..f5cfb9a 100644 --- a/core/src/jets/jet_dyn.rs +++ b/core/src/jets/jet_dyn.rs @@ -106,6 +106,12 @@ pub struct BitWriterHandle<'a> { pub _writer: BitWriter<&'a mut Vec>, } +#[repr(C)] +pub struct AllJetsHandle { + pub jets: *const CCustomJet, + pub len: usize, +} + #[derive(WrapperApi)] pub struct CustomJetApi { _cmp: extern "C" fn(_self: CCustomJet, other: CCustomJet) -> COrdering, @@ -114,6 +120,7 @@ pub struct CustomJetApi { _debug_fmt: extern "C" fn(_self: CCustomJet, fmt_handle: *mut FmtHandle) -> *const (), // *const std::fmt::Result _display_fmt: extern "C" fn(_self: CCustomJet, fmt_handle: *mut FmtHandle) -> *const (), // *const std::fmt::Result _from_str: extern "C" fn(name: *const StrHandle) -> *const (), // *const Result + _all_jets: extern "C" fn() -> AllJetsHandle, _cmr: extern "C" fn(_self: CCustomJet) -> *const CmrHandle, _source_ty: extern "C" fn(_self: CCustomJet) -> *const TypeNameHandle, _target_ty: extern "C" fn(_self: CCustomJet) -> *const TypeNameHandle, @@ -132,6 +139,13 @@ impl CustomJetApi { } } + pub fn all_jets(&self) -> Box<[CCustomJet]> { + unsafe { + let AllJetsHandle { jets, len } = (self._all_jets)(); + Box::from(std::slice::from_raw_parts(jets, len)) + } + } + pub fn cmp(&self, lhs: CCustomJet, rhs: CCustomJet) -> std::cmp::Ordering { (self._cmp)(lhs, rhs).into() } @@ -225,11 +239,13 @@ impl CustomJetApi { pub fn c_jet_ptr( &self, jet: CCustomJet, - ) -> Box bool> { + ) -> Box bool + Send + Sync> { unsafe { *Box::from_raw((self._c_jet_ptr)(jet) as *mut Box< - dyn Fn(&mut CFrameItem, CFrameItem, &ElementsUnchainedEnv) -> bool, + dyn Fn(&mut CFrameItem, CFrameItem, &ElementsUnchainedEnv) -> bool + + Send + + Sync, >) } } diff --git a/jet_plugins/src/c_wrappers.rs b/jet_plugins/src/c_wrappers.rs index e2681a9..2303e42 100644 --- a/jet_plugins/src/c_wrappers.rs +++ b/jet_plugins/src/c_wrappers.rs @@ -129,6 +129,25 @@ pub(crate) fn cmr_c_wrapper() -> proc_macro2::TokenStream { } } +pub(crate) fn all_jets() -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let all_jets_handle = StaticTokenInfo::all_jets_handle(); + let jet_self_handle = StaticTokenInfo::jet_self_handle(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _all_jets() -> #all_jets_handle { + let all_boxed: Box<[#jet_self_handle]> = #enum_ident::ALL.iter().map(|jet| jet.into()).collect::>().into_boxed_slice(); + let len = all_boxed.len(); + + #all_jets_handle { + jets: Box::into_raw(all_boxed) as *const #jet_self_handle, + len + } + } + } +} + pub(crate) fn from_str_c_wrapper() -> proc_macro2::TokenStream { let str_handle = StaticTokenInfo::str_handle(); let enum_ident = StaticTokenInfo::enum_ident(); diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index 7f3f3e8..dedc008 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -15,9 +15,9 @@ use helpers::snake_to_pascal_case; use crate::{ c_wrappers::{ - c_jet_ptr_wrapper, cmp_c_wrapper, cmr_c_wrapper, cost_c_wrapper, debug_fmt_c_wrapper, - decode_c_wrapper, display_fmt_c_wrapper, encode_c_wrapper, from_str_c_wrapper, - hash_c_wrapper, partial_cmp_c_wrapper, src_trg_ty_c_wrapper, + all_jets, c_jet_ptr_wrapper, cmp_c_wrapper, cmr_c_wrapper, cost_c_wrapper, + debug_fmt_c_wrapper, decode_c_wrapper, display_fmt_c_wrapper, encode_c_wrapper, + from_str_c_wrapper, hash_c_wrapper, partial_cmp_c_wrapper, src_trg_ty_c_wrapper, }, elements_decode_tree::hardcoded_decode_tree, helpers::{JET_ENC_BITLEN, JetBranchCode, JetDecodeTree, pascal_to_snake_case}, @@ -58,6 +58,7 @@ const HASHER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::HasherHan const FMT_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::FmtHandle"; const STR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::StrHandle"; const COST_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CostHandle"; +const ALL_JETS_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::AllJetsHandle"; const CUSTOM_JET_COST: u32 = 1000; @@ -170,6 +171,10 @@ impl StaticTokenInfo { fn cost_handle() -> syn::Path { syn::parse_str(COST_HANDLE).expect("Failed to find CostHandle by given path") } + + fn all_jets_handle() -> syn::Path { + syn::parse_str(ALL_JETS_HANDLE).expect("Failed to find AllJetsHandle by given path") + } } /// Implements `Jet` trait and C FFI compatible with `simplicity_unchained_core::jets::jet_dyn` interface. @@ -229,6 +234,7 @@ fn impl_c_ffi() -> proc_macro2::TokenStream { let hash = hash_c_wrapper(); let debug_fmt = debug_fmt_c_wrapper(); let display_fmt = display_fmt_c_wrapper(); + let all_jets = all_jets(); let cmr_c_wrapper = cmr_c_wrapper(); let src_ty_c_wrapper = src_trg_ty_c_wrapper("source"); @@ -245,6 +251,7 @@ fn impl_c_ffi() -> proc_macro2::TokenStream { #hash #debug_fmt #display_fmt + #all_jets #cmr_c_wrapper #src_ty_c_wrapper #trg_ty_c_wrapper @@ -551,6 +558,8 @@ fn impl_all_constant(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStrea let enum_ident = StaticTokenInfo::enum_ident(); let elements_enum_by_path = StaticTokenInfo::elements_enum_path(); let custom_jets_num = variants.len(); + let jet_self_handle = StaticTokenInfo::jet_self_handle(); + quote! { impl #enum_ident { @@ -615,6 +624,14 @@ fn impl_all_constant(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStrea Self::ALL[idx] } } + + impl Into<#jet_self_handle> for &#enum_ident { + fn into(self) -> #jet_self_handle { + #jet_self_handle { + index: self.variant_to_index() + } + } + } } } diff --git a/service/src/custom_jet.rs b/service/src/custom_jet.rs index 4aaf80f..a331773 100644 --- a/service/src/custom_jet.rs +++ b/service/src/custom_jet.rs @@ -2,9 +2,9 @@ use dlopen2::wrapper::Container; use hal_simplicity::simplicity::jet::Jet; use simplicity_unchained_core::jets::{ environments::ElementsUnchainedEnv, - jet_dyn::{CCustomJet, CustomJetApi, JetSelfHandle}, + jet_dyn::{CCustomJet, CustomJetApi}, }; -use std::sync::LazyLock; +use std::{collections::HashMap, sync::LazyLock}; // TODO: make env var static JET_DLL: LazyLock> = LazyLock::new(|| { @@ -12,17 +12,45 @@ static JET_DLL: LazyLock> = LazyLock::new(|| { .expect("Failed to load lib") }); -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct CustomJet { - _jet: CCustomJet, +fn all_jets() -> Box<[CustomJet]> { + JET_DLL + .all_jets() + .iter() + .map(|&jet| CustomJet(jet)) + .collect() } +static ALL_JETS: LazyLock> = LazyLock::new(|| all_jets()); + +static C_JET_PTRS: LazyLock< + HashMap< + CustomJet, + &'static ( + dyn Fn( + &mut hal_simplicity::simplicity::ffi::CFrameItem, + hal_simplicity::simplicity::ffi::CFrameItem, + &ElementsUnchainedEnv, + ) -> bool + + Send + + Sync + ), + >, +> = LazyLock::new(|| { + ALL_JETS + .iter() + .map(|&jet| (jet, &*Box::leak(JET_DLL.c_jet_ptr(jet.0)))) + .collect::>() +}); + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct CustomJet(CCustomJet); + impl std::str::FromStr for CustomJet { type Err = hal_simplicity::simplicity::Error; fn from_str(s: &str) -> Result { match JET_DLL.from_str(s) { - Ok(_jet) => Ok(Self { _jet }), + Ok(_jet) => Ok(Self(_jet)), Err(err) => Err(err), } } @@ -30,31 +58,31 @@ impl std::str::FromStr for CustomJet { impl PartialOrd for CustomJet { fn partial_cmp(&self, other: &Self) -> Option { - JET_DLL.partial_cmp(self._jet, other._jet) + JET_DLL.partial_cmp(self.0, other.0) } } impl Ord for CustomJet { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - JET_DLL.cmp(self._jet, other._jet) + JET_DLL.cmp(self.0, other.0) } } impl core::hash::Hash for CustomJet { fn hash(&self, state: &mut H) { - JET_DLL.hash(self._jet, state); + JET_DLL.hash(self.0, state); } } impl std::fmt::Debug for CustomJet { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - JET_DLL.debug_fmt(self._jet, f) + JET_DLL.debug_fmt(self.0, f) } } impl std::fmt::Display for CustomJet { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - JET_DLL.display_fmt(self._jet, f) + JET_DLL.display_fmt(self.0, f) } } @@ -67,28 +95,28 @@ impl Jet for CustomJet { } fn cmr(&self) -> hal_simplicity::simplicity::Cmr { - JET_DLL.cmr(self._jet) + JET_DLL.cmr(self.0) } fn source_ty(&self) -> hal_simplicity::simplicity::jet::type_name::TypeName { - JET_DLL.source_ty(self._jet) + JET_DLL.source_ty(self.0) } fn target_ty(&self) -> hal_simplicity::simplicity::jet::type_name::TypeName { - JET_DLL.target_ty(self._jet) + JET_DLL.target_ty(self.0) } fn encode( &self, w: &mut hal_simplicity::simplicity::BitWriter, ) -> std::io::Result { - JET_DLL.encode(self._jet, w) + JET_DLL.encode(self.0, w) } fn decode>( bits: &mut hal_simplicity::simplicity::BitIter, ) -> Result { - JET_DLL.decode(bits).map(|_jet| Self { _jet }) + JET_DLL.decode(bits).map(|_jet| Self(_jet)) } fn c_jet_ptr( @@ -98,11 +126,13 @@ impl Jet for CustomJet { hal_simplicity::simplicity::ffi::CFrameItem, &Self::CJetEnvironment, ) -> bool { - todo!() + C_JET_PTRS + .get(self) + .expect("All jets must be initialized on startup") } fn cost(&self) -> hal_simplicity::simplicity::Cost { - JET_DLL.cost(self._jet) + JET_DLL.cost(self.0) } } @@ -209,4 +239,13 @@ mod test { assert_eq!(elements_decoded.unwrap().to_string(), "input_asset"); } + + #[test] + fn test_dll_c_jet_ptr() { + let custom_jet = CustomJet::from_str("custom_jet_1").expect("Failed to load jet"); + let elements_jet = CustomJet::from_str("input_asset").expect("Failed to load jet"); + + let _ = custom_jet.c_jet_ptr(); + let _ = elements_jet.c_jet_ptr(); + } } From 4531a274bca81ec84866d523ace01bc712618ea1 Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Mon, 2 Mar 2026 13:52:38 +0200 Subject: [PATCH 10/14] dll env var and small refactor --- core/src/jets/jet_dyn.rs | 7 +- jet_plugins/src/lib.rs | 82 ----------------------- service/src/custom_jet.rs | 132 ++++++++++++++++++++++++++----------- service/src/jet_backend.rs | 120 +++++++++++++++++++++++++++++++++ service/src/main.rs | 1 + 5 files changed, 218 insertions(+), 124 deletions(-) create mode 100644 service/src/jet_backend.rs diff --git a/core/src/jets/jet_dyn.rs b/core/src/jets/jet_dyn.rs index f5cfb9a..e1ec8fe 100644 --- a/core/src/jets/jet_dyn.rs +++ b/core/src/jets/jet_dyn.rs @@ -227,15 +227,14 @@ impl CustomJetApi { let decoded = (self._decode)(&mut handle); - let res = unsafe { + unsafe { *Box::from_raw( decoded as *mut Result, ) - }; - - res + } } + #[allow(clippy::type_complexity)] pub fn c_jet_ptr( &self, jet: CCustomJet, diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index dedc008..607c61c 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -51,7 +51,6 @@ const CMR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CmrHandle"; const TYPENAME_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::TypeNameHandle"; const BIT_WRITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitWriterHandle"; const BIT_ITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitIterHandle"; -const DECODE_FFI_RES: &str = "simplicity_unchained_core::jets::jet_dyn::DecodeFFIRes"; const C_ORDERING: &str = "simplicity_unchained_core::jets::jet_dyn::COrdering"; const C_OPTION_ORDERING: &str = "simplicity_unchained_core::jets::jet_dyn::COptionOrdering"; const HASHER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::HasherHandle"; @@ -144,10 +143,6 @@ impl StaticTokenInfo { syn::parse_str(BIT_ITER_HANDLE).expect("Failed to find BitIterHandle by given path") } - fn decode_ffi_res() -> syn::Path { - syn::parse_str(DECODE_FFI_RES).expect("Failed to find DecodeFFIRes by given path") - } - fn c_ordering() -> syn::Path { syn::parse_str(C_ORDERING).expect("Failed to find COrdering by given path") } @@ -741,80 +736,3 @@ impl Parse for JetDef { }) } } - -#[cfg(test)] -mod test { - use super::*; - #[test] - fn test_extenstion_def_builder() { - let input = quote! { - "custom_jet1" => custom_jet1, b"h",b"h"; - "custom_jet2" => custom_jet2, b"h",b"h"; - }; - - let parsed: JetsInput = parse2(input).expect("Failed to parse JetsInput"); - let variants_names = build_custom_fields(&parsed); - let expanded = impl_enum_definition(&variants_names); - - println!("{}", expanded.to_string()); - } - - #[test] - fn test_cmr_impl() { - let input = quote! { - "custom_jet1" => custom_jet1, b"h",b"h"; - "custom_jet2" => custom_jet2, b"h",b"h"; - }; - - let parsed: JetsInput = parse2(input).expect("Failed to parse JetsInput"); - let variants_names = build_custom_fields(&parsed); - let cmrs = impl_cmr(&variants_names); - - println!("{}", cmrs.to_string()); - } - - #[test] - fn test_source_impl() { - let input = quote! { - "custom_jet1" => custom_jet1, b"h",b"h"; - "custom_jet2" => custom_jet2, b"h",b"h"; - }; - - let parsed: JetsInput = parse2(input).expect("Failed to parse JetsInput"); - let variants_names = build_custom_fields(&parsed); - let source_tys = build_source_tys(&parsed); - let tys = impl_src_trg_ty(&variants_names, &source_tys, "source"); - - println!("{}", tys.to_string()); - } - - #[test] - fn test_encode_impl() { - let input = quote! { - "custom_jet1" => custom_jet1, b"h",b"h"; - "custom_jet2" => custom_jet2, b"h",b"h"; - }; - - let parsed: JetsInput = parse2(input).expect("Failed to parse JetsInput"); - let variants_names = build_custom_fields(&parsed); - let codes = build_jet_codes(&variants_names); - let encode_impl = impl_jet_encode(&variants_names, &codes); - - println!("{}", encode_impl.to_string()); - } - - #[test] - fn test_decode_impl() { - let input = quote! { - "custom_jet1" => custom_jet1, b"h",b"h"; - "custom_jet2" => custom_jet2, b"h",b"h"; - }; - - let parsed: JetsInput = parse2(input).expect("Failed to parse JetsInput"); - let variants_names = build_custom_fields(&parsed); - let codes = build_jet_codes(&variants_names); - let decode_impl = impl_jet_decode(); - - println!("{}", decode_impl.to_string()); - } -} diff --git a/service/src/custom_jet.rs b/service/src/custom_jet.rs index a331773..3ba85f8 100644 --- a/service/src/custom_jet.rs +++ b/service/src/custom_jet.rs @@ -6,50 +6,61 @@ use simplicity_unchained_core::jets::{ }; use std::{collections::HashMap, sync::LazyLock}; -// TODO: make env var -static JET_DLL: LazyLock> = LazyLock::new(|| { - unsafe { Container::::load("../target/debug/libplugin_test.so") } - .expect("Failed to load lib") +pub(crate) static JET_DLL: LazyLock>> = LazyLock::new(|| { + std::env::var("JET_DLL_PATH") + .ok() + .and_then(|path| unsafe { Container::::load(&path) }.ok()) }); -fn all_jets() -> Box<[CustomJet]> { +fn all_jets() -> Option> { JET_DLL - .all_jets() - .iter() - .map(|&jet| CustomJet(jet)) - .collect() + .as_ref() + .map(|dll| dll.all_jets().iter().map(|&jet| CustomJet(jet)).collect()) } -static ALL_JETS: LazyLock> = LazyLock::new(|| all_jets()); +static ALL_JETS: LazyLock>> = LazyLock::new(|| all_jets()); static C_JET_PTRS: LazyLock< - HashMap< - CustomJet, - &'static ( - dyn Fn( - &mut hal_simplicity::simplicity::ffi::CFrameItem, - hal_simplicity::simplicity::ffi::CFrameItem, - &ElementsUnchainedEnv, - ) -> bool - + Send - + Sync - ), + Option< + HashMap< + CustomJet, + &'static ( + dyn Fn( + &mut hal_simplicity::simplicity::ffi::CFrameItem, + hal_simplicity::simplicity::ffi::CFrameItem, + &ElementsUnchainedEnv, + ) -> bool + + Send + + Sync + ), + >, >, > = LazyLock::new(|| { - ALL_JETS - .iter() - .map(|&jet| (jet, &*Box::leak(JET_DLL.c_jet_ptr(jet.0)))) - .collect::>() + ALL_JETS.as_ref().map(|jets| { + jets.iter() + .map(|&jet| { + ( + jet, + &*Box::leak( + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .c_jet_ptr(jet.0), + ), + ) + }) + .collect::>() + }) }); #[derive(Clone, Copy, PartialEq, Eq)] -pub struct CustomJet(CCustomJet); +pub(crate) struct CustomJet(CCustomJet); impl std::str::FromStr for CustomJet { type Err = hal_simplicity::simplicity::Error; fn from_str(s: &str) -> Result { - match JET_DLL.from_str(s) { + match JET_DLL.as_ref().expect("DLL is not loaded").from_str(s) { Ok(_jet) => Ok(Self(_jet)), Err(err) => Err(err), } @@ -58,31 +69,46 @@ impl std::str::FromStr for CustomJet { impl PartialOrd for CustomJet { fn partial_cmp(&self, other: &Self) -> Option { - JET_DLL.partial_cmp(self.0, other.0) + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .partial_cmp(self.0, other.0) } } impl Ord for CustomJet { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - JET_DLL.cmp(self.0, other.0) + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .cmp(self.0, other.0) } } impl core::hash::Hash for CustomJet { fn hash(&self, state: &mut H) { - JET_DLL.hash(self.0, state); + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .hash(self.0, state); } } impl std::fmt::Debug for CustomJet { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - JET_DLL.debug_fmt(self.0, f) + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .debug_fmt(self.0, f) } } impl std::fmt::Display for CustomJet { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - JET_DLL.display_fmt(self.0, f) + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .display_fmt(self.0, f) } } @@ -95,28 +121,41 @@ impl Jet for CustomJet { } fn cmr(&self) -> hal_simplicity::simplicity::Cmr { - JET_DLL.cmr(self.0) + JET_DLL.as_ref().expect("DLL is not loaded").cmr(self.0) } fn source_ty(&self) -> hal_simplicity::simplicity::jet::type_name::TypeName { - JET_DLL.source_ty(self.0) + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .source_ty(self.0) } fn target_ty(&self) -> hal_simplicity::simplicity::jet::type_name::TypeName { - JET_DLL.target_ty(self.0) + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .target_ty(self.0) } fn encode( &self, w: &mut hal_simplicity::simplicity::BitWriter, ) -> std::io::Result { - JET_DLL.encode(self.0, w) + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .encode(self.0, w) } fn decode>( bits: &mut hal_simplicity::simplicity::BitIter, ) -> Result { - JET_DLL.decode(bits).map(|_jet| Self(_jet)) + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .decode(bits) + .map(|_jet| Self(_jet)) } fn c_jet_ptr( @@ -127,12 +166,14 @@ impl Jet for CustomJet { &Self::CJetEnvironment, ) -> bool { C_JET_PTRS + .as_ref() + .expect("DLL is not loaded") .get(self) .expect("All jets must be initialized on startup") } fn cost(&self) -> hal_simplicity::simplicity::Cost { - JET_DLL.cost(self.0) + JET_DLL.as_ref().expect("DLL is not loaded").cost(self.0) } } @@ -149,6 +190,9 @@ mod test { #[test] fn test_dll_from_str() { + unsafe { + std::env::set_var("JET_DLL_PATH", "../target/debug/libplugin_test.so"); + } let jet = CustomJet::from_str("custom_jet_1"); assert!(jet.is_ok()); @@ -157,6 +201,9 @@ mod test { #[test] fn test_dll_hash() { + unsafe { + std::env::set_var("JET_DLL_PATH", "../target/debug/libplugin_test.so"); + } let jet = CustomJet::from_str("custom_jet_1").expect("Failed to load jet"); let mut hasher = std::collections::hash_map::DefaultHasher::new(); @@ -167,6 +214,9 @@ mod test { #[test] fn test_dll_encode() { + unsafe { + std::env::set_var("JET_DLL_PATH", "../target/debug/libplugin_test.so"); + } let custom_jet = CustomJet::from_str("custom_jet_1").expect("Failed to load jet"); let elements_jet = CustomJet::from_str("input_asset").expect("Failed to load jet"); @@ -213,6 +263,9 @@ mod test { #[test] fn test_dll_decode() { + unsafe { + std::env::set_var("JET_DLL_PATH", "../target/debug/libplugin_test.so"); + } let custom_jet = CustomJet::from_str("custom_jet_1").expect("Failed to load jet"); let elements_jet = CustomJet::from_str("input_asset").expect("Failed to load jet"); @@ -242,6 +295,9 @@ mod test { #[test] fn test_dll_c_jet_ptr() { + unsafe { + std::env::set_var("JET_DLL_PATH", "../target/debug/libplugin_test.so"); + } let custom_jet = CustomJet::from_str("custom_jet_1").expect("Failed to load jet"); let elements_jet = CustomJet::from_str("input_asset").expect("Failed to load jet"); diff --git a/service/src/jet_backend.rs b/service/src/jet_backend.rs new file mode 100644 index 0000000..2705f42 --- /dev/null +++ b/service/src/jet_backend.rs @@ -0,0 +1,120 @@ +use std::{collections::HashMap, sync::LazyLock}; + +use crate::custom_jet::{CustomJet, JET_DLL}; +use hal_simplicity::simplicity::{ + BitIter, BitWriter, Cmr, + ffi::CFrameItem, + jet::{Elements, Jet, type_name::TypeName}, +}; +use simplicity_unchained_core::jets::environments::ElementsUnchainedEnv; + +static ELEMENTS_JET_PTRS: LazyLock< + HashMap< + Elements, + &'static (dyn Fn(&mut CFrameItem, CFrameItem, &ElementsUnchainedEnv) -> bool + Send + Sync), + >, +> = LazyLock::new(|| { + Elements::ALL + .iter() + .map(|&jet| { + let closure = + move |dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv| { + jet.c_jet_ptr()(dst, src, env.env.c_tx_env()) + }; + let leaked: &'static ( + dyn Fn(&mut CFrameItem, CFrameItem, &ElementsUnchainedEnv) -> bool + + Send + + Sync + ) = Box::leak(Box::new(closure)); + (jet, leaked) + }) + .collect() +}); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum JetBackend { + Elements(Elements), + Custom(CustomJet), +} + +impl Jet for JetBackend { + type CJetEnvironment = ElementsUnchainedEnv; + type Environment = ElementsUnchainedEnv; + + fn cmr(&self) -> Cmr { + match self { + Self::Elements(jet) => jet.cmr(), + Self::Custom(jet) => jet.cmr(), + } + } + fn source_ty(&self) -> TypeName { + match self { + Self::Elements(jet) => jet.source_ty(), + Self::Custom(jet) => jet.source_ty(), + } + } + + fn target_ty(&self) -> TypeName { + match self { + Self::Elements(jet) => jet.target_ty(), + Self::Custom(jet) => jet.target_ty(), + } + } + + fn encode(&self, w: &mut BitWriter) -> std::io::Result { + match self { + Self::Elements(jet) => jet.encode(w), + Self::Custom(jet) => jet.encode(w), + } + } + + fn decode>( + bits: &mut BitIter, + ) -> Result { + if JET_DLL.is_none() { + return Elements::decode(bits).map(|jet| Self::Elements(jet)); + } + CustomJet::decode(bits).map(|jet| Self::Custom(jet)) + } + + fn c_jet_env(env: &Self::Environment) -> &Self::CJetEnvironment { + env + } + + fn c_jet_ptr(&self) -> &dyn Fn(&mut CFrameItem, CFrameItem, &Self::CJetEnvironment) -> bool { + match self { + Self::Elements(jet) => ELEMENTS_JET_PTRS + .get(jet) + .expect("All Elements jets must be initialized"), + Self::Custom(jet) => jet.c_jet_ptr(), + } + } + + fn cost(&self) -> hal_simplicity::simplicity::Cost { + match self { + Self::Elements(jet) => jet.cost(), + Self::Custom(jet) => jet.cost(), + } + } +} + +impl std::str::FromStr for JetBackend { + type Err = hal_simplicity::simplicity::Error; + + fn from_str(s: &str) -> Result { + if JET_DLL.is_none() { + return Elements::from_str(s).map(|jet| Self::Elements(jet)); + } + + CustomJet::from_str(s).map(|jet| Self::Custom(jet)) + } +} + +impl std::fmt::Display for JetBackend { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Elements(jet) => jet.fmt(f), + Self::Custom(jet) => jet.fmt(f), + } + } +} diff --git a/service/src/main.rs b/service/src/main.rs index 6bac61f..39d4223 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -2,6 +2,7 @@ mod cli; mod config; mod custom_jet; mod handlers; +mod jet_backend; mod validation; use cli::{Cli, Commands}; From 6a9fed15184b26dcf7d0a1bb3138eb987ba8227d Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Wed, 4 Mar 2026 11:32:29 +0200 Subject: [PATCH 11/14] return decode via unsafe ptr read --- core/src/jets/elements.rs | 1 - core/src/jets/jet_dyn.rs | 46 ++++++++++++++++++---- jet_plugins/src/c_wrappers.rs | 20 +++++++--- jet_plugins/src/helpers.rs | 19 +++++---- jet_plugins/src/lib.rs | 74 +++++++++++++++++++++++++++++++---- service/src/custom_jet.rs | 14 ++++++- service/src/jet_backend.rs | 9 ++++- 7 files changed, 148 insertions(+), 35 deletions(-) diff --git a/core/src/jets/elements.rs b/core/src/jets/elements.rs index 22e2d9f..8affab5 100644 --- a/core/src/jets/elements.rs +++ b/core/src/jets/elements.rs @@ -44,7 +44,6 @@ impl ElementsExtension { const ALL_JETS_NUM: usize = Elements::ALL.len() + 2; const fn build_all_variants() -> [Self; Self::ALL_JETS_NUM] { - // Maybe worth adding Uninit field to enum or use one of available enum variants to avoid unsafe code struct AllVariantsBuilder { data: [MaybeUninit; ElementsExtension::ALL_JETS_NUM], len: usize, diff --git a/core/src/jets/jet_dyn.rs b/core/src/jets/jet_dyn.rs index e1ec8fe..b1be496 100644 --- a/core/src/jets/jet_dyn.rs +++ b/core/src/jets/jet_dyn.rs @@ -98,7 +98,13 @@ impl From for Option { #[repr(C)] pub struct BitIterHandle<'a> { - pub _iter: &'a mut dyn Iterator, + pub data: &'a [u8], +} + +#[repr(C)] +pub struct DecodeResHandle { + pub jet: *const (), // *const Result + pub bits_read: usize, } #[repr(C)] @@ -125,7 +131,7 @@ pub struct CustomJetApi { _source_ty: extern "C" fn(_self: CCustomJet) -> *const TypeNameHandle, _target_ty: extern "C" fn(_self: CCustomJet) -> *const TypeNameHandle, _encode: extern "C" fn(_self: CCustomJet, w: *mut BitWriterHandle) -> *const (), // *std::io::Result - _decode: extern "C" fn(w: *mut BitIterHandle) -> *const (), // (*const Result, usize) + _decode: extern "C" fn(w: *mut BitIterHandle) -> DecodeResHandle, _c_jet_ptr: extern "C" fn(_self: CCustomJet) -> *const (), // * Box bool> _cost: extern "C" fn(_self: CCustomJet) -> *const CostHandle, } @@ -220,18 +226,42 @@ impl CustomJetApi { pub fn decode>( &self, bits: &mut BitIter, + max_jet_len: u32, ) -> Result { - let mut handle = BitIterHandle { - _iter: bits as &mut dyn Iterator, - }; + let mut bits_copy = unsafe { std::ptr::read(bits) }; + let mut buffer = Vec::with_capacity(max_jet_len.div_ceil(8) as usize); + let mut writer = BitWriter::from(&mut buffer); + + let mut i = 0; + while let Some(bit) = bits_copy.next() + && i < max_jet_len + { + print!("{}", if bit { 1 } else { 0 }); + writer + .write_bit(bit) + .expect("Writing to vec should not fail"); + i += 1; + } - let decoded = (self._decode)(&mut handle); + writer.flush_all().expect("Writing to vec should not fail"); - unsafe { + let mut handle = BitIterHandle { data: &buffer }; + + let DecodeResHandle { jet, bits_read } = (self._decode)(&mut handle); + + let decoded = unsafe { *Box::from_raw( - decoded as *mut Result, + jet as *mut Result, ) + }; + + if decoded.is_ok() { + for _ in 0..bits_read { + bits.next(); + } } + + decoded } #[allow(clippy::type_complexity)] diff --git a/jet_plugins/src/c_wrappers.rs b/jet_plugins/src/c_wrappers.rs index 2303e42..c9438c9 100644 --- a/jet_plugins/src/c_wrappers.rs +++ b/jet_plugins/src/c_wrappers.rs @@ -38,13 +38,19 @@ pub(crate) fn decode_c_wrapper() -> proc_macro2::TokenStream { let bititer_handle = StaticTokenInfo::bititer_handle(); let jet_self = StaticTokenInfo::jet_self_handle(); let enum_ident = StaticTokenInfo::enum_ident(); + let jet_trait = StaticTokenInfo::jet_trait_path(); + let bit_iter = StaticTokenInfo::bit_iter_path(); + let decode_res_handle = StaticTokenInfo::decode_res_handle(); quote! { #[unsafe(no_mangle)] - pub extern "C" fn _decode(w: *mut #bititer_handle) -> *const () + pub extern "C" fn _decode(w: *mut #bititer_handle) -> #decode_res_handle { - let bit_iter: &mut dyn Iterator = unsafe { &mut *(*w)._iter }; - let decode_res = #enum_ident::decode_bool_iter(bit_iter); + + let data: &[u8] = unsafe { (*w).data }; + let mut bit_iter = #bit_iter::from(data); + + let decode_res = <#enum_ident as #jet_trait>::decode(&mut bit_iter); let res = Box::new(match decode_res { Ok(jet) => { @@ -55,9 +61,13 @@ pub(crate) fn decode_c_wrapper() -> proc_macro2::TokenStream { } Err(err) => Err(err) }); + let bits_read = bit_iter.n_total_read(); - // *Result - Box::into_raw(res) as *const () + #decode_res_handle { + // *Result + jet: Box::into_raw(res) as *const (), + bits_read + } } } diff --git a/jet_plugins/src/helpers.rs b/jet_plugins/src/helpers.rs index 409cbd8..756cc24 100644 --- a/jet_plugins/src/helpers.rs +++ b/jet_plugins/src/helpers.rs @@ -6,10 +6,9 @@ const JETIV: sha256::Midstate = sha256::Midstate([ 0x95, 0x32, 0xee, 0x28, 0xcd, 0xca, 0x69, 0xde, 0xc8, 0xa0, 0xa2, 0x18, 0xb7, 0x9b, 0xe3, 0x62, 0xf7, 0x40, 0xce, 0xaf, 0x64, 0x7f, 0x15, 0xb3, 0x8a, 0xed, 0x91, 0x68, 0x16, 0x3f, 0x92, 0x1b, ]); -/// Prefix is empty because we use entire hardcoded tree right now. Could be useful if tree was implemented as fallback. -pub const ENCODE_PREFIX: &[u8] = &[]; -pub type JetCodeBits = u16; -pub const JET_ENC_BITLEN: usize = 8 * std::mem::size_of::(); +pub const ENCODE_PREFIX: &[u8] = &[1, 1, 1, 1]; +pub type JetCodeBits = u32; +pub const JET_ENC_BITLEN: usize = 16 + ENCODE_PREFIX.len(); // `Jet::encode` uses `write_bits_be` which is bounded to u64 const _: () = { let _ = 0 as JetCodeBits; @@ -79,13 +78,13 @@ impl JetBranchCode { /// as jet's encoding alongside with `ENCODE_PREFIX` pub fn from_ident_fixed(token: proc_macro2::Ident) -> Self { let mut bits = 0 as JetCodeBits; - let mut cursor = 1; + let mut cursor = 1 << (JET_ENC_BITLEN - 1); for &bit in ENCODE_PREFIX { if bit == 1 { bits |= cursor; } - cursor <<= 1; + cursor >>= 1; } let mut token_branch = [0 as u8; std::mem::size_of::()]; @@ -99,12 +98,12 @@ impl JetBranchCode { if token_bits & 1 == 1 { bits |= cursor; } - cursor <<= 1; + cursor >>= 1; token_bits >>= 1; } Self { - bits: bits.reverse_bits(), + bits, len: JET_ENC_BITLEN, token, } @@ -139,7 +138,7 @@ impl JetDecodeTree { for JetBranchCode { bits, len, token } in branches { let mut curr = &mut res; - let mut cursor = bits.reverse_bits(); + let mut cursor = 1 << (len - 1); for _ in 0..len { if curr.token.is_some() { @@ -148,7 +147,7 @@ impl JetDecodeTree { bits ); } - let bit = (cursor & 1) == 1; + let bit = (bits & cursor) != 0; match bit { false => { diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index 607c61c..30c6f77 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -58,6 +58,7 @@ const FMT_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::FmtHandle"; const STR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::StrHandle"; const COST_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CostHandle"; const ALL_JETS_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::AllJetsHandle"; +const DECODE_RES_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::DecodeResHandle"; const CUSTOM_JET_COST: u32 = 1000; @@ -170,6 +171,10 @@ impl StaticTokenInfo { fn all_jets_handle() -> syn::Path { syn::parse_str(ALL_JETS_HANDLE).expect("Failed to find AllJetsHandle by given path") } + + fn decode_res_handle() -> syn::Path { + syn::parse_str(DECODE_RES_HANDLE).expect("Failed to find DecodeResHandle by given path") + } } /// Implements `Jet` trait and C FFI compatible with `simplicity_unchained_core::jets::jet_dyn` interface. @@ -312,7 +317,8 @@ fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2 let jet_codes = build_jet_codes(names); let jet_encode = impl_jet_encode(names, &jet_codes); - let jet_decode_bool_iter = impl_jet_decode_bool_iter(jet_codes); + //let jet_decode_bool_iter = impl_jet_decode_bool_iter(jet_codes); + let jet_decode_unsafe = impl_jet_decode_unsafe(jet_codes); let jet_decode = impl_jet_decode(); let c_jet_ptr = impl_c_jet_ptr(names, jets); @@ -343,7 +349,7 @@ fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2 }; } impl #enum_ident { - #jet_decode_bool_iter + #jet_decode_unsafe } impl #jet_trait_path for #enum_ident { #associated_types @@ -422,13 +428,67 @@ fn impl_c_jet_ptr_boxed( fn impl_jet_decode() -> proc_macro2::TokenStream { let bit_iter = StaticTokenInfo::bit_iter_path(); let decode_err = StaticTokenInfo::decode_err(); + let enum_ident = StaticTokenInfo::enum_ident(); quote! { + /// # Safety + /// + /// Due to the lack of a `Clone` bound on `I`, the underlying implementation uses + /// `ptr::read` to create bitwise copies of the iterator. This is unsafe and may cause + /// undefined behavior if `I` contains types that manage unique resources. + /// This works correctly for common slice-based iterators like `Copied>`. + /// + /// See for details. fn decode>(bits: &mut #bit_iter) -> Result { - let dyn_iter: &mut dyn Iterator = bits; - Self::decode_bool_iter(dyn_iter) + + #enum_ident::decode_unsafe(bits) } + } +} + +fn impl_jet_decode_unsafe(codes: Vec) -> proc_macro2::TokenStream { + let custom_decode_tree: proc_macro2::TokenStream = JetDecodeTree::from_branches(codes).into(); + let decode_err = StaticTokenInfo::decode_err(); + let elements_ident = StaticTokenInfo::elements_enum_path(); + let bit_iter = StaticTokenInfo::bit_iter_path(); + let jet_trait = StaticTokenInfo::jet_trait_path(); + + quote! { + pub fn decode_unsafe>(bits: &mut #bit_iter) -> Result { + let (mut elements_iter, mut custom_iter) = + unsafe { (std::ptr::read(bits), std::ptr::read(bits)) }; + + let bits_read = bits.n_total_read(); + let try_elements = <#elements_ident as #jet_trait>::decode(&mut elements_iter); + + if let Ok(jet) = try_elements { + for _ in 0..(elements_iter.n_total_read() - bits_read) { + bits.next(); + } + + std::mem::forget(elements_iter); + std::mem::forget(custom_iter); + + return Ok(Self::Elements(jet)); + } + + let custom_iter_ref = &mut custom_iter; + let try_custom = decode_bits!(custom_iter_ref, { + #custom_decode_tree + }); + + if try_custom.is_ok() { + for _ in 0..(custom_iter.n_total_read() - bits_read) { + bits.next(); + } + } + + std::mem::forget(elements_iter); + std::mem::forget(custom_iter); + + try_custom + } } } @@ -449,10 +509,8 @@ fn impl_jet_encode( variants: &[proc_macro2::Ident], codes: &[JetBranchCode], ) -> proc_macro2::TokenStream { - let code_len = JET_ENC_BITLEN + 4; // prefix bits are hardcoded - let codes_bits = codes - .iter() - .map(|code| (code.bits as u64) | (15 << JET_ENC_BITLEN)); + let code_len = JET_ENC_BITLEN; + let codes_bits = codes.iter().map(|code| code.bits); let bit_writer = StaticTokenInfo::bit_writer_path(); quote! { diff --git a/service/src/custom_jet.rs b/service/src/custom_jet.rs index 3ba85f8..7e441c6 100644 --- a/service/src/custom_jet.rs +++ b/service/src/custom_jet.rs @@ -53,6 +53,8 @@ static C_JET_PTRS: LazyLock< }) }); +const MAX_JET_BIT_LEN: u32 = 30; + #[derive(Clone, Copy, PartialEq, Eq)] pub(crate) struct CustomJet(CCustomJet); @@ -148,13 +150,21 @@ impl Jet for CustomJet { .encode(self.0, w) } + /// # Safety + /// + /// Due to the lack of a `Clone` bound on `I`, the underlying implementation uses + /// `ptr::read` to create bitwise copies of the iterator. This is unsafe and may cause + /// undefined behavior if `I` contains types that manage unique resources. + /// This works correctly for common slice-based iterators like `Copied>`. + /// + /// See for details. fn decode>( bits: &mut hal_simplicity::simplicity::BitIter, ) -> Result { JET_DLL .as_ref() .expect("DLL is not loaded") - .decode(bits) + .decode(bits, MAX_JET_BIT_LEN) .map(|_jet| Self(_jet)) } @@ -280,7 +290,7 @@ mod test { w.flush_all().unwrap(); - let mut bit_iter = BitIter::from(buffer); + let mut bit_iter = BitIter::from(buffer.as_ref()); let custom_decoded = CustomJet::decode(&mut bit_iter); assert!(custom_decoded.is_ok()); diff --git a/service/src/jet_backend.rs b/service/src/jet_backend.rs index 2705f42..44ca191 100644 --- a/service/src/jet_backend.rs +++ b/service/src/jet_backend.rs @@ -67,7 +67,14 @@ impl Jet for JetBackend { Self::Custom(jet) => jet.encode(w), } } - + /// # Safety + /// + /// Due to the lack of a `Clone` bound on `I`, the underlying implementation uses + /// `ptr::read` to create bitwise copies of the iterator. This is unsafe and may cause + /// undefined behavior if `I` contains types that manage unique resources. + /// This works correctly for common slice-based iterators like `Copied>`. + /// + /// See for details. fn decode>( bits: &mut BitIter, ) -> Result { From 943f80ea3522c1d18d970ecfec5d7ce379350a78 Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Thu, 5 Mar 2026 11:52:08 +0200 Subject: [PATCH 12/14] dynamic base jet type and static `c_jet_ptr` map --- core/src/jets/environments.rs | 13 + core/src/jets/jet_dyn.rs | 16 +- jet_plugins/src/c_wrappers.rs | 57 +- jet_plugins/src/elements_decode_tree.rs | 2666 ----------------------- jet_plugins/src/elements_idents.rs | 473 ---- jet_plugins/src/helpers.rs | 24 - jet_plugins/src/jet_trait.rs | 398 ++++ jet_plugins/src/lib.rs | 721 +----- jet_plugins/src/static_tokens.rs | 139 ++ plugin_test/src/lib.rs | 36 +- service/src/custom_jet.rs | 51 +- 11 files changed, 725 insertions(+), 3869 deletions(-) delete mode 100644 jet_plugins/src/elements_decode_tree.rs delete mode 100644 jet_plugins/src/elements_idents.rs create mode 100644 jet_plugins/src/jet_trait.rs create mode 100644 jet_plugins/src/static_tokens.rs diff --git a/core/src/jets/environments.rs b/core/src/jets/environments.rs index b34dcca..ef47ca8 100644 --- a/core/src/jets/environments.rs +++ b/core/src/jets/environments.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use hal_simplicity::simplicity::ffi::CElementsTxEnv; use hal_simplicity::simplicity::jet::elements::ElementsEnv; use hal_simplicity::simplicity::elements::{Transaction, script::Script}; @@ -17,3 +18,15 @@ impl UnchainedEnv { Self { redeem_script, env } } } + +impl<'a> Into<&'a CElementsTxEnv> for &'a ElementsUnchainedEnv { + fn into(self) -> &'a CElementsTxEnv { + self.env.c_tx_env() + } +} + +impl<'a> Into<&'a ()> for &'a BitcoinUnchainedEnv { + fn into(self) -> &'a () { + &self.env + } +} diff --git a/core/src/jets/jet_dyn.rs b/core/src/jets/jet_dyn.rs index b1be496..f34ff59 100644 --- a/core/src/jets/jet_dyn.rs +++ b/core/src/jets/jet_dyn.rs @@ -4,8 +4,6 @@ use hal_simplicity::simplicity::{ }; use std::{fmt::Formatter, hash::Hasher, io::Write}; -use crate::jets::environments::ElementsUnchainedEnv; - #[repr(C)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct CCustomJet { @@ -132,7 +130,7 @@ pub struct CustomJetApi { _target_ty: extern "C" fn(_self: CCustomJet) -> *const TypeNameHandle, _encode: extern "C" fn(_self: CCustomJet, w: *mut BitWriterHandle) -> *const (), // *std::io::Result _decode: extern "C" fn(w: *mut BitIterHandle) -> DecodeResHandle, - _c_jet_ptr: extern "C" fn(_self: CCustomJet) -> *const (), // * Box bool> + _c_jet_ptr: extern "C" fn(_self: CCustomJet) -> *const (), // * &'static dyn Fn(&mut CFrameItem, CFrameItem, &T) -> bool _cost: extern "C" fn(_self: CCustomJet) -> *const CostHandle, } @@ -265,17 +263,15 @@ impl CustomJetApi { } #[allow(clippy::type_complexity)] - pub fn c_jet_ptr( + pub fn c_jet_ptr( &self, jet: CCustomJet, - ) -> Box bool + Send + Sync> { + ) -> &'static (dyn Fn(&mut CFrameItem, CFrameItem, &T) -> bool + Send + Sync) { unsafe { *Box::from_raw((self._c_jet_ptr)(jet) - as *mut Box< - dyn Fn(&mut CFrameItem, CFrameItem, &ElementsUnchainedEnv) -> bool - + Send - + Sync, - >) + as *mut &'static ( + dyn Fn(&mut CFrameItem, CFrameItem, &T) -> bool + Send + Sync + )) } } diff --git a/jet_plugins/src/c_wrappers.rs b/jet_plugins/src/c_wrappers.rs index c9438c9..17eb762 100644 --- a/jet_plugins/src/c_wrappers.rs +++ b/jet_plugins/src/c_wrappers.rs @@ -1,6 +1,41 @@ -use crate::StaticTokenInfo; +use crate::static_tokens::StaticTokenInfo; use quote::quote; +pub(crate) fn impl_c_ffi(unchained_env: &syn::Path) -> proc_macro2::TokenStream { + let cmp = cmp_c_wrapper(); + let partial_cmp = partial_cmp_c_wrapper(); + let hash = hash_c_wrapper(); + let debug_fmt = debug_fmt_c_wrapper(); + let display_fmt = display_fmt_c_wrapper(); + let all_jets = all_jets(); + + let cmr_c_wrapper = cmr_c_wrapper(); + let src_ty_c_wrapper = src_trg_ty_c_wrapper("source"); + let trg_ty_c_wrapper = src_trg_ty_c_wrapper("target"); + let encode_c_wrapper = encode_c_wrapper(); + let decode_c_wrapper = decode_c_wrapper(); + let cost_c_wrapper = cost_c_wrapper(); + let c_jet_ptr_wrapper = c_jet_ptr_wrapper(unchained_env); + let from_str = from_str_c_wrapper(); + + quote! { + #cmp + #partial_cmp + #hash + #debug_fmt + #display_fmt + #all_jets + #cmr_c_wrapper + #src_ty_c_wrapper + #trg_ty_c_wrapper + #encode_c_wrapper + #decode_c_wrapper + #cost_c_wrapper + #c_jet_ptr_wrapper + #from_str + } +} + pub(crate) fn cost_c_wrapper() -> proc_macro2::TokenStream { let jet_self = StaticTokenInfo::jet_self_handle(); let enum_ident = StaticTokenInfo::enum_ident(); @@ -19,16 +54,30 @@ pub(crate) fn cost_c_wrapper() -> proc_macro2::TokenStream { } } -pub(crate) fn c_jet_ptr_wrapper() -> proc_macro2::TokenStream { +pub(crate) fn c_jet_ptr_wrapper(unchained_env: &syn::Path) -> proc_macro2::TokenStream { let jet_self = StaticTokenInfo::jet_self_handle(); let enum_ident = StaticTokenInfo::enum_ident(); + let c_frame_item = StaticTokenInfo::cframe_item_path(); quote! { #[unsafe(no_mangle)] pub extern "C" fn _c_jet_ptr(_self: #jet_self) -> *const () { let jet = #enum_ident::variant_from_index(_self.index); - let boxed = Box::new(<#enum_ident>::c_jet_ptr_boxed(&jet)); - // * Box bool> + let boxed: Box< + &'static ( + dyn Fn( + &mut #c_frame_item, + #c_frame_item, + &#unchained_env, + ) -> bool + + Send + + Sync + )> + = Box::new(*C_JET_PTRS + .get(&jet) + .expect("All enum's variants should be initialized") + ); + // * &'static dyn Fn(&mut CFrameItem, CFrameItem, &Self::CJetEnvironment) -> bool Box::into_raw(boxed) as *const () } } diff --git a/jet_plugins/src/elements_decode_tree.rs b/jet_plugins/src/elements_decode_tree.rs deleted file mode 100644 index 8249eb4..0000000 --- a/jet_plugins/src/elements_decode_tree.rs +++ /dev/null @@ -1,2666 +0,0 @@ -use crate::StaticTokenInfo; -use quote::quote; - -pub(crate) fn hardcoded_decode_tree( - custom_branches: proc_macro2::TokenStream, -) -> proc_macro2::TokenStream { - let enum_ident = StaticTokenInfo::enum_ident(); - let elements_enum = StaticTokenInfo::elements_enum_path(); - quote! { - decode_bits!(bits, { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Verify)}, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Low1)}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Low8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Low16)}, - 1 => {#enum_ident::Elements(#elements_enum::Low32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Low64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::High1)}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::High8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::High16)}, - 1 => {#enum_ident::Elements(#elements_enum::High32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::High64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Complement1)}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Complement8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Complement16)}, - 1 => {#enum_ident::Elements(#elements_enum::Complement32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Complement64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::And1)}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::And8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::And16)}, - 1 => {#enum_ident::Elements(#elements_enum::And32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::And64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Or1)}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Or8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Or16)}, - 1 => {#enum_ident::Elements(#elements_enum::Or32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Or64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Xor1)}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Xor8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Xor16)}, - 1 => {#enum_ident::Elements(#elements_enum::Xor32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Xor64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Maj1)}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Maj8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Maj16)}, - 1 => {#enum_ident::Elements(#elements_enum::Maj32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Maj64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::XorXor1)}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::XorXor8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::XorXor16)}, - 1 => {#enum_ident::Elements(#elements_enum::XorXor32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::XorXor64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Ch1)}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Ch8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Ch16)}, - 1 => {#enum_ident::Elements(#elements_enum::Ch32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Ch64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Some1)}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Some8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Some16)}, - 1 => {#enum_ident::Elements(#elements_enum::Some32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Some64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::All8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::All16)}, - 1 => {#enum_ident::Elements(#elements_enum::All32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::All64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Eq1)}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Eq8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Eq16)}, - 1 => {#enum_ident::Elements(#elements_enum::Eq32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Eq64)}, - 1 => {} - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Eq256)}, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift8_1)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift16_1)}, - 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift32_1)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_1)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift8_2)}, - 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift16_2)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift32_2)}, - 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_2)} - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift8_4)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift16_4)}, - 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift32_4)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_4)}, - 1 => {} - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift16_8)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift32_8)}, - 1 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_8)} - }, - 1 => {} - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift32_16)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_16)}, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullLeftShift64_32)}, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::FullRightShift8_1)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift16_1)}, - 1 => {#enum_ident::Elements(#elements_enum::FullRightShift32_1)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift64_1)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift8_2)}, - 1 => {#enum_ident::Elements(#elements_enum::FullRightShift16_2)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift32_2)}, - 1 => {#enum_ident::Elements(#elements_enum::FullRightShift64_2)} - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift8_4)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift16_4)}, - 1 => {#enum_ident::Elements(#elements_enum::FullRightShift32_4)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift64_4)}, - 1 => {} - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift16_8)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift32_8)}, - 1 => {#enum_ident::Elements(#elements_enum::FullRightShift64_8)} - }, - 1 => {} - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift32_16)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift64_16)}, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullRightShift64_32)}, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Leftmost8_1)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost16_1)}, - 1 => {#enum_ident::Elements(#elements_enum::Leftmost32_1)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost64_1)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost8_2)}, - 1 => {#enum_ident::Elements(#elements_enum::Leftmost16_2)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost32_2)}, - 1 => {#enum_ident::Elements(#elements_enum::Leftmost64_2)} - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost8_4)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost16_4)}, - 1 => {#enum_ident::Elements(#elements_enum::Leftmost32_4)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost64_4)}, - 1 => {} - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost16_8)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost32_8)}, - 1 => {#enum_ident::Elements(#elements_enum::Leftmost64_8)} - }, - 1 => {} - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost32_16)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost64_16)}, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Leftmost64_32)}, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Rightmost8_1)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost16_1)}, - 1 => {#enum_ident::Elements(#elements_enum::Rightmost32_1)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost64_1)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost8_2)}, - 1 => {#enum_ident::Elements(#elements_enum::Rightmost16_2)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost32_2)}, - 1 => {#enum_ident::Elements(#elements_enum::Rightmost64_2)} - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost8_4)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost16_4)}, - 1 => {#enum_ident::Elements(#elements_enum::Rightmost32_4)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost64_4)}, - 1 => {} - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost16_8)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost32_8)}, - 1 => {#enum_ident::Elements(#elements_enum::Rightmost64_8)} - }, - 1 => {} - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost32_16)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost64_16)}, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Rightmost64_32)}, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::LeftPadLow1_8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow1_16)}, - 1 => {#enum_ident::Elements(#elements_enum::LeftPadLow1_32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow1_64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow8_16)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow8_32)}, - 1 => {#enum_ident::Elements(#elements_enum::LeftPadLow8_64)} - }, - 1 => {} - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow16_32)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow16_64)}, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadLow32_64)}, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::LeftPadHigh1_8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh1_16)}, - 1 => {#enum_ident::Elements(#elements_enum::LeftPadHigh1_32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh1_64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh8_16)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh8_32)}, - 1 => {#enum_ident::Elements(#elements_enum::LeftPadHigh8_64)} - }, - 1 => {} - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh16_32)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh16_64)}, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftPadHigh32_64)}, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::LeftExtend1_8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftExtend1_16)}, - 1 => {#enum_ident::Elements(#elements_enum::LeftExtend1_32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftExtend1_64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftExtend8_16)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftExtend8_32)}, - 1 => {#enum_ident::Elements(#elements_enum::LeftExtend8_64)} - }, - 1 => {} - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftExtend16_32)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftExtend16_64)}, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftExtend32_64)}, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::RightPadLow1_8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadLow1_16)}, - 1 => {#enum_ident::Elements(#elements_enum::RightPadLow1_32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadLow1_64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadLow8_16)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadLow8_32)}, - 1 => {#enum_ident::Elements(#elements_enum::RightPadLow8_64)} - }, - 1 => {} - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadLow16_32)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadLow16_64)}, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadLow32_64)}, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::RightPadHigh1_8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh1_16)}, - 1 => {#enum_ident::Elements(#elements_enum::RightPadHigh1_32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh1_64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh8_16)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh8_32)}, - 1 => {#enum_ident::Elements(#elements_enum::RightPadHigh8_64)} - }, - 1 => {} - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh16_32)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh16_64)}, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightPadHigh32_64)}, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightExtend8_16)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightExtend8_32)}, - 1 => {#enum_ident::Elements(#elements_enum::RightExtend8_64)} - }, - 1 => {} - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::RightExtend16_32)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightExtend16_64)}, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightExtend32_64)}, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::LeftShiftWith8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftShiftWith16)}, - 1 => {#enum_ident::Elements(#elements_enum::LeftShiftWith32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftShiftWith64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::RightShiftWith8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightShiftWith16)}, - 1 => {#enum_ident::Elements(#elements_enum::RightShiftWith32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::RightShiftWith64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::LeftShift8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftShift16)}, - 1 => {#enum_ident::Elements(#elements_enum::LeftShift32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftShift64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::RightShift8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightShift16)}, - 1 => {#enum_ident::Elements(#elements_enum::RightShift32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::RightShift64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::LeftRotate8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftRotate16)}, - 1 => {#enum_ident::Elements(#elements_enum::LeftRotate32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::LeftRotate64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::RightRotate8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::RightRotate16)}, - 1 => {#enum_ident::Elements(#elements_enum::RightRotate32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::RightRotate64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => {} - } - } - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::One8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::One16)}, - 1 => {#enum_ident::Elements(#elements_enum::One32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::One64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::FullAdd8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullAdd16)}, - 1 => {#enum_ident::Elements(#elements_enum::FullAdd32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FullAdd64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Add8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Add16)}, - 1 => {#enum_ident::Elements(#elements_enum::Add32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Add64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::FullIncrement8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullIncrement16)}, - 1 => {#enum_ident::Elements(#elements_enum::FullIncrement32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FullIncrement64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Increment8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Increment16)}, - 1 => {#enum_ident::Elements(#elements_enum::Increment32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Increment64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::FullSubtract8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullSubtract16)}, - 1 => {#enum_ident::Elements(#elements_enum::FullSubtract32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FullSubtract64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Subtract8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Subtract16)}, - 1 => {#enum_ident::Elements(#elements_enum::Subtract32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Subtract64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Negate8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Negate16)}, - 1 => {#enum_ident::Elements(#elements_enum::Negate32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Negate64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::FullDecrement8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullDecrement16)}, - 1 => {#enum_ident::Elements(#elements_enum::FullDecrement32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FullDecrement64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Decrement8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Decrement16)}, - 1 => {#enum_ident::Elements(#elements_enum::Decrement32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Decrement64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::FullMultiply8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FullMultiply16)}, - 1 => {#enum_ident::Elements(#elements_enum::FullMultiply32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FullMultiply64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Multiply8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Multiply16)}, - 1 => {#enum_ident::Elements(#elements_enum::Multiply32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Multiply64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::IsZero8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::IsZero16)}, - 1 => {#enum_ident::Elements(#elements_enum::IsZero32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::IsZero64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::IsOne8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::IsOne16)}, - 1 => {#enum_ident::Elements(#elements_enum::IsOne32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::IsOne64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Le8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Le16)}, - 1 => {#enum_ident::Elements(#elements_enum::Le32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Le64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Lt8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Lt16)}, - 1 => {#enum_ident::Elements(#elements_enum::Lt32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Lt64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Min8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Min16)}, - 1 => {#enum_ident::Elements(#elements_enum::Min32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Min64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Max8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Max16)}, - 1 => {#enum_ident::Elements(#elements_enum::Max32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Max64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Median8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Median16)}, - 1 => {#enum_ident::Elements(#elements_enum::Median32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Median64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::DivMod128_64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::DivMod8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::DivMod16)}, - 1 => {#enum_ident::Elements(#elements_enum::DivMod32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::DivMod64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Divide8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Divide16)}, - 1 => {#enum_ident::Elements(#elements_enum::Divide32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Divide64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Modulo8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Modulo16)}, - 1 => {#enum_ident::Elements(#elements_enum::Modulo32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Modulo64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::Divides8)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Divides16)}, - 1 => {#enum_ident::Elements(#elements_enum::Divides32)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Divides64)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Sha256Block)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Sha256Iv)}, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add1)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add2)}, - 1 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add4)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add8)}, - 1 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add16)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add32)}, - 1 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add64)} - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add128)}, - 1 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add256)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Add512)}, - 1 => {} - } - }, - 1 => {} - } - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8AddBuffer511)}, - 1 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Finalize)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Sha256Ctx8Init)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => {} - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::PointVerify1)}, - 1 => {} - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Decompress)}, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::LinearVerify1)}, - 1 => {} - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::LinearCombination1)}, - 1 => {} - }, - 1 => {#enum_ident::Elements(#elements_enum::Scale)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Generate)}, - 1 => {#enum_ident::Elements(#elements_enum::GejInfinity)} - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::GejNormalize)}, - 1 => {#enum_ident::Elements(#elements_enum::GejNegate)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::GeNegate)}, - 1 => {#enum_ident::Elements(#elements_enum::GejDouble)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::GejAdd)}, - 1 => {#enum_ident::Elements(#elements_enum::GejGeAddEx)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::GejGeAdd)}, - 1 => {#enum_ident::Elements(#elements_enum::GejRescale)} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::GejIsInfinity)}, - 1 => {#enum_ident::Elements(#elements_enum::GejEquiv)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::GejGeEquiv)}, - 1 => {#enum_ident::Elements(#elements_enum::GejXEquiv)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::GejYIsOdd)}, - 1 => {#enum_ident::Elements(#elements_enum::GejIsOnCurve)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::GeIsOnCurve)}, - 1 => {#enum_ident::Elements(#elements_enum::ScalarNormalize)} - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::ScalarNegate)}, - 1 => {#enum_ident::Elements(#elements_enum::ScalarAdd)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::ScalarSquare)}, - 1 => {#enum_ident::Elements(#elements_enum::ScalarMultiply)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::ScalarMultiplyLambda)}, - 1 => {#enum_ident::Elements(#elements_enum::ScalarInvert)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::ScalarIsZero)}, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {}, - 1 => { - 0 => {}, - 1 => {#enum_ident::Elements(#elements_enum::FeNormalize)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FeNegate)}, - 1 => {#enum_ident::Elements(#elements_enum::FeAdd)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FeSquare)}, - 1 => {#enum_ident::Elements(#elements_enum::FeMultiply)} - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FeMultiplyBeta)}, - 1 => {#enum_ident::Elements(#elements_enum::FeInvert)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::FeSquareRoot)}, - 1 => {#enum_ident::Elements(#elements_enum::FeIsZero)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::FeIsOdd)}, - 1 => {} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::HashToCurve)}, - 1 => {#enum_ident::Elements(#elements_enum::Swu)} - } - } - } - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::CheckSigVerify)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Bip0340Verify)}, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => {}, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::ParseLock)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::ParseSequence)}, - 1 => {#enum_ident::Elements(#elements_enum::TapdataInit)} - }, - 1 => {} - } - } - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::SigAllHash)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::TxHash)}, - 1 => {#enum_ident::Elements(#elements_enum::TapEnvHash)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::OutputsHash)}, - 1 => {#enum_ident::Elements(#elements_enum::InputsHash)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::IssuancesHash)}, - 1 => {#enum_ident::Elements(#elements_enum::InputUtxosHash)} - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::OutputHash)}, - 1 => {#enum_ident::Elements(#elements_enum::OutputAmountsHash)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::OutputScriptsHash)}, - 1 => {#enum_ident::Elements(#elements_enum::OutputNoncesHash)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::OutputRangeProofsHash)}, - 1 => {#enum_ident::Elements(#elements_enum::OutputSurjectionProofsHash)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::InputHash)}, - 1 => {#enum_ident::Elements(#elements_enum::InputOutpointsHash)} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::InputSequencesHash)}, - 1 => {#enum_ident::Elements(#elements_enum::InputAnnexesHash)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::InputScriptSigsHash)}, - 1 => {#enum_ident::Elements(#elements_enum::IssuanceHash)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::IssuanceAssetAmountsHash)}, - 1 => {#enum_ident::Elements(#elements_enum::IssuanceTokenAmountsHash)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::IssuanceRangeProofsHash)}, - 1 => {#enum_ident::Elements(#elements_enum::IssuanceBlindingEntropyHash)} - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::InputUtxoHash)}, - 1 => {#enum_ident::Elements(#elements_enum::InputAmountsHash)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::InputScriptsHash)}, - 1 => {#enum_ident::Elements(#elements_enum::TapleafHash)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::TappathHash)}, - 1 => {#enum_ident::Elements(#elements_enum::OutpointHash)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::AssetAmountHash)}, - 1 => {#enum_ident::Elements(#elements_enum::NonceHash)} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::AnnexHash)}, - 1 => {#enum_ident::Elements(#elements_enum::BuildTapleafSimplicity)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::BuildTapbranch)}, - 1 => {#enum_ident::Elements(#elements_enum::BuildTaptweak)} - } - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::CheckLockHeight)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::CheckLockTime)}, - 1 => {#enum_ident::Elements(#elements_enum::CheckLockDistance)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::CheckLockDuration)}, - 1 => {#enum_ident::Elements(#elements_enum::TxLockHeight)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::TxLockTime)}, - 1 => {#enum_ident::Elements(#elements_enum::TxLockDistance)} - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::TxLockDuration)}, - 1 => {#enum_ident::Elements(#elements_enum::TxIsFinal)} - }, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - } - } - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::Issuance)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::IssuanceAsset)}, - 1 => {#enum_ident::Elements(#elements_enum::IssuanceToken)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::IssuanceEntropy)}, - 1 => {#enum_ident::Elements(#elements_enum::CalculateIssuanceEntropy)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::CalculateAsset)}, - 1 => {#enum_ident::Elements(#elements_enum::CalculateExplicitToken)} - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::CalculateConfidentialToken)}, - 1 => {#enum_ident::Elements(#elements_enum::LbtcAsset)} - }, - 1 => {} - }, - 1 => {} - } - }, - 1 => {} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::ScriptCMR)}, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::InternalKey)}, - 1 => {#enum_ident::Elements(#elements_enum::CurrentIndex)} - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::NumInputs)}, - 1 => {#enum_ident::Elements(#elements_enum::NumOutputs)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::LockTime)}, - 1 => {#enum_ident::Elements(#elements_enum::OutputAsset)} - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::OutputAmount)}, - 1 => {#enum_ident::Elements(#elements_enum::OutputNonce)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::OutputScriptHash)}, - 1 => {#enum_ident::Elements(#elements_enum::OutputNullDatum)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::OutputIsFee)}, - 1 => {#enum_ident::Elements(#elements_enum::OutputSurjectionProof)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::OutputRangeProof)}, - 1 => {#enum_ident::Elements(#elements_enum::TotalFee)} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::CurrentPegin)}, - 1 => {#enum_ident::Elements(#elements_enum::CurrentPrevOutpoint)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::CurrentAsset)}, - 1 => {#enum_ident::Elements(#elements_enum::CurrentAmount)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::CurrentScriptHash)}, - 1 => {#enum_ident::Elements(#elements_enum::CurrentSequence)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::CurrentAnnexHash)}, - 1 => {#enum_ident::Elements(#elements_enum::CurrentScriptSigHash)} - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::CurrentReissuanceBlinding)}, - 1 => {#enum_ident::Elements(#elements_enum::CurrentNewIssuanceContract)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::CurrentReissuanceEntropy)}, - 1 => {#enum_ident::Elements(#elements_enum::CurrentIssuanceAssetAmount)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::CurrentIssuanceTokenAmount)}, - 1 => {#enum_ident::Elements(#elements_enum::CurrentIssuanceAssetProof)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::CurrentIssuanceTokenProof)}, - 1 => {#enum_ident::Elements(#elements_enum::InputPegin)} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::InputPrevOutpoint)}, - 1 => {#enum_ident::Elements(#elements_enum::InputAsset)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::InputAmount)}, - 1 => {#enum_ident::Elements(#elements_enum::InputScriptHash)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::InputSequence)}, - 1 => {#enum_ident::Elements(#elements_enum::InputAnnexHash)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::InputScriptSigHash)}, - 1 => {#enum_ident::Elements(#elements_enum::ReissuanceBlinding)} - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::NewIssuanceContract)}, - 1 => {#enum_ident::Elements(#elements_enum::ReissuanceEntropy)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::IssuanceAssetAmount)}, - 1 => {#enum_ident::Elements(#elements_enum::IssuanceTokenAmount)} - } - }, - 1 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::IssuanceAssetProof)}, - 1 => {#enum_ident::Elements(#elements_enum::IssuanceTokenProof)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::TapleafVersion)}, - 1 => {#enum_ident::Elements(#elements_enum::Tappath)} - } - } - } - }, - 1 => { - 0 => { - 0 => { - 0 => { - 0 => {#enum_ident::Elements(#elements_enum::Version)}, - 1 => {#enum_ident::Elements(#elements_enum::GenesisBlockHash)} - }, - 1 => { - 0 => {#enum_ident::Elements(#elements_enum::TransactionId)}, - 1 => {} - } - }, - 1 => {} - }, - 1 => {} - } - } - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - } - } - } - }, - 1 => {} - }, - 1 => {} - }, - 1 => {} - }, - 1 => { - #custom_branches - } - } - } - } - }) - } -} diff --git a/jet_plugins/src/elements_idents.rs b/jet_plugins/src/elements_idents.rs deleted file mode 100644 index 7b2a75c..0000000 --- a/jet_plugins/src/elements_idents.rs +++ /dev/null @@ -1,473 +0,0 @@ -pub(crate) const ELEMENTS_FIELDS: [&str; 471] = [ - "Add16", - "Add32", - "Add64", - "Add8", - "All16", - "All32", - "All64", - "All8", - "And1", - "And16", - "And32", - "And64", - "And8", - "AnnexHash", - "AssetAmountHash", - "Bip0340Verify", - "BuildTapbranch", - "BuildTapleafSimplicity", - "BuildTaptweak", - "CalculateAsset", - "CalculateConfidentialToken", - "CalculateExplicitToken", - "CalculateIssuanceEntropy", - "Ch1", - "Ch16", - "Ch32", - "Ch64", - "Ch8", - "CheckLockDistance", - "CheckLockDuration", - "CheckLockHeight", - "CheckLockTime", - "CheckSigVerify", - "Complement1", - "Complement16", - "Complement32", - "Complement64", - "Complement8", - "CurrentAmount", - "CurrentAnnexHash", - "CurrentAsset", - "CurrentIndex", - "CurrentIssuanceAssetAmount", - "CurrentIssuanceAssetProof", - "CurrentIssuanceTokenAmount", - "CurrentIssuanceTokenProof", - "CurrentNewIssuanceContract", - "CurrentPegin", - "CurrentPrevOutpoint", - "CurrentReissuanceBlinding", - "CurrentReissuanceEntropy", - "CurrentScriptHash", - "CurrentScriptSigHash", - "CurrentSequence", - "Decompress", - "Decrement16", - "Decrement32", - "Decrement64", - "Decrement8", - "DivMod128_64", - "DivMod16", - "DivMod32", - "DivMod64", - "DivMod8", - "Divide16", - "Divide32", - "Divide64", - "Divide8", - "Divides16", - "Divides32", - "Divides64", - "Divides8", - "Eq1", - "Eq16", - "Eq256", - "Eq32", - "Eq64", - "Eq8", - "FeAdd", - "FeInvert", - "FeIsOdd", - "FeIsZero", - "FeMultiply", - "FeMultiplyBeta", - "FeNegate", - "FeNormalize", - "FeSquare", - "FeSquareRoot", - "FullAdd16", - "FullAdd32", - "FullAdd64", - "FullAdd8", - "FullDecrement16", - "FullDecrement32", - "FullDecrement64", - "FullDecrement8", - "FullIncrement16", - "FullIncrement32", - "FullIncrement64", - "FullIncrement8", - "FullLeftShift16_1", - "FullLeftShift16_2", - "FullLeftShift16_4", - "FullLeftShift16_8", - "FullLeftShift32_1", - "FullLeftShift32_16", - "FullLeftShift32_2", - "FullLeftShift32_4", - "FullLeftShift32_8", - "FullLeftShift64_1", - "FullLeftShift64_16", - "FullLeftShift64_2", - "FullLeftShift64_32", - "FullLeftShift64_4", - "FullLeftShift64_8", - "FullLeftShift8_1", - "FullLeftShift8_2", - "FullLeftShift8_4", - "FullMultiply16", - "FullMultiply32", - "FullMultiply64", - "FullMultiply8", - "FullRightShift16_1", - "FullRightShift16_2", - "FullRightShift16_4", - "FullRightShift16_8", - "FullRightShift32_1", - "FullRightShift32_16", - "FullRightShift32_2", - "FullRightShift32_4", - "FullRightShift32_8", - "FullRightShift64_1", - "FullRightShift64_16", - "FullRightShift64_2", - "FullRightShift64_32", - "FullRightShift64_4", - "FullRightShift64_8", - "FullRightShift8_1", - "FullRightShift8_2", - "FullRightShift8_4", - "FullSubtract16", - "FullSubtract32", - "FullSubtract64", - "FullSubtract8", - "GeIsOnCurve", - "GeNegate", - "GejAdd", - "GejDouble", - "GejEquiv", - "GejGeAdd", - "GejGeAddEx", - "GejGeEquiv", - "GejInfinity", - "GejIsInfinity", - "GejIsOnCurve", - "GejNegate", - "GejNormalize", - "GejRescale", - "GejXEquiv", - "GejYIsOdd", - "Generate", - "GenesisBlockHash", - "HashToCurve", - "High1", - "High16", - "High32", - "High64", - "High8", - "Increment16", - "Increment32", - "Increment64", - "Increment8", - "InputAmount", - "InputAmountsHash", - "InputAnnexHash", - "InputAnnexesHash", - "InputAsset", - "InputHash", - "InputOutpointsHash", - "InputPegin", - "InputPrevOutpoint", - "InputScriptHash", - "InputScriptSigHash", - "InputScriptSigsHash", - "InputScriptsHash", - "InputSequence", - "InputSequencesHash", - "InputUtxoHash", - "InputUtxosHash", - "InputsHash", - "InternalKey", - "IsOne16", - "IsOne32", - "IsOne64", - "IsOne8", - "IsZero16", - "IsZero32", - "IsZero64", - "IsZero8", - "Issuance", - "IssuanceAsset", - "IssuanceAssetAmount", - "IssuanceAssetAmountsHash", - "IssuanceAssetProof", - "IssuanceBlindingEntropyHash", - "IssuanceEntropy", - "IssuanceHash", - "IssuanceRangeProofsHash", - "IssuanceToken", - "IssuanceTokenAmount", - "IssuanceTokenAmountsHash", - "IssuanceTokenProof", - "IssuancesHash", - "LbtcAsset", - "Le16", - "Le32", - "Le64", - "Le8", - "LeftExtend16_32", - "LeftExtend16_64", - "LeftExtend1_16", - "LeftExtend1_32", - "LeftExtend1_64", - "LeftExtend1_8", - "LeftExtend32_64", - "LeftExtend8_16", - "LeftExtend8_32", - "LeftExtend8_64", - "LeftPadHigh16_32", - "LeftPadHigh16_64", - "LeftPadHigh1_16", - "LeftPadHigh1_32", - "LeftPadHigh1_64", - "LeftPadHigh1_8", - "LeftPadHigh32_64", - "LeftPadHigh8_16", - "LeftPadHigh8_32", - "LeftPadHigh8_64", - "LeftPadLow16_32", - "LeftPadLow16_64", - "LeftPadLow1_16", - "LeftPadLow1_32", - "LeftPadLow1_64", - "LeftPadLow1_8", - "LeftPadLow32_64", - "LeftPadLow8_16", - "LeftPadLow8_32", - "LeftPadLow8_64", - "LeftRotate16", - "LeftRotate32", - "LeftRotate64", - "LeftRotate8", - "LeftShift16", - "LeftShift32", - "LeftShift64", - "LeftShift8", - "LeftShiftWith16", - "LeftShiftWith32", - "LeftShiftWith64", - "LeftShiftWith8", - "Leftmost16_1", - "Leftmost16_2", - "Leftmost16_4", - "Leftmost16_8", - "Leftmost32_1", - "Leftmost32_16", - "Leftmost32_2", - "Leftmost32_4", - "Leftmost32_8", - "Leftmost64_1", - "Leftmost64_16", - "Leftmost64_2", - "Leftmost64_32", - "Leftmost64_4", - "Leftmost64_8", - "Leftmost8_1", - "Leftmost8_2", - "Leftmost8_4", - "LinearCombination1", - "LinearVerify1", - "LockTime", - "Low1", - "Low16", - "Low32", - "Low64", - "Low8", - "Lt16", - "Lt32", - "Lt64", - "Lt8", - "Maj1", - "Maj16", - "Maj32", - "Maj64", - "Maj8", - "Max16", - "Max32", - "Max64", - "Max8", - "Median16", - "Median32", - "Median64", - "Median8", - "Min16", - "Min32", - "Min64", - "Min8", - "Modulo16", - "Modulo32", - "Modulo64", - "Modulo8", - "Multiply16", - "Multiply32", - "Multiply64", - "Multiply8", - "Negate16", - "Negate32", - "Negate64", - "Negate8", - "NewIssuanceContract", - "NonceHash", - "NumInputs", - "NumOutputs", - "One16", - "One32", - "One64", - "One8", - "Or1", - "Or16", - "Or32", - "Or64", - "Or8", - "OutpointHash", - "OutputAmount", - "OutputAmountsHash", - "OutputAsset", - "OutputHash", - "OutputIsFee", - "OutputNonce", - "OutputNoncesHash", - "OutputNullDatum", - "OutputRangeProof", - "OutputRangeProofsHash", - "OutputScriptHash", - "OutputScriptsHash", - "OutputSurjectionProof", - "OutputSurjectionProofsHash", - "OutputsHash", - "ParseLock", - "ParseSequence", - "PointVerify1", - "ReissuanceBlinding", - "ReissuanceEntropy", - "RightExtend16_32", - "RightExtend16_64", - "RightExtend32_64", - "RightExtend8_16", - "RightExtend8_32", - "RightExtend8_64", - "RightPadHigh16_32", - "RightPadHigh16_64", - "RightPadHigh1_16", - "RightPadHigh1_32", - "RightPadHigh1_64", - "RightPadHigh1_8", - "RightPadHigh32_64", - "RightPadHigh8_16", - "RightPadHigh8_32", - "RightPadHigh8_64", - "RightPadLow16_32", - "RightPadLow16_64", - "RightPadLow1_16", - "RightPadLow1_32", - "RightPadLow1_64", - "RightPadLow1_8", - "RightPadLow32_64", - "RightPadLow8_16", - "RightPadLow8_32", - "RightPadLow8_64", - "RightRotate16", - "RightRotate32", - "RightRotate64", - "RightRotate8", - "RightShift16", - "RightShift32", - "RightShift64", - "RightShift8", - "RightShiftWith16", - "RightShiftWith32", - "RightShiftWith64", - "RightShiftWith8", - "Rightmost16_1", - "Rightmost16_2", - "Rightmost16_4", - "Rightmost16_8", - "Rightmost32_1", - "Rightmost32_16", - "Rightmost32_2", - "Rightmost32_4", - "Rightmost32_8", - "Rightmost64_1", - "Rightmost64_16", - "Rightmost64_2", - "Rightmost64_32", - "Rightmost64_4", - "Rightmost64_8", - "Rightmost8_1", - "Rightmost8_2", - "Rightmost8_4", - "ScalarAdd", - "ScalarInvert", - "ScalarIsZero", - "ScalarMultiply", - "ScalarMultiplyLambda", - "ScalarNegate", - "ScalarNormalize", - "ScalarSquare", - "Scale", - "ScriptCMR", - "Sha256Block", - "Sha256Ctx8Add1", - "Sha256Ctx8Add128", - "Sha256Ctx8Add16", - "Sha256Ctx8Add2", - "Sha256Ctx8Add256", - "Sha256Ctx8Add32", - "Sha256Ctx8Add4", - "Sha256Ctx8Add512", - "Sha256Ctx8Add64", - "Sha256Ctx8Add8", - "Sha256Ctx8AddBuffer511", - "Sha256Ctx8Finalize", - "Sha256Ctx8Init", - "Sha256Iv", - "SigAllHash", - "Some1", - "Some16", - "Some32", - "Some64", - "Some8", - "Subtract16", - "Subtract32", - "Subtract64", - "Subtract8", - "Swu", - "TapEnvHash", - "TapdataInit", - "TapleafHash", - "TapleafVersion", - "Tappath", - "TappathHash", - "TotalFee", - "TransactionId", - "TxHash", - "TxIsFinal", - "TxLockDistance", - "TxLockDuration", - "TxLockHeight", - "TxLockTime", - "Verify", - "Version", - "Xor1", - "Xor16", - "Xor32", - "Xor64", - "Xor8", - "XorXor1", - "XorXor16", - "XorXor32", - "XorXor64", - "XorXor8", -]; diff --git a/jet_plugins/src/helpers.rs b/jet_plugins/src/helpers.rs index 756cc24..206aadf 100644 --- a/jet_plugins/src/helpers.rs +++ b/jet_plugins/src/helpers.rs @@ -65,15 +65,6 @@ pub struct JetBranchCode { } impl JetBranchCode { - // Shifts number's MSB to type MSB - pub fn new(bits: JetCodeBits, len: usize, token: proc_macro2::Ident) -> Self { - Self { - bits: bits << (JET_ENC_BITLEN - len), - len, - token, - } - } - /// Hashes identifier string and takes `JET_ENC_BITLEN - ENCODE_PREFIX.len()` bits of that hash /// as jet's encoding alongside with `ENCODE_PREFIX` pub fn from_ident_fixed(token: proc_macro2::Ident) -> Self { @@ -301,21 +292,6 @@ mod test { lines.join("\n") } - #[test] - fn depr_test_decode_tree() { - let branches = vec![ - JetBranchCode::new(0b11101, 5, quote::format_ident!("Ident1")), - JetBranchCode::new(0b1111, 4, quote::format_ident!("Ident2")), - ]; - - let tree = JetDecodeTree::from_branches(branches); - let tree_tokens: proc_macro2::TokenStream = tree.into(); - - let formatted = format_arms(tree_tokens); - - println!("{}", formatted); - } - #[test] fn test_decode_tree() { let branches = vec![ diff --git a/jet_plugins/src/jet_trait.rs b/jet_plugins/src/jet_trait.rs new file mode 100644 index 0000000..dc37d6f --- /dev/null +++ b/jet_plugins/src/jet_trait.rs @@ -0,0 +1,398 @@ +use crate::{ + CUSTOM_JET_COST, JetsInput, StaticTokenInfo, + helpers::{JET_ENC_BITLEN, JetBranchCode, JetDecodeTree, cmr}, + pascal_to_snake_case, +}; +use quote::quote; + +pub(crate) fn jet_trait_full( + base_type: &syn::Path, + unchained_env: &syn::Path, + names: &[proc_macro2::Ident], + jets: &JetsInput, +) -> proc_macro2::TokenStream { + let associated_types = associated_types_impl(unchained_env); + let c_jet_env = c_jet_env_impl(); + let cmr = cmr_impl(names); + + let src_ty = src_trg_ty_impl(names, &build_source_tys(jets), "source"); + let trgt_ty = src_trg_ty_impl(names, &&build_target_tys(jets), "target"); + + let jet_codes = build_jet_codes(names); + + let jet_encode = encode_impl(names, &jet_codes); + let jet_decode_unsafe = decode_unsafe_impl(base_type, jet_codes); + let jet_decode = decode_impl(); + + let c_jet_ptr = c_jet_ptr_impl(); + let c_jet_ptr_builder = c_jet_ptr_table_impl(base_type, unchained_env, names, jets); + let cost = cost_impl(); + + let jet_trait_path = StaticTokenInfo::jet_trait_path(); + let enum_ident = StaticTokenInfo::enum_ident(); + + let invalid_jet_err = StaticTokenInfo::invalid_jet_err(); + let end_of_stream_err = StaticTokenInfo::end_of_stream_err(); + + let fmt_impl = fmt_trait_full(&names); + let from_str_impl = from_str_trait_full(base_type, &names); + + quote! { + // maybe consider moving it somewhere + macro_rules! decode_bits { + ($bits:ident, {}) => { + Err(#invalid_jet_err.into()) + }; + ($bits:ident, {$jet:expr}) => { + Ok($jet) + }; + ($bits:ident, { 0 => $false_branch:tt, 1 => $true_branch:tt }) => { + match $bits.next() { + None => Err(#end_of_stream_err.into()), + Some(false) => decode_bits!($bits, $false_branch), + Some(true) => decode_bits!($bits, $true_branch), + } + }; + } + #c_jet_ptr_builder + impl #enum_ident { + #jet_decode_unsafe + } + impl #jet_trait_path for #enum_ident { + #associated_types + #c_jet_env + #cmr + #src_ty + #trgt_ty + #jet_encode + #jet_decode + #c_jet_ptr + #cost + } + + #fmt_impl + #from_str_impl + } +} + +// TODO: settle on namings +fn from_str_trait_full( + base_type: &syn::Path, + names: &[proc_macro2::Ident], +) -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let snake_case_names = names + .iter() + .map(|name| pascal_to_snake_case(&name.to_string())); + let simpl_err_ty = StaticTokenInfo::simplicity_error_ty(); + quote! { + impl std::str::FromStr for #enum_ident { + type Err = #simpl_err_ty; + + fn from_str(s: &str) -> Result { + match s { + #(#snake_case_names => Ok(Self::#names), )* + _ => { + let inner_jet = s.parse::<#base_type>()?; + Ok(Self::BaseJets(inner_jet)) + } + } + } + } + } +} + +// TODO: settle on namings +fn fmt_trait_full(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let snake_case_names = names + .iter() + .map(|name| pascal_to_snake_case(&name.to_string())); + quote! { + impl std::fmt::Display for #enum_ident { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::BaseJets(inner_jet) => f.write_str(&inner_jet.to_string()), + #(Self::#names => f.write_str(#snake_case_names), )* + } + } + } + } +} + +fn cost_impl() -> proc_macro2::TokenStream { + let cost_path = StaticTokenInfo::cost_path(); + quote! { + fn cost(&self) -> #cost_path { + match self { + Self::BaseJets(inner_jet) => inner_jet.cost(), + _ => #cost_path::from_milliweight(#CUSTOM_JET_COST) + } + } + } +} + +fn c_jet_ptr_impl() -> proc_macro2::TokenStream { + let c_frame_path = StaticTokenInfo::cframe_item_path(); + + quote! { + fn c_jet_ptr(&self) -> &dyn Fn(&mut #c_frame_path, #c_frame_path, &Self::CJetEnvironment) -> bool { + C_JET_PTRS.get(self).expect("All enum variants should be initialized") + } + } +} + +/// May have issues with env casting +fn c_jet_ptr_table_impl( + base_type: &syn::Path, + unchained_env: &syn::Path, + names: &[proc_macro2::Ident], + inputs: &JetsInput, +) -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let c_frame_item = StaticTokenInfo::cframe_item_path(); + let jet_trait = StaticTokenInfo::jet_trait_path(); + let funcs = inputs.jets.iter().map(|jet| jet.func.clone()); + + quote! { + static C_JET_PTRS: std::sync::LazyLock< + std::collections::HashMap< + #enum_ident, + &'static ( + dyn Fn( + &mut #c_frame_item, + #c_frame_item, + &#unchained_env, + ) -> bool + + Send + + Sync + ), + >, + > = std::sync::LazyLock::new(|| build_c_jet_ptrs()); + + fn build_c_jet_ptrs() + -> std::collections::HashMap< + #enum_ident, + &'static ( + dyn Fn( + &mut #c_frame_item, + #c_frame_item, + &#unchained_env, + ) -> bool + + Send + + Sync + ), + > { + #enum_ident::ALL + .iter() + .map(|jet| { + let boxed: Box< + dyn Fn(&mut #c_frame_item, #c_frame_item, &#unchained_env) -> bool + Send + Sync, + > = match jet { + #enum_ident::BaseJets(inner_jet) => Box::new( + move |dst: &mut #c_frame_item, src: #c_frame_item, env: &#unchained_env| -> bool { + <#base_type as #jet_trait>::c_jet_ptr(inner_jet)(dst, src, env.into()) + }, + ), + // custom jets + #(#enum_ident::#names => Box::new( + move |dst: &mut #c_frame_item, src: #c_frame_item, env: &#unchained_env| -> bool { + #funcs(dst, src, env) + }, + ), )* + }; + let leaked: &'static ( + dyn Fn(&mut #c_frame_item, #c_frame_item, &#unchained_env) -> bool + + Send + + Sync + ) = Box::leak(boxed); + (*jet, leaked) + }) + .collect() + } + } +} + +fn decode_impl() -> proc_macro2::TokenStream { + let bit_iter = StaticTokenInfo::bit_iter_path(); + let decode_err = StaticTokenInfo::decode_err(); + let enum_ident = StaticTokenInfo::enum_ident(); + + quote! { + /// # Safety + /// + /// Due to the lack of a `Clone` bound on `I`, the underlying implementation uses + /// `ptr::read` to create bitwise copies of the iterator. This is unsafe and may cause + /// undefined behavior if `I` contains types that manage unique resources. + /// This works correctly for common slice-based iterators like `Copied>`. + /// + /// See for details. + fn decode>(bits: &mut #bit_iter) -> Result { + + #enum_ident::decode_unsafe(bits) + } + } +} + +fn decode_unsafe_impl( + base_type: &syn::Path, + codes: Vec, +) -> proc_macro2::TokenStream { + let custom_decode_tree: proc_macro2::TokenStream = JetDecodeTree::from_branches(codes).into(); + let decode_err = StaticTokenInfo::decode_err(); + let bit_iter = StaticTokenInfo::bit_iter_path(); + let jet_trait = StaticTokenInfo::jet_trait_path(); + + quote! { + pub fn decode_unsafe>(bits: &mut #bit_iter) -> Result { + let (mut elements_iter, mut custom_iter) = + unsafe { (std::ptr::read(bits), std::ptr::read(bits)) }; + + let bits_read = bits.n_total_read(); + + let try_elements = <#base_type as #jet_trait>::decode(&mut elements_iter); + + if let Ok(jet) = try_elements { + for _ in 0..(elements_iter.n_total_read() - bits_read) { + bits.next(); + } + + std::mem::forget(elements_iter); + std::mem::forget(custom_iter); + + return Ok(Self::BaseJets(jet)); + } + + let custom_iter_ref = &mut custom_iter; + let try_custom = decode_bits!(custom_iter_ref, { + #custom_decode_tree + }); + + if try_custom.is_ok() { + for _ in 0..(custom_iter.n_total_read() - bits_read) { + bits.next(); + } + } + + std::mem::forget(elements_iter); + std::mem::forget(custom_iter); + + try_custom + } + } +} + +fn encode_impl( + variants: &[proc_macro2::Ident], + codes: &[JetBranchCode], +) -> proc_macro2::TokenStream { + let code_len = JET_ENC_BITLEN; + let codes_bits = codes.iter().map(|code| code.bits); + let bit_writer = StaticTokenInfo::bit_writer_path(); + + quote! { + fn encode(&self, w: &mut #bit_writer) -> std::io::Result { + if let Self::BaseJets(inner_jet) = self { + return inner_jet.encode(w); + } + + let (n, len) = match self { + #(Self::#variants => (#codes_bits, #code_len), )* + _ => unreachable!(), + }; + + w.write_bits_be(n as u64, len) + } + } +} + +fn src_trg_ty_impl( + variants: &[proc_macro2::Ident], + types: &[Vec], + mode: &str, +) -> proc_macro2::TokenStream { + let type_name_path = StaticTokenInfo::type_name_path(); + let source_or_target_ty = match mode { + "source" => quote::format_ident!("source_ty"), + "target" => quote::format_ident!("target_ty"), + _ => unreachable!(), + }; + quote! { + fn #source_or_target_ty(&self) -> #type_name_path { + if let Self::BaseJets(inner_jet) = self { + return inner_jet.#source_or_target_ty(); + } + + let name = match self { + #(Self::#variants => &[#(#types,)*],)* + _ => unreachable!(), + }; + + #type_name_path(name) + } + } +} + +fn cmr_impl(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { + let cmr_by_path = StaticTokenInfo::cmr_path(); + let cmrs = variants + .iter() + .map(|ident| { + let ident_str = ident.to_string(); + cmr(&pascal_to_snake_case(&ident_str)) + }) + .collect::>(); + + quote! { + fn cmr(&self) -> #cmr_by_path { + if let Self::BaseJets(inner_jet) = self { + return inner_jet.cmr(); + } + + let bytes = match self { + #(Self::#variants => [#(#cmrs,)*],)* + _ => unreachable!(), + }; + + #cmr_by_path::from_byte_array(bytes) + } + } +} + +fn c_jet_env_impl() -> proc_macro2::TokenStream { + quote! { + fn c_jet_env(env: &Self::Environment) -> &Self::CJetEnvironment { + // For the time being, we are goint to use the initial environment for unchained jets, + // as we are going to implement them in rust. + env + } + } +} + +fn associated_types_impl(unchained_env: &syn::Path) -> proc_macro2::TokenStream { + quote! { + type Environment = #unchained_env; + type CJetEnvironment = #unchained_env; + } +} + +fn build_jet_codes(variants: &[proc_macro2::Ident]) -> Vec { + variants + .iter() + .map(|ident| JetBranchCode::from_ident_fixed(ident.clone())) + .collect() +} + +fn build_target_tys(jets: &JetsInput) -> Vec> { + jets.jets + .iter() + .map(|jet| jet.target_type.value()) + .collect() +} + +fn build_source_tys(jets: &JetsInput) -> Vec> { + jets.jets + .iter() + .map(|jet| jet.source_type.value()) + .collect() +} diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index 30c6f77..c99d6f8 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -1,4 +1,3 @@ -#![recursion_limit = "256"] use quote::quote; use syn::{ Ident, LitByteStr, LitStr, Token, @@ -8,177 +7,23 @@ use syn::{ }; mod c_wrappers; -mod elements_decode_tree; -mod elements_idents; mod helpers; +mod jet_trait; +mod static_tokens; use helpers::snake_to_pascal_case; use crate::{ - c_wrappers::{ - all_jets, c_jet_ptr_wrapper, cmp_c_wrapper, cmr_c_wrapper, cost_c_wrapper, - debug_fmt_c_wrapper, decode_c_wrapper, display_fmt_c_wrapper, encode_c_wrapper, - from_str_c_wrapper, hash_c_wrapper, partial_cmp_c_wrapper, src_trg_ty_c_wrapper, - }, - elements_decode_tree::hardcoded_decode_tree, - helpers::{JET_ENC_BITLEN, JetBranchCode, JetDecodeTree, pascal_to_snake_case}, + c_wrappers::impl_c_ffi, helpers::pascal_to_snake_case, jet_trait::jet_trait_full, + static_tokens::StaticTokenInfo, }; -const STRUCT_EXTENSION_NAME: &str = "JetExtension"; -// Elements stuff -const ELEMENTS_ENUM_PATH: &str = - "simplicity_unchained_core::__simplicity::simplicity::jet::Elements"; -const JET_TRAIT_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::jet::Jet"; -const UNCHAINED_ENV_PATH: &str = - "simplicity_unchained_core::jets::environments::ElementsUnchainedEnv"; -const BIT_WRITER_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::BitWriter"; -const BIT_ITER_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::BitIter"; -const CMR_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::Cmr"; -const COST_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::Cost"; -const INVALID_JET_ERR: &str = - "simplicity_unchained_core::__simplicity::simplicity::decode::Error::InvalidJet"; -const END_OF_STREAM_ERR: &str = - "simplicity_unchained_core::__simplicity::simplicity::decode::Error::EndOfStream"; -const DECODE_ERR_TY: &str = "simplicity_unchained_core::__simplicity::simplicity::decode::Error"; -const TYPE_NAME_PATH: &str = - "simplicity_unchained_core::__simplicity::simplicity::jet::type_name::TypeName"; -const CFRAME_ITEM_PATH: &str = - "simplicity_unchained_core::__simplicity::simplicity::ffi::CFrameItem"; -const SIMPLICITY_ERROR_TY: &str = "simplicity_unchained_core::__simplicity::simplicity::Error"; - -// C FFI stuff -const JET_SELF_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CCustomJet"; -const CMR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CmrHandle"; -const TYPENAME_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::TypeNameHandle"; -const BIT_WRITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitWriterHandle"; -const BIT_ITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitIterHandle"; -const C_ORDERING: &str = "simplicity_unchained_core::jets::jet_dyn::COrdering"; -const C_OPTION_ORDERING: &str = "simplicity_unchained_core::jets::jet_dyn::COptionOrdering"; -const HASHER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::HasherHandle"; -const FMT_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::FmtHandle"; -const STR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::StrHandle"; -const COST_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CostHandle"; -const ALL_JETS_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::AllJetsHandle"; -const DECODE_RES_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::DecodeResHandle"; - const CUSTOM_JET_COST: u32 = 1000; -pub(crate) struct StaticTokenInfo {} - -impl StaticTokenInfo { - fn enum_ident() -> proc_macro2::Ident { - quote::format_ident!("{}", STRUCT_EXTENSION_NAME) - } - - fn elements_enum_path() -> syn::Path { - syn::parse_str(ELEMENTS_ENUM_PATH).expect("Failed to find elements enum by given path") - } - - fn jet_trait_path() -> syn::Path { - syn::parse_str(JET_TRAIT_PATH).expect("Failed to find Jet trait by given path") - } - - fn unchained_env_path() -> syn::Path { - syn::parse_str(UNCHAINED_ENV_PATH).expect("Failed to find UnchainedEnv by given path") - } - - fn bit_iter_path() -> syn::Path { - syn::parse_str(BIT_ITER_PATH).expect("Failed to find BitIter by given path") - } - - fn bit_writer_path() -> syn::Path { - syn::parse_str(BIT_WRITER_PATH).expect("Failed to find BitWriter by given path") - } - - fn invalid_jet_err() -> syn::Path { - syn::parse_str(INVALID_JET_ERR) - .expect("Failed to find simplicity::decode::Error::InvalidJet by given path") - } - - fn end_of_stream_err() -> syn::Path { - syn::parse_str(END_OF_STREAM_ERR) - .expect("Failed to find simplicity::decode::Error::EndOfStream by given path") - } - - fn decode_err() -> syn::Path { - syn::parse_str(DECODE_ERR_TY) - .expect("Failed to find simplicity::decode::Error by given path") - } - - fn cmr_path() -> syn::Path { - syn::parse_str(CMR_PATH).expect("Failed to find Cmr by given path") - } - - fn cost_path() -> syn::Path { - syn::parse_str(COST_PATH).expect("Failed to find Cost by given path") - } - - fn type_name_path() -> syn::Path { - syn::parse_str(TYPE_NAME_PATH).expect("Failed to find TypeName by given path") - } - - fn cframe_item_path() -> syn::Path { - syn::parse_str(CFRAME_ITEM_PATH).expect("Failed to find CFrame by given path") - } - - fn simplicity_error_ty() -> syn::Path { - syn::parse_str(SIMPLICITY_ERROR_TY).expect("Failed to find simplicity::Error by given path") - } - - fn jet_self_handle() -> syn::Path { - syn::parse_str(JET_SELF_HANDLE).expect("Failed to find JetSelfHandle by given path") - } - - fn cmr_handle() -> syn::Path { - syn::parse_str(CMR_HANDLE).expect("Failed to find CmrHandle by given path") - } - - fn typename_handle() -> syn::Path { - syn::parse_str(TYPENAME_HANDLE).expect("Failed to find TypeNameHandle by given path") - } - - fn bitwriter_handle() -> syn::Path { - syn::parse_str(BIT_WRITER_HANDLE).expect("Failed to find BitWriterHandle by given path") - } - - fn bititer_handle() -> syn::Path { - syn::parse_str(BIT_ITER_HANDLE).expect("Failed to find BitIterHandle by given path") - } - - fn c_ordering() -> syn::Path { - syn::parse_str(C_ORDERING).expect("Failed to find COrdering by given path") - } - - fn c_option_ordering() -> syn::Path { - syn::parse_str(C_OPTION_ORDERING).expect("Failed to find COptionOrdering by given path") - } - - fn hasher_handle() -> syn::Path { - syn::parse_str(HASHER_HANDLE).expect("Failed to find HasherHandle by given path") - } - - fn fmt_handle() -> syn::Path { - syn::parse_str(FMT_HANDLE).expect("Failed to find FmtHandle by given path") - } - - fn str_handle() -> syn::Path { - syn::parse_str(STR_HANDLE).expect("Failed to find StrHandle by given path") - } - - fn cost_handle() -> syn::Path { - syn::parse_str(COST_HANDLE).expect("Failed to find CostHandle by given path") - } - - fn all_jets_handle() -> syn::Path { - syn::parse_str(ALL_JETS_HANDLE).expect("Failed to find AllJetsHandle by given path") - } - - fn decode_res_handle() -> syn::Path { - syn::parse_str(DECODE_RES_HANDLE).expect("Failed to find DecodeResHandle by given path") - } -} - /// Implements `Jet` trait and C FFI compatible with `simplicity_unchained_core::jets::jet_dyn` interface. +/// /// ## Arguments +/// - `base_type` - a collection of jets the extension will be built on; +/// - `env` - environment type; /// - `name: literal` - name of jet in snake case; /// - `function: Fn(CFrameItem, CFrameItem, &ElementsUnchainedEnv)`; /// @@ -186,438 +31,139 @@ impl StaticTokenInfo { /// /// - `source_type: &[u8]` /// - `target_type: &[u8]` +/// +/// ## Prerequisites +/// Make sure that input env type implements `Into`, where T is type, which base jets use as env for `c_jet_ptr()`. +/// /// ## Usage /// ```rust /// use jet_plugins::register_jets; /// use simplicity_unchained_core::jets::environments::ElementsUnchainedEnv; /// use simplicity_unchained_core::__simplicity::simplicity::ffi::CFrameItem; /// -/// fn custom_jet_1(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { +/// fn custom_jet1(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { /// false /// } /// -/// fn custom_jet_2(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { +/// fn custom_jet2(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { /// false /// } -/// -/// register_jets![ -/// "custom_jet_1" => custom_jet_1, b"h",b"h"; -/// "custom_jet_2" => custom_jet_2, b"h",b"h"; -/// ]; +/// register_jets!( +/// simplicity_unchained_core::__simplicity::simplicity::jet::Elements, +/// simplicity_unchained_core::jets::environments::ElementsUnchainedEnv, +/// "custom_jet1" => custom_jet1, b"h", b"h", // source/target type +/// "custom_jet2" => custom_jet2, b"h", b"h", // source/target type +/// ); /// ``` #[proc_macro] pub fn register_jets(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = parse2(input.into()).expect("Failed to parse JetsInput"); + let input: JetsInput = parse2(input.into()).expect("Failed to parse JetsInput"); + let base_type = &input.base_type; + let unchained_env = &input.env_type; let names = build_custom_fields(&input); - let self_impl = self_impl_full(&names); - let jet_trait_impl = jet_trait_full(&names, &input); - let fmt_impl = fmt_trait_full(&names); - let from_str_impl = from_str_trait_full(&names); - - let c_ffi = impl_c_ffi(); + let self_impl = self_impl_full(base_type, &names); + let jet_trait_impl = jet_trait_full(base_type, unchained_env, &names, &input); + let c_ffi = impl_c_ffi(unchained_env); quote! { #self_impl #jet_trait_impl - #fmt_impl - #from_str_impl #c_ffi } .into() } -fn impl_c_ffi() -> proc_macro2::TokenStream { - let cmp = cmp_c_wrapper(); - let partial_cmp = partial_cmp_c_wrapper(); - let hash = hash_c_wrapper(); - let debug_fmt = debug_fmt_c_wrapper(); - let display_fmt = display_fmt_c_wrapper(); - let all_jets = all_jets(); - - let cmr_c_wrapper = cmr_c_wrapper(); - let src_ty_c_wrapper = src_trg_ty_c_wrapper("source"); - let trg_ty_c_wrapper = src_trg_ty_c_wrapper("target"); - let encode_c_wrapper = encode_c_wrapper(); - let decode_c_wrapper = decode_c_wrapper(); - let cost_c_wrapper = cost_c_wrapper(); - let c_jet_ptr_wrapper = c_jet_ptr_wrapper(); - let from_str = from_str_c_wrapper(); - - quote! { - #cmp - #partial_cmp - #hash - #debug_fmt - #display_fmt - #all_jets - #cmr_c_wrapper - #src_ty_c_wrapper - #trg_ty_c_wrapper - #encode_c_wrapper - #decode_c_wrapper - #cost_c_wrapper - #c_jet_ptr_wrapper - #from_str - } +pub(crate) struct JetsInput { + base_type: syn::Path, + _comma1: Token![,], + env_type: syn::Path, + _comma2: Token![,], + jets: Punctuated, } -// TODO: settle on namings -fn from_str_trait_full(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { - let enum_ident = StaticTokenInfo::enum_ident(); - let snake_case_names = names - .iter() - .map(|name| pascal_to_snake_case(&name.to_string())); - let simpl_err_ty = StaticTokenInfo::simplicity_error_ty(); - let elements_enum = StaticTokenInfo::elements_enum_path(); - quote! { - impl std::str::FromStr for #enum_ident { - type Err = #simpl_err_ty; - - fn from_str(s: &str) -> Result { - match s { - #(#snake_case_names => Ok(Self::#names), )* - _ => { - let inner_jet = s.parse::<#elements_enum>()?; - Ok(Self::Elements(inner_jet)) - } - } - } - } +impl Parse for JetsInput { + fn parse(input: ParseStream) -> syn::Result { + Ok(JetsInput { + base_type: input.parse()?, + _comma1: input.parse()?, + env_type: input.parse()?, + _comma2: input.parse()?, + jets: input.parse_terminated(JetDef::parse, Token![,])?, + }) } } -// TODO: settle on namings -fn fmt_trait_full(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { - let enum_ident = StaticTokenInfo::enum_ident(); - let snake_case_names = names - .iter() - .map(|name| pascal_to_snake_case(&name.to_string())); - quote! { - impl std::fmt::Display for #enum_ident { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Elements(inner_jet) => f.write_str(&inner_jet.to_string()), - #(Self::#names => f.write_str(#snake_case_names), )* - } - } - } - } +struct JetDef { + name: LitStr, + _arrow: Token![=>], + pub func: Ident, + _comma1: Token![,], + source_type: LitByteStr, + _comma2: Token![,], + target_type: LitByteStr, } -fn jet_trait_full(names: &[proc_macro2::Ident], jets: &JetsInput) -> proc_macro2::TokenStream { - let associated_types = impl_associated_types(); - let c_jet_env = impl_c_jet_env(); - let cmr = impl_cmr(names); - - let src_ty = impl_src_trg_ty(names, &build_source_tys(jets), "source"); - let trgt_ty = impl_src_trg_ty(names, &&build_target_tys(jets), "target"); - - let jet_codes = build_jet_codes(names); - - let jet_encode = impl_jet_encode(names, &jet_codes); - //let jet_decode_bool_iter = impl_jet_decode_bool_iter(jet_codes); - let jet_decode_unsafe = impl_jet_decode_unsafe(jet_codes); - let jet_decode = impl_jet_decode(); - - let c_jet_ptr = impl_c_jet_ptr(names, jets); - let c_jet_ptr_boxed = impl_c_jet_ptr_boxed(names, jets); - let cost = impl_cost(); - - let jet_trait_path = StaticTokenInfo::jet_trait_path(); - let enum_ident = StaticTokenInfo::enum_ident(); - - let invalid_jet_err = StaticTokenInfo::invalid_jet_err(); - let end_of_stream_err = StaticTokenInfo::end_of_stream_err(); - - quote! { - // maybe consider moving it somewhere - macro_rules! decode_bits { - ($bits:ident, {}) => { - Err(#invalid_jet_err.into()) - }; - ($bits:ident, {$jet:expr}) => { - Ok($jet) - }; - ($bits:ident, { 0 => $false_branch:tt, 1 => $true_branch:tt }) => { - match $bits.next() { - None => Err(#end_of_stream_err.into()), - Some(false) => decode_bits!($bits, $false_branch), - Some(true) => decode_bits!($bits, $true_branch), - } - }; - } - impl #enum_ident { - #jet_decode_unsafe - } - impl #jet_trait_path for #enum_ident { - #associated_types - #c_jet_env - #cmr - #src_ty - #trgt_ty - #jet_encode - #jet_decode - #c_jet_ptr - #cost - } - impl #enum_ident { - #c_jet_ptr_boxed - } - +impl Parse for JetDef { + fn parse(input: ParseStream) -> syn::Result { + Ok(JetDef { + // TODO: guarantee that its non-empty and starts from letter + name: input.parse()?, + _arrow: input.parse()?, + func: input.parse()?, + _comma1: input.parse()?, + source_type: input.parse()?, + _comma2: input.parse()?, + target_type: input.parse()?, + }) } } -fn self_impl_full(names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { - let definition = impl_enum_definition(&names); - let all_impl = impl_all_constant(&names); - let c_jet_ptr_shims = c_jet_ptr_shims(); +fn self_impl_full(base_type: &syn::Path, names: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { + let definition = enum_definition_impl(base_type, &names); + let all_impl = all_constant_impl(base_type, &names); quote! { #definition #all_impl - #c_jet_ptr_shims - } -} - -fn impl_cost() -> proc_macro2::TokenStream { - let cost_path = StaticTokenInfo::cost_path(); - quote! { - fn cost(&self) -> #cost_path { - match self { - Self::Elements(inner_jet) => inner_jet.cost(), - _ => #cost_path::from_milliweight(#CUSTOM_JET_COST) - } - } - } -} - -fn impl_c_jet_ptr(names: &[proc_macro2::Ident], inputs: &JetsInput) -> proc_macro2::TokenStream { - let c_frame_path = StaticTokenInfo::cframe_item_path(); - let funcs = inputs.0.iter().map(|jet| jet.func.clone()); - - quote! { - fn c_jet_ptr(&self) -> &dyn Fn(&mut #c_frame_path, #c_frame_path, &Self::CJetEnvironment) -> bool { - match self { - Self::Elements(inner) => Self::elements_c_jet_ptr_wrapper(self), - #(Self::#names => &#funcs, )* - } - } - } -} - -fn impl_c_jet_ptr_boxed( - names: &[proc_macro2::Ident], - inputs: &JetsInput, -) -> proc_macro2::TokenStream { - let c_frame_path = StaticTokenInfo::cframe_item_path(); - let funcs = inputs.0.iter().map(|jet| jet.func.clone()); - let unchained_env = StaticTokenInfo::unchained_env_path(); - - quote! { - pub fn c_jet_ptr_boxed(&self) -> Box bool> { - match self { - Self::Elements(inner) => Self::elements_c_jet_ptr_wrapper_boxed(self), - #(Self::#names => Box::new(#funcs), )* - } - } } } -fn impl_jet_decode() -> proc_macro2::TokenStream { - let bit_iter = StaticTokenInfo::bit_iter_path(); - let decode_err = StaticTokenInfo::decode_err(); - let enum_ident = StaticTokenInfo::enum_ident(); - - quote! { - /// # Safety - /// - /// Due to the lack of a `Clone` bound on `I`, the underlying implementation uses - /// `ptr::read` to create bitwise copies of the iterator. This is unsafe and may cause - /// undefined behavior if `I` contains types that manage unique resources. - /// This works correctly for common slice-based iterators like `Copied>`. - /// - /// See for details. - fn decode>(bits: &mut #bit_iter) -> Result { - - #enum_ident::decode_unsafe(bits) - } - } -} - -fn impl_jet_decode_unsafe(codes: Vec) -> proc_macro2::TokenStream { - let custom_decode_tree: proc_macro2::TokenStream = JetDecodeTree::from_branches(codes).into(); - let decode_err = StaticTokenInfo::decode_err(); - let elements_ident = StaticTokenInfo::elements_enum_path(); - let bit_iter = StaticTokenInfo::bit_iter_path(); - let jet_trait = StaticTokenInfo::jet_trait_path(); - - quote! { - pub fn decode_unsafe>(bits: &mut #bit_iter) -> Result { - let (mut elements_iter, mut custom_iter) = - unsafe { (std::ptr::read(bits), std::ptr::read(bits)) }; - - let bits_read = bits.n_total_read(); - - let try_elements = <#elements_ident as #jet_trait>::decode(&mut elements_iter); - - if let Ok(jet) = try_elements { - for _ in 0..(elements_iter.n_total_read() - bits_read) { - bits.next(); - } - - std::mem::forget(elements_iter); - std::mem::forget(custom_iter); - - return Ok(Self::Elements(jet)); - } - - let custom_iter_ref = &mut custom_iter; - let try_custom = decode_bits!(custom_iter_ref, { - #custom_decode_tree - }); - - if try_custom.is_ok() { - for _ in 0..(custom_iter.n_total_read() - bits_read) { - bits.next(); - } - } - - std::mem::forget(elements_iter); - std::mem::forget(custom_iter); - - try_custom - } - } -} - -fn impl_jet_decode_bool_iter(codes: Vec) -> proc_macro2::TokenStream { - let custom_decode_tree: proc_macro2::TokenStream = JetDecodeTree::from_branches(codes).into(); - let decode_err = StaticTokenInfo::decode_err(); - let entire_tree = hardcoded_decode_tree(custom_decode_tree); - - quote! { - pub fn decode_bool_iter<>(bits: &mut dyn Iterator) -> Result { - #entire_tree - } - - } -} - -fn impl_jet_encode( +fn enum_definition_impl( + base_type: &syn::Path, variants: &[proc_macro2::Ident], - codes: &[JetBranchCode], ) -> proc_macro2::TokenStream { - let code_len = JET_ENC_BITLEN; - let codes_bits = codes.iter().map(|code| code.bits); - let bit_writer = StaticTokenInfo::bit_writer_path(); - + let enum_ident = StaticTokenInfo::enum_ident(); quote! { - fn encode(&self, w: &mut #bit_writer) -> std::io::Result { - if let Self::Elements(inner_jet) = self { - return inner_jet.encode(w); - } - - let (n, len) = match self { - #(Self::#variants => (#codes_bits, #code_len), )* - _ => unreachable!(), - }; - - w.write_bits_be(n as u64, len) + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] + pub enum #enum_ident { + BaseJets(#base_type), + #( #variants, )* } } } -fn build_jet_codes(variants: &[proc_macro2::Ident]) -> Vec { - variants +fn build_custom_fields(jets: &JetsInput) -> Vec { + jets.jets .iter() - .map(|ident| JetBranchCode::from_ident_fixed(ident.clone())) - .collect() + .map(|jet| quote::format_ident!("{}", snake_to_pascal_case(&jet.name.value()))) + .collect::>() } -fn impl_src_trg_ty( +fn all_constant_impl( + base_type: &syn::Path, variants: &[proc_macro2::Ident], - types: &[Vec], - mode: &str, ) -> proc_macro2::TokenStream { - let type_name_path = StaticTokenInfo::type_name_path(); - let source_or_target_ty = match mode { - "source" => quote::format_ident!("source_ty"), - "target" => quote::format_ident!("target_ty"), - _ => unreachable!(), - }; - quote! { - fn #source_or_target_ty(&self) -> #type_name_path { - if let Self::Elements(inner_jet) = self { - return inner_jet.#source_or_target_ty(); - } - - let name = match self { - #(Self::#variants => &[#(#types,)*],)* - _ => unreachable!(), - }; - - #type_name_path(name) - } - } -} - -fn impl_cmr(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { - let cmr_by_path = StaticTokenInfo::cmr_path(); - let cmrs = variants - .iter() - .map(|ident| { - let ident_str = ident.to_string(); - helpers::cmr(&helpers::pascal_to_snake_case(&ident_str)) - }) - .collect::>(); - - quote! { - fn cmr(&self) -> #cmr_by_path { - if let Self::Elements(inner_jet) = self { - return inner_jet.cmr(); - } - - let bytes = match self { - #(Self::#variants => [#(#cmrs,)*],)* - _ => unreachable!(), - }; - - #cmr_by_path::from_byte_array(bytes) - } - } -} - -fn impl_c_jet_env() -> proc_macro2::TokenStream { - quote! { - fn c_jet_env(env: &Self::Environment) -> &Self::CJetEnvironment { - // For the time being, we are goint to use the initial environment for unchained jets, - // as we are going to implement them in rust. - env - } - } -} - -fn impl_associated_types() -> proc_macro2::TokenStream { - let unchained_env = StaticTokenInfo::unchained_env_path(); - quote! { - type Environment = #unchained_env; - type CJetEnvironment = #unchained_env; - } -} - -fn impl_all_constant(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); - let elements_enum_by_path = StaticTokenInfo::elements_enum_path(); let custom_jets_num = variants.len(); let jet_self_handle = StaticTokenInfo::jet_self_handle(); quote! { impl #enum_ident { - // TODO: reference local elements instead of dep - const ALL_JETS_NUM: usize = #elements_enum_by_path::ALL.len() + #custom_jets_num; + // TODO: BaseJets local elements instead of dep + const ALL_JETS_NUM: usize = #base_type::ALL.len() + #custom_jets_num; pub const ALL: [Self; Self::ALL_JETS_NUM] = Self::build_all_variants(); const fn build_all_variants() -> [Self; Self::ALL_JETS_NUM] { @@ -659,8 +205,8 @@ fn impl_all_constant(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStrea let mut i = 0; - while i < #elements_enum_by_path::ALL.len() { - builder.push(#enum_ident::Elements(#elements_enum_by_path::ALL[i])); + while i < #base_type::ALL.len() { + builder.push(#enum_ident::BaseJets(#base_type::ALL[i])); i += 1; } @@ -687,110 +233,3 @@ fn impl_all_constant(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStrea } } } - -// TODO: move this wrappers to some place to avoid reimplementing them for each extension - -// seems it's impossible to wrap `c_jet_ptr` via closure so we should generate a lot of -// boilerplate wrappers -fn c_jet_ptr_shims() -> proc_macro2::TokenStream { - let idents = elements_idents::ELEMENTS_FIELDS - .iter() - .map(|name| quote::format_ident!("{}", name)) - .collect::>(); - - let idents_snake = elements_idents::ELEMENTS_FIELDS - .iter() - .map(|name| quote::format_ident!("{}", pascal_to_snake_case(name))) - .collect::>(); - - let elements_enum = StaticTokenInfo::elements_enum_path(); - let c_frame_item = StaticTokenInfo::cframe_item_path(); - let unchained_env = StaticTokenInfo::unchained_env_path(); - let enum_ident = StaticTokenInfo::enum_ident(); - let jet_trait = StaticTokenInfo::jet_trait_path(); - - quote! { - - impl #enum_ident { - #( - fn #idents_snake(frame: &mut #c_frame_item, arg: #c_frame_item, env: &#unchained_env) -> bool { - <#elements_enum as #jet_trait>::c_jet_ptr(&#elements_enum::#idents)(frame, arg, env.env.c_tx_env()) - } - )* - - fn elements_c_jet_ptr_wrapper(&self) -> &'static dyn Fn(&mut #c_frame_item, #c_frame_item, &#unchained_env) -> bool { - match self { - #( Self::Elements(#elements_enum::#idents) => &Self::#idents_snake), *, - _ => unreachable!() - } - } - - fn elements_c_jet_ptr_wrapper_boxed(&self) -> Box bool> { - match self { - #( Self::Elements(#elements_enum::#idents) => Box::new(Self::#idents_snake)), *, - _ => unreachable!() - } - } - } - } -} - -fn impl_enum_definition(variants: &[proc_macro2::Ident]) -> proc_macro2::TokenStream { - let enum_ident = StaticTokenInfo::enum_ident(); - let elements_enum_by_path = StaticTokenInfo::elements_enum_path(); - quote! { - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] - pub enum #enum_ident { - Elements(#elements_enum_by_path), - #( #variants, )* - } - } -} - -fn build_target_tys(jets: &JetsInput) -> Vec> { - jets.0.iter().map(|jet| jet.target_type.value()).collect() -} - -fn build_source_tys(jets: &JetsInput) -> Vec> { - jets.0.iter().map(|jet| jet.source_type.value()).collect() -} - -fn build_custom_fields(jets: &JetsInput) -> Vec { - jets.0 - .iter() - .map(|jet| quote::format_ident!("{}", snake_to_pascal_case(&jet.name.value()))) - .collect::>() -} - -struct JetsInput(Punctuated); - -impl Parse for JetsInput { - fn parse(input: ParseStream) -> syn::Result { - Ok(JetsInput(input.parse_terminated(JetDef::parse, Token![;])?)) - } -} - -struct JetDef { - name: LitStr, - _arrow: Token![=>], - pub func: Ident, - _comma1: Token![,], - source_type: LitByteStr, - _comma2: Token![,], - target_type: LitByteStr, -} - -impl Parse for JetDef { - fn parse(input: ParseStream) -> syn::Result { - Ok(JetDef { - // TODO: guarantee that its non-empty and starts from letter - name: input.parse()?, - _arrow: input.parse()?, - func: input.parse()?, - _comma1: input.parse()?, - source_type: input.parse()?, - _comma2: input.parse()?, - target_type: input.parse()?, - }) - } -} diff --git a/jet_plugins/src/static_tokens.rs b/jet_plugins/src/static_tokens.rs new file mode 100644 index 0000000..24164d2 --- /dev/null +++ b/jet_plugins/src/static_tokens.rs @@ -0,0 +1,139 @@ +const STRUCT_EXTENSION_NAME: &str = "JetExtension"; + +const JET_TRAIT_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::jet::Jet"; +const BIT_WRITER_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::BitWriter"; +const BIT_ITER_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::BitIter"; +const CMR_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::Cmr"; +const COST_PATH: &str = "simplicity_unchained_core::__simplicity::simplicity::Cost"; +const INVALID_JET_ERR: &str = + "simplicity_unchained_core::__simplicity::simplicity::decode::Error::InvalidJet"; +const END_OF_STREAM_ERR: &str = + "simplicity_unchained_core::__simplicity::simplicity::decode::Error::EndOfStream"; +const DECODE_ERR_TY: &str = "simplicity_unchained_core::__simplicity::simplicity::decode::Error"; +const TYPE_NAME_PATH: &str = + "simplicity_unchained_core::__simplicity::simplicity::jet::type_name::TypeName"; +const CFRAME_ITEM_PATH: &str = + "simplicity_unchained_core::__simplicity::simplicity::ffi::CFrameItem"; +const SIMPLICITY_ERROR_TY: &str = "simplicity_unchained_core::__simplicity::simplicity::Error"; + +// C FFI stuff +const JET_SELF_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CCustomJet"; +const CMR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CmrHandle"; +const TYPENAME_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::TypeNameHandle"; +const BIT_WRITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitWriterHandle"; +const BIT_ITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitIterHandle"; +const C_ORDERING: &str = "simplicity_unchained_core::jets::jet_dyn::COrdering"; +const C_OPTION_ORDERING: &str = "simplicity_unchained_core::jets::jet_dyn::COptionOrdering"; +const HASHER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::HasherHandle"; +const FMT_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::FmtHandle"; +const STR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::StrHandle"; +const COST_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CostHandle"; +const ALL_JETS_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::AllJetsHandle"; +const DECODE_RES_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::DecodeResHandle"; + +pub(crate) struct StaticTokenInfo {} + +impl StaticTokenInfo { + pub fn enum_ident() -> proc_macro2::Ident { + quote::format_ident!("{}", STRUCT_EXTENSION_NAME) + } + + pub fn jet_trait_path() -> syn::Path { + syn::parse_str(JET_TRAIT_PATH).expect("Failed to find Jet trait by given path") + } + + pub fn bit_iter_path() -> syn::Path { + syn::parse_str(BIT_ITER_PATH).expect("Failed to find BitIter by given path") + } + + pub fn bit_writer_path() -> syn::Path { + syn::parse_str(BIT_WRITER_PATH).expect("Failed to find BitWriter by given path") + } + + pub fn invalid_jet_err() -> syn::Path { + syn::parse_str(INVALID_JET_ERR) + .expect("Failed to find simplicity::decode::Error::InvalidJet by given path") + } + + pub fn end_of_stream_err() -> syn::Path { + syn::parse_str(END_OF_STREAM_ERR) + .expect("Failed to find simplicity::decode::Error::EndOfStream by given path") + } + + pub fn decode_err() -> syn::Path { + syn::parse_str(DECODE_ERR_TY) + .expect("Failed to find simplicity::decode::Error by given path") + } + + pub fn cmr_path() -> syn::Path { + syn::parse_str(CMR_PATH).expect("Failed to find Cmr by given path") + } + + pub fn cost_path() -> syn::Path { + syn::parse_str(COST_PATH).expect("Failed to find Cost by given path") + } + + pub fn type_name_path() -> syn::Path { + syn::parse_str(TYPE_NAME_PATH).expect("Failed to find TypeName by given path") + } + + pub fn cframe_item_path() -> syn::Path { + syn::parse_str(CFRAME_ITEM_PATH).expect("Failed to find CFrame by given path") + } + + pub fn simplicity_error_ty() -> syn::Path { + syn::parse_str(SIMPLICITY_ERROR_TY).expect("Failed to find simplicity::Error by given path") + } + + pub fn jet_self_handle() -> syn::Path { + syn::parse_str(JET_SELF_HANDLE).expect("Failed to find JetSelfHandle by given path") + } + + pub fn cmr_handle() -> syn::Path { + syn::parse_str(CMR_HANDLE).expect("Failed to find CmrHandle by given path") + } + + pub fn typename_handle() -> syn::Path { + syn::parse_str(TYPENAME_HANDLE).expect("Failed to find TypeNameHandle by given path") + } + + pub fn bitwriter_handle() -> syn::Path { + syn::parse_str(BIT_WRITER_HANDLE).expect("Failed to find BitWriterHandle by given path") + } + + pub fn bititer_handle() -> syn::Path { + syn::parse_str(BIT_ITER_HANDLE).expect("Failed to find BitIterHandle by given path") + } + + pub fn c_ordering() -> syn::Path { + syn::parse_str(C_ORDERING).expect("Failed to find COrdering by given path") + } + + pub fn c_option_ordering() -> syn::Path { + syn::parse_str(C_OPTION_ORDERING).expect("Failed to find COptionOrdering by given path") + } + + pub fn hasher_handle() -> syn::Path { + syn::parse_str(HASHER_HANDLE).expect("Failed to find HasherHandle by given path") + } + + pub fn fmt_handle() -> syn::Path { + syn::parse_str(FMT_HANDLE).expect("Failed to find FmtHandle by given path") + } + + pub fn str_handle() -> syn::Path { + syn::parse_str(STR_HANDLE).expect("Failed to find StrHandle by given path") + } + + pub fn cost_handle() -> syn::Path { + syn::parse_str(COST_HANDLE).expect("Failed to find CostHandle by given path") + } + + pub fn all_jets_handle() -> syn::Path { + syn::parse_str(ALL_JETS_HANDLE).expect("Failed to find AllJetsHandle by given path") + } + + pub fn decode_res_handle() -> syn::Path { + syn::parse_str(DECODE_RES_HANDLE).expect("Failed to find DecodeResHandle by given path") + } +} diff --git a/plugin_test/src/lib.rs b/plugin_test/src/lib.rs index 4006501..b2fc724 100644 --- a/plugin_test/src/lib.rs +++ b/plugin_test/src/lib.rs @@ -1,16 +1,42 @@ use hal_simplicity::simplicity::ffi::CFrameItem; use jet_plugins::register_jets; -use simplicity_unchained_core::jets::environments::ElementsUnchainedEnv; +use simplicity_unchained_core::jets::environments::{BitcoinUnchainedEnv, ElementsUnchainedEnv}; -pub fn custom_jet1(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { +pub fn custom_jet1_elements( + _dst: &mut CFrameItem, + _: CFrameItem, + _: &ElementsUnchainedEnv, +) -> bool { false } -pub fn custom_jet2(_dst: &mut CFrameItem, src: CFrameItem, env: &ElementsUnchainedEnv) -> bool { +pub fn custom_jet2_elements( + _dst: &mut CFrameItem, + _: CFrameItem, + _: &ElementsUnchainedEnv, +) -> bool { false } + +pub fn custom_jet1_bitcoin(_dst: &mut CFrameItem, _: CFrameItem, _: &BitcoinUnchainedEnv) -> bool { + false +} + +pub fn custom_jet2_bitcoin(_dst: &mut CFrameItem, _: CFrameItem, _: &BitcoinUnchainedEnv) -> bool { + false +} + +//register_jets!( +// hal_simplicity::simplicity::jet::Bitcoin, +// simplicity_unchained_core::jets::environments::BitcoinUnchainedEnv, +// "custom_jet1" => custom_jet1_bitcoin, b"h", b"h", +// "custom_jet2" => custom_jet2_bitcoin, b"h", b"h", +//); + register_jets!( - "custom_jet1" => custom_jet1, b"h", b"h"; - "custom_jet2" => custom_jet2, b"h", b"h"; + hal_simplicity::simplicity::jet::Elements, + simplicity_unchained_core::jets::environments::ElementsUnchainedEnv, + "custom_jet1" => custom_jet1_elements, b"h", b"h", + "custom_jet2" => custom_jet2_elements, b"h", b"h", ); diff --git a/service/src/custom_jet.rs b/service/src/custom_jet.rs index 7e441c6..7e5b8c6 100644 --- a/service/src/custom_jet.rs +++ b/service/src/custom_jet.rs @@ -4,55 +4,15 @@ use simplicity_unchained_core::jets::{ environments::ElementsUnchainedEnv, jet_dyn::{CCustomJet, CustomJetApi}, }; -use std::{collections::HashMap, sync::LazyLock}; +use std::sync::LazyLock; pub(crate) static JET_DLL: LazyLock>> = LazyLock::new(|| { std::env::var("JET_DLL_PATH") .ok() .and_then(|path| unsafe { Container::::load(&path) }.ok()) }); - -fn all_jets() -> Option> { - JET_DLL - .as_ref() - .map(|dll| dll.all_jets().iter().map(|&jet| CustomJet(jet)).collect()) -} - -static ALL_JETS: LazyLock>> = LazyLock::new(|| all_jets()); - -static C_JET_PTRS: LazyLock< - Option< - HashMap< - CustomJet, - &'static ( - dyn Fn( - &mut hal_simplicity::simplicity::ffi::CFrameItem, - hal_simplicity::simplicity::ffi::CFrameItem, - &ElementsUnchainedEnv, - ) -> bool - + Send - + Sync - ), - >, - >, -> = LazyLock::new(|| { - ALL_JETS.as_ref().map(|jets| { - jets.iter() - .map(|&jet| { - ( - jet, - &*Box::leak( - JET_DLL - .as_ref() - .expect("DLL is not loaded") - .c_jet_ptr(jet.0), - ), - ) - }) - .collect::>() - }) -}); - +/// `simplicity_unchained_core::jets::jet_dyn::decode` copies input bits into a separate buffer to transfer them via FFI. +/// This constant controls how many bits will be read. const MAX_JET_BIT_LEN: u32 = 30; #[derive(Clone, Copy, PartialEq, Eq)] @@ -175,11 +135,10 @@ impl Jet for CustomJet { hal_simplicity::simplicity::ffi::CFrameItem, &Self::CJetEnvironment, ) -> bool { - C_JET_PTRS + JET_DLL .as_ref() .expect("DLL is not loaded") - .get(self) - .expect("All jets must be initialized on startup") + .c_jet_ptr(self.0) } fn cost(&self) -> hal_simplicity::simplicity::Cost { From c6492ec64acc8d3897ebcebb696c140201ca7b33 Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Thu, 5 Mar 2026 17:12:57 +0200 Subject: [PATCH 13/14] move custom_jet def to core and change all_jets func to return static ref --- {service/src => core/src/jets}/custom_jet.rs | 41 ++++++----- {service/src => core/src/jets}/jet_backend.rs | 4 +- core/src/jets/jet_dyn.rs | 71 ++++++++----------- core/src/jets/mod.rs | 2 + jet_plugins/src/c_wrappers.rs | 12 ++-- jet_plugins/src/lib.rs | 2 +- jet_plugins/src/static_tokens.rs | 2 +- service/src/main.rs | 2 - 8 files changed, 66 insertions(+), 70 deletions(-) rename {service/src => core/src/jets}/custom_jet.rs (91%) rename {service/src => core/src/jets}/jet_backend.rs (97%) diff --git a/service/src/custom_jet.rs b/core/src/jets/custom_jet.rs similarity index 91% rename from service/src/custom_jet.rs rename to core/src/jets/custom_jet.rs index 7e5b8c6..fb72447 100644 --- a/service/src/custom_jet.rs +++ b/core/src/jets/custom_jet.rs @@ -1,9 +1,6 @@ +use crate::jets::{environments::ElementsUnchainedEnv, jet_dyn::CustomJetApi}; use dlopen2::wrapper::Container; use hal_simplicity::simplicity::jet::Jet; -use simplicity_unchained_core::jets::{ - environments::ElementsUnchainedEnv, - jet_dyn::{CCustomJet, CustomJetApi}, -}; use std::sync::LazyLock; pub(crate) static JET_DLL: LazyLock>> = LazyLock::new(|| { @@ -15,15 +12,24 @@ pub(crate) static JET_DLL: LazyLock>> = LazyLock: /// This constant controls how many bits will be read. const MAX_JET_BIT_LEN: u32 = 30; +#[repr(C)] #[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) struct CustomJet(CCustomJet); +pub struct CustomJet { + pub index: usize, +} + +impl CustomJet { + pub fn all() -> &'static [Self] { + JET_DLL.as_ref().expect("DLL is not loaded").all_jets() + } +} impl std::str::FromStr for CustomJet { type Err = hal_simplicity::simplicity::Error; fn from_str(s: &str) -> Result { match JET_DLL.as_ref().expect("DLL is not loaded").from_str(s) { - Ok(_jet) => Ok(Self(_jet)), + Ok(_jet) => Ok(_jet), Err(err) => Err(err), } } @@ -34,7 +40,7 @@ impl PartialOrd for CustomJet { JET_DLL .as_ref() .expect("DLL is not loaded") - .partial_cmp(self.0, other.0) + .partial_cmp(*self, *other) } } @@ -43,7 +49,7 @@ impl Ord for CustomJet { JET_DLL .as_ref() .expect("DLL is not loaded") - .cmp(self.0, other.0) + .cmp(*self, *other) } } @@ -52,7 +58,7 @@ impl core::hash::Hash for CustomJet { JET_DLL .as_ref() .expect("DLL is not loaded") - .hash(self.0, state); + .hash(*self, state); } } @@ -61,7 +67,7 @@ impl std::fmt::Debug for CustomJet { JET_DLL .as_ref() .expect("DLL is not loaded") - .debug_fmt(self.0, f) + .debug_fmt(*self, f) } } @@ -70,7 +76,7 @@ impl std::fmt::Display for CustomJet { JET_DLL .as_ref() .expect("DLL is not loaded") - .display_fmt(self.0, f) + .display_fmt(*self, f) } } @@ -83,21 +89,21 @@ impl Jet for CustomJet { } fn cmr(&self) -> hal_simplicity::simplicity::Cmr { - JET_DLL.as_ref().expect("DLL is not loaded").cmr(self.0) + JET_DLL.as_ref().expect("DLL is not loaded").cmr(*self) } fn source_ty(&self) -> hal_simplicity::simplicity::jet::type_name::TypeName { JET_DLL .as_ref() .expect("DLL is not loaded") - .source_ty(self.0) + .source_ty(*self) } fn target_ty(&self) -> hal_simplicity::simplicity::jet::type_name::TypeName { JET_DLL .as_ref() .expect("DLL is not loaded") - .target_ty(self.0) + .target_ty(*self) } fn encode( @@ -107,7 +113,7 @@ impl Jet for CustomJet { JET_DLL .as_ref() .expect("DLL is not loaded") - .encode(self.0, w) + .encode(*self, w) } /// # Safety @@ -125,7 +131,6 @@ impl Jet for CustomJet { .as_ref() .expect("DLL is not loaded") .decode(bits, MAX_JET_BIT_LEN) - .map(|_jet| Self(_jet)) } fn c_jet_ptr( @@ -138,11 +143,11 @@ impl Jet for CustomJet { JET_DLL .as_ref() .expect("DLL is not loaded") - .c_jet_ptr(self.0) + .c_jet_ptr(*self) } fn cost(&self) -> hal_simplicity::simplicity::Cost { - JET_DLL.as_ref().expect("DLL is not loaded").cost(self.0) + JET_DLL.as_ref().expect("DLL is not loaded").cost(*self) } } diff --git a/service/src/jet_backend.rs b/core/src/jets/jet_backend.rs similarity index 97% rename from service/src/jet_backend.rs rename to core/src/jets/jet_backend.rs index 44ca191..a5ced8d 100644 --- a/service/src/jet_backend.rs +++ b/core/src/jets/jet_backend.rs @@ -1,12 +1,12 @@ use std::{collections::HashMap, sync::LazyLock}; -use crate::custom_jet::{CustomJet, JET_DLL}; +use crate::jets::custom_jet::{CustomJet, JET_DLL}; +use crate::jets::environments::ElementsUnchainedEnv; use hal_simplicity::simplicity::{ BitIter, BitWriter, Cmr, ffi::CFrameItem, jet::{Elements, Jet, type_name::TypeName}, }; -use simplicity_unchained_core::jets::environments::ElementsUnchainedEnv; static ELEMENTS_JET_PTRS: LazyLock< HashMap< diff --git a/core/src/jets/jet_dyn.rs b/core/src/jets/jet_dyn.rs index f34ff59..4ef16ea 100644 --- a/core/src/jets/jet_dyn.rs +++ b/core/src/jets/jet_dyn.rs @@ -4,12 +4,7 @@ use hal_simplicity::simplicity::{ }; use std::{fmt::Formatter, hash::Hasher, io::Write}; -#[repr(C)] -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct CCustomJet { - pub index: usize, -} - +use crate::jets::custom_jet::CustomJet; #[repr(C)] pub struct JetSelfHandle(()); @@ -112,65 +107,65 @@ pub struct BitWriterHandle<'a> { #[repr(C)] pub struct AllJetsHandle { - pub jets: *const CCustomJet, + pub jets: *const CustomJet, pub len: usize, } #[derive(WrapperApi)] pub struct CustomJetApi { - _cmp: extern "C" fn(_self: CCustomJet, other: CCustomJet) -> COrdering, - _partial_cmp: extern "C" fn(_self: CCustomJet, other: CCustomJet) -> COptionOrdering, - _hash: extern "C" fn(_self: CCustomJet, hasher_handle: *mut HasherHandle), - _debug_fmt: extern "C" fn(_self: CCustomJet, fmt_handle: *mut FmtHandle) -> *const (), // *const std::fmt::Result - _display_fmt: extern "C" fn(_self: CCustomJet, fmt_handle: *mut FmtHandle) -> *const (), // *const std::fmt::Result + _cmp: extern "C" fn(_self: CustomJet, other: CustomJet) -> COrdering, + _partial_cmp: extern "C" fn(_self: CustomJet, other: CustomJet) -> COptionOrdering, + _hash: extern "C" fn(_self: CustomJet, hasher_handle: *mut HasherHandle), + _debug_fmt: extern "C" fn(_self: CustomJet, fmt_handle: *mut FmtHandle) -> *const (), // *const std::fmt::Result + _display_fmt: extern "C" fn(_self: CustomJet, fmt_handle: *mut FmtHandle) -> *const (), // *const std::fmt::Result _from_str: extern "C" fn(name: *const StrHandle) -> *const (), // *const Result _all_jets: extern "C" fn() -> AllJetsHandle, - _cmr: extern "C" fn(_self: CCustomJet) -> *const CmrHandle, - _source_ty: extern "C" fn(_self: CCustomJet) -> *const TypeNameHandle, - _target_ty: extern "C" fn(_self: CCustomJet) -> *const TypeNameHandle, - _encode: extern "C" fn(_self: CCustomJet, w: *mut BitWriterHandle) -> *const (), // *std::io::Result + _cmr: extern "C" fn(_self: CustomJet) -> *const CmrHandle, + _source_ty: extern "C" fn(_self: CustomJet) -> *const TypeNameHandle, + _target_ty: extern "C" fn(_self: CustomJet) -> *const TypeNameHandle, + _encode: extern "C" fn(_self: CustomJet, w: *mut BitWriterHandle) -> *const (), // *std::io::Result _decode: extern "C" fn(w: *mut BitIterHandle) -> DecodeResHandle, - _c_jet_ptr: extern "C" fn(_self: CCustomJet) -> *const (), // * &'static dyn Fn(&mut CFrameItem, CFrameItem, &T) -> bool - _cost: extern "C" fn(_self: CCustomJet) -> *const CostHandle, + _c_jet_ptr: extern "C" fn(_self: CustomJet) -> *const (), // * &'static dyn Fn(&mut CFrameItem, CFrameItem, &T) -> bool + _cost: extern "C" fn(_self: CustomJet) -> *const CostHandle, } impl CustomJetApi { - pub fn from_str(&self, name: &str) -> Result { + pub fn from_str(&self, name: &str) -> Result { let str_handle = StrHandle { _str: &name }; unsafe { *Box::from_raw((self._from_str)(&str_handle) - as *mut Result) + as *mut Result) } } - pub fn all_jets(&self) -> Box<[CCustomJet]> { + pub fn all_jets(&self) -> &'static [CustomJet] { unsafe { let AllJetsHandle { jets, len } = (self._all_jets)(); - Box::from(std::slice::from_raw_parts(jets, len)) + std::slice::from_raw_parts(jets, len) } } - pub fn cmp(&self, lhs: CCustomJet, rhs: CCustomJet) -> std::cmp::Ordering { + pub fn cmp(&self, lhs: CustomJet, rhs: CustomJet) -> std::cmp::Ordering { (self._cmp)(lhs, rhs).into() } - pub fn partial_cmp(&self, lhs: CCustomJet, rhs: CCustomJet) -> Option { + pub fn partial_cmp(&self, lhs: CustomJet, rhs: CustomJet) -> Option { (self._partial_cmp)(lhs, rhs).into() } - pub fn hash(&self, jet: CCustomJet, hasher: &mut H) { + pub fn hash(&self, jet: CustomJet, hasher: &mut H) { let mut hasher_handle = HasherHandle { _hasher: hasher }; (self._hash)(jet, &mut hasher_handle) } - pub fn debug_fmt(&self, jet: CCustomJet, formatter: &mut Formatter) -> std::fmt::Result { + pub fn debug_fmt(&self, jet: CustomJet, formatter: &mut Formatter) -> std::fmt::Result { let mut fmt_handle = FmtHandle { _formatter: formatter, }; unsafe { *Box::from_raw((self._debug_fmt(jet, &mut fmt_handle)) as *mut std::fmt::Result) } } - pub fn display_fmt(&self, jet: CCustomJet, formatter: &mut Formatter) -> std::fmt::Result { + pub fn display_fmt(&self, jet: CustomJet, formatter: &mut Formatter) -> std::fmt::Result { let mut fmt_handle = FmtHandle { _formatter: formatter, }; @@ -179,23 +174,19 @@ impl CustomJetApi { } } - pub fn cmr(&self, jet: CCustomJet) -> Cmr { + pub fn cmr(&self, jet: CustomJet) -> Cmr { *unsafe { Box::from_raw((self._cmr)(jet) as *mut Cmr) } } - pub fn source_ty(&self, jet: CCustomJet) -> TypeName { + pub fn source_ty(&self, jet: CustomJet) -> TypeName { *unsafe { Box::from_raw((self._source_ty)(jet) as *mut TypeName) } } - pub fn target_ty(&self, jet: CCustomJet) -> TypeName { + pub fn target_ty(&self, jet: CustomJet) -> TypeName { *unsafe { Box::from_raw((self._target_ty)(jet) as *mut TypeName) } } - pub fn encode( - &self, - jet: CCustomJet, - w: &mut BitWriter, - ) -> std::io::Result { + pub fn encode(&self, jet: CustomJet, w: &mut BitWriter) -> std::io::Result { let mut buffer = Vec::new(); let mut _writer = BitWriter::new(&mut buffer); @@ -225,7 +216,7 @@ impl CustomJetApi { &self, bits: &mut BitIter, max_jet_len: u32, - ) -> Result { + ) -> Result { let mut bits_copy = unsafe { std::ptr::read(bits) }; let mut buffer = Vec::with_capacity(max_jet_len.div_ceil(8) as usize); let mut writer = BitWriter::from(&mut buffer); @@ -248,9 +239,7 @@ impl CustomJetApi { let DecodeResHandle { jet, bits_read } = (self._decode)(&mut handle); let decoded = unsafe { - *Box::from_raw( - jet as *mut Result, - ) + *Box::from_raw(jet as *mut Result) }; if decoded.is_ok() { @@ -265,7 +254,7 @@ impl CustomJetApi { #[allow(clippy::type_complexity)] pub fn c_jet_ptr( &self, - jet: CCustomJet, + jet: CustomJet, ) -> &'static (dyn Fn(&mut CFrameItem, CFrameItem, &T) -> bool + Send + Sync) { unsafe { *Box::from_raw((self._c_jet_ptr)(jet) @@ -275,7 +264,7 @@ impl CustomJetApi { } } - pub fn cost(&self, jet: CCustomJet) -> Cost { + pub fn cost(&self, jet: CustomJet) -> Cost { unsafe { *Box::from_raw(self._cost(jet) as *mut Cost) } } } diff --git a/core/src/jets/mod.rs b/core/src/jets/mod.rs index 13c5a98..9d0ff3d 100644 --- a/core/src/jets/mod.rs +++ b/core/src/jets/mod.rs @@ -1,7 +1,9 @@ pub mod bitcoin; +pub mod custom_jet; pub mod elements; pub mod environments; pub mod exec; +pub mod jet_backend; pub mod jet_dyn; #[cfg(test)] mod tests; diff --git a/jet_plugins/src/c_wrappers.rs b/jet_plugins/src/c_wrappers.rs index 17eb762..fd81e0a 100644 --- a/jet_plugins/src/c_wrappers.rs +++ b/jet_plugins/src/c_wrappers.rs @@ -194,14 +194,16 @@ pub(crate) fn all_jets() -> proc_macro2::TokenStream { let jet_self_handle = StaticTokenInfo::jet_self_handle(); quote! { + + static ALL_JETS_HANDLE: std::sync::LazyLock> = std::sync::LazyLock::new(|| { + #enum_ident::ALL.iter().map(|jet| jet.into()).collect::>().into_boxed_slice() + }); + #[unsafe(no_mangle)] pub extern "C" fn _all_jets() -> #all_jets_handle { - let all_boxed: Box<[#jet_self_handle]> = #enum_ident::ALL.iter().map(|jet| jet.into()).collect::>().into_boxed_slice(); - let len = all_boxed.len(); - #all_jets_handle { - jets: Box::into_raw(all_boxed) as *const #jet_self_handle, - len + jets: ALL_JETS_HANDLE.as_ptr(), + len: ALL_JETS_HANDLE.len() } } } diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index c99d6f8..6556379 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -163,7 +163,7 @@ fn all_constant_impl( impl #enum_ident { // TODO: BaseJets local elements instead of dep - const ALL_JETS_NUM: usize = #base_type::ALL.len() + #custom_jets_num; + pub const ALL_JETS_NUM: usize = #base_type::ALL.len() + #custom_jets_num; pub const ALL: [Self; Self::ALL_JETS_NUM] = Self::build_all_variants(); const fn build_all_variants() -> [Self; Self::ALL_JETS_NUM] { diff --git a/jet_plugins/src/static_tokens.rs b/jet_plugins/src/static_tokens.rs index 24164d2..b2a0f13 100644 --- a/jet_plugins/src/static_tokens.rs +++ b/jet_plugins/src/static_tokens.rs @@ -17,7 +17,7 @@ const CFRAME_ITEM_PATH: &str = const SIMPLICITY_ERROR_TY: &str = "simplicity_unchained_core::__simplicity::simplicity::Error"; // C FFI stuff -const JET_SELF_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CCustomJet"; +const JET_SELF_HANDLE: &str = "simplicity_unchained_core::jets::custom_jet::CustomJet"; const CMR_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::CmrHandle"; const TYPENAME_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::TypeNameHandle"; const BIT_WRITER_HANDLE: &str = "simplicity_unchained_core::jets::jet_dyn::BitWriterHandle"; diff --git a/service/src/main.rs b/service/src/main.rs index 39d4223..06f4f80 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -1,8 +1,6 @@ mod cli; mod config; -mod custom_jet; mod handlers; -mod jet_backend; mod validation; use cli::{Cli, Commands}; From 747f86b795e4d1bb6fd1d5841ffd7db841263562 Mon Sep 17 00:00:00 2001 From: topologoanatom Date: Fri, 6 Mar 2026 16:32:42 +0200 Subject: [PATCH 14/14] add interface for conversion from base type --- core/src/jets/custom_jet.rs | 24 +++++++++ core/src/jets/jet_dyn.rs | 12 ++++- core/src/runner.rs | 13 +++-- jet_plugins/src/c_wrappers.rs | 64 ++++++++++++++++++----- jet_plugins/src/lib.rs | 13 ++++- plugin_test/src/lib.rs | 97 +++++++++++++++++++++++++---------- plugin_test/src/lib1.rs | 42 +++++++++++++++ 7 files changed, 218 insertions(+), 47 deletions(-) create mode 100644 plugin_test/src/lib1.rs diff --git a/core/src/jets/custom_jet.rs b/core/src/jets/custom_jet.rs index fb72447..7f477d6 100644 --- a/core/src/jets/custom_jet.rs +++ b/core/src/jets/custom_jet.rs @@ -22,6 +22,24 @@ impl CustomJet { pub fn all() -> &'static [Self] { JET_DLL.as_ref().expect("DLL is not loaded").all_jets() } + + pub unsafe fn to_base_jet(&self) -> Option { + unsafe { + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .to_base_jet(*self) + } + } + + pub unsafe fn from_base_jet(jet: &T) -> Self { + unsafe { + JET_DLL + .as_ref() + .expect("DLL is not loaded") + .from_base_jet(jet) + } + } } impl std::str::FromStr for CustomJet { @@ -153,6 +171,7 @@ impl Jet for CustomJet { #[cfg(test)] mod test { + use core::panic; use std::{ hash::{Hash, Hasher}, str::FromStr, @@ -164,6 +183,7 @@ mod test { #[test] fn test_dll_from_str() { + panic!("Rebuild `plugin_test` using code from `lib1.rs`"); unsafe { std::env::set_var("JET_DLL_PATH", "../target/debug/libplugin_test.so"); } @@ -175,6 +195,7 @@ mod test { #[test] fn test_dll_hash() { + panic!("Rebuild `plugin_test` using code from `lib1.rs`"); unsafe { std::env::set_var("JET_DLL_PATH", "../target/debug/libplugin_test.so"); } @@ -188,6 +209,7 @@ mod test { #[test] fn test_dll_encode() { + panic!("Rebuild `plugin_test` using code from `lib1.rs`"); unsafe { std::env::set_var("JET_DLL_PATH", "../target/debug/libplugin_test.so"); } @@ -237,6 +259,7 @@ mod test { #[test] fn test_dll_decode() { + panic!("Rebuild `plugin_test` using code from `lib1.rs`"); unsafe { std::env::set_var("JET_DLL_PATH", "../target/debug/libplugin_test.so"); } @@ -269,6 +292,7 @@ mod test { #[test] fn test_dll_c_jet_ptr() { + panic!("Rebuild `plugin_test` using code from `lib1.rs`"); unsafe { std::env::set_var("JET_DLL_PATH", "../target/debug/libplugin_test.so"); } diff --git a/core/src/jets/jet_dyn.rs b/core/src/jets/jet_dyn.rs index 4ef16ea..bf6d4a4 100644 --- a/core/src/jets/jet_dyn.rs +++ b/core/src/jets/jet_dyn.rs @@ -127,6 +127,8 @@ pub struct CustomJetApi { _decode: extern "C" fn(w: *mut BitIterHandle) -> DecodeResHandle, _c_jet_ptr: extern "C" fn(_self: CustomJet) -> *const (), // * &'static dyn Fn(&mut CFrameItem, CFrameItem, &T) -> bool _cost: extern "C" fn(_self: CustomJet) -> *const CostHandle, + _to_base_jet: extern "C" fn(_self: CustomJet) -> *const (), // * Option + _from_base_jet: extern "C" fn(jet: *const ()) -> CustomJet, // * BaseJetType } impl CustomJetApi { @@ -231,7 +233,7 @@ impl CustomJetApi { .expect("Writing to vec should not fail"); i += 1; } - + println!(); writer.flush_all().expect("Writing to vec should not fail"); let mut handle = BitIterHandle { data: &buffer }; @@ -267,4 +269,12 @@ impl CustomJetApi { pub fn cost(&self, jet: CustomJet) -> Cost { unsafe { *Box::from_raw(self._cost(jet) as *mut Cost) } } + + pub unsafe fn to_base_jet(&self, jet: CustomJet) -> Option { + *unsafe { Box::from_raw(self._to_base_jet(jet) as *mut Option) } + } + + pub unsafe fn from_base_jet(&self, jet: *const T) -> CustomJet { + self._from_base_jet(jet as *const ()) + } } diff --git a/core/src/runner.rs b/core/src/runner.rs index 3e36b35..0b08409 100644 --- a/core/src/runner.rs +++ b/core/src/runner.rs @@ -16,7 +16,7 @@ use hal_simplicity::simplicity::{ use hex_literal::hex; use crate::jets::bitcoin::CoreExtension; -use crate::jets::elements::ElementsExtension; +use crate::jets::custom_jet::CustomJet; use crate::jets::environments::UnchainedEnv; use crate::{BitcoinNetwork, ElementsNetwork}; @@ -61,8 +61,8 @@ impl SimplicityRunner { redeem_script: Script, network: ElementsNetwork, ) -> Result { - let program = Program::::from_str(program, witness) - .map_err(RunnerError::ProgramParse)?; + let program = + Program::::from_str(program, witness).map_err(RunnerError::ProgramParse)?; let elements_env = elements_execution_environment( &pset, @@ -176,11 +176,16 @@ mod tests { #[test] fn it_works() { + unsafe { + std::env::set_var("JET_DLL_PATH", "../target/debug/libplugin_test.so"); + } let script = Script::from_hex( "5221033523982d58e94be3b735731593f8225043880d53727235b566c515d24a0f7baf21025eb4655feae15a304653e27441ca8e8ced2bef89c22ab6b20424b4c07b3d14cc52ae" ).unwrap(); - let program = "4gTaj1eRafl6Ylk5SOxn0YFNK1owqziBW1okfwoUbyI60plzg9+aftH+iEF0HPfdhmi6u3/nCaFpYYsjIjrE6oXS8IEIFCbTPw2hAxCY+ss0X9VBirWgK0d+njZWFwRZorrYK2HgFBAIQKE2ACCP8CEChBhocKkGHHCxbGAgG0DgoHCw"; + // Dependent on program, `decode` may provoke doube free inside `let redeem_prog = wit_hex.map()` + // in Program::from_str() func because of owning iterators. Patch it locally + let program = "4AmwAAR/oHAIsFIGQVZtoIwCBNiDgUA="; let pset_bytes = hex!( "70736574ff0102040200000001030400000000010401010105010201fb04020000000001014e01499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000186a000220020649be4e4f326f85b2187adb0698d9cab59c4b1c747cc6d884211e90e60484cf001070001080100010e201b42ba45a12d33a9efd32b738e603f5c606dcd59353fc5826f7486d4d5459858010f0400000000011004ffffffff00010308b88201000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140104220020649be4e4f326f85b2187adb0698d9cab59c4b1c747cc6d884211e90e60484cf000010308e80300000000000007fc04707365740220499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c1401040000" diff --git a/jet_plugins/src/c_wrappers.rs b/jet_plugins/src/c_wrappers.rs index fd81e0a..e1afbcb 100644 --- a/jet_plugins/src/c_wrappers.rs +++ b/jet_plugins/src/c_wrappers.rs @@ -1,7 +1,10 @@ use crate::static_tokens::StaticTokenInfo; use quote::quote; -pub(crate) fn impl_c_ffi(unchained_env: &syn::Path) -> proc_macro2::TokenStream { +pub(crate) fn impl_c_ffi( + base_type: &syn::Path, + unchained_env: &syn::Path, +) -> proc_macro2::TokenStream { let cmp = cmp_c_wrapper(); let partial_cmp = partial_cmp_c_wrapper(); let hash = hash_c_wrapper(); @@ -17,6 +20,8 @@ pub(crate) fn impl_c_ffi(unchained_env: &syn::Path) -> proc_macro2::TokenStream let cost_c_wrapper = cost_c_wrapper(); let c_jet_ptr_wrapper = c_jet_ptr_wrapper(unchained_env); let from_str = from_str_c_wrapper(); + let to_base_jet = to_base_jet(); + let from_base_jet = from_base_jet(base_type); quote! { #cmp @@ -33,10 +38,12 @@ pub(crate) fn impl_c_ffi(unchained_env: &syn::Path) -> proc_macro2::TokenStream #cost_c_wrapper #c_jet_ptr_wrapper #from_str + #to_base_jet + #from_base_jet } } -pub(crate) fn cost_c_wrapper() -> proc_macro2::TokenStream { +fn cost_c_wrapper() -> proc_macro2::TokenStream { let jet_self = StaticTokenInfo::jet_self_handle(); let enum_ident = StaticTokenInfo::enum_ident(); let jet_trait = StaticTokenInfo::jet_trait_path(); @@ -54,7 +61,7 @@ pub(crate) fn cost_c_wrapper() -> proc_macro2::TokenStream { } } -pub(crate) fn c_jet_ptr_wrapper(unchained_env: &syn::Path) -> proc_macro2::TokenStream { +fn c_jet_ptr_wrapper(unchained_env: &syn::Path) -> proc_macro2::TokenStream { let jet_self = StaticTokenInfo::jet_self_handle(); let enum_ident = StaticTokenInfo::enum_ident(); let c_frame_item = StaticTokenInfo::cframe_item_path(); @@ -83,7 +90,7 @@ pub(crate) fn c_jet_ptr_wrapper(unchained_env: &syn::Path) -> proc_macro2::Token } } -pub(crate) fn decode_c_wrapper() -> proc_macro2::TokenStream { +fn decode_c_wrapper() -> proc_macro2::TokenStream { let bititer_handle = StaticTokenInfo::bititer_handle(); let jet_self = StaticTokenInfo::jet_self_handle(); let enum_ident = StaticTokenInfo::enum_ident(); @@ -122,7 +129,7 @@ pub(crate) fn decode_c_wrapper() -> proc_macro2::TokenStream { } } -pub(crate) fn encode_c_wrapper() -> proc_macro2::TokenStream { +fn encode_c_wrapper() -> proc_macro2::TokenStream { let bitwriter_handle = StaticTokenInfo::bitwriter_handle(); let jet_self = StaticTokenInfo::jet_self_handle(); let enum_ident = StaticTokenInfo::enum_ident(); @@ -142,7 +149,7 @@ pub(crate) fn encode_c_wrapper() -> proc_macro2::TokenStream { } } -pub(crate) fn src_trg_ty_c_wrapper(mode: &str) -> proc_macro2::TokenStream { +fn src_trg_ty_c_wrapper(mode: &str) -> proc_macro2::TokenStream { let src_trg_ty = quote::format_ident!("{}", { match mode { "source" | "target" => format!("{}_ty", mode), @@ -172,7 +179,7 @@ pub(crate) fn src_trg_ty_c_wrapper(mode: &str) -> proc_macro2::TokenStream { } } -pub(crate) fn cmr_c_wrapper() -> proc_macro2::TokenStream { +fn cmr_c_wrapper() -> proc_macro2::TokenStream { let cmr_handle = StaticTokenInfo::cmr_handle(); let jet_self = StaticTokenInfo::jet_self_handle(); let enum_ident = StaticTokenInfo::enum_ident(); @@ -188,7 +195,7 @@ pub(crate) fn cmr_c_wrapper() -> proc_macro2::TokenStream { } } -pub(crate) fn all_jets() -> proc_macro2::TokenStream { +fn all_jets() -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); let all_jets_handle = StaticTokenInfo::all_jets_handle(); let jet_self_handle = StaticTokenInfo::jet_self_handle(); @@ -209,7 +216,36 @@ pub(crate) fn all_jets() -> proc_macro2::TokenStream { } } -pub(crate) fn from_str_c_wrapper() -> proc_macro2::TokenStream { +fn from_base_jet(base_type: &syn::Path) -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_self_handle = StaticTokenInfo::jet_self_handle(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _from_base_jet(jet_ptr: *const ()) -> #jet_self_handle { + let jet = unsafe { &*(jet_ptr as *const #base_type) }; + (&#enum_ident::from_base_jet(*jet)).into() + } + } +} + +fn to_base_jet() -> proc_macro2::TokenStream { + let enum_ident = StaticTokenInfo::enum_ident(); + let jet_self_handle = StaticTokenInfo::jet_self_handle(); + + quote! { + #[unsafe(no_mangle)] + pub extern "C" fn _to_base_jet(_self: #jet_self_handle) -> *const () { + let inner_jet = #enum_ident::variant_from_index(_self.index); + let try_to_base = Box::new(inner_jet.to_base_jet()); + + // * Option + Box::into_raw(try_to_base) as *const () + } + } +} + +fn from_str_c_wrapper() -> proc_macro2::TokenStream { let str_handle = StaticTokenInfo::str_handle(); let enum_ident = StaticTokenInfo::enum_ident(); let jet_self = StaticTokenInfo::jet_self_handle(); @@ -238,7 +274,7 @@ pub(crate) fn from_str_c_wrapper() -> proc_macro2::TokenStream { } } -pub(crate) fn display_fmt_c_wrapper() -> proc_macro2::TokenStream { +fn display_fmt_c_wrapper() -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); let jet_self = StaticTokenInfo::jet_self_handle(); let fmt_handle = StaticTokenInfo::fmt_handle(); @@ -254,7 +290,7 @@ pub(crate) fn display_fmt_c_wrapper() -> proc_macro2::TokenStream { } } -pub(crate) fn debug_fmt_c_wrapper() -> proc_macro2::TokenStream { +fn debug_fmt_c_wrapper() -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); let jet_self = StaticTokenInfo::jet_self_handle(); let fmt_handle = StaticTokenInfo::fmt_handle(); @@ -270,7 +306,7 @@ pub(crate) fn debug_fmt_c_wrapper() -> proc_macro2::TokenStream { } } -pub(crate) fn hash_c_wrapper() -> proc_macro2::TokenStream { +fn hash_c_wrapper() -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); let jet_self = StaticTokenInfo::jet_self_handle(); let hasher_handle = StaticTokenInfo::hasher_handle(); @@ -285,7 +321,7 @@ pub(crate) fn hash_c_wrapper() -> proc_macro2::TokenStream { } } -pub(crate) fn partial_cmp_c_wrapper() -> proc_macro2::TokenStream { +fn partial_cmp_c_wrapper() -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); let jet_self = StaticTokenInfo::jet_self_handle(); let c_option_ordering = StaticTokenInfo::c_option_ordering(); @@ -300,7 +336,7 @@ pub(crate) fn partial_cmp_c_wrapper() -> proc_macro2::TokenStream { } } -pub(crate) fn cmp_c_wrapper() -> proc_macro2::TokenStream { +fn cmp_c_wrapper() -> proc_macro2::TokenStream { let enum_ident = StaticTokenInfo::enum_ident(); let jet_self = StaticTokenInfo::jet_self_handle(); let c_ordering = StaticTokenInfo::c_ordering(); diff --git a/jet_plugins/src/lib.rs b/jet_plugins/src/lib.rs index 6556379..bce1b8a 100644 --- a/jet_plugins/src/lib.rs +++ b/jet_plugins/src/lib.rs @@ -65,7 +65,7 @@ pub fn register_jets(input: proc_macro::TokenStream) -> proc_macro::TokenStream let self_impl = self_impl_full(base_type, &names); let jet_trait_impl = jet_trait_full(base_type, unchained_env, &names, &input); - let c_ffi = impl_c_ffi(unchained_env); + let c_ffi = impl_c_ffi(base_type, unchained_env); quote! { #self_impl @@ -222,6 +222,17 @@ fn all_constant_impl( pub fn variant_from_index(idx: usize) -> Self { Self::ALL[idx] } + + pub fn to_base_jet(&self) -> Option<#base_type> { + match self { + Self::BaseJets(jet) => Some(*jet), + _ => None + } + } + + pub fn from_base_jet(jet: #base_type) -> Self { + Self::BaseJets(jet) + } } impl Into<#jet_self_handle> for &#enum_ident { diff --git a/plugin_test/src/lib.rs b/plugin_test/src/lib.rs index b2fc724..c2ee896 100644 --- a/plugin_test/src/lib.rs +++ b/plugin_test/src/lib.rs @@ -1,42 +1,85 @@ -use hal_simplicity::simplicity::ffi::CFrameItem; +use hal_simplicity::simplicity::{elements::opcodes::all::OP_PUSHBYTES_33, ffi::CFrameItem}; use jet_plugins::register_jets; +use simplicity_unchained_core::jets::environments::ElementsUnchainedEnv; -use simplicity_unchained_core::jets::environments::{BitcoinUnchainedEnv, ElementsUnchainedEnv}; +#[allow(unused)] +unsafe extern "C" { + fn rustsimplicity_0_6_write8(dst: *mut CFrameItem, n: u64); + fn rustsimplicity_0_6_write16(dst: *mut CFrameItem, n: u16); + fn rustsimplicity_0_6_write32(dst: *mut CFrameItem, n: u32); + fn rustsimplicity_0_6_write64(dst: *mut CFrameItem, n: u64); -pub fn custom_jet1_elements( - _dst: &mut CFrameItem, - _: CFrameItem, - _: &ElementsUnchainedEnv, -) -> bool { - false + fn rustsimplicity_0_6_read8(src: *const CFrameItem) -> u64; + fn rustsimplicity_0_6_read16(src: *const CFrameItem) -> u16; + fn rustsimplicity_0_6_read32(src: *const CFrameItem) -> u32; + fn rustsimplicity_0_6_read64(src: *const CFrameItem) -> u64; } -pub fn custom_jet2_elements( - _dst: &mut CFrameItem, - _: CFrameItem, - _: &ElementsUnchainedEnv, +/// 2^8 |- 2^8 +/// +/// Returns the opcode at the given index in the redeem script, each opcode is one byte. +pub fn get_opcode_from_script( + dst: &mut CFrameItem, + src: CFrameItem, + env: &ElementsUnchainedEnv, ) -> bool { - false -} + let index = unsafe { rustsimplicity_0_6_read8(&src as *const CFrameItem) } as usize; + if index >= env.redeem_script.len() { + return false; + } -pub fn custom_jet1_bitcoin(_dst: &mut CFrameItem, _: CFrameItem, _: &BitcoinUnchainedEnv) -> bool { - false -} + let opcode = env.redeem_script.as_bytes()[index]; -pub fn custom_jet2_bitcoin(_dst: &mut CFrameItem, _: CFrameItem, _: &BitcoinUnchainedEnv) -> bool { - false + unsafe { + rustsimplicity_0_6_write8(dst as *mut CFrameItem, opcode as u64); + } + + true } +/// 2^8 |- 2^256 +/// +/// Each pubkey is encoded as: [OP_PUSHBYTES_33][0x02 or 0x03][32 bytes X coordinate] +pub fn get_pubkey_from_script( + dst: &mut CFrameItem, + src: CFrameItem, + env: &ElementsUnchainedEnv, +) -> bool { + let start_index = unsafe { rustsimplicity_0_6_read8(&src as *const CFrameItem) } as usize; + if start_index + 34 > env.redeem_script.len() { + return false; + } + + let push_bytes_opcode = env.redeem_script.as_bytes()[start_index]; + if push_bytes_opcode != OP_PUSHBYTES_33.into_u8() { + return false; + } -//register_jets!( -// hal_simplicity::simplicity::jet::Bitcoin, -// simplicity_unchained_core::jets::environments::BitcoinUnchainedEnv, -// "custom_jet1" => custom_jet1_bitcoin, b"h", b"h", -// "custom_jet2" => custom_jet2_bitcoin, b"h", b"h", -//); + let prefix = env.redeem_script.as_bytes()[start_index + 1]; + if prefix != 0x02 && prefix != 0x03 { + return false; + } + + let x_only_pubkey_start = start_index + 2; + + let pubkey_bytes = &env.redeem_script.as_bytes()[x_only_pubkey_start..x_only_pubkey_start + 32]; + + let words: Vec = pubkey_bytes + .chunks(4) + .map(|chunk| u32::from_be_bytes(chunk.try_into().expect("Chunk with incorrect length"))) + .collect::>(); + + for word in words.iter() { + unsafe { + rustsimplicity_0_6_write32(dst as *mut CFrameItem, *word); + } + } + + true +} register_jets!( hal_simplicity::simplicity::jet::Elements, simplicity_unchained_core::jets::environments::ElementsUnchainedEnv, - "custom_jet1" => custom_jet1_elements, b"h", b"h", - "custom_jet2" => custom_jet2_elements, b"h", b"h", + "get_opcode_from_script" => get_opcode_from_script, b"c", b"c", + "get_pubkey_from_script" => get_pubkey_from_script, b"c", b"h", ); diff --git a/plugin_test/src/lib1.rs b/plugin_test/src/lib1.rs new file mode 100644 index 0000000..b2fc724 --- /dev/null +++ b/plugin_test/src/lib1.rs @@ -0,0 +1,42 @@ +use hal_simplicity::simplicity::ffi::CFrameItem; +use jet_plugins::register_jets; + +use simplicity_unchained_core::jets::environments::{BitcoinUnchainedEnv, ElementsUnchainedEnv}; + +pub fn custom_jet1_elements( + _dst: &mut CFrameItem, + _: CFrameItem, + _: &ElementsUnchainedEnv, +) -> bool { + false +} + +pub fn custom_jet2_elements( + _dst: &mut CFrameItem, + _: CFrameItem, + _: &ElementsUnchainedEnv, +) -> bool { + false +} + +pub fn custom_jet1_bitcoin(_dst: &mut CFrameItem, _: CFrameItem, _: &BitcoinUnchainedEnv) -> bool { + false +} + +pub fn custom_jet2_bitcoin(_dst: &mut CFrameItem, _: CFrameItem, _: &BitcoinUnchainedEnv) -> bool { + false +} + +//register_jets!( +// hal_simplicity::simplicity::jet::Bitcoin, +// simplicity_unchained_core::jets::environments::BitcoinUnchainedEnv, +// "custom_jet1" => custom_jet1_bitcoin, b"h", b"h", +// "custom_jet2" => custom_jet2_bitcoin, b"h", b"h", +//); + +register_jets!( + hal_simplicity::simplicity::jet::Elements, + simplicity_unchained_core::jets::environments::ElementsUnchainedEnv, + "custom_jet1" => custom_jet1_elements, b"h", b"h", + "custom_jet2" => custom_jet2_elements, b"h", b"h", +);