fix invalid signature when sign offer re-requested after timeout

This commit is contained in:
woodser 2024-06-06 09:33:38 -04:00
parent e12ec197bf
commit 88290c9dff
5 changed files with 63 additions and 50 deletions

View file

@ -230,9 +230,12 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
offerBookService.addOfferBookChangedListener(new OfferBookChangedListener() { offerBookService.addOfferBookChangedListener(new OfferBookChangedListener() {
@Override @Override
public void onAdded(Offer offer) { public void onAdded(Offer offer) {
// cancel offer if reserved funds spent
Optional<OpenOffer> openOfferOptional = getOpenOfferById(offer.getId()); Optional<OpenOffer> openOfferOptional = getOpenOfferById(offer.getId());
if (openOfferOptional.isPresent() && openOfferOptional.get().getState() != OpenOffer.State.RESERVED && offer.isReservedFundsSpent()) { if (openOfferOptional.isPresent() && openOfferOptional.get().getState() != OpenOffer.State.RESERVED && offer.isReservedFundsSpent()) {
closeOpenOffer(offer); log.warn("Canceling open offer because reserved funds have been spent, offerId={}, state={}", offer.getId(), openOfferOptional.get().getState());
cancelOpenOffer(openOfferOptional.get(), null, null);
} }
} }
@Override @Override
@ -552,7 +555,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
if (openOffer.isCanceled()) latch.countDown(); if (openOffer.isCanceled()) latch.countDown();
else { else {
log.warn("Error processing unposted offer {}: {}", openOffer.getId(), errorMessage); log.warn("Error processing unposted offer {}: {}", openOffer.getId(), errorMessage);
doCancel(openOffer); doCancelOffer(openOffer);
offer.setErrorMessage(errorMessage); offer.setErrorMessage(errorMessage);
latch.countDown(); latch.countDown();
errorMessageHandler.handleErrorMessage(errorMessage); errorMessageHandler.handleErrorMessage(errorMessage);
@ -622,19 +625,19 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
offerBookService.removeOffer(openOffer.getOffer().getOfferPayload(), offerBookService.removeOffer(openOffer.getOffer().getOfferPayload(),
() -> { () -> {
ThreadUtils.submitToPool(() -> { // TODO: this runs off thread and then shows popup when done. should show overlay spinner until done ThreadUtils.submitToPool(() -> { // TODO: this runs off thread and then shows popup when done. should show overlay spinner until done
doCancel(openOffer); doCancelOffer(openOffer);
resultHandler.handleResult(); if (resultHandler != null) resultHandler.handleResult();
}); });
}, },
errorMessageHandler); errorMessageHandler);
} else { } else {
ThreadUtils.submitToPool(() -> { ThreadUtils.submitToPool(() -> {
doCancel(openOffer); doCancelOffer(openOffer);
resultHandler.handleResult(); if (resultHandler != null) resultHandler.handleResult();
}); });
} }
} else { } else {
errorMessageHandler.handleErrorMessage("You can't remove an offer that is currently edited."); if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage("You can't remove an offer that is currently edited.");
} }
} }
@ -708,7 +711,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
} }
// remove open offer which thaws its key images // remove open offer which thaws its key images
private void doCancel(@NotNull OpenOffer openOffer) { private void doCancelOffer(@NotNull OpenOffer openOffer) {
Offer offer = openOffer.getOffer(); Offer offer = openOffer.getOffer();
offer.setState(Offer.State.REMOVED); offer.setState(Offer.State.REMOVED);
openOffer.setState(OpenOffer.State.CANCELED); openOffer.setState(OpenOffer.State.CANCELED);
@ -874,7 +877,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
if (scheduledOffer.getNumProcessingAttempts() >= MAX_PROCESS_ATTEMPTS) { if (scheduledOffer.getNumProcessingAttempts() >= MAX_PROCESS_ATTEMPTS) {
log.warn("Offer canceled after {} attempts, offerId={}, error={}", scheduledOffer.getNumProcessingAttempts(), scheduledOffer.getId(), errorMessage); 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); 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);
doCancel(scheduledOffer); doCancelOffer(scheduledOffer);
} }
errorMessages.add(errorMessage); errorMessages.add(errorMessage);
} }
@ -1754,7 +1757,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
} else { } else {
// cancel and recreate offer // cancel and recreate offer
doCancel(openOffer); doCancelOffer(openOffer);
Offer updatedOffer = new Offer(openOffer.getOffer().getOfferPayload()); Offer updatedOffer = new Offer(openOffer.getOffer().getOfferPayload());
updatedOffer.setPriceFeedService(priceFeedService); updatedOffer.setPriceFeedService(priceFeedService);
OpenOffer updatedOpenOffer = new OpenOffer(updatedOffer, openOffer.getTriggerPrice()); OpenOffer updatedOpenOffer = new OpenOffer(updatedOffer, openOffer.getTriggerPrice());
@ -1770,7 +1773,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
}, (errorMessage) -> { }, (errorMessage) -> {
if (!updatedOpenOffer.isCanceled()) { if (!updatedOpenOffer.isCanceled()) {
log.warn("Error reposting offer {}: {}", updatedOpenOffer.getId(), errorMessage); log.warn("Error reposting offer {}: {}", updatedOpenOffer.getId(), errorMessage);
doCancel(updatedOpenOffer); doCancelOffer(updatedOpenOffer);
updatedOffer.setErrorMessage(errorMessage); updatedOffer.setErrorMessage(errorMessage);
} }
latch.countDown(); latch.countDown();

View file

@ -94,11 +94,18 @@ public class PlaceOfferProtocol {
log.debug("handleSignOfferResponse() " + model.getOpenOffer().getOffer().getId()); log.debug("handleSignOfferResponse() " + model.getOpenOffer().getOffer().getId());
model.setSignOfferResponse(response); model.setSignOfferResponse(response);
// ignore if unexpected signer
if (!model.getOpenOffer().getOffer().getOfferPayload().getArbitratorSigner().equals(sender)) { if (!model.getOpenOffer().getOffer().getOfferPayload().getArbitratorSigner().equals(sender)) {
log.warn("Ignoring sign offer response from different sender"); log.warn("Ignoring sign offer response from different sender");
return; return;
} }
// ignore if payloads have different timestamps
if (model.getOpenOffer().getOffer().getOfferPayload().getDate() != response.getSignedOfferPayload().getDate()) {
log.warn("Ignoring sign offer response from arbitrator for offer payload with different timestamp");
return;
}
// ignore if timer already stopped // ignore if timer already stopped
if (timeoutTimer == null) { if (timeoutTimer == null) {
log.warn("Ignoring sign offer response from arbitrator because timeout has expired for offer " + model.getOpenOffer().getOffer().getId()); log.warn("Ignoring sign offer response from arbitrator because timeout has expired for offer " + model.getOpenOffer().getOffer().getId());

View file

@ -42,11 +42,14 @@ public class MakerProcessSignOfferResponse extends Task<PlaceOfferModel> {
// validate arbitrator signature // validate arbitrator signature
if (!HavenoUtils.isArbitratorSignatureValid(model.getSignOfferResponse().getSignedOfferPayload(), arbitrator)) { if (!HavenoUtils.isArbitratorSignatureValid(model.getSignOfferResponse().getSignedOfferPayload(), arbitrator)) {
throw new RuntimeException("Offer payload has invalid arbitrator signature"); throw new RuntimeException("Arbitrator's offer payload has invalid signature, offerId=" + offer.getId());
} }
// set arbitrator signature for maker's offer // set arbitrator signature for maker's offer
offer.getOfferPayload().setArbitratorSignature(model.getSignOfferResponse().getSignedOfferPayload().getArbitratorSignature()); offer.getOfferPayload().setArbitratorSignature(model.getSignOfferResponse().getSignedOfferPayload().getArbitratorSignature());
if (!HavenoUtils.isArbitratorSignatureValid(offer.getOfferPayload(), arbitrator)) {
throw new RuntimeException("Maker's offer payload has invalid signature, offerId=" + offer.getId());
}
offer.setState(Offer.State.AVAILABLE); offer.setState(Offer.State.AVAILABLE);
complete(); complete();
} catch (Exception e) { } catch (Exception e) {

View file

@ -364,7 +364,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
trade.onShutDownStarted(); trade.onShutDownStarted();
} catch (Exception e) { } catch (Exception e) {
if (e.getMessage() != null && e.getMessage().contains("Connection reset")) return; // expected if shut down with ctrl+c if (e.getMessage() != null && e.getMessage().contains("Connection reset")) return; // expected if shut down with ctrl+c
log.warn("Error notifying {} {} that shut down started {}", getClass().getSimpleName(), trade.getId()); log.warn("Error notifying {} {} that shut down started {}", trade.getClass().getSimpleName(), trade.getId());
e.printStackTrace(); e.printStackTrace();
} }
}); });

View file

@ -221,7 +221,7 @@ public class ArbitratorProcessDepositRequest extends TradeTask {
processModel.getP2PService().sendEncryptedDirectMessage(nodeAddress, pubKeyRing, response, new SendDirectMessageListener() { processModel.getP2PService().sendEncryptedDirectMessage(nodeAddress, pubKeyRing, response, new SendDirectMessageListener() {
@Override @Override
public void onArrived() { public void onArrived() {
log.info("{} arrived: trading peer={}; offerId={}; uid={}", response.getClass().getSimpleName(), nodeAddress, trade.getId()); log.info("{} arrived: trading peer={}; offerId={}; uid={}", response.getClass().getSimpleName(), nodeAddress, trade.getId(), trade.getUid());
} }
@Override @Override
public void onFault(String errorMessage) { public void onFault(String errorMessage) {