mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-01-03 09:29:44 +00:00
reprocess scheduled offers on new block
This commit is contained in:
parent
a1e554473a
commit
35f275805b
5 changed files with 55 additions and 27 deletions
|
@ -103,6 +103,12 @@ public final class OpenOffer implements Tradable {
|
||||||
@Setter
|
@Setter
|
||||||
transient private long mempoolStatus = -1;
|
transient private long mempoolStatus = -1;
|
||||||
transient final private ObjectProperty<State> stateProperty = new SimpleObjectProperty<>(state);
|
transient final private ObjectProperty<State> stateProperty = new SimpleObjectProperty<>(state);
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
transient boolean isProcessing = false;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
transient int numProcessingAttempts = 0;
|
||||||
|
|
||||||
public OpenOffer(Offer offer) {
|
public OpenOffer(Offer offer) {
|
||||||
this(offer, 0, false);
|
this(offer, 0, false);
|
||||||
|
@ -193,9 +199,9 @@ public final class OpenOffer implements Tradable {
|
||||||
proto.getScheduledTxHashesList(),
|
proto.getScheduledTxHashesList(),
|
||||||
ProtoUtil.stringOrNullFromProto(proto.getSplitOutputTxHash()),
|
ProtoUtil.stringOrNullFromProto(proto.getSplitOutputTxHash()),
|
||||||
proto.getSplitOutputTxFee(),
|
proto.getSplitOutputTxFee(),
|
||||||
proto.getReserveTxHash(),
|
ProtoUtil.stringOrNullFromProto(proto.getReserveTxHash()),
|
||||||
proto.getReserveTxHex(),
|
ProtoUtil.stringOrNullFromProto(proto.getReserveTxHex()),
|
||||||
proto.getReserveTxKey());
|
ProtoUtil.stringOrNullFromProto(proto.getReserveTxKey()));
|
||||||
return openOffer;
|
return openOffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
private static final long REPUBLISH_AGAIN_AT_STARTUP_DELAY_SEC = 30;
|
private static final long REPUBLISH_AGAIN_AT_STARTUP_DELAY_SEC = 30;
|
||||||
private static final long REPUBLISH_INTERVAL_MS = TimeUnit.MINUTES.toMillis(30);
|
private static final long REPUBLISH_INTERVAL_MS = TimeUnit.MINUTES.toMillis(30);
|
||||||
private static final long REFRESH_INTERVAL_MS = OfferPayload.TTL / 2;
|
private static final long REFRESH_INTERVAL_MS = OfferPayload.TTL / 2;
|
||||||
|
private static final int MAX_PROCESS_ATTEMPTS = 5;
|
||||||
|
|
||||||
private final CoreContext coreContext;
|
private final CoreContext coreContext;
|
||||||
private final KeyRing keyRing;
|
private final KeyRing keyRing;
|
||||||
|
@ -156,7 +157,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
private final SignedOfferList signedOffers = new SignedOfferList();
|
private final SignedOfferList signedOffers = new SignedOfferList();
|
||||||
private final PersistenceManager<SignedOfferList> signedOfferPersistenceManager;
|
private final PersistenceManager<SignedOfferList> signedOfferPersistenceManager;
|
||||||
private final Map<String, PlaceOfferProtocol> placeOfferProtocols = new HashMap<String, PlaceOfferProtocol>();
|
private final Map<String, PlaceOfferProtocol> placeOfferProtocols = new HashMap<String, PlaceOfferProtocol>();
|
||||||
private BigInteger lastUnlockedBalance;
|
|
||||||
private boolean stopped;
|
private boolean stopped;
|
||||||
private Timer periodicRepublishOffersTimer, periodicRefreshOffersTimer, retryRepublishOffersTimer;
|
private Timer periodicRepublishOffersTimer, periodicRefreshOffersTimer, retryRepublishOffersTimer;
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -471,17 +471,13 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
log.warn("Error processing unposted offers: " + errorMessage);
|
log.warn("Error processing unposted offers: " + errorMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
// register to process unposted offers when unlocked balance increases
|
// register to process unposted offers on new block
|
||||||
if (xmrWalletService.getWallet() != null) lastUnlockedBalance = xmrWalletService.getAvailableBalance();
|
|
||||||
xmrWalletService.addWalletListener(new MoneroWalletListener() {
|
xmrWalletService.addWalletListener(new MoneroWalletListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) {
|
public void onNewBlock(long height) {
|
||||||
if (lastUnlockedBalance == null || lastUnlockedBalance.compareTo(newUnlockedBalance) < 0) {
|
processScheduledOffers((transaction) -> {}, (errorMessage) -> {
|
||||||
processScheduledOffers((transaction) -> {}, (errorMessage) -> {
|
log.warn("Error processing unposted offers on new block {}: {}", height, errorMessage);
|
||||||
log.warn("Error processing unposted offers on new unlocked balance: " + errorMessage); // TODO: popup to notify user that offer did not post
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
lastUnlockedBalance = newUnlockedBalance;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -860,8 +856,12 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
processUnpostedOffer(openOffers, scheduledOffer, (transaction) -> {
|
processUnpostedOffer(openOffers, scheduledOffer, (transaction) -> {
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}, errorMessage -> {
|
}, errorMessage -> {
|
||||||
log.warn("Error processing unposted offer {}: {}", scheduledOffer.getId(), errorMessage);
|
log.warn("Error processing unposted offer, offerId={}, attempt={}/{}, error={}", scheduledOffer.getId(), scheduledOffer.getNumProcessingAttempts(), MAX_PROCESS_ATTEMPTS, errorMessage);
|
||||||
onCancelled(scheduledOffer);
|
if (scheduledOffer.getNumProcessingAttempts() >= MAX_PROCESS_ATTEMPTS) {
|
||||||
|
log.warn("Offer canceled after {} attempts, offerId={}, error={}", scheduledOffer.getNumProcessingAttempts(), scheduledOffer.getId(), errorMessage);
|
||||||
|
HavenoUtils.havenoSetup.getTopErrorMsg().set("Offer canceled after " + scheduledOffer.getNumProcessingAttempts() + " attempts. Please switch to a better Monero connection and try again.\n\nOffer ID: " + scheduledOffer.getId() + "\nError: " + errorMessage);
|
||||||
|
onCancelled(scheduledOffer);
|
||||||
|
}
|
||||||
errorMessages.add(errorMessage);
|
errorMessages.add(errorMessage);
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
});
|
});
|
||||||
|
@ -875,6 +875,26 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processUnpostedOffer(List<OpenOffer> openOffers, OpenOffer openOffer, TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
private void processUnpostedOffer(List<OpenOffer> openOffers, OpenOffer openOffer, TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
|
||||||
|
// skip if already processing
|
||||||
|
if (openOffer.isProcessing()) {
|
||||||
|
resultHandler.handleResult(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process offer
|
||||||
|
openOffer.setProcessing(true);
|
||||||
|
doProcessUnpostedOffer(openOffers, openOffer, (transaction) -> {
|
||||||
|
openOffer.setProcessing(false);
|
||||||
|
resultHandler.handleResult(transaction);
|
||||||
|
}, (errorMsg) -> {
|
||||||
|
openOffer.setProcessing(false);
|
||||||
|
openOffer.setNumProcessingAttempts(openOffer.getNumProcessingAttempts() + 1);
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMsg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doProcessUnpostedOffer(List<OpenOffer> openOffers, OpenOffer openOffer, TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ import haveno.network.p2p.P2PService;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import monero.wallet.model.MoneroTxWallet;
|
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
@ -71,8 +70,6 @@ public class PlaceOfferModel implements Model {
|
||||||
@Setter
|
@Setter
|
||||||
private Transaction transaction;
|
private Transaction transaction;
|
||||||
@Setter
|
@Setter
|
||||||
private MoneroTxWallet reserveTx;
|
|
||||||
@Setter
|
|
||||||
private SignOfferResponse signOfferResponse;
|
private SignOfferResponse signOfferResponse;
|
||||||
@Setter
|
@Setter
|
||||||
@Getter
|
@Getter
|
||||||
|
|
|
@ -51,6 +51,13 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
|
// skip if reserve tx already created
|
||||||
|
if (openOffer.getReserveTxHash() != null && !openOffer.getReserveTxHash().isEmpty()) {
|
||||||
|
log.info("Reserve tx already created for offerId={}", openOffer.getShortId());
|
||||||
|
complete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// verify monero connection
|
// verify monero connection
|
||||||
model.getXmrWalletService().getConnectionService().verifyConnection();
|
model.getXmrWalletService().getConnectionService().verifyConnection();
|
||||||
|
|
||||||
|
@ -102,7 +109,6 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
||||||
openOffer.setReserveTxHex(reserveTx.getFullHex());
|
openOffer.setReserveTxHex(reserveTx.getFullHex());
|
||||||
openOffer.setReserveTxKey(reserveTx.getKey());
|
openOffer.setReserveTxKey(reserveTx.getKey());
|
||||||
offer.getOfferPayload().setReserveTxKeyImages(reservedKeyImages);
|
offer.getOfferPayload().setReserveTxKeyImages(reservedKeyImages);
|
||||||
model.setReserveTx(reserveTx);
|
|
||||||
}
|
}
|
||||||
complete();
|
complete();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import haveno.common.handlers.ResultHandler;
|
||||||
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.OpenOffer;
|
||||||
import haveno.core.offer.availability.DisputeAgentSelection;
|
import haveno.core.offer.availability.DisputeAgentSelection;
|
||||||
import haveno.core.offer.messages.SignOfferRequest;
|
import haveno.core.offer.messages.SignOfferRequest;
|
||||||
import haveno.core.offer.placeoffer.PlaceOfferModel;
|
import haveno.core.offer.placeoffer.PlaceOfferModel;
|
||||||
|
@ -47,8 +48,6 @@ import java.util.UUID;
|
||||||
public class MakerSendSignOfferRequest extends Task<PlaceOfferModel> {
|
public class MakerSendSignOfferRequest extends Task<PlaceOfferModel> {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MakerSendSignOfferRequest.class);
|
private static final Logger log = LoggerFactory.getLogger(MakerSendSignOfferRequest.class);
|
||||||
|
|
||||||
private boolean failed = false;
|
|
||||||
|
|
||||||
@SuppressWarnings({"unused"})
|
@SuppressWarnings({"unused"})
|
||||||
public MakerSendSignOfferRequest(TaskRunner taskHandler, PlaceOfferModel model) {
|
public MakerSendSignOfferRequest(TaskRunner taskHandler, PlaceOfferModel model) {
|
||||||
super(taskHandler, model);
|
super(taskHandler, model);
|
||||||
|
@ -56,7 +55,8 @@ public class MakerSendSignOfferRequest extends Task<PlaceOfferModel> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() {
|
protected void run() {
|
||||||
Offer offer = model.getOpenOffer().getOffer();
|
OpenOffer openOffer = model.getOpenOffer();
|
||||||
|
Offer offer = openOffer.getOffer();
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
|
@ -71,9 +71,9 @@ public class MakerSendSignOfferRequest extends Task<PlaceOfferModel> {
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
Version.getP2PMessageVersion(),
|
Version.getP2PMessageVersion(),
|
||||||
new Date().getTime(),
|
new Date().getTime(),
|
||||||
model.getReserveTx().getHash(),
|
openOffer.getReserveTxHash(),
|
||||||
model.getReserveTx().getFullHex(),
|
openOffer.getReserveTxHex(),
|
||||||
model.getReserveTx().getKey(),
|
openOffer.getReserveTxKey(),
|
||||||
offer.getOfferPayload().getReserveTxKeyImages(),
|
offer.getOfferPayload().getReserveTxKeyImages(),
|
||||||
returnAddress);
|
returnAddress);
|
||||||
|
|
||||||
|
@ -81,8 +81,7 @@ public class MakerSendSignOfferRequest extends Task<PlaceOfferModel> {
|
||||||
sendSignOfferRequests(request, () -> {
|
sendSignOfferRequests(request, () -> {
|
||||||
complete();
|
complete();
|
||||||
}, (errorMessage) -> {
|
}, (errorMessage) -> {
|
||||||
appendToErrorMessage("Error signing offer " + request.getOfferId() + ": " + errorMessage);
|
failed("Error signing offer " + request.getOfferId() + ": " + errorMessage);
|
||||||
failed(errorMessage);
|
|
||||||
});
|
});
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
offer.setErrorMessage("An error occurred.\n" +
|
offer.setErrorMessage("An error occurred.\n" +
|
||||||
|
|
Loading…
Reference in a new issue