@bitgo/wasm-utxo 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # wasm-utxo
2
+
3
+ This project is the successor of the Javascript `utxo-lib` package.
4
+
5
+ It provides WASM bindings for the `rust-bitcoin` and `rust-miniscript` crates
6
+ that help verify and co-sign transactions built by the BitGo Wallet Platform API.
7
+
8
+ ## Documentation
9
+
10
+ - **[`src/wasm-bindgen.md`](src/wasm-bindgen.md)** - Guide for creating WASM bindings using the namespace pattern
11
+ - **[`js/README.md`](js/README.md)** - TypeScript wrapper layer architecture and best practices
12
+ - **[`cli/README.md`](cli/README.md)** - Command-line interface for address and PSBT operations
13
+
14
+ ## Status
15
+
16
+ This project is under active development.
17
+
18
+ | Feature | Bitcoin | BitcoinCash | BitcoinGold | Dash | Doge | Litecoin | Zcash |
19
+ | --------------------------------------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- |
20
+ | Descriptor Wallet: Address Support | ✅ Complete | 🚫 | 🚫 | 🚫 | 🚫 | 🚫 | 🚫 |
21
+ | Descriptor Wallet: Transaction Support | ✅ Complete | 🚫 | 🚫 | 🚫 | 🚫 | 🚫 | 🚫 |
22
+ | FixedScript Wallet: Address Generation | ✅ Complete | ✅ Complete | ✅ Complete | ✅ Complete | ✅ Complete | ✅ Complete | ✅ Complete |
23
+ | FixedScript Wallet: Transaction Support | ⏳ TODO | ⏳ TODO | ⏳ TODO | ⏳ TODO | ⏳ TODO | ⏳ TODO | ⏳ TODO |
24
+
25
+ ## Building
26
+
27
+ If your system has problems with `wasm-pack` (Mac M1), you can use the `Container.mk` Makefile to build the wasm files:
28
+
29
+ ```bash
30
+ cd packages/wasm-utxo
31
+ make -f Container.mk build-image
32
+ make -f Container.mk build-wasm
33
+ ```
@@ -0,0 +1,88 @@
1
+ type Key = string;
2
+ type Identities = "a" | "s" | "c" | "t" | "d" | "v" | "j" | "n" | "l" | "u" | "r";
3
+ type PrefixWith<T, P extends string> = {
4
+ [K in keyof T & string as `${P}:${K}`]: T[K];
5
+ };
6
+ type PrefixIdUnion<T> = {
7
+ [P in Identities]: PrefixWith<T, P>;
8
+ }[Identities];
9
+ type Wrap<T> = T | PrefixIdUnion<T>;
10
+ type Miniscript = Wrap<{
11
+ pk: Key;
12
+ }> | Wrap<{
13
+ pkh: Key;
14
+ }> | Wrap<{
15
+ wpkh: Key;
16
+ }> | Wrap<{
17
+ multi: [number, ...Key[]];
18
+ }> | Wrap<{
19
+ sortedmulti: [number, ...Key[]];
20
+ }> | Wrap<{
21
+ multi_a: [number, ...Key[]];
22
+ }> | Wrap<{
23
+ sortedmulti_a: [number, ...Key[]];
24
+ }> | Wrap<{
25
+ tr: Key | [Key, Miniscript];
26
+ }> | Wrap<{
27
+ sh: Miniscript;
28
+ }> | Wrap<{
29
+ wsh: Miniscript;
30
+ }> | Wrap<{
31
+ and_v: [Miniscript, Miniscript];
32
+ }> | Wrap<{
33
+ and_b: [Miniscript, Miniscript];
34
+ }> | Wrap<{
35
+ andor: [Miniscript, Miniscript, Miniscript];
36
+ }> | Wrap<{
37
+ or_b: [Miniscript, Miniscript];
38
+ }> | Wrap<{
39
+ or_c: [Miniscript, Miniscript];
40
+ }> | Wrap<{
41
+ or_d: [Miniscript, Miniscript];
42
+ }> | Wrap<{
43
+ or_i: [Miniscript, Miniscript];
44
+ }> | Wrap<{
45
+ thresh: [number, ...Miniscript[]];
46
+ }> | Wrap<{
47
+ sha256: string;
48
+ }> | Wrap<{
49
+ ripemd160: string;
50
+ }> | Wrap<{
51
+ hash256: string;
52
+ }> | Wrap<{
53
+ hash160: string;
54
+ }> | Wrap<{
55
+ older: number;
56
+ }> | Wrap<{
57
+ after: number;
58
+ }>;
59
+ type TapTree = [TapTree, TapTree] | Miniscript;
60
+ type Descriptor = {
61
+ sh: Miniscript | {
62
+ wsh: Miniscript;
63
+ };
64
+ } | {
65
+ wsh: Miniscript;
66
+ } | {
67
+ pk: Key;
68
+ } | {
69
+ pkh: Key;
70
+ } | {
71
+ wpkh: Key;
72
+ } | {
73
+ combo: Key;
74
+ } | {
75
+ tr: [Key, TapTree];
76
+ } | {
77
+ addr: string;
78
+ } | {
79
+ raw: string;
80
+ } | {
81
+ rawtr: string;
82
+ };
83
+ export type TapTreeNode = TapTree;
84
+ export type MiniscriptNode = Miniscript;
85
+ export type DescriptorNode = Descriptor;
86
+ /** Format a Miniscript or Descriptor node as a descriptor string (without checksum) */
87
+ export declare function formatNode(n: MiniscriptNode | DescriptorNode): string;
88
+ export {};
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Format a TapTree as a string
3
+ * If the argument is an array, descend recursively with formatTr and wrap result in curly braces
4
+ * Otherwise, format the node with formatN
5
+ */
6
+ function formatTr(tree) {
7
+ return Array.isArray(tree) ? `{` + tree.map(formatTr).join(",") + `}` : formatN(tree);
8
+ }
9
+ function formatN(n) {
10
+ if (typeof n === "string") {
11
+ return n;
12
+ }
13
+ if (typeof n === "number") {
14
+ return String(n);
15
+ }
16
+ if (Array.isArray(n)) {
17
+ return n.map(formatN).join(",");
18
+ }
19
+ if (n && typeof n === "object") {
20
+ const entries = Object.entries(n);
21
+ if (entries.length !== 1) {
22
+ throw new Error(`Invalid node: ${n}`);
23
+ }
24
+ const [name, value] = entries[0];
25
+ if (name === "tr" && Array.isArray(value)) {
26
+ const [key, tree] = value;
27
+ return formatN({ tr: formatN([key, formatTr(tree)]) });
28
+ }
29
+ return `${name}(${formatN(value)})`;
30
+ }
31
+ throw new Error(`Invalid node: ${n}`);
32
+ }
33
+ /** Format a Miniscript or Descriptor node as a descriptor string (without checksum) */
34
+ export function formatNode(n) {
35
+ return formatN(n);
36
+ }
@@ -0,0 +1,4 @@
1
+ import { DescriptorNode, MiniscriptNode } from "./formatNode";
2
+ import { Descriptor, Miniscript } from "../index";
3
+ export declare function fromDescriptor(d: Descriptor): DescriptorNode;
4
+ export declare function fromMiniscript(m: Miniscript): MiniscriptNode;
@@ -0,0 +1,120 @@
1
+ function getSingleEntry(v) {
2
+ if (typeof v === "object" && v) {
3
+ const entries = Object.entries(v);
4
+ if (entries.length === 1) {
5
+ return entries[0];
6
+ }
7
+ }
8
+ throw new Error("Expected single entry object");
9
+ }
10
+ function node(type, value) {
11
+ return { [type]: fromUnknown(value) };
12
+ }
13
+ function wrap(type, value) {
14
+ const n = fromWasmNode(value);
15
+ const [name, inner] = getSingleEntry(n);
16
+ return { [`${type}:${name}`]: inner };
17
+ }
18
+ function fromUnknown(v) {
19
+ if (typeof v === "number" || typeof v === "string") {
20
+ return v;
21
+ }
22
+ if (Array.isArray(v)) {
23
+ return v.map(fromUnknown);
24
+ }
25
+ if (typeof v === "object" && v) {
26
+ const [type, value] = getSingleEntry(v);
27
+ switch (type) {
28
+ case "Bare":
29
+ case "Single":
30
+ case "Ms":
31
+ case "XPub":
32
+ case "relLockTime":
33
+ case "absLockTime":
34
+ return fromUnknown(value);
35
+ case "Sh":
36
+ case "Wsh":
37
+ case "Tr":
38
+ case "Pk":
39
+ case "Pkh":
40
+ case "PkH":
41
+ case "Wpkh":
42
+ case "Combo":
43
+ case "SortedMulti":
44
+ case "Addr":
45
+ case "Raw":
46
+ case "RawTr":
47
+ case "After":
48
+ case "Older":
49
+ case "Sha256":
50
+ case "Hash256":
51
+ case "Ripemd160":
52
+ case "Hash160":
53
+ return node(type.toLocaleLowerCase(), value);
54
+ case "PkK":
55
+ return node("pk", value);
56
+ case "RawPkH":
57
+ return node("raw_pkh", value);
58
+ // Wrappers
59
+ case "Alt":
60
+ return wrap("a", value);
61
+ case "Swap":
62
+ return wrap("s", value);
63
+ case "Check":
64
+ return fromUnknown(value);
65
+ case "DupIf":
66
+ return wrap("d", value);
67
+ case "Verify":
68
+ return wrap("v", value);
69
+ case "ZeroNotEqual":
70
+ return wrap("n", value);
71
+ case "Drop":
72
+ return wrap("r", value);
73
+ // Conjunctions
74
+ case "AndV":
75
+ return node("and_v", value);
76
+ case "AndB":
77
+ return node("and_b", value);
78
+ case "AndOr":
79
+ if (!Array.isArray(value)) {
80
+ throw new Error(`Invalid AndOr node: ${JSON.stringify(value)}`);
81
+ }
82
+ const [cond, success, failure] = value;
83
+ if (failure === false) {
84
+ return node("and_n", [cond, success]);
85
+ }
86
+ return node("andor", [cond, success, failure]);
87
+ // Disjunctions
88
+ case "OrB":
89
+ return node("or_b", value);
90
+ case "OrD":
91
+ return node("or_d", value);
92
+ case "OrC":
93
+ return node("or_c", value);
94
+ case "OrI":
95
+ return node("or_i", value);
96
+ // Thresholds
97
+ case "Thresh":
98
+ return node("thresh", value);
99
+ case "Multi":
100
+ return node("multi", value);
101
+ case "MultiA":
102
+ return node("multi_a", value);
103
+ case "Tree":
104
+ if (!Array.isArray(value) || value.length !== 2) {
105
+ throw new Error(`Invalid Tree node: ${JSON.stringify(value)}`);
106
+ }
107
+ return [fromUnknown(value[0]), fromUnknown(value[1])];
108
+ }
109
+ }
110
+ throw new Error(`Unknown node ${JSON.stringify(v)}`);
111
+ }
112
+ function fromWasmNode(v) {
113
+ return fromUnknown(v);
114
+ }
115
+ export function fromDescriptor(d) {
116
+ return fromWasmNode(d.node());
117
+ }
118
+ export function fromMiniscript(m) {
119
+ return fromWasmNode(m.node());
120
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./formatNode";
2
+ export * from "./fromWasmNode";
@@ -0,0 +1,2 @@
1
+ export * from "./formatNode";
2
+ export * from "./fromWasmNode";
@@ -0,0 +1,37 @@
1
+ export * as address from "./address";
2
+ export * as ast from "./ast";
3
+ export * as utxolibCompat from "./utxolibCompat";
4
+ export * as fixedScriptWallet from "./fixedScriptWallet";
5
+ export type { CoinName } from "./coinName";
6
+ export type { Triple } from "./triple";
7
+ export type { AddressFormat } from "./address";
8
+ export type DescriptorPkType = "derivable" | "definite" | "string";
9
+ export type ScriptContext = "tap" | "segwitv0" | "legacy";
10
+ export type SignPsbtResult = {
11
+ [inputIndex: number]: [pubkey: string][];
12
+ };
13
+ declare module "./wasm/wasm_utxo" {
14
+ interface WrapDescriptor {
15
+ /** These are not the same types of nodes as in the ast module */
16
+ node(): unknown;
17
+ }
18
+ namespace WrapDescriptor {
19
+ function fromString(descriptor: string, pkType: DescriptorPkType): WrapDescriptor;
20
+ function fromStringDetectType(descriptor: string): WrapDescriptor;
21
+ }
22
+ interface WrapMiniscript {
23
+ /** These are not the same types of nodes as in the ast module */
24
+ node(): unknown;
25
+ }
26
+ namespace WrapMiniscript {
27
+ function fromString(miniscript: string, ctx: ScriptContext): WrapMiniscript;
28
+ function fromBitcoinScript(script: Uint8Array, ctx: ScriptContext): WrapMiniscript;
29
+ }
30
+ interface WrapPsbt {
31
+ signWithXprv(this: WrapPsbt, xprv: string): SignPsbtResult;
32
+ signWithPrv(this: WrapPsbt, prv: Uint8Array): SignPsbtResult;
33
+ }
34
+ }
35
+ export { WrapDescriptor as Descriptor } from "./wasm/wasm_utxo";
36
+ export { WrapMiniscript as Miniscript } from "./wasm/wasm_utxo";
37
+ export { WrapPsbt as Psbt } from "./wasm/wasm_utxo";
@@ -0,0 +1,11 @@
1
+ import * as wasm from "./wasm/wasm_utxo";
2
+ // we need to access the wasm module here, otherwise webpack gets all weird
3
+ // and forgets to include it in the bundle
4
+ void wasm;
5
+ export * as address from "./address";
6
+ export * as ast from "./ast";
7
+ export * as utxolibCompat from "./utxolibCompat";
8
+ export * as fixedScriptWallet from "./fixedScriptWallet";
9
+ export { WrapDescriptor as Descriptor } from "./wasm/wasm_utxo";
10
+ export { WrapMiniscript as Miniscript } from "./wasm/wasm_utxo";
11
+ export { WrapPsbt as Psbt } from "./wasm/wasm_utxo";
@@ -0,0 +1,75 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ export class AddressNamespace {
4
+ private constructor();
5
+ free(): void;
6
+ [Symbol.dispose](): void;
7
+ static to_output_script_with_coin(address: string, coin: string): Uint8Array;
8
+ static from_output_script_with_coin(script: Uint8Array, coin: string, format?: string | null): string;
9
+ }
10
+ export class FixedScriptWalletNamespace {
11
+ private constructor();
12
+ free(): void;
13
+ [Symbol.dispose](): void;
14
+ static output_script(keys: any, chain: number, index: number, network: any): Uint8Array;
15
+ static address(keys: any, chain: number, index: number, network: any, address_format?: string | null): string;
16
+ }
17
+ export class UtxolibCompatNamespace {
18
+ private constructor();
19
+ free(): void;
20
+ [Symbol.dispose](): void;
21
+ /**
22
+ * Convert address string to output script
23
+ *
24
+ * # Arguments
25
+ * * `address` - The address string
26
+ * * `network` - The UtxolibNetwork object from JavaScript
27
+ * * `format` - Optional address format (currently unused for decoding as all formats are accepted)
28
+ */
29
+ static to_output_script(address: string, network: any, format?: string | null): Uint8Array;
30
+ /**
31
+ * Convert output script to address string
32
+ *
33
+ * # Arguments
34
+ * * `script` - The output script as a byte array
35
+ * * `network` - The UtxolibNetwork object from JavaScript
36
+ * * `format` - Optional address format: "default" or "cashaddr" (only applicable for Bitcoin Cash and eCash)
37
+ */
38
+ static from_output_script(script: Uint8Array, network: any, format?: string | null): string;
39
+ }
40
+ export class WrapDescriptor {
41
+ private constructor();
42
+ free(): void;
43
+ [Symbol.dispose](): void;
44
+ hasWildcard(): boolean;
45
+ scriptPubkey(): Uint8Array;
46
+ toAsmString(): string;
47
+ atDerivationIndex(index: number): WrapDescriptor;
48
+ maxWeightToSatisfy(): number;
49
+ node(): any;
50
+ encode(): Uint8Array;
51
+ descType(): any;
52
+ toString(): string;
53
+ }
54
+ export class WrapMiniscript {
55
+ private constructor();
56
+ free(): void;
57
+ [Symbol.dispose](): void;
58
+ toAsmString(): string;
59
+ node(): any;
60
+ encode(): Uint8Array;
61
+ toString(): string;
62
+ }
63
+ export class WrapPsbt {
64
+ private constructor();
65
+ free(): void;
66
+ [Symbol.dispose](): void;
67
+ static deserialize(psbt: Uint8Array): WrapPsbt;
68
+ finalize(): void;
69
+ signWithPrv(prv: Uint8Array): any;
70
+ signWithXprv(xprv: string): any;
71
+ updateInputWithDescriptor(input_index: number, descriptor: WrapDescriptor): void;
72
+ updateOutputWithDescriptor(output_index: number, descriptor: WrapDescriptor): void;
73
+ clone(): WrapPsbt;
74
+ serialize(): Uint8Array;
75
+ }
@@ -0,0 +1,4 @@
1
+ import * as wasm from "./wasm_utxo_bg.wasm";
2
+ export * from "./wasm_utxo_bg.js";
3
+ import { __wbg_set_wasm } from "./wasm_utxo_bg.js";
4
+ __wbg_set_wasm(wasm);