diff --git a/packages/wasm-miniscript/src/descriptor.rs b/packages/wasm-miniscript/src/descriptor.rs new file mode 100644 index 0000000..cc9c995 --- /dev/null +++ b/packages/wasm-miniscript/src/descriptor.rs @@ -0,0 +1,108 @@ +use std::str::FromStr; +use miniscript::{DefiniteDescriptorKey, Descriptor, DescriptorPublicKey}; +use miniscript::bitcoin::ScriptBuf; +use miniscript::bitcoin::secp256k1::Secp256k1; +use miniscript::descriptor::KeyMap; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::{JsError, JsValue}; +use crate::try_into_js_value::TryIntoJsValue; + +enum WrapDescriptorEnum { + Derivable(Descriptor, KeyMap), + Definite(Descriptor), + String(Descriptor), +} + +#[wasm_bindgen] +pub struct WrapDescriptor(WrapDescriptorEnum); + +#[wasm_bindgen] +impl WrapDescriptor { + pub fn node(&self) -> Result { + Ok(match &self.0 { + WrapDescriptorEnum::Derivable(desc, _) => desc.try_to_js_value()?, + WrapDescriptorEnum::Definite(desc) => desc.try_to_js_value()?, + WrapDescriptorEnum::String(desc) => desc.try_to_js_value()?, + }) + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + match &self.0 { + WrapDescriptorEnum::Derivable(desc, _) => desc.to_string(), + WrapDescriptorEnum::Definite(desc) => desc.to_string(), + WrapDescriptorEnum::String(desc) => desc.to_string(), + } + } + + #[wasm_bindgen(js_name = hasWildcard)] + pub fn has_wildcard(&self) -> bool { + match &self.0 { + WrapDescriptorEnum::Derivable(desc, _) => desc.has_wildcard(), + WrapDescriptorEnum::Definite(_) => false, + WrapDescriptorEnum::String(_) => false, + } + } + + #[wasm_bindgen(js_name = atDerivationIndex)] + pub fn at_derivation_index(&self, index: u32) -> Result { + match &self.0 { + WrapDescriptorEnum::Derivable(desc, _keys) => { + let d = desc.at_derivation_index(index)?; + Ok(WrapDescriptor(WrapDescriptorEnum::Definite(d))) + } + _ => Err(JsError::new("Cannot derive from a definite descriptor")), + } + } + + #[wasm_bindgen(js_name = scriptPubkey)] + pub fn script_pubkey(&self) -> Result, JsError> { + match &self.0 { + WrapDescriptorEnum::Definite(desc) => { + Ok(desc.script_pubkey().to_bytes()) + } + _ => Err(JsError::new("Cannot derive from a non-definite descriptor")), + } + } + + fn explicit_script(&self) -> Result { + match &self.0 { + WrapDescriptorEnum::Definite(desc) => { + Ok(desc.explicit_script()?) + } + WrapDescriptorEnum::Derivable(_, _) => { + Err(JsError::new("Cannot encode a derivable descriptor")) + } + WrapDescriptorEnum::String(_) => Err(JsError::new("Cannot encode a string descriptor")), + } + } + + pub fn encode(&self) -> Result, JsError> { + Ok(self.explicit_script()?.to_bytes()) + } + + #[wasm_bindgen(js_name = toAsmString)] + pub fn to_asm_string(&self) -> Result { + Ok(self.explicit_script()?.to_asm_string()) + } +} + +#[wasm_bindgen] +pub fn descriptor_from_string(descriptor: &str, pk_type: &str) -> Result { + match pk_type { + "derivable" => { + let secp = Secp256k1::new(); + let (desc, keys) = Descriptor::parse_descriptor(&secp, descriptor)?; + Ok(WrapDescriptor(WrapDescriptorEnum::Derivable(desc, keys))) + } + "definite" => { + let desc = Descriptor::::from_str(descriptor)?; + Ok(WrapDescriptor(WrapDescriptorEnum::Definite(desc))) + } + "string" => { + let desc = Descriptor::::from_str(descriptor)?; + Ok(WrapDescriptor(WrapDescriptorEnum::String(desc))) + } + _ => Err(JsError::new("Invalid descriptor type")), + } +} \ No newline at end of file diff --git a/packages/wasm-miniscript/src/error.rs b/packages/wasm-miniscript/src/error.rs new file mode 100644 index 0000000..7e137cd --- /dev/null +++ b/packages/wasm-miniscript/src/error.rs @@ -0,0 +1,36 @@ +use wasm_bindgen::JsValue; +use std::fmt; +use miniscript::bitcoin; + +#[derive(Debug, Clone)] +enum WrapError { + Miniscript(String), + Bitcoin(String), +} + +impl std::error::Error for WrapError {} + +impl fmt::Display for WrapError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + WrapError::Miniscript(e) => write!(f, "Miniscript error: {}", e), + WrapError::Bitcoin(e) => write!(f, "Bitcoin error: {}", e), + } + } +} + +impl From for WrapError { + fn from(e: miniscript::Error) -> Self { + WrapError::Miniscript(e.to_string()) + } +} + +impl From for WrapError { + fn from(e: bitcoin::consensus::encode::Error) -> Self { + WrapError::Bitcoin(e.to_string()) + } +} + +pub fn wrap_err(r: Result) -> Result { + r.map_err(|e| JsValue::from_str(&format!("{:?}", e))) +} \ No newline at end of file diff --git a/packages/wasm-miniscript/src/lib.rs b/packages/wasm-miniscript/src/lib.rs index 848e88d..e6ff0dc 100644 --- a/packages/wasm-miniscript/src/lib.rs +++ b/packages/wasm-miniscript/src/lib.rs @@ -1,263 +1,10 @@ -mod traits; -use crate::traits::TryIntoJsValue; -use miniscript::bitcoin::secp256k1::Secp256k1; -use miniscript::bitcoin::{PublicKey, ScriptBuf}; -use miniscript::descriptor::KeyMap; -use miniscript::{ - bitcoin, bitcoin::XOnlyPublicKey, DefiniteDescriptorKey, Descriptor, DescriptorPublicKey, - Legacy, Miniscript, Segwitv0, Tap, -}; -use std::fmt; -use std::str::FromStr; -use wasm_bindgen::prelude::*; -#[derive(Debug, Clone)] -enum WrapError { - Miniscript(String), - Bitcoin(String), -} +mod try_into_js_value; +mod miniscript; +mod error; +mod descriptor; -impl std::error::Error for WrapError {} -impl fmt::Display for WrapError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - WrapError::Miniscript(e) => write!(f, "Miniscript error: {}", e), - WrapError::Bitcoin(e) => write!(f, "Bitcoin error: {}", e), - } - } -} - -impl From for WrapError { - fn from(e: miniscript::Error) -> Self { - WrapError::Miniscript(e.to_string()) - } -} - -impl From for WrapError { - fn from(e: bitcoin::consensus::encode::Error) -> Self { - WrapError::Bitcoin(e.to_string()) - } -} - -fn wrap_err(r: Result) -> Result { - r.map_err(|e| JsValue::from_str(&format!("{:?}", e))) -} - -enum WrapMiniscriptEnum { - Tap(Miniscript), - Segwit(Miniscript), - Legacy(Miniscript), -} - -// Define the macro to simplify operations on WrapMiniscriptEnum variants -// apply a func to the miniscript variant -macro_rules! unwrap_apply { - ($self:expr, |$ms:ident| $func:expr) => { - match $self { - WrapMiniscriptEnum::Tap($ms) => $func, - WrapMiniscriptEnum::Segwit($ms) => $func, - WrapMiniscriptEnum::Legacy($ms) => $func, - } - }; -} - -#[wasm_bindgen] -pub struct WrapMiniscript(WrapMiniscriptEnum); - -#[wasm_bindgen] -impl WrapMiniscript { - #[wasm_bindgen(js_name = node)] - pub fn node(&self) -> Result { - unwrap_apply!(&self.0, |ms| ms.try_to_js_value()) - } - - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - unwrap_apply!(&self.0, |ms| ms.to_string()) - } - - #[wasm_bindgen(js_name = encode)] - pub fn encode(&self) -> Vec { - unwrap_apply!(&self.0, |ms| ms.encode().into_bytes()) - } - - #[wasm_bindgen(js_name = toAsmString)] - pub fn to_asm_string(&self) -> Result { - unwrap_apply!(&self.0, |ms| Ok(ms.encode().to_asm_string())) - } -} - -impl From> for WrapMiniscript { - fn from(miniscript: Miniscript) -> Self { - WrapMiniscript(WrapMiniscriptEnum::Tap(miniscript)) - } -} - -impl From> for WrapMiniscript { - fn from(miniscript: Miniscript) -> Self { - WrapMiniscript(WrapMiniscriptEnum::Segwit(miniscript)) - } -} - -impl From> for WrapMiniscript { - fn from(miniscript: Miniscript) -> Self { - WrapMiniscript(WrapMiniscriptEnum::Legacy(miniscript)) - } -} - -#[wasm_bindgen] -pub fn miniscript_from_string(script: &str, context_type: &str) -> Result { - match context_type { - "tap" => Ok(WrapMiniscript::from(wrap_err(Miniscript::< - XOnlyPublicKey, - Tap, - >::from_str(script))?)), - "segwitv0" => Ok(WrapMiniscript::from(wrap_err(Miniscript::< - PublicKey, - Segwitv0, - >::from_str(script))?)), - "legacy" => Ok(WrapMiniscript::from(wrap_err(Miniscript::< - PublicKey, - Legacy, - >::from_str(script))?)), - _ => Err(JsValue::from_str("Invalid context type")), - } -} - -#[wasm_bindgen] -pub fn miniscript_from_bitcoin_script( - script: &[u8], - context_type: &str, -) -> Result { - let script = bitcoin::Script::from_bytes(script); - match context_type { - "tap" => Ok(WrapMiniscript::from(wrap_err(Miniscript::< - XOnlyPublicKey, - Tap, - >::parse(script))?)), - "segwitv0" => Ok(WrapMiniscript::from(wrap_err(Miniscript::< - PublicKey, - Segwitv0, - >::parse(script))?)), - "legacy" => Ok(WrapMiniscript::from(wrap_err(Miniscript::< - PublicKey, - Legacy, - >::parse(script))?)), - _ => Err(JsValue::from_str("Invalid context type")), - } -} - -enum WrapDescriptorEnum { - Derivable(Descriptor, KeyMap), - Definite(Descriptor), - String(Descriptor), -} - -#[wasm_bindgen] -pub struct WrapDescriptor(WrapDescriptorEnum); - -#[wasm_bindgen] -impl WrapDescriptor { - pub fn node(&self) -> Result { - Ok(match &self.0 { - WrapDescriptorEnum::Derivable(desc, _) => desc.try_to_js_value()?, - WrapDescriptorEnum::Definite(desc) => desc.try_to_js_value()?, - WrapDescriptorEnum::String(desc) => desc.try_to_js_value()?, - }) - } - - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - match &self.0 { - WrapDescriptorEnum::Derivable(desc, _) => desc.to_string(), - WrapDescriptorEnum::Definite(desc) => desc.to_string(), - WrapDescriptorEnum::String(desc) => desc.to_string(), - } - } - - #[wasm_bindgen(js_name = hasWildcard)] - pub fn has_wildcard(&self) -> bool { - match &self.0 { - WrapDescriptorEnum::Derivable(desc, _) => desc.has_wildcard(), - WrapDescriptorEnum::Definite(_) => false, - WrapDescriptorEnum::String(_) => false, - } - } - - #[wasm_bindgen(js_name = atDerivationIndex)] - pub fn at_derivation_index(&self, index: u32) -> Result { - match &self.0 { - WrapDescriptorEnum::Derivable(desc, _keys) => { - let d = desc.at_derivation_index(index)?; - Ok(WrapDescriptor(WrapDescriptorEnum::Definite(d))) - } - _ => Err(JsError::new("Cannot derive from a definite descriptor")), - } - } - - #[wasm_bindgen(js_name = scriptPubkey)] - pub fn script_pubkey(&self) -> Result, JsError> { - match &self.0 { - WrapDescriptorEnum::Definite(desc) => { - Ok(desc.script_pubkey().to_bytes()) - } - _ => Err(JsError::new("Cannot derive from a non-definite descriptor")), - } - } - - fn explicit_script(&self) -> Result { - match &self.0 { - WrapDescriptorEnum::Definite(desc) => { - Ok(desc.explicit_script()?) - } - WrapDescriptorEnum::Derivable(_, _) => { - Err(JsError::new("Cannot encode a derivable descriptor")) - } - WrapDescriptorEnum::String(_) => Err(JsError::new("Cannot encode a string descriptor")), - } - } - - pub fn encode(&self) -> Result, JsError> { - Ok(self.explicit_script()?.to_bytes()) - } - - #[wasm_bindgen(js_name = toAsmString)] - pub fn to_asm_string(&self) -> Result { - Ok(self.explicit_script()?.to_asm_string()) - } -} - -#[wasm_bindgen] -pub fn descriptor_from_string(descriptor: &str, pk_type: &str) -> Result { - match pk_type { - "derivable" => { - let secp = Secp256k1::new(); - let (desc, keys) = Descriptor::parse_descriptor(&secp, descriptor)?; - Ok(WrapDescriptor(WrapDescriptorEnum::Derivable(desc, keys))) - } - "definite" => { - let desc = Descriptor::::from_str(descriptor)?; - Ok(WrapDescriptor(WrapDescriptorEnum::Definite(desc))) - } - "string" => { - let desc = Descriptor::::from_str(descriptor)?; - Ok(WrapDescriptor(WrapDescriptorEnum::String(desc))) - } - _ => Err(JsError::new("Invalid descriptor type")), - } -} - - -#[test] -pub fn panic_xprv() { - - let (d,m) = Descriptor::parse_descriptor( - &Secp256k1::new(), - "wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", - ).unwrap(); - - let dd = d.at_derivation_index(0).unwrap(); - - let _ = dd.explicit_script().unwrap(); -} +pub use miniscript::miniscript_from_string; +pub use miniscript::miniscript_from_bitcoin_script; +pub use descriptor::descriptor_from_string; \ No newline at end of file diff --git a/packages/wasm-miniscript/src/miniscript.rs b/packages/wasm-miniscript/src/miniscript.rs new file mode 100644 index 0000000..afc80ab --- /dev/null +++ b/packages/wasm-miniscript/src/miniscript.rs @@ -0,0 +1,125 @@ +use std::str::FromStr; +use miniscript::bitcoin::{PublicKey, XOnlyPublicKey}; +use miniscript::{bitcoin, Legacy, Miniscript, Segwitv0, Tap}; + +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::{JsError, JsValue}; + +use crate::try_into_js_value::TryIntoJsValue; + +// Define the macro to simplify operations on WrapMiniscriptEnum variants +// apply a func to the miniscript variant +macro_rules! unwrap_apply { + ($self:expr, |$ms:ident| $func:expr) => { + match $self { + WrapMiniscriptEnum::Tap($ms) => $func, + WrapMiniscriptEnum::Segwit($ms) => $func, + WrapMiniscriptEnum::Legacy($ms) => $func, + } + }; +} + +pub enum WrapMiniscriptEnum { + Tap(Miniscript), + Segwit(Miniscript), + Legacy(Miniscript), +} + +#[wasm_bindgen] +pub struct WrapMiniscript(WrapMiniscriptEnum); + +#[wasm_bindgen] +impl WrapMiniscript { + #[wasm_bindgen(js_name = node)] + pub fn node(&self) -> Result { + unwrap_apply!(&self.0, |ms| ms.try_to_js_value()) + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + unwrap_apply!(&self.0, |ms| ms.to_string()) + } + + #[wasm_bindgen(js_name = encode)] + pub fn encode(&self) -> Vec { + unwrap_apply!(&self.0, |ms| ms.encode().into_bytes()) + } + + #[wasm_bindgen(js_name = toAsmString)] + pub fn to_asm_string(&self) -> Result { + unwrap_apply!(&self.0, |ms| Ok(ms.encode().to_asm_string())) + } +} + +impl From> for WrapMiniscript { + fn from(miniscript: Miniscript) -> Self { + WrapMiniscript(WrapMiniscriptEnum::Tap(miniscript)) + } +} + +impl From> for WrapMiniscript { + fn from(miniscript: Miniscript) -> Self { + WrapMiniscript(WrapMiniscriptEnum::Segwit(miniscript)) + } +} + +impl From> for WrapMiniscript { + fn from(miniscript: Miniscript) -> Self { + WrapMiniscript(WrapMiniscriptEnum::Legacy(miniscript)) + } +} + +#[wasm_bindgen] +pub fn miniscript_from_string(script: &str, context_type: &str) -> Result { + match context_type { + "tap" => Ok(WrapMiniscript::from(crate::error::wrap_err(Miniscript::< + XOnlyPublicKey, + Tap, + >::from_str(script))?)), + "segwitv0" => Ok(WrapMiniscript::from(crate::error::wrap_err(Miniscript::< + PublicKey, + Segwitv0, + >::from_str(script))?)), + "legacy" => Ok(WrapMiniscript::from(crate::error::wrap_err(Miniscript::< + PublicKey, + Legacy, + >::from_str(script))?)), + _ => Err(JsValue::from_str("Invalid context type")), + } +} + +#[wasm_bindgen] +pub fn miniscript_from_bitcoin_script( + script: &[u8], + context_type: &str, +) -> Result { + let script = bitcoin::Script::from_bytes(script); + match context_type { + "tap" => Ok(WrapMiniscript::from(crate::error::wrap_err(Miniscript::< + XOnlyPublicKey, + Tap, + >::parse(script))?)), + "segwitv0" => Ok(WrapMiniscript::from(crate::error::wrap_err(Miniscript::< + PublicKey, + Segwitv0, + >::parse(script))?)), + "legacy" => Ok(WrapMiniscript::from(crate::error::wrap_err(Miniscript::< + PublicKey, + Legacy, + >::parse(script))?)), + _ => Err(JsValue::from_str("Invalid context type")), + } +} + + +#[test] +pub fn panic_xprv() { + let (d,m) = Descriptor::parse_descriptor( + &Secp256k1::new(), + "wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", + ).unwrap(); + + let dd = d.at_derivation_index(0).unwrap(); + + let _ = dd.explicit_script().unwrap(); +} diff --git a/packages/wasm-miniscript/src/traits.rs b/packages/wasm-miniscript/src/try_into_js_value.rs similarity index 100% rename from packages/wasm-miniscript/src/traits.rs rename to packages/wasm-miniscript/src/try_into_js_value.rs