From b337a789c6c42c11210b52473b563acb4b1138b6 Mon Sep 17 00:00:00 2001 From: julia-zack Date: Fri, 3 Jan 2025 18:32:33 -0300 Subject: [PATCH] Create method to remove utxos used for a release transaction --- .../main/java/co/rsk/peg/BridgeSupport.java | 61 +++++++++++-------- .../java/co/rsk/peg/BridgeSupportSvpTest.java | 4 ++ 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java b/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java index 3469465466..37d7c31dba 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java @@ -1097,7 +1097,17 @@ private void processSvpFundTransactionUnsigned(Keccak256 rskTxHash, Federation p BtcTransaction svpFundTransactionUnsigned = createSvpFundTransaction(proposedFederation, spendableValueFromProposedFederation); provider.setSvpFundTxHashUnsigned(svpFundTransactionUnsigned.getHash()); PegoutsWaitingForConfirmations pegoutsWaitingForConfirmations = provider.getPegoutsWaitingForConfirmations(); - settleReleaseRequest(pegoutsWaitingForConfirmations, svpFundTransactionUnsigned, rskTxHash, spendableValueFromProposedFederation); + + Wallet activeFederationWallet = getActiveFederationWallet(true); + List utxosToUse = activeFederationWallet + .getUTXOProvider() + .getOpenTransactionOutputs(activeFederationWallet.getWatchedAddresses()); + settleReleaseRequest(utxosToUse, pegoutsWaitingForConfirmations, svpFundTransactionUnsigned, rskTxHash, spendableValueFromProposedFederation); + } catch (UTXOProviderException e) { + logger.error( + "[processSvpFundTransactionUnsigned] Error when trying to remove spent utxos. Error message: {}", + e.getMessage() + ); } catch (InsufficientMoneyException e) { logger.error( "[processSvpFundTransactionUnsigned] Insufficient funds for creating the fund transaction. Error message: {}", @@ -1295,7 +1305,7 @@ private void migrateFunds( Keccak256 rskTxHash, Wallet retiringFederationWallet, Address activeFederationAddress, - List availableUTXOs) throws IOException { + List utxosToUse) throws IOException { PegoutsWaitingForConfirmations pegoutsWaitingForConfirmations = provider.getPegoutsWaitingForConfirmations(); Pair> createResult = createMigrationTransaction(retiringFederationWallet, activeFederationAddress); @@ -1310,12 +1320,8 @@ private void migrateFunds( Coin amountMigrated = selectedUTXOs.stream() .map(UTXO::getValue) .reduce(Coin.ZERO, Coin::add); - settleReleaseRequest(pegoutsWaitingForConfirmations, migrationTransaction, rskTxHash, amountMigrated); - // Mark UTXOs as spent - availableUTXOs.removeIf(utxo -> selectedUTXOs.stream().anyMatch(selectedUtxo -> - utxo.getHash().equals(selectedUtxo.getHash()) && utxo.getIndex() == selectedUtxo.getIndex() - )); + settleReleaseRequest(utxosToUse, pegoutsWaitingForConfirmations, migrationTransaction, rskTxHash, amountMigrated); } /** @@ -1361,11 +1367,23 @@ private void processPegoutRequests(Transaction rskTx) { } } - private void settleReleaseRequest(PegoutsWaitingForConfirmations pegoutsWaitingForConfirmations, BtcTransaction pegoutTransaction, Keccak256 releaseCreationTxHash, Coin requestedAmount) { - addPegoutToPegoutsWaitingForConfirmations(pegoutsWaitingForConfirmations, pegoutTransaction, releaseCreationTxHash); - savePegoutTxSigHash(pegoutTransaction); - logReleaseRequested(releaseCreationTxHash, pegoutTransaction, requestedAmount); - logPegoutTransactionCreated(pegoutTransaction); + private void settleReleaseRequest(List utxosToUse, PegoutsWaitingForConfirmations pegoutsWaitingForConfirmations, BtcTransaction releaseTransaction, Keccak256 releaseCreationTxHash, Coin requestedAmount) { + removeSpentUtxos(utxosToUse, releaseTransaction); + addPegoutToPegoutsWaitingForConfirmations(pegoutsWaitingForConfirmations, releaseTransaction, releaseCreationTxHash); + savePegoutTxSigHash(releaseTransaction); + logReleaseRequested(releaseCreationTxHash, releaseTransaction, requestedAmount); + logPegoutTransactionCreated(releaseTransaction); + } + + private void removeSpentUtxos(List utxosToUse, BtcTransaction releaseTx) { + List utxosToRemove = utxosToUse.stream() + .filter(utxo -> releaseTx.getInputs().stream().anyMatch(input -> + input.getOutpoint().getHash().equals(utxo.getHash()) && input.getOutpoint().getIndex() == utxo.getIndex()) + ).toList(); + + logger.debug("[removeSpentUtxos] Used {} UTXOs for this release", utxosToRemove.size()); + + utxosToUse.removeAll(utxosToRemove); } private void addPegoutToPegoutsWaitingForConfirmations(PegoutsWaitingForConfirmations pegoutsWaitingForConfirmations, BtcTransaction pegoutTransaction, Keccak256 releaseCreationTxHash) { @@ -1423,7 +1441,7 @@ private void logPegoutTransactionCreated(BtcTransaction pegoutTransaction) { private void processPegoutsIndividually( ReleaseRequestQueue pegoutRequests, ReleaseTransactionBuilder txBuilder, - List availableUTXOs, + List utxosToUse, PegoutsWaitingForConfirmations pegoutsWaitingForConfirmations, Wallet wallet ) { @@ -1448,11 +1466,7 @@ private void processPegoutsIndividually( BtcTransaction generatedTransaction = result.getBtcTx(); Keccak256 pegoutCreationTxHash = pegoutRequest.getRskTxHash(); - settleReleaseRequest(pegoutsWaitingForConfirmations, generatedTransaction, pegoutCreationTxHash, pegoutRequest.getAmount()); - - // Mark UTXOs as spent - List selectedUTXOs = result.getSelectedUTXOs(); - availableUTXOs.removeAll(selectedUTXOs); + settleReleaseRequest(utxosToUse, pegoutsWaitingForConfirmations, generatedTransaction, pegoutCreationTxHash, pegoutRequest.getAmount()); adjustBalancesIfChangeOutputWasDust(generatedTransaction, pegoutRequest.getAmount(), wallet); @@ -1463,7 +1477,7 @@ private void processPegoutsIndividually( private void processPegoutsInBatch( ReleaseRequestQueue pegoutRequests, ReleaseTransactionBuilder txBuilder, - List availableUTXOs, + List utxosToUse, PegoutsWaitingForConfirmations pegoutsWaitingForConfirmations, Wallet wallet, Transaction rskTx) { @@ -1512,18 +1526,13 @@ private void processPegoutsInBatch( BtcTransaction batchPegoutTransaction = result.getBtcTx(); Keccak256 batchPegoutCreationTxHash = rskTx.getHash(); - settleReleaseRequest(pegoutsWaitingForConfirmations, batchPegoutTransaction, batchPegoutCreationTxHash, totalPegoutValue); + settleReleaseRequest(utxosToUse, pegoutsWaitingForConfirmations, batchPegoutTransaction, batchPegoutCreationTxHash, totalPegoutValue); // Remove batched requests from the queue after successfully batching pegouts pegoutRequests.removeEntries(pegoutEntries); - // Mark UTXOs as spent - List selectedUTXOs = result.getSelectedUTXOs(); - logger.debug("[processPegoutsInBatch] used {} UTXOs for this pegout", selectedUTXOs.size()); - availableUTXOs.removeAll(selectedUTXOs); - eventLogger.logBatchPegoutCreated(batchPegoutTransaction.getHash(), - pegoutEntries.stream().map(ReleaseRequestQueue.Entry::getRskTxHash).collect(Collectors.toList())); + pegoutEntries.stream().map(ReleaseRequestQueue.Entry::getRskTxHash).toList()); adjustBalancesIfChangeOutputWasDust(batchPegoutTransaction, totalPegoutValue, wallet); } diff --git a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportSvpTest.java b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportSvpTest.java index c116f6ed9b..c65cba67fc 100644 --- a/rskj-core/src/test/java/co/rsk/peg/BridgeSupportSvpTest.java +++ b/rskj-core/src/test/java/co/rsk/peg/BridgeSupportSvpTest.java @@ -291,6 +291,9 @@ void updateCollections_whenThereAreNoEnoughUTXOs_shouldNotCreateFundTransaction( @Test void updateCollections_whenFundTxCanBeCreated_createsExpectedFundTxAndSavesTheHashInStorageEntryAndPerformsPegoutActions() throws Exception { + // arrange + int activeFederationUtxosSizeBeforeCreatingFundTx = federationSupport.getActiveFederationBtcUTXOs().size(); + // act bridgeSupport.updateCollections(rskTx); bridgeStorageProvider.save(); @@ -298,6 +301,7 @@ void updateCollections_whenFundTxCanBeCreated_createsExpectedFundTxAndSavesTheHa // assert Sha256Hash svpFundTxHashUnsigned = assertSvpFundTxHashUnsignedIsInStorage(); assertSvpFundTxReleaseWasSettled(svpFundTxHashUnsigned); + assertActiveFederationUtxosSize(activeFederationUtxosSizeBeforeCreatingFundTx - 1); assertSvpFundTransactionHasExpectedInputsAndOutputs(); }