mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-20 10:22:18 +01:00
Add priv. notifications
This commit is contained in:
parent
dc5cc522e2
commit
3e2f09b4a1
@ -57,7 +57,7 @@ public final class Alert implements StoragePayload {
|
||||
}
|
||||
}
|
||||
|
||||
public void setSigAndStoragePubKey(String signatureAsBase64, PublicKey storagePublicKey) {
|
||||
public void setSigAndPubKey(String signatureAsBase64, PublicKey storagePublicKey) {
|
||||
this.signatureAsBase64 = signatureAsBase64;
|
||||
this.storagePublicKey = storagePublicKey;
|
||||
this.storagePublicKeyBytes = new X509EncodedKeySpec(this.storagePublicKey.getEncoded()).getEncoded();
|
||||
|
@ -125,7 +125,7 @@ public class AlertManager {
|
||||
private void signAndAddSignatureToAlertMessage(Alert alert) {
|
||||
String alertMessageAsHex = Utils.HEX.encode(alert.message.getBytes());
|
||||
String signatureAsBase64 = alertSigningKey.signMessage(alertMessageAsHex);
|
||||
alert.setSigAndStoragePubKey(signatureAsBase64, keyRing.getSignatureKeyPair().getPublic());
|
||||
alert.setSigAndPubKey(signatureAsBase64, keyRing.getSignatureKeyPair().getPublic());
|
||||
}
|
||||
|
||||
private boolean verifySignature(Alert alert) {
|
||||
|
@ -34,5 +34,7 @@ public class AlertModule extends AppModule {
|
||||
protected final void configure() {
|
||||
bind(AlertManager.class).in(Singleton.class);
|
||||
bind(AlertService.class).in(Singleton.class);
|
||||
bind(PrivateNotificationManager.class).in(Singleton.class);
|
||||
bind(PrivateNotificationService.class).in(Singleton.class);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.alert;
|
||||
|
||||
import io.bitsquare.app.Version;
|
||||
import io.bitsquare.common.crypto.Sig;
|
||||
import io.bitsquare.common.wire.Payload;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
public final class PrivateNotification implements Payload {
|
||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||
private static final long serialVersionUID = Version.P2P_NETWORK_VERSION;
|
||||
private static final Logger log = LoggerFactory.getLogger(PrivateNotification.class);
|
||||
|
||||
public final String message;
|
||||
private String signatureAsBase64;
|
||||
private transient PublicKey publicKey;
|
||||
private byte[] publicKeyBytes;
|
||||
|
||||
public PrivateNotification(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
try {
|
||||
in.defaultReadObject();
|
||||
publicKey = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
|
||||
} catch (Throwable t) {
|
||||
log.warn("Exception at readObject: " + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void setSigAndPubKey(String signatureAsBase64, PublicKey storagePublicKey) {
|
||||
this.signatureAsBase64 = signatureAsBase64;
|
||||
this.publicKey = storagePublicKey;
|
||||
this.publicKeyBytes = new X509EncodedKeySpec(this.publicKey.getEncoded()).getEncoded();
|
||||
}
|
||||
|
||||
public String getSignatureAsBase64() {
|
||||
return signatureAsBase64;
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.alert;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.crypto.DecryptedMsgWithPubKey;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.SignatureException;
|
||||
|
||||
import static org.bitcoinj.core.Utils.HEX;
|
||||
|
||||
public class PrivateNotificationManager {
|
||||
private static final Logger log = LoggerFactory.getLogger(PrivateNotificationManager.class);
|
||||
|
||||
private final PrivateNotificationService privateNotificationService;
|
||||
private final KeyRing keyRing;
|
||||
private final ObjectProperty<PrivateNotification> privateNotificationMessageProperty = new SimpleObjectProperty<>();
|
||||
|
||||
// Pub key for developer global privateNotification message
|
||||
private static final String pubKeyAsHex = "02ba7c5de295adfe57b60029f3637a2c6b1d0e969a8aaefb9e0ddc3a7963f26925";
|
||||
private ECKey privateNotificationSigningKey;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor, Initialization
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public PrivateNotificationManager(PrivateNotificationService privateNotificationService, KeyRing keyRing) {
|
||||
this.privateNotificationService = privateNotificationService;
|
||||
this.keyRing = keyRing;
|
||||
|
||||
privateNotificationService.addDecryptedDirectMessageListener(this::handleMessage);
|
||||
privateNotificationService.addDecryptedMailboxListener(this::handleMessage);
|
||||
}
|
||||
|
||||
private void handleMessage(DecryptedMsgWithPubKey decryptedMsgWithPubKey, NodeAddress senderNodeAddress) {
|
||||
Message message = decryptedMsgWithPubKey.message;
|
||||
if (message instanceof PrivateNotificationMessage) {
|
||||
PrivateNotificationMessage privateNotificationMessage = (PrivateNotificationMessage) message;
|
||||
log.trace("Received privateNotificationMessage: " + privateNotificationMessage);
|
||||
if (privateNotificationMessage.getSenderNodeAddress().equals(senderNodeAddress)) {
|
||||
final PrivateNotification privateNotification = privateNotificationMessage.privateNotification;
|
||||
if (verifySignature(privateNotification))
|
||||
privateNotificationMessageProperty.set(privateNotification);
|
||||
} else {
|
||||
log.warn("Peer address not matching for privateNotificationMessage");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public ReadOnlyObjectProperty<PrivateNotification> privateNotificationProperty() {
|
||||
return privateNotificationMessageProperty;
|
||||
}
|
||||
|
||||
public boolean sendPrivateNotificationMessageIfKeyIsValid(PrivateNotification privateNotification, Offer offer, String privKeyString) {
|
||||
// if there is a previous message we remove that first
|
||||
// if (user.getDevelopersPrivateNotification() != null)
|
||||
// removePrivateNotificationMessageIfKeyIsValid(privKeyString);
|
||||
|
||||
boolean isKeyValid = isKeyValid(privKeyString);
|
||||
if (isKeyValid) {
|
||||
signAndAddSignatureToPrivateNotificationMessage(privateNotification);
|
||||
// user.setDevelopersPrivateNotification(privateNotification);
|
||||
privateNotificationService.sendPrivateNotificationMessage(privateNotification, offer, null, null);
|
||||
}
|
||||
|
||||
return isKeyValid;
|
||||
}
|
||||
|
||||
public boolean removePrivateNotificationMessageIfKeyIsValid(String privKeyString) {
|
||||
/* PrivateNotification developersPrivateNotification = user.getDevelopersPrivateNotification();
|
||||
if (isKeyValid(privKeyString) && developersPrivateNotification != null) {
|
||||
privateNotificationService.removePrivateNotificationMessage(developersPrivateNotification, null, null);
|
||||
user.setDevelopersPrivateNotification(null);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}*/
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isKeyValid(String privKeyString) {
|
||||
try {
|
||||
privateNotificationSigningKey = ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyString)));
|
||||
return pubKeyAsHex.equals(Utils.HEX.encode(privateNotificationSigningKey.getPubKey()));
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void signAndAddSignatureToPrivateNotificationMessage(PrivateNotification privateNotification) {
|
||||
String privateNotificationMessageAsHex = Utils.HEX.encode(privateNotification.message.getBytes());
|
||||
String signatureAsBase64 = privateNotificationSigningKey.signMessage(privateNotificationMessageAsHex);
|
||||
privateNotification.setSigAndPubKey(signatureAsBase64, keyRing.getSignatureKeyPair().getPublic());
|
||||
}
|
||||
|
||||
private boolean verifySignature(PrivateNotification privateNotification) {
|
||||
String privateNotificationMessageAsHex = Utils.HEX.encode(privateNotification.message.getBytes());
|
||||
try {
|
||||
ECKey.fromPublicOnly(HEX.decode(pubKeyAsHex)).verifyMessage(privateNotificationMessageAsHex, privateNotification.getSignatureAsBase64());
|
||||
return true;
|
||||
} catch (SignatureException e) {
|
||||
log.warn("verifySignature failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package io.bitsquare.alert;
|
||||
|
||||
import io.bitsquare.app.Version;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.p2p.messaging.MailboxMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PrivateNotificationMessage implements MailboxMessage {
|
||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||
private static final long serialVersionUID = Version.P2P_NETWORK_VERSION;
|
||||
private static final Logger log = LoggerFactory.getLogger(PrivateNotificationMessage.class);
|
||||
private NodeAddress myNodeAddress;
|
||||
public PrivateNotification privateNotification;
|
||||
private final String uid = UUID.randomUUID().toString();
|
||||
private final int messageVersion = Version.getP2PMessageVersion();
|
||||
|
||||
public PrivateNotificationMessage(PrivateNotification privateNotification, NodeAddress myNodeAddress) {
|
||||
this.myNodeAddress = myNodeAddress;
|
||||
this.privateNotification = privateNotification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeAddress getSenderNodeAddress() {
|
||||
return myNodeAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUID() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMessageVersion() {
|
||||
return messageVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof PrivateNotificationMessage)) return false;
|
||||
|
||||
PrivateNotificationMessage that = (PrivateNotificationMessage) o;
|
||||
|
||||
if (messageVersion != that.messageVersion) return false;
|
||||
if (myNodeAddress != null ? !myNodeAddress.equals(that.myNodeAddress) : that.myNodeAddress != null)
|
||||
return false;
|
||||
if (privateNotification != null ? !privateNotification.equals(that.privateNotification) : that.privateNotification != null)
|
||||
return false;
|
||||
return !(uid != null ? !uid.equals(that.uid) : that.uid != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myNodeAddress != null ? myNodeAddress.hashCode() : 0;
|
||||
result = 31 * result + (privateNotification != null ? privateNotification.hashCode() : 0);
|
||||
result = 31 * result + (uid != null ? uid.hashCode() : 0);
|
||||
result = 31 * result + messageVersion;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PrivateNotificationMessage{" +
|
||||
"myNodeAddress=" + myNodeAddress +
|
||||
", privateNotification=" + privateNotification +
|
||||
", uid='" + uid + '\'' +
|
||||
", messageVersion=" + messageVersion +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.alert;
|
||||
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.messaging.DecryptedDirectMessageListener;
|
||||
import io.bitsquare.p2p.messaging.DecryptedMailboxListener;
|
||||
import io.bitsquare.p2p.messaging.SendMailboxMessageListener;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Used to load global privateNotification messages.
|
||||
* The message is signed by the project developers private key and use data protection.
|
||||
*/
|
||||
public class PrivateNotificationService {
|
||||
private static final Logger log = LoggerFactory.getLogger(PrivateNotificationService.class);
|
||||
private final P2PService p2PService;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor, Initialization
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public PrivateNotificationService(P2PService p2PService) {
|
||||
this.p2PService = p2PService;
|
||||
}
|
||||
|
||||
public void addDecryptedMailboxListener(DecryptedMailboxListener listener) {
|
||||
p2PService.addDecryptedMailboxListener(listener);
|
||||
}
|
||||
|
||||
public void addDecryptedDirectMessageListener(DecryptedDirectMessageListener listener) {
|
||||
p2PService.addDecryptedDirectMessageListener(listener);
|
||||
}
|
||||
|
||||
|
||||
public void sendPrivateNotificationMessage(PrivateNotification privateNotification, Offer offer, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) {
|
||||
p2PService.sendEncryptedMailboxMessage(offer.getOffererNodeAddress(),
|
||||
offer.getPubKeyRing(),
|
||||
new PrivateNotificationMessage(privateNotification, p2PService.getNetworkNode().getNodeAddress()),
|
||||
new SendMailboxMessageListener() {
|
||||
@Override
|
||||
public void onArrived() {
|
||||
log.trace("PrivateNotificationMessage arrived at peer. PrivateNotificationMessage = " + privateNotification);
|
||||
if (resultHandler != null) resultHandler.handleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStoredInMailbox() {
|
||||
log.trace("PrivateNotificationMessage was stored in mailbox. PrivateNotificationMessage = " + privateNotification);
|
||||
if (resultHandler != null) resultHandler.handleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage) {
|
||||
if (errorMessageHandler != null)
|
||||
errorMessageHandler.handleErrorMessage("Add privateNotificationMessage failed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void removePrivateNotificationMessage(PrivateNotification privateNotification, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) {
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package io.bitsquare.gui.components;
|
||||
|
||||
import io.bitsquare.alert.PrivateNotificationManager;
|
||||
import io.bitsquare.gui.main.overlays.editor.PeerInfoWithTagEditor;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Group;
|
||||
@ -24,6 +26,8 @@ public class PeerInfoIcon extends Group {
|
||||
private final String hostName;
|
||||
private final String tooltipText;
|
||||
private final int numTrades;
|
||||
private PrivateNotificationManager privateNotificationManager;
|
||||
private Offer offer;
|
||||
private final Map<String, String> peerTagMap;
|
||||
private final Label numTradesLabel;
|
||||
private final double SIZE = 26;
|
||||
@ -33,10 +37,12 @@ public class PeerInfoIcon extends Group {
|
||||
private final Pane tagPane;
|
||||
private final Pane numTradesPane;
|
||||
|
||||
public PeerInfoIcon(String hostName, String tooltipText, int numTrades) {
|
||||
public PeerInfoIcon(String hostName, String tooltipText, int numTrades, PrivateNotificationManager privateNotificationManager, Offer offer) {
|
||||
this.hostName = hostName;
|
||||
this.tooltipText = tooltipText;
|
||||
this.numTrades = numTrades;
|
||||
this.privateNotificationManager = privateNotificationManager;
|
||||
this.offer = offer;
|
||||
|
||||
peerTagMap = Preferences.INSTANCE.getPeerTagMap();
|
||||
|
||||
@ -97,7 +103,7 @@ public class PeerInfoIcon extends Group {
|
||||
|
||||
getChildren().addAll(background, avatarImageView, tagPane, numTradesPane);
|
||||
|
||||
setOnMouseClicked(e -> new PeerInfoWithTagEditor()
|
||||
setOnMouseClicked(e -> new PeerInfoWithTagEditor(privateNotificationManager, offer)
|
||||
.hostName(hostName)
|
||||
.numTrades(numTrades)
|
||||
.position(localToScene(new Point2D(0, 0)))
|
||||
|
@ -20,6 +20,8 @@ package io.bitsquare.gui.main;
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.alert.Alert;
|
||||
import io.bitsquare.alert.AlertManager;
|
||||
import io.bitsquare.alert.PrivateNotification;
|
||||
import io.bitsquare.alert.PrivateNotificationManager;
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.app.DevFlags;
|
||||
import io.bitsquare.app.Log;
|
||||
@ -101,6 +103,7 @@ public class MainViewModel implements ViewModel {
|
||||
private final DisputeManager disputeManager;
|
||||
final Preferences preferences;
|
||||
private final AlertManager alertManager;
|
||||
private PrivateNotificationManager privateNotificationManager;
|
||||
private final WalletPasswordWindow walletPasswordWindow;
|
||||
private final NotificationCenter notificationCenter;
|
||||
private final TacWindow tacWindow;
|
||||
@ -171,7 +174,8 @@ public class MainViewModel implements ViewModel {
|
||||
PriceFeed priceFeed,
|
||||
ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager,
|
||||
OpenOfferManager openOfferManager, DisputeManager disputeManager, Preferences preferences,
|
||||
User user, AlertManager alertManager, WalletPasswordWindow walletPasswordWindow,
|
||||
User user, AlertManager alertManager, PrivateNotificationManager privateNotificationManager,
|
||||
WalletPasswordWindow walletPasswordWindow,
|
||||
NotificationCenter notificationCenter, TacWindow tacWindow, Clock clock,
|
||||
KeyRing keyRing, Navigation navigation, BSFormatter formatter) {
|
||||
this.priceFeed = priceFeed;
|
||||
@ -185,6 +189,7 @@ public class MainViewModel implements ViewModel {
|
||||
this.disputeManager = disputeManager;
|
||||
this.preferences = preferences;
|
||||
this.alertManager = alertManager;
|
||||
this.privateNotificationManager = privateNotificationManager;
|
||||
this.walletPasswordWindow = walletPasswordWindow;
|
||||
this.notificationCenter = notificationCenter;
|
||||
this.tacWindow = tacWindow;
|
||||
@ -517,6 +522,7 @@ public class MainViewModel implements ViewModel {
|
||||
openOfferManager.onAllServicesInitialized();
|
||||
arbitratorManager.onAllServicesInitialized();
|
||||
alertManager.alertMessageProperty().addListener((observable, oldValue, newValue) -> displayAlertIfPresent(newValue));
|
||||
privateNotificationManager.privateNotificationProperty().addListener((observable, oldValue, newValue) -> displayPrivateNotification(newValue));
|
||||
displayAlertIfPresent(alertManager.alertMessageProperty().get());
|
||||
|
||||
setupBtcNumPeersWatcher();
|
||||
@ -865,6 +871,12 @@ public class MainViewModel implements ViewModel {
|
||||
new DisplayAlertMessageWindow().alertMessage(alert).show();
|
||||
}
|
||||
|
||||
private void displayPrivateNotification(PrivateNotification privateNotification) {
|
||||
new Popup<>().headLine("Important notification from Bitsquare developers!")
|
||||
.information(privateNotification.message)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void swapPendingOfferFundingEntries() {
|
||||
tradeManager.getAddressEntriesForAvailableBalanceStream()
|
||||
.filter(addressEntry -> addressEntry.getOfferId() != null)
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
package io.bitsquare.gui.main.offer.offerbook;
|
||||
|
||||
import io.bitsquare.alert.PrivateNotificationManager;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
@ -68,6 +69,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||
private final Navigation navigation;
|
||||
private final OfferDetailsWindow offerDetailsWindow;
|
||||
private BSFormatter formatter;
|
||||
private PrivateNotificationManager privateNotificationManager;
|
||||
|
||||
private ComboBox<TradeCurrency> currencyComboBox;
|
||||
private ComboBox<PaymentMethod> paymentMethodComboBox;
|
||||
@ -87,12 +89,13 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
OfferBookView(OfferBookViewModel model, Navigation navigation, OfferDetailsWindow offerDetailsWindow, BSFormatter formatter) {
|
||||
OfferBookView(OfferBookViewModel model, Navigation navigation, OfferDetailsWindow offerDetailsWindow, BSFormatter formatter, PrivateNotificationManager privateNotificationManager) {
|
||||
super(model);
|
||||
|
||||
this.navigation = navigation;
|
||||
this.offerDetailsWindow = offerDetailsWindow;
|
||||
this.formatter = formatter;
|
||||
this.privateNotificationManager = privateNotificationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -706,7 +709,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||
boolean hasTraded = numPastTrades > 0;
|
||||
String tooltipText = hasTraded ? "Offerers onion address: " + hostName + "\n" +
|
||||
"You have already traded " + numPastTrades + " times with that offerer." : "Offerers onion address: " + hostName;
|
||||
Node identIcon = new PeerInfoIcon(hostName, tooltipText, numPastTrades);
|
||||
Node identIcon = new PeerInfoIcon(hostName, tooltipText, numPastTrades, privateNotificationManager, newItem.getOffer());
|
||||
setPadding(new Insets(-2, 0, -2, 0));
|
||||
if (identIcon != null)
|
||||
setGraphic(identIcon);
|
||||
|
@ -1,8 +1,11 @@
|
||||
package io.bitsquare.gui.main.overlays.editor;
|
||||
|
||||
import io.bitsquare.alert.PrivateNotificationManager;
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import io.bitsquare.gui.main.overlays.Overlay;
|
||||
import io.bitsquare.gui.main.overlays.windows.SendPrivateNotificationWindow;
|
||||
import io.bitsquare.gui.util.FormBuilder;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.animation.Interpolator;
|
||||
import javafx.animation.KeyFrame;
|
||||
@ -16,6 +19,7 @@ import javafx.geometry.Point2D;
|
||||
import javafx.scene.Camera;
|
||||
import javafx.scene.PerspectiveCamera;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.transform.Rotate;
|
||||
@ -38,9 +42,13 @@ public class PeerInfoWithTagEditor extends Overlay<PeerInfoWithTagEditor> {
|
||||
private String hostName;
|
||||
private int numTrades;
|
||||
private ChangeListener<Boolean> focusListener;
|
||||
private PrivateNotificationManager privateNotificationManager;
|
||||
private Offer offer;
|
||||
|
||||
|
||||
public PeerInfoWithTagEditor() {
|
||||
public PeerInfoWithTagEditor(PrivateNotificationManager privateNotificationManager, Offer offer) {
|
||||
this.privateNotificationManager = privateNotificationManager;
|
||||
this.offer = offer;
|
||||
width = 400;
|
||||
type = Type.Undefined;
|
||||
if (INSTANCE != null)
|
||||
@ -123,6 +131,14 @@ public class PeerInfoWithTagEditor extends Overlay<PeerInfoWithTagEditor> {
|
||||
Map<String, String> peerTagMap = Preferences.INSTANCE.getPeerTagMap();
|
||||
String tag = peerTagMap.containsKey(hostName) ? peerTagMap.get(hostName) : "";
|
||||
inputTextField.setText(tag);
|
||||
|
||||
Button button = FormBuilder.addButton(gridPane, ++rowIndex, "Send private message");
|
||||
button.setOnAction(e -> {
|
||||
new SendPrivateNotificationWindow(offer)
|
||||
.onAddAlertMessage(privateNotificationManager::sendPrivateNotificationMessageIfKeyIsValid)
|
||||
.onRemoveAlertMessage(privateNotificationManager::removePrivateNotificationMessageIfKeyIsValid)
|
||||
.show();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.gui.main.overlays.windows;
|
||||
|
||||
import io.bitsquare.alert.PrivateNotification;
|
||||
import io.bitsquare.common.util.Tuple2;
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import io.bitsquare.gui.main.overlays.Overlay;
|
||||
import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextArea;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.bitsquare.gui.util.FormBuilder.addLabelInputTextField;
|
||||
import static io.bitsquare.gui.util.FormBuilder.addLabelTextArea;
|
||||
|
||||
public class SendPrivateNotificationWindow extends Overlay<SendPrivateNotificationWindow> {
|
||||
private static final Logger log = LoggerFactory.getLogger(SendPrivateNotificationWindow.class);
|
||||
private Button sendButton;
|
||||
private SendPrivateNotificationHandler sendPrivateNotificationHandler;
|
||||
private RemoveAlertMessageHandler removeAlertMessageHandler;
|
||||
private Offer offer;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
public interface SendPrivateNotificationHandler {
|
||||
boolean handle(PrivateNotification privateNotification, Offer offer, String privKey);
|
||||
}
|
||||
|
||||
public interface RemoveAlertMessageHandler {
|
||||
boolean handle(String privKey);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Public API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public SendPrivateNotificationWindow(Offer offer) {
|
||||
this.offer = offer;
|
||||
type = Type.Attention;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (headLine == null)
|
||||
headLine = "Edit ban list";
|
||||
|
||||
width = 600;
|
||||
createGridPane();
|
||||
addHeadLine();
|
||||
addSeparator();
|
||||
addContent();
|
||||
applyStyles();
|
||||
display();
|
||||
}
|
||||
|
||||
public SendPrivateNotificationWindow onAddAlertMessage(SendPrivateNotificationHandler sendPrivateNotificationHandler) {
|
||||
this.sendPrivateNotificationHandler = sendPrivateNotificationHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SendPrivateNotificationWindow onRemoveAlertMessage(RemoveAlertMessageHandler removeAlertMessageHandler) {
|
||||
this.removeAlertMessageHandler = removeAlertMessageHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Protected
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void setupKeyHandler(Scene scene) {
|
||||
if (!hideCloseButton) {
|
||||
scene.setOnKeyPressed(e -> {
|
||||
if (e.getCode() == KeyCode.ESCAPE) {
|
||||
e.consume();
|
||||
doClose();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void addContent() {
|
||||
InputTextField keyInputTextField = addLabelInputTextField(gridPane, ++rowIndex, "Ban list private key:", 10).second;
|
||||
Tuple2<Label, TextArea> labelTextAreaTuple2 = addLabelTextArea(gridPane, ++rowIndex, "Private alert message:", "Enter message");
|
||||
TextArea alertMessageTextArea = labelTextAreaTuple2.second;
|
||||
Label first = labelTextAreaTuple2.first;
|
||||
first.setMinWidth(150);
|
||||
|
||||
sendButton = new Button("Send private alert message");
|
||||
sendButton.setOnAction(e -> {
|
||||
if (alertMessageTextArea.getText().length() > 0 && keyInputTextField.getText().length() > 0) {
|
||||
if (sendPrivateNotificationHandler.handle(
|
||||
new PrivateNotification(alertMessageTextArea.getText()),
|
||||
offer,
|
||||
keyInputTextField.getText()))
|
||||
hide();
|
||||
else
|
||||
new Popup().warning("The key you entered was not correct.").width(300).onClose(() -> blurAgain()).show();
|
||||
}
|
||||
});
|
||||
|
||||
Button removeAlertMessageButton = new Button("Remove notification");
|
||||
removeAlertMessageButton.setOnAction(e -> {
|
||||
if (keyInputTextField.getText().length() > 0) {
|
||||
if (removeAlertMessageHandler.handle(keyInputTextField.getText()))
|
||||
hide();
|
||||
else
|
||||
new Popup().warning("The key you entered was not correct.").width(300).onClose(() -> blurAgain()).show();
|
||||
}
|
||||
});
|
||||
|
||||
closeButton = new Button("Close");
|
||||
closeButton.setOnAction(e -> {
|
||||
hide();
|
||||
closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run());
|
||||
});
|
||||
|
||||
HBox hBox = new HBox();
|
||||
hBox.setSpacing(10);
|
||||
GridPane.setRowIndex(hBox, ++rowIndex);
|
||||
GridPane.setColumnIndex(hBox, 1);
|
||||
hBox.getChildren().addAll(sendButton, removeAlertMessageButton, closeButton);
|
||||
gridPane.getChildren().add(hBox);
|
||||
GridPane.setMargin(hBox, new Insets(10, 0, 0, 0));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -17,6 +17,7 @@
|
||||
|
||||
package io.bitsquare.gui.main.portfolio.closedtrades;
|
||||
|
||||
import io.bitsquare.alert.PrivateNotificationManager;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.HyperlinkWithIcon;
|
||||
@ -52,14 +53,16 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||
private final BSFormatter formatter;
|
||||
private final OfferDetailsWindow offerDetailsWindow;
|
||||
private final TradeDetailsWindow tradeDetailsWindow;
|
||||
private PrivateNotificationManager privateNotificationManager;
|
||||
private SortedList<ClosedTradableListItem> sortedList;
|
||||
|
||||
@Inject
|
||||
public ClosedTradesView(ClosedTradesViewModel model, BSFormatter formatter, OfferDetailsWindow offerDetailsWindow, TradeDetailsWindow tradeDetailsWindow) {
|
||||
public ClosedTradesView(ClosedTradesViewModel model, BSFormatter formatter, OfferDetailsWindow offerDetailsWindow, TradeDetailsWindow tradeDetailsWindow, PrivateNotificationManager privateNotificationManager) {
|
||||
super(model);
|
||||
this.formatter = formatter;
|
||||
this.offerDetailsWindow = offerDetailsWindow;
|
||||
this.tradeDetailsWindow = tradeDetailsWindow;
|
||||
this.privateNotificationManager = privateNotificationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -235,7 +238,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||
|
||||
int numPastTrades = model.getNumPastTrades(newItem.getTradable());
|
||||
String hostName = ((Trade) newItem.getTradable()).getTradingPeerNodeAddress().hostName;
|
||||
Node identIcon = new PeerInfoIcon(hostName, "Trading peers onion address: " + hostName, numPastTrades);
|
||||
Node identIcon = new PeerInfoIcon(hostName, "Trading peers onion address: " + hostName, numPastTrades, privateNotificationManager, newItem.getTradable().getOffer());
|
||||
setPadding(new Insets(-2, 0, -2, 0));
|
||||
if (identIcon != null)
|
||||
setGraphic(identIcon);
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
package io.bitsquare.gui.main.portfolio.pendingtrades;
|
||||
|
||||
import io.bitsquare.alert.PrivateNotificationManager;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
@ -50,6 +51,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
|
||||
private final TradeDetailsWindow tradeDetailsWindow;
|
||||
private final BSFormatter formatter;
|
||||
private PrivateNotificationManager privateNotificationManager;
|
||||
@FXML
|
||||
TableView<PendingTradesListItem> tableView;
|
||||
@FXML
|
||||
@ -70,10 +72,11 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public PendingTradesView(PendingTradesViewModel model, TradeDetailsWindow tradeDetailsWindow, BSFormatter formatter) {
|
||||
public PendingTradesView(PendingTradesViewModel model, TradeDetailsWindow tradeDetailsWindow, BSFormatter formatter, PrivateNotificationManager privateNotificationManager) {
|
||||
super(model);
|
||||
this.tradeDetailsWindow = tradeDetailsWindow;
|
||||
this.formatter = formatter;
|
||||
this.privateNotificationManager = privateNotificationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -454,7 +457,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
boolean hasTraded = numPastTrades > 0;
|
||||
String tooltipText = hasTraded ? "Trading peers onion address: " + hostName + "\n" +
|
||||
"You have already traded " + numPastTrades + " times with that peer." : "Trading peers onion address: " + hostName;
|
||||
Node identIcon = new PeerInfoIcon(hostName, tooltipText, numPastTrades);
|
||||
Node identIcon = new PeerInfoIcon(hostName, tooltipText, numPastTrades, privateNotificationManager, newItem.getTrade().getOffer());
|
||||
setPadding(new Insets(-2, 0, -2, 0));
|
||||
if (identIcon != null)
|
||||
setGraphic(identIcon);
|
||||
|
Loading…
Reference in New Issue
Block a user