From d7a0d08197c9e8e864d59dd28bfc45189fb75e3c Mon Sep 17 00:00:00 2001 From: Otto Allmendinger Date: Wed, 21 Aug 2024 13:36:52 +0200 Subject: [PATCH 1/2] fix: use correct version of wasm-miniscript in ui Issue: BTC-0 --- package-lock.json | 7 +------ packages/wasm-miniscript-ui/package.json | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 29c611d..4ce0817 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18060,7 +18060,7 @@ "license": "MIT", "dependencies": { "@bitgo/utxo-lib": "^10.1.0", - "@bitgo/wasm-miniscript": "^1.0.0", + "@bitgo/wasm-miniscript": "0.0.0-semantic-release-managed", "assert": "^2.1.0", "buffer": "^6.0.3", "crypto-browserify": "^3.12.0", @@ -18083,11 +18083,6 @@ "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4" } - }, - "packages/wasm-miniscript-ui/node_modules/@bitgo/wasm-miniscript": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@bitgo/wasm-miniscript/-/wasm-miniscript-1.0.1.tgz", - "integrity": "sha512-1odAvjF2rHcZlff6d5alstkB0k/0IRl3uzvm3Y8lvbGJbaZZS6dOmxBbmND6/i49itNOm5XB5oBzsG0l/lDnNw==" } } } diff --git a/packages/wasm-miniscript-ui/package.json b/packages/wasm-miniscript-ui/package.json index b49b5d3..3252ca8 100644 --- a/packages/wasm-miniscript-ui/package.json +++ b/packages/wasm-miniscript-ui/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@bitgo/utxo-lib": "^10.1.0", - "@bitgo/wasm-miniscript": "^1.0.0", + "@bitgo/wasm-miniscript": "0.0.0-semantic-release-managed", "assert": "^2.1.0", "buffer": "^6.0.3", "crypto-browserify": "^3.12.0", From 9b792a78d2643c91f79e614edfddb475f53bc869 Mon Sep 17 00:00:00 2001 From: Otto Allmendinger Date: Wed, 14 Aug 2024 15:56:45 +0200 Subject: [PATCH 2/2] refactor: use static methods for miniscript and descriptor Issue: BTC-1338 --- packages/wasm-miniscript-ui/src/index.ts | 15 ++--- packages/wasm-miniscript-ui/src/sharing.ts | 11 +--- packages/wasm-miniscript/js/index.ts | 56 +++++------------ packages/wasm-miniscript/src/descriptor.rs | 52 ++++++++-------- packages/wasm-miniscript/src/lib.rs | 5 +- packages/wasm-miniscript/src/miniscript.rs | 72 +++++++++++----------- packages/wasm-miniscript/test/test.ts | 14 ++--- 7 files changed, 93 insertions(+), 132 deletions(-) diff --git a/packages/wasm-miniscript-ui/src/index.ts b/packages/wasm-miniscript-ui/src/index.ts index 0af9e5b..158948f 100644 --- a/packages/wasm-miniscript-ui/src/index.ts +++ b/packages/wasm-miniscript-ui/src/index.ts @@ -1,18 +1,11 @@ -import { - descriptorFromString, - miniscriptFromBitcoinScript, - miniscriptFromString, - ScriptContext, -} from "@bitgo/wasm-miniscript"; - import * as utxolib from "@bitgo/utxo-lib"; +import { Descriptor, Miniscript, ScriptContext } from "@bitgo/wasm-miniscript"; import "./style.css"; import { getElement } from "./html"; import { buildOptions, getOptions, Options } from "./options"; import { getHtmlForAst } from "./htmlAST"; -import { Descriptor, Miniscript } from "@bitgo/wasm-miniscript"; import { fromHex, toHex } from "./hex"; import { getShare, setShare, Share } from "./sharing"; @@ -22,7 +15,7 @@ function createMiniscriptFromBitcoinScriptDetectScriptContext( const formats = ["tap", "segwitv0", "legacy"] as const; for (const format of formats) { try { - return [miniscriptFromBitcoinScript(script, format), format]; + return [Miniscript.fromBitcoinScript(script, format), format]; } catch (e) { // ignore } @@ -168,7 +161,7 @@ function applyUpdate(changedEl: HTMLElement, options: Options) { changedEl === elEditDescriptor || changedEl === getElement("input-derivation-index", HTMLInputElement) ) { - const descriptor = descriptorFromString(elEditDescriptor.value, "derivable"); + const descriptor = Descriptor.fromString(elEditDescriptor.value, "derivable"); setHtmlContent(elDescriptorAst, getHtmlForAst(descriptor.node())); const descriptorAtIndex = descriptor.atDerivationIndex(options.derivationIndex); return applyUpdateWith( @@ -189,7 +182,7 @@ function applyUpdate(changedEl: HTMLElement, options: Options) { changedEl === getElement("input-script-context", HTMLSelectElement) ) { try { - const script = miniscriptFromString(elEditMiniscript.value, options.scriptContext); + const script = Miniscript.fromString(elEditMiniscript.value, options.scriptContext); return applyUpdateWith( changedEl, { descriptor: null, miniscript: script, scriptBytes: undefined, scriptAsm: undefined }, diff --git a/packages/wasm-miniscript-ui/src/sharing.ts b/packages/wasm-miniscript-ui/src/sharing.ts index 1f51fe5..434c52d 100644 --- a/packages/wasm-miniscript-ui/src/sharing.ts +++ b/packages/wasm-miniscript-ui/src/sharing.ts @@ -1,10 +1,5 @@ import * as t from "io-ts"; -import { - Descriptor, - descriptorFromString, - Miniscript, - miniscriptFromString, -} from "@bitgo/wasm-miniscript"; +import { Descriptor, Miniscript } from "@bitgo/wasm-miniscript"; import { fromHex, toHex } from "./hex"; import { ScriptContext } from "./codec"; @@ -67,11 +62,11 @@ export function getShare( } if ("d" in v) { - return { descriptor: descriptorFromString(v.d, "derivable") }; + return { descriptor: Descriptor.fromString(v.d, "derivable") }; } if ("ms" in v && "sc" in v) { return { - miniscript: miniscriptFromString(v.ms, v.sc), + miniscript: Miniscript.fromString(v.ms, v.sc), scriptContext: v.sc, }; } diff --git a/packages/wasm-miniscript/js/index.ts b/packages/wasm-miniscript/js/index.ts index fb05739..1403456 100644 --- a/packages/wasm-miniscript/js/index.ts +++ b/packages/wasm-miniscript/js/index.ts @@ -4,50 +4,28 @@ import * as wasm from "./wasm/wasm_miniscript"; // and forgets to include it in the bundle void wasm; -export type MiniscriptNode = unknown; - -export type Miniscript = { - node(): MiniscriptNode; - toString(): string; - encode(): Uint8Array; - toAsmString(): string; -}; - -export function isMiniscript(obj: unknown): obj is Miniscript { - return obj instanceof wasm.WrapMiniscript; -} +export type DescriptorPkType = "derivable" | "definite" | "string"; export type ScriptContext = "tap" | "segwitv0" | "legacy"; -export function miniscriptFromString(script: string, scriptContext: ScriptContext): Miniscript { - return wasm.miniscript_from_string(script, scriptContext); -} - -export function miniscriptFromBitcoinScript( - script: Uint8Array, - scriptContext: ScriptContext, -): Miniscript { - return wasm.miniscript_from_bitcoin_script(script, scriptContext); -} +declare module "./wasm/wasm_miniscript" { + interface WrapDescriptor { + node(): unknown; + } -export type DescriptorNode = unknown; + namespace WrapDescriptor { + function fromString(descriptor: string, pkType: DescriptorPkType): WrapDescriptor; + } -export type Descriptor = { - node(): DescriptorNode; - toString(): string; - hasWildcard(): boolean; - atDerivationIndex(index: number): Descriptor; - encode(): Uint8Array; - toAsmString(): string; - scriptPubkey(): Uint8Array; -}; + interface WrapMiniscript { + node(): unknown; + } -export function isDescriptor(obj: unknown): obj is Descriptor { - return obj instanceof wasm.WrapDescriptor; + namespace WrapMiniscript { + function fromString(miniscript: string, ctx: ScriptContext): WrapMiniscript; + function fromBitcoinScript(script: Uint8Array, ctx: ScriptContext): WrapMiniscript; + } } -type DescriptorPkType = "derivable" | "definite" | "string"; - -export function descriptorFromString(descriptor: string, pkType: DescriptorPkType): Descriptor { - return wasm.descriptor_from_string(descriptor, pkType); -} +export { WrapDescriptor as Descriptor } from "./wasm/wasm_miniscript"; +export { WrapMiniscript as Miniscript } from "./wasm/wasm_miniscript"; diff --git a/packages/wasm-miniscript/src/descriptor.rs b/packages/wasm-miniscript/src/descriptor.rs index cc9c995..74c770f 100644 --- a/packages/wasm-miniscript/src/descriptor.rs +++ b/packages/wasm-miniscript/src/descriptor.rs @@ -1,11 +1,11 @@ -use std::str::FromStr; -use miniscript::{DefiniteDescriptorKey, Descriptor, DescriptorPublicKey}; -use miniscript::bitcoin::ScriptBuf; +use crate::try_into_js_value::TryIntoJsValue; use miniscript::bitcoin::secp256k1::Secp256k1; +use miniscript::bitcoin::ScriptBuf; use miniscript::descriptor::KeyMap; +use miniscript::{DefiniteDescriptorKey, Descriptor, DescriptorPublicKey}; +use std::str::FromStr; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::{JsError, JsValue}; -use crate::try_into_js_value::TryIntoJsValue; enum WrapDescriptorEnum { Derivable(Descriptor, KeyMap), @@ -58,18 +58,14 @@ impl WrapDescriptor { #[wasm_bindgen(js_name = scriptPubkey)] pub fn script_pubkey(&self) -> Result, JsError> { match &self.0 { - WrapDescriptorEnum::Definite(desc) => { - Ok(desc.script_pubkey().to_bytes()) - } + 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::Definite(desc) => Ok(desc.explicit_script()?), WrapDescriptorEnum::Derivable(_, _) => { Err(JsError::new("Cannot encode a derivable descriptor")) } @@ -85,24 +81,24 @@ impl WrapDescriptor { 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))) + #[wasm_bindgen(js_name = fromString, skip_typescript)] + pub fn 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")), } - _ => Err(JsError::new("Invalid descriptor type")), } -} \ No newline at end of file +} diff --git a/packages/wasm-miniscript/src/lib.rs b/packages/wasm-miniscript/src/lib.rs index e6ff0dc..dc7ca4f 100644 --- a/packages/wasm-miniscript/src/lib.rs +++ b/packages/wasm-miniscript/src/lib.rs @@ -5,6 +5,5 @@ mod miniscript; mod error; mod descriptor; -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 +pub use miniscript::WrapMiniscript; +pub use descriptor::WrapDescriptor; \ No newline at end of file diff --git a/packages/wasm-miniscript/src/miniscript.rs b/packages/wasm-miniscript/src/miniscript.rs index 79122c0..d58e8eb 100644 --- a/packages/wasm-miniscript/src/miniscript.rs +++ b/packages/wasm-miniscript/src/miniscript.rs @@ -48,6 +48,42 @@ impl WrapMiniscript { pub fn to_asm_string(&self) -> Result { unwrap_apply!(&self.0, |ms| Ok(ms.encode().to_asm_string())) } + + #[wasm_bindgen(js_name = fromString, skip_typescript)] + pub fn from_string(script: &str, context_type: &str) -> Result { + match context_type { + "tap" => Ok(WrapMiniscript::from( + Miniscript::::from_str(script).map_err(JsError::from)?, + )), + "segwitv0" => Ok(WrapMiniscript::from( + Miniscript::::from_str(script).map_err(JsError::from)?, + )), + "legacy" => Ok(WrapMiniscript::from( + Miniscript::::from_str(script).map_err(JsError::from)?, + )), + _ => Err(JsError::new("Invalid context type")), + } + } + + #[wasm_bindgen(js_name = fromBitcoinScript, skip_typescript)] + pub fn from_bitcoin_script( + script: &[u8], + context_type: &str, + ) -> Result { + let script = bitcoin::Script::from_bytes(script); + match context_type { + "tap" => Ok(WrapMiniscript::from( + Miniscript::::parse(script).map_err(JsError::from)?, + )), + "segwitv0" => Ok(WrapMiniscript::from( + Miniscript::::parse(script).map_err(JsError::from)?, + )), + "legacy" => Ok(WrapMiniscript::from( + Miniscript::::parse(script).map_err(JsError::from)?, + )), + _ => Err(JsError::new("Invalid context type")), + } + } } impl From> for WrapMiniscript { @@ -68,42 +104,6 @@ impl From> for WrapMiniscript { } } -#[wasm_bindgen] -pub fn miniscript_from_string(script: &str, context_type: &str) -> Result { - match context_type { - "tap" => Ok(WrapMiniscript::from( - Miniscript::::from_str(script).map_err(JsError::from)?, - )), - "segwitv0" => Ok(WrapMiniscript::from( - Miniscript::::from_str(script).map_err(JsError::from)?, - )), - "legacy" => Ok(WrapMiniscript::from( - Miniscript::::from_str(script).map_err(JsError::from)?, - )), - _ => Err(JsError::new("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( - Miniscript::::parse(script).map_err(JsError::from)?, - )), - "segwitv0" => Ok(WrapMiniscript::from( - Miniscript::::parse(script).map_err(JsError::from)?, - )), - "legacy" => Ok(WrapMiniscript::from( - Miniscript::::parse(script).map_err(JsError::from)?, - )), - _ => Err(JsError::new("Invalid context type")), - } -} - #[test] pub fn panic_xprv() { use miniscript::bitcoin::secp256k1::Secp256k1; diff --git a/packages/wasm-miniscript/test/test.ts b/packages/wasm-miniscript/test/test.ts index f611cf0..db21f23 100644 --- a/packages/wasm-miniscript/test/test.ts +++ b/packages/wasm-miniscript/test/test.ts @@ -1,14 +1,14 @@ import * as assert from "assert"; -import { descriptorFromString, miniscriptFromString, miniscriptFromBitcoinScript } from "../js"; +import { Miniscript, Descriptor } from "../js"; import { fixtures } from "./descriptorFixtures"; describe("AST", function () { it("should get ast", function () { const pubkey = Buffer.alloc(32, 1).toString("hex"); - const result = miniscriptFromString(`multi_a(1,${pubkey})`, "tap"); + const result = Miniscript.fromString(`multi_a(1,${pubkey})`, "tap"); console.dir(result.node(), { depth: null }); console.dir(result.encode(), { depth: null }); - console.dir(miniscriptFromBitcoinScript(result.encode(), "tap").toString()); + console.dir(Miniscript.fromBitcoinScript(result.encode(), "tap").toString()); }); }); @@ -20,7 +20,7 @@ function removeChecksum(descriptor: string): string { describe("Descriptor fixtures", function () { fixtures.valid.forEach((fixture, i) => { it("should parse fixture " + i, function () { - const descriptor = descriptorFromString(fixture.descriptor, "string"); + const descriptor = Descriptor.fromString(fixture.descriptor, "string"); assert.doesNotThrow(() => descriptor.node()); let descriptorString = descriptor.toString(); if (fixture.checksumRequired === false) { @@ -34,7 +34,7 @@ describe("Descriptor fixtures", function () { assert.strictEqual(descriptorString, expected); assert.doesNotThrow(() => - descriptorFromString(fixture.descriptor, "derivable").atDerivationIndex(0), + Descriptor.fromString(fixture.descriptor, "derivable").atDerivationIndex(0), ); const nonDerivable = [33, 34, 35, 41, 42, 43]; @@ -43,14 +43,14 @@ describe("Descriptor fixtures", function () { console.log("Skipping encoding test for fixture", fixture.descriptor, i); } else { assert.doesNotThrow(() => - descriptorFromString(fixture.descriptor, "derivable").atDerivationIndex(0).encode(), + Descriptor.fromString(fixture.descriptor, "derivable").atDerivationIndex(0).encode(), ); let descriptorString = fixture.descriptor; if (fixture.checksumRequired === false) { descriptorString = removeChecksum(descriptorString); } - const descriptor = descriptorFromString(descriptorString, "derivable"); + const descriptor = Descriptor.fromString(descriptorString, "derivable"); assert.strictEqual( Buffer.from(descriptor.atDerivationIndex(fixture.index ?? 0).scriptPubkey()).toString( "hex",