mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-06-13 17:43:08 -04:00
add ui checks for un-authenticated node, fix broadcast issue
This commit is contained in:
parent
88ab1419fa
commit
232c5b46ff
25 changed files with 329 additions and 219 deletions
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue