Skip to content

Commit

Permalink
Use watched scripts to pick up broadcast of staged txs
Browse files Browse the repository at this point in the history
Since the multisig escrow outputs of the deposit & warning txs do not
belong to the user, bitcoinj won't pick up any txs spending them unless
a corresponding watched script (the ScriptPubKey) is added to the
wallet. To this end, provide a trade task to add watched scripts for
those three outputs, which runs just before the client or the peer might
broadcast the deposit tx. Also remove them upon withdrawal of funds at
the end of the trade (closed normally or through a dispute).

We need to add watched scripts for the deposit tx output and both the
user's and the peer's warning tx outputs, so that the peer's warning,
redirect and claim txs are all picked up, regardless of any message sent
to the client.

TODO: Possibly find a way to clear out old watched scripts from failed
 trades, as they will otherwise remain in the user's wallet permanently,
 creating a growing burden for the wallet. Also, we should possibly re-
 add all the watched scripts if the wallet is restored from seed.
  • Loading branch information
stejbac committed Sep 8, 2024
1 parent 95aaca9 commit c927fb6
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 0 deletions.
6 changes: 6 additions & 0 deletions core/src/main/java/bisq/core/trade/TradeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.script.Script;

import javax.inject.Inject;
import javax.inject.Named;
Expand Down Expand Up @@ -723,6 +724,11 @@ public void onTradeCompleted(Trade trade) {

// TODO The address entry should have been removed already. Check and if its the case remove that.
btcWalletService.resetAddressEntriesForPendingTrade(trade.getId());
// FIXME: If the trade fails, any watched scripts will remain in the wallet permanently, which is not ideal.
List<Script> watchedScripts = trade.getWatchedScripts(btcWalletService);
if (!watchedScripts.isEmpty()) {
btcWalletService.getWallet().removeWatchedScripts(watchedScripts);
}
requestPersistence();
}

Expand Down
26 changes: 26 additions & 0 deletions core/src/main/java/bisq/core/trade/model/bisq_v1/Trade.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.script.Script;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
Expand All @@ -65,6 +67,7 @@
import javafx.collections.ObservableList;

import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -707,6 +710,29 @@ public Transaction getClaimTx(BtcWalletService btcWalletService) {
return signedClaimTx != null ? btcWalletService.getTxFromSerializedTx(signedClaimTx) : null;
}

public List<Script> getWatchedScripts(BtcWalletService btcWalletService) {
if (!hasV5Protocol()) {
return List.of();
}
Transaction depositTx = btcWalletService.getTxFromSerializedTx(processModel.getPreparedDepositTx());
TransactionOutput depositTxOutput = depositTx.getOutput(0);
Script depositScriptPubKey = depositTxOutput.getScriptPubKey();

Transaction warningTx = btcWalletService.getTxFromSerializedTx(processModel.getFinalizedWarningTx());
TransactionOutput warningTxOutput = warningTx.getOutput(0);
Script warningScriptPubKey = warningTxOutput.getScriptPubKey();

if (processModel.getTradePeer().getFinalizedWarningTx() == null) {
log.warn("Missing peer's finalized warning tx. Cannot find watched script for its escrow output.");
return List.of(depositScriptPubKey, warningScriptPubKey);
}
Transaction peersWarningTx = btcWalletService.getTxFromSerializedTx(processModel.getTradePeer().getFinalizedWarningTx());
TransactionOutput peersWarningTxOutput = peersWarningTx.getOutput(0);
Script peersWarningScriptPubKey = peersWarningTxOutput.getScriptPubKey();

return List.of(depositScriptPubKey, warningScriptPubKey, peersWarningScriptPubKey);
}

public void addAndPersistChatMessage(ChatMessage chatMessage) {
if (!chatMessages.contains(chatMessage)) {
chatMessages.add(chatMessage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import bisq.core.trade.protocol.bisq_v1.tasks.seller.SellerSendPayoutTxPublishedMessage;
import bisq.core.trade.protocol.bisq_v1.tasks.seller.SellerSignAndFinalizePayoutTx;
import bisq.core.trade.protocol.bisq_v5.messages.PreparedTxBuyerSignaturesMessage;
import bisq.core.trade.protocol.bisq_v5.tasks.AddWatchedScriptsToWallet;
import bisq.core.trade.protocol.bisq_v5.tasks.seller.SellerProcessPreparedTxBuyerSignaturesMessage;
import bisq.core.trade.protocol.bisq_v5.tasks.seller.SellerSendsDepositTxAndSellerPaymentAccountMessage;

Expand Down Expand Up @@ -82,6 +83,7 @@ protected void handle(PreparedTxBuyerSignaturesMessage message, NodeAddress peer
.with(message)
.from(peer))
.setup(tasks(SellerProcessPreparedTxBuyerSignaturesMessage.class,
AddWatchedScriptsToWallet.class,
SellerSendsDepositTxAndSellerPaymentAccountMessage.class,
SellerPublishesDepositTx.class,
SellerPublishesTradeStatistics.class))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import bisq.core.trade.protocol.bisq_v1.tasks.maker.MakerVerifyTakerFeePayment;
import bisq.core.trade.protocol.bisq_v5.messages.DepositTxAndSellerPaymentAccountMessage;
import bisq.core.trade.protocol.bisq_v5.messages.PreparedTxBuyerSignaturesRequest;
import bisq.core.trade.protocol.bisq_v5.tasks.AddWatchedScriptsToWallet;
import bisq.core.trade.protocol.bisq_v5.tasks.CreateFeeBumpAddressEntries;
import bisq.core.trade.protocol.bisq_v5.tasks.CreateRedirectTxs;
import bisq.core.trade.protocol.bisq_v5.tasks.CreateWarningTxs;
Expand Down Expand Up @@ -143,6 +144,7 @@ protected void handle(PreparedTxBuyerSignaturesRequest message, NodeAddress peer
FinalizeWarningTxs.class,
FinalizeRedirectTxs.class,
MakerRemovesOpenOffer.class,
AddWatchedScriptsToWallet.class,
BuyerSendsPreparedTxBuyerSignaturesMessage.class)
.withTimeout(120))
.executeTasks();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import bisq.core.trade.protocol.bisq_v1.tasks.taker.TakerVerifyMakerFeePayment;
import bisq.core.trade.protocol.bisq_v5.messages.DepositTxAndSellerPaymentAccountMessage;
import bisq.core.trade.protocol.bisq_v5.messages.InputsForDepositTxResponse_v5;
import bisq.core.trade.protocol.bisq_v5.tasks.AddWatchedScriptsToWallet;
import bisq.core.trade.protocol.bisq_v5.tasks.CreateFeeBumpAddressEntries;
import bisq.core.trade.protocol.bisq_v5.tasks.CreateRedirectTxs;
import bisq.core.trade.protocol.bisq_v5.tasks.CreateWarningTxs;
Expand Down Expand Up @@ -135,6 +136,7 @@ private void handle(InputsForDepositTxResponse_v5 message, NodeAddress peer) {
TakerVerifyAndSignContract.class,
TakerPublishFeeTx.class,
BuyerAsTakerSignsDepositTx.class,
AddWatchedScriptsToWallet.class,
BuyerSetupDepositTxListener.class,
BuyerSendsPreparedTxBuyerSignaturesMessage.class)
.withTimeout(120))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import bisq.core.trade.protocol.bisq_v1.tasks.seller.SellerPublishesTradeStatistics;
import bisq.core.trade.protocol.bisq_v1.tasks.seller_as_maker.SellerAsMakerCreatesUnsignedDepositTx;
import bisq.core.trade.protocol.bisq_v5.messages.PreparedTxBuyerSignaturesMessage;
import bisq.core.trade.protocol.bisq_v5.tasks.AddWatchedScriptsToWallet;
import bisq.core.trade.protocol.bisq_v5.tasks.CreateFeeBumpAddressEntries;
import bisq.core.trade.protocol.bisq_v5.tasks.CreateRedirectTxs;
import bisq.core.trade.protocol.bisq_v5.tasks.CreateWarningTxs;
Expand Down Expand Up @@ -150,6 +151,7 @@ protected void handle(PreparedTxBuyerSignaturesMessage message, NodeAddress peer
FinalizeWarningTxs.class,
FinalizeRedirectTxs.class,
MakerRemovesOpenOffer.class,
AddWatchedScriptsToWallet.class,
SellerSendsDepositTxAndSellerPaymentAccountMessage.class,
SellerPublishesDepositTx.class,
SellerPublishesTradeStatistics.class))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.core.trade.protocol.bisq_v5.tasks;

import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask;

import bisq.common.taskrunner.TaskRunner;

import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.script.Script;

import java.util.List;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class AddWatchedScriptsToWallet extends TradeTask {
public AddWatchedScriptsToWallet(TaskRunner<Trade> taskHandler, Trade trade) {
super(taskHandler, trade);
}

@Override
protected void run() {
try {
runInterceptHook();

BtcWalletService btcWalletService = processModel.getBtcWalletService();

long creationTimeSeconds = System.currentTimeMillis() / 1000;
List<Script> watchedScripts = trade.getWatchedScripts(btcWalletService);
watchedScripts.forEach(script -> script.setCreationTimeSeconds(creationTimeSeconds));

// TODO: Possibly find a way to re-add watched scripts in the event that the wallet is restored from seed.
btcWalletService.getWallet().addWatchedScripts(watchedScripts);

complete();
} catch (Throwable t) {
failed(t);
}
}
}

0 comments on commit c927fb6

Please sign in to comment.