Skip to content

Commit 33456b8

Browse files
feat(miniscript): add custom error type for wasm-miniscript
Replaces JsError with WasmMiniscriptError. This allows writing tests for funcs that return `Result<_, WasmMiniscriptError>`. Previously it was a pain because `JsError` did not implement Debug. Issue: BTC-1826
1 parent 5d2b3c5 commit 33456b8

File tree

7 files changed

+172
-84
lines changed

7 files changed

+172
-84
lines changed
Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
use crate::error::WasmMiniscriptError;
12
use crate::try_into_js_value::TryIntoJsValue;
23
use miniscript::bitcoin::secp256k1::Secp256k1;
34
use miniscript::bitcoin::ScriptBuf;
45
use miniscript::descriptor::KeyMap;
56
use miniscript::{DefiniteDescriptorKey, Descriptor, DescriptorPublicKey};
67
use std::str::FromStr;
7-
use wasm_bindgen::prelude::wasm_bindgen;
8-
use wasm_bindgen::{JsError, JsValue};
8+
use wasm_bindgen::prelude::*;
99

1010
pub(crate) enum WrapDescriptorEnum {
1111
Derivable(Descriptor<DescriptorPublicKey>, KeyMap),
@@ -18,7 +18,7 @@ pub struct WrapDescriptor(pub(crate) WrapDescriptorEnum);
1818

1919
#[wasm_bindgen]
2020
impl WrapDescriptor {
21-
pub fn node(&self) -> Result<JsValue, JsError> {
21+
pub fn node(&self) -> Result<JsValue, WasmMiniscriptError> {
2222
Ok(match &self.0 {
2323
WrapDescriptorEnum::Derivable(desc, _) => desc.try_to_js_value()?,
2424
WrapDescriptorEnum::Definite(desc) => desc.try_to_js_value()?,
@@ -45,18 +45,20 @@ impl WrapDescriptor {
4545
}
4646

4747
#[wasm_bindgen(js_name = atDerivationIndex)]
48-
pub fn at_derivation_index(&self, index: u32) -> Result<WrapDescriptor, JsError> {
48+
pub fn at_derivation_index(&self, index: u32) -> Result<WrapDescriptor, WasmMiniscriptError> {
4949
match &self.0 {
5050
WrapDescriptorEnum::Derivable(desc, _keys) => {
5151
let d = desc.at_derivation_index(index)?;
5252
Ok(WrapDescriptor(WrapDescriptorEnum::Definite(d)))
5353
}
54-
_ => Err(JsError::new("Cannot derive from a definite descriptor")),
54+
_ => Err(WasmMiniscriptError::new(
55+
"Cannot derive from a definite descriptor",
56+
)),
5557
}
5658
}
5759

5860
#[wasm_bindgen(js_name = descType)]
59-
pub fn desc_type(&self) -> Result<JsValue, JsError> {
61+
pub fn desc_type(&self) -> Result<JsValue, WasmMiniscriptError> {
6062
(match &self.0 {
6163
WrapDescriptorEnum::Derivable(desc, _) => desc.desc_type(),
6264
WrapDescriptorEnum::Definite(desc) => desc.desc_type(),
@@ -66,34 +68,36 @@ impl WrapDescriptor {
6668
}
6769

6870
#[wasm_bindgen(js_name = scriptPubkey)]
69-
pub fn script_pubkey(&self) -> Result<Vec<u8>, JsError> {
71+
pub fn script_pubkey(&self) -> Result<Vec<u8>, WasmMiniscriptError> {
7072
match &self.0 {
7173
WrapDescriptorEnum::Definite(desc) => Ok(desc.script_pubkey().to_bytes()),
72-
_ => Err(JsError::new("Cannot derive from a non-definite descriptor")),
74+
_ => Err(WasmMiniscriptError::new("Cannot derive from a non-definite descriptor")),
7375
}
7476
}
7577

76-
fn explicit_script(&self) -> Result<ScriptBuf, JsError> {
78+
fn explicit_script(&self) -> Result<ScriptBuf, WasmMiniscriptError> {
7779
match &self.0 {
7880
WrapDescriptorEnum::Definite(desc) => Ok(desc.explicit_script()?),
79-
WrapDescriptorEnum::Derivable(_, _) => {
80-
Err(JsError::new("Cannot encode a derivable descriptor"))
81-
}
82-
WrapDescriptorEnum::String(_) => Err(JsError::new("Cannot encode a string descriptor")),
81+
WrapDescriptorEnum::Derivable(_, _) => Err(WasmMiniscriptError::new(
82+
"Cannot encode a derivable descriptor",
83+
)),
84+
WrapDescriptorEnum::String(_) => Err(WasmMiniscriptError::new(
85+
"Cannot encode a string descriptor",
86+
)),
8387
}
8488
}
8589

86-
pub fn encode(&self) -> Result<Vec<u8>, JsError> {
90+
pub fn encode(&self) -> Result<Vec<u8>, WasmMiniscriptError> {
8791
Ok(self.explicit_script()?.to_bytes())
8892
}
8993

9094
#[wasm_bindgen(js_name = toAsmString)]
91-
pub fn to_asm_string(&self) -> Result<String, JsError> {
95+
pub fn to_asm_string(&self) -> Result<String, WasmMiniscriptError> {
9296
Ok(self.explicit_script()?.to_asm_string())
9397
}
9498

9599
#[wasm_bindgen(js_name = maxWeightToSatisfy)]
96-
pub fn max_weight_to_satisfy(&self) -> Result<u32, JsError> {
100+
pub fn max_weight_to_satisfy(&self) -> Result<u32, WasmMiniscriptError> {
97101
let weight = (match &self.0 {
98102
WrapDescriptorEnum::Derivable(desc, _) => desc.max_weight_to_satisfy(),
99103
WrapDescriptorEnum::Definite(desc) => desc.max_weight_to_satisfy(),
@@ -102,26 +106,33 @@ impl WrapDescriptor {
102106
weight
103107
.to_wu()
104108
.try_into()
105-
.map_err(|_| JsError::new("Weight exceeds u32"))
109+
.map_err(|_| WasmMiniscriptError::new("Weight exceeds u32"))
110+
}
111+
112+
fn from_string_derivable(descriptor: &str) -> Result<WrapDescriptor, WasmMiniscriptError> {
113+
let secp = Secp256k1::new();
114+
let (desc, keys) = Descriptor::parse_descriptor(&secp, descriptor)?;
115+
Ok(WrapDescriptor(WrapDescriptorEnum::Derivable(desc, keys)))
116+
}
117+
118+
fn from_string_definite(descriptor: &str) -> Result<WrapDescriptor, WasmMiniscriptError> {
119+
let desc = Descriptor::<DefiniteDescriptorKey>::from_str(descriptor)?;
120+
Ok(WrapDescriptor(WrapDescriptorEnum::Definite(desc)))
106121
}
107122

108123
#[wasm_bindgen(js_name = fromString, skip_typescript)]
109-
pub fn from_string(descriptor: &str, pk_type: &str) -> Result<WrapDescriptor, JsError> {
124+
pub fn from_string(
125+
descriptor: &str,
126+
pk_type: &str,
127+
) -> Result<WrapDescriptor, WasmMiniscriptError> {
110128
match pk_type {
111-
"derivable" => {
112-
let secp = Secp256k1::new();
113-
let (desc, keys) = Descriptor::parse_descriptor(&secp, descriptor)?;
114-
Ok(WrapDescriptor(WrapDescriptorEnum::Derivable(desc, keys)))
115-
}
116-
"definite" => {
117-
let desc = Descriptor::<DefiniteDescriptorKey>::from_str(descriptor)?;
118-
Ok(WrapDescriptor(WrapDescriptorEnum::Definite(desc)))
119-
}
129+
"derivable" => WrapDescriptor::from_string_derivable(descriptor),
130+
"definite" => WrapDescriptor::from_string_definite(descriptor),
120131
"string" => {
121132
let desc = Descriptor::<String>::from_str(descriptor)?;
122133
Ok(WrapDescriptor(WrapDescriptorEnum::String(desc)))
123134
}
124-
_ => Err(JsError::new("Invalid descriptor type")),
135+
_ => Err(WasmMiniscriptError::new("Invalid descriptor type")),
125136
}
126137
}
127-
}
138+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use core::fmt;
2+
3+
#[derive(Debug, Clone)]
4+
pub enum WasmMiniscriptError {
5+
StringError(String),
6+
}
7+
8+
impl std::error::Error for WasmMiniscriptError {}
9+
impl fmt::Display for WasmMiniscriptError {
10+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11+
match self {
12+
WasmMiniscriptError::StringError(s) => write!(f, "{}", s),
13+
}
14+
}
15+
}
16+
17+
impl From<&str> for WasmMiniscriptError {
18+
fn from(s: &str) -> Self {
19+
WasmMiniscriptError::StringError(s.to_string())
20+
}
21+
}
22+
23+
impl From<String> for WasmMiniscriptError {
24+
fn from(s: String) -> Self {
25+
WasmMiniscriptError::StringError(s)
26+
}
27+
}
28+
29+
impl From<miniscript::Error> for WasmMiniscriptError {
30+
fn from(err: miniscript::Error) -> Self {
31+
WasmMiniscriptError::StringError(err.to_string())
32+
}
33+
}
34+
35+
impl From<miniscript::descriptor::ConversionError> for WasmMiniscriptError {
36+
fn from(err: miniscript::descriptor::ConversionError) -> Self {
37+
WasmMiniscriptError::StringError(err.to_string())
38+
}
39+
}
40+
41+
impl WasmMiniscriptError {
42+
pub fn new(s: &str) -> WasmMiniscriptError {
43+
WasmMiniscriptError::StringError(s.to_string())
44+
}
45+
}

packages/wasm-miniscript/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod descriptor;
2+
mod error;
23
mod miniscript;
34
mod psbt;
45
mod try_into_js_value;

packages/wasm-miniscript/src/miniscript.rs

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
use crate::error::WasmMiniscriptError;
2+
use crate::try_into_js_value::TryIntoJsValue;
13
use miniscript::bitcoin::{PublicKey, XOnlyPublicKey};
24
use miniscript::{bitcoin, Legacy, Miniscript, Segwitv0, Tap};
35
use std::str::FromStr;
46
use wasm_bindgen::prelude::wasm_bindgen;
5-
use wasm_bindgen::{JsError, JsValue};
6-
7-
use crate::try_into_js_value::TryIntoJsValue;
7+
use wasm_bindgen::JsValue;
88

99
// Define the macro to simplify operations on WrapMiniscriptEnum variants
1010
// apply a func to the miniscript variant
@@ -30,7 +30,7 @@ pub struct WrapMiniscript(WrapMiniscriptEnum);
3030
#[wasm_bindgen]
3131
impl WrapMiniscript {
3232
#[wasm_bindgen(js_name = node)]
33-
pub fn node(&self) -> Result<JsValue, JsError> {
33+
pub fn node(&self) -> Result<JsValue, WasmMiniscriptError> {
3434
unwrap_apply!(&self.0, |ms| ms.try_to_js_value())
3535
}
3636

@@ -45,43 +45,52 @@ impl WrapMiniscript {
4545
}
4646

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

5252
#[wasm_bindgen(js_name = fromString, skip_typescript)]
53-
pub fn from_string(script: &str, context_type: &str) -> Result<WrapMiniscript, JsError> {
53+
pub fn from_string(
54+
script: &str,
55+
context_type: &str,
56+
) -> Result<WrapMiniscript, WasmMiniscriptError> {
5457
match context_type {
5558
"tap" => Ok(WrapMiniscript::from(
56-
Miniscript::<XOnlyPublicKey, Tap>::from_str(script).map_err(JsError::from)?,
59+
Miniscript::<XOnlyPublicKey, Tap>::from_str(script)
60+
.map_err(WasmMiniscriptError::from)?,
5761
)),
5862
"segwitv0" => Ok(WrapMiniscript::from(
59-
Miniscript::<PublicKey, Segwitv0>::from_str(script).map_err(JsError::from)?,
63+
Miniscript::<PublicKey, Segwitv0>::from_str(script)
64+
.map_err(WasmMiniscriptError::from)?,
6065
)),
6166
"legacy" => Ok(WrapMiniscript::from(
62-
Miniscript::<PublicKey, Legacy>::from_str(script).map_err(JsError::from)?,
67+
Miniscript::<PublicKey, Legacy>::from_str(script)
68+
.map_err(WasmMiniscriptError::from)?,
6369
)),
64-
_ => Err(JsError::new("Invalid context type")),
70+
_ => Err(WasmMiniscriptError::new("Invalid context type")),
6571
}
6672
}
6773

6874
#[wasm_bindgen(js_name = fromBitcoinScript, skip_typescript)]
6975
pub fn from_bitcoin_script(
7076
script: &[u8],
7177
context_type: &str,
72-
) -> Result<WrapMiniscript, JsError> {
78+
) -> Result<WrapMiniscript, WasmMiniscriptError> {
7379
let script = bitcoin::Script::from_bytes(script);
7480
match context_type {
7581
"tap" => Ok(WrapMiniscript::from(
76-
Miniscript::<XOnlyPublicKey, Tap>::parse(script).map_err(JsError::from)?,
82+
Miniscript::<XOnlyPublicKey, Tap>::parse(script)
83+
.map_err(WasmMiniscriptError::from)?,
7784
)),
7885
"segwitv0" => Ok(WrapMiniscript::from(
79-
Miniscript::<PublicKey, Segwitv0>::parse(script).map_err(JsError::from)?,
86+
Miniscript::<PublicKey, Segwitv0>::parse(script)
87+
.map_err(WasmMiniscriptError::from)?,
8088
)),
8189
"legacy" => Ok(WrapMiniscript::from(
82-
Miniscript::<PublicKey, Legacy>::parse(script).map_err(JsError::from)?,
90+
Miniscript::<PublicKey, Legacy>::parse(script)
91+
.map_err(WasmMiniscriptError::from)?,
8392
)),
84-
_ => Err(JsError::new("Invalid context type")),
93+
_ => Err(WasmMiniscriptError::new("Invalid context type")),
8594
}
8695
}
8796
}

packages/wasm-miniscript/src/psbt.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::descriptor::WrapDescriptorEnum;
2+
use crate::error::WasmMiniscriptError;
23
use crate::try_into_js_value::TryIntoJsValue;
34
use crate::WrapDescriptor;
45
use miniscript::bitcoin::secp256k1::{Context, Secp256k1, Signing};
@@ -130,30 +131,37 @@ impl WrapPsbt {
130131
}
131132

132133
#[wasm_bindgen(js_name = signWithXprv)]
133-
pub fn sign_with_xprv(&mut self, xprv: String) -> Result<JsValue, JsError> {
134-
let key = bip32::Xpriv::from_str(&xprv).map_err(|_| JsError::new("Invalid xprv"))?;
134+
pub fn sign_with_xprv(&mut self, xprv: String) -> Result<JsValue, WasmMiniscriptError> {
135+
let key =
136+
bip32::Xpriv::from_str(&xprv).map_err(|_| WasmMiniscriptError::new("Invalid xprv"))?;
135137
self.0
136138
.sign(&key, &Secp256k1::new())
137-
.map_err(|(_, errors)| JsError::new(&format!("{} errors: {:?}", errors.len(), errors)))
139+
.map_err(|(_, errors)| {
140+
WasmMiniscriptError::new(&format!("{} errors: {:?}", errors.len(), errors))
141+
})
138142
.and_then(|r| r.try_to_js_value())
139143
}
140144

141145
#[wasm_bindgen(js_name = signWithPrv)]
142-
pub fn sign_with_prv(&mut self, prv: Vec<u8>) -> Result<JsValue, JsError> {
146+
pub fn sign_with_prv(&mut self, prv: Vec<u8>) -> Result<JsValue, WasmMiniscriptError> {
143147
let privkey = PrivateKey::from_slice(&prv, miniscript::bitcoin::network::Network::Bitcoin)
144-
.map_err(|_| JsError::new("Invalid private key"))?;
148+
.map_err(|_| WasmMiniscriptError::new("Invalid private key"))?;
145149
let secp = Secp256k1::new();
146150
self.0
147151
.sign(&SingleKeySigner::from_privkey(privkey, &secp), &secp)
148-
.map_err(|(r, errors)| JsError::new(&format!("{} errors: {:?}", errors.len(), errors)))
152+
.map_err(|(r, errors)| {
153+
WasmMiniscriptError::new(&format!("{} errors: {:?}", errors.len(), errors))
154+
})
149155
.and_then(|r| r.try_to_js_value())
150156
}
151157

152158
#[wasm_bindgen(js_name = finalize)]
153-
pub fn finalize_mut(&mut self) -> Result<(), JsError> {
159+
pub fn finalize_mut(&mut self) -> Result<(), WasmMiniscriptError> {
154160
self.0
155161
.finalize_mut(&Secp256k1::verification_only())
156-
.map_err(|vec_err| JsError::new(&format!("{} errors: {:?}", vec_err.len(), vec_err)))
162+
.map_err(|vec_err| {
163+
WasmMiniscriptError::new(&format!("{} errors: {:?}", vec_err.len(), vec_err))
164+
})
157165
}
158166
}
159167

0 commit comments

Comments
 (0)