mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-10-12 02:20:53 -04:00
use nack flow if cannot create payout tx and stop repeat sending
This commit is contained in:
parent
0bf6052b7c
commit
4989eab498
7 changed files with 44 additions and 21 deletions
|
@ -542,7 +542,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
|||
break;
|
||||
} catch (Exception e) {
|
||||
if (trade.isPayoutPublished()) return null;
|
||||
if (HavenoUtils.isTransactionRejected(e) || HavenoUtils.isNotEnoughSigners(e) || HavenoUtils.isFailedToParse(e)) throw new IllegalArgumentException(e);
|
||||
if (HavenoUtils.isMultisigError(e)) throw new IllegalArgumentException(e);
|
||||
log.warn("Failed to submit dispute payout tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
|
||||
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
|
||||
if (trade.getXmrConnectionService().isConnected()) trade.requestSwitchToNextBestConnection(sourceConnection);
|
||||
|
|
|
@ -630,22 +630,35 @@ public class HavenoUtils {
|
|||
return isConnectionRefused(e) || isReadTimeout(e) || XmrWalletBase.isSyncWithProgressTimeout(e);
|
||||
}
|
||||
|
||||
public static boolean isNotEnoughSigners(Throwable e) {
|
||||
private static boolean isNotEnoughSigners(Throwable e) {
|
||||
return e != null && e.getMessage().contains("Not enough signers");
|
||||
}
|
||||
|
||||
public static boolean isFailedToParse(Throwable e) {
|
||||
private static boolean isFailedToParse(Throwable e) {
|
||||
return e != null && e.getMessage().contains("Failed to parse");
|
||||
}
|
||||
|
||||
private static boolean isStaleData(Throwable e) {
|
||||
return e != null && e.getMessage().contains("stale data");
|
||||
}
|
||||
|
||||
private static boolean isNoTransactionCreated(Throwable e) {
|
||||
return e != null && e.getMessage().contains("No transaction created");
|
||||
}
|
||||
|
||||
private static boolean isLRNotFound(Throwable e) {
|
||||
return e != null && e.getMessage().contains("LR not found for enough participants");
|
||||
}
|
||||
|
||||
// TODO: handling specific error messages is brittle, inverse so all errors are illegal except known local issues?
|
||||
public static boolean isMultisigError(Throwable e) {
|
||||
return isLRNotFound(e) || isNotEnoughSigners(e) || isNoTransactionCreated(e) || isFailedToParse(e) || isStaleData(e);
|
||||
}
|
||||
|
||||
public static boolean isTransactionRejected(Throwable e) {
|
||||
return e != null && e.getMessage().contains("was rejected");
|
||||
}
|
||||
|
||||
public static boolean isLRNotFound(Throwable e) {
|
||||
return e != null && e.getMessage().contains("LR not found for enough participants");
|
||||
}
|
||||
|
||||
public static boolean isIllegal(Throwable e) {
|
||||
return e instanceof IllegalArgumentException || e instanceof IllegalStateException;
|
||||
}
|
||||
|
|
|
@ -828,7 +828,6 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
setState(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG);
|
||||
for (TradePeer peer : getAllPeers()) {
|
||||
peer.setPaymentReceivedMessage(null);
|
||||
peer.setPaymentReceivedMessageState(MessageState.UNDEFINED);
|
||||
}
|
||||
setPayoutTxHex(null);
|
||||
}
|
||||
|
@ -1385,7 +1384,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
.setRelay(false)
|
||||
.setPriority(XmrWalletService.PROTOCOL_FEE_PRIORITY));
|
||||
} catch (Exception e) {
|
||||
if (HavenoUtils.isLRNotFound(e)) throw new IllegalStateException(e);
|
||||
if (HavenoUtils.isMultisigError(e)) throw new IllegalStateException(e);
|
||||
else throw e;
|
||||
}
|
||||
|
||||
|
@ -1409,6 +1408,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
} catch (IllegalArgumentException | IllegalStateException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
if (HavenoUtils.isMultisigError(e)) throw new IllegalStateException(e);
|
||||
if (e.getMessage().contains("not possible")) throw new IllegalArgumentException("Loser payout is too small to cover the mining fee");
|
||||
handleWalletError(e, sourceConnection, i + 1);
|
||||
doPollWallet();
|
||||
|
@ -1557,7 +1557,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
setPayoutStatePublished();
|
||||
} catch (Exception e) {
|
||||
if (!isPayoutPublished()) {
|
||||
if (HavenoUtils.isTransactionRejected(e) || HavenoUtils.isNotEnoughSigners(e) || HavenoUtils.isFailedToParse(e)) throw new IllegalArgumentException(e);
|
||||
if (HavenoUtils.isTransactionRejected(e) || HavenoUtils.isMultisigError(e)) throw new IllegalArgumentException(e);
|
||||
throw new RuntimeException("Failed to submit payout tx for " + getClass().getSimpleName() + " " + getId() + ", error=" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
@ -2191,13 +2191,13 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
throw new RuntimeException("Trade is not maker, taker, or arbitrator");
|
||||
}
|
||||
|
||||
private List<TradePeer> getOtherPeers() {
|
||||
public List<TradePeer> getOtherPeers() {
|
||||
List<TradePeer> peers = getAllPeers();
|
||||
if (!peers.remove(getSelf())) throw new IllegalStateException("Failed to remove self from list of peers");
|
||||
return peers;
|
||||
}
|
||||
|
||||
private List<TradePeer> getAllPeers() {
|
||||
public List<TradePeer> getAllPeers() {
|
||||
List<TradePeer> peers = new ArrayList<TradePeer>();
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue