From e29bff5d901029984eef0cbf62d930de0018a096 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Thu, 20 Mar 2025 08:04:30 -0400 Subject: [PATCH] verify offer versions when signing --- .../main/java/haveno/common/app/Version.java | 19 +++++++++++++ .../haveno/core/app/DomainInitialisation.java | 7 ++--- .../haveno/core/filter/FilterManager.java | 4 +++ .../haveno/core/offer/OpenOfferManager.java | 28 ++++++++++++++++++- .../core/xmr/wallet/XmrWalletService.java | 4 +-- 5 files changed, 55 insertions(+), 7 deletions(-) diff --git a/common/src/main/java/haveno/common/app/Version.java b/common/src/main/java/haveno/common/app/Version.java index 74f3ceb9d6..1d7b99f747 100644 --- a/common/src/main/java/haveno/common/app/Version.java +++ b/common/src/main/java/haveno/common/app/Version.java @@ -72,6 +72,25 @@ public class Version { return false; } + public static int compare(String version1, String version2) { + if (version1.equals(version2)) + return 0; + else if (getMajorVersion(version1) > getMajorVersion(version2)) + return 1; + else if (getMajorVersion(version1) < getMajorVersion(version2)) + return -1; + else if (getMinorVersion(version1) > getMinorVersion(version2)) + return 1; + else if (getMinorVersion(version1) < getMinorVersion(version2)) + return -1; + else if (getPatchVersion(version1) > getPatchVersion(version2)) + return 1; + else if (getPatchVersion(version1) < getPatchVersion(version2)) + return -1; + else + return 0; + } + private static int getSubVersion(String version, int index) { final String[] split = version.split("\\."); checkArgument(split.length == 3, "Version number must be in semantic version format (contain 2 '.'). version=" + version); diff --git a/core/src/main/java/haveno/core/app/DomainInitialisation.java b/core/src/main/java/haveno/core/app/DomainInitialisation.java index 646d80d9dd..220fb05040 100644 --- a/core/src/main/java/haveno/core/app/DomainInitialisation.java +++ b/core/src/main/java/haveno/core/app/DomainInitialisation.java @@ -178,6 +178,9 @@ public class DomainInitialisation { closedTradableManager.onAllServicesInitialized(); failedTradesManager.onAllServicesInitialized(); + filterManager.setFilterWarningHandler(filterWarningHandler); + filterManager.onAllServicesInitialized(); + openOfferManager.onAllServicesInitialized(); balances.onAllServicesInitialized(); @@ -199,10 +202,6 @@ public class DomainInitialisation { priceFeedService.setCurrencyCodeOnInit(); priceFeedService.startRequestingPrices(); - filterManager.setFilterWarningHandler(filterWarningHandler); - filterManager.onAllServicesInitialized(); - - mobileNotificationService.onAllServicesInitialized(); myOfferTakenEvents.onAllServicesInitialized(); tradeEvents.onAllServicesInitialized(); diff --git a/core/src/main/java/haveno/core/filter/FilterManager.java b/core/src/main/java/haveno/core/filter/FilterManager.java index cb7e0e9b21..2cebb66f3a 100644 --- a/core/src/main/java/haveno/core/filter/FilterManager.java +++ b/core/src/main/java/haveno/core/filter/FilterManager.java @@ -406,6 +406,10 @@ public class FilterManager { .anyMatch(e -> e.equals(address)); } + public String getDisableTradeBelowVersion() { + return getFilter() == null || getFilter().getDisableTradeBelowVersion() == null || getFilter().getDisableTradeBelowVersion().isEmpty() ? null : getFilter().getDisableTradeBelowVersion(); + } + public boolean requireUpdateToNewVersionForTrading() { if (getFilter() == null) { return false; diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index 81d2b2b821..91e2284d46 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -737,7 +737,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe doCancelOffer(openOffer, true); } - // remove open offer which thaws its key images + // cancel open offer which thaws its key images private void doCancelOffer(@NotNull OpenOffer openOffer, boolean resetAddressEntries) { Offer offer = openOffer.getOffer(); offer.setState(Offer.State.REMOVED); @@ -1396,6 +1396,32 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe return; } + // verify the trade protocol version + if (request.getOfferPayload().getProtocolVersion() != Version.TRADE_PROTOCOL_VERSION) { + errorMessage = "Unsupported protocol version: " + request.getOfferPayload().getProtocolVersion(); + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + + // verify the min version number + if (filterManager.getDisableTradeBelowVersion() != null) { + if (Version.compare(request.getOfferPayload().getVersionNr(), filterManager.getDisableTradeBelowVersion()) < 0) { + errorMessage = "Offer version number is too low: " + request.getOfferPayload().getVersionNr() + " < " + filterManager.getDisableTradeBelowVersion(); + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + } + + // verify the max version number + if (Version.compare(request.getOfferPayload().getVersionNr(), Version.VERSION) > 0) { + errorMessage = "Offer version number is too high: " + request.getOfferPayload().getVersionNr() + " > " + Version.VERSION; + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + // verify maker and taker fees boolean hasBuyerAsTakerWithoutDeposit = offer.getDirection() == OfferDirection.SELL && offer.isPrivateOffer() && offer.getChallengeHash() != null && offer.getChallengeHash().length() > 0 && offer.getTakerFeePct() == 0; if (hasBuyerAsTakerWithoutDeposit) { diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 97aef9545f..7bfc37f8e2 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -767,7 +767,7 @@ public class XmrWalletService extends XmrWalletBase { BigInteger minerFeeEstimate = getFeeEstimate(tx.getWeight()); double minerFeeDiff = tx.getFee().subtract(minerFeeEstimate).abs().doubleValue() / minerFeeEstimate.doubleValue(); if (minerFeeDiff > MINER_FEE_TOLERANCE) throw new RuntimeException("Miner fee is not within " + (MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + minerFeeEstimate + " but was " + tx.getFee() + ", diff%=" + minerFeeDiff); - log.info("Trade tx fee {} is within tolerance, diff%={}", tx.getFee(), minerFeeDiff); + log.info("Trade miner fee {} is within tolerance, diff%={}", tx.getFee(), minerFeeDiff); // verify proof to fee address BigInteger actualTradeFee = BigInteger.ZERO; @@ -785,7 +785,7 @@ public class XmrWalletService extends XmrWalletBase { // verify trade fee amount if (!actualTradeFee.equals(tradeFeeAmount)) { if (equalsWithinFractionError(actualTradeFee, tradeFeeAmount)) { - log.warn("Trade tx fee amount is within fraction error, expected " + tradeFeeAmount + " but was " + actualTradeFee); + log.warn("Trade fee amount is within fraction error, expected " + tradeFeeAmount + " but was " + actualTradeFee); } else { throw new RuntimeException("Invalid trade fee amount, expected " + tradeFeeAmount + " but was " + actualTradeFee); }