@bitgo/wasm-utxo 1.7.0 → 1.8.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.
@@ -100,6 +100,46 @@ export declare class BitGoPsbt {
100
100
  * ```
101
101
  */
102
102
  verifySignature(inputIndex: number, key: BIP32Arg | ECPairArg): boolean;
103
+ /**
104
+ * Sign a single input with a private key
105
+ *
106
+ * This method signs a specific input using the provided key. It accepts either:
107
+ * - An xpriv (BIP32Arg: base58 string, BIP32 instance, or WasmBIP32) for wallet inputs - derives the key and signs
108
+ * - A raw privkey (ECPairArg: Buffer, ECPair instance, or WasmECPair) for replay protection inputs - signs directly
109
+ *
110
+ * This method automatically detects and handles different input types:
111
+ * - For regular inputs: uses standard PSBT signing
112
+ * - For MuSig2 inputs: uses the FirstRound state stored by generateMusig2Nonces()
113
+ * - For replay protection inputs: signs with legacy P2SH sighash
114
+ *
115
+ * @param inputIndex - The index of the input to sign (0-based)
116
+ * @param key - Either an xpriv (BIP32Arg) or a raw privkey (ECPairArg)
117
+ * @throws Error if signing fails, or if generateMusig2Nonces() was not called first for MuSig2 inputs
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * // Parse transaction to identify input types
122
+ * const parsed = psbt.parseTransactionWithWalletKeys(walletKeys, replayProtection);
123
+ *
124
+ * // Sign regular wallet inputs with xpriv
125
+ * for (let i = 0; i < parsed.inputs.length; i++) {
126
+ * const input = parsed.inputs[i];
127
+ * if (input.scriptId !== null && input.scriptType !== "p2shP2pk") {
128
+ * psbt.sign(i, userXpriv);
129
+ * }
130
+ * }
131
+ *
132
+ * // Sign replay protection inputs with raw privkey
133
+ * const userPrivkey = bip32.fromBase58(userXpriv).privateKey!;
134
+ * for (let i = 0; i < parsed.inputs.length; i++) {
135
+ * const input = parsed.inputs[i];
136
+ * if (input.scriptType === "p2shP2pk") {
137
+ * psbt.sign(i, userPrivkey);
138
+ * }
139
+ * }
140
+ * ```
141
+ */
142
+ sign(inputIndex: number, key: BIP32Arg | ECPairArg): void;
103
143
  /**
104
144
  * @deprecated - use verifySignature with the replay protection key instead
105
145
  *
@@ -127,6 +167,65 @@ export declare class BitGoPsbt {
127
167
  * @returns The serialized PSBT as a byte array
128
168
  */
129
169
  serialize(): Uint8Array;
170
+ /**
171
+ * Generate and store MuSig2 nonces for all MuSig2 inputs
172
+ *
173
+ * This method generates nonces using the State-Machine API and stores them in the PSBT.
174
+ * The nonces are stored as proprietary fields in the PSBT and will be included when serialized.
175
+ * After ALL participants have generated their nonces, you can sign MuSig2 inputs using
176
+ * sign().
177
+ *
178
+ * @param key - The extended private key (xpriv) for signing. Can be a base58 string, BIP32 instance, or WasmBIP32
179
+ * @param sessionId - Optional 32-byte session ID for nonce generation. **Only allowed on testnets**.
180
+ * On mainnets, a secure random session ID is always generated automatically.
181
+ * Must be unique per signing session.
182
+ * @throws Error if nonce generation fails, sessionId length is invalid, or custom sessionId is
183
+ * provided on a mainnet (security restriction)
184
+ *
185
+ * @security The sessionId MUST be cryptographically random and unique for each signing session.
186
+ * Never reuse a sessionId with the same key! On mainnets, sessionId is always randomly
187
+ * generated for security. Custom sessionId is only allowed on testnets for testing purposes.
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * // Phase 1: Both parties generate nonces (with auto-generated session ID)
192
+ * psbt.generateMusig2Nonces(userXpriv);
193
+ * // Nonces are stored in the PSBT
194
+ * // Send PSBT to counterparty
195
+ *
196
+ * // Phase 2: After receiving counterparty PSBT with their nonces
197
+ * const counterpartyPsbt = BitGoPsbt.fromBytes(counterpartyPsbtBytes, network);
198
+ * psbt.combineMusig2Nonces(counterpartyPsbt);
199
+ * // Sign MuSig2 key path inputs
200
+ * const parsed = psbt.parseTransactionWithWalletKeys(walletKeys, replayProtection);
201
+ * for (let i = 0; i < parsed.inputs.length; i++) {
202
+ * if (parsed.inputs[i].scriptType === "p2trMusig2KeyPath") {
203
+ * psbt.sign(i, userXpriv);
204
+ * }
205
+ * }
206
+ * ```
207
+ */
208
+ generateMusig2Nonces(key: BIP32Arg, sessionId?: Uint8Array): void;
209
+ /**
210
+ * Combine/merge data from another PSBT into this one
211
+ *
212
+ * This method copies MuSig2 nonces and signatures (proprietary key-value pairs) from the
213
+ * source PSBT to this PSBT. This is useful for merging PSBTs during the nonce exchange
214
+ * and signature collection phases.
215
+ *
216
+ * @param sourcePsbt - The source PSBT containing data to merge
217
+ * @throws Error if networks don't match
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * // After receiving counterparty's PSBT with their nonces
222
+ * const counterpartyPsbt = BitGoPsbt.fromBytes(counterpartyPsbtBytes, network);
223
+ * psbt.combineMusig2Nonces(counterpartyPsbt);
224
+ * // Now can sign with all nonces present
225
+ * psbt.sign(0, userXpriv);
226
+ * ```
227
+ */
228
+ combineMusig2Nonces(sourcePsbt: BitGoPsbt): void;
130
229
  /**
131
230
  * Finalize all inputs in the PSBT
132
231
  *
@@ -99,6 +99,64 @@ class BitGoPsbt {
99
99
  const wasmECPair = ecpair_js_1.ECPair.from(key).wasm;
100
100
  return this.wasm.verify_signature_with_pub(inputIndex, wasmECPair);
101
101
  }
102
+ /**
103
+ * Sign a single input with a private key
104
+ *
105
+ * This method signs a specific input using the provided key. It accepts either:
106
+ * - An xpriv (BIP32Arg: base58 string, BIP32 instance, or WasmBIP32) for wallet inputs - derives the key and signs
107
+ * - A raw privkey (ECPairArg: Buffer, ECPair instance, or WasmECPair) for replay protection inputs - signs directly
108
+ *
109
+ * This method automatically detects and handles different input types:
110
+ * - For regular inputs: uses standard PSBT signing
111
+ * - For MuSig2 inputs: uses the FirstRound state stored by generateMusig2Nonces()
112
+ * - For replay protection inputs: signs with legacy P2SH sighash
113
+ *
114
+ * @param inputIndex - The index of the input to sign (0-based)
115
+ * @param key - Either an xpriv (BIP32Arg) or a raw privkey (ECPairArg)
116
+ * @throws Error if signing fails, or if generateMusig2Nonces() was not called first for MuSig2 inputs
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * // Parse transaction to identify input types
121
+ * const parsed = psbt.parseTransactionWithWalletKeys(walletKeys, replayProtection);
122
+ *
123
+ * // Sign regular wallet inputs with xpriv
124
+ * for (let i = 0; i < parsed.inputs.length; i++) {
125
+ * const input = parsed.inputs[i];
126
+ * if (input.scriptId !== null && input.scriptType !== "p2shP2pk") {
127
+ * psbt.sign(i, userXpriv);
128
+ * }
129
+ * }
130
+ *
131
+ * // Sign replay protection inputs with raw privkey
132
+ * const userPrivkey = bip32.fromBase58(userXpriv).privateKey!;
133
+ * for (let i = 0; i < parsed.inputs.length; i++) {
134
+ * const input = parsed.inputs[i];
135
+ * if (input.scriptType === "p2shP2pk") {
136
+ * psbt.sign(i, userPrivkey);
137
+ * }
138
+ * }
139
+ * ```
140
+ */
141
+ sign(inputIndex, key) {
142
+ // Detect key type
143
+ // If string or has 'derive' method → BIP32Arg
144
+ // Otherwise → ECPairArg
145
+ if (typeof key === "string" ||
146
+ (typeof key === "object" &&
147
+ key !== null &&
148
+ "derive" in key &&
149
+ typeof key.derive === "function")) {
150
+ // It's a BIP32Arg
151
+ const wasmKey = bip32_js_1.BIP32.from(key);
152
+ this.wasm.sign_with_xpriv(inputIndex, wasmKey.wasm);
153
+ }
154
+ else {
155
+ // It's an ECPairArg
156
+ const wasmKey = ecpair_js_1.ECPair.from(key);
157
+ this.wasm.sign_with_privkey(inputIndex, wasmKey.wasm);
158
+ }
159
+ }
102
160
  /**
103
161
  * @deprecated - use verifySignature with the replay protection key instead
104
162
  *
@@ -131,6 +189,70 @@ class BitGoPsbt {
131
189
  serialize() {
132
190
  return this.wasm.serialize();
133
191
  }
192
+ /**
193
+ * Generate and store MuSig2 nonces for all MuSig2 inputs
194
+ *
195
+ * This method generates nonces using the State-Machine API and stores them in the PSBT.
196
+ * The nonces are stored as proprietary fields in the PSBT and will be included when serialized.
197
+ * After ALL participants have generated their nonces, you can sign MuSig2 inputs using
198
+ * sign().
199
+ *
200
+ * @param key - The extended private key (xpriv) for signing. Can be a base58 string, BIP32 instance, or WasmBIP32
201
+ * @param sessionId - Optional 32-byte session ID for nonce generation. **Only allowed on testnets**.
202
+ * On mainnets, a secure random session ID is always generated automatically.
203
+ * Must be unique per signing session.
204
+ * @throws Error if nonce generation fails, sessionId length is invalid, or custom sessionId is
205
+ * provided on a mainnet (security restriction)
206
+ *
207
+ * @security The sessionId MUST be cryptographically random and unique for each signing session.
208
+ * Never reuse a sessionId with the same key! On mainnets, sessionId is always randomly
209
+ * generated for security. Custom sessionId is only allowed on testnets for testing purposes.
210
+ *
211
+ * @example
212
+ * ```typescript
213
+ * // Phase 1: Both parties generate nonces (with auto-generated session ID)
214
+ * psbt.generateMusig2Nonces(userXpriv);
215
+ * // Nonces are stored in the PSBT
216
+ * // Send PSBT to counterparty
217
+ *
218
+ * // Phase 2: After receiving counterparty PSBT with their nonces
219
+ * const counterpartyPsbt = BitGoPsbt.fromBytes(counterpartyPsbtBytes, network);
220
+ * psbt.combineMusig2Nonces(counterpartyPsbt);
221
+ * // Sign MuSig2 key path inputs
222
+ * const parsed = psbt.parseTransactionWithWalletKeys(walletKeys, replayProtection);
223
+ * for (let i = 0; i < parsed.inputs.length; i++) {
224
+ * if (parsed.inputs[i].scriptType === "p2trMusig2KeyPath") {
225
+ * psbt.sign(i, userXpriv);
226
+ * }
227
+ * }
228
+ * ```
229
+ */
230
+ generateMusig2Nonces(key, sessionId) {
231
+ const wasmKey = bip32_js_1.BIP32.from(key);
232
+ this.wasm.generate_musig2_nonces(wasmKey.wasm, sessionId);
233
+ }
234
+ /**
235
+ * Combine/merge data from another PSBT into this one
236
+ *
237
+ * This method copies MuSig2 nonces and signatures (proprietary key-value pairs) from the
238
+ * source PSBT to this PSBT. This is useful for merging PSBTs during the nonce exchange
239
+ * and signature collection phases.
240
+ *
241
+ * @param sourcePsbt - The source PSBT containing data to merge
242
+ * @throws Error if networks don't match
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * // After receiving counterparty's PSBT with their nonces
247
+ * const counterpartyPsbt = BitGoPsbt.fromBytes(counterpartyPsbtBytes, network);
248
+ * psbt.combineMusig2Nonces(counterpartyPsbt);
249
+ * // Now can sign with all nonces present
250
+ * psbt.sign(0, userXpriv);
251
+ * ```
252
+ */
253
+ combineMusig2Nonces(sourcePsbt) {
254
+ this.wasm.combine_musig2_nonces(sourcePsbt.wasm);
255
+ }
134
256
  /**
135
257
  * Finalize all inputs in the PSBT
136
258
  *
@@ -19,6 +19,46 @@ export class BitGoPsbt {
19
19
  * Get the unsigned transaction ID
20
20
  */
21
21
  unsigned_txid(): string;
22
+ /**
23
+ * Sign a single input with an extended private key (xpriv)
24
+ *
25
+ * This method signs a specific input using the provided xpriv. It accepts:
26
+ * - An xpriv (WasmBIP32) for wallet inputs - derives the key and signs
27
+ *
28
+ * This method automatically detects and handles different input types:
29
+ * - For regular inputs: uses standard PSBT signing
30
+ * - For MuSig2 inputs: uses the FirstRound state stored by generate_musig2_nonces()
31
+ * - For replay protection inputs: returns error (use sign_with_privkey instead)
32
+ *
33
+ * # Arguments
34
+ * - `input_index`: The index of the input to sign (0-based)
35
+ * - `xpriv`: The extended private key as a WasmBIP32 instance
36
+ *
37
+ * # Returns
38
+ * - `Ok(())` if signing was successful
39
+ * - `Err(WasmUtxoError)` if signing fails
40
+ */
41
+ sign_with_xpriv(input_index: number, xpriv: WasmBIP32): void;
42
+ /**
43
+ * Sign a single input with a raw private key
44
+ *
45
+ * This method signs a specific input using the provided ECPair. It accepts:
46
+ * - A raw privkey (WasmECPair) for replay protection inputs - signs directly
47
+ *
48
+ * This method automatically detects and handles different input types:
49
+ * - For replay protection inputs: signs with legacy P2SH sighash
50
+ * - For regular inputs: uses standard PSBT signing
51
+ * - For MuSig2 inputs: returns error (requires FirstRound, use sign_with_xpriv instead)
52
+ *
53
+ * # Arguments
54
+ * - `input_index`: The index of the input to sign (0-based)
55
+ * - `ecpair`: The ECPair containing the private key
56
+ *
57
+ * # Returns
58
+ * - `Ok(())` if signing was successful
59
+ * - `Err(WasmUtxoError)` if signing fails
60
+ */
61
+ sign_with_privkey(input_index: number, ecpair: WasmECPair): void;
22
62
  /**
23
63
  * Extract the final transaction from a finalized PSBT
24
64
  *
@@ -41,6 +81,52 @@ export class BitGoPsbt {
41
81
  * - `Err(WasmUtxoError)` if any input failed to finalize
42
82
  */
43
83
  finalize_all_inputs(): void;
84
+ /**
85
+ * Combine/merge data from another PSBT into this one
86
+ *
87
+ * This method copies MuSig2 nonces and signatures (proprietary key-value pairs) from the
88
+ * source PSBT to this PSBT. This is useful for merging PSBTs during the nonce exchange
89
+ * and signature collection phases.
90
+ *
91
+ * # Arguments
92
+ * * `source_psbt` - The source PSBT containing data to merge
93
+ *
94
+ * # Returns
95
+ * Ok(()) if data was successfully merged
96
+ *
97
+ * # Errors
98
+ * Returns error if networks don't match
99
+ */
100
+ combine_musig2_nonces(source_psbt: BitGoPsbt): void;
101
+ /**
102
+ * Generate and store MuSig2 nonces for all MuSig2 inputs
103
+ *
104
+ * This method generates nonces using the State-Machine API and stores them in the PSBT.
105
+ * The nonces are stored as proprietary fields in the PSBT and will be included when serialized.
106
+ * After ALL participants have generated their nonces, they can sign MuSig2 inputs using
107
+ * sign_with_xpriv().
108
+ *
109
+ * # Arguments
110
+ * * `xpriv` - The extended private key (xpriv) for signing
111
+ * * `session_id_bytes` - Optional 32-byte session ID for nonce generation. **Only allowed on testnets**.
112
+ * On mainnets, a secure random session ID is always generated automatically.
113
+ * Must be unique per signing session.
114
+ *
115
+ * # Returns
116
+ * Ok(()) if nonces were successfully generated and stored
117
+ *
118
+ * # Errors
119
+ * Returns error if:
120
+ * - Nonce generation fails
121
+ * - session_id length is invalid
122
+ * - Custom session_id is provided on a mainnet (security restriction)
123
+ *
124
+ * # Security
125
+ * The session_id MUST be cryptographically random and unique for each signing session.
126
+ * Never reuse a session_id with the same key! On mainnets, session_id is always randomly
127
+ * generated for security. Custom session_id is only allowed on testnets for testing purposes.
128
+ */
129
+ generate_musig2_nonces(xpriv: WasmBIP32, session_id_bytes?: Uint8Array | null): void;
44
130
  /**
45
131
  * Verify if a valid signature exists for a given ECPair key at the specified input index
46
132
  *