From e09422e468978f1890e511b6b5f79d6236586a84 Mon Sep 17 00:00:00 2001 From: Polybius93 Date: Fri, 28 Jun 2024 15:16:21 +0200 Subject: [PATCH] feat: add user input finalization function --- src/dlc-handlers/private-key-dlc-handler.ts | 6 ++- .../software-wallet-dlc-handler.ts | 2 - src/functions/bitcoin/bitcoin-functions.ts | 23 ++++++++++ src/functions/bitcoin/psbt-functions.ts | 45 ++++++++++--------- tests/mocks/constants.ts | 2 + tests/unit/sign-transactions.test.ts | 2 - 6 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/dlc-handlers/private-key-dlc-handler.ts b/src/dlc-handlers/private-key-dlc-handler.ts index b4999e6..14da2e8 100644 --- a/src/dlc-handlers/private-key-dlc-handler.ts +++ b/src/dlc-handlers/private-key-dlc-handler.ts @@ -10,6 +10,7 @@ import { createTaprootMultisigPayment, deriveUnhardenedKeyPairFromRootPrivateKey, deriveUnhardenedPublicKey, + finalizeUserInputIndices, getBalance, getFeeRate, getUnspendableKeyCommittedToUUID, @@ -263,7 +264,7 @@ export class PrivateKeyDLCHandler { } } - signPSBT(psbt: Transaction, transactionType: 'funding' | 'deposit' | 'closing'): Transaction { + signPSBT(psbt: Transaction, transactionType: 'funding' | 'deposit' | 'withdraw'): Transaction { switch (transactionType) { case 'funding': psbt.sign(this.getPrivateKey('p2wpkh')); @@ -272,8 +273,9 @@ export class PrivateKeyDLCHandler { case 'deposit': psbt.sign(this.getPrivateKey('p2tr')); psbt.sign(this.getPrivateKey('p2wpkh')); + finalizeUserInputIndices(psbt, this.getPayment().nativeSegwitPayment); break; - case 'closing': + case 'withdraw': psbt.sign(this.getPrivateKey('p2tr')); break; default: diff --git a/src/dlc-handlers/software-wallet-dlc-handler.ts b/src/dlc-handlers/software-wallet-dlc-handler.ts index 49d56b9..deec837 100644 --- a/src/dlc-handlers/software-wallet-dlc-handler.ts +++ b/src/dlc-handlers/software-wallet-dlc-handler.ts @@ -222,8 +222,6 @@ export class SoftwareWalletDLCHandler { customFeeRate ?? BigInt(await getFeeRate(this.bitcoinBlockchainFeeRecommendationAPI, feeRateMultiplier)); - console.log('vault', vault); - const withdrawalTransaction = await createWithdrawalTransaction( this.bitcoinBlockchainAPI, withdrawAmount, diff --git a/src/functions/bitcoin/bitcoin-functions.ts b/src/functions/bitcoin/bitcoin-functions.ts index 08ab8ed..0206cab 100644 --- a/src/functions/bitcoin/bitcoin-functions.ts +++ b/src/functions/bitcoin/bitcoin-functions.ts @@ -428,6 +428,29 @@ export function validateScript(script: Uint8Array, outputScript: Uint8Array): bo ); } +export function finalizeUserInputIndices( + transaction: Transaction, + userPayment: P2TROut | P2Ret +): Transaction { + const userPaymentScript = userPayment.script; + createRangeFromLength(transaction.inputsLength).forEach(index => { + const inputScript = transaction.getInput(index).witnessUtxo?.script; + + if (!inputScript) { + throw new Error('Could not get Input Script'); + } + + if ( + inputScript.length === userPaymentScript.length && + inputScript.every((value, index) => value === userPaymentScript[index]) + ) { + transaction.finalizeIdx(index); + } + }); + + return transaction; +} + /** * Converts an ECDSA Public Key to a Schnorr Public Key. * @param publicKey - The ECDSA Public Key. diff --git a/src/functions/bitcoin/psbt-functions.ts b/src/functions/bitcoin/psbt-functions.ts index 6d1be43..3e1eb80 100644 --- a/src/functions/bitcoin/psbt-functions.ts +++ b/src/functions/bitcoin/psbt-functions.ts @@ -57,7 +57,13 @@ export async function createFundingTransaction( network: bitcoinNetwork, }); - const fundingTX = selected?.tx; + if (!selected) { + throw new Error( + 'Failed to select Inputs for the Funding Transaction. Ensure sufficient funds are available.' + ); + } + + const fundingTX = selected.tx; if (!fundingTX) throw new Error('Could not create Funding Transaction'); @@ -127,8 +133,6 @@ export async function createDepositTransaction( const userUTXOs = await getUTXOs(depositPayment, bitcoinBlockchainURL); - console.log('userUTXOs', userUTXOs); - const additionalDepositOutputs = [ { address: feeAddress, @@ -140,8 +144,6 @@ export async function createDepositTransaction( }, ]; - console.log('additionalDepositOutputs', additionalDepositOutputs); - const additionalDepositSelected = selectUTXO(userUTXOs, additionalDepositOutputs, 'default', { changeAddress: depositPaymentAddress, feePerByte: feeRate, @@ -150,8 +152,6 @@ export async function createDepositTransaction( network: bitcoinNetwork, }); - console.log('additionalDepositSelected', additionalDepositSelected); - if (!additionalDepositSelected) { throw new Error( 'Failed to select Inputs for the Additional Deposit Transaction. Ensure sufficient funds are available.' @@ -168,20 +168,17 @@ export async function createDepositTransaction( ...multisigPayment, }; - const depositInputPromises = additionalDepositSelected.inputs.map(async input => { - const txID = input.txid; - if (!txID) { - throw new Error('Could not get Transaction ID from Input'); - } - const utxo = userUTXOs.find((utxo: any) => utxo.txid === txID && utxo.index === input.index); - console.log('utxo', utxo); - if (utxo) { - return utxo; - } - }); + const depositInputPromises = additionalDepositSelected.inputs + .map(async input => { + const txID = input.txid; + if (!txID) { + throw new Error('Could not get Transaction ID from Input'); + } + return userUTXOs.find((utxo: any) => utxo.txid === txID && utxo.index === input.index); + }) + .filter(utxo => utxo !== undefined); const depositInputs = await Promise.all(depositInputPromises); - console.log('depositInputs', depositInputs); depositInputs.push(vaultInput); const depositOutputs = [ @@ -203,8 +200,6 @@ export async function createDepositTransaction( network: bitcoinNetwork, }); - console.log('depositSelected', depositSelected); - if (!depositSelected) { throw new Error( 'Failed to select Inputs for the Deposit Transaction. Ensure sufficient funds are available.' @@ -316,7 +311,13 @@ export async function createWithdrawalTransaction( network: bitcoinNetwork, }); - const withdrawTX = selected?.tx; + if (!selected) { + throw new Error( + 'Failed to select Inputs for the Withdrawal Transaction. Ensure sufficient funds are available.' + ); + } + + const withdrawTX = selected.tx; if (!withdrawTX) throw new Error('Could not create Withdrawal Transaction'); diff --git a/tests/mocks/constants.ts b/tests/mocks/constants.ts index 02916b8..eb9d3a6 100644 --- a/tests/mocks/constants.ts +++ b/tests/mocks/constants.ts @@ -27,10 +27,12 @@ export const TEST_VAULT: RawVault = { protocolContract: '0x6e692DB944162f8b4250aA25eCEe80608457D7a7', timestamp: BigNumber.from('0x665da025'), valueLocked: BigNumber.from('0x0f4240'), + valueMinted: BigNumber.from('0x0f4240'), creator: '0x0DD4f29E21F10cb2E485cf9bDAb9F2dD1f240Bfa', status: 0, fundingTxId: '', closingTxId: '', + withdrawTxId: '', btcFeeRecipient: '031131cd88bcea8c1d84da8e034bb24c2f6e748c571922dc363e7e088f5df0436c', btcMintFeeBasisPoints: BigNumber.from('0x64'), btcRedeemFeeBasisPoints: BigNumber.from('0x64'), diff --git a/tests/unit/sign-transactions.test.ts b/tests/unit/sign-transactions.test.ts index bf062f7..c02a17d 100644 --- a/tests/unit/sign-transactions.test.ts +++ b/tests/unit/sign-transactions.test.ts @@ -17,8 +17,6 @@ describe('Create and Sign Vault related Transactions', () => { let dlcHandler: PrivateKeyDLCHandler; let fundingTransaction: Transaction; let signedFundingTransaction: Transaction; - let closingTransaction: Transaction; - let partiallySignedClosingTransaction: Transaction; it('should initialize a Private Key DLC Handler', async () => { dlcHandler = new PrivateKeyDLCHandler(