add ui checks for un-authenticated node, fix broadcast issue

This commit is contained in:
Manfred Karrer 2015-10-30 00:42:20 +01:00
parent 88ab1419fa
commit 232c5b46ff
25 changed files with 329 additions and 219 deletions

View file

@ -19,7 +19,7 @@ package io.bitsquare.alert;
import com.google.inject.Inject; import com.google.inject.Inject;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.p2p.storage.HashSetChangedListener; import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.data.ProtectedData; import io.bitsquare.p2p.storage.data.ProtectedData;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
@ -59,7 +59,7 @@ public class AlertManager {
this.keyRing = keyRing; this.keyRing = keyRing;
this.user = user; this.user = user;
alertService.addHashSetChangedListener(new HashSetChangedListener() { alertService.addHashSetChangedListener(new HashMapChangedListener() {
@Override @Override
public void onAdded(ProtectedData entry) { public void onAdded(ProtectedData entry) {
Serializable data = entry.expirablePayload; Serializable data = entry.expirablePayload;

View file

@ -20,7 +20,7 @@ package io.bitsquare.alert;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashSetChangedListener; import io.bitsquare.p2p.storage.HashMapChangedListener;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -45,8 +45,8 @@ public class AlertService {
this.p2PService = p2PService; this.p2PService = p2PService;
} }
public void addHashSetChangedListener(HashSetChangedListener hashSetChangedListener) { public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) {
p2PService.addHashSetChangedListener(hashSetChangedListener); p2PService.addHashSetChangedListener(hashMapChangedListener);
} }
public void addAlertMessage(Alert alert, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) { public void addAlertMessage(Alert alert, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) {

View file

@ -26,7 +26,7 @@ import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.P2PServiceListener; import io.bitsquare.p2p.P2PServiceListener;
import io.bitsquare.p2p.storage.HashSetChangedListener; import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.data.ProtectedData; import io.bitsquare.p2p.storage.data.ProtectedData;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@ -95,7 +95,7 @@ public class ArbitratorManager {
this.arbitratorService = arbitratorService; this.arbitratorService = arbitratorService;
this.user = user; this.user = user;
arbitratorService.addHashSetChangedListener(new HashSetChangedListener() { arbitratorService.addHashSetChangedListener(new HashMapChangedListener() {
@Override @Override
public void onAdded(ProtectedData entry) { public void onAdded(ProtectedData entry) {
applyArbitrators(); applyArbitrators();

View file

@ -21,7 +21,7 @@ import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashSetChangedListener; import io.bitsquare.p2p.storage.HashMapChangedListener;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -51,8 +51,8 @@ public class ArbitratorService {
this.p2PService = p2PService; this.p2PService = p2PService;
} }
public void addHashSetChangedListener(HashSetChangedListener hashSetChangedListener) { public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) {
p2PService.addHashSetChangedListener(hashSetChangedListener); p2PService.addHashSetChangedListener(hashMapChangedListener);
} }
public void addArbitrator(Arbitrator arbitrator, final ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void addArbitrator(Arbitrator arbitrator, final ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {

View file

@ -20,7 +20,7 @@ package io.bitsquare.trade.offer;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashSetChangedListener; import io.bitsquare.p2p.storage.HashMapChangedListener;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -47,8 +47,8 @@ public class OfferBookService {
this.p2PService = p2PService; this.p2PService = p2PService;
} }
public void addHashSetChangedListener(HashSetChangedListener hashSetChangedListener) { public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) {
p2PService.addHashSetChangedListener(hashSetChangedListener); p2PService.addHashSetChangedListener(hashMapChangedListener);
} }
public void addOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void addOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {

View file

@ -34,6 +34,7 @@ import io.bitsquare.gui.common.view.guice.InjectorViewFactory;
import io.bitsquare.gui.main.MainView; import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.main.debug.DebugView; import io.bitsquare.gui.main.debug.DebugView;
import io.bitsquare.gui.popups.EmptyWalletPopup; import io.bitsquare.gui.popups.EmptyWalletPopup;
import io.bitsquare.gui.popups.Popup;
import io.bitsquare.gui.popups.SendAlertMessagePopup; import io.bitsquare.gui.popups.SendAlertMessagePopup;
import io.bitsquare.gui.util.ImageUtil; import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
@ -213,11 +214,7 @@ public class BitsquareApp extends Application {
try { try {
throwable.printStackTrace(); throwable.printStackTrace();
try { try {
Dialogs.create() new Popup().error(throwable.getMessage()).show();
.owner(primaryStage)
.title("Error")
.message("A fatal exception occurred at startup.")
.showException(throwable);
} catch (Throwable throwable3) { } catch (Throwable throwable3) {
log.error("Error at displaying Throwable."); log.error("Error at displaying Throwable.");
throwable3.printStackTrace(); throwable3.printStackTrace();

View file

@ -219,14 +219,24 @@ public class ArbitratorRegistrationView extends ActivatableViewAndModel<VBox, Ar
} }
private void onRevoke() { private void onRevoke() {
model.onRevoke( if (model.isAuthenticated()) {
() -> new Popup().information("You have successfully removed your arbitrator from the P2P network.").show(), model.onRevoke(
(errorMessage) -> new Popup().error("Could not remove arbitrator.\nError message: " + errorMessage).show()); () -> new Popup().information("You have successfully removed your arbitrator from the P2P network.").show(),
(errorMessage) -> new Popup().error("Could not remove arbitrator.\nError message: " + errorMessage).show());
} else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
"That might take up to about 2 minutes at startup.").show();
}
} }
private void onRegister() { private void onRegister() {
model.onRegister( if (model.isAuthenticated()) {
() -> new Popup().information("You have successfully registered your arbitrator to the P2P network.").show(), model.onRegister(
(errorMessage) -> new Popup().error("Could not register arbitrator.\nError message: " + errorMessage).show()); () -> new Popup().information("You have successfully registered your arbitrator to the P2P network.").show(),
(errorMessage) -> new Popup().error("Could not register arbitrator.\nError message: " + errorMessage).show());
} else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
"That might take up to about 2 minutes at startup.").show();
}
} }
} }

View file

@ -183,4 +183,8 @@ class ArbitratorRegistrationViewModel extends ActivatableViewModel {
registrationEditDisabled.set(!allDataValid || myArbitratorProperty.get() != null); registrationEditDisabled.set(!allDataValid || myArbitratorProperty.get() != null);
revokeButtonDisabled.set(!allDataValid || myArbitratorProperty.get() == null); revokeButtonDisabled.set(!allDataValid || myArbitratorProperty.get() == null);
} }
boolean isAuthenticated() {
return p2PService.isAuthenticated();
}
} }

View file

@ -30,7 +30,6 @@ import io.bitsquare.gui.popups.WalletPasswordPopup;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.locale.Country; import io.bitsquare.locale.Country;
import io.bitsquare.locale.TradeCurrency; import io.bitsquare.locale.TradeCurrency;
import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.payment.PaymentAccount; import io.bitsquare.payment.PaymentAccount;
import io.bitsquare.payment.SepaAccount; import io.bitsquare.payment.SepaAccount;
@ -51,6 +50,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* Domain for that UI element. * Domain for that UI element.
* Note that the create offer domain has a deeper scope in the application domain (TradeManager). * Note that the create offer domain has a deeper scope in the application domain (TradeManager).
@ -62,8 +63,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
private final TradeWalletService tradeWalletService; private final TradeWalletService tradeWalletService;
private final Preferences preferences; private final Preferences preferences;
private final User user; private final User user;
private final Address address;
private final KeyRing keyRing; private final KeyRing keyRing;
private P2PService p2PService;
private final WalletPasswordPopup walletPasswordPopup; private final WalletPasswordPopup walletPasswordPopup;
private final BSFormatter formatter; private final BSFormatter formatter;
private final String offerId; private final String offerId;
@ -110,10 +111,10 @@ class CreateOfferDataModel extends ActivatableDataModel {
this.preferences = preferences; this.preferences = preferences;
this.user = user; this.user = user;
this.keyRing = keyRing; this.keyRing = keyRing;
this.p2PService = p2PService;
this.walletPasswordPopup = walletPasswordPopup; this.walletPasswordPopup = walletPasswordPopup;
this.formatter = formatter; this.formatter = formatter;
address = p2PService.getAddress();
offerId = UUID.randomUUID().toString(); offerId = UUID.randomUUID().toString();
addressEntry = walletService.getAddressEntryByOfferId(offerId); addressEntry = walletService.getAddressEntryByOfferId(offerId);
offerFeeAsCoin = FeePolicy.CREATE_OFFER_FEE; offerFeeAsCoin = FeePolicy.CREATE_OFFER_FEE;
@ -199,8 +200,9 @@ class CreateOfferDataModel extends ActivatableDataModel {
// That is optional and set to null if not supported (AltCoins, OKPay,...) // That is optional and set to null if not supported (AltCoins, OKPay,...)
Country country = paymentAccount.getCountry(); Country country = paymentAccount.getCountry();
checkNotNull(p2PService.getAddress(), "Address must not be null");
return new Offer(offerId, return new Offer(offerId,
address, p2PService.getAddress(),
keyRing.getPubKeyRing(), keyRing.getPubKeyRing(),
direction, direction,
fiatPrice, fiatPrice,

View file

@ -201,17 +201,22 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private void onPlaceOffer() { private void onPlaceOffer() {
Offer offer = model.getOffer(); Offer offer = model.getOffer();
if (model.getShowPlaceOfferConfirmation()) { if (model.isAuthenticated()) {
offerDetailsPopup.onPlaceOffer(o -> model.onPlaceOffer(o)).show(offer); if (model.getShowPlaceOfferConfirmation()) {
} else { offerDetailsPopup.onPlaceOffer(o -> model.onPlaceOffer(o)).show(offer);
if (model.hasAcceptedArbitrators()) {
model.onPlaceOffer(offer);
} else { } else {
new Popup().warning("You have no arbitrator selected.\n" + if (model.hasAcceptedArbitrators()) {
"Please select at least one arbitrator.").show(); model.onPlaceOffer(offer);
} else {
new Popup().warning("You have no arbitrator selected.\n" +
"Please select at least one arbitrator.").show();
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, ArbitratorSelectionView.class); navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, ArbitratorSelectionView.class);
}
} }
} else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
"That might take up to about 2 minutes at startup.").show();
} }
} }

View file

@ -26,6 +26,7 @@ import io.bitsquare.gui.util.validation.FiatValidator;
import io.bitsquare.gui.util.validation.InputValidator; import io.bitsquare.gui.util.validation.InputValidator;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.locale.TradeCurrency; import io.bitsquare.locale.TradeCurrency;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.payment.PaymentAccount; import io.bitsquare.payment.PaymentAccount;
import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.offer.Offer;
import javafx.beans.property.*; import javafx.beans.property.*;
@ -42,6 +43,7 @@ import static javafx.beans.binding.Bindings.createStringBinding;
class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel> implements ViewModel { class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel> implements ViewModel {
private final BtcValidator btcValidator; private final BtcValidator btcValidator;
private P2PService p2PService;
private final BSFormatter formatter; private final BSFormatter formatter;
private final FiatValidator fiatValidator; private final FiatValidator fiatValidator;
@ -100,11 +102,13 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
@Inject @Inject
public CreateOfferViewModel(CreateOfferDataModel dataModel, FiatValidator fiatValidator, BtcValidator btcValidator, public CreateOfferViewModel(CreateOfferDataModel dataModel, FiatValidator fiatValidator, BtcValidator btcValidator,
P2PService p2PService,
BSFormatter formatter) { BSFormatter formatter) {
super(dataModel); super(dataModel);
this.fiatValidator = fiatValidator; this.fiatValidator = fiatValidator;
this.btcValidator = btcValidator; this.btcValidator = btcValidator;
this.p2PService = p2PService;
this.formatter = formatter; this.formatter = formatter;
paymentLabel = BSResources.get("createOffer.fundsBox.paymentLabel", dataModel.getOfferId()); paymentLabel = BSResources.get("createOffer.fundsBox.paymentLabel", dataModel.getOfferId());
@ -335,8 +339,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
if (!dataModel.isMinAmountLessOrEqualAmount()) { if (!dataModel.isMinAmountLessOrEqualAmount()) {
amountValidationResult.set(new InputValidator.ValidationResult(false, amountValidationResult.set(new InputValidator.ValidationResult(false,
BSResources.get("createOffer.validation.amountSmallerThanMinAmount"))); BSResources.get("createOffer.validation.amountSmallerThanMinAmount")));
} } else {
else {
amountValidationResult.set(result); amountValidationResult.set(result);
if (minAmount.get() != null) if (minAmount.get() != null)
minAmountValidationResult.set(isBtcInputValid(minAmount.get())); minAmountValidationResult.set(isBtcInputValid(minAmount.get()));
@ -357,8 +360,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
if (!dataModel.isMinAmountLessOrEqualAmount()) { if (!dataModel.isMinAmountLessOrEqualAmount()) {
minAmountValidationResult.set(new InputValidator.ValidationResult(false, minAmountValidationResult.set(new InputValidator.ValidationResult(false,
BSResources.get("createOffer.validation.minAmountLargerThanAmount"))); BSResources.get("createOffer.validation.minAmountLargerThanAmount")));
} } else {
else {
minAmountValidationResult.set(result); minAmountValidationResult.set(result);
if (amount.get() != null) if (amount.get() != null)
amountValidationResult.set(isBtcInputValid(amount.get())); amountValidationResult.set(isBtcInputValid(amount.get()));
@ -467,6 +469,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
return dataModel.hasAcceptedArbitrators(); return dataModel.hasAcceptedArbitrators();
} }
boolean isAuthenticated() {
return p2PService.isAuthenticated();
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Utils // Utils
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -486,8 +492,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
if (!dataModel.isMinAmountLessOrEqualAmount()) { if (!dataModel.isMinAmountLessOrEqualAmount()) {
amountValidationResult.set(new InputValidator.ValidationResult(false, amountValidationResult.set(new InputValidator.ValidationResult(false,
BSResources.get("createOffer.validation.amountSmallerThanMinAmount"))); BSResources.get("createOffer.validation.amountSmallerThanMinAmount")));
} } else {
else {
if (amount.get() != null) if (amount.get() != null)
amountValidationResult.set(isBtcInputValid(amount.get())); amountValidationResult.set(isBtcInputValid(amount.get()));
if (minAmount.get() != null) if (minAmount.get() != null)
@ -524,7 +529,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
} }
private void updateButtonDisableState() { private void updateButtonDisableState() {
isPlaceOfferButtonDisabled.set(!(isBtcInputValid(amount.get()).isValid && isPlaceOfferButtonDisabled.set(
!(isBtcInputValid(amount.get()).isValid &&
isBtcInputValid(minAmount.get()).isValid && isBtcInputValid(minAmount.get()).isValid &&
isFiatInputValid(price.get()).isValid && isFiatInputValid(price.get()).isValid &&
isFiatInputValid(volume.get()).isValid && isFiatInputValid(volume.get()).isValid &&
@ -544,4 +550,5 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
public boolean getShowPlaceOfferConfirmation() { public boolean getShowPlaceOfferConfirmation() {
return dataModel.getShowPlaceOfferConfirmation(); return dataModel.getShowPlaceOfferConfirmation();
} }
} }

View file

@ -17,7 +17,7 @@
package io.bitsquare.gui.main.offer.offerbook; package io.bitsquare.gui.main.offer.offerbook;
import io.bitsquare.p2p.storage.HashSetChangedListener; import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.data.ProtectedData; import io.bitsquare.p2p.storage.data.ProtectedData;
import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.offer.Offer;
@ -53,7 +53,7 @@ public class OfferBook {
@Inject @Inject
OfferBook(OfferBookService offerBookService, TradeManager tradeManager) { OfferBook(OfferBookService offerBookService, TradeManager tradeManager) {
this.offerBookService = offerBookService; this.offerBookService = offerBookService;
offerBookService.addHashSetChangedListener(new HashSetChangedListener() { offerBookService.addHashSetChangedListener(new HashMapChangedListener() {
@Override @Override
public void onAdded(ProtectedData entry) { public void onAdded(ProtectedData entry) {
log.debug("onAdded " + entry); log.debug("onAdded " + entry);

View file

@ -26,6 +26,21 @@ public class OfferBookListItem {
this.offer = offer; this.offer = offer;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof OfferBookListItem)) return false;
OfferBookListItem that = (OfferBookListItem) o;
return !(offer != null ? !offer.equals(that.offer) : that.offer != null);
}
@Override
public int hashCode() {
return offer != null ? offer.hashCode() : 0;
}
public Offer getOffer() { public Offer getOffer() {
return offer; return offer;
} }

View file

@ -211,8 +211,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
showWarning("You don't have an arbitrator selected.", showWarning("You don't have an arbitrator selected.",
"You need to setup at least one arbitrator to be able to trade.\n" + "You need to setup at least one arbitrator to be able to trade.\n" +
"Do you want to do this now?", ArbitratorSelectionView.class); "Do you want to do this now?", ArbitratorSelectionView.class);
} } else {
else {
createOfferButton.setDisable(true); createOfferButton.setDisable(true);
offerActionHandler.onCreateOffer(model.getTradeCurrency()); offerActionHandler.onCreateOffer(model.getTradeCurrency());
} }
@ -231,21 +230,30 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
} }
private void onTakeOffer(Offer offer) { private void onTakeOffer(Offer offer) {
offerActionHandler.onTakeOffer(offer); if (model.isAuthenticated())
offerActionHandler.onTakeOffer(offer);
else
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
"That might take up to about 2 minutes at startup.").show();
} }
private void onRemoveOpenOffer(Offer offer) { private void onRemoveOpenOffer(Offer offer) {
model.onRemoveOpenOffer(offer, if (model.isAuthenticated()) {
() -> { model.onRemoveOpenOffer(offer,
log.debug("Remove offer was successful"); () -> {
new Popup().information("You can withdraw the funds you paid in from the funds screens.").show(); log.debug("Remove offer was successful");
navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class); new Popup().information("You can withdraw the funds you paid in from the funds screens.").show();
}, navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class);
(message) -> { },
log.error(message); (message) -> {
new Popup().warning("Remove offer failed:\n" + message).show(); log.error(message);
}); new Popup().warning("Remove offer failed:\n" + message).show();
});
} else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
"That might take up to about 2 minutes at startup.").show();
}
} }
private void showWarning(String masthead, String message, Class target) { private void showWarning(String masthead, String message, Class target) {
@ -446,8 +454,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
title = model.getDirectionLabel(offer); title = model.getDirectionLabel(offer);
button.setOnAction(e -> onTakeOffer(offer)); button.setOnAction(e -> onTakeOffer(offer));
} }
} } else {
else {
title = "Not matching"; title = "Not matching";
iconView.setId(null); iconView.setId(null);
button.setOnAction(e -> onShowInfo(isPaymentAccountValidForOffer, hasMatchingArbitrator)); button.setOnAction(e -> onShowInfo(isPaymentAccountValidForOffer, hasMatchingArbitrator));
@ -455,8 +462,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
button.setText(title); button.setText(title);
setGraphic(button); setGraphic(button);
} } else {
else {
setGraphic(null); setGraphic(null);
TableRow tableRow = getTableRow(); TableRow tableRow = getTableRow();
if (tableRow != null) tableRow.setOpacity(1); if (tableRow != null) tableRow.setOpacity(1);

View file

@ -27,6 +27,7 @@ import io.bitsquare.locale.CountryUtil;
import io.bitsquare.locale.CurrencyUtil; import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.locale.TradeCurrency; import io.bitsquare.locale.TradeCurrency;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.payment.PaymentMethod; import io.bitsquare.payment.PaymentMethod;
import io.bitsquare.payment.SepaAccount; import io.bitsquare.payment.SepaAccount;
import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.offer.Offer;
@ -49,6 +50,7 @@ class OfferBookViewModel extends ActivatableViewModel {
private final User user; private final User user;
private final OfferBook offerBook; private final OfferBook offerBook;
private final Preferences preferences; private final Preferences preferences;
private final P2PService p2PService;
private final BSFormatter formatter; private final BSFormatter formatter;
private final FilteredList<OfferBookListItem> filteredItems; private final FilteredList<OfferBookListItem> filteredItems;
@ -69,7 +71,7 @@ class OfferBookViewModel extends ActivatableViewModel {
@Inject @Inject
public OfferBookViewModel(User user, OpenOfferManager openOfferManager, OfferBook offerBook, public OfferBookViewModel(User user, OpenOfferManager openOfferManager, OfferBook offerBook,
Preferences preferences, Preferences preferences, P2PService p2PService,
BSFormatter formatter) { BSFormatter formatter) {
super(); super();
@ -77,6 +79,7 @@ class OfferBookViewModel extends ActivatableViewModel {
this.user = user; this.user = user;
this.offerBook = offerBook; this.offerBook = offerBook;
this.preferences = preferences; this.preferences = preferences;
this.p2PService = p2PService;
this.formatter = formatter; this.formatter = formatter;
offerBookListItems = offerBook.getOfferBookListItems(); offerBookListItems = offerBook.getOfferBookListItems();
@ -155,6 +158,10 @@ class OfferBookViewModel extends ActivatableViewModel {
return list; return list;
} }
boolean isAuthenticated() {
return p2PService.isAuthenticated();
}
public TradeCurrency getTradeCurrency() { public TradeCurrency getTradeCurrency() {
return tradeCurrency; return tradeCurrency;
} }

View file

@ -75,4 +75,6 @@ class OpenOffersDataModel extends ActivatableDataModel {
// we sort by date, earliest first // we sort by date, earliest first
list.sort((o1, o2) -> o2.getOffer().getDate().compareTo(o1.getOffer().getDate())); list.sort((o1, o2) -> o2.getOffer().getDate().compareTo(o1.getOffer().getDate()));
} }
} }

View file

@ -38,8 +38,10 @@ import javax.inject.Inject;
@FxmlView @FxmlView
public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersViewModel> { public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersViewModel> {
@FXML TableView<OpenOfferListItem> table; @FXML
@FXML TableColumn<OpenOfferListItem, OpenOfferListItem> priceColumn, amountColumn, volumeColumn, TableView<OpenOfferListItem> table;
@FXML
TableColumn<OpenOfferListItem, OpenOfferListItem> priceColumn, amountColumn, volumeColumn,
directionColumn, dateColumn, offerIdColumn, removeItemColumn; directionColumn, dateColumn, offerIdColumn, removeItemColumn;
private final Navigation navigation; private final Navigation navigation;
private final OfferDetailsPopup offerDetailsPopup; private final OfferDetailsPopup offerDetailsPopup;
@ -71,18 +73,22 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
} }
private void onCancelOpenOffer(OpenOffer openOffer) { private void onCancelOpenOffer(OpenOffer openOffer) {
model.onCancelOpenOffer(openOffer, if (model.isAuthenticated()) {
() -> { model.onCancelOpenOffer(openOffer,
log.debug("Remove offer was successful"); () -> {
new Popup().information("You can withdraw the funds you paid in from the funds screens.") log.debug("Remove offer was successful");
.onClose(() -> navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class)) new Popup().information("You can withdraw the funds you paid in from the funds screens.")
.show(); .onClose(() -> navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class))
}, .show();
(message) -> { },
log.error(message); (message) -> {
new Popup().warning("Remove offer failed:\n" + message).show(); log.error(message);
}); new Popup().warning("Remove offer failed:\n" + message).show();
});
} else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
"That might take up to about 2 minutes at startup.").show();
}
} }
/* private void openOfferDetails(OpenOfferListItem item) { /* private void openOfferDetails(OpenOfferListItem item) {
@ -144,8 +150,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
Tooltip.install(hyperlink, new Tooltip(model.getTradeId(item))); Tooltip.install(hyperlink, new Tooltip(model.getTradeId(item)));
hyperlink.setOnAction(event -> offerDetailsPopup.show(item.getOffer())); hyperlink.setOnAction(event -> offerDetailsPopup.show(item.getOffer()));
setGraphic(hyperlink); setGraphic(hyperlink);
} } else {
else {
setGraphic(null); setGraphic(null);
setId(null); setId(null);
} }
@ -281,8 +286,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
if (item != null) { if (item != null) {
button.setOnAction(event -> onCancelOpenOffer(item.getOpenOffer())); button.setOnAction(event -> onCancelOpenOffer(item.getOpenOffer()));
setGraphic(button); setGraphic(button);
} } else {
else {
setGraphic(null); setGraphic(null);
} }
} }

View file

@ -23,16 +23,19 @@ import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.gui.common.model.ActivatableWithDataModel; import io.bitsquare.gui.common.model.ActivatableWithDataModel;
import io.bitsquare.gui.common.model.ViewModel; import io.bitsquare.gui.common.model.ViewModel;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.trade.offer.OpenOffer; import io.bitsquare.trade.offer.OpenOffer;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel> implements ViewModel { class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel> implements ViewModel {
private P2PService p2PService;
private final BSFormatter formatter; private final BSFormatter formatter;
@Inject @Inject
public OpenOffersViewModel(OpenOffersDataModel dataModel, BSFormatter formatter) { public OpenOffersViewModel(OpenOffersDataModel dataModel, P2PService p2PService, BSFormatter formatter) {
super(dataModel); super(dataModel);
this.p2PService = p2PService;
this.formatter = formatter; this.formatter = formatter;
} }
@ -70,4 +73,7 @@ class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel>
return formatter.formatDateTime(item.getOffer().getDate()); return formatter.formatDateTime(item.getOffer().getDate());
} }
boolean isAuthenticated() {
return p2PService.isAuthenticated();
}
} }

View file

@ -24,6 +24,7 @@ import io.bitsquare.gui.common.model.ViewModel;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.gui.util.validation.*; import io.bitsquare.gui.util.validation.*;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.payment.PaymentMethod; import io.bitsquare.payment.PaymentMethod;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import javafx.beans.property.*; import javafx.beans.property.*;
@ -72,6 +73,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
private final InputValidator inputValidator; private final InputValidator inputValidator;
private final OKPayValidator okPayValidator; private final OKPayValidator okPayValidator;
private final AltCoinAddressValidator altCoinAddressValidator; private final AltCoinAddressValidator altCoinAddressValidator;
private P2PService p2PService;
private final ObjectProperty<BuyerState> buyerState = new SimpleObjectProperty<>(PendingTradesViewModel.BuyerState.UNDEFINED); private final ObjectProperty<BuyerState> buyerState = new SimpleObjectProperty<>(PendingTradesViewModel.BuyerState.UNDEFINED);
private final ObjectProperty<SellerState> sellerState = new SimpleObjectProperty<>(UNDEFINED); private final ObjectProperty<SellerState> sellerState = new SimpleObjectProperty<>(UNDEFINED);
@ -92,7 +94,8 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
BICValidator bicValidator, BICValidator bicValidator,
InputValidator inputValidator, InputValidator inputValidator,
OKPayValidator okPayValidator, OKPayValidator okPayValidator,
AltCoinAddressValidator altCoinAddressValidator AltCoinAddressValidator altCoinAddressValidator,
P2PService p2PService
) { ) {
super(dataModel); super(dataModel);
@ -103,6 +106,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
this.inputValidator = inputValidator; this.inputValidator = inputValidator;
this.okPayValidator = okPayValidator; this.okPayValidator = okPayValidator;
this.altCoinAddressValidator = altCoinAddressValidator; this.altCoinAddressValidator = altCoinAddressValidator;
this.p2PService = p2PService;
} }
@Override @Override
@ -215,6 +219,10 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
return btcAddressValidator; return btcAddressValidator;
} }
public boolean isAuthenticated() {
return p2PService.isAuthenticated();
}
// columns // columns
String formatTradeId(String value) { String formatTradeId(String value) {
return value; return value;

View file

@ -19,6 +19,7 @@ package io.bitsquare.gui.main.portfolio.pendingtrades.steps;
import io.bitsquare.gui.components.TxIdTextField; import io.bitsquare.gui.components.TxIdTextField;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel; import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.popups.Popup;
import io.bitsquare.gui.util.Layout; import io.bitsquare.gui.util.Layout;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
@ -103,13 +104,18 @@ public class ConfirmPaymentReceivedView extends TradeStepDetailsView {
private void onPaymentReceived(ActionEvent actionEvent) { private void onPaymentReceived(ActionEvent actionEvent) {
log.debug("onPaymentReceived"); log.debug("onPaymentReceived");
confirmFiatReceivedButton.setDisable(true); if (model.isAuthenticated()) {
confirmFiatReceivedButton.setDisable(true);
statusProgressIndicator.setVisible(true); statusProgressIndicator.setVisible(true);
statusProgressIndicator.setProgress(-1); statusProgressIndicator.setProgress(-1);
statusLabel.setText("Sending message to trading partner..."); statusLabel.setText("Sending message to trading partner...");
model.fiatPaymentReceived(); model.fiatPaymentReceived();
} else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
"That might take up to about 2 minutes at startup.").show();
}
} }

View file

@ -22,6 +22,7 @@ import io.bitsquare.gui.components.TitledGroupBg;
import io.bitsquare.gui.components.TxIdTextField; import io.bitsquare.gui.components.TxIdTextField;
import io.bitsquare.gui.components.paymentmethods.*; import io.bitsquare.gui.components.paymentmethods.*;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel; import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.popups.Popup;
import io.bitsquare.gui.util.Layout; import io.bitsquare.gui.util.Layout;
import io.bitsquare.payment.PaymentAccountContractData; import io.bitsquare.payment.PaymentAccountContractData;
import io.bitsquare.payment.PaymentMethod; import io.bitsquare.payment.PaymentMethod;
@ -124,13 +125,18 @@ public class StartPaymentView extends TradeStepDetailsView {
private void onPaymentStarted(ActionEvent actionEvent) { private void onPaymentStarted(ActionEvent actionEvent) {
log.debug("onPaymentStarted"); log.debug("onPaymentStarted");
paymentStartedButton.setDisable(true); if (model.isAuthenticated()) {
paymentStartedButton.setDisable(true);
statusProgressIndicator.setVisible(true); statusProgressIndicator.setVisible(true);
statusProgressIndicator.setProgress(-1); statusProgressIndicator.setProgress(-1);
statusLabel.setText("Sending message to trading partner..."); statusLabel.setText("Sending message to trading partner...");
model.fiatPaymentStarted(); model.fiatPaymentStarted();
} else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
"That might take up to about 2 minutes at startup.").show();
}
} }

View file

@ -17,7 +17,7 @@ import io.bitsquare.p2p.routing.Neighbor;
import io.bitsquare.p2p.routing.Routing; import io.bitsquare.p2p.routing.Routing;
import io.bitsquare.p2p.routing.RoutingListener; import io.bitsquare.p2p.routing.RoutingListener;
import io.bitsquare.p2p.seed.SeedNodesRepository; import io.bitsquare.p2p.seed.SeedNodesRepository;
import io.bitsquare.p2p.storage.HashSetChangedListener; import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.ProtectedExpirableDataStorage; import io.bitsquare.p2p.storage.ProtectedExpirableDataStorage;
import io.bitsquare.p2p.storage.data.ExpirableMailboxPayload; import io.bitsquare.p2p.storage.data.ExpirableMailboxPayload;
import io.bitsquare.p2p.storage.data.ExpirablePayload; import io.bitsquare.p2p.storage.data.ExpirablePayload;
@ -48,6 +48,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
public class P2PService { public class P2PService {
private static final Logger log = LoggerFactory.getLogger(P2PService.class); private static final Logger log = LoggerFactory.getLogger(P2PService.class);
@Nullable
private final EncryptionService encryptionService; private final EncryptionService encryptionService;
private final SetupListener setupListener; private final SetupListener setupListener;
private KeyRing keyRing; private KeyRing keyRing;
@ -84,7 +85,7 @@ public class P2PService {
@Named(ProgramArguments.PORT_KEY) int port, @Named(ProgramArguments.PORT_KEY) int port,
@Named(ProgramArguments.TOR_DIR) File torDir, @Named(ProgramArguments.TOR_DIR) File torDir,
@Named(ProgramArguments.USE_LOCALHOST) boolean useLocalhost, @Named(ProgramArguments.USE_LOCALHOST) boolean useLocalhost,
EncryptionService encryptionService, @Nullable EncryptionService encryptionService,
KeyRing keyRing, KeyRing keyRing,
@Named("storage.dir") File storageDir) { @Named("storage.dir") File storageDir) {
this.encryptionService = encryptionService; this.encryptionService = encryptionService;
@ -142,7 +143,27 @@ public class P2PService {
@Override @Override
public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) { public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) {
authenticatedPeerAddresses.add(peerAddress); authenticatedPeerAddresses.add(peerAddress);
authenticatedToFirstPeer = true;
if (!authenticatedToFirstPeer) {
authenticatedToFirstPeer = true;
Address address = connection.getPeerAddress();
SettableFuture<Connection> future = sendMessage(address,
new GetDataSetMessage(addToListAndGetNonce()));
Futures.addCallback(future, new FutureCallback<Connection>() {
@Override
public void onSuccess(@Nullable Connection connection) {
log.info("onPeerAddressAuthenticated Send GetAllDataMessage to " + address + " succeeded.");
connectedSeedNodes.add(address);
}
@Override
public void onFailure(Throwable throwable) {
log.warn("onPeerAddressAuthenticated Send GetAllDataMessage to " + address + " failed. " +
"Exception:" + throwable.getMessage());
}
});
}
P2PService.this.authenticated = true; P2PService.this.authenticated = true;
dataStorage.setAuthenticated(true); dataStorage.setAuthenticated(true);
@ -182,15 +203,18 @@ public class P2PService {
HashSet<ProtectedData> set = ((DataSetMessage) message).set; HashSet<ProtectedData> set = ((DataSetMessage) message).set;
set.stream().forEach(e -> dataStorage.add(e, connection.getPeerAddress())); set.stream().forEach(e -> dataStorage.add(e, connection.getPeerAddress()));
set.stream().filter(e -> e instanceof ProtectedMailboxData).forEach(e -> tryDecryptMailboxData((ProtectedMailboxData) e)); // TODO done in addHashSetChangedListener
// set.stream().filter(e -> e instanceof ProtectedMailboxData).forEach(e -> tryDecryptMailboxData((ProtectedMailboxData) e));
dataReceived(); dataReceived();
} else if (message instanceof SealedAndSignedMessage) { } else if (message instanceof SealedAndSignedMessage) {
try { if (encryptionService != null) {
DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerifyMessage((SealedAndSignedMessage) message); try {
UserThread.execute(() -> decryptedMailListeners.stream().forEach(e -> e.onMailMessage(decryptedMessageWithPubKey, connection.getPeerAddress()))); DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerifyMessage((SealedAndSignedMessage) message);
} catch (CryptoException e) { UserThread.execute(() -> decryptedMailListeners.stream().forEach(e -> e.onMailMessage(decryptedMessageWithPubKey, connection.getPeerAddress())));
log.info("Decryption of SealedAndSignedMessage failed. That is expected if the message is not intended for us."); } catch (CryptoException e) {
log.info("Decryption of SealedAndSignedMessage failed. That is expected if the message is not intended for us.");
}
} }
} }
}); });
@ -217,7 +241,7 @@ public class P2PService {
} }
}); });
dataStorage.addHashSetChangedListener(new HashSetChangedListener() { dataStorage.addHashMapChangedListener(new HashMapChangedListener() {
@Override @Override
public void onAdded(ProtectedData entry) { public void onAdded(ProtectedData entry) {
if (entry instanceof ProtectedMailboxData) if (entry instanceof ProtectedMailboxData)
@ -317,24 +341,26 @@ public class P2PService {
} }
private void doSendEncryptedMailMessage(Address peerAddress, PubKeyRing pubKeyRing, MailMessage message, SendMailMessageListener sendMailMessageListener) { private void doSendEncryptedMailMessage(Address peerAddress, PubKeyRing pubKeyRing, MailMessage message, SendMailMessageListener sendMailMessageListener) {
try { if (encryptionService != null) {
SealedAndSignedMessage sealedAndSignedMessage = encryptionService.encryptAndSignMessage(pubKeyRing, message); try {
SettableFuture<Connection> future = sendMessage(peerAddress, sealedAndSignedMessage); SealedAndSignedMessage sealedAndSignedMessage = encryptionService.encryptAndSignMessage(pubKeyRing, message);
Futures.addCallback(future, new FutureCallback<Connection>() { SettableFuture<Connection> future = sendMessage(peerAddress, sealedAndSignedMessage);
@Override Futures.addCallback(future, new FutureCallback<Connection>() {
public void onSuccess(@Nullable Connection connection) { @Override
UserThread.execute(() -> sendMailMessageListener.onArrived()); public void onSuccess(@Nullable Connection connection) {
} UserThread.execute(() -> sendMailMessageListener.onArrived());
}
@Override @Override
public void onFailure(Throwable throwable) { public void onFailure(Throwable throwable) {
throwable.printStackTrace(); throwable.printStackTrace();
UserThread.execute(() -> sendMailMessageListener.onFault()); UserThread.execute(() -> sendMailMessageListener.onFault());
} }
}); });
} catch (CryptoException e) { } catch (CryptoException e) {
e.printStackTrace(); e.printStackTrace();
UserThread.execute(() -> sendMailMessageListener.onFault()); UserThread.execute(() -> sendMailMessageListener.onFault());
}
} }
} }
@ -358,34 +384,36 @@ public class P2PService {
} }
private void trySendEncryptedMailboxMessage(Address peerAddress, PubKeyRing peersPubKeyRing, MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) { private void trySendEncryptedMailboxMessage(Address peerAddress, PubKeyRing peersPubKeyRing, MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) {
try { if (encryptionService != null) {
SealedAndSignedMessage sealedAndSignedMessage = encryptionService.encryptAndSignMessage(peersPubKeyRing, message); try {
SettableFuture<Connection> future = sendMessage(peerAddress, sealedAndSignedMessage); SealedAndSignedMessage sealedAndSignedMessage = encryptionService.encryptAndSignMessage(peersPubKeyRing, message);
Futures.addCallback(future, new FutureCallback<Connection>() { SettableFuture<Connection> future = sendMessage(peerAddress, sealedAndSignedMessage);
@Override Futures.addCallback(future, new FutureCallback<Connection>() {
public void onSuccess(@Nullable Connection connection) { @Override
log.trace("SendEncryptedMailboxMessage onSuccess"); public void onSuccess(@Nullable Connection connection) {
UserThread.execute(() -> sendMailboxMessageListener.onArrived()); log.trace("SendEncryptedMailboxMessage onSuccess");
} UserThread.execute(() -> sendMailboxMessageListener.onArrived());
}
@Override @Override
public void onFailure(Throwable throwable) { public void onFailure(Throwable throwable) {
log.trace("SendEncryptedMailboxMessage onFailure"); log.trace("SendEncryptedMailboxMessage onFailure");
log.debug(throwable.toString()); log.debug(throwable.toString());
log.info("We cannot send message to peer. Peer might be offline. We will store message in mailbox."); log.info("We cannot send message to peer. Peer might be offline. We will store message in mailbox.");
log.trace("create MailboxEntry with peerAddress " + peerAddress); log.trace("create MailboxEntry with peerAddress " + peerAddress);
PublicKey receiverStoragePublicKey = peersPubKeyRing.getStorageSignaturePubKey(); PublicKey receiverStoragePublicKey = peersPubKeyRing.getStorageSignaturePubKey();
addMailboxData(new ExpirableMailboxPayload(sealedAndSignedMessage, addMailboxData(new ExpirableMailboxPayload(sealedAndSignedMessage,
keyRing.getStorageSignatureKeyPair().getPublic(), keyRing.getStorageSignatureKeyPair().getPublic(),
receiverStoragePublicKey), receiverStoragePublicKey),
receiverStoragePublicKey); receiverStoragePublicKey);
UserThread.execute(() -> sendMailboxMessageListener.onStoredInMailbox()); UserThread.execute(() -> sendMailboxMessageListener.onStoredInMailbox());
} }
}); });
} catch (CryptoException e) { } catch (CryptoException e) {
e.printStackTrace(); e.printStackTrace();
log.error("sendEncryptedMessage failed"); log.error("sendEncryptedMessage failed");
UserThread.execute(() -> sendMailboxMessageListener.onFault()); UserThread.execute(() -> sendMailboxMessageListener.onFault());
}
} }
} }
@ -481,8 +509,8 @@ public class P2PService {
p2pServiceListeners.remove(listener); p2pServiceListeners.remove(listener);
} }
public void addHashSetChangedListener(HashSetChangedListener hashSetChangedListener) { public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) {
dataStorage.addHashSetChangedListener(hashSetChangedListener); dataStorage.addHashMapChangedListener(hashMapChangedListener);
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -598,29 +626,31 @@ public class P2PService {
} }
private void tryDecryptMailboxData(ProtectedMailboxData mailboxData) { private void tryDecryptMailboxData(ProtectedMailboxData mailboxData) {
ExpirablePayload data = mailboxData.expirablePayload; if (encryptionService != null) {
if (data instanceof ExpirableMailboxPayload) { ExpirablePayload data = mailboxData.expirablePayload;
ExpirableMailboxPayload mailboxEntry = (ExpirableMailboxPayload) data; if (data instanceof ExpirableMailboxPayload) {
SealedAndSignedMessage sealedAndSignedMessage = mailboxEntry.sealedAndSignedMessage; ExpirableMailboxPayload mailboxEntry = (ExpirableMailboxPayload) data;
try { SealedAndSignedMessage sealedAndSignedMessage = mailboxEntry.sealedAndSignedMessage;
DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerifyMessage(sealedAndSignedMessage); try {
if (decryptedMessageWithPubKey.message instanceof MailboxMessage) { DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerifyMessage(sealedAndSignedMessage);
MailboxMessage mailboxMessage = (MailboxMessage) decryptedMessageWithPubKey.message; if (decryptedMessageWithPubKey.message instanceof MailboxMessage) {
Address senderAddress = mailboxMessage.getSenderAddress(); MailboxMessage mailboxMessage = (MailboxMessage) decryptedMessageWithPubKey.message;
checkNotNull(senderAddress, "senderAddress must not be null for mailbox messages"); Address senderAddress = mailboxMessage.getSenderAddress();
checkNotNull(senderAddress, "senderAddress must not be null for mailbox messages");
log.trace("mailboxData.publicKey " + mailboxData.ownerStoragePubKey.hashCode()); log.trace("mailboxData.publicKey " + mailboxData.ownerStoragePubKey.hashCode());
log.trace("keyRing.getStorageSignatureKeyPair().getPublic() " + keyRing.getStorageSignatureKeyPair().getPublic().hashCode()); log.trace("keyRing.getStorageSignatureKeyPair().getPublic() " + keyRing.getStorageSignatureKeyPair().getPublic().hashCode());
log.trace("keyRing.getMsgSignatureKeyPair().getPublic() " + keyRing.getMsgSignatureKeyPair().getPublic().hashCode()); log.trace("keyRing.getMsgSignatureKeyPair().getPublic() " + keyRing.getMsgSignatureKeyPair().getPublic().hashCode());
log.trace("keyRing.getMsgEncryptionKeyPair().getPublic() " + keyRing.getMsgEncryptionKeyPair().getPublic().hashCode()); log.trace("keyRing.getMsgEncryptionKeyPair().getPublic() " + keyRing.getMsgEncryptionKeyPair().getPublic().hashCode());
mailboxMap.put(decryptedMessageWithPubKey, mailboxData); mailboxMap.put(decryptedMessageWithPubKey, mailboxData);
log.trace("Decryption of SealedAndSignedMessage succeeded. senderAddress=" + senderAddress + " / my address=" + getAddress()); log.trace("Decryption of SealedAndSignedMessage succeeded. senderAddress=" + senderAddress + " / my address=" + getAddress());
UserThread.execute(() -> decryptedMailboxListeners.stream().forEach(e -> e.onMailboxMessageAdded(decryptedMessageWithPubKey, senderAddress))); UserThread.execute(() -> decryptedMailboxListeners.stream().forEach(e -> e.onMailboxMessageAdded(decryptedMessageWithPubKey, senderAddress)));
}
} catch (CryptoException e) {
log.trace("Decryption of SealedAndSignedMessage failed. That is expected if the message is not intended for us.");
} }
} catch (CryptoException e) {
log.trace("Decryption of SealedAndSignedMessage failed. That is expected if the message is not intended for us.");
} }
} }
} }

View file

@ -2,7 +2,7 @@ package io.bitsquare.p2p.storage;
import io.bitsquare.p2p.storage.data.ProtectedData; import io.bitsquare.p2p.storage.data.ProtectedData;
public interface HashSetChangedListener { public interface HashMapChangedListener {
void onAdded(ProtectedData entry); void onAdded(ProtectedData entry);
void onRemoved(ProtectedData entry); void onRemoved(ProtectedData entry);

View file

@ -31,7 +31,7 @@ public class ProtectedExpirableDataStorage {
private final Routing routing; private final Routing routing;
private final Map<BigInteger, ProtectedData> map = new ConcurrentHashMap<>(); private final Map<BigInteger, ProtectedData> map = new ConcurrentHashMap<>();
private final List<HashSetChangedListener> hashSetChangedListeners = new CopyOnWriteArrayList<>(); private final List<HashMapChangedListener> hashMapChangedListeners = new CopyOnWriteArrayList<>();
private ConcurrentHashMap<BigInteger, Integer> sequenceNumberMap = new ConcurrentHashMap<>(); private ConcurrentHashMap<BigInteger, Integer> sequenceNumberMap = new ConcurrentHashMap<>();
private final Storage<ConcurrentHashMap> storage; private final Storage<ConcurrentHashMap> storage;
private boolean authenticated; private boolean authenticated;
@ -104,12 +104,26 @@ public class ProtectedExpirableDataStorage {
BigInteger hashOfPayload = getHashAsBigInteger(protectedData.expirablePayload); BigInteger hashOfPayload = getHashAsBigInteger(protectedData.expirablePayload);
boolean containsKey = map.containsKey(hashOfPayload); boolean containsKey = map.containsKey(hashOfPayload);
boolean result = checkPublicKeys(protectedData, true) boolean result = checkPublicKeys(protectedData, true)
&& isSequenceNrValid(protectedData, hashOfPayload) && checkSignature(protectedData);
&& checkSignature(protectedData)
&& (!containsKey || checkIfStoredDataMatchesNewData(protectedData, hashOfPayload)) if (containsKey) {
&& doAddProtectedExpirableData(protectedData, hashOfPayload, sender); result &= checkIfStoredDataMatchesNewData(protectedData, hashOfPayload)
&& isSequenceNrValid(protectedData, hashOfPayload);
}
if (result) { if (result) {
map.put(hashOfPayload, protectedData);
log.trace("Data added to our map and it will be broadcasted to our neighbors.");
UserThread.execute(() -> hashMapChangedListeners.stream().forEach(e -> e.onAdded(protectedData)));
StringBuilder sb = new StringBuilder("\n\nSet after addProtectedExpirableData:\n");
map.values().stream().forEach(e -> sb.append(e.toString() + "\n\n"));
sb.append("\n\n");
log.trace(sb.toString());
if (!containsKey)
broadcast(new AddDataMessage(protectedData), sender);
sequenceNumberMap.put(hashOfPayload, protectedData.sequenceNumber); sequenceNumberMap.put(hashOfPayload, protectedData.sequenceNumber);
storage.queueUpForSave(); storage.queueUpForSave();
} else { } else {
@ -126,10 +140,14 @@ public class ProtectedExpirableDataStorage {
&& checkPublicKeys(protectedData, false) && checkPublicKeys(protectedData, false)
&& isSequenceNrValid(protectedData, hashOfPayload) && isSequenceNrValid(protectedData, hashOfPayload)
&& checkSignature(protectedData) && checkSignature(protectedData)
&& checkIfStoredDataMatchesNewData(protectedData, hashOfPayload) && checkIfStoredDataMatchesNewData(protectedData, hashOfPayload);
&& doRemoveProtectedExpirableData(protectedData, hashOfPayload, sender);
if (result) { if (result) {
doRemoveProtectedExpirableData(protectedData, hashOfPayload);
broadcast(new RemoveDataMessage(protectedData), sender);
sequenceNumberMap.put(hashOfPayload, protectedData.sequenceNumber); sequenceNumberMap.put(hashOfPayload, protectedData.sequenceNumber);
storage.queueUpForSave(); storage.queueUpForSave();
} else { } else {
@ -147,10 +165,13 @@ public class ProtectedExpirableDataStorage {
&& isSequenceNrValid(protectedMailboxData, hashOfData) && isSequenceNrValid(protectedMailboxData, hashOfData)
&& protectedMailboxData.receiversPubKey.equals(protectedMailboxData.ownerStoragePubKey) // at remove both keys are the same (only receiver is able to remove data) && protectedMailboxData.receiversPubKey.equals(protectedMailboxData.ownerStoragePubKey) // at remove both keys are the same (only receiver is able to remove data)
&& checkSignature(protectedMailboxData) && checkSignature(protectedMailboxData)
&& checkIfStoredMailboxDataMatchesNewMailboxData(protectedMailboxData, hashOfData) && checkIfStoredMailboxDataMatchesNewMailboxData(protectedMailboxData, hashOfData);
&& doRemoveProtectedExpirableData(protectedMailboxData, hashOfData, sender);
if (result) { if (result) {
doRemoveProtectedExpirableData(protectedMailboxData, hashOfData);
broadcast(new RemoveMailboxDataMessage(protectedMailboxData), sender);
sequenceNumberMap.put(hashOfData, protectedMailboxData.sequenceNumber); sequenceNumberMap.put(hashOfData, protectedMailboxData.sequenceNumber);
storage.queueUpForSave(); storage.queueUpForSave();
} else { } else {
@ -190,8 +211,8 @@ public class ProtectedExpirableDataStorage {
return new ProtectedMailboxData(expirableMailboxPayload, expirableMailboxPayload.getTTL(), storageSignaturePubKey.getPublic(), sequenceNumber, signature, receiversPublicKey); return new ProtectedMailboxData(expirableMailboxPayload, expirableMailboxPayload.getTTL(), storageSignaturePubKey.getPublic(), sequenceNumber, signature, receiversPublicKey);
} }
public void addHashSetChangedListener(HashSetChangedListener hashSetChangedListener) { public void addHashMapChangedListener(HashMapChangedListener hashMapChangedListener) {
hashSetChangedListeners.add(hashSetChangedListener); hashMapChangedListeners.add(hashMapChangedListener);
} }
public void addMessageListener(MessageListener messageListener) { public void addMessageListener(MessageListener messageListener) {
@ -203,11 +224,22 @@ public class ProtectedExpirableDataStorage {
// Private // Private
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void doRemoveProtectedExpirableData(ProtectedData protectedData, BigInteger hashOfPayload) {
map.remove(hashOfPayload);
log.trace("Data removed from our map. We broadcast the message to our neighbors.");
UserThread.execute(() -> hashMapChangedListeners.stream().forEach(e -> e.onRemoved(protectedData)));
StringBuilder sb = new StringBuilder("\n\nSet after removeProtectedExpirableData:\n");
map.values().stream().forEach(e -> sb.append(e.toString() + "\n\n"));
sb.append("\n\n");
log.trace(sb.toString());
}
private boolean isSequenceNrValid(ProtectedData data, BigInteger hashOfData) { private boolean isSequenceNrValid(ProtectedData data, BigInteger hashOfData) {
int newSequenceNumber = data.sequenceNumber; int newSequenceNumber = data.sequenceNumber;
Integer storedSequenceNumber = sequenceNumberMap.get(hashOfData); Integer storedSequenceNumber = sequenceNumberMap.get(hashOfData);
if (sequenceNumberMap.containsKey(hashOfData) && newSequenceNumber <= storedSequenceNumber) { if (sequenceNumberMap.containsKey(hashOfData) && newSequenceNumber <= storedSequenceNumber) {
log.warn("Sequence number is invalid. That might happen in rare cases. newSequenceNumber=" log.warn("Sequence number is invalid. newSequenceNumber="
+ newSequenceNumber + " / storedSequenceNumber=" + storedSequenceNumber); + newSequenceNumber + " / storedSequenceNumber=" + storedSequenceNumber);
return false; return false;
} else { } else {
@ -274,34 +306,6 @@ public class ProtectedExpirableDataStorage {
} }
} }
private boolean doAddProtectedExpirableData(ProtectedData data, BigInteger hashOfData, Address sender) {
map.put(hashOfData, data);
log.trace("Data added to our map and it will be broadcasted to our neighbors.");
UserThread.execute(() -> hashSetChangedListeners.stream().forEach(e -> e.onAdded(data)));
broadcast(new AddDataMessage(data), sender);
StringBuilder sb = new StringBuilder("\n\nSet after addProtectedExpirableData:\n");
map.values().stream().forEach(e -> sb.append(e.toString() + "\n\n"));
sb.append("\n\n");
log.trace(sb.toString());
return true;
}
private boolean doRemoveProtectedExpirableData(ProtectedData data, BigInteger hashOfData, Address sender) {
map.remove(hashOfData);
log.trace("Data removed from our map. We broadcast the message to our neighbors.");
UserThread.execute(() -> hashSetChangedListeners.stream().forEach(e -> e.onRemoved(data)));
if (data instanceof ProtectedMailboxData)
broadcast(new RemoveMailboxDataMessage((ProtectedMailboxData) data), sender);
else
broadcast(new RemoveDataMessage(data), sender);
StringBuilder sb = new StringBuilder("\n\nSet after removeProtectedExpirableData:\n");
map.values().stream().forEach(e -> sb.append(e.toString() + "\n\n"));
sb.append("\n\n");
log.trace(sb.toString());
return true;
}
private void broadcast(BroadcastMessage message, Address sender) { private void broadcast(BroadcastMessage message, Address sender) {
if (authenticated) { if (authenticated) {

View file

@ -19,7 +19,7 @@ public class ProtectedData implements Serializable {
public final int sequenceNumber; public final int sequenceNumber;
public final byte[] signature; public final byte[] signature;
@VisibleForTesting @VisibleForTesting
public Date date; transient public Date date;
public ProtectedData(ExpirablePayload expirablePayload, long ttl, PublicKey ownerStoragePubKey, int sequenceNumber, byte[] signature) { public ProtectedData(ExpirablePayload expirablePayload, long ttl, PublicKey ownerStoragePubKey, int sequenceNumber, byte[] signature) {
this.expirablePayload = expirablePayload; this.expirablePayload = expirablePayload;
@ -34,15 +34,6 @@ public class ProtectedData implements Serializable {
try { try {
in.defaultReadObject(); in.defaultReadObject();
ttl = expirablePayload.getTTL(); ttl = expirablePayload.getTTL();
// in case the reported creation date is in the future
// we reset the date to the current time
if (date.getTime() > new Date().getTime()) {
log.warn("Date of object is in future. " +
"That might be ok as clocks are not synced but could be also a spam attack. " +
"date=" + date + " / now=" + new Date());
date = new Date();
}
date = new Date(); date = new Date();
} catch (Throwable t) { } catch (Throwable t) {