@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.
Files changed (33) hide show
  1. package/dist/cjs/js/fixedScriptWallet/chains.d.ts +5 -0
  2. package/dist/cjs/js/fixedScriptWallet/chains.js +6 -2
  3. package/dist/cjs/js/fixedScriptWallet/index.d.ts +19 -2
  4. package/dist/cjs/js/fixedScriptWallet/index.js +25 -1
  5. package/dist/cjs/js/fixedScriptWallet/scriptType.d.ts +15 -5
  6. package/dist/cjs/js/fixedScriptWallet/scriptType.js +28 -0
  7. package/dist/cjs/js/testutils/AcidTest.d.ts +132 -0
  8. package/dist/cjs/js/testutils/AcidTest.js +306 -0
  9. package/dist/cjs/js/testutils/index.d.ts +2 -0
  10. package/dist/cjs/js/testutils/index.js +18 -0
  11. package/dist/cjs/js/testutils/keys.d.ts +76 -0
  12. package/dist/cjs/js/testutils/keys.js +154 -0
  13. package/dist/cjs/js/wasm/wasm_utxo.d.ts +10 -0
  14. package/dist/cjs/js/wasm/wasm_utxo.js +31 -0
  15. package/dist/cjs/js/wasm/wasm_utxo_bg.wasm +0 -0
  16. package/dist/cjs/js/wasm/wasm_utxo_bg.wasm.d.ts +2 -1
  17. package/dist/esm/js/fixedScriptWallet/chains.d.ts +5 -0
  18. package/dist/esm/js/fixedScriptWallet/chains.js +6 -3
  19. package/dist/esm/js/fixedScriptWallet/index.d.ts +19 -2
  20. package/dist/esm/js/fixedScriptWallet/index.js +21 -1
  21. package/dist/esm/js/fixedScriptWallet/scriptType.d.ts +15 -5
  22. package/dist/esm/js/fixedScriptWallet/scriptType.js +27 -1
  23. package/dist/esm/js/testutils/AcidTest.d.ts +132 -0
  24. package/dist/esm/js/testutils/AcidTest.js +302 -0
  25. package/dist/esm/js/testutils/index.d.ts +2 -0
  26. package/dist/esm/js/testutils/index.js +2 -0
  27. package/dist/esm/js/testutils/keys.d.ts +76 -0
  28. package/dist/esm/js/testutils/keys.js +113 -0
  29. package/dist/esm/js/wasm/wasm_utxo.d.ts +10 -0
  30. package/dist/esm/js/wasm/wasm_utxo_bg.js +31 -0
  31. package/dist/esm/js/wasm/wasm_utxo_bg.wasm +0 -0
  32. package/dist/esm/js/wasm/wasm_utxo_bg.wasm.d.ts +2 -1
  33. 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
- // Initialize from WASM (called once at load time)
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 from WASM: ${n}`);
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
- * Fixed-script wallet output script types (2-of-3 multisig)
2
+ * All output script types for fixed-script wallets (2-of-3 multisig)
3
3
  *
4
- * This type represents the abstract script type, independent of chain (external/internal).
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 type OutputScriptType = "p2sh" | "p2shP2wsh" | "p2wsh" | "p2tr" | "p2trLegacy" | "p2trMusig2";
7
+ export declare const outputScriptTypes: readonly ["p2sh", "p2shP2wsh", "p2wsh", "p2trLegacy", "p2trMusig2"];
8
8
  /**
9
- * Input script types for fixed-script wallets
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 type InputScriptType = "p2shP2pk" | "p2sh" | "p2shP2wsh" | "p2wsh" | "p2trLegacy" | "p2trMusig2ScriptPath" | "p2trMusig2KeyPath";
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,2 @@
1
+ export * from "./keys.js";
2
+ export * from "./AcidTest.js";
@@ -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);