Skip to content

Commit

Permalink
feat: update taproot related psbt functions to handle public keys cor…
Browse files Browse the repository at this point in the history
…rectly
  • Loading branch information
Polybius93 committed Jul 15, 2024
1 parent bac028f commit f2ee37e
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 126 deletions.
160 changes: 90 additions & 70 deletions src/dlc-handlers/ledger-dlc-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import {
getBalance,
getFeeRate,
getInputByPaymentTypeArray,
getInputIndicesByScript,
getUnspendableKeyCommittedToUUID,
} from '../functions/bitcoin/bitcoin-functions.js';
import {
addNativeSegwitSignaturesToPSBT,
addTaprootInputSignaturesToPSBT,
addFundingSignaturesBasedOnPaymentType,
addTaprooMultisigInputSignaturesToPSBT,
createDepositTransaction,
createFundingTransaction,
createWithdrawTransaction,
Expand Down Expand Up @@ -296,8 +297,10 @@ export class LedgerDLCHandler {
customFeeRate?: bigint
): Promise<Psbt> {
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 ??
Expand Down Expand Up @@ -353,7 +356,6 @@ export class LedgerDLCHandler {

await updateTaprootInputs(
taprootInputsToSign,
taprootDerivedPublicKey,
fundingDerivedPublicKey,
this.masterFingerprint,
formattedFundingPSBT
Expand All @@ -375,8 +377,10 @@ export class LedgerDLCHandler {
customFeeRate?: bigint
): Promise<Psbt> {
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 ??
Expand Down Expand Up @@ -417,7 +421,6 @@ export class LedgerDLCHandler {
await updateTaprootInputs(
taprootInputsToSign,
taprootDerivedPublicKey,
fundingDerivedPublicKey,
this.masterFingerprint,
formattedWithdrawPSBT
);
Expand Down Expand Up @@ -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;
}
Expand All @@ -499,61 +521,59 @@ export class LedgerDLCHandler {
psbt: Psbt,
transactionType: 'funding' | 'deposit' | 'withdraw'
): Promise<Transaction> {
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;
}
}
7 changes: 0 additions & 7 deletions src/functions/bitcoin/bitcoin-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 0 additions & 2 deletions src/functions/bitcoin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
getFeeAmount,
getFeeRecipientAddressFromPublicKey,
getInputIndicesByScript,
getPublicKeyFromExtendedPublicKey,
} from '../bitcoin/bitcoin-functions.js';
import {
broadcastTransaction,
Expand All @@ -29,5 +28,4 @@ export {
getBalance,
getFeeRecipientAddressFromPublicKey,
getInputIndicesByScript,
getPublicKeyFromExtendedPublicKey,
};
90 changes: 43 additions & 47 deletions src/functions/bitcoin/psbt-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Psbt> {
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,
Expand Down Expand Up @@ -514,7 +487,7 @@ async function addNativeSegwitBip32Derivation(
masterFingerPrint: string,
nativeSegwitPublicKey: Buffer,
inputSigningConfiguration: BitcoinInputSigningConfig[]
): Promise<Psbt> {
): Promise<void> {
inputSigningConfiguration.forEach(({ index, derivationPath }) => {
psbt.updateInput(index, {
bip32Derivation: [
Expand All @@ -526,8 +499,6 @@ async function addNativeSegwitBip32Derivation(
],
});
});

return psbt;
}

/**
Expand All @@ -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!,
},
],
})
);
}

0 comments on commit f2ee37e

Please sign in to comment.