Skip to content

Commit b8f2dbc

Browse files
Merge remote-tracking branch 'origin/master' into beta
2 parents f6d0330 + 3f87455 commit b8f2dbc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+2109
-101
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
js/wasm
2-
target/
2+
test/fixtures/
3+
target/

packages/wasm-miniscript/Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/wasm-miniscript/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,8 @@ crate-type = ["cdylib"]
1010
wasm-bindgen = "0.2"
1111
js-sys = "0.3"
1212
miniscript = { git = "https://github.com/BitGo/rust-miniscript", branch = "opdrop" }
13+
14+
[profile.release]
15+
# this is required to make webpack happy
16+
# https://github.com/webpack/webpack/issues/15566#issuecomment-2558347645
17+
strip = true

packages/wasm-miniscript/Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ endef
3030
js/wasm/:
3131
$(call BUILD,$@,nodejs)
3232

33-
.PHONY: dist/wasm/
34-
dist/wasm/:
33+
.PHONY: dist/node/js/wasm/
34+
dist/node/js/wasm/:
3535
$(call BUILD,$@,nodejs)
3636

37-
.PHONY: dist/browser/wasm/
38-
dist/browser/wasm/:
37+
.PHONY: dist/browser/js/wasm/
38+
dist/browser/js/wasm/:
3939
$(call BUILD,$@,browser)
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
3+
This file contains type definitions for building an Abstract Syntax Tree for
4+
Bitcoin Descriptors and Miniscript expressions.
5+
6+
Currently, the types do not encode any validity or soundness checks, so it is
7+
possible to construct invalid descriptors.
8+
9+
*/
10+
type Key = string;
11+
12+
// https://bitcoin.sipa.be/miniscript/
13+
// r is for custom bitgo extension OP_DROP
14+
type Identities = "a" | "s" | "c" | "t" | "d" | "v" | "j" | "n" | "l" | "u" | "r";
15+
16+
// Union of all possible prefixes: { f: T } => { 'a:f': T } | { 's:f': T } | ...
17+
type PrefixWith<T, P extends string> = {
18+
[K in keyof T & string as `${P}:${K}`]: T[K];
19+
};
20+
type PrefixIdUnion<T> = { [P in Identities]: PrefixWith<T, P> }[Identities];
21+
22+
// Wrap a type with a union of all possible prefixes
23+
type Wrap<T> = T | PrefixIdUnion<T>;
24+
25+
type Miniscript =
26+
| Wrap<{ pk: Key }>
27+
| Wrap<{ pkh: Key }>
28+
| Wrap<{ wpkh: Key }>
29+
| Wrap<{ multi: [number, ...Key[]] }>
30+
| Wrap<{ sortedmulti: [number, ...Key[]] }>
31+
| Wrap<{ multi_a: [number, ...Key[]] }>
32+
| Wrap<{ sortedmulti_a: [number, ...Key[]] }>
33+
| Wrap<{ tr: Key | [Key, Miniscript] }>
34+
| Wrap<{ sh: Miniscript }>
35+
| Wrap<{ wsh: Miniscript }>
36+
| Wrap<{ and_v: [Miniscript, Miniscript] }>
37+
| Wrap<{ and_b: [Miniscript, Miniscript] }>
38+
| Wrap<{ andor: [Miniscript, Miniscript, Miniscript] }>
39+
| Wrap<{ or_b: [Miniscript, Miniscript] }>
40+
| Wrap<{ or_c: [Miniscript, Miniscript] }>
41+
| Wrap<{ or_d: [Miniscript, Miniscript] }>
42+
| Wrap<{ or_i: [Miniscript, Miniscript] }>
43+
| Wrap<{ thresh: [number, ...Miniscript[]] }>
44+
| Wrap<{ sha256: string }>
45+
| Wrap<{ ripemd160: string }>
46+
| Wrap<{ hash256: string }>
47+
| Wrap<{ hash160: string }>
48+
| Wrap<{ older: number }>
49+
| Wrap<{ after: number }>;
50+
51+
// Top level descriptor expressions
52+
// https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md#reference
53+
type Descriptor =
54+
| { sh: Miniscript | { wsh: Miniscript } }
55+
| { wsh: Miniscript }
56+
| { pk: Key }
57+
| { pkh: Key }
58+
| { wpkh: Key }
59+
| { combo: Key }
60+
| { tr: [Key, Miniscript] }
61+
| { addr: string }
62+
| { raw: string }
63+
| { rawtr: string };
64+
65+
type Node = Miniscript | Descriptor | number | string;
66+
67+
function formatN(n: Node | Node[]): string {
68+
if (typeof n === "string") {
69+
return n;
70+
}
71+
if (typeof n === "number") {
72+
return String(n);
73+
}
74+
if (Array.isArray(n)) {
75+
return n.map(formatN).join(",");
76+
}
77+
if (n && typeof n === "object") {
78+
const entries = Object.entries(n);
79+
if (entries.length !== 1) {
80+
throw new Error(`Invalid node: ${n}`);
81+
}
82+
const [name, value] = entries[0];
83+
return `${name}(${formatN(value)})`;
84+
}
85+
throw new Error(`Invalid node: ${n}`);
86+
}
87+
88+
export type MiniscriptNode = Miniscript;
89+
export type DescriptorNode = Descriptor;
90+
91+
/** Format a Miniscript or Descriptor node as a descriptor string (without checksum) */
92+
export function formatNode(n: MiniscriptNode | DescriptorNode): string {
93+
return formatN(n);
94+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { DescriptorNode, MiniscriptNode } from "./formatNode";
2+
import { Descriptor, Miniscript } from "../index";
3+
4+
function getSingleEntry(v: unknown): [string, unknown] {
5+
if (typeof v === "object" && v) {
6+
const entries = Object.entries(v);
7+
if (entries.length === 1) {
8+
return entries[0];
9+
}
10+
}
11+
12+
throw new Error("Expected single entry object");
13+
}
14+
15+
function node(type: string, value: unknown): MiniscriptNode | DescriptorNode {
16+
return { [type]: fromUnknown(value) } as MiniscriptNode | DescriptorNode;
17+
}
18+
19+
function wrap(type: string, value: unknown): MiniscriptNode {
20+
const n = fromWasmNode(value);
21+
const [name, inner] = getSingleEntry(n);
22+
return { [`${type}:${name}`]: inner } as MiniscriptNode;
23+
}
24+
25+
type Node = DescriptorNode | MiniscriptNode | string | number;
26+
27+
function fromUnknown(v: unknown): Node | Node[] {
28+
if (typeof v === "number" || typeof v === "string") {
29+
return v;
30+
}
31+
if (Array.isArray(v)) {
32+
return v.map(fromUnknown) as Node[];
33+
}
34+
if (typeof v === "object" && v) {
35+
const [type, value] = getSingleEntry(v);
36+
switch (type) {
37+
case "Bare":
38+
case "Single":
39+
case "Ms":
40+
case "XPub":
41+
case "relLockTime":
42+
case "absLockTime":
43+
return fromUnknown(value);
44+
case "Sh":
45+
case "Wsh":
46+
case "Tr":
47+
case "Pk":
48+
case "Pkh":
49+
case "PkH":
50+
case "Wpkh":
51+
case "Combo":
52+
case "SortedMulti":
53+
case "Addr":
54+
case "Raw":
55+
case "RawTr":
56+
case "After":
57+
case "Older":
58+
case "Sha256":
59+
case "Hash256":
60+
case "Ripemd160":
61+
case "Hash160":
62+
return node(type.toLocaleLowerCase(), value);
63+
case "PkK":
64+
return node("pk", value);
65+
case "RawPkH":
66+
return node("raw_pkh", value);
67+
68+
// Wrappers
69+
case "Alt":
70+
return wrap("a", value);
71+
case "Swap":
72+
return wrap("s", value);
73+
case "Check":
74+
return fromUnknown(value);
75+
case "DupIf":
76+
return wrap("d", value);
77+
case "Verify":
78+
return wrap("v", value);
79+
case "ZeroNotEqual":
80+
return wrap("n", value);
81+
82+
// Conjunctions
83+
case "AndV":
84+
return node("and_v", value);
85+
case "AndB":
86+
return node("and_b", value);
87+
case "AndOr":
88+
if (!Array.isArray(value)) {
89+
throw new Error(`Invalid AndOr node: ${JSON.stringify(value)}`);
90+
}
91+
const [cond, success, failure] = value;
92+
if (failure === false) {
93+
return node("and_n", [cond, success]);
94+
}
95+
return node("andor", [cond, success, failure]);
96+
97+
// Disjunctions
98+
case "OrB":
99+
return node("or_b", value);
100+
case "OrD":
101+
return node("or_d", value);
102+
case "OrC":
103+
return node("or_c", value);
104+
case "OrI":
105+
return node("or_i", value);
106+
107+
// Thresholds
108+
case "Thresh":
109+
return node("thresh", value);
110+
case "Multi":
111+
return node("multi", value);
112+
case "MultiA":
113+
return node("multi_a", value);
114+
}
115+
}
116+
117+
throw new Error(`Unknown node ${JSON.stringify(v)}`);
118+
}
119+
120+
function fromWasmNode(v: unknown): DescriptorNode | MiniscriptNode {
121+
return fromUnknown(v) as DescriptorNode | MiniscriptNode;
122+
}
123+
124+
export function fromDescriptor(d: Descriptor): DescriptorNode {
125+
return fromWasmNode(d.node()) as DescriptorNode;
126+
}
127+
128+
export function fromMiniscript(m: Miniscript): MiniscriptNode {
129+
return fromWasmNode(m.node()) as MiniscriptNode;
130+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./formatNode";
2+
export * from "./fromWasmNode";

packages/wasm-miniscript/js/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type ScriptContext = "tap" | "segwitv0" | "legacy";
1010

1111
declare module "./wasm/wasm_miniscript" {
1212
interface WrapDescriptor {
13+
/** These are not the same types of nodes as in the ast module */
1314
node(): unknown;
1415
}
1516

@@ -18,6 +19,7 @@ declare module "./wasm/wasm_miniscript" {
1819
}
1920

2021
interface WrapMiniscript {
22+
/** These are not the same types of nodes as in the ast module */
2123
node(): unknown;
2224
}
2325

@@ -30,3 +32,5 @@ declare module "./wasm/wasm_miniscript" {
3032
export { WrapDescriptor as Descriptor } from "./wasm/wasm_miniscript";
3133
export { WrapMiniscript as Miniscript } from "./wasm/wasm_miniscript";
3234
export { WrapPsbt as Psbt } from "./wasm/wasm_miniscript";
35+
36+
export * as ast from "./ast";

packages/wasm-miniscript/package.json

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,29 @@
88
"url": "git+https://github.com/BitGo/wasm-miniscript.git"
99
},
1010
"files": [
11-
"dist/wasm/wasm_miniscript.d.ts",
12-
"dist/wasm/wasm_miniscript.js",
13-
"dist/wasm/wasm_miniscript_bg.wasm",
14-
"dist/wasm/wasm_miniscript_bg.wasm.d.ts",
15-
"dist/browser/wasm/wasm_miniscript.d.ts",
16-
"dist/browser/wasm/wasm_miniscript.js",
17-
"dist/browser/wasm/wasm_miniscript_bg.js",
18-
"dist/browser/wasm/wasm_miniscript_bg.wasm",
19-
"dist/browser/wasm/wasm_miniscript_bg.wasm.d.ts",
20-
"dist/browser/index.d.ts",
21-
"dist/browser/index.js",
22-
"dist/index.js",
23-
"dist/index.d.ts"
11+
"dist/*/js/wasm/wasm_miniscript.d.ts",
12+
"dist/*/js/wasm/wasm_miniscript.js",
13+
"dist/*/js/wasm/wasm_miniscript_bg.js",
14+
"dist/*/js/wasm/wasm_miniscript_bg.wasm",
15+
"dist/*/js/wasm/wasm_miniscript_bg.wasm.d.ts",
16+
"dist/*/js/ast/*",
17+
"dist/*/js/index.*"
2418
],
25-
"main": "dist/index.js",
26-
"types": "dist/index.d.ts",
19+
"main": "dist/node/js/index.js",
20+
"types": "dist/node/js/index.d.ts",
2721
"sideEffects": [
28-
"./dist/wasm/wasm_miniscript.js"
22+
"./dist/node/js/wasm/wasm_miniscript.js",
23+
"./dist/browser/js/wasm/wasm_miniscript.js"
2924
],
3025
"browser": {
31-
"./dist/index.js": "./dist/browser/index.js",
32-
"./dist/index.d.ts": "./dist/browser/index.d.ts",
33-
"./dist/wasm/wasm_miniscript_bg.wasm": "./dist/browser/wasm/wasm_miniscript_bg.wasm",
34-
"./dist/wasm/wasm_miniscript.js": "./dist/browser/wasm/wasm_miniscript.js",
35-
"./dist/wasm/wasm_miniscript_bg.js": "./dist/browser/wasm/wasm_miniscript_bg.js",
36-
"./dist/wasm/wasm_miniscript.d.ts": "./dist/browser/wasm/wasm_miniscript.d.ts"
26+
"./dist/node/js/index.js": "./dist/browser/js/index.js"
3727
},
3828
"scripts": {
3929
"test": "mocha --recursive test",
40-
"build:wasm": "make js/wasm/ && make dist/wasm/ && make dist/browser/wasm/",
41-
"build:ts-browser": "tsc --module es2020 --target es2020 --outDir dist/browser",
42-
"build:ts": "tsc && npm run build:ts-browser",
30+
"build:wasm": "make js/wasm/ && make dist/node/js/wasm/ && make dist/browser/js/wasm/",
31+
"build:ts-browser": "tsc --noEmit false --module es2020 --target es2020 --outDir dist/browser",
32+
"build:ts-node": "tsc --noEmit false --outDir dist/node",
33+
"build:ts": "npm run build:ts-browser && npm run build:ts-node",
4334
"build": "npm run build:wasm && npm run build:ts",
4435
"check-fmt": "prettier --check . && cargo fmt -- --check"
4536
},

packages/wasm-miniscript/src/psbt.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ impl WrapPsbt {
2727
pub fn update_input_with_descriptor(
2828
&mut self,
2929
input_index: usize,
30-
descriptor: WrapDescriptor,
30+
descriptor: &WrapDescriptor,
3131
) -> Result<(), JsError> {
32-
match descriptor.0 {
32+
match &descriptor.0 {
3333
WrapDescriptorEnum::Definite(d) => self
3434
.0
3535
.update_input_with_descriptor(input_index, &d)
@@ -47,9 +47,9 @@ impl WrapPsbt {
4747
pub fn update_output_with_descriptor(
4848
&mut self,
4949
output_index: usize,
50-
descriptor: WrapDescriptor,
50+
descriptor: &WrapDescriptor,
5151
) -> Result<(), JsError> {
52-
match descriptor.0 {
52+
match &descriptor.0 {
5353
WrapDescriptorEnum::Definite(d) => self
5454
.0
5555
.update_output_with_descriptor(output_index, &d)

0 commit comments

Comments
 (0)