UNPKG

lotus-sdk

Version:

Central repository for several classes of tools for integrating with, and building for, the Lotusia ecosystem

105 lines (104 loc) 4.64 kB
import { Address } from '../address.js'; import { BN, Point, Hash } from '../crypto/index.js'; import { musigKeyAgg, musigPartialSign, musigPartialSigVerify, musigTaggedHash, MUSIG_TAG_NONCE_COEFF, } from '../crypto/musig2.js'; import { buildKeyPathTaproot, buildScriptPathTaproot, calculateTapTweak, tweakPublicKey, } from '../taproot.js'; export function buildMuSigTaprootKey(signerPubKeys, state) { const keyAggContext = musigKeyAgg(signerPubKeys); const aggregatedPubKey = keyAggContext.aggregatedPubKey; const merkleRoot = Buffer.alloc(32); const tweak = calculateTapTweak(aggregatedPubKey, merkleRoot); const commitment = tweakPublicKey(aggregatedPubKey, merkleRoot); const script = buildKeyPathTaproot(aggregatedPubKey, state); return { aggregatedPubKey, commitment, script, keyAggContext, merkleRoot, tweak, }; } export function buildMuSigTaprootKeyWithScripts(signerPubKeys, scriptTree, state) { const keyAggContext = musigKeyAgg(signerPubKeys); const aggregatedPubKey = keyAggContext.aggregatedPubKey; const { script, commitment, merkleRoot, leaves } = buildScriptPathTaproot(aggregatedPubKey, scriptTree, state); const tweak = calculateTapTweak(aggregatedPubKey, merkleRoot); return { aggregatedPubKey, commitment, script, keyAggContext, merkleRoot, tweak, leaves, }; } export function signTaprootKeyPathWithMuSig2(secretNonce, privateKey, keyAggContext, signerIndex, aggregatedNonce, message, tweak) { const n = Point.getN(); const commitment = keyAggContext.aggregatedPubKey.addScalar(tweak); const modifiedKeyAggContext = { ...keyAggContext, aggregatedPubKey: commitment, }; const partialSig = musigPartialSign(secretNonce, privateKey, modifiedKeyAggContext, signerIndex, aggregatedNonce, message); if (signerIndex === 0) { const { R1, R2 } = aggregatedNonce; const nonceCoefData = Buffer.concat([ commitment.toBuffer(), Point.pointToCompressed(R1), Point.pointToCompressed(R2), message, ]); const b = new BN(musigTaggedHash(MUSIG_TAG_NONCE_COEFF, nonceCoefData), 'be'); const R = R1.add(R2.mul(b)); const R_x = R.getX().toArrayLike(Buffer, 'be', 32); const commitment_compressed = Point.pointToCompressed(commitment.point); const challengeData = Buffer.concat([R_x, commitment_compressed, message]); const e = new BN(Hash.sha256(challengeData), 'be').umod(n); const tweakBN = new BN(tweak, 'be').umod(n); const tweakTerm = e.mul(tweakBN).umod(n); return partialSig.add(tweakTerm).umod(n); } return partialSig; } export function verifyTaprootKeyPathMuSigPartial(partialSig, publicNonce, publicKey, keyAggContext, signerIndex, aggregatedNonce, message, tweak) { const n = Point.getN(); const commitment = keyAggContext.aggregatedPubKey.addScalar(tweak); const modifiedKeyAggContext = { ...keyAggContext, aggregatedPubKey: commitment, }; let adjustedPartialSig = partialSig; if (signerIndex === 0) { const { R1, R2 } = aggregatedNonce; const nonceCoefData = Buffer.concat([ commitment.toBuffer(), Point.pointToCompressed(R1), Point.pointToCompressed(R2), message, ]); const b = new BN(musigTaggedHash(MUSIG_TAG_NONCE_COEFF, nonceCoefData), 'be'); const R = R1.add(R2.mul(b)); const R_x = R.getX().toArrayLike(Buffer, 'be', 32); const commitment_compressed = Point.pointToCompressed(commitment.point); const challengeData = Buffer.concat([R_x, commitment_compressed, message]); const e = new BN(Hash.sha256(challengeData), 'be').umod(n); const tweakBN = new BN(tweak, 'be').umod(n); const tweakTerm = e.mul(tweakBN).umod(n); adjustedPartialSig = partialSig.sub(tweakTerm).umod(n); } return musigPartialSigVerify(adjustedPartialSig, publicNonce, publicKey, modifiedKeyAggContext, signerIndex, aggregatedNonce, message); } export function isMuSigTaprootOutput(script) { return script.isPayToTaproot(); } export function createMuSigTaprootAddress(signerPubKeys, network = 'livenet', state) { const result = buildMuSigTaprootKey(signerPubKeys, state); const address = Address.fromTaprootCommitment(result.commitment, network); return { address, script: result.script, commitment: result.commitment, keyAggContext: result.keyAggContext, }; }