From 4989eab49846c38c72b047282f4d55254cb9556f Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 24 Sep 2025 01:38:59 -0400 Subject: [PATCH] use nack flow if cannot create payout tx and stop repeat sending --- .../arbitration/ArbitrationManager.java | 2 +- .../java/haveno/core/trade/HavenoUtils.java | 25 ++++++++++++++----- .../main/java/haveno/core/trade/Trade.java | 16 ++++++------ .../core/trade/protocol/TradeProtocol.java | 2 +- .../tasks/ProcessPaymentReceivedMessage.java | 7 ++++-- .../SellerPreparePaymentReceivedMessage.java | 8 +++++- .../SellerSendPaymentReceivedMessage.java | 5 ++-- 7 files changed, 44 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java b/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java index 3b2ec3735a..163eb15477 100644 --- a/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java +++ b/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java @@ -542,7 +542,7 @@ public final class ArbitrationManager extends DisputeManager getOtherPeers() { + public List getOtherPeers() { List peers = getAllPeers(); if (!peers.remove(getSelf())) throw new IllegalStateException("Failed to remove self from list of peers"); return peers; } - private List getAllPeers() { + public List getAllPeers() { List peers = new ArrayList(); peers.add(getMaker()); peers.add(getTaker()); @@ -3194,7 +3194,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { processModel.setPaymentSentPayoutTxStale(true); if (paymentReceivedNackSender != null) { paymentReceivedNackSender.setPaymentReceivedMessage(null); - paymentReceivedNackSender.setPaymentReceivedMessageState(MessageState.UNDEFINED); + paymentReceivedNackSender.setPaymentReceivedMessageState(MessageState.NACKED); } if (!isPayoutPublished()) { getSelf().setUnsignedPayoutTxHex(null); @@ -3302,8 +3302,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { // rescan blockchain rescanBlockchain(); - // import multisig hex - log.warn("Importing multisig hex to recover wallet data for {} {}", getClass().getSimpleName(), getShortId()); + // must import multisig hex after rescan + log.warn("Importing multisig hex after rescanning blockchain for {} {}", getClass().getSimpleName(), getShortId()); importMultisigHex(); // poll wallet diff --git a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java index b4c809a20e..60e80feb6d 100644 --- a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java @@ -120,7 +120,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D private boolean makerInitTradeRequestHasBeenNacked = false; private PaymentReceivedMessage lastAckedPaymentReceivedMessage = null; - private static int MAX_PAYMENT_RECEIVED_NACKS = 5; + private static int MAX_PAYMENT_RECEIVED_NACKS = 6; private int numPaymentReceivedNacks = 0; /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java index 55cd2fd5ca..502b673cad 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java @@ -47,7 +47,6 @@ import haveno.core.trade.messages.PaymentSentMessage; import haveno.core.util.Validator; import lombok.extern.slf4j.Slf4j; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @Slf4j @@ -66,7 +65,6 @@ public class ProcessPaymentReceivedMessage extends TradeTask { PaymentReceivedMessage message = (PaymentReceivedMessage) processModel.getTradeMessage(); checkNotNull(message); Validator.checkTradeId(processModel.getOfferId(), message); - checkArgument(message.getUnsignedPayoutTxHex() != null || message.getSignedPayoutTxHex() != null, "No payout tx hex provided"); // verify signature of payment received message HavenoUtils.verifyPaymentReceivedMessage(trade, message); @@ -146,6 +144,11 @@ public class ProcessPaymentReceivedMessage extends TradeTask { // handle if payout tx not published if (!trade.isPayoutPublished()) { + // nack with updated multisig info if no payout tx provided + if (message.getUnsignedPayoutTxHex() == null && message.getSignedPayoutTxHex() == null && message.getPayoutTxId() == null) { + throw new IllegalStateException("No payout tx provided in PaymentReceivedMessage for " + trade.getClass().getSimpleName() + " " + trade.getId()); + } + // wait to publish payout tx if defer flag set from seller (payout is expected) if (message.isDeferPublishPayout()) { log.info("Deferring publishing payout tx for {} {}", trade.getClass().getSimpleName(), trade.getId()); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerPreparePaymentReceivedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerPreparePaymentReceivedMessage.java index 87a8c9b669..6617cac1e9 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerPreparePaymentReceivedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerPreparePaymentReceivedMessage.java @@ -107,7 +107,13 @@ public class SellerPreparePaymentReceivedMessage extends TradeTask { trade.requestPersistence(); complete(); } catch (Throwable t) { - failed(t); + if (HavenoUtils.isIllegal(t)) { + log.error("Illegal exception preparing payment received message in {} {}: {}", trade.getClass().getSimpleName(), trade.getId(), t.getMessage(), t); + trade.exportMultisigHex(); + complete(); // proceed to send the message to perform nack flow with updated multsig state + } else { + failed(t); + } } } diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java index 34bb458c1a..1c2b3bbc69 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java @@ -96,6 +96,9 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag try { runInterceptHook(); + // reset ack state + getReceiver().setPaymentReceivedMessageState(MessageState.UNDEFINED); + // skip if stopped if (stopSending()) { if (!isCompleted()) complete(); @@ -149,8 +152,6 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag // verify message if (trade.isPayoutPublished()) { checkArgument(message.getUpdatedMultisigHex() != null || message.getPayoutTxId() != null, "PaymentReceivedMessage does not include updated multisig hex or payout tx id after payout published"); - } else { - checkArgument(message.getUnsignedPayoutTxHex() != null || message.getSignedPayoutTxHex() != null, "PaymentReceivedMessage does not include payout tx hex"); } // sign message