diff --git a/core/src/main/java/bisq/core/trade/DelayedPayoutTxValidation.java b/core/src/main/java/bisq/core/trade/DelayedPayoutTxValidation.java index c3ed177eb6e..6c835d2408b 100644 --- a/core/src/main/java/bisq/core/trade/DelayedPayoutTxValidation.java +++ b/core/src/main/java/bisq/core/trade/DelayedPayoutTxValidation.java @@ -29,6 +29,7 @@ import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionInput; +import org.bitcoinj.core.TransactionOutPoint; import org.bitcoinj.core.TransactionOutput; import java.util.List; @@ -70,6 +71,12 @@ public static class InvalidLockTimeException extends Exception { } } + public static class InvalidInputException extends Exception { + InvalidInputException(String msg) { + super(msg); + } + } + public static void validatePayoutTx(Trade trade, Transaction delayedPayoutTx, DaoFacade daoFacade, @@ -184,4 +191,19 @@ public static void validatePayoutTx(Trade trade, throw new DonationAddressException(errorMsg); } } + + public static void validatePayoutTxInput(Transaction depositTx, + Transaction delayedPayoutTx) + throws InvalidInputException { + TransactionInput input = delayedPayoutTx.getInput(0); + checkNotNull(input, "delayedPayoutTx.getInput(0) must not be null"); + // input.getConnectedOutput() is null as the tx is not committed at that point + + TransactionOutPoint outpoint = input.getOutpoint(); + if (!outpoint.getHash().toString().equals(depositTx.getHashAsString()) || outpoint.getIndex() != 0) { + throw new InvalidInputException("Input of delayed payout transaction does not point to output of deposit tx.\n" + + "Delayed payout tx=" + delayedPayoutTx + "\n" + + "Deposit tx=" + depositTx); + } + } } diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesFinalDelayedPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesFinalDelayedPayoutTx.java index 468ec7dca81..fa1aeacdbe6 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesFinalDelayedPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesFinalDelayedPayoutTx.java @@ -27,6 +27,8 @@ import lombok.extern.slf4j.Slf4j; +import static com.google.common.base.Preconditions.checkNotNull; + @Slf4j public class BuyerVerifiesFinalDelayedPayoutTx extends TradeTask { @SuppressWarnings({"unused"}) @@ -40,18 +42,25 @@ protected void run() { runInterceptHook(); Transaction delayedPayoutTx = trade.getDelayedPayoutTx(); + checkNotNull(delayedPayoutTx, "trade.getDelayedPayoutTx() must not be null"); // Check again tx DelayedPayoutTxValidation.validatePayoutTx(trade, delayedPayoutTx, processModel.getDaoFacade(), processModel.getBtcWalletService()); + // Now as we know the deposit tx we can also verify the input + Transaction depositTx = trade.getDepositTx(); + checkNotNull(depositTx, "trade.getDepositTx() must not be null"); + DelayedPayoutTxValidation.validatePayoutTxInput(depositTx, delayedPayoutTx); + complete(); } catch (DelayedPayoutTxValidation.DonationAddressException | DelayedPayoutTxValidation.MissingDelayedPayoutTxException | DelayedPayoutTxValidation.InvalidTxException | DelayedPayoutTxValidation.InvalidLockTimeException | - DelayedPayoutTxValidation.AmountMismatchException e) { + DelayedPayoutTxValidation.AmountMismatchException | + DelayedPayoutTxValidation.InvalidInputException e) { failed(e.getMessage()); } catch (Throwable t) { failed(t);