re-sign offers on edit if applicable
This commit is contained in:
parent
93369c4211
commit
dc43e1c329
6 changed files with 153 additions and 133 deletions
|
@ -135,7 +135,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 NUM_ATTEMPTS_THRESHOLD = 5; // process pending offer only on republish cycle after this many attempts
|
private static final int NUM_ATTEMPTS_THRESHOLD = 5; // process offer only on republish cycle after this many attempts
|
||||||
|
|
||||||
private final CoreContext coreContext;
|
private final CoreContext coreContext;
|
||||||
private final KeyRing keyRing;
|
private final KeyRing keyRing;
|
||||||
|
@ -475,19 +475,19 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
// .forEach(openOffer -> OfferUtil.getInvalidMakerFeeTxErrorMessage(openOffer.getOffer(), btcWalletService)
|
// .forEach(openOffer -> OfferUtil.getInvalidMakerFeeTxErrorMessage(openOffer.getOffer(), btcWalletService)
|
||||||
// .ifPresent(errorMsg -> invalidOffers.add(new Tuple2<>(openOffer, errorMsg))));
|
// .ifPresent(errorMsg -> invalidOffers.add(new Tuple2<>(openOffer, errorMsg))));
|
||||||
|
|
||||||
// process pending offers
|
// processs offers
|
||||||
processPendingOffers(false, (transaction) -> {}, (errorMessage) -> {
|
processOffers(false, (transaction) -> {}, (errorMessage) -> {
|
||||||
log.warn("Error processing pending offers on bootstrap: " + errorMessage);
|
log.warn("Error processing offers on bootstrap: " + errorMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
// register to process pending offers on new block
|
// register to process offers on new block
|
||||||
xmrWalletService.addWalletListener(new MoneroWalletListener() {
|
xmrWalletService.addWalletListener(new MoneroWalletListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onNewBlock(long height) {
|
public void onNewBlock(long height) {
|
||||||
|
|
||||||
// process each pending offer on new block a few times, then rely on period republish
|
// process each offer on new block a few times, then rely on period republish
|
||||||
processPendingOffers(true, (transaction) -> {}, (errorMessage) -> {
|
processOffers(true, (transaction) -> {}, (errorMessage) -> {
|
||||||
log.warn("Error processing pending offers on new block {}: {}", height, errorMessage);
|
log.warn("Error processing offers on new block {}: {}", height, errorMessage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -555,13 +555,13 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
synchronized (processOffersLock) {
|
synchronized (processOffersLock) {
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
addOpenOffer(openOffer);
|
addOpenOffer(openOffer);
|
||||||
processPendingOffer(getOpenOffers(), openOffer, (transaction) -> {
|
processOffer(getOpenOffers(), openOffer, (transaction) -> {
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
resultHandler.handleResult(transaction);
|
resultHandler.handleResult(transaction);
|
||||||
}, (errorMessage) -> {
|
}, (errorMessage) -> {
|
||||||
if (!openOffer.isCanceled()) {
|
if (!openOffer.isCanceled()) {
|
||||||
log.warn("Error processing pending offer {}: {}", openOffer.getId(), errorMessage);
|
log.warn("Error processing offer {}: {}", openOffer.getId(), errorMessage);
|
||||||
doCancelOffer(openOffer, resetAddressEntriesOnError);
|
doCancelOffer(openOffer, resetAddressEntriesOnError);
|
||||||
}
|
}
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
|
@ -578,8 +578,9 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
if (openOfferOptional.isPresent()) {
|
if (openOfferOptional.isPresent()) {
|
||||||
cancelOpenOffer(openOfferOptional.get(), resultHandler, errorMessageHandler);
|
cancelOpenOffer(openOfferOptional.get(), resultHandler, errorMessageHandler);
|
||||||
} else {
|
} else {
|
||||||
log.warn("Offer was not found in our list of open offers. We still try to remove it from the offerbook.");
|
String errorMsg = "Offer was not found in our list of open offers. We still try to remove it from the offerbook.";
|
||||||
errorMessageHandler.handleErrorMessage("Offer was not found in our list of open offers. " + "We still try to remove it from the offerbook.");
|
log.warn(errorMsg);
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMsg);
|
||||||
offerBookService.removeOffer(offer.getOfferPayload(), () -> offer.setState(Offer.State.REMOVED), null);
|
offerBookService.removeOffer(offer.getOfferPayload(), () -> offer.setState(Offer.State.REMOVED), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -706,12 +707,21 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
|
|
||||||
addOpenOffer(editedOpenOffer);
|
addOpenOffer(editedOpenOffer);
|
||||||
|
|
||||||
if (editedOpenOffer.isAvailable())
|
// reset arbitrator signature if invalid
|
||||||
maybeRepublishOffer(editedOpenOffer);
|
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(editedOpenOffer.getOffer().getOfferPayload().getArbitratorSigner());
|
||||||
|
if (arbitrator == null || !HavenoUtils.isArbitratorSignatureValid(editedOpenOffer.getOffer().getOfferPayload(), arbitrator)) {
|
||||||
|
editedOpenOffer.getOffer().getOfferPayload().setArbitratorSignature(null);
|
||||||
|
editedOpenOffer.getOffer().getOfferPayload().setArbitratorSigner(null);
|
||||||
|
}
|
||||||
|
|
||||||
offersToBeEdited.remove(openOffer.getId());
|
// process offer which might sign and publish
|
||||||
requestPersistence();
|
processOffer(getOpenOffers(), editedOpenOffer, (transaction) -> {
|
||||||
resultHandler.handleResult();
|
offersToBeEdited.remove(openOffer.getId());
|
||||||
|
requestPersistence();
|
||||||
|
resultHandler.handleResult();
|
||||||
|
}, (errorMsg) -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMsg);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
errorMessageHandler.handleErrorMessage("There is no offer with this id existing to be published.");
|
errorMessageHandler.handleErrorMessage("There is no offer with this id existing to be published.");
|
||||||
}
|
}
|
||||||
|
@ -728,6 +738,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
} else {
|
} else {
|
||||||
resultHandler.handleResult();
|
resultHandler.handleResult();
|
||||||
}
|
}
|
||||||
|
requestPersistence();
|
||||||
} else {
|
} else {
|
||||||
errorMessageHandler.handleErrorMessage("Editing of offer can't be canceled as it is not edited.");
|
errorMessageHandler.handleErrorMessage("Editing of offer can't be canceled as it is not edited.");
|
||||||
}
|
}
|
||||||
|
@ -882,7 +893,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Place offer helpers
|
// Place offer helpers
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
private void processPendingOffers(boolean skipOffersWithTooManyAttempts,
|
private void processOffers(boolean skipOffersWithTooManyAttempts,
|
||||||
TransactionResultHandler resultHandler, // TODO (woodser): transaction not needed with result handler
|
TransactionResultHandler resultHandler, // TODO (woodser): transaction not needed with result handler
|
||||||
ErrorMessageHandler errorMessageHandler) {
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
|
@ -890,23 +901,13 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
synchronized (processOffersLock) {
|
synchronized (processOffersLock) {
|
||||||
List<OpenOffer> openOffers = getOpenOffers();
|
List<OpenOffer> openOffers = getOpenOffers();
|
||||||
removeOffersWithDuplicateKeyImages(openOffers);
|
removeOffersWithDuplicateKeyImages(openOffers);
|
||||||
for (OpenOffer pendingOffer : openOffers) {
|
for (OpenOffer offer : openOffers) {
|
||||||
if (pendingOffer.getState() != OpenOffer.State.PENDING) continue;
|
if (skipOffersWithTooManyAttempts && offer.getNumProcessingAttempts() > NUM_ATTEMPTS_THRESHOLD) continue; // skip offers with too many attempts
|
||||||
if (skipOffersWithTooManyAttempts && pendingOffer.getNumProcessingAttempts() > NUM_ATTEMPTS_THRESHOLD) continue; // skip offers with too many attempts
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
processPendingOffer(openOffers, pendingOffer, (transaction) -> {
|
processOffer(openOffers, offer, (transaction) -> {
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}, errorMessage -> {
|
}, errorMessage -> {
|
||||||
if (!pendingOffer.isCanceled()) {
|
errorMessages.add(errorMessage);
|
||||||
String warnMessage = "Error processing pending offer, offerId=" + pendingOffer.getId() + ", attempt=" + pendingOffer.getNumProcessingAttempts() + ": " + errorMessage;
|
|
||||||
errorMessages.add(warnMessage);
|
|
||||||
|
|
||||||
// cancel offer if invalid
|
|
||||||
if (pendingOffer.getOffer().getState() == Offer.State.INVALID) {
|
|
||||||
log.warn("Canceling offer because it's invalid: {}", pendingOffer.getId());
|
|
||||||
doCancelOffer(pendingOffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
});
|
});
|
||||||
HavenoUtils.awaitLatch(latch);
|
HavenoUtils.awaitLatch(latch);
|
||||||
|
@ -943,7 +944,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processPendingOffer(List<OpenOffer> openOffers, OpenOffer openOffer, TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
private void processOffer(List<OpenOffer> openOffers, OpenOffer openOffer, TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
|
||||||
// skip if already processing
|
// skip if already processing
|
||||||
if (openOffer.isProcessing()) {
|
if (openOffer.isProcessing()) {
|
||||||
|
@ -953,23 +954,33 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
|
|
||||||
// process offer
|
// process offer
|
||||||
openOffer.setProcessing(true);
|
openOffer.setProcessing(true);
|
||||||
doProcessPendingOffer(openOffers, openOffer, (transaction) -> {
|
doProcessOffer(openOffers, openOffer, (transaction) -> {
|
||||||
openOffer.setProcessing(false);
|
openOffer.setProcessing(false);
|
||||||
resultHandler.handleResult(transaction);
|
resultHandler.handleResult(transaction);
|
||||||
}, (errorMsg) -> {
|
}, (errorMsg) -> {
|
||||||
openOffer.setProcessing(false);
|
openOffer.setProcessing(false);
|
||||||
openOffer.setNumProcessingAttempts(openOffer.getNumProcessingAttempts() + 1);
|
openOffer.setNumProcessingAttempts(openOffer.getNumProcessingAttempts() + 1);
|
||||||
openOffer.getOffer().setErrorMessage(errorMsg);
|
openOffer.getOffer().setErrorMessage(errorMsg);
|
||||||
|
if (!openOffer.isCanceled()) {
|
||||||
|
errorMsg = "Error processing offer, offerId=" + openOffer.getId() + ", attempt=" + openOffer.getNumProcessingAttempts() + ": " + errorMsg;
|
||||||
|
openOffer.getOffer().setErrorMessage(errorMsg);
|
||||||
|
|
||||||
|
// cancel offer if invalid
|
||||||
|
if (openOffer.getOffer().getState() == Offer.State.INVALID) {
|
||||||
|
log.warn("Canceling offer because it's invalid: {}", openOffer.getId());
|
||||||
|
doCancelOffer(openOffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
errorMessageHandler.handleErrorMessage(errorMsg);
|
errorMessageHandler.handleErrorMessage(errorMsg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doProcessPendingOffer(List<OpenOffer> openOffers, OpenOffer openOffer, TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
private void doProcessOffer(List<OpenOffer> openOffers, OpenOffer openOffer, TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// done processing if wallet not initialized
|
// done processing if canceled or wallet not initialized
|
||||||
if (xmrWalletService.getWallet() == null) {
|
if (openOffer.isCanceled() || xmrWalletService.getWallet() == null) {
|
||||||
resultHandler.handleResult(null);
|
resultHandler.handleResult(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -982,6 +993,33 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate non-pending state
|
||||||
|
if (!openOffer.isPending()) {
|
||||||
|
boolean isValid = true;
|
||||||
|
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(openOffer.getOffer().getOfferPayload().getArbitratorSigner());
|
||||||
|
if (openOffer.getOffer().getOfferPayload().getArbitratorSigner() == null) {
|
||||||
|
isValid = false;
|
||||||
|
} else if (arbitrator == null) {
|
||||||
|
log.warn("Offer {} signed by unavailable arbitrator, reposting", openOffer.getId());
|
||||||
|
isValid = false;
|
||||||
|
} else if (!HavenoUtils.isArbitratorSignatureValid(openOffer.getOffer().getOfferPayload(), arbitrator)) {
|
||||||
|
log.warn("Offer {} has invalid arbitrator signature, reposting", openOffer.getId());
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
if ((openOffer.getOffer().getOfferPayload().getReserveTxKeyImages() != null || openOffer.getOffer().getOfferPayload().getReserveTxKeyImages().isEmpty()) && (openOffer.getReserveTxHash() == null || openOffer.getReserveTxHash().isEmpty())) {
|
||||||
|
log.warn("Offer {} is missing reserve tx hash but has reserved key images, reposting", openOffer.getId());
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
if (isValid) {
|
||||||
|
resultHandler.handleResult(null);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
openOffer.getOffer().getOfferPayload().setArbitratorSignature(null);
|
||||||
|
openOffer.getOffer().getOfferPayload().setArbitratorSigner(null);
|
||||||
|
if (openOffer.isAvailable()) openOffer.setState(OpenOffer.State.PENDING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// cancel offer if scheduled txs unavailable
|
// cancel offer if scheduled txs unavailable
|
||||||
if (openOffer.getScheduledTxHashes() != null) {
|
if (openOffer.getScheduledTxHashes() != null) {
|
||||||
boolean scheduledTxsAvailable = true;
|
boolean scheduledTxsAvailable = true;
|
||||||
|
@ -999,6 +1037,12 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sign and post offer if already funded
|
||||||
|
if (openOffer.getReserveTxHash() != null) {
|
||||||
|
signAndPostOffer(openOffer, false, resultHandler, errorMessageHandler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// get amount needed to reserve offer
|
// get amount needed to reserve offer
|
||||||
BigInteger amountNeeded = openOffer.getOffer().getAmountNeeded();
|
BigInteger amountNeeded = openOffer.getOffer().getAmountNeeded();
|
||||||
|
|
||||||
|
@ -1020,13 +1064,11 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
} else {
|
} else {
|
||||||
splitOrSchedule(splitOutputTx, openOffers, openOffer, amountNeeded, resultHandler, errorMessageHandler);
|
splitOrSchedule(splitOutputTx, openOffers, openOffer, amountNeeded, resultHandler, errorMessageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// sign and post offer if enough funds
|
// sign and post offer if enough funds
|
||||||
boolean hasFundsReserved = openOffer.getReserveTxHash() != null;
|
|
||||||
boolean hasSufficientBalance = xmrWalletService.getAvailableBalance().compareTo(amountNeeded) >= 0;
|
boolean hasSufficientBalance = xmrWalletService.getAvailableBalance().compareTo(amountNeeded) >= 0;
|
||||||
if (hasFundsReserved || hasSufficientBalance) {
|
if (hasSufficientBalance) {
|
||||||
signAndPostOffer(openOffer, true, resultHandler, errorMessageHandler);
|
signAndPostOffer(openOffer, true, resultHandler, errorMessageHandler);
|
||||||
return;
|
return;
|
||||||
} else if (openOffer.getScheduledTxHashes() == null) {
|
} else if (openOffer.getScheduledTxHashes() == null) {
|
||||||
|
@ -1036,7 +1078,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (!openOffer.isCanceled()) log.error("Error processing pending offer: {}\n", e.getMessage(), e);
|
if (!openOffer.isCanceled()) log.error("Error processing offer: {}\n", e.getMessage(), e);
|
||||||
errorMessageHandler.handleErrorMessage(e.getMessage());
|
errorMessageHandler.handleErrorMessage(e.getMessage());
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
|
@ -1335,7 +1377,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
transaction -> {
|
transaction -> {
|
||||||
|
|
||||||
// set offer state
|
// set offer state
|
||||||
openOffer.setState(OpenOffer.State.AVAILABLE);
|
|
||||||
openOffer.setScheduledTxHashes(null);
|
openOffer.setScheduledTxHashes(null);
|
||||||
openOffer.setScheduledAmount(null);
|
openOffer.setScheduledAmount(null);
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
|
@ -1949,10 +1990,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeRepublishOffer(OpenOffer openOffer) {
|
|
||||||
maybeRepublishOffer(openOffer, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maybeRepublishOffer(OpenOffer openOffer, @Nullable Runnable completeHandler) {
|
private void maybeRepublishOffer(OpenOffer openOffer, @Nullable Runnable completeHandler) {
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
|
|
||||||
|
@ -1962,76 +1999,48 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine if offer is valid
|
// reprocess offer then publish
|
||||||
boolean isValid = true;
|
synchronized (processOffersLock) {
|
||||||
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(openOffer.getOffer().getOfferPayload().getArbitratorSigner());
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
if (arbitrator == null) {
|
processOffer(getOpenOffers(), openOffer, (transaction) -> {
|
||||||
log.warn("Offer {} signed by unavailable arbitrator, reposting", openOffer.getId());
|
requestPersistence();
|
||||||
isValid = false;
|
latch.countDown();
|
||||||
} else if (!HavenoUtils.isArbitratorSignatureValid(openOffer.getOffer().getOfferPayload(), arbitrator)) {
|
|
||||||
log.warn("Offer {} has invalid arbitrator signature, reposting", openOffer.getId());
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
if ((openOffer.getOffer().getOfferPayload().getReserveTxKeyImages() != null || openOffer.getOffer().getOfferPayload().getReserveTxKeyImages().isEmpty()) && (openOffer.getReserveTxHash() == null || openOffer.getReserveTxHash().isEmpty())) {
|
|
||||||
log.warn("Offer {} is missing reserve tx hash but has reserved key images, reposting", openOffer.getId());
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if valid, re-add offer to book
|
// skip if prevented from publishing
|
||||||
if (isValid) {
|
if (preventedFromPublishing(openOffer)) {
|
||||||
offerBookService.addOffer(openOffer.getOffer(),
|
|
||||||
() -> {
|
|
||||||
if (!stopped) {
|
|
||||||
|
|
||||||
// refresh means we send only the data needed to refresh the TTL (hash, signature and sequence no.)
|
|
||||||
if (periodicRefreshOffersTimer == null) {
|
|
||||||
startPeriodicRefreshOffersTimer();
|
|
||||||
}
|
|
||||||
if (completeHandler != null) {
|
|
||||||
completeHandler.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
if (!stopped) {
|
|
||||||
log.error("Adding offer to P2P network failed. " + errorMessage);
|
|
||||||
stopRetryRepublishOffersTimer();
|
|
||||||
retryRepublishOffersTimer = UserThread.runAfter(OpenOfferManager.this::republishOffers,
|
|
||||||
RETRY_REPUBLISH_DELAY_SEC);
|
|
||||||
if (completeHandler != null) completeHandler.run();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// reset offer state to pending
|
|
||||||
openOffer.getOffer().getOfferPayload().setArbitratorSignature(null);
|
|
||||||
openOffer.getOffer().getOfferPayload().setArbitratorSigner(null);
|
|
||||||
openOffer.getOffer().setState(Offer.State.UNKNOWN);
|
|
||||||
openOffer.setState(OpenOffer.State.PENDING);
|
|
||||||
|
|
||||||
// republish offer
|
|
||||||
synchronized (processOffersLock) {
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
processPendingOffer(getOpenOffers(), openOffer, (transaction) -> {
|
|
||||||
requestPersistence();
|
|
||||||
latch.countDown();
|
|
||||||
if (completeHandler != null) completeHandler.run();
|
if (completeHandler != null) completeHandler.run();
|
||||||
}, (errorMessage) -> {
|
return;
|
||||||
if (!openOffer.isCanceled()) {
|
}
|
||||||
log.warn("Error republishing offer {}: {}", openOffer.getId(), errorMessage);
|
|
||||||
openOffer.getOffer().setErrorMessage(errorMessage);
|
// publish offer to books
|
||||||
|
offerBookService.addOffer(openOffer.getOffer(),
|
||||||
|
() -> {
|
||||||
|
if (!stopped) {
|
||||||
|
|
||||||
// cancel offer if invalid
|
// refresh means we send only the data needed to refresh the TTL (hash, signature and sequence no.)
|
||||||
if (openOffer.getOffer().getState() == Offer.State.INVALID) {
|
if (periodicRefreshOffersTimer == null) {
|
||||||
log.warn("Canceling offer because it's invalid: {}", openOffer.getId());
|
startPeriodicRefreshOffersTimer();
|
||||||
doCancelOffer(openOffer);
|
}
|
||||||
}
|
if (completeHandler != null) {
|
||||||
}
|
completeHandler.run();
|
||||||
latch.countDown();
|
}
|
||||||
if (completeHandler != null) completeHandler.run();
|
}
|
||||||
});
|
},
|
||||||
HavenoUtils.awaitLatch(latch);
|
errorMessage -> {
|
||||||
}
|
if (!stopped) {
|
||||||
|
log.error("Adding offer to P2P network failed. " + errorMessage);
|
||||||
|
stopRetryRepublishOffersTimer();
|
||||||
|
retryRepublishOffersTimer = UserThread.runAfter(OpenOfferManager.this::republishOffers,
|
||||||
|
RETRY_REPUBLISH_DELAY_SEC);
|
||||||
|
if (completeHandler != null) completeHandler.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, (errorMessage) -> {
|
||||||
|
log.warn("Error republishing offer {}: {}", openOffer.getId(), errorMessage);
|
||||||
|
latch.countDown();
|
||||||
|
if (completeHandler != null) completeHandler.run();
|
||||||
|
});
|
||||||
|
HavenoUtils.awaitLatch(latch);
|
||||||
}
|
}
|
||||||
}, THREAD_ID);
|
}, THREAD_ID);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import haveno.common.handlers.ErrorMessageHandler;
|
||||||
import haveno.common.taskrunner.TaskRunner;
|
import haveno.common.taskrunner.TaskRunner;
|
||||||
import haveno.core.locale.Res;
|
import haveno.core.locale.Res;
|
||||||
import haveno.core.offer.messages.SignOfferResponse;
|
import haveno.core.offer.messages.SignOfferResponse;
|
||||||
import haveno.core.offer.placeoffer.tasks.AddToOfferBook;
|
import haveno.core.offer.placeoffer.tasks.MaybeAddToOfferBook;
|
||||||
import haveno.core.offer.placeoffer.tasks.MakerProcessSignOfferResponse;
|
import haveno.core.offer.placeoffer.tasks.MakerProcessSignOfferResponse;
|
||||||
import haveno.core.offer.placeoffer.tasks.MakerReserveOfferFunds;
|
import haveno.core.offer.placeoffer.tasks.MakerReserveOfferFunds;
|
||||||
import haveno.core.offer.placeoffer.tasks.MakerSendSignOfferRequest;
|
import haveno.core.offer.placeoffer.tasks.MakerSendSignOfferRequest;
|
||||||
|
@ -135,7 +135,7 @@ public class PlaceOfferProtocol {
|
||||||
);
|
);
|
||||||
taskRunner.addTasks(
|
taskRunner.addTasks(
|
||||||
MakerProcessSignOfferResponse.class,
|
MakerProcessSignOfferResponse.class,
|
||||||
AddToOfferBook.class
|
MaybeAddToOfferBook.class
|
||||||
);
|
);
|
||||||
|
|
||||||
taskRunner.run();
|
taskRunner.run();
|
||||||
|
|
|
@ -20,13 +20,14 @@ 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.OpenOffer;
|
||||||
import haveno.core.offer.placeoffer.PlaceOfferModel;
|
import haveno.core.offer.placeoffer.PlaceOfferModel;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
public class AddToOfferBook extends Task<PlaceOfferModel> {
|
public class MaybeAddToOfferBook extends Task<PlaceOfferModel> {
|
||||||
|
|
||||||
public AddToOfferBook(TaskRunner<PlaceOfferModel> taskHandler, PlaceOfferModel model) {
|
public MaybeAddToOfferBook(TaskRunner<PlaceOfferModel> taskHandler, PlaceOfferModel model) {
|
||||||
super(taskHandler, model);
|
super(taskHandler, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,17 +36,22 @@ public class AddToOfferBook extends Task<PlaceOfferModel> {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
checkNotNull(model.getSignOfferResponse().getSignedOfferPayload().getArbitratorSignature(), "Offer's arbitrator signature is null: " + model.getOpenOffer().getOffer().getId());
|
checkNotNull(model.getSignOfferResponse().getSignedOfferPayload().getArbitratorSignature(), "Offer's arbitrator signature is null: " + model.getOpenOffer().getOffer().getId());
|
||||||
model.getOfferBookService().addOffer(new Offer(model.getSignOfferResponse().getSignedOfferPayload()),
|
if (model.getOpenOffer().isPending() || model.getOpenOffer().isAvailable()) {
|
||||||
() -> {
|
model.getOfferBookService().addOffer(new Offer(model.getSignOfferResponse().getSignedOfferPayload()),
|
||||||
model.setOfferAddedToOfferBook(true);
|
() -> {
|
||||||
complete();
|
model.getOpenOffer().setState(OpenOffer.State.AVAILABLE);
|
||||||
},
|
model.setOfferAddedToOfferBook(true);
|
||||||
errorMessage -> {
|
complete();
|
||||||
model.getOpenOffer().getOffer().setErrorMessage("Could not add offer to offerbook.\n" +
|
},
|
||||||
"Please check your network connection and try again.");
|
errorMessage -> {
|
||||||
|
model.getOpenOffer().getOffer().setErrorMessage("Could not add offer to offerbook.\n" +
|
||||||
failed(errorMessage);
|
"Please check your network connection and try again.");
|
||||||
});
|
failed(errorMessage);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
complete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
model.getOpenOffer().getOffer().setErrorMessage("An error occurred.\n" +
|
model.getOpenOffer().getOffer().setErrorMessage("An error occurred.\n" +
|
||||||
"Error message:\n"
|
"Error message:\n"
|
|
@ -22,7 +22,7 @@ import haveno.common.taskrunner.Task;
|
||||||
import haveno.common.util.Tuple2;
|
import haveno.common.util.Tuple2;
|
||||||
import haveno.core.offer.availability.tasks.ProcessOfferAvailabilityResponse;
|
import haveno.core.offer.availability.tasks.ProcessOfferAvailabilityResponse;
|
||||||
import haveno.core.offer.availability.tasks.SendOfferAvailabilityRequest;
|
import haveno.core.offer.availability.tasks.SendOfferAvailabilityRequest;
|
||||||
import haveno.core.offer.placeoffer.tasks.AddToOfferBook;
|
import haveno.core.offer.placeoffer.tasks.MaybeAddToOfferBook;
|
||||||
import haveno.core.offer.placeoffer.tasks.MakerReserveOfferFunds;
|
import haveno.core.offer.placeoffer.tasks.MakerReserveOfferFunds;
|
||||||
import haveno.core.offer.placeoffer.tasks.ValidateOffer;
|
import haveno.core.offer.placeoffer.tasks.ValidateOffer;
|
||||||
import haveno.core.trade.protocol.tasks.ApplyFilter;
|
import haveno.core.trade.protocol.tasks.ApplyFilter;
|
||||||
|
@ -72,7 +72,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
||||||
FXCollections.observableArrayList(Arrays.asList(
|
FXCollections.observableArrayList(Arrays.asList(
|
||||||
ValidateOffer.class,
|
ValidateOffer.class,
|
||||||
MakerReserveOfferFunds.class,
|
MakerReserveOfferFunds.class,
|
||||||
AddToOfferBook.class)
|
MaybeAddToOfferBook.class)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ package haveno.desktop.main.portfolio.editoffer;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.name.Named;
|
import com.google.inject.name.Named;
|
||||||
|
|
||||||
|
import haveno.common.UserThread;
|
||||||
import haveno.common.handlers.ErrorMessageHandler;
|
import haveno.common.handlers.ErrorMessageHandler;
|
||||||
import haveno.common.handlers.ResultHandler;
|
import haveno.common.handlers.ResultHandler;
|
||||||
import haveno.core.account.witness.AccountAgeWitnessService;
|
import haveno.core.account.witness.AccountAgeWitnessService;
|
||||||
|
@ -226,8 +228,10 @@ class EditOfferDataModel extends MutableOfferDataModel {
|
||||||
|
|
||||||
openOfferManager.editOpenOfferPublish(editedOffer, triggerPrice, initialState, () -> {
|
openOfferManager.editOpenOfferPublish(editedOffer, triggerPrice, initialState, () -> {
|
||||||
openOffer = null;
|
openOffer = null;
|
||||||
resultHandler.handleResult();
|
UserThread.execute(() -> resultHandler.handleResult());
|
||||||
}, errorMessageHandler);
|
}, (errorMsg) -> {
|
||||||
|
UserThread.execute(() -> errorMessageHandler.handleErrorMessage(errorMsg));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCancelEditOffer(ErrorMessageHandler errorMessageHandler) {
|
public void onCancelEditOffer(ErrorMessageHandler errorMessageHandler) {
|
||||||
|
|
|
@ -205,7 +205,8 @@ public class EditOfferView extends MutableOfferView<EditOfferViewModel> {
|
||||||
cancelButton.setDisable(true);
|
cancelButton.setDisable(true);
|
||||||
busyAnimation.play();
|
busyAnimation.play();
|
||||||
spinnerInfoLabel.setText(Res.get("editOffer.publishOffer"));
|
spinnerInfoLabel.setText(Res.get("editOffer.publishOffer"));
|
||||||
//edit offer
|
|
||||||
|
// edit offer
|
||||||
model.onPublishOffer(() -> {
|
model.onPublishOffer(() -> {
|
||||||
String key = "editOfferSuccess";
|
String key = "editOfferSuccess";
|
||||||
if (DontShowAgainLookup.showAgain(key)) {
|
if (DontShowAgainLookup.showAgain(key)) {
|
||||||
|
|
Loading…
Reference in a new issue