@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 +33 -0
- package/dist/browser/js/ast/formatNode.d.ts +88 -0
- package/dist/browser/js/ast/formatNode.js +36 -0
- package/dist/browser/js/ast/fromWasmNode.d.ts +4 -0
- package/dist/browser/js/ast/fromWasmNode.js +120 -0
- package/dist/browser/js/ast/index.d.ts +2 -0
- package/dist/browser/js/ast/index.js +2 -0
- package/dist/browser/js/index.d.ts +37 -0
- package/dist/browser/js/index.js +11 -0
- package/dist/browser/js/wasm/wasm_utxo.d.ts +75 -0
- package/dist/browser/js/wasm/wasm_utxo.js +4 -0
- package/dist/browser/js/wasm/wasm_utxo_bg.js +1156 -0
- package/dist/browser/js/wasm/wasm_utxo_bg.wasm +0 -0
- package/dist/browser/js/wasm/wasm_utxo_bg.wasm.d.ts +49 -0
- package/dist/node/js/ast/formatNode.d.ts +88 -0
- package/dist/node/js/ast/formatNode.js +39 -0
- package/dist/node/js/ast/fromWasmNode.d.ts +4 -0
- package/dist/node/js/ast/fromWasmNode.js +124 -0
- package/dist/node/js/ast/index.d.ts +2 -0
- package/dist/node/js/ast/index.js +18 -0
- package/dist/node/js/index.d.ts +37 -0
- package/dist/node/js/index.js +50 -0
- package/dist/node/js/wasm/wasm_utxo.d.ts +75 -0
- package/dist/node/js/wasm/wasm_utxo.js +1163 -0
- package/dist/node/js/wasm/wasm_utxo_bg.wasm +0 -0
- package/dist/node/js/wasm/wasm_utxo_bg.wasm.d.ts +49 -0
- package/package.json +47 -5
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,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,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
|
+
}
|