@bitgo/wasm-utxo 1.28.0 → 1.29.0
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/dist/cjs/js/fixedScriptWallet/chains.d.ts +5 -0
- package/dist/cjs/js/fixedScriptWallet/chains.js +6 -2
- package/dist/cjs/js/fixedScriptWallet/index.d.ts +19 -2
- package/dist/cjs/js/fixedScriptWallet/index.js +25 -1
- package/dist/cjs/js/fixedScriptWallet/scriptType.d.ts +15 -5
- package/dist/cjs/js/fixedScriptWallet/scriptType.js +28 -0
- package/dist/cjs/js/testutils/AcidTest.d.ts +132 -0
- package/dist/cjs/js/testutils/AcidTest.js +306 -0
- package/dist/cjs/js/testutils/index.d.ts +2 -0
- package/dist/cjs/js/testutils/index.js +18 -0
- package/dist/cjs/js/testutils/keys.d.ts +76 -0
- package/dist/cjs/js/testutils/keys.js +154 -0
- package/dist/cjs/js/wasm/wasm_utxo.d.ts +10 -0
- package/dist/cjs/js/wasm/wasm_utxo.js +31 -0
- package/dist/cjs/js/wasm/wasm_utxo_bg.wasm +0 -0
- package/dist/cjs/js/wasm/wasm_utxo_bg.wasm.d.ts +2 -1
- package/dist/esm/js/fixedScriptWallet/chains.d.ts +5 -0
- package/dist/esm/js/fixedScriptWallet/chains.js +6 -3
- package/dist/esm/js/fixedScriptWallet/index.d.ts +19 -2
- package/dist/esm/js/fixedScriptWallet/index.js +21 -1
- package/dist/esm/js/fixedScriptWallet/scriptType.d.ts +15 -5
- package/dist/esm/js/fixedScriptWallet/scriptType.js +27 -1
- package/dist/esm/js/testutils/AcidTest.d.ts +132 -0
- package/dist/esm/js/testutils/AcidTest.js +302 -0
- package/dist/esm/js/testutils/index.d.ts +2 -0
- package/dist/esm/js/testutils/index.js +2 -0
- package/dist/esm/js/testutils/keys.d.ts +76 -0
- package/dist/esm/js/testutils/keys.js +113 -0
- package/dist/esm/js/wasm/wasm_utxo.d.ts +10 -0
- package/dist/esm/js/wasm/wasm_utxo_bg.js +31 -0
- package/dist/esm/js/wasm/wasm_utxo_bg.wasm +0 -0
- package/dist/esm/js/wasm/wasm_utxo_bg.wasm.d.ts +2 -1
- package/package.json +1 -1
|
@@ -5,6 +5,11 @@ export declare const chainCodes: readonly [0, 1, 10, 11, 20, 21, 30, 31, 40, 41]
|
|
|
5
5
|
export type ChainCode = (typeof chainCodes)[number];
|
|
6
6
|
/** Whether a chain is for receiving (external) or change (internal) addresses */
|
|
7
7
|
export type Scope = "internal" | "external";
|
|
8
|
+
/**
|
|
9
|
+
* Assert that a number is a valid chain code.
|
|
10
|
+
* @throws Error if the number is not a valid chain code
|
|
11
|
+
*/
|
|
12
|
+
export declare function assertChainCode(n: number): ChainCode;
|
|
8
13
|
/**
|
|
9
14
|
* ChainCode namespace with utility functions for working with chain codes.
|
|
10
15
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ChainCode = exports.chainCodes = void 0;
|
|
4
|
+
exports.assertChainCode = assertChainCode;
|
|
4
5
|
/**
|
|
5
6
|
* Chain code utilities for BitGo fixed-script wallets.
|
|
6
7
|
*
|
|
@@ -14,10 +15,13 @@ exports.chainCodes = [0, 1, 10, 11, 20, 21, 30, 31, 40, 41];
|
|
|
14
15
|
const chainCodeSet = new Set(exports.chainCodes);
|
|
15
16
|
const chainToMeta = new Map();
|
|
16
17
|
const scriptTypeToChain = new Map();
|
|
17
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Assert that a number is a valid chain code.
|
|
20
|
+
* @throws Error if the number is not a valid chain code
|
|
21
|
+
*/
|
|
18
22
|
function assertChainCode(n) {
|
|
19
23
|
if (!chainCodeSet.has(n)) {
|
|
20
|
-
throw new Error(`Invalid chain code
|
|
24
|
+
throw new Error(`Invalid chain code: ${n}`);
|
|
21
25
|
}
|
|
22
26
|
return n;
|
|
23
27
|
}
|
|
@@ -3,8 +3,8 @@ export { RootWalletKeys, type WalletKeysArg, type IWalletKeys } from "./RootWall
|
|
|
3
3
|
export { ReplayProtection, type ReplayProtectionArg } from "./ReplayProtection.js";
|
|
4
4
|
export { outputScript, address } from "./address.js";
|
|
5
5
|
export { Dimensions } from "./Dimensions.js";
|
|
6
|
-
export { type OutputScriptType, type InputScriptType, type ScriptType } from "./scriptType.js";
|
|
7
|
-
export { ChainCode, chainCodes, type Scope } from "./chains.js";
|
|
6
|
+
export { outputScriptTypes, inputScriptTypes, type OutputScriptType, type InputScriptType, type ScriptType, } from "./scriptType.js";
|
|
7
|
+
export { ChainCode, chainCodes, assertChainCode, type Scope } from "./chains.js";
|
|
8
8
|
export { BitGoPsbt, type NetworkName, type ScriptId, type ParsedInput, type ParsedOutput, type ParsedTransaction, type SignPath, type CreateEmptyOptions, type AddInputOptions, type AddOutputOptions, type AddWalletInputOptions, type AddWalletOutputOptions, } from "./BitGoPsbt.js";
|
|
9
9
|
export { ZcashBitGoPsbt, type ZcashNetworkName, type CreateEmptyZcashOptions, } from "./ZcashBitGoPsbt.js";
|
|
10
10
|
import type { ScriptType } from "./scriptType.js";
|
|
@@ -34,3 +34,20 @@ import type { ScriptType } from "./scriptType.js";
|
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
36
|
export declare function supportsScriptType(coin: CoinName, scriptType: ScriptType): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Create an OP_RETURN output script with optional data
|
|
39
|
+
*
|
|
40
|
+
* @param data - Optional data bytes to include in the OP_RETURN script
|
|
41
|
+
* @returns The OP_RETURN script as a Uint8Array
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* // Empty OP_RETURN
|
|
46
|
+
* const script = createOpReturnScript();
|
|
47
|
+
*
|
|
48
|
+
* // OP_RETURN with data
|
|
49
|
+
* const data = new Uint8Array([1, 2, 3, 4]);
|
|
50
|
+
* const script = createOpReturnScript(data);
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function createOpReturnScript(data?: Uint8Array): Uint8Array;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ZcashBitGoPsbt = exports.BitGoPsbt = exports.chainCodes = exports.ChainCode = exports.Dimensions = exports.address = exports.outputScript = exports.ReplayProtection = exports.RootWalletKeys = void 0;
|
|
3
|
+
exports.ZcashBitGoPsbt = exports.BitGoPsbt = exports.assertChainCode = exports.chainCodes = exports.ChainCode = exports.inputScriptTypes = exports.outputScriptTypes = exports.Dimensions = exports.address = exports.outputScript = exports.ReplayProtection = exports.RootWalletKeys = void 0;
|
|
4
4
|
exports.supportsScriptType = supportsScriptType;
|
|
5
|
+
exports.createOpReturnScript = createOpReturnScript;
|
|
5
6
|
const wasm_utxo_js_1 = require("../wasm/wasm_utxo.js");
|
|
6
7
|
var RootWalletKeys_js_1 = require("./RootWalletKeys.js");
|
|
7
8
|
Object.defineProperty(exports, "RootWalletKeys", { enumerable: true, get: function () { return RootWalletKeys_js_1.RootWalletKeys; } });
|
|
@@ -12,9 +13,13 @@ Object.defineProperty(exports, "outputScript", { enumerable: true, get: function
|
|
|
12
13
|
Object.defineProperty(exports, "address", { enumerable: true, get: function () { return address_js_1.address; } });
|
|
13
14
|
var Dimensions_js_1 = require("./Dimensions.js");
|
|
14
15
|
Object.defineProperty(exports, "Dimensions", { enumerable: true, get: function () { return Dimensions_js_1.Dimensions; } });
|
|
16
|
+
var scriptType_js_1 = require("./scriptType.js");
|
|
17
|
+
Object.defineProperty(exports, "outputScriptTypes", { enumerable: true, get: function () { return scriptType_js_1.outputScriptTypes; } });
|
|
18
|
+
Object.defineProperty(exports, "inputScriptTypes", { enumerable: true, get: function () { return scriptType_js_1.inputScriptTypes; } });
|
|
15
19
|
var chains_js_1 = require("./chains.js");
|
|
16
20
|
Object.defineProperty(exports, "ChainCode", { enumerable: true, get: function () { return chains_js_1.ChainCode; } });
|
|
17
21
|
Object.defineProperty(exports, "chainCodes", { enumerable: true, get: function () { return chains_js_1.chainCodes; } });
|
|
22
|
+
Object.defineProperty(exports, "assertChainCode", { enumerable: true, get: function () { return chains_js_1.assertChainCode; } });
|
|
18
23
|
// Bitcoin-like PSBT (for all non-Zcash networks)
|
|
19
24
|
var BitGoPsbt_js_1 = require("./BitGoPsbt.js");
|
|
20
25
|
Object.defineProperty(exports, "BitGoPsbt", { enumerable: true, get: function () { return BitGoPsbt_js_1.BitGoPsbt; } });
|
|
@@ -49,3 +54,22 @@ Object.defineProperty(exports, "ZcashBitGoPsbt", { enumerable: true, get: functi
|
|
|
49
54
|
function supportsScriptType(coin, scriptType) {
|
|
50
55
|
return wasm_utxo_js_1.FixedScriptWalletNamespace.supports_script_type(coin, scriptType);
|
|
51
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Create an OP_RETURN output script with optional data
|
|
59
|
+
*
|
|
60
|
+
* @param data - Optional data bytes to include in the OP_RETURN script
|
|
61
|
+
* @returns The OP_RETURN script as a Uint8Array
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Empty OP_RETURN
|
|
66
|
+
* const script = createOpReturnScript();
|
|
67
|
+
*
|
|
68
|
+
* // OP_RETURN with data
|
|
69
|
+
* const data = new Uint8Array([1, 2, 3, 4]);
|
|
70
|
+
* const script = createOpReturnScript(data);
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
function createOpReturnScript(data) {
|
|
74
|
+
return wasm_utxo_js_1.FixedScriptWalletNamespace.create_op_return_script(data);
|
|
75
|
+
}
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* All output script types for fixed-script wallets (2-of-3 multisig)
|
|
3
3
|
*
|
|
4
|
-
* This
|
|
4
|
+
* This represents the abstract script type, independent of chain (external/internal).
|
|
5
5
|
* Use this for checking network support or when you need the script type without derivation info.
|
|
6
6
|
*/
|
|
7
|
-
export
|
|
7
|
+
export declare const outputScriptTypes: readonly ["p2sh", "p2shP2wsh", "p2wsh", "p2trLegacy", "p2trMusig2"];
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Output script type for fixed-script wallets
|
|
10
|
+
*
|
|
11
|
+
* Note: "p2tr" is an alias for "p2trLegacy" for backward compatibility.
|
|
12
|
+
*/
|
|
13
|
+
export type OutputScriptType = (typeof outputScriptTypes)[number] | "p2tr";
|
|
14
|
+
/**
|
|
15
|
+
* All input script types for fixed-script wallets
|
|
10
16
|
*
|
|
11
17
|
* These are more specific than output types and include single-sig and taproot variants.
|
|
12
18
|
*/
|
|
13
|
-
export
|
|
19
|
+
export declare const inputScriptTypes: readonly ["p2shP2pk", "p2sh", "p2shP2wsh", "p2wsh", "p2trLegacy", "p2trMusig2ScriptPath", "p2trMusig2KeyPath"];
|
|
20
|
+
/**
|
|
21
|
+
* Input script type for fixed-script wallets
|
|
22
|
+
*/
|
|
23
|
+
export type InputScriptType = (typeof inputScriptTypes)[number];
|
|
14
24
|
/**
|
|
15
25
|
* Union of all script types that can be checked for network support
|
|
16
26
|
*/
|
|
@@ -1,2 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inputScriptTypes = exports.outputScriptTypes = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* All output script types for fixed-script wallets (2-of-3 multisig)
|
|
6
|
+
*
|
|
7
|
+
* This represents the abstract script type, independent of chain (external/internal).
|
|
8
|
+
* Use this for checking network support or when you need the script type without derivation info.
|
|
9
|
+
*/
|
|
10
|
+
exports.outputScriptTypes = [
|
|
11
|
+
"p2sh",
|
|
12
|
+
"p2shP2wsh",
|
|
13
|
+
"p2wsh",
|
|
14
|
+
"p2trLegacy",
|
|
15
|
+
"p2trMusig2",
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* All input script types for fixed-script wallets
|
|
19
|
+
*
|
|
20
|
+
* These are more specific than output types and include single-sig and taproot variants.
|
|
21
|
+
*/
|
|
22
|
+
exports.inputScriptTypes = [
|
|
23
|
+
"p2shP2pk",
|
|
24
|
+
"p2sh",
|
|
25
|
+
"p2shP2wsh",
|
|
26
|
+
"p2wsh",
|
|
27
|
+
"p2trLegacy",
|
|
28
|
+
"p2trMusig2ScriptPath",
|
|
29
|
+
"p2trMusig2KeyPath",
|
|
30
|
+
];
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { BitGoPsbt } from "../fixedScriptWallet/BitGoPsbt.js";
|
|
2
|
+
import { RootWalletKeys } from "../fixedScriptWallet/RootWalletKeys.js";
|
|
3
|
+
import { BIP32 } from "../bip32.js";
|
|
4
|
+
import { inputScriptTypes, outputScriptTypes, type InputScriptType, type OutputScriptType, type ScriptId } from "../fixedScriptWallet/index.js";
|
|
5
|
+
import type { CoinName } from "../coinName.js";
|
|
6
|
+
import type { Triple } from "../triple.js";
|
|
7
|
+
export declare const signStages: readonly ["unsigned", "halfsigned", "fullsigned"];
|
|
8
|
+
export type SignStage = (typeof signStages)[number];
|
|
9
|
+
export declare const txFormats: readonly ["psbt", "psbt-lite"];
|
|
10
|
+
export type TxFormat = (typeof txFormats)[number];
|
|
11
|
+
/**
|
|
12
|
+
* Utility type to make union variants mutually exclusive.
|
|
13
|
+
* For each variant T in the union, adds `?: never` for all keys from other variants.
|
|
14
|
+
*/
|
|
15
|
+
type Exclusive<T, U = T> = T extends unknown ? T & Partial<Record<Exclude<U extends unknown ? keyof U : never, keyof T>, never>> : never;
|
|
16
|
+
/** Base input fields */
|
|
17
|
+
type InputBase = {
|
|
18
|
+
value: bigint;
|
|
19
|
+
/** Wallet keys to use. Defaults to root wallet keys */
|
|
20
|
+
walletKeys?: RootWalletKeys;
|
|
21
|
+
};
|
|
22
|
+
/** Input variant types */
|
|
23
|
+
type InputVariant = {
|
|
24
|
+
scriptType: InputScriptType;
|
|
25
|
+
index?: number;
|
|
26
|
+
} | {
|
|
27
|
+
scriptId: ScriptId;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Input configuration for AcidTest PSBT
|
|
31
|
+
*
|
|
32
|
+
* Either specify `scriptType` (chain derived from type, index defaults to position)
|
|
33
|
+
* or specify `scriptId` (explicit chain + index).
|
|
34
|
+
*/
|
|
35
|
+
export type Input = InputBase & Exclusive<InputVariant>;
|
|
36
|
+
/** Base output fields */
|
|
37
|
+
type OutputBase = {
|
|
38
|
+
value: bigint;
|
|
39
|
+
/** Wallet keys to use. Defaults to root wallet keys. null = external output (no bip32 derivation) */
|
|
40
|
+
walletKeys?: RootWalletKeys | null;
|
|
41
|
+
};
|
|
42
|
+
/** Output variant types */
|
|
43
|
+
type OutputVariant = {
|
|
44
|
+
scriptType: OutputScriptType;
|
|
45
|
+
index?: number;
|
|
46
|
+
} | {
|
|
47
|
+
scriptId: ScriptId;
|
|
48
|
+
} | {
|
|
49
|
+
opReturn: string;
|
|
50
|
+
} | {
|
|
51
|
+
address: string;
|
|
52
|
+
} | {
|
|
53
|
+
script: Uint8Array;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Output configuration for AcidTest PSBT
|
|
57
|
+
*
|
|
58
|
+
* Specify one of:
|
|
59
|
+
* - `scriptType` (chain derived from type, index defaults to position)
|
|
60
|
+
* - `scriptId` (explicit chain + index)
|
|
61
|
+
* - `opReturn` for OP_RETURN data output
|
|
62
|
+
* - `address` for address-based output
|
|
63
|
+
* - `script` for raw script output
|
|
64
|
+
*/
|
|
65
|
+
export type Output = OutputBase & Exclusive<OutputVariant>;
|
|
66
|
+
type SuiteConfig = {
|
|
67
|
+
/**
|
|
68
|
+
* By default, we exclude p2trMusig2ScriptPath from the inputs since
|
|
69
|
+
* it uses user + backup keys (not typical 2-of-3 with user + bitgo).
|
|
70
|
+
* Set to true to include this input type.
|
|
71
|
+
*/
|
|
72
|
+
includeP2trMusig2ScriptPath?: boolean;
|
|
73
|
+
};
|
|
74
|
+
export { inputScriptTypes, outputScriptTypes };
|
|
75
|
+
/**
|
|
76
|
+
* Creates a valid PSBT with as many features as possible (kitchen sink).
|
|
77
|
+
*
|
|
78
|
+
* - Inputs:
|
|
79
|
+
* - All wallet script types supported by the network
|
|
80
|
+
* - A p2shP2pk input (for replay protection)
|
|
81
|
+
* - Outputs:
|
|
82
|
+
* - All wallet script types supported by the network
|
|
83
|
+
* - A p2sh output with derivation info of a different wallet
|
|
84
|
+
* - A p2sh output with no derivation info (external output)
|
|
85
|
+
* - An OP_RETURN output
|
|
86
|
+
*
|
|
87
|
+
* Signature stages:
|
|
88
|
+
* - unsigned: No signatures
|
|
89
|
+
* - halfsigned: One signature per input (user key)
|
|
90
|
+
* - fullsigned: Two signatures per input (user + bitgo)
|
|
91
|
+
*
|
|
92
|
+
* Transaction formats:
|
|
93
|
+
* - psbt: Full PSBT with non_witness_utxo
|
|
94
|
+
* - psbt-lite: Only witness_utxo (no non_witness_utxo)
|
|
95
|
+
*/
|
|
96
|
+
export declare class AcidTest {
|
|
97
|
+
readonly network: CoinName;
|
|
98
|
+
readonly signStage: SignStage;
|
|
99
|
+
readonly txFormat: TxFormat;
|
|
100
|
+
readonly rootWalletKeys: RootWalletKeys;
|
|
101
|
+
readonly otherWalletKeys: RootWalletKeys;
|
|
102
|
+
readonly inputs: Input[];
|
|
103
|
+
readonly outputs: Output[];
|
|
104
|
+
private readonly userXprv;
|
|
105
|
+
private readonly backupXprv;
|
|
106
|
+
private readonly bitgoXprv;
|
|
107
|
+
constructor(network: CoinName, signStage: SignStage, txFormat: TxFormat, rootWalletKeys: RootWalletKeys, otherWalletKeys: RootWalletKeys, inputs: Input[], outputs: Output[], xprvTriple: Triple<BIP32>);
|
|
108
|
+
/**
|
|
109
|
+
* Create an AcidTest with specific configuration
|
|
110
|
+
*/
|
|
111
|
+
static withConfig(network: CoinName, signStage: SignStage, txFormat: TxFormat, suiteConfig?: SuiteConfig): AcidTest;
|
|
112
|
+
/**
|
|
113
|
+
* Get a human-readable name for this test configuration
|
|
114
|
+
*/
|
|
115
|
+
get name(): string;
|
|
116
|
+
/**
|
|
117
|
+
* Get the BIP32 user key for replay protection (p2shP2pk)
|
|
118
|
+
*/
|
|
119
|
+
getReplayProtectionKey(): BIP32;
|
|
120
|
+
/**
|
|
121
|
+
* Create the actual PSBT with all inputs and outputs
|
|
122
|
+
*/
|
|
123
|
+
createPsbt(): BitGoPsbt;
|
|
124
|
+
/**
|
|
125
|
+
* Sign the PSBT according to the sign stage
|
|
126
|
+
*/
|
|
127
|
+
private signPsbt;
|
|
128
|
+
/**
|
|
129
|
+
* Generate test suite for all networks, sign stages, and tx formats
|
|
130
|
+
*/
|
|
131
|
+
static forAllNetworksSignStagesTxFormats(suiteConfig?: SuiteConfig): AcidTest[];
|
|
132
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AcidTest = exports.outputScriptTypes = exports.inputScriptTypes = exports.txFormats = exports.signStages = void 0;
|
|
4
|
+
const BitGoPsbt_js_1 = require("../fixedScriptWallet/BitGoPsbt.js");
|
|
5
|
+
const ZcashBitGoPsbt_js_1 = require("../fixedScriptWallet/ZcashBitGoPsbt.js");
|
|
6
|
+
const ecpair_js_1 = require("../ecpair.js");
|
|
7
|
+
const index_js_1 = require("../fixedScriptWallet/index.js");
|
|
8
|
+
Object.defineProperty(exports, "inputScriptTypes", { enumerable: true, get: function () { return index_js_1.inputScriptTypes; } });
|
|
9
|
+
Object.defineProperty(exports, "outputScriptTypes", { enumerable: true, get: function () { return index_js_1.outputScriptTypes; } });
|
|
10
|
+
const coinName_js_1 = require("../coinName.js");
|
|
11
|
+
const keys_js_1 = require("./keys.js");
|
|
12
|
+
exports.signStages = ["unsigned", "halfsigned", "fullsigned"];
|
|
13
|
+
exports.txFormats = ["psbt", "psbt-lite"];
|
|
14
|
+
/**
|
|
15
|
+
* Creates a valid PSBT with as many features as possible (kitchen sink).
|
|
16
|
+
*
|
|
17
|
+
* - Inputs:
|
|
18
|
+
* - All wallet script types supported by the network
|
|
19
|
+
* - A p2shP2pk input (for replay protection)
|
|
20
|
+
* - Outputs:
|
|
21
|
+
* - All wallet script types supported by the network
|
|
22
|
+
* - A p2sh output with derivation info of a different wallet
|
|
23
|
+
* - A p2sh output with no derivation info (external output)
|
|
24
|
+
* - An OP_RETURN output
|
|
25
|
+
*
|
|
26
|
+
* Signature stages:
|
|
27
|
+
* - unsigned: No signatures
|
|
28
|
+
* - halfsigned: One signature per input (user key)
|
|
29
|
+
* - fullsigned: Two signatures per input (user + bitgo)
|
|
30
|
+
*
|
|
31
|
+
* Transaction formats:
|
|
32
|
+
* - psbt: Full PSBT with non_witness_utxo
|
|
33
|
+
* - psbt-lite: Only witness_utxo (no non_witness_utxo)
|
|
34
|
+
*/
|
|
35
|
+
class AcidTest {
|
|
36
|
+
network;
|
|
37
|
+
signStage;
|
|
38
|
+
txFormat;
|
|
39
|
+
rootWalletKeys;
|
|
40
|
+
otherWalletKeys;
|
|
41
|
+
inputs;
|
|
42
|
+
outputs;
|
|
43
|
+
// Store private keys for signing
|
|
44
|
+
userXprv;
|
|
45
|
+
backupXprv;
|
|
46
|
+
bitgoXprv;
|
|
47
|
+
constructor(network, signStage, txFormat, rootWalletKeys, otherWalletKeys, inputs, outputs, xprvTriple) {
|
|
48
|
+
this.network = network;
|
|
49
|
+
this.signStage = signStage;
|
|
50
|
+
this.txFormat = txFormat;
|
|
51
|
+
this.rootWalletKeys = rootWalletKeys;
|
|
52
|
+
this.otherWalletKeys = otherWalletKeys;
|
|
53
|
+
this.inputs = inputs;
|
|
54
|
+
this.outputs = outputs;
|
|
55
|
+
this.userXprv = xprvTriple[0];
|
|
56
|
+
this.backupXprv = xprvTriple[1];
|
|
57
|
+
this.bitgoXprv = xprvTriple[2];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create an AcidTest with specific configuration
|
|
61
|
+
*/
|
|
62
|
+
static withConfig(network, signStage, txFormat, suiteConfig = {}) {
|
|
63
|
+
const rootWalletKeys = (0, keys_js_1.getDefaultWalletKeys)();
|
|
64
|
+
const otherWalletKeys = (0, keys_js_1.getWalletKeysForSeed)("too many secrets");
|
|
65
|
+
// Filter inputs based on network support
|
|
66
|
+
const inputs = index_js_1.inputScriptTypes
|
|
67
|
+
.filter((scriptType) => {
|
|
68
|
+
// p2shP2pk is always supported (single-sig replay protection)
|
|
69
|
+
if (scriptType === "p2shP2pk")
|
|
70
|
+
return true;
|
|
71
|
+
// Map input script types to output script types for support check
|
|
72
|
+
if (scriptType === "p2trMusig2KeyPath" || scriptType === "p2trMusig2ScriptPath") {
|
|
73
|
+
return (0, index_js_1.supportsScriptType)(network, "p2trMusig2");
|
|
74
|
+
}
|
|
75
|
+
return (0, index_js_1.supportsScriptType)(network, scriptType);
|
|
76
|
+
})
|
|
77
|
+
.filter((scriptType) => (suiteConfig.includeP2trMusig2ScriptPath ?? false) ||
|
|
78
|
+
scriptType !== "p2trMusig2ScriptPath")
|
|
79
|
+
.map((scriptType, index) => ({
|
|
80
|
+
scriptType,
|
|
81
|
+
value: BigInt(10000 + index * 10000), // Deterministic amounts
|
|
82
|
+
}));
|
|
83
|
+
// Filter outputs based on network support
|
|
84
|
+
const outputs = index_js_1.outputScriptTypes
|
|
85
|
+
.filter((scriptType) => (0, index_js_1.supportsScriptType)(network, scriptType))
|
|
86
|
+
.map((scriptType, index) => ({
|
|
87
|
+
scriptType,
|
|
88
|
+
value: BigInt(900 + index * 100), // Deterministic amounts
|
|
89
|
+
}));
|
|
90
|
+
// Test other wallet output (with derivation info)
|
|
91
|
+
outputs.push({ scriptType: "p2sh", value: BigInt(800), walletKeys: otherWalletKeys });
|
|
92
|
+
// Test non-wallet output (no derivation info)
|
|
93
|
+
outputs.push({ scriptType: "p2sh", value: BigInt(700), walletKeys: null });
|
|
94
|
+
// Test OP_RETURN output
|
|
95
|
+
outputs.push({ opReturn: "setec astronomy", value: BigInt(0) });
|
|
96
|
+
// Get private keys for signing
|
|
97
|
+
const xprvTriple = (0, keys_js_1.getKeyTriple)("default");
|
|
98
|
+
return new AcidTest(network, signStage, txFormat, rootWalletKeys, otherWalletKeys, inputs, outputs, xprvTriple);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get a human-readable name for this test configuration
|
|
102
|
+
*/
|
|
103
|
+
get name() {
|
|
104
|
+
return `${this.network} ${this.signStage} ${this.txFormat}`;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get the BIP32 user key for replay protection (p2shP2pk)
|
|
108
|
+
*/
|
|
109
|
+
getReplayProtectionKey() {
|
|
110
|
+
return this.rootWalletKeys.userKey();
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create the actual PSBT with all inputs and outputs
|
|
114
|
+
*/
|
|
115
|
+
createPsbt() {
|
|
116
|
+
// Use ZcashBitGoPsbt for Zcash networks
|
|
117
|
+
const isZcash = this.network === "zec" || this.network === "tzec";
|
|
118
|
+
const psbt = isZcash
|
|
119
|
+
? ZcashBitGoPsbt_js_1.ZcashBitGoPsbt.createEmptyWithConsensusBranchId(this.network, this.rootWalletKeys, {
|
|
120
|
+
version: 2,
|
|
121
|
+
lockTime: 0,
|
|
122
|
+
consensusBranchId: 0xc2d6d0b4, // NU5
|
|
123
|
+
})
|
|
124
|
+
: BitGoPsbt_js_1.BitGoPsbt.createEmpty(this.network, this.rootWalletKeys, {
|
|
125
|
+
version: 2,
|
|
126
|
+
lockTime: 0,
|
|
127
|
+
});
|
|
128
|
+
// Add inputs with deterministic outpoints
|
|
129
|
+
this.inputs.forEach((input, index) => {
|
|
130
|
+
// Resolve scriptId: either from explicit scriptId or from scriptType + index
|
|
131
|
+
const scriptId = input.scriptId ?? {
|
|
132
|
+
chain: index_js_1.ChainCode.value("p2sh", "external"),
|
|
133
|
+
index: input.index ?? index,
|
|
134
|
+
};
|
|
135
|
+
const walletKeys = input.walletKeys ?? this.rootWalletKeys;
|
|
136
|
+
// Get scriptType: either explicit or derive from scriptId chain
|
|
137
|
+
const scriptType = input.scriptType ?? index_js_1.ChainCode.scriptType((0, index_js_1.assertChainCode)(scriptId.chain));
|
|
138
|
+
if (scriptType === "p2shP2pk") {
|
|
139
|
+
// Add replay protection input
|
|
140
|
+
const replayKey = this.getReplayProtectionKey();
|
|
141
|
+
// Convert BIP32 to ECPair using public key
|
|
142
|
+
const ecpair = ecpair_js_1.ECPair.fromPublicKey(replayKey.publicKey);
|
|
143
|
+
psbt.addReplayProtectionInput({
|
|
144
|
+
txid: "0".repeat(64),
|
|
145
|
+
vout: index,
|
|
146
|
+
value: input.value,
|
|
147
|
+
}, ecpair);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// Determine signing path based on input type
|
|
151
|
+
let signPath;
|
|
152
|
+
if (scriptType === "p2trMusig2ScriptPath") {
|
|
153
|
+
// Script path uses user + backup
|
|
154
|
+
signPath = { signer: "user", cosigner: "backup" };
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// Default: user + bitgo
|
|
158
|
+
signPath = { signer: "user", cosigner: "bitgo" };
|
|
159
|
+
}
|
|
160
|
+
psbt.addWalletInput({
|
|
161
|
+
txid: "0".repeat(64),
|
|
162
|
+
vout: index,
|
|
163
|
+
value: input.value,
|
|
164
|
+
}, walletKeys, {
|
|
165
|
+
scriptId,
|
|
166
|
+
signPath,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
// Add outputs
|
|
171
|
+
this.outputs.forEach((output, index) => {
|
|
172
|
+
if (output.opReturn !== undefined) {
|
|
173
|
+
// OP_RETURN output
|
|
174
|
+
const data = new TextEncoder().encode(output.opReturn);
|
|
175
|
+
const script = (0, index_js_1.createOpReturnScript)(data);
|
|
176
|
+
psbt.addOutput(script, output.value);
|
|
177
|
+
}
|
|
178
|
+
else if (output.address !== undefined) {
|
|
179
|
+
// Address-based output
|
|
180
|
+
psbt.addOutput(output.address, output.value);
|
|
181
|
+
}
|
|
182
|
+
else if (output.script !== undefined) {
|
|
183
|
+
// Raw script output
|
|
184
|
+
psbt.addOutput(output.script, output.value);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
// Wallet output: resolve scriptId from scriptType or explicit scriptId
|
|
188
|
+
const scriptId = output.scriptId ?? {
|
|
189
|
+
chain: output.scriptType ? index_js_1.ChainCode.value(output.scriptType, "external") : 0,
|
|
190
|
+
index: output.index ?? index,
|
|
191
|
+
};
|
|
192
|
+
if (output.walletKeys === null) {
|
|
193
|
+
// External output (no wallet keys, no bip32 derivation)
|
|
194
|
+
// Use high index for external outputs if not specified
|
|
195
|
+
const externalScriptId = output.scriptId ?? {
|
|
196
|
+
chain: scriptId.chain,
|
|
197
|
+
index: output.index ?? 1000 + index,
|
|
198
|
+
};
|
|
199
|
+
const script = (0, index_js_1.outputScript)(this.rootWalletKeys, externalScriptId.chain, externalScriptId.index, this.network);
|
|
200
|
+
psbt.addOutput(script, output.value);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
// Wallet output (with or without different wallet keys)
|
|
204
|
+
const walletKeys = output.walletKeys ?? this.rootWalletKeys;
|
|
205
|
+
psbt.addWalletOutput(walletKeys, {
|
|
206
|
+
chain: scriptId.chain,
|
|
207
|
+
index: scriptId.index,
|
|
208
|
+
value: output.value,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
// Apply signing based on stage
|
|
214
|
+
if (this.signStage !== "unsigned") {
|
|
215
|
+
this.signPsbt(psbt);
|
|
216
|
+
}
|
|
217
|
+
return psbt;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Sign the PSBT according to the sign stage
|
|
221
|
+
*/
|
|
222
|
+
signPsbt(psbt) {
|
|
223
|
+
// Use private keys stored in constructor
|
|
224
|
+
const userKey = this.userXprv;
|
|
225
|
+
const backupKey = this.backupXprv;
|
|
226
|
+
const bitgoKey = this.bitgoXprv;
|
|
227
|
+
// Generate MuSig2 nonces for user if needed
|
|
228
|
+
const hasMusig2Inputs = this.inputs.some((input) => input.scriptType === "p2trMusig2KeyPath" || input.scriptType === "p2trMusig2ScriptPath");
|
|
229
|
+
if (hasMusig2Inputs) {
|
|
230
|
+
const isZcash = this.network === "zec" || this.network === "tzec";
|
|
231
|
+
if (isZcash) {
|
|
232
|
+
throw new Error("Zcash does not support MuSig2/Taproot inputs");
|
|
233
|
+
}
|
|
234
|
+
// Generate nonces with user key
|
|
235
|
+
psbt.generateMusig2Nonces(userKey);
|
|
236
|
+
if (this.signStage === "fullsigned") {
|
|
237
|
+
// Create a second PSBT with cosigner nonces for combination
|
|
238
|
+
// For p2trMusig2ScriptPath use backup, for p2trMusig2KeyPath use bitgo
|
|
239
|
+
// Since we might have both types, we need to generate nonces separately
|
|
240
|
+
const bytes = psbt.serialize();
|
|
241
|
+
const hasKeyPath = this.inputs.some((input) => input.scriptType === "p2trMusig2KeyPath");
|
|
242
|
+
const hasScriptPath = this.inputs.some((input) => input.scriptType === "p2trMusig2ScriptPath");
|
|
243
|
+
if (hasKeyPath && !hasScriptPath) {
|
|
244
|
+
// Only key path inputs - generate bitgo nonces for all
|
|
245
|
+
const psbt2 = BitGoPsbt_js_1.BitGoPsbt.fromBytes(bytes, this.network);
|
|
246
|
+
psbt2.generateMusig2Nonces(bitgoKey);
|
|
247
|
+
psbt.combineMusig2Nonces(psbt2);
|
|
248
|
+
}
|
|
249
|
+
else if (hasScriptPath && !hasKeyPath) {
|
|
250
|
+
// Only script path inputs - generate backup nonces for all
|
|
251
|
+
const psbt2 = BitGoPsbt_js_1.BitGoPsbt.fromBytes(bytes, this.network);
|
|
252
|
+
psbt2.generateMusig2Nonces(backupKey);
|
|
253
|
+
psbt.combineMusig2Nonces(psbt2);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
const psbt2 = BitGoPsbt_js_1.BitGoPsbt.fromBytes(bytes, this.network);
|
|
257
|
+
psbt2.generateMusig2Nonces(bitgoKey);
|
|
258
|
+
psbt.combineMusig2Nonces(psbt2);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Sign all wallet inputs with user key (bulk - more efficient)
|
|
263
|
+
psbt.sign(userKey);
|
|
264
|
+
// Sign replay protection inputs with raw private key
|
|
265
|
+
const hasReplayProtection = this.inputs.some((input) => input.scriptType === "p2shP2pk");
|
|
266
|
+
if (hasReplayProtection) {
|
|
267
|
+
if (!userKey.privateKey) {
|
|
268
|
+
throw new Error("User key must have private key for signing replay protection inputs");
|
|
269
|
+
}
|
|
270
|
+
psbt.sign(userKey.privateKey);
|
|
271
|
+
}
|
|
272
|
+
// For fullsigned, sign with cosigner
|
|
273
|
+
if (this.signStage === "fullsigned") {
|
|
274
|
+
const hasScriptPath = this.inputs.some((input) => input.scriptType === "p2trMusig2ScriptPath");
|
|
275
|
+
if (hasScriptPath) {
|
|
276
|
+
// Mixed case: script path uses backup, others use bitgo
|
|
277
|
+
// Need per-input signing (slow) to handle different cosigners
|
|
278
|
+
this.inputs.forEach((input, index) => {
|
|
279
|
+
if (input.scriptType === "p2shP2pk") {
|
|
280
|
+
// Replay protection is single-sig, already fully signed
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (input.scriptType === "p2trMusig2ScriptPath") {
|
|
284
|
+
psbt.signInput(index, backupKey);
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
psbt.signInput(index, bitgoKey);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
// No script path - can use bulk signing with bitgo (fast)
|
|
293
|
+
psbt.sign(bitgoKey);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Generate test suite for all networks, sign stages, and tx formats
|
|
299
|
+
*/
|
|
300
|
+
static forAllNetworksSignStagesTxFormats(suiteConfig = {}) {
|
|
301
|
+
return coinName_js_1.coinNames
|
|
302
|
+
.filter((network) => (0, coinName_js_1.isMainnet)(network) && network !== "bsv") // Exclude bitcoinsv
|
|
303
|
+
.flatMap((network) => exports.signStages.flatMap((signStage) => exports.txFormats.map((txFormat) => AcidTest.withConfig(network, signStage, txFormat, suiteConfig))));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
exports.AcidTest = AcidTest;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./keys.js"), exports);
|
|
18
|
+
__exportStar(require("./AcidTest.js"), exports);
|