From 5107c6ba578cd658f06b92e554eb0022d15944e0 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Mon, 17 Mar 2025 08:32:34 -0400 Subject: [PATCH] fixes to process payout tx and revert to payment sent state --- .../main/java/haveno/core/trade/Trade.java | 29 ++++++++++++++----- .../core/trade/protocol/SellerProtocol.java | 2 +- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index 0f6606c91a..75ef6ead4c 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -644,7 +644,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { // reset seller's payment received state if no ack receive if (this instanceof SellerTrade && getState().ordinal() >= Trade.State.SELLER_CONFIRMED_PAYMENT_RECEIPT.ordinal() && getState().ordinal() < Trade.State.SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG.ordinal()) { log.warn("Resetting state of {} {} from {} to {} because no ack was received", getClass().getSimpleName(), getId(), getState(), Trade.State.BUYER_SENT_PAYMENT_SENT_MSG); - setState(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG); + resetToPaymentSentState(); } // handle trade state events @@ -770,6 +770,12 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { isFullyInitialized = true; } + public void resetToPaymentSentState() { + setState(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG); + for (TradePeer peer : getAllPeers()) peer.setPaymentReceivedMessage(null); + setPayoutTxHex(null); + } + public void reprocessApplicableMessages() { if (!isDepositRequested() || isPayoutUnlocked() || isCompleted()) return; getProtocol().maybeReprocessPaymentSentMessage(false); @@ -1365,7 +1371,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { MoneroTxSet describedTxSet = wallet.describeTxSet(new MoneroTxSet().setMultisigTxHex(payoutTxHex)); if (describedTxSet.getTxs() == null || describedTxSet.getTxs().size() != 1) throw new IllegalArgumentException("Bad payout tx"); // TODO (woodser): test nack MoneroTxWallet payoutTx = describedTxSet.getTxs().get(0); - if (payoutTxId == null) updatePayout(payoutTx); // update payout tx if not signed + if (payoutTxId == null) updatePayout(payoutTx); // update payout tx if id currently unknown // verify payout tx has exactly 2 destinations if (payoutTx.getOutgoingTransfer() == null || payoutTx.getOutgoingTransfer().getDestinations() == null || payoutTx.getOutgoingTransfer().getDestinations().size() != 2) throw new IllegalArgumentException("Payout tx does not have exactly two destinations"); @@ -1396,6 +1402,9 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { BigInteger expectedSellerPayout = sellerDepositAmount.subtract(tradeAmount).subtract(txCostSplit); if (!sellerPayoutDestination.getAmount().equals(expectedSellerPayout)) throw new IllegalArgumentException("Seller destination amount is not deposit amount - trade amount - 1/2 tx costs, " + sellerPayoutDestination.getAmount() + " vs " + expectedSellerPayout); + // update payout tx + updatePayout(payoutTx); + // check connection boolean doSign = sign && getPayoutTxHex() == null; if (doSign || publish) verifyDaemonConnection(); @@ -1404,19 +1413,15 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { if (doSign) { // sign tx + String signedPayoutTxHex; try { MoneroMultisigSignResult result = wallet.signMultisigTxHex(payoutTxHex); if (result.getSignedMultisigTxHex() == null) throw new IllegalArgumentException("Error signing payout tx, signed multisig hex is null"); - setPayoutTxHex(result.getSignedMultisigTxHex()); + signedPayoutTxHex = result.getSignedMultisigTxHex(); } catch (Exception e) { throw new IllegalStateException(e); } - // describe result - describedTxSet = wallet.describeMultisigTxSet(getPayoutTxHex()); - payoutTx = describedTxSet.getTxs().get(0); - updatePayout(payoutTx); - // verify fee is within tolerance by recreating payout tx // TODO (monero-project): creating tx will require exchanging updated multisig hex if message needs reprocessed. provide weight with describe_transfer so fee can be estimated? log.info("Creating fee estimate tx for {} {}", getClass().getSimpleName(), getId()); @@ -1426,6 +1431,14 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { double feeDiff = payoutTx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); // TODO: use BigDecimal? if (feeDiff > XmrWalletService.MINER_FEE_TOLERANCE) throw new IllegalArgumentException("Miner fee is not within " + (XmrWalletService.MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + payoutTx.getFee()); log.info("Payout tx fee {} is within tolerance, diff %={}", payoutTx.getFee(), feeDiff); + + // set signed payout tx hex + setPayoutTxHex(signedPayoutTxHex); + + // describe result + describedTxSet = wallet.describeMultisigTxSet(getPayoutTxHex()); + payoutTx = describedTxSet.getTxs().get(0); + updatePayout(payoutTx); } // save trade state diff --git a/core/src/main/java/haveno/core/trade/protocol/SellerProtocol.java b/core/src/main/java/haveno/core/trade/protocol/SellerProtocol.java index 4daa800229..021de47450 100644 --- a/core/src/main/java/haveno/core/trade/protocol/SellerProtocol.java +++ b/core/src/main/java/haveno/core/trade/protocol/SellerProtocol.java @@ -146,7 +146,7 @@ public class SellerProtocol extends DisputeProtocol { resultHandler.handleResult(); }, (errorMessage) -> { log.warn("Error confirming payment received, reverting state to {}, error={}", Trade.State.BUYER_SENT_PAYMENT_SENT_MSG, errorMessage); - trade.setState(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG); + trade.resetToPaymentSentState(); handleTaskRunnerFault(event, errorMessage); }))) .run(() -> trade.advanceState(Trade.State.SELLER_CONFIRMED_PAYMENT_RECEIPT))