enable editing offers

This commit is contained in:
woodser 2023-08-14 13:14:02 -04:00
parent cb7d9364e5
commit b2b4706f14
11 changed files with 93 additions and 26 deletions

View file

@ -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) {

View file

@ -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;

View file

@ -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<String>(openOffer.scheduledTxHashes);
this.splitOutputTxHash = openOffer.splitOutputTxHash;
this.reserveTxHash = openOffer.reserveTxHash;
this.reserveTxHex = openOffer.reserveTxHex;
this.reserveTxKey = openOffer.reserveTxKey;
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -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);

View file

@ -41,7 +41,7 @@ public class MakerProcessSignOfferResponse extends Task<PlaceOfferModel> {
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");
}

View file

@ -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());
}
/**

View file

@ -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());
}

View file

@ -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) {

View file

@ -467,6 +467,7 @@ public class XmrWalletService {
* @return tuple with the verified tx and its actual security deposit
*/
public Tuple2<MoneroTx, BigInteger> verifyTradeTx(String offerId, BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, String txHash, String txHex, String txKey, List<String> 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;

View file

@ -1078,7 +1078,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
hbox.setSpacing(8);
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().add(button);
//hbox.getChildren().add(button2); // TODO: re-enable editing offers
hbox.getChildren().add(button2);
HBox.setHgrow(button, Priority.ALWAYS);
HBox.setHgrow(button2, Priority.ALWAYS);
}

View file

@ -153,7 +153,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
setPaymentMethodColumnCellFactory();
setDateColumnCellFactory();
setDeactivateColumnCellFactory();
// setEditColumnCellFactory(); // TODO: re-enable to edit offer price, etc?
setEditColumnCellFactory();
setTriggerIconColumnCellFactory();
setTriggerPriceColumnCellFactory();
setRemoveColumnCellFactory();