mirror of
https://github.com/boldsuck/haveno.git
synced 2025-01-09 09:39:23 +00:00
enable editing offers
This commit is contained in:
parent
cb7d9364e5
commit
b2b4706f14
11 changed files with 93 additions and 26 deletions
|
@ -243,7 +243,7 @@ public class OfferFilterService {
|
||||||
public boolean hasValidSignature(Offer offer) {
|
public boolean hasValidSignature(Offer offer) {
|
||||||
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner());
|
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner());
|
||||||
if (arbitrator == null) return false; // invalid arbitrator
|
if (arbitrator == null) return false; // invalid arbitrator
|
||||||
return HavenoUtils.isArbitratorSignatureValid(offer, arbitrator);
|
return HavenoUtils.isArbitratorSignatureValid(offer.getOfferPayload(), arbitrator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReservedFundsSpent(Offer offer) {
|
public boolean isReservedFundsSpent(Offer offer) {
|
||||||
|
|
|
@ -243,6 +243,52 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
|
||||||
return this.hash;
|
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
|
@Override
|
||||||
public long getTTL() {
|
public long getTTL() {
|
||||||
return TTL;
|
return TTL;
|
||||||
|
|
|
@ -30,6 +30,7 @@ import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -103,6 +104,21 @@ public final class OpenOffer implements Tradable {
|
||||||
state = State.SCHEDULED;
|
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
|
// PROTO BUFFER
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -624,7 +624,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
openOffer.setState(OpenOffer.State.CANCELED);
|
openOffer.setState(OpenOffer.State.CANCELED);
|
||||||
removeOpenOffer(openOffer);
|
removeOpenOffer(openOffer);
|
||||||
|
|
||||||
OpenOffer editedOpenOffer = new OpenOffer(editedOffer, triggerPrice);
|
OpenOffer editedOpenOffer = new OpenOffer(editedOffer, triggerPrice, openOffer);
|
||||||
editedOpenOffer.setState(originalState);
|
editedOpenOffer.setState(originalState);
|
||||||
|
|
||||||
addOpenOffer(editedOpenOffer);
|
addOpenOffer(editedOpenOffer);
|
||||||
|
@ -1172,8 +1172,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
true);
|
true);
|
||||||
|
|
||||||
// arbitrator signs offer to certify they have valid reserve tx
|
// arbitrator signs offer to certify they have valid reserve tx
|
||||||
String offerPayloadAsJson = JsonUtil.objectToJson(request.getOfferPayload());
|
byte[] signature = HavenoUtils.signOffer(request.getOfferPayload(), keyRing);
|
||||||
byte[] signature = HavenoUtils.sign(keyRing, offerPayloadAsJson);
|
|
||||||
OfferPayload signedOfferPayload = request.getOfferPayload();
|
OfferPayload signedOfferPayload = request.getOfferPayload();
|
||||||
signedOfferPayload.setArbitratorSignature(signature);
|
signedOfferPayload.setArbitratorSignature(signature);
|
||||||
|
|
||||||
|
|
|
@ -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");
|
Arbitrator arbitrator = checkNotNull(model.getUser().getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner()), "user.getAcceptedArbitratorByAddress(arbitratorSigner) must not be null");
|
||||||
|
|
||||||
// validate arbitrator signature
|
// 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");
|
throw new RuntimeException("Offer payload has invalid arbitrator signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,13 @@ package haveno.core.trade;
|
||||||
import com.google.common.base.CaseFormat;
|
import com.google.common.base.CaseFormat;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import haveno.common.config.Config;
|
import haveno.common.config.Config;
|
||||||
|
import haveno.common.crypto.CryptoException;
|
||||||
import haveno.common.crypto.Hash;
|
import haveno.common.crypto.Hash;
|
||||||
import haveno.common.crypto.KeyRing;
|
import haveno.common.crypto.KeyRing;
|
||||||
import haveno.common.crypto.PubKeyRing;
|
import haveno.common.crypto.PubKeyRing;
|
||||||
import haveno.common.crypto.Sig;
|
import haveno.common.crypto.Sig;
|
||||||
import haveno.common.util.Utilities;
|
import haveno.common.util.Utilities;
|
||||||
import haveno.core.app.HavenoSetup;
|
import haveno.core.app.HavenoSetup;
|
||||||
import haveno.core.offer.Offer;
|
|
||||||
import haveno.core.offer.OfferPayload;
|
import haveno.core.offer.OfferPayload;
|
||||||
import haveno.core.support.dispute.arbitration.ArbitrationManager;
|
import haveno.core.support.dispute.arbitration.ArbitrationManager;
|
||||||
import haveno.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
import haveno.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||||
|
@ -245,6 +245,10 @@ public class HavenoUtils {
|
||||||
return sign(keyRing.getSignatureKeyPair().getPrivate(), message);
|
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) {
|
public static byte[] sign(PrivateKey privateKey, String message) {
|
||||||
return sign(privateKey, message.getBytes(Charsets.UTF_8));
|
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) {
|
public static void verifySignature(PubKeyRing pubKeyRing, byte[] bytes, byte[] signature) {
|
||||||
try {
|
try {
|
||||||
Sig.verify(pubKeyRing.getSignaturePubKey(), bytes, signature);
|
boolean isValid = Sig.verify(pubKeyRing.getSignaturePubKey(), bytes, signature);
|
||||||
} catch (Exception e) {
|
if (!isValid) throw new IllegalArgumentException("Signature verification failed.");
|
||||||
throw new RuntimeException(e);
|
} 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.
|
* Check if the arbitrator signature is valid for an offer.
|
||||||
*
|
*
|
||||||
|
@ -289,20 +305,8 @@ public class HavenoUtils {
|
||||||
* @param arbitrator is the original signing arbitrator
|
* @param arbitrator is the original signing arbitrator
|
||||||
* @return true if the arbitrator's signature is valid for the offer
|
* @return true if the arbitrator's signature is valid for the offer
|
||||||
*/
|
*/
|
||||||
public static boolean isArbitratorSignatureValid(Offer offer, Arbitrator arbitrator) {
|
public static boolean isArbitratorSignatureValid(OfferPayload offer, Arbitrator arbitrator) {
|
||||||
|
return isSignatureValid(arbitrator.getPubKeyRing(), offer.getSignatureHash(), offer.getArbitratorSignature());
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -71,6 +71,7 @@ public class ArbitratorProcessReserveTx extends TradeTask {
|
||||||
null,
|
null,
|
||||||
true);
|
true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
throw new RuntimeException("Error processing reserve tx from " + (isFromTaker ? "taker " : "maker ") + request.getSenderNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage());
|
throw new RuntimeException("Error processing reserve tx from " + (isFromTaker ? "taker " : "maker ") + request.getSenderNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -157,7 +157,7 @@ public class FormattingUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String formatPrice(Price price) {
|
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) {
|
public static String formatMarketPrice(double price, String currencyCode) {
|
||||||
|
|
|
@ -467,6 +467,7 @@ public class XmrWalletService {
|
||||||
* @return tuple with the verified tx and its actual security deposit
|
* @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) {
|
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();
|
MoneroDaemonRpc daemon = getDaemon();
|
||||||
MoneroWallet wallet = getWallet();
|
MoneroWallet wallet = getWallet();
|
||||||
MoneroTx tx = null;
|
MoneroTx tx = null;
|
||||||
|
|
|
@ -1078,7 +1078,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
|
||||||
hbox.setSpacing(8);
|
hbox.setSpacing(8);
|
||||||
hbox.setAlignment(Pos.CENTER);
|
hbox.setAlignment(Pos.CENTER);
|
||||||
hbox.getChildren().add(button);
|
hbox.getChildren().add(button);
|
||||||
//hbox.getChildren().add(button2); // TODO: re-enable editing offers
|
hbox.getChildren().add(button2);
|
||||||
HBox.setHgrow(button, Priority.ALWAYS);
|
HBox.setHgrow(button, Priority.ALWAYS);
|
||||||
HBox.setHgrow(button2, Priority.ALWAYS);
|
HBox.setHgrow(button2, Priority.ALWAYS);
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
|
||||||
setPaymentMethodColumnCellFactory();
|
setPaymentMethodColumnCellFactory();
|
||||||
setDateColumnCellFactory();
|
setDateColumnCellFactory();
|
||||||
setDeactivateColumnCellFactory();
|
setDeactivateColumnCellFactory();
|
||||||
// setEditColumnCellFactory(); // TODO: re-enable to edit offer price, etc?
|
setEditColumnCellFactory();
|
||||||
setTriggerIconColumnCellFactory();
|
setTriggerIconColumnCellFactory();
|
||||||
setTriggerPriceColumnCellFactory();
|
setTriggerPriceColumnCellFactory();
|
||||||
setRemoveColumnCellFactory();
|
setRemoveColumnCellFactory();
|
||||||
|
|
Loading…
Reference in a new issue