UNPKG

@arklabs/wallet-sdk

Version:

Bitcoin wallet SDK with Taproot and Ark integration

64 lines (63 loc) 2.41 kB
import * as musig from "@scure/btc-signer/musig2"; import { bytesToNumberBE } from "@noble/curves/abstract/utils"; import { CURVE } from "@noble/secp256k1"; import { aggregateKeys } from './keys.js'; import { schnorr } from "@noble/curves/secp256k1"; // Add this error type for decode failures export class PartialSignatureError extends Error { constructor(message) { super(message); this.name = "PartialSignatureError"; } } // Implement a concrete class for PartialSignature export class PartialSig { constructor(s, R) { this.s = s; this.R = R; if (s.length !== 32) { throw new PartialSignatureError("Invalid s length"); } if (R.length !== 33) { throw new PartialSignatureError("Invalid R length"); } } /** * Encodes the partial signature into bytes * Returns a 32-byte array containing just the s value */ encode() { // Return copy of s bytes return new Uint8Array(this.s); } /** * Decodes a partial signature from bytes * @param bytes - 32-byte array containing s value */ static decode(bytes) { if (bytes.length !== 32) { throw new PartialSignatureError("Invalid partial signature length"); } // Verify s is less than curve order const s = bytesToNumberBE(bytes); if (s >= CURVE.n) { throw new PartialSignatureError("s value overflows curve order"); } // For decode we don't have R, so we'll need to compute it later const R = new Uint8Array(33); // Zero R for now return new PartialSig(bytes, R); } } /** * Generates a MuSig2 partial signature */ export function sign(secNonce, privateKey, combinedNonce, publicKeys, message, options) { let tweakBytes; if (options?.taprootTweak !== undefined) { const { preTweakedKey } = aggregateKeys(options?.sortKeys ? musig.sortKeys(publicKeys) : publicKeys, true); tweakBytes = schnorr.utils.taggedHash("TapTweak", preTweakedKey.subarray(1), options.taprootTweak); } const session = new musig.Session(combinedNonce, options?.sortKeys ? musig.sortKeys(publicKeys) : publicKeys, message, tweakBytes ? [tweakBytes] : undefined, tweakBytes ? [true] : undefined); const partialSig = session.sign(secNonce, privateKey); return PartialSig.decode(partialSig); }