mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-25 15:29:23 -05:00
synchronize reserving funds for open offer to fix race condition
This commit is contained in:
parent
9d9635ff50
commit
f0862b7aeb
@ -20,18 +20,11 @@ package haveno.core.offer.placeoffer.tasks;
|
|||||||
import haveno.common.taskrunner.Task;
|
import haveno.common.taskrunner.Task;
|
||||||
import haveno.common.taskrunner.TaskRunner;
|
import haveno.common.taskrunner.TaskRunner;
|
||||||
import haveno.core.offer.Offer;
|
import haveno.core.offer.Offer;
|
||||||
import haveno.core.offer.OfferDirection;
|
|
||||||
import haveno.core.offer.placeoffer.PlaceOfferModel;
|
import haveno.core.offer.placeoffer.PlaceOfferModel;
|
||||||
import haveno.core.trade.HavenoUtils;
|
|
||||||
import haveno.core.xmr.model.XmrAddressEntry;
|
import haveno.core.xmr.model.XmrAddressEntry;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import monero.daemon.model.MoneroOutput;
|
|
||||||
import monero.wallet.model.MoneroTxWallet;
|
import monero.wallet.model.MoneroTxWallet;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
||||||
|
|
||||||
@ -51,14 +44,8 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||||||
model.getXmrWalletService().getConnectionService().verifyConnection();
|
model.getXmrWalletService().getConnectionService().verifyConnection();
|
||||||
|
|
||||||
// create reserve tx
|
// create reserve tx
|
||||||
BigInteger penaltyFee = HavenoUtils.multiply(offer.getAmount(), offer.getPenaltyFeePct());
|
MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(model.getOpenOffer());
|
||||||
BigInteger makerFee = offer.getMaxMakerFee();
|
model.setReserveTx(reserveTx);
|
||||||
BigInteger sendAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.ZERO : offer.getAmount();
|
|
||||||
BigInteger securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit();
|
|
||||||
String returnAddress = model.getXmrWalletService().getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString();
|
|
||||||
XmrAddressEntry fundingEntry = model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.OFFER_FUNDING).orElse(null);
|
|
||||||
Integer preferredSubaddressIndex = fundingEntry == null ? null : fundingEntry.getSubaddressIndex();
|
|
||||||
MoneroTxWallet reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, 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()) {
|
if (!model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).isPresent()) {
|
||||||
@ -67,17 +54,6 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||||||
|
|
||||||
// reset protocol timeout
|
// reset protocol timeout
|
||||||
model.getProtocol().startTimeoutTimer();
|
model.getProtocol().startTimeoutTimer();
|
||||||
|
|
||||||
// collect reserved key images
|
|
||||||
List<String> reservedKeyImages = new ArrayList<String>();
|
|
||||||
for (MoneroOutput input : reserveTx.getInputs()) reservedKeyImages.add(input.getKeyImage().getHex());
|
|
||||||
|
|
||||||
// save offer state
|
|
||||||
model.setReserveTx(reserveTx);
|
|
||||||
model.getOpenOffer().setReserveTxHash(reserveTx.getHash());
|
|
||||||
model.getOpenOffer().setReserveTxHex(reserveTx.getFullHex());
|
|
||||||
model.getOpenOffer().setReserveTxKey(reserveTx.getKey());
|
|
||||||
offer.getOfferPayload().setReserveTxKeyImages(reservedKeyImages);
|
|
||||||
complete();
|
complete();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
offer.setErrorMessage("An error occurred.\n" +
|
offer.setErrorMessage("An error occurred.\n" +
|
||||||
|
@ -33,6 +33,8 @@ import haveno.common.util.Utilities;
|
|||||||
import haveno.core.api.AccountServiceListener;
|
import haveno.core.api.AccountServiceListener;
|
||||||
import haveno.core.api.CoreAccountService;
|
import haveno.core.api.CoreAccountService;
|
||||||
import haveno.core.api.XmrConnectionService;
|
import haveno.core.api.XmrConnectionService;
|
||||||
|
import haveno.core.offer.Offer;
|
||||||
|
import haveno.core.offer.OfferDirection;
|
||||||
import haveno.core.offer.OpenOffer;
|
import haveno.core.offer.OpenOffer;
|
||||||
import haveno.core.trade.BuyerTrade;
|
import haveno.core.trade.BuyerTrade;
|
||||||
import haveno.core.trade.HavenoUtils;
|
import haveno.core.trade.HavenoUtils;
|
||||||
@ -546,6 +548,40 @@ public class XmrWalletService {
|
|||||||
return new ArrayList<Integer>(subaddressIndices);
|
return new ArrayList<Integer>(subaddressIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reserve tx for an open offer and freeze its inputs.
|
||||||
|
*
|
||||||
|
* @param openOffer is the open offer to create a reserve tx for
|
||||||
|
*/
|
||||||
|
public MoneroTxWallet createReserveTx(OpenOffer openOffer) {
|
||||||
|
synchronized (walletLock) {
|
||||||
|
|
||||||
|
// collect offer data
|
||||||
|
Offer offer = openOffer.getOffer();
|
||||||
|
BigInteger penaltyFee = HavenoUtils.multiply(offer.getAmount(), offer.getPenaltyFeePct());
|
||||||
|
BigInteger makerFee = offer.getMaxMakerFee();
|
||||||
|
BigInteger sendAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.ZERO : offer.getAmount();
|
||||||
|
BigInteger securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit();
|
||||||
|
String returnAddress = getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString();
|
||||||
|
XmrAddressEntry fundingEntry = getAddressEntry(offer.getId(), XmrAddressEntry.Context.OFFER_FUNDING).orElse(null);
|
||||||
|
Integer preferredSubaddressIndex = fundingEntry == null ? null : fundingEntry.getSubaddressIndex();
|
||||||
|
|
||||||
|
// create reserve tx
|
||||||
|
MoneroTxWallet reserveTx = createReserveTx(penaltyFee, makerFee, sendAmount, securityDeposit, returnAddress, openOffer.isReserveExactAmount(), preferredSubaddressIndex);
|
||||||
|
|
||||||
|
// collect reserved key images
|
||||||
|
List<String> reservedKeyImages = new ArrayList<String>();
|
||||||
|
for (MoneroOutput input : reserveTx.getInputs()) reservedKeyImages.add(input.getKeyImage().getHex());
|
||||||
|
|
||||||
|
// save offer state
|
||||||
|
openOffer.setReserveTxHash(reserveTx.getHash());
|
||||||
|
openOffer.setReserveTxHex(reserveTx.getFullHex());
|
||||||
|
openOffer.setReserveTxKey(reserveTx.getKey());
|
||||||
|
offer.getOfferPayload().setReserveTxKeyImages(reservedKeyImages);
|
||||||
|
return reserveTx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the reserve tx and freeze its inputs. The full amount is returned
|
* Create the reserve tx and freeze its inputs. The full amount is returned
|
||||||
* to the sender's payout address less the penalty and mining fees.
|
* to the sender's payout address less the penalty and mining fees.
|
||||||
|
Loading…
Reference in New Issue
Block a user