From f2ee37e0b29d0aa02f44bf8e4c2ce74c63ccd639 Mon Sep 17 00:00:00 2001 From: Polybius93 Date: Mon, 15 Jul 2024 16:55:06 +0200 Subject: [PATCH] feat: update taproot related psbt functions to handle public keys correctly --- src/dlc-handlers/ledger-dlc-handler.ts | 160 ++++++++++++--------- src/functions/bitcoin/bitcoin-functions.ts | 7 - src/functions/bitcoin/index.ts | 2 - src/functions/bitcoin/psbt-functions.ts | 90 ++++++------ 4 files changed, 133 insertions(+), 126 deletions(-) diff --git a/src/dlc-handlers/ledger-dlc-handler.ts b/src/dlc-handlers/ledger-dlc-handler.ts index 80c1084..beac555 100644 --- a/src/dlc-handlers/ledger-dlc-handler.ts +++ b/src/dlc-handlers/ledger-dlc-handler.ts @@ -13,11 +13,12 @@ import { getBalance, getFeeRate, getInputByPaymentTypeArray, + getInputIndicesByScript, getUnspendableKeyCommittedToUUID, } from '../functions/bitcoin/bitcoin-functions.js'; import { - addNativeSegwitSignaturesToPSBT, - addTaprootInputSignaturesToPSBT, + addFundingSignaturesBasedOnPaymentType, + addTaprooMultisigInputSignaturesToPSBT, createDepositTransaction, createFundingTransaction, createWithdrawTransaction, @@ -296,8 +297,10 @@ export class LedgerDLCHandler { customFeeRate?: bigint ): Promise { try { - const { fundingPayment, fundingDerivedPublicKey, taprootDerivedPublicKey, multisigPayment } = - await this.createPayment(vault.uuid, attestorGroupPublicKey); + const { fundingPayment, fundingDerivedPublicKey, multisigPayment } = await this.createPayment( + vault.uuid, + attestorGroupPublicKey + ); const feeRate = customFeeRate ?? @@ -353,7 +356,6 @@ export class LedgerDLCHandler { await updateTaprootInputs( taprootInputsToSign, - taprootDerivedPublicKey, fundingDerivedPublicKey, this.masterFingerprint, formattedFundingPSBT @@ -375,8 +377,10 @@ export class LedgerDLCHandler { customFeeRate?: bigint ): Promise { try { - const { fundingPayment, fundingDerivedPublicKey, taprootDerivedPublicKey, multisigPayment } = - await this.createPayment(vault.uuid, attestorGroupPublicKey); + const { fundingPayment, taprootDerivedPublicKey, multisigPayment } = await this.createPayment( + vault.uuid, + attestorGroupPublicKey + ); const feeRate = customFeeRate ?? @@ -417,7 +421,6 @@ export class LedgerDLCHandler { await updateTaprootInputs( taprootInputsToSign, taprootDerivedPublicKey, - fundingDerivedPublicKey, this.masterFingerprint, formattedWithdrawPSBT ); @@ -474,23 +477,42 @@ export class LedgerDLCHandler { ); const taprootInputsToSign = getTaprootInputsToSign(depositInputByPaymentTypeArray); - const nativeSegwitInputsToSign = getNativeSegwitInputsToSign(depositInputByPaymentTypeArray); + + const taprootUserInputsToSign = taprootInputsToSign.filter(inputSigningConfig => { + return !inputSigningConfig.isMultisigInput; + }); + + const taprootMultisigInputsToSign = taprootInputsToSign.filter(inputSigningConfig => { + return inputSigningConfig.isMultisigInput; + }); await updateTaprootInputs( - taprootInputsToSign, + taprootMultisigInputsToSign, taprootDerivedPublicKey, - fundingDerivedPublicKey, this.masterFingerprint, formattedDepositPSBT ); - await updateNativeSegwitInputs( - nativeSegwitInputsToSign, - fundingDerivedPublicKey, - this.masterFingerprint, - formattedDepositPSBT, - this.bitcoinBlockchainAPI - ); + if (taprootUserInputsToSign.length !== 0) { + await updateTaprootInputs( + taprootUserInputsToSign, + fundingDerivedPublicKey, + this.masterFingerprint, + formattedDepositPSBT + ); + } + + const nativeSegwitInputsToSign = getNativeSegwitInputsToSign(depositInputByPaymentTypeArray); + + if (nativeSegwitInputsToSign.length !== 0) { + await updateNativeSegwitInputs( + nativeSegwitInputsToSign, + fundingDerivedPublicKey, + this.masterFingerprint, + formattedDepositPSBT, + this.bitcoinBlockchainAPI + ); + } return formattedDepositPSBT; } @@ -499,61 +521,59 @@ export class LedgerDLCHandler { psbt: Psbt, transactionType: 'funding' | 'deposit' | 'withdraw' ): Promise { + let transaction: Transaction; + const { fundingWalletPolicy, multisigWalletPolicy, multisigWalletPolicyHMac } = this.getPolicyInformation(); - if (transactionType === 'funding') { - const signatures = await this.ledgerApp.signPsbt(psbt.toBase64(), fundingWalletPolicy, null); - switch (this.fundingPaymentType) { - case 'wpkh': - addNativeSegwitSignaturesToPSBT(psbt, signatures); - break; - case 'tr': - addTaprootInputSignaturesToPSBT('funding', psbt, signatures); - break; - default: - throw new Error('Invalid Funding Payment Type'); - } - const fundingTransaction = Transaction.fromPSBT(psbt.toBuffer()); - fundingTransaction.finalize(); - return fundingTransaction; - } else if (transactionType === 'deposit') { - const multisigSignatures = await this.ledgerApp.signPsbt( - psbt.toBase64(), - multisigWalletPolicy, - multisigWalletPolicyHMac - ); - addTaprootInputSignaturesToPSBT('depositWithdraw', psbt, multisigSignatures); - const userSignatures = await this.ledgerApp.signPsbt( - psbt.toBase64(), - fundingWalletPolicy, - null - ); - switch (this.fundingPaymentType) { - case 'wpkh': - addNativeSegwitSignaturesToPSBT(psbt, userSignatures); - break; - case 'tr': - addTaprootInputSignaturesToPSBT('funding', psbt, userSignatures); - break; - default: - throw new Error('Invalid Funding Payment Type'); - } - const userInputIndices = userSignatures.map(signature => signature[0]); - const depositTransaction = Transaction.fromPSBT(psbt.toBuffer()); - userInputIndices.forEach(index => { - depositTransaction.finalizeIdx(index); - }); - return depositTransaction; - } else { - const multisigSignatures = await this.ledgerApp.signPsbt( - psbt.toBase64(), - multisigWalletPolicy, - multisigWalletPolicyHMac - ); - addTaprootInputSignaturesToPSBT('depositWithdraw', psbt, multisigSignatures); - const withdrawTransaction = Transaction.fromPSBT(psbt.toBuffer()); - return withdrawTransaction; + switch (transactionType) { + case 'funding': + addFundingSignaturesBasedOnPaymentType( + psbt, + this.fundingPaymentType, + await this.ledgerApp.signPsbt(psbt.toBase64(), fundingWalletPolicy, null) + ); + transaction = Transaction.fromPSBT(psbt.toBuffer()); + transaction.finalize(); + break; + case 'deposit': + addTaprooMultisigInputSignaturesToPSBT( + psbt, + await this.ledgerApp.signPsbt( + psbt.toBase64(), + multisigWalletPolicy, + multisigWalletPolicyHMac + ) + ); + + addFundingSignaturesBasedOnPaymentType( + psbt, + this.fundingPaymentType, + await this.ledgerApp.signPsbt(psbt.toBase64(), fundingWalletPolicy, null) + ); + + transaction = Transaction.fromPSBT(psbt.toBuffer()); + + getInputIndicesByScript(this.getPayment().fundingPayment.script, transaction).forEach( + index => { + transaction.finalizeIdx(index); + } + ); + break; + case 'withdraw': + addTaprooMultisigInputSignaturesToPSBT( + psbt, + await this.ledgerApp.signPsbt( + psbt.toBase64(), + multisigWalletPolicy, + multisigWalletPolicyHMac + ) + ); + transaction = Transaction.fromPSBT(psbt.toBuffer()); + break; + default: + throw new Error('Invalid Transaction Type'); } + return transaction; } } diff --git a/src/functions/bitcoin/bitcoin-functions.ts b/src/functions/bitcoin/bitcoin-functions.ts index 733e3fd..04515c4 100644 --- a/src/functions/bitcoin/bitcoin-functions.ts +++ b/src/functions/bitcoin/bitcoin-functions.ts @@ -58,13 +58,6 @@ export function deriveUnhardenedPublicKey( .publicKey; } -export function getPublicKeyFromExtendedPublicKey( - extendedPublicKey: string, - bitcoinNetwork: Network -): Buffer { - return bip32.fromBase58(extendedPublicKey, bitcoinNetwork).publicKey; -} - /** * Derives the Account Key Pair from the Root Private Key. * @param rootPrivateKey - The Root Private Key. diff --git a/src/functions/bitcoin/index.ts b/src/functions/bitcoin/index.ts index 0db90b9..8755d3a 100644 --- a/src/functions/bitcoin/index.ts +++ b/src/functions/bitcoin/index.ts @@ -3,7 +3,6 @@ import { getFeeAmount, getFeeRecipientAddressFromPublicKey, getInputIndicesByScript, - getPublicKeyFromExtendedPublicKey, } from '../bitcoin/bitcoin-functions.js'; import { broadcastTransaction, @@ -29,5 +28,4 @@ export { getBalance, getFeeRecipientAddressFromPublicKey, getInputIndicesByScript, - getPublicKeyFromExtendedPublicKey, }; diff --git a/src/functions/bitcoin/psbt-functions.ts b/src/functions/bitcoin/psbt-functions.ts index 81bbcda..66d1b3f 100644 --- a/src/functions/bitcoin/psbt-functions.ts +++ b/src/functions/bitcoin/psbt-functions.ts @@ -410,33 +410,6 @@ export function getNativeSegwitInputsToSign( * @returns The updated PSBT. */ export async function updateTaprootInputs( - inputsToUpdate: BitcoinInputSigningConfig[] = [], - multisigPublicKey: Buffer, - fundingPublicKey: Buffer, - masterFingerprint: string, - psbt: Psbt -): Promise { - inputsToUpdate.forEach(({ index, derivationPath, isMultisigInput }) => { - console.log('index', index); - console.log('derivationPath', derivationPath); - psbt.updateInput(index, { - tapBip32Derivation: [ - { - masterFingerprint: Buffer.from(masterFingerprint, 'hex'), - pubkey: isMultisigInput - ? ecdsaPublicKeyToSchnorr(multisigPublicKey) - : ecdsaPublicKeyToSchnorr(fundingPublicKey), - path: derivationPath, - leafHashes: [], - }, - ], - }); - }); - - return psbt; -} - -export async function updateTaprootMultisigInputs( inputsToUpdate: BitcoinInputSigningConfig[] = [], taprootPublicKey: Buffer, masterFingerprint: string, @@ -514,7 +487,7 @@ async function addNativeSegwitBip32Derivation( masterFingerPrint: string, nativeSegwitPublicKey: Buffer, inputSigningConfiguration: BitcoinInputSigningConfig[] -): Promise { +): Promise { inputSigningConfiguration.forEach(({ index, derivationPath }) => { psbt.updateInput(index, { bip32Derivation: [ @@ -526,8 +499,6 @@ async function addNativeSegwitBip32Derivation( ], }); }); - - return psbt; } /** @@ -550,25 +521,50 @@ export function addNativeSegwitSignaturesToPSBT( * @returns The updated PSBT. */ export function addTaprootInputSignaturesToPSBT( - psbtType: 'funding' | 'depositWithdraw', psbt: Psbt, signatures: [number, PartialSignature][] ): void { - if (psbtType === 'funding') { - signatures.forEach(([index, signature]) => - psbt.updateInput(index, { tapKeySig: signature.signature }) - ); - } else { - signatures.forEach(([index, signature]) => - psbt.updateInput(index, { - tapScriptSig: [ - { - signature: signature.signature, - pubkey: signature.pubkey, - leafHash: signature.tapleafHash!, - }, - ], - }) - ); + signatures.forEach(([index, signature]) => + psbt.updateInput(index, { tapKeySig: signature.signature }) + ); +} + +export function addFundingSignaturesBasedOnPaymentType( + psbt: Psbt, + paymentType: 'wpkh' | 'tr', + signatures: [number, PartialSignature][] +): void { + switch (paymentType) { + case 'wpkh': + addNativeSegwitSignaturesToPSBT(psbt, signatures); + break; + case 'tr': + addTaprootInputSignaturesToPSBT(psbt, signatures); + break; + default: + throw new Error('Invalid Funding Payment Type'); } } + +/** + * This function updates the PSBT with the received Taproot Partial Signatures. + * @param psbt - The PSBT to update. + * @param signatures - An array of tuples containing the index of the input and the PartialSignature. + * @returns The updated PSBT. + */ +export function addTaprooMultisigInputSignaturesToPSBT( + psbt: Psbt, + signatures: [number, PartialSignature][] +): void { + signatures.forEach(([index, signature]) => + psbt.updateInput(index, { + tapScriptSig: [ + { + signature: signature.signature, + pubkey: signature.pubkey, + leafHash: signature.tapleafHash!, + }, + ], + }) + ); +}