Skip to content

Commit 6b768cf

Browse files
Merge pull request #58 from BitGo/BTC-2652.verify-signature
feat(wasm-utxo): add PSBT signature verification and txid methods
2 parents 58ddf16 + 12180b0 commit 6b768cf

File tree

8 files changed

+1184
-99
lines changed

8 files changed

+1184
-99
lines changed

packages/wasm-utxo/js/fixedScriptWallet.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ export class BitGoPsbt {
9494
return new BitGoPsbt(wasm);
9595
}
9696

97+
/**
98+
* Get the unsigned transaction ID
99+
* @returns The unsigned transaction ID
100+
*/
101+
unsignedTxid(): string {
102+
return this.wasm.unsigned_txid();
103+
}
104+
97105
/**
98106
* Parse transaction with wallet keys to identify wallet inputs/outputs
99107
* @param walletKeys - The wallet keys to use for identification
@@ -121,4 +129,44 @@ export class BitGoPsbt {
121129
parseOutputsWithWalletKeys(walletKeys: WalletKeys): ParsedOutput[] {
122130
return this.wasm.parse_outputs_with_wallet_keys(walletKeys);
123131
}
132+
133+
/**
134+
* Verify if a valid signature exists for a given extended public key at the specified input index.
135+
*
136+
* This method derives the public key from the xpub using the derivation path found in the
137+
* PSBT input, then verifies the signature. It supports:
138+
* - ECDSA signatures (for legacy/SegWit inputs)
139+
* - Schnorr signatures (for Taproot script path inputs)
140+
* - MuSig2 partial signatures (for Taproot keypath MuSig2 inputs)
141+
*
142+
* @param inputIndex - The index of the input to check (0-based)
143+
* @param xpub - The extended public key as a base58-encoded string
144+
* @returns true if a valid signature exists, false if no signature exists
145+
* @throws Error if input index is out of bounds, xpub is invalid, or verification fails
146+
*/
147+
verifySignature(inputIndex: number, xpub: string): boolean {
148+
return this.wasm.verify_signature(inputIndex, xpub);
149+
}
150+
151+
/**
152+
* Verify if a replay protection input has a valid signature.
153+
*
154+
* This method checks if a given input is a replay protection input (like P2shP2pk) and verifies
155+
* the signature. Replay protection inputs don't use standard derivation paths, so this method
156+
* verifies signatures without deriving from xpub.
157+
*
158+
* For P2PK replay protection inputs, this:
159+
* - Extracts the signature from final_script_sig
160+
* - Extracts the public key from redeem_script
161+
* - Computes the legacy P2SH sighash
162+
* - Verifies the ECDSA signature cryptographically
163+
*
164+
* @param inputIndex - The index of the input to check (0-based)
165+
* @param replayProtection - Scripts that identify replay protection inputs (same format as parseTransactionWithWalletKeys)
166+
* @returns true if the input is a replay protection input and has a valid signature, false if no valid signature
167+
* @throws Error if the input is not a replay protection input, index is out of bounds, or scripts are invalid
168+
*/
169+
verifyReplayProtectionSignature(inputIndex: number, replayProtection: ReplayProtection): boolean {
170+
return this.wasm.verify_replay_protection_signature(inputIndex, replayProtection);
171+
}
124172
}

0 commit comments

Comments
 (0)