From 602c503cea030316cd1d63ad77898c49a3ed33e8 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 27 Jan 2016 00:04:03 +0100 Subject: [PATCH] Add callback for broadcaster when sending mailbox msg --- .../common/taskrunner/TaskRunner.java | 3 +- .../bitsquare/arbitration/DisputeManager.java | 10 +- .../io/bitsquare/payment/PaymentMethod.java | 2 +- .../java/io/bitsquare/trade/BuyerTrade.java | 8 +- .../trade/BuyerAsOffererProtocol.java | 14 +- .../protocol/trade/BuyerAsTakerProtocol.java | 14 +- .../trade/protocol/trade/BuyerProtocol.java | 5 +- .../buyer/SendFiatTransferStartedMessage.java | 9 +- .../buyer/SendPayoutTxFinalizedMessage.java | 2 +- .../seller/SendFinalizePayoutTxRequest.java | 2 +- .../taker/SendDepositTxPublishedMessage.java | 2 +- .../tasks/taker/SendPayDepositRequest.java | 2 +- .../io/bitsquare/gui/main/MainViewModel.java | 12 ++ .../pendingtrades/PendingTradesDataModel.java | 10 +- .../pendingtrades/PendingTradesView.java | 2 +- .../pendingtrades/PendingTradesViewModel.java | 11 +- .../pendingtrades/steps/StartPaymentView.java | 34 +++- .../steps/TradeStepDetailsView.java | 15 +- .../java/io/bitsquare/p2p/P2PService.java | 162 +++++++++++------- .../messaging/SendMailboxMessageListener.java | 2 +- .../io/bitsquare/p2p/peers/Broadcaster.java | 18 ++ .../java/io/bitsquare/p2p/P2PServiceTest.java | 4 +- 22 files changed, 231 insertions(+), 112 deletions(-) diff --git a/common/src/main/java/io/bitsquare/common/taskrunner/TaskRunner.java b/common/src/main/java/io/bitsquare/common/taskrunner/TaskRunner.java index cfba7dd077..97d1998758 100644 --- a/common/src/main/java/io/bitsquare/common/taskrunner/TaskRunner.java +++ b/common/src/main/java/io/bitsquare/common/taskrunner/TaskRunner.java @@ -87,8 +87,7 @@ public class TaskRunner { } void handleErrorMessage(String errorMessage) { - log.error("Task failed: " + currentTask.getSimpleName()); - log.error("errorMessage: " + errorMessage); + log.error("Task failed: " + currentTask.getSimpleName() + " / errorMessage: " + errorMessage); failed = true; errorMessageHandler.handleErrorMessage(errorMessage); } diff --git a/core/src/main/java/io/bitsquare/arbitration/DisputeManager.java b/core/src/main/java/io/bitsquare/arbitration/DisputeManager.java index 6f24b37a08..7aa53dcc32 100644 --- a/core/src/main/java/io/bitsquare/arbitration/DisputeManager.java +++ b/core/src/main/java/io/bitsquare/arbitration/DisputeManager.java @@ -196,7 +196,7 @@ public class DisputeManager { } @Override - public void onFault() { + public void onFault(String errorMessage) { log.error("sendEncryptedMessage failed"); } } @@ -263,7 +263,7 @@ public class DisputeManager { } @Override - public void onFault() { + public void onFault(String errorMessage) { log.error("sendEncryptedMessage failed"); } } @@ -313,7 +313,7 @@ public class DisputeManager { } @Override - public void onFault() { + public void onFault(String errorMessage) { log.error("sendEncryptedMessage failed"); } } @@ -354,7 +354,7 @@ public class DisputeManager { } @Override - public void onFault() { + public void onFault(String errorMessage) { log.error("sendEncryptedMessage failed"); } } @@ -381,7 +381,7 @@ public class DisputeManager { } @Override - public void onFault() { + public void onFault(String errorMessage) { log.error("sendEncryptedMessage failed"); } } diff --git a/core/src/main/java/io/bitsquare/payment/PaymentMethod.java b/core/src/main/java/io/bitsquare/payment/PaymentMethod.java index 6bc8ebb555..0a461a70eb 100644 --- a/core/src/main/java/io/bitsquare/payment/PaymentMethod.java +++ b/core/src/main/java/io/bitsquare/payment/PaymentMethod.java @@ -61,7 +61,7 @@ public class PaymentMethod implements Serializable, Comparable { public static final List ALL_VALUES = new ArrayList<>(Arrays.asList( OK_PAY = new PaymentMethod(OK_PAY_ID, 0, DAY), // tx instant so min. wait time PERFECT_MONEY = new PaymentMethod(PERFECT_MONEY_ID, 0, DAY), - SEPA = new PaymentMethod(SEPA_ID, 0, 8 * DAY), // sepa takes 1-3 business days. We use 8 days to include safety for holidays + SEPA = new PaymentMethod(SEPA_ID, 0, 7 * DAY), // sepa takes 1-3 business days. We use 7 days to include safety for holidays SWISH = new PaymentMethod(SWISH_ID, 0, DAY), ALI_PAY = new PaymentMethod(ALI_PAY_ID, 0, DAY), /* FED_WIRE = new PaymentMethod(FED_WIRE_ID, 0, DAY),*/ diff --git a/core/src/main/java/io/bitsquare/trade/BuyerTrade.java b/core/src/main/java/io/bitsquare/trade/BuyerTrade.java index a5be7758c8..d1762b80aa 100644 --- a/core/src/main/java/io/bitsquare/trade/BuyerTrade.java +++ b/core/src/main/java/io/bitsquare/trade/BuyerTrade.java @@ -18,6 +18,8 @@ package io.bitsquare.trade; import io.bitsquare.app.Version; +import io.bitsquare.common.handlers.ErrorMessageHandler; +import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.p2p.NodeAddress; import io.bitsquare.storage.Storage; import io.bitsquare.trade.offer.Offer; @@ -50,9 +52,9 @@ public abstract class BuyerTrade extends Trade implements Serializable { state = State.PREPARATION; } - public void onFiatPaymentStarted() { - checkArgument(tradeProtocol instanceof BuyerProtocol, "tradeProtocol NOT instanceof BuyerProtocol"); - ((BuyerProtocol) tradeProtocol).onFiatPaymentStarted(); + public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { + checkArgument(tradeProtocol instanceof BuyerProtocol, "Check failed: tradeProtocol instanceof BuyerProtocol"); + ((BuyerProtocol) tradeProtocol).onFiatPaymentStarted(resultHandler, errorMessageHandler); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java index 78f89fae6d..1ea72fb790 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java @@ -17,6 +17,8 @@ package io.bitsquare.trade.protocol.trade; +import io.bitsquare.common.handlers.ErrorMessageHandler; +import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.messaging.MailboxMessage; @@ -145,12 +147,18 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc // User clicked the "bank transfer started" button @Override - public void onFiatPaymentStarted() { + public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { buyerAsOffererTrade.setState(Trade.State.FIAT_PAYMENT_STARTED); TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsOffererTrade, - () -> handleTaskRunnerSuccess("onFiatPaymentStarted"), - this::handleTaskRunnerFault); + () -> { + resultHandler.handleResult(); + handleTaskRunnerSuccess("onFiatPaymentStarted"); + }, + (errorMessage) -> { + errorMessageHandler.handleErrorMessage(errorMessage); + handleTaskRunnerFault(errorMessage); + }); taskRunner.addTasks( VerifyTakeOfferFeePayment.class, SendFiatTransferStartedMessage.class diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java index d2e90b914a..132e1928d8 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java @@ -18,6 +18,8 @@ package io.bitsquare.trade.protocol.trade; +import io.bitsquare.common.handlers.ErrorMessageHandler; +import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.messaging.MailboxMessage; @@ -131,12 +133,18 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol // User clicked the "bank transfer started" button @Override - public void onFiatPaymentStarted() { + public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { buyerAsTakerTrade.setState(Trade.State.FIAT_PAYMENT_STARTED); TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade, - () -> handleTaskRunnerSuccess("onFiatPaymentStarted"), - this::handleTaskRunnerFault); + () -> { + resultHandler.handleResult(); + handleTaskRunnerSuccess("onFiatPaymentStarted"); + }, + (errorMessage) -> { + errorMessageHandler.handleErrorMessage(errorMessage); + handleTaskRunnerFault(errorMessage); + }); taskRunner.addTasks( VerifyOfferFeePayment.class, SendFiatTransferStartedMessage.class diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerProtocol.java index 190bdad403..9dc2d0027d 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerProtocol.java @@ -17,6 +17,9 @@ package io.bitsquare.trade.protocol.trade; +import io.bitsquare.common.handlers.ErrorMessageHandler; +import io.bitsquare.common.handlers.ResultHandler; + public interface BuyerProtocol { - void onFiatPaymentStarted(); + void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendFiatTransferStartedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendFiatTransferStartedMessage.java index 77c21a7041..dc0c487200 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendFiatTransferStartedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendFiatTransferStartedMessage.java @@ -36,7 +36,6 @@ public class SendFiatTransferStartedMessage extends TradeTask { protected void run() { try { runInterceptHook(); - processModel.getP2PService().sendEncryptedMailboxMessage( trade.getTradingPeerNodeAddress(), processModel.tradingPeer.getPubKeyRing(), @@ -48,22 +47,22 @@ public class SendFiatTransferStartedMessage extends TradeTask { new SendMailboxMessageListener() { @Override public void onArrived() { - log.trace("Message arrived at peer."); + log.info("Message arrived at peer."); trade.setState(Trade.State.FIAT_PAYMENT_STARTED_MSG_SENT); complete(); } @Override public void onStoredInMailbox() { - log.trace("Message stored in mailbox."); + log.info("Message stored in mailbox."); trade.setState(Trade.State.FIAT_PAYMENT_STARTED_MSG_SENT); complete(); } @Override - public void onFault() { + public void onFault(String errorMessage) { appendToErrorMessage("FiatTransferStartedMessage sending failed"); - failed(); + failed(errorMessage); } } ); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendPayoutTxFinalizedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendPayoutTxFinalizedMessage.java index 63b0f15b68..0796eae3bf 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendPayoutTxFinalizedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendPayoutTxFinalizedMessage.java @@ -61,7 +61,7 @@ public class SendPayoutTxFinalizedMessage extends TradeTask { } @Override - public void onFault() { + public void onFault(String errorMessage) { appendToErrorMessage("PayoutTxFinalizedMessage sending failed"); failed(); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendFinalizePayoutTxRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendFinalizePayoutTxRequest.java index 09a739ecc3..90e9c95e14 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendFinalizePayoutTxRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendFinalizePayoutTxRequest.java @@ -65,7 +65,7 @@ public class SendFinalizePayoutTxRequest extends TradeTask { } @Override - public void onFault() { + public void onFault(String errorMessage) { appendToErrorMessage("FinalizePayoutTxRequest sending failed"); failed(); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendDepositTxPublishedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendDepositTxPublishedMessage.java index 836f274cd7..f5974a0f8f 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendDepositTxPublishedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendDepositTxPublishedMessage.java @@ -61,7 +61,7 @@ public class SendDepositTxPublishedMessage extends TradeTask { } @Override - public void onFault() { + public void onFault(String errorMessage) { appendToErrorMessage("DepositTxPublishedMessage sending failed"); failed(); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java index 06834a12ab..e74d1862b2 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java @@ -72,7 +72,7 @@ public class SendPayDepositRequest extends TradeTask { } @Override - public void onFault() { + public void onFault(String errorMessage) { appendToErrorMessage("PayDepositRequest sending failed"); failed(); } diff --git a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java index 9e92f3404f..322d8b32a3 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java @@ -39,8 +39,11 @@ import io.bitsquare.gui.popups.DisplayAlertMessagePopup; import io.bitsquare.gui.popups.Popup; import io.bitsquare.gui.popups.WalletPasswordPopup; import io.bitsquare.gui.util.BSFormatter; +import io.bitsquare.locale.CountryUtil; +import io.bitsquare.locale.CurrencyUtil; import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PServiceListener; +import io.bitsquare.payment.OKPayAccount; import io.bitsquare.trade.Trade; import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.offer.OpenOffer; @@ -411,6 +414,15 @@ public class MainViewModel implements ViewModel { // now show app showAppScreen.set(true); + + if (BitsquareApp.DEV_MODE && user.getPaymentAccounts().isEmpty()) { + OKPayAccount okPayAccount = new OKPayAccount(); + okPayAccount.setAccountNr("dummy"); + okPayAccount.setAccountName("OKPay dummy"); + okPayAccount.setSelectedTradeCurrency(CurrencyUtil.getDefaultTradeCurrency()); + okPayAccount.setCountry(CountryUtil.getDefaultCountry()); + user.addPaymentAccount(okPayAccount); + } } private void checkPeriodicallyForBtcSyncState() { diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java index a9f7193d06..2d6ad8bad3 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -26,6 +26,8 @@ import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; import io.bitsquare.common.UserThread; import io.bitsquare.common.crypto.KeyRing; +import io.bitsquare.common.handlers.ErrorMessageHandler; +import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.gui.Navigation; import io.bitsquare.gui.common.model.ActivatableDataModel; import io.bitsquare.gui.main.MainView; @@ -54,6 +56,7 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; public class PendingTradesDataModel extends ActivatableDataModel { @@ -152,10 +155,11 @@ public class PendingTradesDataModel extends ActivatableDataModel { } } - void onFiatPaymentStarted() { + void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { checkNotNull(trade, "trade must not be null"); - if (trade instanceof BuyerTrade && trade.getDisputeState() == Trade.DisputeState.NONE) - ((BuyerTrade) trade).onFiatPaymentStarted(); + checkArgument(trade instanceof BuyerTrade, "Check failed: trade instanceof BuyerTrade"); + checkArgument(trade.getDisputeState() == Trade.DisputeState.NONE, "Check failed: trade.getDisputeState() == Trade.DisputeState.NONE"); + ((BuyerTrade) trade).onFiatPaymentStarted(resultHandler, errorMessageHandler); } void onFiatPaymentReceived() { diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java index 1cf38d7548..c4a800fe8f 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java @@ -131,7 +131,7 @@ public class PendingTradesView extends ActivatableViewAndModel { + // We would not really need an update as the success triggers a screen change + removeStatusProgressIndicator(); + statusLabel.setText(""); + + // In case the first send failed we got the support button displayed. + // If it succeeds at a second try we remove the support button again. + if (openSupportTicketButton != null) { + gridPane.getChildren().remove(openSupportTicketButton); + openSupportTicketButton = null; + } + }, errorMessage -> { + removeStatusProgressIndicator(); + statusLabel.setText("Sending message to your trading partner failed.\n" + + "Please try again and if it continue to fail report a bug."); + paymentStartedButton.setDisable(false); + }); + } + + private void removeStatusProgressIndicator() { + statusProgressIndicator.setVisible(false); + statusProgressIndicator.setProgress(0); + statusProgressIndicator.setManaged(false); } diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/TradeStepDetailsView.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/TradeStepDetailsView.java index 736905f6cc..08e89c28d0 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/TradeStepDetailsView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/TradeStepDetailsView.java @@ -22,6 +22,7 @@ import io.bitsquare.gui.components.TitledGroupBg; import io.bitsquare.gui.main.help.Help; import io.bitsquare.gui.main.help.HelpId; import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel; +import io.bitsquare.gui.popups.Popup; import io.bitsquare.gui.util.Layout; import io.bitsquare.trade.Trade; import javafx.geometry.HPos; @@ -52,7 +53,7 @@ public abstract class TradeStepDetailsView extends AnchorPane { protected Label infoLabel; protected TitledGroupBg infoTitledGroupBg; protected Button openDisputeButton; - private Button openSupportTicketButton; + protected Button openSupportTicketButton; private Trade trade; private Subscription errorMessageSubscription; @@ -181,7 +182,12 @@ public abstract class TradeStepDetailsView extends AnchorPane { } private void addErrorLabel() { - if (infoLabel == null) { + new Popup().warning(trade.errorMessageProperty().getValue() + + "\n\nPlease report the problem to your arbitrator. He will forward it to the developers to investigate the problem.\n" + + "After the problem has be analysed you will get back all the funds you paid in.\n" + + "There will be no arbitration fee charged if it was a technical error.").show(); + + /*if (infoLabel == null) { infoTitledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 1, "Error", Layout.GROUP_DISTANCE); infoLabel = addMultilineLabel(gridPane, gridRow, Layout.FIRST_ROW_AND_GROUP_DISTANCE); } @@ -190,11 +196,10 @@ public abstract class TradeStepDetailsView extends AnchorPane { + "\n\nPlease report the problem to your arbitrator. He will forward it to the developers to investigate the problem.\n" + "After the problem has be analysed you will get back all the funds you paid in.\n" + "There will be no arbitration fee charged if it was a technical error."); - infoLabel.setStyle(" -fx-text-fill: -bs-error-red;"); + infoLabel.setStyle(" -fx-text-fill: -bs-error-red;");*/ if (openSupportTicketButton == null) { - openSupportTicketButton = addButtonAfterGroup(gridPane, ++gridRow, "Request support"); - GridPane.setColumnIndex(openSupportTicketButton, 0); + openSupportTicketButton = addButton(gridPane, ++gridRow, "Request support"); GridPane.setHalignment(openSupportTicketButton, HPos.LEFT); openSupportTicketButton.setOnAction(e -> model.dataModel.onOpenSupportTicket()); } diff --git a/network/src/main/java/io/bitsquare/p2p/P2PService.java b/network/src/main/java/io/bitsquare/p2p/P2PService.java index 346d8b105e..58d12e9335 100644 --- a/network/src/main/java/io/bitsquare/p2p/P2PService.java +++ b/network/src/main/java/io/bitsquare/p2p/P2PService.java @@ -27,6 +27,7 @@ import io.bitsquare.p2p.storage.data.ExpirableMailboxPayload; import io.bitsquare.p2p.storage.data.ExpirablePayload; import io.bitsquare.p2p.storage.data.ProtectedData; import io.bitsquare.p2p.storage.data.ProtectedMailboxData; +import io.bitsquare.p2p.storage.messages.AddDataMessage; import javafx.beans.property.*; import javafx.beans.value.ChangeListener; import org.fxmisc.easybind.EasyBind; @@ -45,7 +46,8 @@ import java.util.concurrent.CopyOnWriteArraySet; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -public class P2PService implements SetupListener, MessageListener, ConnectionListener, RequestDataManager.Listener, HashMapChangedListener { +public class P2PService implements SetupListener, MessageListener, ConnectionListener, RequestDataManager.Listener, + HashMapChangedListener { private static final Logger log = LoggerFactory.getLogger(P2PService.class); private final SeedNodesRepository seedNodesRepository; @@ -56,6 +58,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis // set in init private NetworkNode networkNode; + private Broadcaster broadcaster; private P2PDataStorage p2PDataStorage; private PeerManager peerManager; private RequestDataManager requestDataManager; @@ -118,7 +121,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis networkNode.addConnectionListener(this); networkNode.addMessageListener(this); - Broadcaster broadcaster = new Broadcaster(networkNode); + broadcaster = new Broadcaster(networkNode); p2PDataStorage = new P2PDataStorage(broadcaster, networkNode, storageDir); p2PDataStorage.addHashMapChangedListener(this); @@ -455,77 +458,110 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis } } - public void sendEncryptedMailboxMessage(NodeAddress peerNodeAddress, PubKeyRing peersPubKeyRing, - MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) { + public void sendEncryptedMailboxMessage(NodeAddress peersNodeAddress, PubKeyRing peersPubKeyRing, + MailboxMessage message, + SendMailboxMessageListener sendMailboxMessageListener) { Log.traceCall("message " + message); - checkNotNull(peerNodeAddress, "PeerAddress must not be null (sendEncryptedMailboxMessage)"); - checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen."); - checkArgument(!optionalKeyRing.get().getPubKeyRing().equals(peersPubKeyRing), "We got own keyring instead of that from peer"); + checkNotNull(peersNodeAddress, + "PeerAddress must not be null (sendEncryptedMailboxMessage)"); + checkNotNull(networkNode.getNodeAddress(), + "My node address must not be null at sendEncryptedMailboxMessage"); + checkArgument(optionalKeyRing.isPresent(), + "keyRing not set. Seems that is called on a seed node which must not happen."); + checkArgument(!optionalKeyRing.get().getPubKeyRing().equals(peersPubKeyRing), + "We got own keyring instead of that from peer"); + checkArgument(optionalEncryptionService.isPresent(), + "EncryptionService not set. Seems that is called on a seed node which must not happen."); if (isNetworkReady()) { - trySendEncryptedMailboxMessage(peerNodeAddress, peersPubKeyRing, message, sendMailboxMessageListener); + if (!networkNode.getAllConnections().isEmpty()) { + try { + log.info("\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + + "Encrypt message:\nmessage={}" + + "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", message); + DirectMessage directMessage = new DirectMessage( + networkNode.getNodeAddress(), + optionalEncryptionService.get().encryptAndSign(peersPubKeyRing, message), + peersNodeAddress.getAddressPrefixHash()); + SettableFuture future = networkNode.sendMessage(peersNodeAddress, directMessage); + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(@Nullable Connection connection) { + log.trace("SendEncryptedMailboxMessage onSuccess"); + sendMailboxMessageListener.onArrived(); + } + + @Override + public void onFailure(@NotNull Throwable throwable) { + log.trace("SendEncryptedMailboxMessage onFailure"); + log.debug(throwable.toString()); + log.info("We cannot send message to peer. Peer might be offline. We will store message in mailbox."); + log.trace("create MailboxEntry with peerAddress " + peersNodeAddress); + PublicKey receiverStoragePublicKey = peersPubKeyRing.getSignaturePubKey(); + addMailboxData(new ExpirableMailboxPayload(directMessage, + optionalKeyRing.get().getSignatureKeyPair().getPublic(), + receiverStoragePublicKey), + receiverStoragePublicKey, + sendMailboxMessageListener); + } + }); + } catch (CryptoException e) { + log.error("sendEncryptedMessage failed"); + e.printStackTrace(); + sendMailboxMessageListener.onFault("Data already exist in our local database"); + } + } else { + sendMailboxMessageListener.onFault("There are no P2P network nodes connected. " + + "Please check your internet connection."); + } } else { throw new NetworkNotReadyException(); } } - // send message and if it fails (peer offline) we store the data to the network - private void trySendEncryptedMailboxMessage(NodeAddress peersNodeAddress, PubKeyRing peersPubKeyRing, - MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) { - Log.traceCall(); - checkNotNull(networkNode.getNodeAddress(), "My node address must not be null at trySendEncryptedMailboxMessage"); - checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen."); - checkArgument(optionalEncryptionService.isPresent(), "EncryptionService not set. Seems that is called on a seed node which must not happen."); - checkNotNull(networkNode.getNodeAddress(), "networkNode.getNodeAddress() must not be null."); - try { - log.info("\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + - "Encrypt message:\nmessage={}" - + "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", message); - DirectMessage directMessage = new DirectMessage( - networkNode.getNodeAddress(), - optionalEncryptionService.get().encryptAndSign(peersPubKeyRing, message), - peersNodeAddress.getAddressPrefixHash()); - SettableFuture future = networkNode.sendMessage(peersNodeAddress, directMessage); - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(@Nullable Connection connection) { - log.trace("SendEncryptedMailboxMessage onSuccess"); - sendMailboxMessageListener.onArrived(); - } - @Override - public void onFailure(@NotNull Throwable throwable) { - log.trace("SendEncryptedMailboxMessage onFailure"); - log.debug(throwable.toString()); - log.info("We cannot send message to peer. Peer might be offline. We will store message in mailbox."); - log.trace("create MailboxEntry with peerAddress " + peersNodeAddress); - PublicKey receiverStoragePublicKey = peersPubKeyRing.getSignaturePubKey(); - addMailboxData(new ExpirableMailboxPayload(directMessage, - optionalKeyRing.get().getSignatureKeyPair().getPublic(), - receiverStoragePublicKey), - receiverStoragePublicKey); - sendMailboxMessageListener.onStoredInMailbox(); - } - }); - } catch (CryptoException e) { - log.error("sendEncryptedMessage failed"); - e.printStackTrace(); - sendMailboxMessageListener.onFault(); - } - } - - private void addMailboxData(ExpirableMailboxPayload expirableMailboxPayload, PublicKey receiversPublicKey) { + private void addMailboxData(ExpirableMailboxPayload expirableMailboxPayload, + PublicKey receiversPublicKey, + SendMailboxMessageListener sendMailboxMessageListener) { Log.traceCall(); - checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen."); + checkArgument(optionalKeyRing.isPresent(), + "keyRing not set. Seems that is called on a seed node which must not happen."); + if (isNetworkReady()) { - try { - ProtectedMailboxData protectedMailboxData = p2PDataStorage.getMailboxDataWithSignedSeqNr( - expirableMailboxPayload, - optionalKeyRing.get().getSignatureKeyPair(), - receiversPublicKey); - p2PDataStorage.add(protectedMailboxData, networkNode.getNodeAddress()); - } catch (CryptoException e) { - log.error("Signing at getDataWithSignedSeqNr failed. That should never happen."); + if (!networkNode.getAllConnections().isEmpty()) { + try { + ProtectedMailboxData protectedMailboxData = p2PDataStorage.getMailboxDataWithSignedSeqNr( + expirableMailboxPayload, + optionalKeyRing.get().getSignatureKeyPair(), + receiversPublicKey); + + Timer sendMailboxMessageTimeoutTimer = UserThread.runAfter(() -> { + boolean result = p2PDataStorage.remove(protectedMailboxData, networkNode.getNodeAddress()); + log.debug("remove result=" + result); + sendMailboxMessageListener.onFault("A timeout occurred when trying to broadcast mailbox data."); + }, 30); + broadcaster.addOneTimeListener(message -> { + if (message instanceof AddDataMessage && + ((AddDataMessage) message).data.equals(protectedMailboxData)) { + sendMailboxMessageListener.onStoredInMailbox(); + sendMailboxMessageTimeoutTimer.cancel(); + } + }); + + + boolean result = p2PDataStorage.add(protectedMailboxData, networkNode.getNodeAddress()); + if (!result) { + sendMailboxMessageTimeoutTimer.cancel(); + sendMailboxMessageListener.onFault("Data already exists in our local database"); + boolean result2 = p2PDataStorage.remove(protectedMailboxData, networkNode.getNodeAddress()); + log.debug("remove result=" + result2); + } + } catch (CryptoException e) { + log.error("Signing at getDataWithSignedSeqNr failed. That should never happen."); + } + } else { + sendMailboxMessageListener.onFault("There are no P2P network nodes connected. " + + "Please check your internet connection."); } } else { throw new NetworkNotReadyException(); @@ -651,8 +687,6 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis /////////////////////////////////////////////////////////////////////////////////////////// public boolean isNetworkReady() { - log.debug("###### isNetworkReady networkReadyBinding " + networkReadyBinding.get()); - log.debug("###### isNetworkReady hiddenServicePublished.get() && preliminaryDataReceived.get() " + (hiddenServicePublished.get() && preliminaryDataReceived.get())); return hiddenServicePublished.get() && preliminaryDataReceived.get(); } diff --git a/network/src/main/java/io/bitsquare/p2p/messaging/SendMailboxMessageListener.java b/network/src/main/java/io/bitsquare/p2p/messaging/SendMailboxMessageListener.java index 5fab3488ce..2485bec2a7 100644 --- a/network/src/main/java/io/bitsquare/p2p/messaging/SendMailboxMessageListener.java +++ b/network/src/main/java/io/bitsquare/p2p/messaging/SendMailboxMessageListener.java @@ -5,5 +5,5 @@ public interface SendMailboxMessageListener { void onStoredInMailbox(); - void onFault(); + void onFault(String errorMessage); } diff --git a/network/src/main/java/io/bitsquare/p2p/peers/Broadcaster.java b/network/src/main/java/io/bitsquare/p2p/peers/Broadcaster.java index 00a0314b0f..cc16132fdd 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/Broadcaster.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/Broadcaster.java @@ -14,10 +14,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; public class Broadcaster { private static final Logger log = LoggerFactory.getLogger(Broadcaster.class); + + + public interface Listener { + void onBroadcasted(DataBroadcastMessage message); + } + private final NetworkNode networkNode; + private final Set listeners = new CopyOnWriteArraySet<>(); public Broadcaster(NetworkNode networkNode) { this.networkNode = networkNode; @@ -38,6 +46,10 @@ public class Broadcaster { @Override public void onSuccess(Connection connection) { log.trace("Broadcast from " + networkNode.getNodeAddress() + " to " + connection + " succeeded."); + listeners.stream().forEach(listener -> { + listener.onBroadcasted(message); + listeners.remove(listener); + }); } @Override @@ -51,4 +63,10 @@ public class Broadcaster { "message = {}", message); } } + + // That listener gets immediately removed after the handler is called + public void addOneTimeListener(Listener listener) { + listeners.add(listener); + } + } diff --git a/network/src/test/java/io/bitsquare/p2p/P2PServiceTest.java b/network/src/test/java/io/bitsquare/p2p/P2PServiceTest.java index f9b19543f3..be59dcde72 100644 --- a/network/src/test/java/io/bitsquare/p2p/P2PServiceTest.java +++ b/network/src/test/java/io/bitsquare/p2p/P2PServiceTest.java @@ -317,7 +317,7 @@ public class P2PServiceTest { } @Override - public void onFault() { + public void onFault(String errorMessage) { log.error("onFault"); } } @@ -353,7 +353,7 @@ public class P2PServiceTest { } @Override - public void onFault() { + public void onFault(String errorMessage) { log.error("onFault"); } }