From b2b4706f14c8617623bba04603d78f072313ca9f Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 14 Aug 2023 13:14:02 -0400 Subject: [PATCH] enable editing offers --- .../haveno/core/offer/OfferFilterService.java | 2 +- .../java/haveno/core/offer/OfferPayload.java | 46 +++++++++++++++++++ .../java/haveno/core/offer/OpenOffer.java | 16 +++++++ .../haveno/core/offer/OpenOfferManager.java | 5 +- .../tasks/MakerProcessSignOfferResponse.java | 2 +- .../java/haveno/core/trade/HavenoUtils.java | 40 ++++++++-------- .../tasks/ArbitratorProcessReserveTx.java | 1 + .../haveno/core/util/FormattingUtils.java | 2 +- .../core/xmr/wallet/XmrWalletService.java | 1 + .../main/offer/offerbook/OfferBookView.java | 2 +- .../portfolio/openoffer/OpenOffersView.java | 2 +- 11 files changed, 93 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/OfferFilterService.java b/core/src/main/java/haveno/core/offer/OfferFilterService.java index 1339f315..433e9f3a 100644 --- a/core/src/main/java/haveno/core/offer/OfferFilterService.java +++ b/core/src/main/java/haveno/core/offer/OfferFilterService.java @@ -243,7 +243,7 @@ public class OfferFilterService { public boolean hasValidSignature(Offer offer) { Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner()); if (arbitrator == null) return false; // invalid arbitrator - return HavenoUtils.isArbitratorSignatureValid(offer, arbitrator); + return HavenoUtils.isArbitratorSignatureValid(offer.getOfferPayload(), arbitrator); } public boolean isReservedFundsSpent(Offer offer) { diff --git a/core/src/main/java/haveno/core/offer/OfferPayload.java b/core/src/main/java/haveno/core/offer/OfferPayload.java index 2db05fec..074fbdd7 100644 --- a/core/src/main/java/haveno/core/offer/OfferPayload.java +++ b/core/src/main/java/haveno/core/offer/OfferPayload.java @@ -243,6 +243,52 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay return this.hash; } + public byte[] getSignatureHash() { + + // create copy with ignored fields standardized + OfferPayload signee = new OfferPayload( + id, + date, + ownerNodeAddress, + pubKeyRing, + direction, + price, + 0, + false, + amount, + minAmount, + baseCurrencyCode, + counterCurrencyCode, + paymentMethodId, + makerPaymentAccountId, + offerFeeTxId, + countryCode, + acceptedCountryCodes, + bankId, + acceptedBankIds, + versionNr, + blockHeightAtOfferCreation, + makerFee, + buyerSecurityDeposit, + sellerSecurityDeposit, + maxTradeLimit, + maxTradePeriod, + useAutoClose, + useReOpenAfterAutoClose, + lowerClosePrice, + upperClosePrice, + isPrivateOffer, + hashOfChallenge, + extraDataMap, + protocolVersion, + arbitratorSigner, + null, + reserveTxKeyImages + ); + + return signee.getHash(); + } + @Override public long getTTL() { return TTL; diff --git a/core/src/main/java/haveno/core/offer/OpenOffer.java b/core/src/main/java/haveno/core/offer/OpenOffer.java index b7e9efa5..0811cb7b 100644 --- a/core/src/main/java/haveno/core/offer/OpenOffer.java +++ b/core/src/main/java/haveno/core/offer/OpenOffer.java @@ -30,6 +30,7 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Optional; @@ -103,6 +104,21 @@ public final class OpenOffer implements Tradable { state = State.SCHEDULED; } + public OpenOffer(Offer offer, long triggerPrice, OpenOffer openOffer) { + this.offer = offer; + this.triggerPrice = triggerPrice; + + // copy open offer fields + this.state = openOffer.state; + this.reserveExactAmount = openOffer.reserveExactAmount; + this.scheduledAmount = openOffer.scheduledAmount; + this.scheduledTxHashes = openOffer.scheduledTxHashes == null ? null : new ArrayList(openOffer.scheduledTxHashes); + this.splitOutputTxHash = openOffer.splitOutputTxHash; + this.reserveTxHash = openOffer.reserveTxHash; + this.reserveTxHex = openOffer.reserveTxHex; + this.reserveTxKey = openOffer.reserveTxKey; + } + /////////////////////////////////////////////////////////////////////////////////////////// // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index a17445ce..b6454508 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -624,7 +624,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe openOffer.setState(OpenOffer.State.CANCELED); removeOpenOffer(openOffer); - OpenOffer editedOpenOffer = new OpenOffer(editedOffer, triggerPrice); + OpenOffer editedOpenOffer = new OpenOffer(editedOffer, triggerPrice, openOffer); editedOpenOffer.setState(originalState); addOpenOffer(editedOpenOffer); @@ -1172,8 +1172,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe true); // arbitrator signs offer to certify they have valid reserve tx - String offerPayloadAsJson = JsonUtil.objectToJson(request.getOfferPayload()); - byte[] signature = HavenoUtils.sign(keyRing, offerPayloadAsJson); + byte[] signature = HavenoUtils.signOffer(request.getOfferPayload(), keyRing); OfferPayload signedOfferPayload = request.getOfferPayload(); signedOfferPayload.setArbitratorSignature(signature); diff --git a/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerProcessSignOfferResponse.java b/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerProcessSignOfferResponse.java index 1616c7ef..ecd6ea1f 100644 --- a/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerProcessSignOfferResponse.java +++ b/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerProcessSignOfferResponse.java @@ -41,7 +41,7 @@ public class MakerProcessSignOfferResponse extends Task { Arbitrator arbitrator = checkNotNull(model.getUser().getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner()), "user.getAcceptedArbitratorByAddress(arbitratorSigner) must not be null"); // validate arbitrator signature - if (!HavenoUtils.isArbitratorSignatureValid(new Offer(model.getSignOfferResponse().getSignedOfferPayload()), arbitrator)) { + if (!HavenoUtils.isArbitratorSignatureValid(model.getSignOfferResponse().getSignedOfferPayload(), arbitrator)) { throw new RuntimeException("Offer payload has invalid arbitrator signature"); } diff --git a/core/src/main/java/haveno/core/trade/HavenoUtils.java b/core/src/main/java/haveno/core/trade/HavenoUtils.java index 6e86f3b5..f5550393 100644 --- a/core/src/main/java/haveno/core/trade/HavenoUtils.java +++ b/core/src/main/java/haveno/core/trade/HavenoUtils.java @@ -20,13 +20,13 @@ package haveno.core.trade; import com.google.common.base.CaseFormat; import com.google.common.base.Charsets; import haveno.common.config.Config; +import haveno.common.crypto.CryptoException; import haveno.common.crypto.Hash; import haveno.common.crypto.KeyRing; import haveno.common.crypto.PubKeyRing; import haveno.common.crypto.Sig; import haveno.common.util.Utilities; import haveno.core.app.HavenoSetup; -import haveno.core.offer.Offer; import haveno.core.offer.OfferPayload; import haveno.core.support.dispute.arbitration.ArbitrationManager; import haveno.core.support.dispute.arbitration.arbitrator.Arbitrator; @@ -245,6 +245,10 @@ public class HavenoUtils { return sign(keyRing.getSignatureKeyPair().getPrivate(), message); } + public static byte[] sign(KeyRing keyRing, byte[] bytes) { + return sign(keyRing.getSignatureKeyPair().getPrivate(), bytes); + } + public static byte[] sign(PrivateKey privateKey, String message) { return sign(privateKey, message.getBytes(Charsets.UTF_8)); } @@ -263,9 +267,10 @@ public class HavenoUtils { public static void verifySignature(PubKeyRing pubKeyRing, byte[] bytes, byte[] signature) { try { - Sig.verify(pubKeyRing.getSignaturePubKey(), bytes, signature); - } catch (Exception e) { - throw new RuntimeException(e); + boolean isValid = Sig.verify(pubKeyRing.getSignaturePubKey(), bytes, signature); + if (!isValid) throw new IllegalArgumentException("Signature verification failed."); + } catch (CryptoException e) { + throw new IllegalArgumentException(e); } } @@ -282,6 +287,17 @@ public class HavenoUtils { } } + /** + * Sign an offer. + * + * @param offer is an unsigned offer to sign + * @param keyRing is the arbitrator's key ring to sign with + * @return the arbitrator's signature + */ + public static byte[] signOffer(OfferPayload offer, KeyRing keyRing) { + return HavenoUtils.sign(keyRing, offer.getSignatureHash()); + } + /** * Check if the arbitrator signature is valid for an offer. * @@ -289,20 +305,8 @@ public class HavenoUtils { * @param arbitrator is the original signing arbitrator * @return true if the arbitrator's signature is valid for the offer */ - public static boolean isArbitratorSignatureValid(Offer offer, Arbitrator arbitrator) { - - // copy offer payload - OfferPayload offerPayloadCopy = OfferPayload.fromProto(offer.toProtoMessage().getOfferPayload()); - - // remove arbitrator signature from signed payload - byte[] signature = offerPayloadCopy.getArbitratorSignature(); - offerPayloadCopy.setArbitratorSignature(null); - - // get unsigned offer payload as json string - String unsignedOfferAsJson = JsonUtil.objectToJson(offerPayloadCopy); - - // verify signature - return isSignatureValid(arbitrator.getPubKeyRing(), unsignedOfferAsJson, signature); + public static boolean isArbitratorSignatureValid(OfferPayload offer, Arbitrator arbitrator) { + return isSignatureValid(arbitrator.getPubKeyRing(), offer.getSignatureHash(), offer.getArbitratorSignature()); } /** diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java index fae55c45..ff109056 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java @@ -71,6 +71,7 @@ public class ArbitratorProcessReserveTx extends TradeTask { null, true); } catch (Exception e) { + e.printStackTrace(); throw new RuntimeException("Error processing reserve tx from " + (isFromTaker ? "taker " : "maker ") + request.getSenderNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage()); } diff --git a/core/src/main/java/haveno/core/util/FormattingUtils.java b/core/src/main/java/haveno/core/util/FormattingUtils.java index 4b862df7..d8df8253 100644 --- a/core/src/main/java/haveno/core/util/FormattingUtils.java +++ b/core/src/main/java/haveno/core/util/FormattingUtils.java @@ -157,7 +157,7 @@ public class FormattingUtils { } public static String formatPrice(Price price) { - return formatPrice(price, getPriceMonetaryFormat(price.getCurrencyCode()), false); + return formatPrice(price, price == null ? null : getPriceMonetaryFormat(price.getCurrencyCode()), false); } public static String formatMarketPrice(double price, String currencyCode) { 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 a836f62c..96f82c76 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -467,6 +467,7 @@ public class XmrWalletService { * @return tuple with the verified tx and its actual security deposit */ public Tuple2 verifyTradeTx(String offerId, BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, String txHash, String txHex, String txKey, List keyImages, boolean isReserveTx) { + if (txHash == null) throw new IllegalArgumentException("Cannot verify trade tx with null id"); MoneroDaemonRpc daemon = getDaemon(); MoneroWallet wallet = getWallet(); MoneroTx tx = null; diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java index 46d61f7a..3a8b8b70 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java @@ -1078,7 +1078,7 @@ abstract public class OfferBookView