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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules/
.idea/
*.iml
*.tsbuildinfo
4 changes: 2 additions & 2 deletions packages/wasm-miniscript/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/wasm-miniscript/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
miniscript = "12"
miniscript = { version = "12.1.0", features = ["no-std"] }
33 changes: 26 additions & 7 deletions packages/wasm-miniscript/js/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import * as wasm from "./wasm/wasm_miniscript";

type MiniscriptNode = unknown;
// we need to access the wasm module here, otherwise webpack gets all weird
// and forgets to include it in the bundle
void wasm;

type Miniscript = {
export type MiniscriptNode = unknown;

export type Miniscript = {
node(): MiniscriptNode;
toString(): string;
encode(): Uint8Array;
toAsmString(): string;
};

type ScriptContext = "tap" | "segwitv0" | "legacy";
export function isMiniscript(obj: unknown): obj is Miniscript {
return obj instanceof wasm.WrapMiniscript;
}

export type ScriptContext = "tap" | "segwitv0" | "legacy";

export function miniscriptFromString(script: string, scriptContext: ScriptContext): Miniscript {
return wasm.miniscript_from_string(script, scriptContext);
Expand All @@ -21,13 +30,23 @@ export function miniscriptFromBitcoinScript(
return wasm.miniscript_from_bitcoin_script(script, scriptContext);
}

type DescriptorNode = unknown;
export type DescriptorNode = unknown;

type Descriptor = {
export type Descriptor = {
node(): DescriptorNode;
toString(): string;
hasWildcard(): boolean;
atDerivationIndex(index: number): Descriptor;
encode(): Uint8Array;
toAsmString(): string;
};

export function descriptorFromString(descriptor: string): Descriptor {
return wasm.descriptor_from_string(descriptor);
export function isDescriptor(obj: unknown): obj is Descriptor {
return obj instanceof wasm.WrapDescriptor;
}

type DescriptorPkType = "derivable" | "definite" | "string";

export function descriptorFromString(descriptor: string, pkType: DescriptorPkType): Descriptor {
return wasm.descriptor_from_string(descriptor, pkType);
}
184 changes: 163 additions & 21 deletions packages/wasm-miniscript/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,44 @@
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 miniscript::{bitcoin, bitcoin::XOnlyPublicKey, Descriptor, Legacy, Miniscript, Segwitv0, Tap};
use miniscript::bitcoin::PublicKey;
use wasm_bindgen::prelude::*;
use crate::traits::TryIntoJsValue;

#[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<miniscript::Error> for WrapError {
fn from(e: miniscript::Error) -> Self {
WrapError::Miniscript(e.to_string())
}
}

impl From<bitcoin::consensus::encode::Error> for WrapError {
fn from(e: bitcoin::consensus::encode::Error) -> Self {
WrapError::Bitcoin(e.to_string())
}
}

fn wrap_err<T, E: std::fmt::Debug>(r: Result<T, E>) -> Result<T, JsValue> {
r.map_err(|e| JsValue::from_str(&format!("{:?}", e)))
Expand Down Expand Up @@ -34,7 +68,7 @@ pub struct WrapMiniscript(WrapMiniscriptEnum);
#[wasm_bindgen]
impl WrapMiniscript {
#[wasm_bindgen(js_name = node)]
pub fn node(&self) -> Result<JsValue, JsValue> {
pub fn node(&self) -> Result<JsValue, JsError> {
unwrap_apply!(&self.0, |ms| ms.try_to_js_value())
}

Expand All @@ -47,6 +81,11 @@ impl WrapMiniscript {
pub fn encode(&self) -> Vec<u8> {
unwrap_apply!(&self.0, |ms| ms.encode().into_bytes())
}

#[wasm_bindgen(js_name = toAsmString)]
pub fn to_asm_string(&self) -> Result<String, JsError> {
unwrap_apply!(&self.0, |ms| Ok(ms.encode().to_asm_string()))
}
}

impl From<Miniscript<XOnlyPublicKey, Tap>> for WrapMiniscript {
Expand All @@ -70,42 +109,145 @@ impl From<Miniscript<PublicKey, Legacy>> for WrapMiniscript {
#[wasm_bindgen]
pub fn miniscript_from_string(script: &str, context_type: &str) -> Result<WrapMiniscript, JsValue> {
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"))
"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<WrapMiniscript, JsValue> {
pub fn miniscript_from_bitcoin_script(
script: &[u8],
context_type: &str,
) -> Result<WrapMiniscript, JsValue> {
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"))
"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<DescriptorPublicKey>, KeyMap),
Definite(Descriptor<DefiniteDescriptorKey>),
String(Descriptor<String>),
}

#[wasm_bindgen]
pub struct WrapDescriptor(Descriptor<String>);
pub struct WrapDescriptor(WrapDescriptorEnum);

#[wasm_bindgen]
impl WrapDescriptor {
pub fn node(&self) -> Result<JsValue, JsValue> {
self.0.try_to_js_value()
pub fn node(&self) -> Result<JsValue, JsError> {
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 {
self.0.to_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<WrapDescriptor, JsError> {
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")),
}
}

fn explicit_script(&self) -> Result<ScriptBuf, JsError> {
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<Vec<u8>, JsError> {
Ok(self.explicit_script()?.to_bytes())
}

#[wasm_bindgen(js_name = toAsmString)]
pub fn to_asm_string(&self) -> Result<String, JsError> {
Ok(self.explicit_script()?.to_asm_string())
}
}

#[wasm_bindgen]
pub fn descriptor_from_string(descriptor: &str) -> Result<WrapDescriptor, JsValue> {
Ok(
WrapDescriptor(Descriptor::<String>::from_str(descriptor)
.map_err(|e| js_sys::Error::new(&format!("{:?}", e)))?))
pub fn descriptor_from_string(descriptor: &str, pk_type: &str) -> Result<WrapDescriptor, JsError> {
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::<DefiniteDescriptorKey>::from_str(descriptor)?;
Ok(WrapDescriptor(WrapDescriptorEnum::Definite(desc)))
}
"string" => {
let desc = Descriptor::<String>::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();
}
Loading