mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-22 19:49:32 +00:00
stop trade protocol if timeout while creating reserve or deposit tx
This commit is contained in:
parent
db12f1c2cb
commit
7eabde63f3
3 changed files with 100 additions and 92 deletions
|
@ -58,8 +58,7 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||
Integer preferredSubaddressIndex = fundingEntry == null ? null : fundingEntry.getSubaddressIndex();
|
||||
MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(makerFee, sendAmount, securityDeposit, returnAddress, model.getOpenOffer().isReserveExactAmount(), preferredSubaddressIndex);
|
||||
|
||||
// check for error in case creating reserve tx exceeded timeout
|
||||
// TODO: better way?
|
||||
// check for error in case creating reserve tx exceeded timeout // TODO: better way?
|
||||
if (!model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).isPresent()) {
|
||||
throw new RuntimeException("An error has occurred posting offer " + offer.getId() + " causing its subaddress entry to be deleted");
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import haveno.core.trade.MakerTrade;
|
|||
import haveno.core.trade.Trade;
|
||||
import haveno.core.trade.Trade.State;
|
||||
import haveno.core.trade.messages.SignContractRequest;
|
||||
import haveno.core.trade.protocol.TradeProtocol;
|
||||
import haveno.core.xmr.model.XmrAddressEntry;
|
||||
import haveno.network.p2p.SendDirectMessageListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -53,103 +54,111 @@ public class MaybeSendSignContractRequest extends TradeTask {
|
|||
@Override
|
||||
protected void run() {
|
||||
try {
|
||||
runInterceptHook();
|
||||
runInterceptHook();
|
||||
|
||||
// skip if arbitrator
|
||||
if (trade instanceof ArbitratorTrade) {
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
// skip if arbitrator
|
||||
if (trade instanceof ArbitratorTrade) {
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
|
||||
// skip if multisig wallet not complete
|
||||
if (processModel.getMultisigAddress() == null) {
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
// skip if multisig wallet not complete
|
||||
if (processModel.getMultisigAddress() == null) {
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
|
||||
// skip if deposit tx already created
|
||||
if (trade.getSelf().getDepositTx() != null) {
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
// skip if deposit tx already created
|
||||
if (trade.getSelf().getDepositTx() != null) {
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize progress steps
|
||||
trade.addInitProgressStep();
|
||||
// initialize progress steps
|
||||
trade.addInitProgressStep();
|
||||
|
||||
// create deposit tx and freeze inputs
|
||||
Integer subaddressIndex = null;
|
||||
boolean reserveExactAmount = false;
|
||||
if (trade instanceof MakerTrade) {
|
||||
reserveExactAmount = processModel.getOpenOfferManager().getOpenOfferById(trade.getId()).get().isReserveExactAmount();
|
||||
if (reserveExactAmount) subaddressIndex = model.getXmrWalletService().getAddressEntry(trade.getId(), XmrAddressEntry.Context.OFFER_FUNDING).get().getSubaddressIndex();
|
||||
}
|
||||
MoneroTxWallet depositTx = trade.getXmrWalletService().createDepositTx(trade, reserveExactAmount, subaddressIndex);
|
||||
// create deposit tx and freeze inputs
|
||||
Integer subaddressIndex = null;
|
||||
boolean reserveExactAmount = false;
|
||||
if (trade instanceof MakerTrade) {
|
||||
reserveExactAmount = processModel.getOpenOfferManager().getOpenOfferById(trade.getId()).get().isReserveExactAmount();
|
||||
if (reserveExactAmount) subaddressIndex = model.getXmrWalletService().getAddressEntry(trade.getId(), XmrAddressEntry.Context.OFFER_FUNDING).get().getSubaddressIndex();
|
||||
}
|
||||
MoneroTxWallet depositTx = trade.getXmrWalletService().createDepositTx(trade, reserveExactAmount, subaddressIndex);
|
||||
|
||||
// collect reserved key images
|
||||
List<String> reservedKeyImages = new ArrayList<String>();
|
||||
for (MoneroOutput input : depositTx.getInputs()) reservedKeyImages.add(input.getKeyImage().getHex());
|
||||
// check if trade still exists
|
||||
if (!processModel.getTradeManager().hasOpenTrade(trade)) {
|
||||
throw new RuntimeException("Trade protocol has timed out while creating reserve tx, tradeId=" + trade.getId());
|
||||
}
|
||||
|
||||
// save process state
|
||||
trade.getSelf().setDepositTx(depositTx);
|
||||
trade.getSelf().setDepositTxHash(depositTx.getHash());
|
||||
trade.getSelf().setDepositTxFee(depositTx.getFee());
|
||||
trade.getSelf().setReserveTxKeyImages(reservedKeyImages);
|
||||
trade.getSelf().setPayoutAddressString(trade.getXmrWalletService().getOrCreateAddressEntry(processModel.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString()); // TODO (woodser): allow custom payout address?
|
||||
trade.getSelf().setPaymentAccountPayload(trade.getProcessModel().getPaymentAccountPayload(trade.getSelf().getPaymentAccountId()));
|
||||
// reset protocol timeout
|
||||
trade.getProtocol().startTimeout(TradeProtocol.TRADE_TIMEOUT);
|
||||
|
||||
// TODO: security deposit should be based on trade amount, not max offer amount
|
||||
BigInteger securityDeposit = trade instanceof BuyerTrade ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee();
|
||||
trade.getSelf().setSecurityDeposit(securityDeposit.subtract(depositTx.getFee()));
|
||||
// collect reserved key images
|
||||
List<String> reservedKeyImages = new ArrayList<String>();
|
||||
for (MoneroOutput input : depositTx.getInputs()) reservedKeyImages.add(input.getKeyImage().getHex());
|
||||
|
||||
// maker signs deposit hash nonce to avoid challenge protocol
|
||||
byte[] sig = null;
|
||||
if (trade instanceof MakerTrade) {
|
||||
sig = HavenoUtils.sign(processModel.getP2PService().getKeyRing(), depositTx.getHash());
|
||||
}
|
||||
// save process state
|
||||
trade.getSelf().setDepositTx(depositTx);
|
||||
trade.getSelf().setDepositTxHash(depositTx.getHash());
|
||||
trade.getSelf().setDepositTxFee(depositTx.getFee());
|
||||
trade.getSelf().setReserveTxKeyImages(reservedKeyImages);
|
||||
trade.getSelf().setPayoutAddressString(trade.getXmrWalletService().getOrCreateAddressEntry(processModel.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString()); // TODO (woodser): allow custom payout address?
|
||||
trade.getSelf().setPaymentAccountPayload(trade.getProcessModel().getPaymentAccountPayload(trade.getSelf().getPaymentAccountId()));
|
||||
|
||||
// create request for peer and arbitrator to sign contract
|
||||
SignContractRequest request = new SignContractRequest(
|
||||
trade.getOffer().getId(),
|
||||
UUID.randomUUID().toString(),
|
||||
Version.getP2PMessageVersion(),
|
||||
new Date().getTime(),
|
||||
trade.getProcessModel().getAccountId(),
|
||||
trade.getSelf().getPaymentAccountPayload().getHash(),
|
||||
trade.getSelf().getPayoutAddressString(),
|
||||
depositTx.getHash(),
|
||||
sig);
|
||||
// TODO: security deposit should be based on trade amount, not max offer amount
|
||||
BigInteger securityDeposit = trade instanceof BuyerTrade ? trade.getBuyerSecurityDepositBeforeMiningFee() : trade.getSellerSecurityDepositBeforeMiningFee();
|
||||
trade.getSelf().setSecurityDeposit(securityDeposit.subtract(depositTx.getFee()));
|
||||
|
||||
// send request to trading peer
|
||||
processModel.getP2PService().sendEncryptedDirectMessage(trade.getTradePeer().getNodeAddress(), trade.getTradePeer().getPubKeyRing(), request, new SendDirectMessageListener() {
|
||||
@Override
|
||||
public void onArrived() {
|
||||
log.info("{} arrived: trading peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), trade.getTradePeer().getNodeAddress(), trade.getId());
|
||||
ack1 = true;
|
||||
if (ack1 && ack2) completeAux();
|
||||
}
|
||||
@Override
|
||||
public void onFault(String errorMessage) {
|
||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), trade.getTradePeer().getNodeAddress(), trade.getId(), errorMessage);
|
||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
||||
failed();
|
||||
}
|
||||
});
|
||||
// maker signs deposit hash nonce to avoid challenge protocol
|
||||
byte[] sig = null;
|
||||
if (trade instanceof MakerTrade) {
|
||||
sig = HavenoUtils.sign(processModel.getP2PService().getKeyRing(), depositTx.getHash());
|
||||
}
|
||||
|
||||
// send request to arbitrator
|
||||
processModel.getP2PService().sendEncryptedDirectMessage(trade.getArbitrator().getNodeAddress(), trade.getArbitrator().getPubKeyRing(), request, new SendDirectMessageListener() {
|
||||
@Override
|
||||
public void onArrived() {
|
||||
log.info("{} arrived: trading peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), trade.getArbitrator().getNodeAddress(), trade.getId());
|
||||
ack2 = true;
|
||||
if (ack1 && ack2) completeAux();
|
||||
}
|
||||
@Override
|
||||
public void onFault(String errorMessage) {
|
||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), trade.getArbitrator().getNodeAddress(), trade.getId(), errorMessage);
|
||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
||||
failed();
|
||||
}
|
||||
});
|
||||
// create request for peer and arbitrator to sign contract
|
||||
SignContractRequest request = new SignContractRequest(
|
||||
trade.getOffer().getId(),
|
||||
UUID.randomUUID().toString(),
|
||||
Version.getP2PMessageVersion(),
|
||||
new Date().getTime(),
|
||||
trade.getProcessModel().getAccountId(),
|
||||
trade.getSelf().getPaymentAccountPayload().getHash(),
|
||||
trade.getSelf().getPayoutAddressString(),
|
||||
depositTx.getHash(),
|
||||
sig);
|
||||
|
||||
// send request to trading peer
|
||||
processModel.getP2PService().sendEncryptedDirectMessage(trade.getTradePeer().getNodeAddress(), trade.getTradePeer().getPubKeyRing(), request, new SendDirectMessageListener() {
|
||||
@Override
|
||||
public void onArrived() {
|
||||
log.info("{} arrived: trading peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), trade.getTradePeer().getNodeAddress(), trade.getId());
|
||||
ack1 = true;
|
||||
if (ack1 && ack2) completeAux();
|
||||
}
|
||||
@Override
|
||||
public void onFault(String errorMessage) {
|
||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), trade.getTradePeer().getNodeAddress(), trade.getId(), errorMessage);
|
||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
||||
failed();
|
||||
}
|
||||
});
|
||||
|
||||
// send request to arbitrator
|
||||
processModel.getP2PService().sendEncryptedDirectMessage(trade.getArbitrator().getNodeAddress(), trade.getArbitrator().getPubKeyRing(), request, new SendDirectMessageListener() {
|
||||
@Override
|
||||
public void onArrived() {
|
||||
log.info("{} arrived: trading peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), trade.getArbitrator().getNodeAddress(), trade.getId());
|
||||
ack2 = true;
|
||||
if (ack1 && ack2) completeAux();
|
||||
}
|
||||
@Override
|
||||
public void onFault(String errorMessage) {
|
||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), trade.getArbitrator().getNodeAddress(), trade.getId(), errorMessage);
|
||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
||||
failed();
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
failed(t);
|
||||
}
|
||||
|
|
|
@ -47,15 +47,15 @@ public class TakerReserveTradeFunds extends TradeTask {
|
|||
String returnAddress = model.getXmrWalletService().getOrCreateAddressEntry(trade.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString();
|
||||
MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(takerFee, sendAmount, securityDeposit, returnAddress, false, null);
|
||||
|
||||
// check if trade still exists
|
||||
if (!processModel.getTradeManager().hasOpenTrade(trade)) {
|
||||
throw new RuntimeException("Trade protocol has timed out while creating reserve tx, tradeId=" + trade.getId());
|
||||
}
|
||||
|
||||
// collect reserved key images
|
||||
List<String> reservedKeyImages = new ArrayList<String>();
|
||||
for (MoneroOutput input : reserveTx.getInputs()) reservedKeyImages.add(input.getKeyImage().getHex());
|
||||
|
||||
// check if trade still exists
|
||||
if (!processModel.getTradeManager().hasOpenTrade(trade)) {
|
||||
throw new RuntimeException("Trade protocol no longer exists after creating reserve tx, tradeId=" + trade.getId());
|
||||
}
|
||||
|
||||
// reset protocol timeout
|
||||
trade.getProtocol().startTimeout(TradeProtocol.TRADE_TIMEOUT);
|
||||
|
||||
|
|
Loading…
Reference in a new issue