diff --git a/core/src/main/java/haveno/core/trade/HavenoUtils.java b/core/src/main/java/haveno/core/trade/HavenoUtils.java index 607d435442..634d97a923 100644 --- a/core/src/main/java/haveno/core/trade/HavenoUtils.java +++ b/core/src/main/java/haveno/core/trade/HavenoUtils.java @@ -45,12 +45,15 @@ import java.security.PrivateKey; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.concurrent.CountDownLatch; import lombok.extern.slf4j.Slf4j; import monero.common.MoneroRpcConnection; +import monero.daemon.model.MoneroOutput; import monero.wallet.model.MoneroDestination; import monero.wallet.model.MoneroTxWallet; @@ -496,4 +499,10 @@ public class HavenoUtils { } return null; } + + public static List getInputKeyImages(MoneroTxWallet tx) { + List inputKeyImages = new ArrayList(); + for (MoneroOutput input : tx.getInputs()) inputKeyImages.add(input.getKeyImage().getHex()); + return inputKeyImages; + } } diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java b/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java index 7536617c57..353c167ae8 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java @@ -31,13 +31,10 @@ import haveno.core.xmr.model.XmrAddressEntry; import haveno.core.xmr.wallet.XmrWalletService; import haveno.network.p2p.SendDirectMessageListener; import lombok.extern.slf4j.Slf4j; -import monero.daemon.model.MoneroOutput; import monero.wallet.model.MoneroTxWallet; import java.math.BigInteger; -import java.util.ArrayList; import java.util.Date; -import java.util.List; import java.util.UUID; // TODO (woodser): separate classes for deposit tx creation and contract request, or combine into ProcessInitMultisigRequest @@ -118,9 +115,15 @@ public class MaybeSendSignContractRequest extends TradeTask { } } catch (Exception e) { - // re-freeze reserved outputs - if (trade.getSelf().getReserveTxKeyImages() != null) { - trade.getXmrWalletService().freezeOutputs(trade.getSelf().getReserveTxKeyImages()); + // thaw deposit inputs + if (depositTx != null) { + trade.getXmrWalletService().thawOutputs(HavenoUtils.getInputKeyImages(depositTx)); + trade.getSelf().setReserveTxKeyImages(null); + } + + // re-freeze maker offer inputs + if (trade instanceof MakerTrade) { + trade.getXmrWalletService().freezeOutputs(trade.getOffer().getOfferPayload().getReserveTxKeyImages()); } throw e; @@ -129,17 +132,13 @@ public class MaybeSendSignContractRequest extends TradeTask { // reset protocol timeout trade.addInitProgressStep(); - // collect reserved key images - List reservedKeyImages = new ArrayList(); - for (MoneroOutput input : depositTx.getInputs()) reservedKeyImages.add(input.getKeyImage().getHex()); - // update trade state BigInteger securityDeposit = trade instanceof BuyerTrade ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee(); trade.getSelf().setSecurityDeposit(securityDeposit.subtract(depositTx.getFee())); trade.getSelf().setDepositTx(depositTx); trade.getSelf().setDepositTxHash(depositTx.getHash()); trade.getSelf().setDepositTxFee(depositTx.getFee()); - trade.getSelf().setReserveTxKeyImages(reservedKeyImages); + trade.getSelf().setReserveTxKeyImages(HavenoUtils.getInputKeyImages(depositTx)); trade.getSelf().setPayoutAddressString(trade.getXmrWalletService().getOrCreateAddressEntry(trade.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString()); // TODO (woodser): allow custom payout address? trade.getSelf().setPaymentAccountPayload(trade.getProcessModel().getPaymentAccountPayload(trade.getSelf().getPaymentAccountId())); } diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java index 5b1e0f335d..ff78eae758 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java @@ -20,17 +20,15 @@ package haveno.core.trade.protocol.tasks; import haveno.common.taskrunner.TaskRunner; import haveno.core.offer.OfferDirection; import haveno.core.trade.HavenoUtils; +import haveno.core.trade.TakerTrade; import haveno.core.trade.Trade; import haveno.core.trade.protocol.TradeProtocol; import haveno.core.xmr.model.XmrAddressEntry; import haveno.core.xmr.wallet.XmrWalletService; import lombok.extern.slf4j.Slf4j; -import monero.daemon.model.MoneroOutput; import monero.wallet.model.MoneroTxWallet; import java.math.BigInteger; -import java.util.ArrayList; -import java.util.List; @Slf4j public class TakerReserveTradeFunds extends TradeTask { @@ -44,6 +42,11 @@ public class TakerReserveTradeFunds extends TradeTask { try { runInterceptHook(); + // taker trade expected + if (!(trade instanceof TakerTrade)) { + throw new RuntimeException("Expected taker trade but was " + trade.getClass().getSimpleName() + " " + trade.getShortId() + ". That should never happen."); + } + // create reserve tx MoneroTxWallet reserveTx = null; synchronized (XmrWalletService.WALLET_LOCK) { @@ -60,31 +63,39 @@ public class TakerReserveTradeFunds extends TradeTask { String returnAddress = trade.getXmrWalletService().getOrCreateAddressEntry(trade.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(); // attempt creating reserve tx - synchronized (HavenoUtils.getWalletFunctionLock()) { - for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) { - try { - reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, takerFee, sendAmount, securityDeposit, returnAddress, false, null); - } catch (Exception e) { - log.warn("Error creating reserve tx, attempt={}/{}, tradeId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, trade.getShortId(), e.getMessage()); - if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; - HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying + try { + synchronized (HavenoUtils.getWalletFunctionLock()) { + for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) { + try { + reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, takerFee, sendAmount, securityDeposit, returnAddress, false, null); + } catch (Exception e) { + log.warn("Error creating reserve tx, attempt={}/{}, tradeId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, trade.getShortId(), e.getMessage()); + if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; + HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying + } + + // check for timeout + if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating reserve tx, tradeId=" + trade.getShortId()); + if (reserveTx != null) break; } - - // check for timeout - if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating reserve tx, tradeId=" + trade.getShortId()); - if (reserveTx != null) break; } + } catch (Exception e) { + + // thaw reserved inputs + if (reserveTx != null) { + model.getXmrWalletService().thawOutputs(HavenoUtils.getInputKeyImages(reserveTx)); + trade.getSelf().setReserveTxKeyImages(null); + } + + throw e; } + // reset protocol timeout trade.startProtocolTimeout(); - // collect reserved key images - List reservedKeyImages = new ArrayList(); - for (MoneroOutput input : reserveTx.getInputs()) reservedKeyImages.add(input.getKeyImage().getHex()); - // update trade state - trade.getTaker().setReserveTxKeyImages(reservedKeyImages); + trade.getTaker().setReserveTxKeyImages(HavenoUtils.getInputKeyImages(reserveTx)); } // save process state