Remove authentication for P2P network, UI improvements

This commit is contained in:
Manfred Karrer 2016-01-24 20:30:33 +01:00
parent 17fa8a424a
commit 2ad72938ec
65 changed files with 1351 additions and 2275 deletions

View file

@ -26,7 +26,7 @@ import io.bitsquare.common.crypto.KeyRing;
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.common.util.Utilities; import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.FirstPeerAuthenticatedListener; import io.bitsquare.p2p.NetWorkReadyListener;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashMapChangedListener; import io.bitsquare.p2p.storage.HashMapChangedListener;
@ -89,7 +89,7 @@ public class ArbitratorManager {
)); ));
private static final String publicKeyForTesting = "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee"; private static final String publicKeyForTesting = "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee";
private final boolean isDevTest; private final boolean isDevTest;
private FirstPeerAuthenticatedListener firstPeerAuthenticatedListener; private NetWorkReadyListener netWorkReadyListener;
private ScheduledThreadPoolExecutor republishArbitratorExecutor; private ScheduledThreadPoolExecutor republishArbitratorExecutor;
@Inject @Inject
@ -121,14 +121,14 @@ public class ArbitratorManager {
if (user.getRegisteredArbitrator() != null) { if (user.getRegisteredArbitrator() != null) {
P2PService p2PService = arbitratorService.getP2PService(); P2PService p2PService = arbitratorService.getP2PService();
if (!p2PService.isAuthenticated()) { if (!p2PService.isNetworkReady()) {
firstPeerAuthenticatedListener = new FirstPeerAuthenticatedListener() { netWorkReadyListener = new NetWorkReadyListener() {
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
republishArbitrator(); republishArbitrator();
} }
}; };
p2PService.addP2PServiceListener(firstPeerAuthenticatedListener); p2PService.addP2PServiceListener(netWorkReadyListener);
} else { } else {
republishArbitrator(); republishArbitrator();
@ -144,8 +144,8 @@ public class ArbitratorManager {
} }
private void republishArbitrator() { private void republishArbitrator() {
if (firstPeerAuthenticatedListener != null) if (netWorkReadyListener != null)
arbitratorService.getP2PService().removeP2PServiceListener(firstPeerAuthenticatedListener); arbitratorService.getP2PService().removeP2PServiceListener(netWorkReadyListener);
Arbitrator registeredArbitrator = user.getRegisteredArbitrator(); Arbitrator registeredArbitrator = user.getRegisteredArbitrator();
if (registeredArbitrator != null) { if (registeredArbitrator != null) {

View file

@ -26,8 +26,8 @@ import io.bitsquare.btc.exceptions.TransactionVerificationException;
import io.bitsquare.btc.exceptions.WalletException; import io.bitsquare.btc.exceptions.WalletException;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.crypto.PubKeyRing; import io.bitsquare.common.crypto.PubKeyRing;
import io.bitsquare.p2p.FirstPeerAuthenticatedListener;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NetWorkReadyListener;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.messaging.DecryptedMsgWithPubKey; import io.bitsquare.p2p.messaging.DecryptedMsgWithPubKey;
@ -67,7 +67,7 @@ public class DisputeManager {
private final DisputeList<Dispute> disputes; private final DisputeList<Dispute> disputes;
transient private final ObservableList<Dispute> disputesObservableList; transient private final ObservableList<Dispute> disputesObservableList;
private final String disputeInfo; private final String disputeInfo;
private final FirstPeerAuthenticatedListener firstPeerAuthenticatedListener; private final NetWorkReadyListener netWorkReadyListener;
private final CopyOnWriteArraySet<DecryptedMsgWithPubKey> decryptedMailboxMessageWithPubKeys = new CopyOnWriteArraySet<>(); private final CopyOnWriteArraySet<DecryptedMsgWithPubKey> decryptedMailboxMessageWithPubKeys = new CopyOnWriteArraySet<>();
private final CopyOnWriteArraySet<DecryptedMsgWithPubKey> decryptedMailMessageWithPubKeys = new CopyOnWriteArraySet<>(); private final CopyOnWriteArraySet<DecryptedMsgWithPubKey> decryptedMailMessageWithPubKeys = new CopyOnWriteArraySet<>();
@ -106,22 +106,22 @@ public class DisputeManager {
p2PService.addDecryptedMailListener((decryptedMessageWithPubKey, senderAddress) -> { p2PService.addDecryptedMailListener((decryptedMessageWithPubKey, senderAddress) -> {
decryptedMailMessageWithPubKeys.add(decryptedMessageWithPubKey); decryptedMailMessageWithPubKeys.add(decryptedMessageWithPubKey);
if (p2PService.isAuthenticated()) if (p2PService.isNetworkReady())
applyMessages(); applyMessages();
}); });
p2PService.addDecryptedMailboxListener((decryptedMessageWithPubKey, senderAddress) -> { p2PService.addDecryptedMailboxListener((decryptedMessageWithPubKey, senderAddress) -> {
decryptedMailboxMessageWithPubKeys.add(decryptedMessageWithPubKey); decryptedMailboxMessageWithPubKeys.add(decryptedMessageWithPubKey);
if (p2PService.isAuthenticated()) if (p2PService.isNetworkReady())
applyMessages(); applyMessages();
}); });
firstPeerAuthenticatedListener = new FirstPeerAuthenticatedListener() { netWorkReadyListener = new NetWorkReadyListener() {
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
applyMessages(); applyMessages();
} }
}; };
p2PService.addP2PServiceListener(firstPeerAuthenticatedListener); p2PService.addP2PServiceListener(netWorkReadyListener);
} }
private void applyMessages() { private void applyMessages() {
@ -143,7 +143,7 @@ public class DisputeManager {
}); });
decryptedMailboxMessageWithPubKeys.clear(); decryptedMailboxMessageWithPubKeys.clear();
p2PService.removeP2PServiceListener(firstPeerAuthenticatedListener); p2PService.removeP2PServiceListener(netWorkReadyListener);
} }

View file

@ -18,6 +18,7 @@
package io.bitsquare.trade; package io.bitsquare.trade;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import io.bitsquare.app.Log;
import io.bitsquare.arbitration.ArbitratorManager; import io.bitsquare.arbitration.ArbitratorManager;
import io.bitsquare.btc.AddressEntry; import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.TradeWalletService;
@ -26,8 +27,8 @@ import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.p2p.FirstPeerAuthenticatedListener;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NetWorkReadyListener;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.messaging.DecryptedMailListener; import io.bitsquare.p2p.messaging.DecryptedMailListener;
@ -81,7 +82,7 @@ public class TradeManager {
private final Storage<TradableList<Trade>> tradableListStorage; private final Storage<TradableList<Trade>> tradableListStorage;
private final TradableList<Trade> trades; private final TradableList<Trade> trades;
private final BooleanProperty pendingTradesInitialized = new SimpleBooleanProperty(); private final BooleanProperty pendingTradesInitialized = new SimpleBooleanProperty();
private final FirstPeerAuthenticatedListener firstPeerAuthenticatedListener; private final NetWorkReadyListener netWorkReadyListener;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -99,6 +100,7 @@ public class TradeManager {
ArbitratorManager arbitratorManager, ArbitratorManager arbitratorManager,
P2PService p2PService, P2PService p2PService,
@Named("storage.dir") File storageDir) { @Named("storage.dir") File storageDir) {
Log.traceCall();
this.user = user; this.user = user;
this.keyRing = keyRing; this.keyRing = keyRing;
this.walletService = walletService; this.walletService = walletService;
@ -147,14 +149,15 @@ public class TradeManager {
} }
}); });
firstPeerAuthenticatedListener = new FirstPeerAuthenticatedListener() { netWorkReadyListener = new NetWorkReadyListener() {
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
Log.traceCall("onNetworkReady");
// give a bit delay to be sure other listeners have executed its work // give a bit delay to be sure other listeners have executed its work
UserThread.runAfter(() -> initPendingTrades(), 100, TimeUnit.MILLISECONDS); UserThread.runAfter(() -> initPendingTrades(), 100, TimeUnit.MILLISECONDS);
} }
}; };
p2PService.addP2PServiceListener(firstPeerAuthenticatedListener); p2PService.addP2PServiceListener(netWorkReadyListener);
} }
@ -163,7 +166,8 @@ public class TradeManager {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void initPendingTrades() { private void initPendingTrades() {
if (firstPeerAuthenticatedListener != null) p2PService.removeP2PServiceListener(firstPeerAuthenticatedListener); Log.traceCall();
if (netWorkReadyListener != null) p2PService.removeP2PServiceListener(netWorkReadyListener);
//List<Trade> failedTrades = new ArrayList<>(); //List<Trade> failedTrades = new ArrayList<>();
for (Trade trade : trades) { for (Trade trade : trades) {
@ -178,7 +182,7 @@ public class TradeManager {
trade.updateDepositTxFromWallet(tradeWalletService); trade.updateDepositTxFromWallet(tradeWalletService);
initTrade(trade); initTrade(trade);
// after we are authenticated we remove mailbox messages. // after network is ready we remove mailbox messages.
DecryptedMsgWithPubKey mailboxMessage = trade.getMailboxMessage(); DecryptedMsgWithPubKey mailboxMessage = trade.getMailboxMessage();
if (mailboxMessage != null) { if (mailboxMessage != null) {
log.trace("initPendingTrades/removeEntryFromMailbox mailboxMessage = " + mailboxMessage); log.trace("initPendingTrades/removeEntryFromMailbox mailboxMessage = " + mailboxMessage);

View file

@ -24,8 +24,8 @@ import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
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.FirstPeerAuthenticatedListener;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NetWorkReadyListener;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.messaging.SendMailMessageListener; import io.bitsquare.p2p.messaging.SendMailMessageListener;
@ -67,7 +67,7 @@ public class OpenOfferManager {
private final TradableList<OpenOffer> openOffers; private final TradableList<OpenOffer> openOffers;
private final Storage<TradableList<OpenOffer>> openOffersStorage; private final Storage<TradableList<OpenOffer>> openOffersStorage;
private boolean shutDownRequested; private boolean shutDownRequested;
private FirstPeerAuthenticatedListener firstPeerAuthenticatedListener; private NetWorkReadyListener netWorkReadyListener;
private final Timer timer = new Timer(); private final Timer timer = new Timer();
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -103,12 +103,12 @@ public class OpenOfferManager {
"OpenOfferManager.ShutDownHook")); "OpenOfferManager.ShutDownHook"));
// Handler for incoming offer availability requests // Handler for incoming offer availability requests
p2PService.addDecryptedMailListener((decryptedMessageWithPubKey, peerAddress) -> { p2PService.addDecryptedMailListener((decryptedMessageWithPubKey, peersNodeAddress) -> {
// We get an encrypted message but don't do the signature check as we don't know the peer yet. // We get an encrypted message but don't do the signature check as we don't know the peer yet.
// A basic sig check is in done also at decryption time // A basic sig check is in done also at decryption time
Message message = decryptedMessageWithPubKey.message; Message message = decryptedMessageWithPubKey.message;
if (message instanceof OfferAvailabilityRequest) if (message instanceof OfferAvailabilityRequest)
handleOfferAvailabilityRequest((OfferAvailabilityRequest) message, peerAddress); handleOfferAvailabilityRequest((OfferAvailabilityRequest) message, peersNodeAddress);
}); });
} }
@ -126,14 +126,14 @@ public class OpenOfferManager {
// Before the TTL is reached we re-publish our offers // Before the TTL is reached we re-publish our offers
// If offer removal at shutdown fails we don't want to have long term dangling dead offers, so we set // If offer removal at shutdown fails we don't want to have long term dangling dead offers, so we set
// TTL quite short and use re-publish as strategy. Offerers need to be online anyway. // TTL quite short and use re-publish as strategy. Offerers need to be online anyway.
if (!p2PService.isAuthenticated()) { if (!p2PService.isNetworkReady()) {
firstPeerAuthenticatedListener = new FirstPeerAuthenticatedListener() { netWorkReadyListener = new NetWorkReadyListener() {
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
startRePublishThread(); startRePublishThread();
} }
}; };
p2PService.addP2PServiceListener(firstPeerAuthenticatedListener); p2PService.addP2PServiceListener(netWorkReadyListener);
} else { } else {
startRePublishThread(); startRePublishThread();
@ -141,8 +141,8 @@ public class OpenOfferManager {
} }
private void startRePublishThread() { private void startRePublishThread() {
if (firstPeerAuthenticatedListener != null) if (netWorkReadyListener != null)
p2PService.removeP2PServiceListener(firstPeerAuthenticatedListener); p2PService.removeP2PServiceListener(netWorkReadyListener);
long period = (long) (Offer.TTL * 0.8); // republish sufficiently before offer would expire long period = (long) (Offer.TTL * 0.8); // republish sufficiently before offer would expire
TimerTask timerTask = new TimerTask() { TimerTask timerTask = new TimerTask() {

View file

@ -56,14 +56,14 @@ public class OfferAvailabilityProtocol {
this.resultHandler = resultHandler; this.resultHandler = resultHandler;
this.errorMessageHandler = errorMessageHandler; this.errorMessageHandler = errorMessageHandler;
decryptedMailListener = (decryptedMessageWithPubKey, peerAddress) -> { decryptedMailListener = (decryptedMessageWithPubKey, peersNodeAddress) -> {
Message message = decryptedMessageWithPubKey.message; Message message = decryptedMessageWithPubKey.message;
if (message instanceof OfferMessage) { if (message instanceof OfferMessage) {
OfferMessage offerMessage = (OfferMessage) message; OfferMessage offerMessage = (OfferMessage) message;
nonEmptyStringOf(offerMessage.offerId); nonEmptyStringOf(offerMessage.offerId);
if (message instanceof OfferAvailabilityResponse if (message instanceof OfferAvailabilityResponse
&& model.offer.getId().equals(offerMessage.offerId)) { && model.offer.getId().equals(offerMessage.offerId)) {
log.trace("handle OfferAvailabilityResponse = " + message.getClass().getSimpleName() + " from " + peerAddress); log.trace("handle OfferAvailabilityResponse = " + message.getClass().getSimpleName() + " from " + peersNodeAddress);
handle((OfferAvailabilityResponse) message); handle((OfferAvailabilityResponse) message);
} }
} }

View file

@ -49,19 +49,19 @@ public abstract class TradeProtocol {
this.trade = trade; this.trade = trade;
this.processModel = trade.getProcessModel(); this.processModel = trade.getProcessModel();
decryptedMailListener = (decryptedMessageWithPubKey, peerAddress) -> { decryptedMailListener = (decryptedMessageWithPubKey, peersNodeAddress) -> {
// We check the sig only as soon we have stored the peers pubKeyRing. // We check the sig only as soon we have stored the peers pubKeyRing.
PubKeyRing tradingPeerPubKeyRing = processModel.tradingPeer.getPubKeyRing(); PubKeyRing tradingPeerPubKeyRing = processModel.tradingPeer.getPubKeyRing();
PublicKey signaturePubKey = decryptedMessageWithPubKey.signaturePubKey; PublicKey signaturePubKey = decryptedMessageWithPubKey.signaturePubKey;
if (tradingPeerPubKeyRing != null && signaturePubKey.equals(tradingPeerPubKeyRing.getSignaturePubKey())) { if (tradingPeerPubKeyRing != null && signaturePubKey.equals(tradingPeerPubKeyRing.getSignaturePubKey())) {
Message message = decryptedMessageWithPubKey.message; Message message = decryptedMessageWithPubKey.message;
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + peerAddress); log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + peersNodeAddress);
if (message instanceof TradeMessage) { if (message instanceof TradeMessage) {
TradeMessage tradeMessage = (TradeMessage) message; TradeMessage tradeMessage = (TradeMessage) message;
nonEmptyStringOf(tradeMessage.tradeId); nonEmptyStringOf(tradeMessage.tradeId);
if (tradeMessage.tradeId.equals(processModel.getId())) { if (tradeMessage.tradeId.equals(processModel.getId())) {
doHandleDecryptedMessage(tradeMessage, peerAddress); doHandleDecryptedMessage(tradeMessage, peersNodeAddress);
} }
} }
} else { } else {

View file

@ -113,7 +113,7 @@ public class MainViewModel implements ViewModel {
private final User user; private final User user;
private int numBTCPeers = 0; private int numBTCPeers = 0;
private Timer checkForBtcSyncStateTimer; private Timer checkForBtcSyncStateTimer;
private ChangeListener<Number> numAuthenticatedPeersListener, btcNumPeersListener; private ChangeListener<Number> numConnectedPeersListener, btcNumPeersListener;
private java.util.Timer numberofBtcPeersTimer; private java.util.Timer numberofBtcPeersTimer;
private java.util.Timer numberofP2PNetworkPeersTimer; private java.util.Timer numberofP2PNetworkPeersTimer;
@ -176,8 +176,8 @@ public class MainViewModel implements ViewModel {
} }
public void shutDown() { public void shutDown() {
if (numAuthenticatedPeersListener != null) if (numConnectedPeersListener != null)
p2PService.getNumAuthenticatedPeers().removeListener(numAuthenticatedPeersListener); p2PService.getNumConnectedPeers().removeListener(numConnectedPeersListener);
if (btcNumPeersListener != null) if (btcNumPeersListener != null)
walletService.numPeersProperty().removeListener(btcNumPeersListener); walletService.numPeersProperty().removeListener(btcNumPeersListener);
@ -208,17 +208,17 @@ public class MainViewModel implements ViewModel {
@Override @Override
public void onRequestingDataCompleted() { public void onRequestingDataCompleted() {
if (p2PService.getNumAuthenticatedPeers().get() == 0) { if (p2PService.getNumConnectedPeers().get() == 0) {
p2PNetworkInfo.set("Initial data received"); p2PNetworkInfo.set("Initial data received");
} else { } else {
updateP2pNetworkInfoWithPeersChanged(p2PService.getNumAuthenticatedPeers().get()); updateP2pNetworkInfoWithPeersChanged(p2PService.getNumConnectedPeers().get());
} }
p2pNetworkInitialized.set(true); p2pNetworkInitialized.set(true);
} }
@Override @Override
public void onNoSeedNodeAvailable() { public void onNoSeedNodeAvailable() {
if (p2PService.getNumAuthenticatedPeers().get() == 0) { if (p2PService.getNumConnectedPeers().get() == 0) {
p2PNetworkInfo.set("No seed nodes available"); p2PNetworkInfo.set("No seed nodes available");
} }
p2pNetworkInitialized.set(true); p2pNetworkInitialized.set(true);
@ -226,7 +226,7 @@ public class MainViewModel implements ViewModel {
@Override @Override
public void onNoPeersAvailable() { public void onNoPeersAvailable() {
if (p2PService.getNumAuthenticatedPeers().get() == 0) { if (p2PService.getNumConnectedPeers().get() == 0) {
p2PNetworkWarnMsg.set("There are no seed nodes or persisted peers available for requesting data.\n" + p2PNetworkWarnMsg.set("There are no seed nodes or persisted peers available for requesting data.\n" +
"Please check your internet connection or try to restart the application."); "Please check your internet connection or try to restart the application.");
p2PNetworkInfo.set("No seed nodes and peers available"); p2PNetworkInfo.set("No seed nodes and peers available");
@ -236,8 +236,8 @@ public class MainViewModel implements ViewModel {
} }
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
updateP2pNetworkInfoWithPeersChanged(p2PService.getNumAuthenticatedPeers().get()); updateP2pNetworkInfoWithPeersChanged(p2PService.getNumConnectedPeers().get());
splashP2PNetworkProgress.set(1); splashP2PNetworkProgress.set(1);
} }
@ -248,7 +248,7 @@ public class MainViewModel implements ViewModel {
+ throwable.getMessage() + ").\n" + + throwable.getMessage() + ").\n" +
"Please check your internet connection or try to restart the application."); "Please check your internet connection or try to restart the application.");
splashP2PNetworkProgress.set(0); splashP2PNetworkProgress.set(0);
if (p2PService.getNumAuthenticatedPeers().get() == 0) if (p2PService.getNumConnectedPeers().get() == 0)
p2PNetworkLabelId.set("splash-error-state-msg"); p2PNetworkLabelId.set("splash-error-state-msg");
} }
}); });
@ -385,14 +385,13 @@ public class MainViewModel implements ViewModel {
.show(); .show();
// update nr of peers in footer // update nr of peers in footer
numAuthenticatedPeersListener = (observable, oldValue, newValue) -> { numConnectedPeersListener = (observable, oldValue, newValue) -> {
if ((int) oldValue > 0 && (int) newValue == 0) { if ((int) oldValue > 0 && (int) newValue == 0) {
// give a bit of tolerance // give a bit of tolerance
if (numberofP2PNetworkPeersTimer != null) if (numberofP2PNetworkPeersTimer != null)
numberofP2PNetworkPeersTimer.cancel(); numberofP2PNetworkPeersTimer.cancel();
numberofP2PNetworkPeersTimer = UserThread.runAfter(() -> { numberofP2PNetworkPeersTimer = UserThread.runAfter(() -> {
if (p2PService.getNumAuthenticatedPeers().get() == 0) { if (p2PService.getNumConnectedPeers().get() == 0) {
p2PNetworkWarnMsg.set("You lost the connection to all P2P network peers.\n" + p2PNetworkWarnMsg.set("You lost the connection to all P2P network peers.\n" +
"Maybe you lost your internet connection or your computer was in hibernate/sleep mode."); "Maybe you lost your internet connection or your computer was in hibernate/sleep mode.");
p2PNetworkLabelId.set("splash-error-state-msg"); p2PNetworkLabelId.set("splash-error-state-msg");
@ -408,7 +407,7 @@ public class MainViewModel implements ViewModel {
updateP2pNetworkInfoWithPeersChanged((int) newValue); updateP2pNetworkInfoWithPeersChanged((int) newValue);
}; };
p2PService.getNumAuthenticatedPeers().addListener(numAuthenticatedPeersListener); p2PService.getNumConnectedPeers().addListener(numConnectedPeersListener);
// now show app // now show app
showAppScreen.set(true); showAppScreen.set(true);
@ -427,8 +426,8 @@ public class MainViewModel implements ViewModel {
} }
} }
private void updateP2pNetworkInfoWithPeersChanged(int numAuthenticatedPeers) { private void updateP2pNetworkInfoWithPeersChanged(int numPeers) {
p2PNetworkInfo.set("Nr. of connections: " + numAuthenticatedPeers); p2PNetworkInfo.set("Nr. of connections: " + numPeers);
} }
private void displayAlertIfPresent(Alert alert) { private void displayAlertIfPresent(Alert alert) {

View file

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

View file

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

View file

@ -28,7 +28,7 @@
<TableView fx:id="table" VBox.vgrow="ALWAYS"> <TableView fx:id="table" VBox.vgrow="ALWAYS">
<columns> <columns>
<TableColumn text="Label" fx:id="labelColumn" minWidth="100" sortable="false"/> <TableColumn text="Details" fx:id="detailsColumn" minWidth="100" sortable="false"/>
<TableColumn text="Address" fx:id="addressColumn" minWidth="240" sortable="false"/> <TableColumn text="Address" fx:id="addressColumn" minWidth="240" sortable="false"/>
<TableColumn text="Balance" fx:id="balanceColumn" minWidth="90" sortable="false"/> <TableColumn text="Balance" fx:id="balanceColumn" minWidth="90" sortable="false"/>
<TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="30" sortable="false"/> <TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="30" sortable="false"/>

View file

@ -50,7 +50,7 @@ public class ReservedView extends ActivatableView<VBox, Void> {
@FXML @FXML
TableView<ReservedListItem> table; TableView<ReservedListItem> table;
@FXML @FXML
TableColumn<ReservedListItem, ReservedListItem> labelColumn, addressColumn, balanceColumn, confidenceColumn; TableColumn<ReservedListItem, ReservedListItem> detailsColumn, addressColumn, balanceColumn, confidenceColumn;
private final WalletService walletService; private final WalletService walletService;
private final TradeManager tradeManager; private final TradeManager tradeManager;
@ -123,8 +123,8 @@ public class ReservedView extends ActivatableView<VBox, Void> {
} }
private void setLabelColumnCellFactory() { private void setLabelColumnCellFactory() {
labelColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); detailsColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
labelColumn.setCellFactory(new Callback<TableColumn<ReservedListItem, ReservedListItem>, detailsColumn.setCellFactory(new Callback<TableColumn<ReservedListItem, ReservedListItem>,
TableCell<ReservedListItem, TableCell<ReservedListItem,
ReservedListItem>>() { ReservedListItem>>() {

View file

@ -29,7 +29,7 @@
<TableView fx:id="table" VBox.vgrow="ALWAYS"> <TableView fx:id="table" VBox.vgrow="ALWAYS">
<columns> <columns>
<TableColumn text="Label" fx:id="labelColumn" minWidth="100" sortable="false"/> <TableColumn text="Details" fx:id="detailsColumn" minWidth="100" sortable="false"/>
<TableColumn text="Address" fx:id="addressColumn" minWidth="240" sortable="false"> <TableColumn text="Address" fx:id="addressColumn" minWidth="240" sortable="false">
<cellValueFactory> <cellValueFactory>
<PropertyValueFactory property="addressString"/> <PropertyValueFactory property="addressString"/>
@ -37,6 +37,7 @@
</TableColumn> </TableColumn>
<TableColumn text="Balance" fx:id="balanceColumn" minWidth="50" sortable="false"/> <TableColumn text="Balance" fx:id="balanceColumn" minWidth="50" sortable="false"/>
<TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="30" sortable="false"/> <TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="30" sortable="false"/>
<TableColumn text="" fx:id="selectColumn" prefWidth="80" sortable="false"/>
</columns> </columns>
</TableView> </TableView>

View file

@ -71,7 +71,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
@FXML @FXML
TextField withdrawFromTextField, withdrawToTextField, amountTextField; TextField withdrawFromTextField, withdrawToTextField, amountTextField;
@FXML @FXML
TableColumn<WithdrawalListItem, WithdrawalListItem> labelColumn, addressColumn, balanceColumn, confidenceColumn; TableColumn<WithdrawalListItem, WithdrawalListItem> detailsColumn, addressColumn, balanceColumn, confidenceColumn, selectColumn;
private final WalletService walletService; private final WalletService walletService;
private final TradeManager tradeManager; private final TradeManager tradeManager;
@ -114,6 +114,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
setAddressColumnCellFactory(); setAddressColumnCellFactory();
setBalanceColumnCellFactory(); setBalanceColumnCellFactory();
setConfidenceColumnCellFactory(); setConfidenceColumnCellFactory();
setSelectColumnCellFactory();
if (BitsquareApp.DEV_MODE) if (BitsquareApp.DEV_MODE)
withdrawToTextField.setText("mxAkWWaQBqwqcYstKzqLku3kzR6pbu2zHq"); withdrawToTextField.setText("mxAkWWaQBqwqcYstKzqLku3kzR6pbu2zHq");
@ -262,8 +263,8 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
} }
private void setLabelColumnCellFactory() { private void setLabelColumnCellFactory() {
labelColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); detailsColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
labelColumn.setCellFactory(new Callback<TableColumn<WithdrawalListItem, WithdrawalListItem>, detailsColumn.setCellFactory(new Callback<TableColumn<WithdrawalListItem, WithdrawalListItem>,
TableCell<WithdrawalListItem, TableCell<WithdrawalListItem,
WithdrawalListItem>>() { WithdrawalListItem>>() {
@ -272,21 +273,25 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
WithdrawalListItem> column) { WithdrawalListItem> column) {
return new TableCell<WithdrawalListItem, WithdrawalListItem>() { return new TableCell<WithdrawalListItem, WithdrawalListItem>() {
private Hyperlink hyperlink;
@Override @Override
public void updateItem(final WithdrawalListItem item, boolean empty) { public void updateItem(final WithdrawalListItem item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
if (item != null && !empty) { if (item != null && !empty) {
hyperlink = new Hyperlink(item.getLabel()); if (detailsAvailable(item)) {
Hyperlink hyperlink = new Hyperlink(item.getLabel());
if (item.getAddressEntry().getOfferId() != null) { if (item.getAddressEntry().getOfferId() != null) {
Tooltip tooltip = new Tooltip(item.getAddressEntry().getShortOfferId()); Tooltip tooltip = new Tooltip(item.getAddressEntry().getShortOfferId());
Tooltip.install(hyperlink, tooltip); Tooltip.install(hyperlink, tooltip);
hyperlink.setOnAction(event -> openDetails(item)); hyperlink.setOnAction(event -> openDetails(item));
}
setGraphic(hyperlink); setGraphic(hyperlink);
}
} else {
Label label = new Label("No info available");
setGraphic(label);
}
} else { } else {
setGraphic(null); setGraphic(null);
setId(null); setId(null);
@ -374,6 +379,42 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
}); });
} }
private void setSelectColumnCellFactory() {
selectColumn.setCellValueFactory((addressListItem) ->
new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
selectColumn.setCellFactory(
new Callback<TableColumn<WithdrawalListItem, WithdrawalListItem>, TableCell<WithdrawalListItem,
WithdrawalListItem>>() {
@Override
public TableCell<WithdrawalListItem, WithdrawalListItem> call(TableColumn<WithdrawalListItem,
WithdrawalListItem> column) {
return new TableCell<WithdrawalListItem, WithdrawalListItem>() {
Button button = new Button("Select");
@Override
public void updateItem(final WithdrawalListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
button.setDefaultButton(true);
button.setMouseTransparent(true);
setGraphic(button);
} else {
setGraphic(null);
}
}
};
}
});
}
private boolean detailsAvailable(WithdrawalListItem item) {
String offerId = item.getAddressEntry().getOfferId();
return closedTradableManager.getTradableById(offerId).isPresent() ||
failedTradesManager.getTradeById(offerId).isPresent();
}
private void openDetails(WithdrawalListItem item) { private void openDetails(WithdrawalListItem item) {
String offerId = item.getAddressEntry().getOfferId(); String offerId = item.getAddressEntry().getOfferId();
Optional<Tradable> tradableOptional = closedTradableManager.getTradableById(offerId); Optional<Tradable> tradableOptional = closedTradableManager.getTradableById(offerId);
@ -384,8 +425,10 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
} else if (tradable instanceof OpenOffer) { } else if (tradable instanceof OpenOffer) {
offerDetailsPopup.show(tradable.getOffer()); offerDetailsPopup.show(tradable.getOffer());
} }
} else if (failedTradesManager.getTradeById(offerId).isPresent()) {
tradeDetailsPopup.show(failedTradesManager.getTradeById(offerId).get());
} else { } else {
failedTradesManager.getTradeById(offerId).ifPresent(trade -> tradeDetailsPopup.show(trade)); log.warn("no details available. A test with detailsAvailable() is missing.");
} }
} }
} }

View file

@ -199,7 +199,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void onPlaceOffer() { private void onPlaceOffer() {
if (model.isAuthenticated()) { if (model.isNetworkReady()) {
Offer offer = model.createAndGetOffer(); Offer offer = model.createAndGetOffer();
if (model.getShowPlaceOfferConfirmation()) { if (model.getShowPlaceOfferConfirmation()) {
offerDetailsPopup.onPlaceOffer(o -> model.onPlaceOffer(o)).show(offer); offerDetailsPopup.onPlaceOffer(o -> model.onPlaceOffer(o)).show(offer);
@ -214,7 +214,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
} }
} }
} else { } else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" + new Popup().warning("You need to wait until your client is bootstrapped in the network.\n" +
"That might take up to about 2 minutes at startup.").show(); "That might take up to about 2 minutes at startup.").show();
} }
} }

View file

@ -485,8 +485,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
return dataModel.hasAcceptedArbitrators(); return dataModel.hasAcceptedArbitrators();
} }
boolean isAuthenticated() { boolean isNetworkReady() {
return p2PService.isAuthenticated(); return p2PService.isNetworkReady();
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -230,15 +230,15 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
} }
private void onTakeOffer(Offer offer) { private void onTakeOffer(Offer offer) {
if (model.isAuthenticated()) if (model.isNetworkReady())
offerActionHandler.onTakeOffer(offer); offerActionHandler.onTakeOffer(offer);
else else
new Popup().warning("You need to wait until your client is authenticated in the network.\n" + new Popup().warning("You need to wait until your client is bootstrapped in the network.\n" +
"That might take up to about 2 minutes at startup.").show(); "That might take up to about 2 minutes at startup.").show();
} }
private void onRemoveOpenOffer(Offer offer) { private void onRemoveOpenOffer(Offer offer) {
if (model.isAuthenticated()) { if (model.isNetworkReady()) {
new Popup().warning("Are you sure you want to remove that offer?\n" + new Popup().warning("Are you sure you want to remove that offer?\n" +
"The offer fee you have paid will be lost if you remove that offer.") "The offer fee you have paid will be lost if you remove that offer.")
.actionButtonText("Remove offer") .actionButtonText("Remove offer")
@ -246,7 +246,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
.closeButtonText("Don't remove the offer") .closeButtonText("Don't remove the offer")
.show(); .show();
} else { } else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" + new Popup().warning("You need to wait until your client is bootstrapped in the network.\n" +
"That might take up to about 2 minutes at startup.").show(); "That might take up to about 2 minutes at startup.").show();
} }
} }

View file

@ -157,8 +157,8 @@ class OfferBookViewModel extends ActivatableViewModel {
return list; return list;
} }
boolean isAuthenticated() { boolean isNetworkReady() {
return p2PService.isAuthenticated(); return p2PService.isNetworkReady();
} }
public TradeCurrency getTradeCurrency() { public TradeCurrency getTradeCurrency() {

View file

@ -73,7 +73,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
} }
private void onRemoveOpenOffer(OpenOffer openOffer) { private void onRemoveOpenOffer(OpenOffer openOffer) {
if (model.isAuthenticated()) { if (model.isNetworkReady()) {
new Popup().warning("Are you sure you want to remove that offer?\n" + new Popup().warning("Are you sure you want to remove that offer?\n" +
"The offer fee you have paid will be lost if you remove that offer.") "The offer fee you have paid will be lost if you remove that offer.")
.actionButtonText("Remove offer") .actionButtonText("Remove offer")
@ -81,7 +81,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
.closeButtonText("Don't remove the offer") .closeButtonText("Don't remove the offer")
.show(); .show();
} else { } else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" + new Popup().warning("You need to wait until your client is bootstrapped in the network.\n" +
"That might take up to about 2 minutes at startup.").show(); "That might take up to about 2 minutes at startup.").show();
} }
} }

View file

@ -73,7 +73,7 @@ class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel>
return formatter.formatDateTime(item.getOffer().getDate()); return formatter.formatDateTime(item.getOffer().getDate());
} }
boolean isAuthenticated() { boolean isNetworkReady() {
return p2PService.isAuthenticated(); return p2PService.isNetworkReady();
} }
} }

View file

@ -219,8 +219,8 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
return btcAddressValidator; return btcAddressValidator;
} }
public boolean isAuthenticated() { public boolean isNetworkReady() {
return p2PService.isAuthenticated(); return p2PService.isNetworkReady();
} }
// columns // columns

View file

@ -107,7 +107,7 @@ public class ConfirmPaymentReceivedView extends TradeStepDetailsView {
private void onPaymentReceived(ActionEvent actionEvent) { private void onPaymentReceived(ActionEvent actionEvent) {
log.debug("onPaymentReceived"); log.debug("onPaymentReceived");
if (model.isAuthenticated()) { if (model.isNetworkReady()) {
Preferences preferences = model.dataModel.getPreferences(); Preferences preferences = model.dataModel.getPreferences();
String key = PopupId.PAYMENT_RECEIVED; String key = PopupId.PAYMENT_RECEIVED;
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) { if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
@ -124,7 +124,7 @@ public class ConfirmPaymentReceivedView extends TradeStepDetailsView {
confirmPaymentReceived(); confirmPaymentReceived();
} }
} else { } else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" + new Popup().warning("You need to wait until your client is bootstrapped in the network.\n" +
"That might take up to about 2 minutes at startup.").show(); "That might take up to about 2 minutes at startup.").show();
} }
} }

View file

@ -140,7 +140,7 @@ public class StartPaymentView extends TradeStepDetailsView {
private void onPaymentStarted(ActionEvent actionEvent) { private void onPaymentStarted(ActionEvent actionEvent) {
log.debug("onPaymentStarted"); log.debug("onPaymentStarted");
if (model.isAuthenticated()) { if (model.isNetworkReady()) {
String key = PopupId.PAYMENT_SENT; String key = PopupId.PAYMENT_SENT;
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) { if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
new Popup().headLine("Confirmation") new Popup().headLine("Confirmation")
@ -154,7 +154,7 @@ public class StartPaymentView extends TradeStepDetailsView {
confirmPaymentStarted(); confirmPaymentStarted();
} }
} else { } else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" + new Popup().warning("You need to wait until your client is bootstrapped in the network.\n" +
"That might take up to about 2 minutes at startup.").show(); "That might take up to about 2 minutes at startup.").show();
} }
} }

View file

@ -37,7 +37,7 @@
<ComboBox fx:id="netWorkComboBox" GridPane.rowIndex="0" GridPane.columnIndex="1"/> <ComboBox fx:id="netWorkComboBox" GridPane.rowIndex="0" GridPane.columnIndex="1"/>
<Label fx:id="bitcoinPeersLabel" text="Connected peers:" GridPane.rowIndex="1"/> <Label fx:id="bitcoinPeersLabel" text="Connected peers:" GridPane.rowIndex="1"/>
<TextArea fx:id="bitcoinPeersTextArea" GridPane.rowIndex="1" GridPane.columnIndex="1" <TextArea fx:id="bitcoinPeersTextArea" GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
editable="false" focusTraversable="false"/> editable="false" focusTraversable="false"/>
@ -62,8 +62,8 @@
</GridPane.margin> </GridPane.margin>
</TextField> </TextField>
<Label fx:id="authenticatedPeersLabel" text="Authenticated peers:" GridPane.rowIndex="4"/> <Label fx:id="p2PPeersLabel" text="Connected peers:" GridPane.rowIndex="4"/>
<TextArea fx:id="authenticatedPeersTextArea" GridPane.rowIndex="4" GridPane.columnIndex="1" <TextArea fx:id="p2PPeersTextArea" GridPane.rowIndex="4" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
editable="false" focusTraversable="false"/> editable="false" focusTraversable="false"/>
<columnConstraints> <columnConstraints>

View file

@ -65,14 +65,14 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
@FXML @FXML
ComboBox<BitcoinNetwork> netWorkComboBox; ComboBox<BitcoinNetwork> netWorkComboBox;
@FXML @FXML
TextArea bitcoinPeersTextArea, authenticatedPeersTextArea; TextArea bitcoinPeersTextArea, p2PPeersTextArea;
@FXML @FXML
Label bitcoinPeersLabel, authenticatedPeersLabel; Label bitcoinPeersLabel, p2PPeersLabel;
private P2PServiceListener p2PServiceListener; private P2PServiceListener p2PServiceListener;
private ChangeListener<Number> numAuthenticatedPeersChangeListener; private ChangeListener<Number> numP2PPeersChangeListener;
private ChangeListener<List<Peer>> bitcoinPeersChangeListener; private ChangeListener<List<Peer>> bitcoinPeersChangeListener;
private final Set<NodeAddress> seedNodeNodeAddresses; private final Set<NodeAddress> seedNodeAddresses;
@Inject @Inject
public NetworkSettingsView(WalletService walletService, P2PService p2PService, Preferences preferences, public NetworkSettingsView(WalletService walletService, P2PService p2PService, Preferences preferences,
@ -84,14 +84,14 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
BitcoinNetwork bitcoinNetwork = preferences.getBitcoinNetwork(); BitcoinNetwork bitcoinNetwork = preferences.getBitcoinNetwork();
boolean useLocalhost = p2PService.getNetworkNode() instanceof LocalhostNetworkNode; boolean useLocalhost = p2PService.getNetworkNode() instanceof LocalhostNetworkNode;
this.seedNodeNodeAddresses = seedNodesRepository.getSeedNodeAddresses(useLocalhost, bitcoinNetwork.ordinal()); this.seedNodeAddresses = seedNodesRepository.getSeedNodeAddresses(useLocalhost, bitcoinNetwork.ordinal());
} }
public void initialize() { public void initialize() {
GridPane.setMargin(bitcoinPeersLabel, new Insets(4, 0, 0, 0)); GridPane.setMargin(bitcoinPeersLabel, new Insets(4, 0, 0, 0));
GridPane.setValignment(bitcoinPeersLabel, VPos.TOP); GridPane.setValignment(bitcoinPeersLabel, VPos.TOP);
GridPane.setMargin(authenticatedPeersLabel, new Insets(4, 0, 0, 0)); GridPane.setMargin(p2PPeersLabel, new Insets(4, 0, 0, 0));
GridPane.setValignment(authenticatedPeersLabel, VPos.TOP); GridPane.setValignment(p2PPeersLabel, VPos.TOP);
bitcoinPeersTextArea.setPrefRowCount(12); bitcoinPeersTextArea.setPrefRowCount(12);
netWorkComboBox.setItems(FXCollections.observableArrayList(BitcoinNetwork.values())); netWorkComboBox.setItems(FXCollections.observableArrayList(BitcoinNetwork.values()));
netWorkComboBox.getSelectionModel().select(preferences.getBitcoinNetwork()); netWorkComboBox.getSelectionModel().select(preferences.getBitcoinNetwork());
@ -127,7 +127,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
} }
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
} }
@Override @Override
@ -152,9 +152,9 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
walletService.connectedPeersProperty().addListener(bitcoinPeersChangeListener); walletService.connectedPeersProperty().addListener(bitcoinPeersChangeListener);
updateBitcoinPeersTextArea(); updateBitcoinPeersTextArea();
numAuthenticatedPeersChangeListener = (observable, oldValue, newValue) -> updateAuthenticatedPeersTextArea(); numP2PPeersChangeListener = (observable, oldValue, newValue) -> updateP2PPeersTextArea();
p2PService.getNumAuthenticatedPeers().addListener(numAuthenticatedPeersChangeListener); p2PService.getNumConnectedPeers().addListener(numP2PPeersChangeListener);
updateAuthenticatedPeersTextArea(); updateP2PPeersTextArea();
} }
@Override @Override
@ -165,18 +165,18 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
if (bitcoinPeersChangeListener != null) if (bitcoinPeersChangeListener != null)
walletService.connectedPeersProperty().removeListener(bitcoinPeersChangeListener); walletService.connectedPeersProperty().removeListener(bitcoinPeersChangeListener);
if (numAuthenticatedPeersChangeListener != null) if (numP2PPeersChangeListener != null)
p2PService.getNumAuthenticatedPeers().removeListener(numAuthenticatedPeersChangeListener); p2PService.getNumConnectedPeers().removeListener(numP2PPeersChangeListener);
} }
private void updateAuthenticatedPeersTextArea() { private void updateP2PPeersTextArea() {
authenticatedPeersTextArea.clear(); p2PPeersTextArea.clear();
p2PService.getAuthenticatedPeerNodeAddresses().stream().forEach(e -> { p2PService.getNodeAddressesOfConnectedPeers().stream().forEach(e -> {
if (authenticatedPeersTextArea.getText().length() > 0) if (p2PPeersTextArea.getText().length() > 0)
authenticatedPeersTextArea.appendText("\n"); p2PPeersTextArea.appendText("\n");
authenticatedPeersTextArea.appendText(e.getFullAddress()); p2PPeersTextArea.appendText(e.getFullAddress());
if (seedNodeNodeAddresses.contains(e)) if (seedNodeAddresses.contains(e))
authenticatedPeersTextArea.appendText(" (Seed node)"); p2PPeersTextArea.appendText(" (Seed node)");
}); });
} }

View file

@ -83,7 +83,7 @@ public class Transitions {
fade.setInterpolator(Interpolator.EASE_IN); fade.setInterpolator(Interpolator.EASE_IN);
fade.setOnFinished(actionEvent -> { fade.setOnFinished(actionEvent -> {
((Pane) (node.getParent())).getChildren().remove(node); ((Pane) (node.getParent())).getChildren().remove(node);
Profiler.printMsgWithTime("fadeOutAndRemove"); //Profiler.printMsgWithTime("fadeOutAndRemove");
if (handler != null) if (handler != null)
handler.handle(actionEvent); handler.handle(actionEvent);
}); });

View file

@ -1,9 +0,0 @@
package io.bitsquare.p2p;
public class AuthenticationException extends Exception {
public AuthenticationException(String message) {
super(message);
}
}

View file

@ -1,7 +1,7 @@
package io.bitsquare.p2p; package io.bitsquare.p2p;
public abstract class FirstPeerAuthenticatedListener implements P2PServiceListener { public abstract class NetWorkReadyListener implements P2PServiceListener {
@Override @Override
public void onTorNodeReady() { public void onTorNodeReady() {
} }
@ -27,5 +27,5 @@ public abstract class FirstPeerAuthenticatedListener implements P2PServiceListen
} }
@Override @Override
abstract public void onFirstPeerAuthenticated(); abstract public void onBootstrapped();
} }

View file

@ -0,0 +1,9 @@
package io.bitsquare.p2p;
public class NetworkNotReadyException extends RuntimeException {
public NetworkNotReadyException() {
super("You must have bootstrapped before adding data to the P2P network.");
}
}

View file

@ -39,6 +39,7 @@ public class NodeAddress implements Serializable {
NodeAddress nodeAddress = (NodeAddress) o; NodeAddress nodeAddress = (NodeAddress) o;
//noinspection SimplifiableIfStatement
if (port != nodeAddress.port) return false; if (port != nodeAddress.port) return false;
return !(hostName != null ? !hostName.equals(nodeAddress.hostName) : nodeAddress.hostName != null); return !(hostName != null ? !hostName.equals(nodeAddress.hostName) : nodeAddress.hostName != null);

View file

@ -8,6 +8,7 @@ import com.google.inject.name.Named;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.app.ProgramArguments; import io.bitsquare.app.ProgramArguments;
import io.bitsquare.common.ByteArray; import io.bitsquare.common.ByteArray;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.CryptoException; import io.bitsquare.common.crypto.CryptoException;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.crypto.PubKeyRing; import io.bitsquare.common.crypto.PubKeyRing;
@ -15,7 +16,8 @@ import io.bitsquare.crypto.EncryptionService;
import io.bitsquare.crypto.SealedAndSignedMessage; import io.bitsquare.crypto.SealedAndSignedMessage;
import io.bitsquare.p2p.messaging.*; import io.bitsquare.p2p.messaging.*;
import io.bitsquare.p2p.network.*; import io.bitsquare.p2p.network.*;
import io.bitsquare.p2p.peers.AuthenticationListener; import io.bitsquare.p2p.peers.Broadcaster;
import io.bitsquare.p2p.peers.PeerExchangeManager;
import io.bitsquare.p2p.peers.PeerManager; import io.bitsquare.p2p.peers.PeerManager;
import io.bitsquare.p2p.peers.RequestDataManager; import io.bitsquare.p2p.peers.RequestDataManager;
import io.bitsquare.p2p.seed.SeedNodesRepository; import io.bitsquare.p2p.seed.SeedNodesRepository;
@ -25,9 +27,10 @@ import io.bitsquare.p2p.storage.data.ExpirableMailboxPayload;
import io.bitsquare.p2p.storage.data.ExpirablePayload; import io.bitsquare.p2p.storage.data.ExpirablePayload;
import io.bitsquare.p2p.storage.data.ProtectedData; import io.bitsquare.p2p.storage.data.ProtectedData;
import io.bitsquare.p2p.storage.data.ProtectedMailboxData; import io.bitsquare.p2p.storage.data.ProtectedMailboxData;
import io.bitsquare.storage.Storage;
import javafx.beans.property.*; import javafx.beans.property.*;
import javafx.beans.value.ChangeListener;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
import org.fxmisc.easybind.monadic.MonadicBinding; import org.fxmisc.easybind.monadic.MonadicBinding;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -42,42 +45,37 @@ import java.util.concurrent.CopyOnWriteArraySet;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
public class P2PService implements SetupListener, MessageListener, ConnectionListener, HashMapChangedListener, AuthenticationListener { public class P2PService implements SetupListener, MessageListener, ConnectionListener, RequestDataManager.Listener, HashMapChangedListener {
private static final Logger log = LoggerFactory.getLogger(P2PService.class); private static final Logger log = LoggerFactory.getLogger(P2PService.class);
private final SeedNodesRepository seedNodesRepository; protected final SeedNodesRepository seedNodesRepository;
private final int port; protected final int port;
private final File torDir; protected final File torDir;
private final boolean useLocalhost; protected final Optional<EncryptionService> optionalEncryptionService;
protected final File storageDir; protected final Optional<KeyRing> optionalKeyRing;
private final Optional<EncryptionService> optionalEncryptionService;
private final Optional<KeyRing> optionalKeyRing;
// set in init // set in init
protected NetworkNode networkNode; private NetworkNode networkNode;
protected PeerManager peerManager; private P2PDataStorage p2PDataStorage;
protected P2PDataStorage dataStorage; private PeerManager peerManager;
private RequestDataManager requestDataManager;
private PeerExchangeManager peerExchangeManager;
private final CopyOnWriteArraySet<DecryptedMailListener> decryptedMailListeners = new CopyOnWriteArraySet<>(); @SuppressWarnings("FieldCanBeLocal")
private final CopyOnWriteArraySet<DecryptedMailboxListener> decryptedMailboxListeners = new CopyOnWriteArraySet<>(); private MonadicBinding<Boolean> networkReadyBinding;
protected final CopyOnWriteArraySet<P2PServiceListener> p2pServiceListeners = new CopyOnWriteArraySet<>(); private final Set<DecryptedMailListener> decryptedMailListeners = new CopyOnWriteArraySet<>();
private final Set<DecryptedMailboxListener> decryptedMailboxListeners = new CopyOnWriteArraySet<>();
private final Set<P2PServiceListener> p2pServiceListeners = new CopyOnWriteArraySet<>();
private final Map<DecryptedMsgWithPubKey, ProtectedMailboxData> mailboxMap = new HashMap<>(); private final Map<DecryptedMsgWithPubKey, ProtectedMailboxData> mailboxMap = new HashMap<>();
private final Set<NodeAddress> authenticatedPeerNodeAddresses = new HashSet<>(); private final Set<Runnable> shutDownResultHandlers = new CopyOnWriteArraySet<>();
private final CopyOnWriteArraySet<Runnable> shutDownResultHandlers = new CopyOnWriteArraySet<>(); private final BooleanProperty hiddenServicePublished = new SimpleBooleanProperty();
protected final BooleanProperty hiddenServicePublished = new SimpleBooleanProperty(); private final BooleanProperty preliminaryDataReceived = new SimpleBooleanProperty();
private final BooleanProperty requestingDataCompleted = new SimpleBooleanProperty(); private final IntegerProperty numConnectedPeers = new SimpleIntegerProperty(0);
protected final BooleanProperty notAuthenticated = new SimpleBooleanProperty(true);
private final IntegerProperty numAuthenticatedPeers = new SimpleIntegerProperty(0);
private NodeAddress seedNodeOfInitialDataRequest;
private volatile boolean shutDownInProgress; private volatile boolean shutDownInProgress;
private boolean shutDownComplete; private boolean shutDownComplete;
@SuppressWarnings("FieldCanBeLocal") private ChangeListener<Connection.State> stateChangeListener;
private MonadicBinding<Boolean> readyForAuthenticationBinding; private Subscription networkReadySubscription;
private final Storage<NodeAddress> dbStorage;
private NodeAddress myOnionNodeAddress;
protected RequestDataManager requestDataManager;
protected Set<NodeAddress> seedNodeNodeAddresses;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -97,89 +95,52 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
this.seedNodesRepository = seedNodesRepository; this.seedNodesRepository = seedNodesRepository;
this.port = port; this.port = port;
this.torDir = torDir; this.torDir = torDir;
this.useLocalhost = useLocalhost;
this.storageDir = storageDir;
optionalEncryptionService = encryptionService == null ? Optional.empty() : Optional.of(encryptionService); optionalEncryptionService = encryptionService == null ? Optional.empty() : Optional.of(encryptionService);
optionalKeyRing = keyRing == null ? Optional.empty() : Optional.of(keyRing); optionalKeyRing = keyRing == null ? Optional.empty() : Optional.of(keyRing);
dbStorage = new Storage<>(storageDir);
init(networkId, storageDir); init(useLocalhost, networkId, storageDir);
} }
private void init(int networkId, File storageDir) { protected void init(boolean useLocalhost, int networkId, File storageDir) {
Log.traceCall(); Log.traceCall();
// lets check if we have already stored our onion address stateChangeListener = (observable, oldValue, newValue) -> {
NodeAddress persistedOnionNodeAddress = dbStorage.initAndGetPersisted("myOnionAddress"); Set<NodeAddress> nodeAddressesOfAllSucceededConnections = networkNode.getNodeAddressesOfSucceededConnections();
if (persistedOnionNodeAddress != null) Set<Connection> allSucceededConnections = networkNode.getSucceededConnections();
this.myOnionNodeAddress = persistedOnionNodeAddress; log.info("List of peers (duplicates possible of inbound/outbound with same node address)\n" + nodeAddressesOfAllSucceededConnections);
log.info("Nr of connections: {} / {}", nodeAddressesOfAllSucceededConnections.size(), allSucceededConnections.size());
UserThread.execute(() -> numConnectedPeers.set(nodeAddressesOfAllSucceededConnections.size()));
};
seedNodeNodeAddresses = seedNodesRepository.getSeedNodeAddresses(useLocalhost, networkId);
// network node
networkNode = useLocalhost ? new LocalhostNetworkNode(port) : new TorNetworkNode(port, torDir); networkNode = useLocalhost ? new LocalhostNetworkNode(port) : new TorNetworkNode(port, torDir);
networkNode.addConnectionListener(this); networkNode.addConnectionListener(this);
networkNode.addMessageListener(this); networkNode.addMessageListener(this);
// peer group Broadcaster broadcaster = new Broadcaster(networkNode);
peerManager = getNewPeerManager();
peerManager.setSeedNodeAddresses(seedNodeNodeAddresses);
peerManager.addAuthenticationListener(this);
// P2P network data storage p2PDataStorage = new P2PDataStorage(broadcaster, networkNode, storageDir);
dataStorage = new P2PDataStorage(peerManager, networkNode, storageDir); p2PDataStorage.addHashMapChangedListener(this);
dataStorage.addHashMapChangedListener(this);
// Request data manager Set<NodeAddress> seedNodeAddresses = seedNodesRepository.getSeedNodeAddresses(useLocalhost, networkId);
requestDataManager = getNewRequestDataManager(); peerManager = new PeerManager(networkNode, seedNodeAddresses, storageDir);
requestDataManager.setRequestDataManagerListener(new RequestDataManager.Listener() {
@Override
public void onNoSeedNodeAvailable() {
p2pServiceListeners.stream().forEach(e -> e.onNoSeedNodeAvailable());
}
@Override requestDataManager = new RequestDataManager(networkNode, p2PDataStorage, peerManager, seedNodeAddresses);
public void onNoPeersAvailable() { requestDataManager.setRequestDataManagerListener(this);
p2pServiceListeners.stream().forEach(e -> e.onNoPeersAvailable());
}
@Override peerExchangeManager = new PeerExchangeManager(networkNode, peerManager, seedNodeAddresses);
public void onDataReceived(NodeAddress nodeAddress) {
if (!requestingDataCompleted.get()) {
seedNodeOfInitialDataRequest = nodeAddress;
requestingDataCompleted.set(true);
}
p2pServiceListeners.stream().forEach(e -> e.onRequestingDataCompleted());
}
});
peerManager.addAuthenticationListener(requestDataManager);
// Test multiple states to check when we are ready for authenticateSeedNode // We need to have both the initial data delivered and the hidden service published
// We need to have both the initial data delivered and the hidden service published before we networkReadyBinding = EasyBind.combine(hiddenServicePublished, preliminaryDataReceived,
// authenticate to a seed node. (hiddenServicePublished, preliminaryDataReceived)
readyForAuthenticationBinding = getNewReadyForAuthenticationBinding(); -> hiddenServicePublished && preliminaryDataReceived);
readyForAuthenticationBinding.subscribe((observable, oldValue, newValue) -> { networkReadySubscription = networkReadyBinding.subscribe((observable, oldValue, newValue) -> {
if (newValue) if (newValue)
authenticateToSeedNode(); onNetworkReady();
}); });
} }
protected MonadicBinding<Boolean> getNewReadyForAuthenticationBinding() {
return EasyBind.combine(hiddenServicePublished, requestingDataCompleted, notAuthenticated,
(hiddenServicePublished, requestingDataCompleted, notAuthenticated)
-> hiddenServicePublished && requestingDataCompleted && notAuthenticated);
}
protected PeerManager getNewPeerManager() {
return new PeerManager(networkNode, storageDir);
}
protected RequestDataManager getNewRequestDataManager() {
return new RequestDataManager(networkNode, dataStorage, peerManager);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// API // API
@ -200,8 +161,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
shutDownResultHandlers.add(shutDownCompleteHandler); shutDownResultHandlers.add(shutDownCompleteHandler);
if (dataStorage != null) if (p2PDataStorage != null)
dataStorage.shutDown(); p2PDataStorage.shutDown();
if (peerManager != null) if (peerManager != null)
peerManager.shutDown(); peerManager.shutDown();
@ -209,11 +170,17 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
if (requestDataManager != null) if (requestDataManager != null)
requestDataManager.shutDown(); requestDataManager.shutDown();
if (peerExchangeManager != null)
peerExchangeManager.shutDown();
if (networkNode != null) if (networkNode != null)
networkNode.shutDown(() -> { networkNode.shutDown(() -> {
shutDownResultHandlers.stream().forEach(e -> e.run()); shutDownResultHandlers.stream().forEach(Runnable::run);
shutDownComplete = true; shutDownComplete = true;
}); });
if (networkReadySubscription != null)
networkReadySubscription.unsubscribe();
} else { } else {
log.debug("shutDown already in progress"); log.debug("shutDown already in progress");
if (shutDownComplete) { if (shutDownComplete) {
@ -229,16 +196,13 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
* Startup sequence: * Startup sequence:
* <p> * <p>
* Variant 1 (normal expected mode): * Variant 1 (normal expected mode):
* onTorNodeReady -> requestDataManager.requestData() * onTorNodeReady -> requestDataManager.firstDataRequestFromAnySeedNode()
* RequestDataManager.Listener.onDataReceived && onHiddenServicePublished -> authenticateSeedNode() * RequestDataManager.Listener.onDataReceived && onHiddenServicePublished -> onNetworkReady()
* RequestDataManager.onPeerAddressAuthenticated -> RequestDataManager.requestDataFromAuthenticatedSeedNode()
* <p> * <p>
* Variant 2 (no seed node available): * Variant 2 (no seed node available):
* onTorNodeReady -> requestDataManager.requestData * onTorNodeReady -> requestDataManager.firstDataRequestFromAnySeedNode
* RequestDataManager.Listener.onNoSeedNodeAvailable && onHiddenServicePublished -> retry after 20-30 until * retry after 20-30 sec until we get at least one seed node connected
* seed node is available and data can be retrieved * RequestDataManager.Listener.onDataReceived && onHiddenServicePublished -> onNetworkReady()
* RequestDataManager.Listener.onDataReceived && onHiddenServicePublished -> authenticateSeedNode()
* RequestDataManager.onPeerAddressAuthenticated -> RequestDataManager.requestDataFromAuthenticatedSeedNode()
*/ */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -247,25 +211,20 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
@Override @Override
public void onTorNodeReady() { public void onTorNodeReady() {
Log.traceCall(); Log.traceCall();
requestDataManager.requestDataFromSeedNodes(seedNodeNodeAddresses); // 1
p2pServiceListeners.stream().forEach(e -> e.onTorNodeReady()); requestDataManager.requestPreliminaryData();
p2pServiceListeners.stream().forEach(SetupListener::onTorNodeReady);
} }
@Override @Override
public void onHiddenServicePublished() { public void onHiddenServicePublished() {
Log.traceCall(); Log.traceCall();
// 3
checkArgument(networkNode.getNodeAddress() != null, "Address must be set when we have the hidden service ready"); checkArgument(networkNode.getNodeAddress() != null, "Address must be set when we have the hidden service ready");
if (myOnionNodeAddress != null) {
checkArgument(networkNode.getNodeAddress().equals(myOnionNodeAddress),
"If we are a seed node networkNode.getAddress() must be same as myOnionAddress.");
} else {
myOnionNodeAddress = networkNode.getNodeAddress();
dbStorage.queueUpForSave(myOnionNodeAddress);
}
hiddenServicePublished.set(true); hiddenServicePublished.set(true);
p2pServiceListeners.stream().forEach(e -> e.onHiddenServicePublished()); p2pServiceListeners.stream().forEach(SetupListener::onHiddenServicePublished);
} }
@Override @Override
@ -274,10 +233,52 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
p2pServiceListeners.stream().forEach(e -> e.onSetupFailed(throwable)); p2pServiceListeners.stream().forEach(e -> e.onSetupFailed(throwable));
} }
protected void authenticateToSeedNode() {
// Called from networkReadyBinding
private void onNetworkReady() {
Log.traceCall(); Log.traceCall();
checkNotNull(seedNodeOfInitialDataRequest != null, "seedNodeOfInitialDataRequest must not be null"); networkReadySubscription.unsubscribe();
peerManager.authenticateToSeedNode(seedNodeOfInitialDataRequest);
Optional<NodeAddress> seedNodeOfPreliminaryDataRequest = requestDataManager.getSeedNodeOfPreliminaryDataRequest();
checkArgument(seedNodeOfPreliminaryDataRequest.isPresent(),
"seedNodeOfPreliminaryDataRequest must be present");
// 4
requestDataManager.updateDataFromConnectedSeedNode();
// 5
peerExchangeManager.getReportedPeersFromFirstSeedNode(seedNodeOfPreliminaryDataRequest.get());
}
///////////////////////////////////////////////////////////////////////////////////////////
// RequestDataManager.Listener implementation
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onPreliminaryDataReceived() {
checkArgument(!preliminaryDataReceived.get(), "preliminaryDataReceived was already set before.");
// 2
preliminaryDataReceived.set(true);
}
@Override
public void onDataUpdate() {
// Result from requestDataManager.updateDataFromConnectedSeedNode();
p2pServiceListeners.stream().forEach(P2PServiceListener::onBootstrapped);
}
@Override
public void onNoSeedNodeAvailable() {
p2pServiceListeners.stream().forEach(P2PServiceListener::onNoSeedNodeAvailable);
}
@Override
public void onNoPeersAvailable() {
p2pServiceListeners.stream().forEach(P2PServiceListener::onNoPeersAvailable);
}
@Override
public void onDataReceived() {
p2pServiceListeners.stream().forEach(P2PServiceListener::onRequestingDataCompleted);
} }
@ -287,13 +288,13 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
@Override @Override
public void onConnection(Connection connection) { public void onConnection(Connection connection) {
connection.getStateProperty().addListener(stateChangeListener);
} }
@Override @Override
public void onDisconnect(Reason reason, Connection connection) { public void onDisconnect(Reason reason, Connection connection) {
Log.traceCall(); connection.getStateProperty().removeListener(stateChangeListener);
connection.getPeerAddressOptional().ifPresent(peerAddresses -> authenticatedPeerNodeAddresses.remove(peerAddresses)); UserThread.runAfter(() -> numConnectedPeers.set(networkNode.getNodeAddressesOfSucceededConnections().size()), 1);
numAuthenticatedPeers.set(authenticatedPeerNodeAddresses.size());
} }
@Override @Override
@ -301,24 +302,6 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
} }
///////////////////////////////////////////////////////////////////////////////////////////
// AuthenticationListener implementation
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onPeerAuthenticated(NodeAddress peerNodeAddress, Connection connection) {
Log.traceCall();
authenticatedPeerNodeAddresses.add(peerNodeAddress);
if (notAuthenticated.get()) {
notAuthenticated.set(false);
p2pServiceListeners.stream().forEach(e -> e.onFirstPeerAuthenticated());
}
numAuthenticatedPeers.set(authenticatedPeerNodeAddresses.size());
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// MessageListener implementation // MessageListener implementation
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -337,15 +320,14 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
// We set connectionType to that connection to avoid that is get closed when // We set connectionType to that connection to avoid that is get closed when
// we get too many connection attempts. // we get too many connection attempts.
// That is used as protection against eclipse attacks. connection.setPeerType(Connection.PeerType.PEER.DIRECT_MSG_PEER);
connection.setConnectionPriority(ConnectionPriority.DIRECT_MSG);
log.info("\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" + log.info("\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" +
"Decrypted SealedAndSignedMessage:\ndecryptedMsgWithPubKey={}" "Decrypted SealedAndSignedMessage:\ndecryptedMsgWithPubKey={}"
+ "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", decryptedMsgWithPubKey); + "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", decryptedMsgWithPubKey);
connection.getPeerAddressOptional().ifPresent(peerAddresses -> connection.getPeersNodeAddressOptional().ifPresent(peersNodeAddress ->
decryptedMailListeners.stream().forEach( decryptedMailListeners.stream().forEach(
e -> e.onMailMessage(decryptedMsgWithPubKey, peerAddresses))); e -> e.onMailMessage(decryptedMsgWithPubKey, peersNodeAddress)));
} else { } else {
log.info("Wrong receiverAddressMaskHash. The message is not intended for us."); log.info("Wrong receiverAddressMaskHash. The message is not intended for us.");
} }
@ -381,21 +363,14 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
SendMailMessageListener sendMailMessageListener) { SendMailMessageListener sendMailMessageListener) {
Log.traceCall(); Log.traceCall();
checkNotNull(peerNodeAddress, "PeerAddress must not be null (sendEncryptedMailMessage)"); checkNotNull(peerNodeAddress, "PeerAddress must not be null (sendEncryptedMailMessage)");
try { if (isNetworkReady()) {
checkAuthentication();
if (!authenticatedPeerNodeAddresses.contains(peerNodeAddress))
peerManager.authenticateToDirectMessagePeer(peerNodeAddress,
() -> doSendEncryptedMailMessage(peerNodeAddress, pubKeyRing, message, sendMailMessageListener),
() -> sendMailMessageListener.onFault());
else
doSendEncryptedMailMessage(peerNodeAddress, pubKeyRing, message, sendMailMessageListener); doSendEncryptedMailMessage(peerNodeAddress, pubKeyRing, message, sendMailMessageListener);
} catch (AuthenticationException e) { } else {
log.error(e.getMessage()); throw new NetworkNotReadyException();
throw new RuntimeException(e);
} }
} }
private void doSendEncryptedMailMessage(NodeAddress peerNodeAddress, PubKeyRing pubKeyRing, MailMessage message, private void doSendEncryptedMailMessage(NodeAddress peersNodeAddress, PubKeyRing pubKeyRing, MailMessage message,
SendMailMessageListener sendMailMessageListener) { SendMailMessageListener sendMailMessageListener) {
Log.traceCall(); Log.traceCall();
checkArgument(optionalEncryptionService.isPresent(), "EncryptionService 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.");
@ -404,12 +379,18 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
"Encrypt message:\nmessage={}" "Encrypt message:\nmessage={}"
+ "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", message); + "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", message);
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage( SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(
optionalEncryptionService.get().encryptAndSign(pubKeyRing, message), peerNodeAddress.getAddressPrefixHash()); optionalEncryptionService.get().encryptAndSign(pubKeyRing, message), peersNodeAddress.getAddressPrefixHash());
SettableFuture<Connection> future = networkNode.sendMessage(peerNodeAddress, sealedAndSignedMessage); SettableFuture<Connection> future = networkNode.sendMessage(peersNodeAddress, sealedAndSignedMessage);
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
@Override @Override
public void onSuccess(@Nullable Connection connection) { public void onSuccess(@Nullable Connection connection) {
sendMailMessageListener.onArrived(); sendMailMessageListener.onArrived();
if (connection != null) {
if (!connection.getPeersNodeAddressOptional().isPresent() && peersNodeAddress != null)
connection.setPeersNodeAddress(peersNodeAddress);
connection.setPeerType(Connection.PeerType.DIRECT_MSG_PEER);
}
} }
@Override @Override
@ -472,27 +453,16 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
checkNotNull(peerNodeAddress, "PeerAddress must not be null (sendEncryptedMailboxMessage)"); 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.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(!optionalKeyRing.get().getPubKeyRing().equals(peersPubKeyRing), "We got own keyring instead of that from peer");
try {
checkAuthentication(); if (isNetworkReady()) {
if (authenticatedPeerNodeAddresses.contains(peerNodeAddress)) {
trySendEncryptedMailboxMessage(peerNodeAddress, peersPubKeyRing, message, sendMailboxMessageListener); trySendEncryptedMailboxMessage(peerNodeAddress, peersPubKeyRing, message, sendMailboxMessageListener);
} else { } else {
peerManager.authenticateToDirectMessagePeer(peerNodeAddress, throw new NetworkNotReadyException();
() -> trySendEncryptedMailboxMessage(peerNodeAddress, peersPubKeyRing, message, sendMailboxMessageListener),
() -> {
log.info("We cannot authenticate to peer. Peer might be offline. We will store message in mailbox.");
trySendEncryptedMailboxMessage(peerNodeAddress, peersPubKeyRing, message, sendMailboxMessageListener);
});
}
} catch (AuthenticationException e) {
log.error(e.getMessage());
//TODO check if boolean return type can avoid throwing an exception
throw new RuntimeException(e);
} }
} }
// send message and if it fails (peer offline) we store the data to the network // send message and if it fails (peer offline) we store the data to the network
private void trySendEncryptedMailboxMessage(NodeAddress peerNodeAddress, PubKeyRing peersPubKeyRing, private void trySendEncryptedMailboxMessage(NodeAddress peersNodeAddress, PubKeyRing peersPubKeyRing,
MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) { MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) {
Log.traceCall(); 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.");
@ -502,13 +472,20 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
"Encrypt message:\nmessage={}" "Encrypt message:\nmessage={}"
+ "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", message); + "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", message);
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage( SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(
optionalEncryptionService.get().encryptAndSign(peersPubKeyRing, message), peerNodeAddress.getAddressPrefixHash()); optionalEncryptionService.get().encryptAndSign(peersPubKeyRing, message), peersNodeAddress.getAddressPrefixHash());
SettableFuture<Connection> future = networkNode.sendMessage(peerNodeAddress, sealedAndSignedMessage); SettableFuture<Connection> future = networkNode.sendMessage(peersNodeAddress, sealedAndSignedMessage);
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
@Override @Override
public void onSuccess(@Nullable Connection connection) { public void onSuccess(@Nullable Connection connection) {
log.trace("SendEncryptedMailboxMessage onSuccess"); log.trace("SendEncryptedMailboxMessage onSuccess");
sendMailboxMessageListener.onArrived(); sendMailboxMessageListener.onArrived();
if (connection != null) {
if (!connection.getPeersNodeAddressOptional().isPresent() && peersNodeAddress != null)
connection.setPeersNodeAddress(peersNodeAddress);
connection.setPeerType(Connection.PeerType.DIRECT_MSG_PEER);
}
} }
@Override @Override
@ -516,7 +493,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
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 " + peerNodeAddress); log.trace("create MailboxEntry with peerAddress " + peersNodeAddress);
PublicKey receiverStoragePublicKey = peersPubKeyRing.getSignaturePubKey(); PublicKey receiverStoragePublicKey = peersPubKeyRing.getSignaturePubKey();
addMailboxData(new ExpirableMailboxPayload(sealedAndSignedMessage, addMailboxData(new ExpirableMailboxPayload(sealedAndSignedMessage,
optionalKeyRing.get().getSignatureKeyPair().getPublic(), optionalKeyRing.get().getSignatureKeyPair().getPublic(),
@ -535,27 +512,25 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
private void addMailboxData(ExpirableMailboxPayload expirableMailboxPayload, PublicKey receiversPublicKey) { private void addMailboxData(ExpirableMailboxPayload expirableMailboxPayload, PublicKey receiversPublicKey) {
Log.traceCall(); 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 { try {
checkAuthentication(); ProtectedMailboxData protectedMailboxData = p2PDataStorage.getMailboxDataWithSignedSeqNr(
ProtectedMailboxData protectedMailboxData = dataStorage.getMailboxDataWithSignedSeqNr(
expirableMailboxPayload, expirableMailboxPayload,
optionalKeyRing.get().getSignatureKeyPair(), optionalKeyRing.get().getSignatureKeyPair(),
receiversPublicKey); receiversPublicKey);
dataStorage.add(protectedMailboxData, networkNode.getNodeAddress()); p2PDataStorage.add(protectedMailboxData, networkNode.getNodeAddress());
} catch (AuthenticationException e) {
log.error(e.getMessage());
//TODO check if boolean return type can avoid throwing an exception
throw new RuntimeException(e);
} catch (CryptoException e) { } catch (CryptoException e) {
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen."); log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
} }
} else {
throw new NetworkNotReadyException();
}
} }
public void removeEntryFromMailbox(DecryptedMsgWithPubKey decryptedMsgWithPubKey) { public void removeEntryFromMailbox(DecryptedMsgWithPubKey decryptedMsgWithPubKey) {
Log.traceCall(); 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.");
try { if (isNetworkReady()) {
checkAuthentication();
if (mailboxMap.containsKey(decryptedMsgWithPubKey)) { if (mailboxMap.containsKey(decryptedMsgWithPubKey)) {
ProtectedMailboxData mailboxData = mailboxMap.get(decryptedMsgWithPubKey); ProtectedMailboxData mailboxData = mailboxMap.get(decryptedMsgWithPubKey);
if (mailboxData != null && mailboxData.expirablePayload instanceof ExpirableMailboxPayload) { if (mailboxData != null && mailboxData.expirablePayload instanceof ExpirableMailboxPayload) {
@ -564,11 +539,11 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
checkArgument(receiversPubKey.equals(optionalKeyRing.get().getSignatureKeyPair().getPublic()), checkArgument(receiversPubKey.equals(optionalKeyRing.get().getSignatureKeyPair().getPublic()),
"receiversPubKey is not matching with our key. That must not happen."); "receiversPubKey is not matching with our key. That must not happen.");
try { try {
ProtectedMailboxData protectedMailboxData = dataStorage.getMailboxDataWithSignedSeqNr( ProtectedMailboxData protectedMailboxData = p2PDataStorage.getMailboxDataWithSignedSeqNr(
expirableMailboxPayload, expirableMailboxPayload,
optionalKeyRing.get().getSignatureKeyPair(), optionalKeyRing.get().getSignatureKeyPair(),
receiversPubKey); receiversPubKey);
dataStorage.removeMailboxData(protectedMailboxData, networkNode.getNodeAddress()); p2PDataStorage.removeMailboxData(protectedMailboxData, networkNode.getNodeAddress());
} catch (CryptoException e) { } catch (CryptoException e) {
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen."); log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
} }
@ -580,11 +555,10 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
log.warn("decryptedMsgWithPubKey not found in mailboxMap. That should never happen." + log.warn("decryptedMsgWithPubKey not found in mailboxMap. That should never happen." +
"\ndecryptedMsgWithPubKey={}\nmailboxMap={}", decryptedMsgWithPubKey, mailboxMap); "\ndecryptedMsgWithPubKey={}\nmailboxMap={}", decryptedMsgWithPubKey, mailboxMap);
} }
} catch (AuthenticationException e) { } else {
log.error(e.getMessage()); throw new NetworkNotReadyException();
//TODO check if boolean return type can avoid throwing an exception
throw new RuntimeException(e);
} }
} }
@ -605,36 +579,36 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
private boolean doAddData(ExpirablePayload expirablePayload, boolean rePublish) { private boolean doAddData(ExpirablePayload expirablePayload, boolean rePublish) {
Log.traceCall(); 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 { try {
checkAuthentication(); ProtectedData protectedData = p2PDataStorage.getDataWithSignedSeqNr(expirablePayload, optionalKeyRing.get().getSignatureKeyPair());
ProtectedData protectedData = dataStorage.getDataWithSignedSeqNr(expirablePayload, optionalKeyRing.get().getSignatureKeyPair());
if (rePublish) if (rePublish)
return dataStorage.rePublish(protectedData, networkNode.getNodeAddress()); return p2PDataStorage.rePublish(protectedData, networkNode.getNodeAddress());
else else
return dataStorage.add(protectedData, networkNode.getNodeAddress()); return p2PDataStorage.add(protectedData, networkNode.getNodeAddress());
} catch (AuthenticationException e) {
log.error(e.getMessage());
return false;
} catch (CryptoException e) { } catch (CryptoException e) {
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen."); log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
return false; return false;
} }
} else {
throw new NetworkNotReadyException();
}
} }
public boolean removeData(ExpirablePayload expirablePayload) { public boolean removeData(ExpirablePayload expirablePayload) {
Log.traceCall(); 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 { try {
checkAuthentication(); ProtectedData protectedData = p2PDataStorage.getDataWithSignedSeqNr(expirablePayload, optionalKeyRing.get().getSignatureKeyPair());
ProtectedData protectedData = dataStorage.getDataWithSignedSeqNr(expirablePayload, optionalKeyRing.get().getSignatureKeyPair()); return p2PDataStorage.remove(protectedData, networkNode.getNodeAddress());
return dataStorage.remove(protectedData, networkNode.getNodeAddress());
} catch (AuthenticationException e) {
log.error(e.getMessage());
return false;
} catch (CryptoException e) { } catch (CryptoException e) {
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen."); log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
return false; return false;
} }
} else {
throw new NetworkNotReadyException();
}
} }
@ -667,7 +641,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
} }
public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) { public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) {
dataStorage.addHashMapChangedListener(hashMapChangedListener); p2PDataStorage.addHashMapChangedListener(hashMapChangedListener);
} }
@ -675,33 +649,30 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public boolean isAuthenticated() { public boolean isNetworkReady() {
return !notAuthenticated.get(); log.debug("###### isNetworkReady networkReadyBinding " + networkReadyBinding.get());
log.debug("###### isNetworkReady hiddenServicePublished.get() && preliminaryDataReceived.get() " + (hiddenServicePublished.get() && preliminaryDataReceived.get()));
return hiddenServicePublished.get() && preliminaryDataReceived.get();
} }
public NetworkNode getNetworkNode() { public NetworkNode getNetworkNode() {
return networkNode; return networkNode;
} }
public PeerManager getPeerManager() {
return peerManager;
}
public NodeAddress getAddress() { public NodeAddress getAddress() {
return networkNode.getNodeAddress(); return networkNode.getNodeAddress();
} }
public Set<NodeAddress> getAuthenticatedPeerNodeAddresses() { public Set<NodeAddress> getNodeAddressesOfConnectedPeers() {
return authenticatedPeerNodeAddresses; return networkNode.getNodeAddressesOfSucceededConnections();
} }
@NotNull public ReadOnlyIntegerProperty getNumConnectedPeers() {
public ReadOnlyIntegerProperty getNumAuthenticatedPeers() { return numConnectedPeers;
return numAuthenticatedPeers;
} }
public Map<ByteArray, ProtectedData> getDataMap() { public Map<ByteArray, ProtectedData> getDataMap() {
return dataStorage.getMap(); return p2PDataStorage.getMap();
} }
@ -710,8 +681,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private boolean verifyAddressPrefixHash(SealedAndSignedMessage sealedAndSignedMessage) { private boolean verifyAddressPrefixHash(SealedAndSignedMessage sealedAndSignedMessage) {
if (myOnionNodeAddress != null) { if (networkNode.getNodeAddress() != null) {
byte[] blurredAddressHash = myOnionNodeAddress.getAddressPrefixHash(); byte[] blurredAddressHash = networkNode.getNodeAddress().getAddressPrefixHash();
return blurredAddressHash != null && return blurredAddressHash != null &&
Arrays.equals(blurredAddressHash, sealedAndSignedMessage.addressPrefixHash); Arrays.equals(blurredAddressHash, sealedAndSignedMessage.addressPrefixHash);
} else { } else {
@ -719,10 +690,4 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
return false; return false;
} }
} }
private void checkAuthentication() throws AuthenticationException {
Log.traceCall();
if (authenticatedPeerNodeAddresses.isEmpty())
throw new AuthenticationException("You must be authenticated before adding data to the P2P network.");
}
} }

View file

@ -11,5 +11,5 @@ public interface P2PServiceListener extends SetupListener {
void onNoPeersAvailable(); void onNoPeersAvailable();
void onFirstPeerAuthenticated(); void onBootstrapped();
} }

View file

@ -1,59 +0,0 @@
package io.bitsquare.p2p;
import io.bitsquare.app.Log;
import io.bitsquare.p2p.peers.PeerManager;
import io.bitsquare.p2p.peers.RequestDataManager;
import io.bitsquare.p2p.peers.SeedNodePeerManager;
import io.bitsquare.p2p.peers.SeedNodeRequestDataManager;
import io.bitsquare.p2p.seed.SeedNodesRepository;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.monadic.MonadicBinding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
public class SeedNodeP2PService extends P2PService {
private static final Logger log = LoggerFactory.getLogger(SeedNodeP2PService.class);
public SeedNodeP2PService(SeedNodesRepository seedNodesRepository,
NodeAddress mySeedNodeNodeAddress,
File torDir,
boolean useLocalhost,
int networkId,
File storageDir) {
super(seedNodesRepository, mySeedNodeNodeAddress.port, torDir, useLocalhost, networkId, storageDir, null, null);
// we remove ourselves from the list of seed nodes
seedNodeNodeAddresses.remove(mySeedNodeNodeAddress);
}
@Override
protected PeerManager getNewPeerManager() {
return new SeedNodePeerManager(networkNode);
}
@Override
protected RequestDataManager getNewRequestDataManager() {
return new SeedNodeRequestDataManager(networkNode, dataStorage, peerManager);
}
@Override
protected MonadicBinding<Boolean> getNewReadyForAuthenticationBinding() {
return EasyBind.combine(hiddenServicePublished, notAuthenticated,
(hiddenServicePublished, notAuthenticated) -> hiddenServicePublished && notAuthenticated);
}
@Override
public void onTorNodeReady() {
Log.traceCall();
p2pServiceListeners.stream().forEach(e -> e.onTorNodeReady());
}
@Override
protected void authenticateToSeedNode() {
Log.traceCall();
((SeedNodePeerManager) peerManager).authenticateToSeedNode();
}
}

View file

@ -47,6 +47,7 @@ public final class DecryptedMsgWithPubKey implements MailMessage {
DecryptedMsgWithPubKey that = (DecryptedMsgWithPubKey) o; DecryptedMsgWithPubKey that = (DecryptedMsgWithPubKey) o;
//noinspection SimplifiableIfStatement
if (message != null ? !message.equals(that.message) : that.message != null) return false; if (message != null ? !message.equals(that.message) : that.message != null) return false;
return !(signaturePubKey != null ? !signaturePubKey.equals(that.signaturePubKey) : that.signaturePubKey != null); return !(signaturePubKey != null ? !signaturePubKey.equals(that.signaturePubKey) : that.signaturePubKey != null);

View file

@ -10,6 +10,9 @@ import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.Utils; import io.bitsquare.p2p.Utils;
import io.bitsquare.p2p.network.messages.CloseConnectionMessage; import io.bitsquare.p2p.network.messages.CloseConnectionMessage;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
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;
@ -35,16 +38,41 @@ public class Connection implements MessageListener {
private static final int MAX_MSG_SIZE = 5 * 1024 * 1024; // 5 MB of compressed data private static final int MAX_MSG_SIZE = 5 * 1024 * 1024; // 5 MB of compressed data
//timeout on blocking Socket operations like ServerSocket.accept() or SocketInputStream.read() //timeout on blocking Socket operations like ServerSocket.accept() or SocketInputStream.read()
private static final int SOCKET_TIMEOUT = 10 * 60 * 1000; // 10 min. private static final int SOCKET_TIMEOUT = 10 * 60 * 1000; // 10 min.
private ConnectionPriority connectionPriority;
public static int getMaxMsgSize() { public static int getMaxMsgSize() {
return MAX_MSG_SIZE; return MAX_MSG_SIZE;
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Enums
///////////////////////////////////////////////////////////////////////////////////////////
public enum Direction {
OUTBOUND,
INBOUND
}
public enum State {
IDLE,
SUCCEEDED,
FAILED
}
public enum PeerType {
SEED_NODE,
PEER,
DIRECT_MSG_PEER
}
///////////////////////////////////////////////////////////////////////////////////////////
// Class fields
///////////////////////////////////////////////////////////////////////////////////////////
private final Socket socket; private final Socket socket;
private final MessageListener messageListener; private final MessageListener messageListener;
private final ConnectionListener connectionListener; private final ConnectionListener connectionListener;
private final Direction direction;
private final String portInfo; private final String portInfo;
private final String uid = UUID.randomUUID().toString(); private final String uid = UUID.randomUUID().toString();
private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
@ -56,8 +84,7 @@ public class Connection implements MessageListener {
private ObjectOutputStream objectOutputStream; private ObjectOutputStream objectOutputStream;
// mutable data, set from other threads but not changed internally. // mutable data, set from other threads but not changed internally.
private Optional<NodeAddress> peerAddressOptional = Optional.empty(); private Optional<NodeAddress> peersNodeAddressOptional = Optional.empty();
private volatile boolean isAuthenticated;
private volatile boolean stopped; private volatile boolean stopped;
//TODO got java.util.zip.DataFormatException: invalid distance too far back //TODO got java.util.zip.DataFormatException: invalid distance too far back
@ -66,14 +93,20 @@ public class Connection implements MessageListener {
private final boolean useCompression = false; private final boolean useCompression = false;
private final ObjectProperty<State> stateProperty = new SimpleObjectProperty<>();
private PeerType peerType;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public Connection(Socket socket, MessageListener messageListener, ConnectionListener connectionListener) { public Connection(Socket socket, MessageListener messageListener, ConnectionListener connectionListener, Direction direction) {
this.socket = socket; this.socket = socket;
this.messageListener = messageListener; this.messageListener = messageListener;
this.connectionListener = connectionListener; this.connectionListener = connectionListener;
this.direction = direction;
stateProperty.set(State.IDLE);
sharedSpace = new SharedSpace(this, socket); sharedSpace = new SharedSpace(this, socket);
@ -118,25 +151,16 @@ public class Connection implements MessageListener {
// API // API
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Called form UserThread
public void setAuthenticated() {
Log.traceCall();
isAuthenticated = true;
}
public void setConnectionPriority(ConnectionPriority connectionPriority) {
this.connectionPriority = connectionPriority;
}
// Called form various threads // Called form various threads
public void sendMessage(Message message) { public void sendMessage(Message message) {
Log.traceCall(); Log.traceCall();
if (!stopped) { if (!stopped) {
try { try {
String peerAddress = peerAddressOptional.isPresent() ? peerAddressOptional.get().toString() : "null"; String peersNodeAddress = peersNodeAddressOptional.isPresent() ? peersNodeAddressOptional.get().toString() : "null";
log.info("\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + log.info("\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" +
"Write object to outputStream to peer: {} (uid={})\nmessage={}" "Write object to outputStream to peer: {} (uid={})\nmessage={}"
+ "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", peerAddress, uid, message); + "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", peersNodeAddress, uid, message);
Object objectToWrite; Object objectToWrite;
if (useCompression) { if (useCompression) {
@ -170,12 +194,6 @@ public class Connection implements MessageListener {
sharedSpace.reportIllegalRequest(illegalRequest); sharedSpace.reportIllegalRequest(illegalRequest);
} }
public synchronized void setPeerAddress(NodeAddress peerNodeAddress) {
Log.traceCall();
checkNotNull(peerNodeAddress, "peerAddress must not be null");
peerAddressOptional = Optional.of(peerNodeAddress);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// MessageListener implementation // MessageListener implementation
@ -188,27 +206,50 @@ public class Connection implements MessageListener {
UserThread.execute(() -> messageListener.onMessage(message, this)); UserThread.execute(() -> messageListener.onMessage(message, this));
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Setters
///////////////////////////////////////////////////////////////////////////////////////////
public void setPeerType(PeerType peerType) {
Log.traceCall(peerType.toString());
this.peerType = peerType;
}
public void setState(State state) {
Log.traceCall(state.toString());
if (state == State.SUCCEEDED) {
String peersNodeAddress = getPeersNodeAddressOptional().isPresent() ? getPeersNodeAddressOptional().get().getFullAddress() : "";
log.info("\n\n############################################################\n" +
"We are successfully connected to:\n" +
"peerAddress= " + peersNodeAddress +
"\nuid=" + getUid() +
"\n############################################################\n");
}
this.stateProperty.set(state);
}
public synchronized void setPeersNodeAddress(NodeAddress peerNodeAddress) {
Log.traceCall();
checkNotNull(peerNodeAddress, "peerAddress must not be null");
peersNodeAddressOptional = Optional.of(peerNodeAddress);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Nullable public synchronized Optional<NodeAddress> getPeersNodeAddressOptional() {
public synchronized NodeAddress getPeerAddress() { return peersNodeAddressOptional;
return peerAddressOptional.isPresent() ? peerAddressOptional.get() : null;
}
public synchronized Optional<NodeAddress> getPeerAddressOptional() {
return peerAddressOptional;
} }
public Date getLastActivityDate() { public Date getLastActivityDate() {
return sharedSpace.getLastActivityDate(); return sharedSpace.getLastActivityDate();
} }
public boolean isAuthenticated() {
return isAuthenticated;
}
public String getUid() { public String getUid() {
return uid; return uid;
} }
@ -217,8 +258,20 @@ public class Connection implements MessageListener {
return stopped; return stopped;
} }
public ConnectionPriority getConnectionPriority() { public PeerType getPeerType() {
return connectionPriority; return peerType;
}
public Direction getDirection() {
return direction;
}
public ReadOnlyObjectProperty<State> getStateProperty() {
return stateProperty;
}
public State getState() {
return stateProperty.get();
} }
@ -241,14 +294,13 @@ public class Connection implements MessageListener {
private void shutDown(boolean sendCloseConnectionMessage, @Nullable Runnable shutDownCompleteHandler) { private void shutDown(boolean sendCloseConnectionMessage, @Nullable Runnable shutDownCompleteHandler) {
Log.traceCall(this.toString()); Log.traceCall(this.toString());
if (!stopped) { if (!stopped) {
String peerAddress = peerAddressOptional.isPresent() ? peerAddressOptional.get().toString() : "null"; String peersNodeAddress = peersNodeAddressOptional.isPresent() ? peersNodeAddressOptional.get().toString() : "null";
log.info("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" + log.info("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" +
"ShutDown connection:" "ShutDown connection:"
+ "\npeerAddress=" + peerAddress + "\npeersNodeAddress=" + peersNodeAddress
+ "\nlocalPort/port=" + sharedSpace.getSocket().getLocalPort() + "\nlocalPort/port=" + sharedSpace.getSocket().getLocalPort()
+ "/" + sharedSpace.getSocket().getPort() + "/" + sharedSpace.getSocket().getPort()
+ "\nuid=" + uid + "\nuid=" + uid
+ "\nisAuthenticated=" + isAuthenticated
+ "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"); + "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n");
log.trace("ShutDown connection requested. Connection=" + this.toString()); log.trace("ShutDown connection requested. Connection=" + this.toString());
@ -281,7 +333,6 @@ public class Connection implements MessageListener {
sharedSpace.stop(); sharedSpace.stop();
if (inputHandler != null) if (inputHandler != null)
inputHandler.stop(); inputHandler.stop();
isAuthenticated = false;
} }
private void doShutDown(@Nullable Runnable shutDownCompleteHandler) { private void doShutDown(@Nullable Runnable shutDownCompleteHandler) {
@ -319,8 +370,9 @@ public class Connection implements MessageListener {
Connection that = (Connection) o; Connection that = (Connection) o;
if (portInfo != null ? !portInfo.equals(that.portInfo) : that.portInfo != null) return false; if (portInfo != null ? !portInfo.equals(that.portInfo) : that.portInfo != null) return false;
//noinspection SimplifiableIfStatement
if (uid != null ? !uid.equals(that.uid) : that.uid != null) return false; if (uid != null ? !uid.equals(that.uid) : that.uid != null) return false;
return peerAddressOptional != null ? peerAddressOptional.equals(that.peerAddressOptional) : that.peerAddressOptional == null; return peersNodeAddressOptional != null ? peersNodeAddressOptional.equals(that.peersNodeAddressOptional) : that.peersNodeAddressOptional == null;
} }
@ -328,21 +380,21 @@ public class Connection implements MessageListener {
public int hashCode() { public int hashCode() {
int result = portInfo != null ? portInfo.hashCode() : 0; int result = portInfo != null ? portInfo.hashCode() : 0;
result = 31 * result + (uid != null ? uid.hashCode() : 0); result = 31 * result + (uid != null ? uid.hashCode() : 0);
result = 31 * result + (peerAddressOptional != null ? peerAddressOptional.hashCode() : 0); result = 31 * result + (peersNodeAddressOptional != null ? peersNodeAddressOptional.hashCode() : 0);
return result; return result;
} }
@Override @Override
public String toString() { public String toString() {
return "Connection{" + return "Connection{" +
"portInfo=" + portInfo + "peerAddress=" + peersNodeAddressOptional +
", peerType=" + peerType +
", direction=" + direction +
", state=" + getState() +
", portInfo=" + portInfo +
", uid='" + uid + '\'' + ", uid='" + uid + '\'' +
", sharedSpace=" + sharedSpace.toString() + ", sharedSpace=" + sharedSpace.toString() +
", peerAddress=" + peerAddressOptional +
", isAuthenticated=" + isAuthenticated +
", stopped=" + stopped + ", stopped=" + stopped +
", stopped=" + stopped +
", connectionType=" + connectionPriority +
", useCompression=" + useCompression + ", useCompression=" + useCompression +
'}'; '}';
} }

View file

@ -1,8 +0,0 @@
package io.bitsquare.p2p.network;
public enum ConnectionPriority {
PASSIVE, // for connections initiated by other peer
ACTIVE, // for connections initiated by us
DIRECT_MSG, // for connections used for direct messaging
AUTH_REQUEST // for connections used for starting the authentication
}

View file

@ -3,8 +3,7 @@ package io.bitsquare.p2p.network;
public enum IllegalRequest { public enum IllegalRequest {
// TODO check for needed allowed tolerance // TODO check for needed allowed tolerance
MaxSizeExceeded(1), MaxSizeExceeded(1),
NotAuthenticated(1), InvalidDataType(0),
InvalidDataType(1),
WrongNetworkId(0); WrongNetworkId(0);
public final int maxTolerance; public final int maxTolerance;

View file

@ -60,7 +60,7 @@ public class LocalhostNetworkNode extends NetworkNode {
//Tor delay simulation //Tor delay simulation
createTorNode(torNode -> { createTorNode(torNode -> {
Log.traceCall("torNode created"); Log.traceCall("torNode created");
setupListeners.stream().forEach(e -> e.onTorNodeReady()); setupListeners.stream().forEach(SetupListener::onTorNodeReady);
// Create Hidden Service (takes about 40 sec.) // Create Hidden Service (takes about 40 sec.)
createHiddenService(hiddenServiceDescriptor -> { createHiddenService(hiddenServiceDescriptor -> {
@ -74,7 +74,7 @@ public class LocalhostNetworkNode extends NetworkNode {
nodeAddress = new NodeAddress("localhost", servicePort); nodeAddress = new NodeAddress("localhost", servicePort);
setupListeners.stream().forEach(e -> e.onHiddenServicePublished()); setupListeners.stream().forEach(SetupListener::onHiddenServicePublished);
}); });
}); });
} }

View file

@ -20,6 +20,7 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -63,11 +64,11 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
abstract public void start(@Nullable SetupListener setupListener); abstract public void start(@Nullable SetupListener setupListener);
public SettableFuture<Connection> sendMessage(@NotNull NodeAddress peerNodeAddress, Message message) { public SettableFuture<Connection> sendMessage(@NotNull NodeAddress peersNodeAddress, Message message) {
Log.traceCall("peerAddress: " + peerNodeAddress + " / message: " + message); Log.traceCall("peerAddress: " + peersNodeAddress + " / message: " + message);
checkNotNull(peerNodeAddress, "peerAddress must not be null"); checkNotNull(peersNodeAddress, "peerAddress must not be null");
Optional<Connection> outboundConnectionOptional = lookupOutboundConnection(peerNodeAddress); Optional<Connection> outboundConnectionOptional = lookupOutboundConnection(peersNodeAddress);
Connection connection = outboundConnectionOptional.isPresent() ? outboundConnectionOptional.get() : null; Connection connection = outboundConnectionOptional.isPresent() ? outboundConnectionOptional.get() : null;
if (connection != null) if (connection != null)
log.trace("We have found a connection in outBoundConnections. Connection.uid=" + connection.getUid()); log.trace("We have found a connection in outBoundConnections. Connection.uid=" + connection.getUid());
@ -79,7 +80,7 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
} }
if (connection == null) { if (connection == null) {
Optional<Connection> inboundConnectionOptional = lookupInboundConnection(peerNodeAddress); Optional<Connection> inboundConnectionOptional = lookupInboundConnection(peersNodeAddress);
if (inboundConnectionOptional.isPresent()) connection = inboundConnectionOptional.get(); if (inboundConnectionOptional.isPresent()) connection = inboundConnectionOptional.get();
if (connection != null) if (connection != null)
log.trace("We have found a connection in inBoundConnections. Connection.uid=" + connection.getUid()); log.trace("We have found a connection in inBoundConnections. Connection.uid=" + connection.getUid());
@ -89,28 +90,29 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
return sendMessage(connection, message); return sendMessage(connection, message);
} else { } else {
log.trace("We have not found any connection for peerAddress {}. " + log.trace("We have not found any connection for peerAddress {}. " +
"We will create a new outbound connection.", peerNodeAddress); "We will create a new outbound connection.", peersNodeAddress);
final SettableFuture<Connection> resultFuture = SettableFuture.create(); final SettableFuture<Connection> resultFuture = SettableFuture.create();
final boolean[] timeoutOccurred = new boolean[1]; final boolean[] timeoutOccurred = new boolean[1];
timeoutOccurred[0] = false; timeoutOccurred[0] = false;
ListenableFuture<Connection> future = executorService.submit(() -> { ListenableFuture<Connection> future = executorService.submit(() -> {
Thread.currentThread().setName("NetworkNode:SendMessage-to-" + peerNodeAddress); Thread.currentThread().setName("NetworkNode:SendMessage-to-" + peersNodeAddress);
Connection newConnection = null;
try { try {
// can take a while when using tor // can take a while when using tor
Socket socket = createSocket(peerNodeAddress); Socket socket = createSocket(peersNodeAddress);
if (timeoutOccurred[0]) if (timeoutOccurred[0])
throw new TimeoutException("Timeout occurred when tried to create Socket to peer: " + peerNodeAddress); throw new TimeoutException("Timeout occurred when tried to create Socket to peer: " + peersNodeAddress);
newConnection = new Connection(socket, NetworkNode.this, NetworkNode.this, Connection.Direction.OUTBOUND);
Connection newConnection = new Connection(socket, NetworkNode.this, NetworkNode.this); newConnection.setPeersNodeAddress(peersNodeAddress);
newConnection.setPeerAddress(peerNodeAddress);
outBoundConnections.add(newConnection); outBoundConnections.add(newConnection);
log.info("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" + log.info("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" +
"NetworkNode created new outbound connection:" "NetworkNode created new outbound connection:"
+ "\npeerAddress=" + peerNodeAddress + "\npeerAddress=" + peersNodeAddress
+ "\nconnection.uid=" + newConnection.getUid() + "\nuid=" + newConnection.getUid()
+ "\nmessage=" + message + "\nmessage=" + message
+ "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"); + "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n");
@ -122,28 +124,17 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
throwable.printStackTrace(); throwable.printStackTrace();
log.error("Executing task failed. " + throwable.getMessage()); log.error("Executing task failed. " + throwable.getMessage());
} }
if (newConnection != null)
newConnection.setState(Connection.State.FAILED);
throw throwable; throw throwable;
} }
}); });
//TODO does not close the connection yet. not clear if socket timeout is enough.
/*Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Thread.currentThread().setName("TimerTask-" + new Random().nextInt(10000));
timeoutOccurred[0] = true;
future.cancel(true);
String message = "Timeout occurred when tried to create Socket to peer: " + peerAddress;
log.info(message);
UserThread.execute(() -> resultFuture.setException(new TimeoutException(message)));
}
}, CREATE_SOCKET_TIMEOUT);*/
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
public void onSuccess(Connection connection) { public void onSuccess(Connection connection) {
UserThread.execute(() -> { UserThread.execute(() -> {
//timer.cancel(); //timer.cancel();
connection.setState(Connection.State.SUCCEEDED);
resultFuture.set(connection); resultFuture.set(connection);
}); });
} }
@ -151,6 +142,13 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
public void onFailure(@NotNull Throwable throwable) { public void onFailure(@NotNull Throwable throwable) {
UserThread.execute(() -> { UserThread.execute(() -> {
//timer.cancel(); //timer.cancel();
if (lookupInboundConnection(peersNodeAddress).isPresent()) {
lookupInboundConnection(peersNodeAddress).get().setState(Connection.State.FAILED);
} else if (lookupOutboundConnection(peersNodeAddress).isPresent()) {
lookupOutboundConnection(peersNodeAddress).get().setState(Connection.State.FAILED);
}
resultFuture.setException(throwable); resultFuture.setException(throwable);
}); });
} }
@ -171,10 +169,12 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
final SettableFuture<Connection> resultFuture = SettableFuture.create(); final SettableFuture<Connection> resultFuture = SettableFuture.create();
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
public void onSuccess(Connection connection) { public void onSuccess(Connection connection) {
connection.setState(Connection.State.SUCCEEDED);
UserThread.execute(() -> resultFuture.set(connection)); UserThread.execute(() -> resultFuture.set(connection));
} }
public void onFailure(@NotNull Throwable throwable) { public void onFailure(@NotNull Throwable throwable) {
connection.setState(Connection.State.FAILED);
UserThread.execute(() -> resultFuture.setException(throwable)); UserThread.execute(() -> resultFuture.setException(throwable));
} }
}); });
@ -182,12 +182,30 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
} }
public Set<Connection> getAllConnections() { public Set<Connection> getAllConnections() {
Log.traceCall(); // Can contain inbound and outbound connections with the same peer node address,
// as connection hashcode is using uid and port info
Set<Connection> set = new HashSet<>(inBoundConnections); Set<Connection> set = new HashSet<>(inBoundConnections);
set.addAll(outBoundConnections); set.addAll(outBoundConnections);
return set; return set;
} }
public Set<Connection> getSucceededConnections() {
// Can contain inbound and outbound connections with the same peer node address,
// as connection hashcode is using uid and port info
return getAllConnections().stream()
.filter(e -> e.getPeersNodeAddressOptional().isPresent())
.filter(e -> e.getState().equals(Connection.State.SUCCEEDED))
.collect(Collectors.toSet());
}
public Set<NodeAddress> getNodeAddressesOfSucceededConnections() {
// Does not contain inbound and outbound connection with the same peer node address
return getSucceededConnections().stream()
.map(e -> e.getPeersNodeAddressOptional().get())
.collect(Collectors.toSet());
}
public void shutDown(Runnable shutDownCompleteHandler) { public void shutDown(Runnable shutDownCompleteHandler) {
Log.traceCall(); Log.traceCall();
log.info("Shutdown NetworkNode"); log.info("Shutdown NetworkNode");
@ -198,7 +216,7 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
server = null; server = null;
} }
getAllConnections().stream().forEach(e -> e.shutDown()); getAllConnections().stream().forEach(Connection::shutDown);
log.info("NetworkNode shutdown complete"); log.info("NetworkNode shutdown complete");
if (shutDownCompleteHandler != null) shutDownCompleteHandler.run(); if (shutDownCompleteHandler != null) shutDownCompleteHandler.run();
@ -305,7 +323,6 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
@Override @Override
public void onConnection(Connection connection) { public void onConnection(Connection connection) {
Log.traceCall("startServerConnectionListener connection=" + connection); Log.traceCall("startServerConnectionListener connection=" + connection);
// we still have not authenticated so put it to the temp list
inBoundConnections.add(connection); inBoundConnections.add(connection);
NetworkNode.this.onConnection(connection); NetworkNode.this.onConnection(connection);
} }
@ -329,19 +346,19 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
executorService.submit(server); executorService.submit(server);
} }
private Optional<Connection> lookupOutboundConnection(NodeAddress peerNodeAddress) { private Optional<Connection> lookupOutboundConnection(NodeAddress peersNodeAddress) {
// Log.traceCall("search for " + peerAddress.toString() + " / outBoundConnections " + outBoundConnections); Log.traceCall("search for " + peersNodeAddress.toString() + " / outBoundConnections " + outBoundConnections);
return outBoundConnections.stream() return outBoundConnections.stream()
.filter(e -> e.getPeerAddressOptional().isPresent() && peerNodeAddress.equals(e.getPeerAddressOptional().get())).findAny(); .filter(e -> e.getPeersNodeAddressOptional().isPresent() && peersNodeAddress.equals(e.getPeersNodeAddressOptional().get())).findAny();
} }
private Optional<Connection> lookupInboundConnection(NodeAddress peerNodeAddress) { private Optional<Connection> lookupInboundConnection(NodeAddress peersNodeAddress) {
// Log.traceCall("search for " + peerAddress.toString() + " / inBoundConnections " + inBoundConnections); Log.traceCall("search for " + peersNodeAddress.toString() + " / inBoundConnections " + inBoundConnections);
return inBoundConnections.stream() return inBoundConnections.stream()
.filter(e -> e.getPeerAddressOptional().isPresent() && peerNodeAddress.equals(e.getPeerAddressOptional().get())).findAny(); .filter(e -> e.getPeersNodeAddressOptional().isPresent() && peersNodeAddress.equals(e.getPeersNodeAddressOptional().get())).findAny();
} }
abstract protected Socket createSocket(NodeAddress peerNodeAddress) throws IOException; abstract protected Socket createSocket(NodeAddress peersNodeAddress) throws IOException;
@Nullable @Nullable
abstract public NodeAddress getNodeAddress(); abstract public NodeAddress getNodeAddress();

View file

@ -43,7 +43,7 @@ class Server implements Runnable {
final Socket socket = serverSocket.accept(); final Socket socket = serverSocket.accept();
if (!stopped && !Thread.currentThread().isInterrupted()) { if (!stopped && !Thread.currentThread().isInterrupted()) {
log.info("Accepted new client on localPort/port " + socket.getLocalPort() + "/" + socket.getPort()); log.info("Accepted new client on localPort/port " + socket.getLocalPort() + "/" + socket.getPort());
Connection connection = new Connection(socket, messageListener, connectionListener); Connection connection = new Connection(socket, messageListener, connectionListener, Connection.Direction.INBOUND);
log.info("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" + log.info("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" +
"Server created new inbound connection:" "Server created new inbound connection:"
@ -71,7 +71,7 @@ class Server implements Runnable {
if (!stopped) { if (!stopped) {
stopped = true; stopped = true;
connections.stream().forEach(e -> e.shutDown()); connections.stream().forEach(Connection::shutDown);
try { try {
serverSocket.close(); serverSocket.close();

View file

@ -73,7 +73,7 @@ public class TorNetworkNode extends NetworkNode {
Log.traceCall("torNode created"); Log.traceCall("torNode created");
TorNetworkNode.this.torNetworkNode = torNode; TorNetworkNode.this.torNetworkNode = torNode;
setupListeners.stream().forEach(e -> e.onTorNodeReady()); setupListeners.stream().forEach(SetupListener::onTorNodeReady);
// Create Hidden Service (takes about 40 sec.) // Create Hidden Service (takes about 40 sec.)
createHiddenService(torNode, createHiddenService(torNode,
@ -84,7 +84,7 @@ public class TorNetworkNode extends NetworkNode {
TorNetworkNode.this.hiddenServiceDescriptor = hiddenServiceDescriptor; TorNetworkNode.this.hiddenServiceDescriptor = hiddenServiceDescriptor;
startServer(hiddenServiceDescriptor.getServerSocket()); startServer(hiddenServiceDescriptor.getServerSocket());
setupListeners.stream().forEach(e -> e.onHiddenServicePublished()); setupListeners.stream().forEach(SetupListener::onHiddenServicePublished);
}); });
}); });
} }
@ -117,16 +117,14 @@ public class TorNetworkNode extends NetworkNode {
}, SHUT_DOWN_TIMEOUT, TimeUnit.MILLISECONDS); }, SHUT_DOWN_TIMEOUT, TimeUnit.MILLISECONDS);
if (executorService != null) { if (executorService != null) {
executorService.submit(() -> { executorService.submit(() -> UserThread.execute(() -> {
UserThread.execute(() -> {
// We want to stay in UserThread // We want to stay in UserThread
super.shutDown(() -> { super.shutDown(() -> {
networkNodeShutDownDoneComplete = true; networkNodeShutDownDoneComplete = true;
if (torShutDownComplete) if (torShutDownComplete)
shutDownExecutorService(); shutDownExecutorService();
}); });
}); }));
});
} else { } else {
log.error("executorService must not be null at shutDown"); log.error("executorService must not be null at shutDown");
} }
@ -188,10 +186,9 @@ public class TorNetworkNode extends NetworkNode {
start(null); start(null);
}, WAIT_BEFORE_RESTART, TimeUnit.MILLISECONDS)); }, WAIT_BEFORE_RESTART, TimeUnit.MILLISECONDS));
} else { } else {
String msg = "We tried to restart tor " + restartCounter String msg = "We tried to restart Tor " + restartCounter
+ " times, but we failed to get tor running. We give up now."; + " times, but it failed to start up. We give up now.";
log.error(msg); log.error(msg);
// TODO display better error msg
throw new RuntimeException(msg); throw new RuntimeException(msg);
} }
} }

View file

@ -1,8 +0,0 @@
package io.bitsquare.p2p.peers;
public class AuthenticationException extends Exception {
public AuthenticationException(String message) {
super(message);
}
}

View file

@ -1,357 +0,0 @@
package io.bitsquare.p2p.peers;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture;
import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.network.Connection;
import io.bitsquare.p2p.network.ConnectionPriority;
import io.bitsquare.p2p.network.MessageListener;
import io.bitsquare.p2p.network.NetworkNode;
import io.bitsquare.p2p.peers.messages.auth.*;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
// Authentication protocol:
// client: send AuthenticationRequest to seedNode
// seedNode: close connection
// seedNode: send AuthenticationChallenge to client on a new connection to test if address is correct
// client: authentication to seedNode done if nonce verification is ok
// client: AuthenticationFinalResponse to seedNode
// seedNode: authentication to client done if nonce verification is ok
public class AuthenticationHandshake implements MessageListener {
private static final Logger log = LoggerFactory.getLogger(AuthenticationHandshake.class);
private final NetworkNode networkNode;
private final NodeAddress myNodeAddress;
private final NodeAddress peerNodeAddress;
private final Supplier<Set<ReportedPeer>> authenticatedAndReportedPeersSupplier;
private final BiConsumer<HashSet<ReportedPeer>, Connection> addReportedPeersConsumer;
private final long startAuthTs;
private long nonce = 0;
private boolean stopped;
private Optional<SettableFuture<Connection>> resultFutureOptional = Optional.empty();
private Timer timeoutTimer, shutDownTimer;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public AuthenticationHandshake(NetworkNode networkNode,
NodeAddress myNodeAddress,
NodeAddress peerNodeAddress,
Supplier<Set<ReportedPeer>> authenticatedAndReportedPeersSupplier,
BiConsumer<HashSet<ReportedPeer>, Connection> addReportedPeersConsumer) {
Log.traceCall("peerAddress " + peerNodeAddress);
this.authenticatedAndReportedPeersSupplier = authenticatedAndReportedPeersSupplier;
this.addReportedPeersConsumer = addReportedPeersConsumer;
this.networkNode = networkNode;
this.myNodeAddress = myNodeAddress;
this.peerNodeAddress = peerNodeAddress;
startAuthTs = System.currentTimeMillis();
networkNode.addMessageListener(this);
}
///////////////////////////////////////////////////////////////////////////////////////////
// MessageListener implementation
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onMessage(Message message, Connection connection) {
// called from other thread but mapped to user thread. That can cause async behaviour.
// Example: We got the AuthenticationHandshake shut down and the message listener
// has been already removed but we still get the onMessage called as the Platform.runLater get called at the next
// cycle. So we need to protect a late call with the stopped flag.
if (!stopped) {
if (message instanceof AuthenticationMessage) {
// We are listening on all connections, so we need to filter out only our peer
if (((AuthenticationMessage) message).senderNodeAddress.equals(peerNodeAddress)) {
Log.traceCall(message.toString());
if (timeoutTimer != null)
timeoutTimer.cancel();
if (message instanceof AuthenticationChallenge) {
// Requesting peer
AuthenticationChallenge authenticationChallenge = (AuthenticationChallenge) message;
// We need to set the address to the connection, otherwise we will not find the connection when sending
// the next message and we would create a new outbound connection instead using the inbound.
connection.setPeerAddress(authenticationChallenge.senderNodeAddress);
// We use the active connectionType if we started the authentication request to another peer
connection.setConnectionPriority(ConnectionPriority.ACTIVE);
log.trace("Received authenticationChallenge from " + peerNodeAddress);
boolean verified = nonce != 0 && nonce == authenticationChallenge.requesterNonce;
if (verified) {
AuthenticationFinalResponse authenticationFinalResponse = new AuthenticationFinalResponse(myNodeAddress,
authenticationChallenge.responderNonce,
new HashSet<>(authenticatedAndReportedPeersSupplier.get()));
SettableFuture<Connection> future = networkNode.sendMessage(peerNodeAddress, authenticationFinalResponse);
log.trace("Sent AuthenticationFinalResponse {} to {}", authenticationFinalResponse, peerNodeAddress);
Futures.addCallback(future, new FutureCallback<Connection>() {
@Override
public void onSuccess(Connection connection) {
log.trace("Successfully sent AuthenticationFinalResponse to {}", peerNodeAddress);
log.info("AuthenticationComplete: Peer with address " + peerNodeAddress
+ " authenticated (" + connection.getUid() + "). Took "
+ (System.currentTimeMillis() - startAuthTs) + " ms.");
completed(connection);
}
@Override
public void onFailure(@NotNull Throwable throwable) {
log.info("AuthenticationFinalResponse sending failed " + throwable.getMessage());
failed(throwable);
}
});
// now we add the reported peers to our list
addReportedPeersConsumer.accept(authenticationChallenge.reportedPeers, connection);
} else {
// We don't call failed as it might be that we get an old authenticationChallenge from a
// previously timed out request
// We simply ignore the authenticationChallenge if the nonce is not matching to avoid that
// the current authentication turn gets terminated as well
log.warn("Verification of nonce failed. Maybe we got an old authenticationChallenge " +
"from a timed out request" +
"\nnonce={} / peerAddress={} / authenticationChallenge={}", nonce, peerNodeAddress, authenticationChallenge);
//failed(new AuthenticationException("Verification of nonce failed. AuthenticationChallenge=" + authenticationChallenge + " / nonceMap=" + nonce));
}
} else if (message instanceof AuthenticationFinalResponse) {
// Responding peer
AuthenticationFinalResponse authenticationFinalResponse = (AuthenticationFinalResponse) message;
log.trace("Received AuthenticationFinalResponse from " + peerNodeAddress + " at " + myNodeAddress);
boolean verified = nonce != 0 && nonce == authenticationFinalResponse.responderNonce;
if (verified) {
addReportedPeersConsumer.accept(authenticationFinalResponse.reportedPeers, connection);
log.info("AuthenticationComplete: Peer with address " + peerNodeAddress
+ " authenticated (" + connection.getUid() + "). Took "
+ (System.currentTimeMillis() - startAuthTs) + " ms.");
completed(connection);
} else {
// We don't call failed as it might be that we get an old authenticationFinalResponse from a
// previously timed out request
// We simply ignore the authenticationFinalResponse if the nonce is not matching to avoid that
// the current authentication turn gets terminated as well
log.warn("Verification of nonce failed. Maybe we got an old authenticationFinalResponse " +
"from a timed out request" +
"\nnonce={} / peerAddress={} / authenticationChallenge={}", nonce, peerNodeAddress, authenticationFinalResponse);
log.warn("Verification of nonce failed. nonce={} / peerAddress={} / authenticationFinalResponse={}", nonce, peerNodeAddress, authenticationFinalResponse);
//failed(new AuthenticationException("Verification of nonce failed. getPeersMessage=" + authenticationFinalResponse + " / nonce=" + nonce));
}
} else if (message instanceof AuthenticationRejection) {
// Any peer
failed(new AuthenticationException("Authentication to peer "
+ ((AuthenticationRejection) message).senderNodeAddress
+ " rejected because of a race conditions."));
}
}
}
} else {
// TODO leave that for debugging for now, but remove it once the network is tested sufficiently
log.info("AuthenticationHandshake (peerAddress={}) already shut down but still got onMessage called. " +
"That can happen because of Thread mapping.", peerNodeAddress);
log.debug("message={}", message);
log.debug("connection={}", connection);
return;
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Authentication initiated by requesting peer
///////////////////////////////////////////////////////////////////////////////////////////
public SettableFuture<Connection> requestAuthentication() {
Log.traceCall("peerAddress " + peerNodeAddress);
// Requesting peer
if (stopped) {
// TODO leave that for debugging for now, but remove it once the network is tested sufficiently
log.warn("AuthenticationHandshake (peerAddress={}) already shut down but still got requestAuthentication called. That must not happen.", peerNodeAddress);
}
resultFutureOptional = Optional.of(SettableFuture.create());
AuthenticationRequest authenticationRequest = new AuthenticationRequest(myNodeAddress, getAndSetNonce());
SettableFuture<Connection> future = networkNode.sendMessage(peerNodeAddress, authenticationRequest);
Futures.addCallback(future, new FutureCallback<Connection>() {
@Override
public void onSuccess(Connection connection) {
log.trace("send AuthenticationRequest to " + peerNodeAddress + " succeeded.");
// We protect that connection from getting closed by maintenance cleanup...
connection.setConnectionPriority(ConnectionPriority.AUTH_REQUEST);
}
@Override
public void onFailure(@NotNull Throwable throwable) {
log.info("Send AuthenticationRequest to " + peerNodeAddress + " failed. " +
"It might be that the peer went offline.\nException:" + throwable.getMessage());
failed(throwable);
}
});
if (timeoutTimer != null)
timeoutTimer.cancel();
timeoutTimer = UserThread.runAfter(() -> failed(new AuthenticationException("Authentication to peer "
+ peerNodeAddress
+ " failed because of a timeout. " +
"We did not get an AuthenticationChallenge message responded after 30 sec.")), 30);
return resultFutureOptional.get();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Responding to authentication request
///////////////////////////////////////////////////////////////////////////////////////////
public SettableFuture<Connection> respondToAuthenticationRequest(AuthenticationRequest authenticationRequest,
Connection connection) {
Log.traceCall("peerAddress " + peerNodeAddress);
// Responding peer
if (stopped) {
// TODO leave that for debugging for now, but remove it once the network is tested sufficiently
log.warn("AuthenticationHandshake (peerAddress={}) already shut down but still got respondToAuthenticationRequest called. That must not happen.", peerNodeAddress);
log.warn("authenticationRequest={}", authenticationRequest);
log.warn("connection={}", connection);
}
resultFutureOptional = Optional.of(SettableFuture.create());
log.info("We shut down inbound connection from peer {} to establish a new " +
"connection with his reported address to verify if his address is correct.", peerNodeAddress);
connection.shutDown(() -> {
if (shutDownTimer != null)
shutDownTimer.cancel();
shutDownTimer = UserThread.runAfter(() -> {
if (!stopped) {
// we delay a bit as listeners for connection.onDisconnect are on other threads and might lead to
// inconsistent state
log.trace("respondToAuthenticationRequest: connection.shutDown complete. peerAddress=" + peerNodeAddress + " / myAddress=" + myNodeAddress);
// we send additionally the reported and authenticated peers to save one message in the protocol.
AuthenticationChallenge authenticationChallenge = new AuthenticationChallenge(myNodeAddress,
authenticationRequest.requesterNonce,
getAndSetNonce(),
new HashSet<>(authenticatedAndReportedPeersSupplier.get()));
SettableFuture<Connection> future = networkNode.sendMessage(peerNodeAddress, authenticationChallenge);
Futures.addCallback(future, new FutureCallback<Connection>() {
@Override
public void onSuccess(Connection connection) {
log.trace("AuthenticationChallenge successfully sent");
// We use passive connectionType for connections created from received authentication
// requests from other peers
connection.setConnectionPriority(ConnectionPriority.PASSIVE);
}
@Override
public void onFailure(@NotNull Throwable throwable) {
log.warn("Failure at sending AuthenticationChallenge to {}. It might be that the peer went offline. Exception={}", peerNodeAddress, throwable.getMessage());
failed(throwable);
}
});
if (timeoutTimer != null)
timeoutTimer.cancel();
timeoutTimer = UserThread.runAfter(() -> failed(new AuthenticationException("Authentication of peer "
+ peerNodeAddress
+ " failed because of a timeout. " +
"We did not get an AuthenticationFinalResponse message responded after 30 sec.\n" +
"")), 30, TimeUnit.SECONDS);
} else {
log.info("AuthenticationHandshake (peerAddress={}) already shut down before we could sent " +
"AuthenticationChallenge. That might happen in rare cases.", peerNodeAddress);
}
}, 2000, TimeUnit.MILLISECONDS); // Don't set the delay too short as the CloseConnectionMessage might arrive too late at the peer
});
return resultFutureOptional.get();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Cancel
///////////////////////////////////////////////////////////////////////////////////////////
public void cancel(NodeAddress peerNodeAddress) {
Log.traceCall();
failed(new AuthenticationException("Authentication to peer "
+ peerNodeAddress
+ " canceled because of a race conditions."));
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getter
///////////////////////////////////////////////////////////////////////////////////////////
public Optional<SettableFuture<Connection>> getResultFutureOptional() {
return resultFutureOptional;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private long getAndSetNonce() {
Log.traceCall();
nonce = new Random().nextLong();
while (nonce == 0)
nonce = new Random().nextLong();
return nonce;
}
private void failed(@NotNull Throwable throwable) {
Log.traceCall(throwable.toString());
shutDown();
if (resultFutureOptional.isPresent())
resultFutureOptional.get().setException(throwable);
else
log.warn("failed called but resultFuture = null. That must never happen.");
}
private void completed(Connection connection) {
Log.traceCall();
shutDown();
if (resultFutureOptional.isPresent())
resultFutureOptional.get().set(connection);
else
log.warn("completed called but resultFuture = null. That must never happen.");
}
private void shutDown() {
Log.traceCall("peerAddress = " + peerNodeAddress);
stopped = true;
if (timeoutTimer != null)
timeoutTimer.cancel();
if (shutDownTimer != null)
shutDownTimer.cancel();
networkNode.removeMessageListener(this);
}
}

View file

@ -1,8 +0,0 @@
package io.bitsquare.p2p.peers;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.network.Connection;
public interface AuthenticationListener {
void onPeerAuthenticated(NodeAddress peerNodeAddress, Connection connection);
}

View file

@ -0,0 +1,60 @@
package io.bitsquare.p2p.peers;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture;
import io.bitsquare.app.Log;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.network.Connection;
import io.bitsquare.p2p.network.NetworkNode;
import io.bitsquare.p2p.storage.messages.DataBroadcastMessage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
public class Broadcaster {
private static final Logger log = LoggerFactory.getLogger(Broadcaster.class);
private final NetworkNode networkNode;
public Broadcaster(NetworkNode networkNode) {
this.networkNode = networkNode;
}
public void broadcast(DataBroadcastMessage message, @Nullable NodeAddress sender) {
Log.traceCall("Sender " + sender + ". Message " + message.toString());
Set<NodeAddress> receivers = networkNode.getNodeAddressesOfSucceededConnections();
if (!receivers.isEmpty()) {
log.info("Broadcast message to {} peers. Message: {}", receivers.size(), message);
receivers.stream()
.filter(e -> !e.equals(sender))
.forEach(nodeAddress -> {
log.trace("Broadcast message from " + networkNode.getNodeAddress() + " to " + nodeAddress + ".");
SettableFuture<Connection> future = networkNode.sendMessage(nodeAddress, message);
Futures.addCallback(future, new FutureCallback<Connection>() {
@Override
public void onSuccess(Connection connection) {
log.trace("Broadcast from " + networkNode.getNodeAddress() + " to " + nodeAddress + " succeeded.");
if (connection != null) {
if (!connection.getPeersNodeAddressOptional().isPresent())
connection.setPeersNodeAddress(nodeAddress);
if (connection.getPeerType() == null)
connection.setPeerType(Connection.PeerType.PEER);
}
}
@Override
public void onFailure(@NotNull Throwable throwable) {
log.info("Broadcast failed. " + throwable.getMessage());
}
});
});
} else {
log.info("Message not broadcasted because we have no available peers yet. " +
"message = {}", message);
}
}
}

View file

@ -10,6 +10,7 @@ import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.network.Connection; import io.bitsquare.p2p.network.Connection;
import io.bitsquare.p2p.network.ConnectionListener;
import io.bitsquare.p2p.network.MessageListener; import io.bitsquare.p2p.network.MessageListener;
import io.bitsquare.p2p.network.NetworkNode; import io.bitsquare.p2p.network.NetworkNode;
import io.bitsquare.p2p.peers.messages.peers.GetPeersRequest; import io.bitsquare.p2p.peers.messages.peers.GetPeersRequest;
@ -19,47 +20,39 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.HashSet; import javax.annotation.Nullable;
import java.util.Map; import java.util.*;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer; import java.util.stream.Collectors;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class PeerExchangeManager implements MessageListener { public class PeerExchangeManager implements MessageListener, ConnectionListener {
private static final Logger log = LoggerFactory.getLogger(PeerExchangeManager.class); private static final Logger log = LoggerFactory.getLogger(PeerExchangeManager.class);
private final NetworkNode networkNode; private final NetworkNode networkNode;
private final Supplier<Set<ReportedPeer>> authenticatedAndReportedPeersSupplier; private final PeerManager peerManager;
private final Supplier<Map<NodeAddress, Peer>> authenticatedPeersSupplier; private final Set<NodeAddress> seedNodeAddresses;
private final Consumer<NodeAddress> removePeerConsumer;
private final BiConsumer<HashSet<ReportedPeer>, Connection> addReportedPeersConsumer;
private final ScheduledThreadPoolExecutor executor; private final ScheduledThreadPoolExecutor executor;
private Timer requestReportedPeersTimer, checkForSeedNodesTimer;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public PeerExchangeManager(NetworkNode networkNode, public PeerExchangeManager(NetworkNode networkNode, PeerManager peerManager, Set<NodeAddress> seedNodeAddresses) {
Supplier<Set<ReportedPeer>> authenticatedAndReportedPeersSupplier,
Supplier<Map<NodeAddress, Peer>> authenticatedPeersSupplier,
Consumer<NodeAddress> removePeerConsumer,
BiConsumer<HashSet<ReportedPeer>, Connection> addReportedPeersConsumer) {
this.networkNode = networkNode; this.networkNode = networkNode;
this.authenticatedAndReportedPeersSupplier = authenticatedAndReportedPeersSupplier; this.peerManager = peerManager;
this.authenticatedPeersSupplier = authenticatedPeersSupplier; this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
this.removePeerConsumer = removePeerConsumer;
this.addReportedPeersConsumer = addReportedPeersConsumer;
networkNode.addMessageListener(this); networkNode.addMessageListener(this);
executor = Utilities.getScheduledThreadPoolExecutor("PeerExchangeManager", 1, 10, 5); executor = Utilities.getScheduledThreadPoolExecutor("PeerExchangeManager", 1, 10, 5);
long delay = new Random().nextInt(60) + 60 * 6; // 6-7 min. long delay = new Random().nextInt(60) + 60 * 6; // 6-7 min.
executor.scheduleAtFixedRate(() -> UserThread.execute(() -> trySendGetPeersRequest()), delay, delay, TimeUnit.SECONDS); executor.scheduleAtFixedRate(() -> UserThread.execute(() -> {
sendGetPeersRequestToAllConnectedPeers();
checkSeedNodes();
}), delay, delay, TimeUnit.SECONDS);
} }
public void shutDown() { public void shutDown() {
@ -67,6 +60,28 @@ public class PeerExchangeManager implements MessageListener {
networkNode.removeMessageListener(this); networkNode.removeMessageListener(this);
MoreExecutors.shutdownAndAwaitTermination(executor, 500, TimeUnit.MILLISECONDS); MoreExecutors.shutdownAndAwaitTermination(executor, 500, TimeUnit.MILLISECONDS);
stopRequestReportedPeersTimer();
stopCheckForSeedNodesTimer();
}
///////////////////////////////////////////////////////////////////////////////////////////
// ConnectionListener implementation
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onConnection(Connection connection) {
}
@Override
public void onDisconnect(Reason reason, Connection connection) {
if (checkForSeedNodesTimer == null)
checkForSeedNodesTimer = UserThread.runAfter(this::checkSeedNodes,
networkNode.getAllConnections().size() * 10, TimeUnit.SECONDS);
}
@Override
public void onError(Throwable throwable) {
} }
@ -82,53 +97,221 @@ public class PeerExchangeManager implements MessageListener {
GetPeersRequest getPeersRequestMessage = (GetPeersRequest) message; GetPeersRequest getPeersRequestMessage = (GetPeersRequest) message;
HashSet<ReportedPeer> reportedPeers = getPeersRequestMessage.reportedPeers; HashSet<ReportedPeer> reportedPeers = getPeersRequestMessage.reportedPeers;
log.trace("Received peers: " + reportedPeers); log.trace("Received peers: " + reportedPeers);
if (!connection.getPeersNodeAddressOptional().isPresent())
connection.setPeersNodeAddress(getPeersRequestMessage.senderNodeAddress);
SettableFuture<Connection> future = networkNode.sendMessage(connection, SettableFuture<Connection> future = networkNode.sendMessage(connection,
new GetPeersResponse(new HashSet<>(authenticatedAndReportedPeersSupplier.get()))); new GetPeersResponse(getReportedPeersHashSet(getPeersRequestMessage.senderNodeAddress)));
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
@Override @Override
public void onSuccess(Connection connection) { public void onSuccess(Connection connection) {
log.trace("GetPeersResponse sent successfully"); log.trace("GetPeersResponse sent successfully");
handleOnSuccess(connection, null);
} }
@Override @Override
public void onFailure(@NotNull Throwable throwable) { public void onFailure(@NotNull Throwable throwable) {
log.info("GetPeersResponse sending failed " + throwable.getMessage()); log.info("GetPeersResponse sending failed " + throwable.getMessage());
removePeerConsumer.accept(getPeersRequestMessage.senderNodeAddress);
} }
}); });
addReportedPeersConsumer.accept(reportedPeers, connection); peerManager.addToReportedPeers(reportedPeers, connection);
} else if (message instanceof GetPeersResponse) { } else if (message instanceof GetPeersResponse) {
GetPeersResponse getPeersResponse = (GetPeersResponse) message; HashSet<ReportedPeer> reportedPeers = ((GetPeersResponse) message).reportedPeers;
HashSet<ReportedPeer> reportedPeers = getPeersResponse.reportedPeers;
log.trace("Received peers: " + reportedPeers); log.trace("Received peers: " + reportedPeers);
addReportedPeersConsumer.accept(reportedPeers, connection); peerManager.addToReportedPeers(reportedPeers, connection);
if (!peerManager.hasSufficientConnections()) {
List<NodeAddress> remainingNodeAddresses = new ArrayList<>(peerManager.getNodeAddressesOfReportedPeers());
networkNode.getNodeAddressesOfSucceededConnections().stream().forEach(remainingNodeAddresses::remove);
if (!remainingNodeAddresses.isEmpty()) {
NodeAddress nodeAddress = remainingNodeAddresses.get(new Random().nextInt(remainingNodeAddresses.size()));
remainingNodeAddresses.remove(nodeAddress);
requestPeersFromReportedPeers(nodeAddress, remainingNodeAddresses);
}
}
} }
} }
} }
private void trySendGetPeersRequest() { ///////////////////////////////////////////////////////////////////////////////////////////
Set<Peer> connectedPeersList = new HashSet<>(authenticatedPeersSupplier.get().values()); // API
if (!connectedPeersList.isEmpty()) { ///////////////////////////////////////////////////////////////////////////////////////////
Log.traceCall();
connectedPeersList.stream() public void getReportedPeersFromFirstSeedNode(NodeAddress nodeAddress) {
.forEach(e -> { getReportedPeersFromSeedNode(nodeAddress, new ArrayList<>(seedNodeAddresses));
SettableFuture<Connection> future = networkNode.sendMessage(e.connection, }
new GetPeersRequest(networkNode.getNodeAddress(), new HashSet<>(authenticatedAndReportedPeersSupplier.get())));
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void getReportedPeersFromSeedNode(NodeAddress nodeAddress, List<NodeAddress> remainingNodeAddresses) {
Log.traceCall("nodeAddress=" + nodeAddress);
stopRequestReportedPeersTimer();
stopCheckForSeedNodesTimer();
SettableFuture<Connection> future = networkNode.sendMessage(nodeAddress,
new GetPeersRequest(networkNode.getNodeAddress(), getReportedPeersHashSet(nodeAddress)));
Futures.addCallback(future, new FutureCallback<Connection>() {
@Override
public void onSuccess(Connection connection) {
log.trace("GetPeersRequest sent successfully");
handleOnSuccess(connection, nodeAddress);
}
@Override
public void onFailure(@NotNull Throwable throwable) {
log.info("GetPeersRequest sending failed " + throwable.getMessage());
if (!remainingNodeAddresses.isEmpty()) {
log.info("There are more seed nodes available for requesting peers. " +
"We will try getReportedPeersFromFirstSeedNode again.");
NodeAddress nextCandidate = remainingNodeAddresses.get(new Random().nextInt(remainingNodeAddresses.size()));
remainingNodeAddresses.remove(nextCandidate);
getReportedPeersFromSeedNode(nextCandidate, remainingNodeAddresses);
} else {
log.info("There is no seed node available for requesting peers. " +
"That is expected if no seed node is online.\n" +
"We will try again to request peers from a seed node after a random pause.");
requestReportedPeersTimer = UserThread.runAfterRandomDelay(() -> {
if (!seedNodeAddresses.isEmpty()) {
List<NodeAddress> nodeAddresses = new ArrayList<>(seedNodeAddresses);
NodeAddress nextCandidate = nodeAddresses.get(new Random().nextInt(nodeAddresses.size()));
nodeAddresses.remove(nextCandidate);
getReportedPeersFromSeedNode(nextCandidate, nodeAddresses);
}
},
30, 40, TimeUnit.SECONDS);
}
}
});
}
private void handleOnSuccess(Connection connection, @Nullable NodeAddress nodeAddress) {
if (connection != null) {
if (!connection.getPeersNodeAddressOptional().isPresent() && nodeAddress != null)
connection.setPeersNodeAddress(nodeAddress);
if (connection.getPeerType() == null)
connection.setPeerType(peerManager.isSeedNode(connection) ? Connection.PeerType.SEED_NODE : Connection.PeerType.PEER);
}
}
private void requestPeersFromReportedPeers(NodeAddress nodeAddress, List<NodeAddress> remainingNodeAddresses) {
Log.traceCall("nodeAddress=" + nodeAddress);
stopRequestReportedPeersTimer();
SettableFuture<Connection> future = networkNode.sendMessage(nodeAddress,
new GetPeersRequest(networkNode.getNodeAddress(), getReportedPeersHashSet(nodeAddress)));
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
@Override @Override
public void onSuccess(Connection connection) { public void onSuccess(Connection connection) {
log.trace("sendGetPeersRequest sent successfully"); log.trace("sendGetPeersRequest sent successfully");
handleOnSuccess(connection, nodeAddress);
} }
@Override @Override
public void onFailure(@NotNull Throwable throwable) { public void onFailure(@NotNull Throwable throwable) {
log.info("sendGetPeersRequest sending failed " + throwable.getMessage()); log.info("sendGetPeersRequest sending failed " + throwable.getMessage());
removePeerConsumer.accept(e.nodeAddress);
peerManager.removePeer(nodeAddress);
if (!remainingNodeAddresses.isEmpty()) {
log.info("There are more reported peers available for requesting peers. " +
"We will try getReportedPeersFromFirstSeedNode again.");
NodeAddress nextCandidate = remainingNodeAddresses.get(new Random().nextInt(remainingNodeAddresses.size()));
remainingNodeAddresses.remove(nextCandidate);
requestPeersFromReportedPeers(nextCandidate, remainingNodeAddresses);
} else {
log.info("There are no more reported peers available for requesting peers. " +
"We will try again to request peers from the reported peers again after a random pause.");
requestReportedPeersTimer = UserThread.runAfterRandomDelay(() -> {
List<NodeAddress> nodeAddresses = new ArrayList<>(peerManager.getNodeAddressesOfReportedPeers());
if (!nodeAddresses.isEmpty()) {
NodeAddress nextCandidate = nodeAddresses.get(new Random().nextInt(nodeAddresses.size()));
nodeAddresses.remove(nextCandidate);
requestPeersFromReportedPeers(nextCandidate, nodeAddresses);
}
},
30, 40, TimeUnit.SECONDS);
}
} }
}); });
}
private void stopRequestReportedPeersTimer() {
if (requestReportedPeersTimer != null) {
requestReportedPeersTimer.cancel();
requestReportedPeersTimer = null;
}
}
private void stopCheckForSeedNodesTimer() {
if (checkForSeedNodesTimer != null) {
checkForSeedNodesTimer.cancel();
checkForSeedNodesTimer = null;
}
}
private void sendGetPeersRequestToAllConnectedPeers() {
// copy set to avoid issues with changes in set (we dont need to be perfectly in sync so no need to use a concurrent set)
Set<NodeAddress> connectedPeers = new HashSet<>(networkNode.getNodeAddressesOfSucceededConnections());
if (!connectedPeers.isEmpty()) {
Log.traceCall("connectedPeers.size=" + connectedPeers.size());
connectedPeers.stream().forEach(nodeAddress ->
UserThread.runAfterRandomDelay(() ->
sendGetPeersRequest(nodeAddress), 3, 6));
}
}
private void sendGetPeersRequest(NodeAddress nodeAddress) {
Log.traceCall("nodeAddress=" + nodeAddress);
SettableFuture<Connection> future = networkNode.sendMessage(nodeAddress,
new GetPeersRequest(networkNode.getNodeAddress(), getReportedPeersHashSet(nodeAddress)));
Futures.addCallback(future, new FutureCallback<Connection>() {
@Override
public void onSuccess(Connection connection) {
log.trace("sendGetPeersRequest sent successfully");
handleOnSuccess(connection, nodeAddress);
}
@Override
public void onFailure(@NotNull Throwable throwable) {
log.info("sendGetPeersRequest sending failed " + throwable.getMessage());
peerManager.removePeer(nodeAddress);
}
}); });
} }
private HashSet<ReportedPeer> getReportedPeersHashSet(@Nullable NodeAddress receiverNodeAddress) {
return new HashSet<>(peerManager.getConnectedAndReportedPeers().stream()
.filter(e -> !peerManager.isSeedNode(e) &&
!e.nodeAddress.equals(networkNode.getNodeAddress()) &&
!e.nodeAddress.equals(receiverNodeAddress)
)
.collect(Collectors.toSet()));
}
private void checkSeedNodes() {
Log.traceCall();
Set<Connection> allConnections = networkNode.getAllConnections();
List<Connection> seedNodes = allConnections.stream()
.filter(peerManager::isSeedNode)
.collect(Collectors.toList());
if (seedNodes.size() == 0 && !seedNodeAddresses.isEmpty()) {
List<NodeAddress> nodeAddresses = new ArrayList<>(seedNodeAddresses);
NodeAddress nextCandidate = nodeAddresses.get(new Random().nextInt(nodeAddresses.size()));
nodeAddresses.remove(nextCandidate);
getReportedPeersFromSeedNode(nextCandidate, nodeAddresses);
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,6 @@ import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.network.Connection; import io.bitsquare.p2p.network.Connection;
import io.bitsquare.p2p.network.ConnectionPriority;
import io.bitsquare.p2p.network.MessageListener; import io.bitsquare.p2p.network.MessageListener;
import io.bitsquare.p2p.network.NetworkNode; import io.bitsquare.p2p.network.NetworkNode;
import io.bitsquare.p2p.peers.messages.data.DataRequest; import io.bitsquare.p2p.peers.messages.data.DataRequest;
@ -25,9 +24,8 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class RequestDataManager implements MessageListener, AuthenticationListener { public class RequestDataManager implements MessageListener {
private static final Logger log = LoggerFactory.getLogger(RequestDataManager.class); private static final Logger log = LoggerFactory.getLogger(RequestDataManager.class);
@ -40,7 +38,11 @@ public class RequestDataManager implements MessageListener, AuthenticationListen
void onNoPeersAvailable(); void onNoPeersAvailable();
void onDataReceived(NodeAddress seedNode); void onDataReceived();
void onPreliminaryDataReceived();
void onDataUpdate();
} }
@ -50,21 +52,22 @@ public class RequestDataManager implements MessageListener, AuthenticationListen
private final HashSet<ReportedPeer> persistedPeers = new HashSet<>(); private final HashSet<ReportedPeer> persistedPeers = new HashSet<>();
private final HashSet<ReportedPeer> remainingPersistedPeers = new HashSet<>(); private final HashSet<ReportedPeer> remainingPersistedPeers = new HashSet<>();
private Listener listener; private Listener listener;
private Optional<NodeAddress> optionalConnectedSeedNodeAddress = Optional.empty(); private Optional<NodeAddress> seedNodeOfPreliminaryDataRequest = Optional.empty();
private Collection<NodeAddress> seedNodeNodeAddresses; private final Collection<NodeAddress> seedNodeAddresses;
protected Timer requestDataFromAuthenticatedSeedNodeTimer; private Timer requestDataFromSeedNodesTimer, requestDataFromPersistedPeersTimer, dataRequestTimeoutTimer;
private Timer requestDataTimer, requestDataWithPersistedPeersTimer; private boolean noSeedNodeAvailableListenerNotified;
private boolean doNotifyNoSeedNodeAvailableListener = true; private boolean dataUpdateRequested;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public RequestDataManager(NetworkNode networkNode, P2PDataStorage dataStorage, PeerManager peerManager) { public RequestDataManager(NetworkNode networkNode, P2PDataStorage dataStorage, PeerManager peerManager, Set<NodeAddress> seedNodeAddresses) {
this.networkNode = networkNode; this.networkNode = networkNode;
this.dataStorage = dataStorage; this.dataStorage = dataStorage;
this.peerManager = peerManager; this.peerManager = peerManager;
this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
networkNode.addMessageListener(this); networkNode.addMessageListener(this);
} }
@ -74,9 +77,9 @@ public class RequestDataManager implements MessageListener, AuthenticationListen
networkNode.removeMessageListener(this); networkNode.removeMessageListener(this);
stopRequestDataTimer(); stopRequestDataFromSeedNodesTimer();
stopRequestDataWithPersistedPeersTimer(); stopRequestDataFromPersistedPeersTimer();
stopRequestDataFromAuthenticatedSeedNodeTimer(); stopDataRequestTimeoutTimer();
} }
@ -88,40 +91,117 @@ public class RequestDataManager implements MessageListener, AuthenticationListen
this.listener = listener; this.listener = listener;
} }
public void requestDataFromSeedNodes(Collection<NodeAddress> seedNodeNodeAddresses) { public void requestPreliminaryData() {
checkNotNull(seedNodeNodeAddresses, "requestDataFromSeedNodes: seedNodeAddresses must not be null."); requestDataFromPeers(seedNodeAddresses);
checkArgument(!seedNodeNodeAddresses.isEmpty(), "requestDataFromSeedNodes: seedNodeAddresses must not be empty.");
this.seedNodeNodeAddresses = seedNodeNodeAddresses;
requestData(seedNodeNodeAddresses);
} }
private void requestData(Collection<NodeAddress> nodeAddresses) { public void updateDataFromConnectedSeedNode() {
Log.traceCall();
checkArgument(seedNodeOfPreliminaryDataRequest.isPresent(), "seedNodeOfPreliminaryDataRequest must be present");
dataUpdateRequested = true;
requestDataFromPeer(seedNodeOfPreliminaryDataRequest.get(), seedNodeAddresses);
}
public Optional<NodeAddress> getSeedNodeOfPreliminaryDataRequest() {
return seedNodeOfPreliminaryDataRequest;
}
///////////////////////////////////////////////////////////////////////////////////////////
// MessageListener implementation
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onMessage(Message message, Connection connection) {
Optional<NodeAddress> peersNodeAddressOptional = connection.getPeersNodeAddressOptional();
if (message instanceof DataRequest) {
// We are a seed node and receive that msg from a new node
Log.traceCall(message.toString());
DataRequest dataRequest = (DataRequest) message;
if (peersNodeAddressOptional.isPresent()) {
checkArgument(peersNodeAddressOptional.get().equals(dataRequest.senderNodeAddress),
"Sender address in message not matching the peers address in our connection.");
} else if (dataRequest.senderNodeAddress != null) {
// If first data request the peer does not has its address
// in case of requesting from first seed node after hidden service is published we did not knew the peers address
connection.setPeersNodeAddress(dataRequest.senderNodeAddress);
}
networkNode.sendMessage(connection, new DataResponse(new HashSet<>(dataStorage.getMap().values())));
} else if (message instanceof DataResponse) {
// We are the new node which has requested the data
Log.traceCall(message.toString());
DataResponse dataResponse = (DataResponse) message;
HashSet<ProtectedData> set = dataResponse.set;
// we keep that connection open as the bootstrapping peer will use that later for a re-sync
// as the hidden service is not published yet the data adding will not be broadcasted to others
peersNodeAddressOptional.ifPresent(peersNodeAddress -> set.stream().forEach(e -> dataStorage.add(e, peersNodeAddress)));
stopDataRequestTimeoutTimer();
connection.getPeersNodeAddressOptional().ifPresent(e -> {
if (!seedNodeOfPreliminaryDataRequest.isPresent()) {
seedNodeOfPreliminaryDataRequest = Optional.of(e);
listener.onPreliminaryDataReceived();
}
if (dataUpdateRequested) {
dataUpdateRequested = false;
listener.onDataUpdate();
}
listener.onDataReceived();
});
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void requestDataFromPeers(Collection<NodeAddress> nodeAddresses) {
Log.traceCall(nodeAddresses.toString()); Log.traceCall(nodeAddresses.toString());
checkArgument(!nodeAddresses.isEmpty(), "requestData: addresses must not be empty."); checkArgument(!nodeAddresses.isEmpty(), "requestDataFromPeers: nodeAddresses must not be empty.");
stopRequestDataTimer(); stopRequestDataFromSeedNodesTimer();
List<NodeAddress> remainingNodeAddresses = new ArrayList<>(nodeAddresses); List<NodeAddress> remainingNodeAddresses = new ArrayList<>(nodeAddresses);
NodeAddress candidate = remainingNodeAddresses.get(new Random().nextInt(remainingNodeAddresses.size())); NodeAddress candidate = remainingNodeAddresses.get(new Random().nextInt(remainingNodeAddresses.size()));
if (!peerManager.isInAuthenticationProcess(candidate)) { requestDataFromPeer(candidate, remainingNodeAddresses);
// We only remove it if it is not in the process of authentication }
remainingNodeAddresses.remove(candidate);
log.info("We try to send a GetAllDataMessage request to node. " + candidate);
SettableFuture<Connection> future = networkNode.sendMessage(candidate, new DataRequest()); private void requestDataFromPeer(NodeAddress nodeAddress, Collection<NodeAddress> remainingNodeAddresses) {
Log.traceCall(nodeAddress.toString());
remainingNodeAddresses.remove(nodeAddress);
log.info("We try to send a DataRequest request to node. " + nodeAddress);
stopDataRequestTimeoutTimer();
dataRequestTimeoutTimer = UserThread.runAfter(() -> {
log.info("firstDataRequestTimeoutTimer called");
if (!remainingNodeAddresses.isEmpty()) {
requestDataFromPeers(remainingNodeAddresses);
} else {
requestDataFromPersistedPeersAfterDelay(nodeAddress);
requestDataFromSeedNodesAfterDelay();
}
},
10, TimeUnit.SECONDS);
SettableFuture<Connection> future = networkNode.sendMessage(nodeAddress, new DataRequest(networkNode.getNodeAddress()));
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
@Override @Override
public void onSuccess(@Nullable Connection connection) { public void onSuccess(@Nullable Connection connection) {
log.info("Send GetAllDataMessage to " + candidate + " succeeded."); log.info("Send DataRequest to " + nodeAddress + " succeeded.");
checkArgument(!optionalConnectedSeedNodeAddress.isPresent(), "We have already a connectedSeedNode. That must not happen.");
optionalConnectedSeedNodeAddress = Optional.of(candidate);
stopRequestDataTimer(); if (connection != null) {
stopRequestDataWithPersistedPeersTimer(); if (!connection.getPeersNodeAddressOptional().isPresent())
connection.setPeersNodeAddress(nodeAddress);
if (connection.getPeerType() == null)
connection.setPeerType(peerManager.isSeedNode(connection) ? Connection.PeerType.SEED_NODE : Connection.PeerType.PEER);
}
} }
@Override @Override
public void onFailure(@NotNull Throwable throwable) { public void onFailure(@NotNull Throwable throwable) {
log.info("Send GetAllDataMessage to " + candidate + " failed. " + log.info("Send DataRequest to " + nodeAddress + " failed. " +
"That is expected if the node is offline. " + "That is expected if the node is offline. " +
"Exception:" + throwable.getMessage()); "Exception:" + throwable.getMessage());
@ -129,61 +209,40 @@ public class RequestDataManager implements MessageListener, AuthenticationListen
log.info("There are more seed nodes available for requesting data. " + log.info("There are more seed nodes available for requesting data. " +
"We will try requestData again."); "We will try requestData again.");
ReportedPeer reportedPeer = new ReportedPeer(candidate); ReportedPeer reportedPeer = new ReportedPeer(nodeAddress);
if (remainingPersistedPeers.contains(reportedPeer)) if (remainingPersistedPeers.contains(reportedPeer))
remainingPersistedPeers.remove(reportedPeer); remainingPersistedPeers.remove(reportedPeer);
requestData(remainingNodeAddresses); requestDataFromPeers(remainingNodeAddresses);
} else { } else {
log.info("There is no seed node available for requesting data. " + log.info("There is no seed node available for requesting data. " +
"That is expected if no seed node is online.\n" + "That is expected if no seed node is online.\n" +
"We will try again to request data from a seed node after a random pause."); "We will try again to request data from a seed node after a random pause.");
requestDataWithPersistedPeers(candidate); requestDataFromPersistedPeersAfterDelay(nodeAddress);
requestDataWithSeedNodeAddresses(); requestDataFromSeedNodesAfterDelay();
} }
} }
}); });
} else if (!remainingNodeAddresses.isEmpty()) {
log.info("The node ({}) is in the process of authentication.\n" +
"We will try requestData again with the remaining addresses.", candidate);
remainingNodeAddresses.remove(candidate);
if (!remainingNodeAddresses.isEmpty()) {
requestData(remainingNodeAddresses);
} else {
log.info("The node ({}) is in the process of authentication.\n" +
"There are no more remaining addresses available.\n" +
"We try requestData with the persistedPeers and after a pause with " +
"the seed nodes again.", candidate);
requestDataWithPersistedPeers(candidate);
requestDataWithSeedNodeAddresses();
}
} else {
log.info("The node ({}) is in the process of authentication.\n" +
"There are no more remaining addresses available.\n" +
"We try requestData with the persistedPeers and after a pause with " +
"the seed nodes again.", candidate);
requestDataWithPersistedPeers(candidate);
requestDataWithSeedNodeAddresses();
}
} }
private void requestDataWithSeedNodeAddresses() { private void requestDataFromSeedNodesAfterDelay() {
Log.traceCall(); Log.traceCall();
// We only want to notify the first time // We only want to notify the first time
if (doNotifyNoSeedNodeAvailableListener) { if (!noSeedNodeAvailableListenerNotified) {
doNotifyNoSeedNodeAvailableListener = false; noSeedNodeAvailableListenerNotified = true;
listener.onNoSeedNodeAvailable(); listener.onNoSeedNodeAvailable();
} }
if (requestDataTimer == null)
requestDataTimer = UserThread.runAfterRandomDelay(() -> requestData(seedNodeNodeAddresses), if (requestDataFromSeedNodesTimer == null)
requestDataFromSeedNodesTimer = UserThread.runAfterRandomDelay(() -> requestDataFromPeers(seedNodeAddresses),
10, 20, TimeUnit.SECONDS); 10, 20, TimeUnit.SECONDS);
} }
private void requestDataWithPersistedPeers(@Nullable NodeAddress failedPeer) { private void requestDataFromPersistedPeersAfterDelay(@Nullable NodeAddress failedPeer) {
Log.traceCall("failedPeer=" + failedPeer); Log.traceCall("failedPeer=" + failedPeer);
stopRequestDataWithPersistedPeersTimer(); stopRequestDataFromPersistedPeersTimer();
if (persistedPeers.isEmpty()) { if (persistedPeers.isEmpty()) {
persistedPeers.addAll(peerManager.getPersistedPeers()); persistedPeers.addAll(peerManager.getPersistedPeers());
@ -203,117 +262,45 @@ public class RequestDataManager implements MessageListener, AuthenticationListen
if (!persistedPeerNodeAddresses.isEmpty()) { if (!persistedPeerNodeAddresses.isEmpty()) {
log.info("We try to use persisted peers for requestData."); log.info("We try to use persisted peers for requestData.");
persistedPeersAvailable = true; persistedPeersAvailable = true;
requestData(persistedPeerNodeAddresses); requestDataFromPeers(persistedPeerNodeAddresses);
} }
} }
if (!persistedPeersAvailable) { if (!persistedPeersAvailable) {
log.warn("No seed nodes and no persisted peers are available for requesting data.\n" + log.warn("No seed nodes and no persisted peers are available for requesting data.\n" +
"We will try again after a random pause."); "We will try again after a random pause.");
doNotifyNoSeedNodeAvailableListener = false; noSeedNodeAvailableListenerNotified = true;
listener.onNoPeersAvailable(); listener.onNoPeersAvailable();
// reset remainingPersistedPeers // reset remainingPersistedPeers
remainingPersistedPeers.clear(); remainingPersistedPeers.clear();
remainingPersistedPeers.addAll(persistedPeers); remainingPersistedPeers.addAll(persistedPeers);
if (!remainingPersistedPeers.isEmpty() && requestDataWithPersistedPeersTimer == null) if (!remainingPersistedPeers.isEmpty() && requestDataFromPersistedPeersTimer == null)
requestDataWithPersistedPeersTimer = UserThread.runAfterRandomDelay(() -> requestDataFromPersistedPeersTimer = UserThread.runAfterRandomDelay(() ->
requestDataWithPersistedPeers(null), requestDataFromPersistedPeersAfterDelay(null),
30, 40, TimeUnit.SECONDS); 30, 40, TimeUnit.SECONDS);
} }
} }
private void stopRequestDataFromSeedNodesTimer() {
/////////////////////////////////////////////////////////////////////////////////////////// if (requestDataFromSeedNodesTimer != null) {
// MessageListener implementation requestDataFromSeedNodesTimer.cancel();
/////////////////////////////////////////////////////////////////////////////////////////// requestDataFromSeedNodesTimer = null;
@Override
public void onMessage(Message message, Connection connection) {
if (message instanceof DataRequest) {
// We are a seed node and receive that msg from a new node
Log.traceCall(message.toString());
networkNode.sendMessage(connection, new DataResponse(new HashSet<>(dataStorage.getMap().values())));
} else if (message instanceof DataResponse) {
// We are the new node which has requested the data
Log.traceCall(message.toString());
DataResponse dataResponse = (DataResponse) message;
HashSet<ProtectedData> set = dataResponse.set;
// we keep that connection open as the bootstrapping peer will use that for the authentication
// as we are not authenticated yet the data adding will not be broadcasted
connection.getPeerAddressOptional().ifPresent(peerAddress -> set.stream().forEach(e -> dataStorage.add(e, peerAddress)));
optionalConnectedSeedNodeAddress.ifPresent(connectedSeedNodeAddress -> listener.onDataReceived(connectedSeedNodeAddress));
} }
} }
private void stopRequestDataFromPersistedPeersTimer() {
/////////////////////////////////////////////////////////////////////////////////////////// if (requestDataFromPersistedPeersTimer != null) {
// AuthenticationListener implementation requestDataFromPersistedPeersTimer.cancel();
/////////////////////////////////////////////////////////////////////////////////////////// requestDataFromPersistedPeersTimer = null;
@Override
public void onPeerAuthenticated(NodeAddress peerNodeAddress, Connection connection) {
optionalConnectedSeedNodeAddress.ifPresent(connectedSeedNodeAddress -> {
// We only request the data again if we have initiated the authentication (ConnectionPriority.ACTIVE)
// We delay a bit to be sure that the authentication state is applied to all listeners
if (connectedSeedNodeAddress.equals(peerNodeAddress) && connection.getConnectionPriority() == ConnectionPriority.ACTIVE) {
// We are the node (can be a seed node as well) which requested the authentication
if (requestDataFromAuthenticatedSeedNodeTimer == null)
requestDataFromAuthenticatedSeedNodeTimer = UserThread.runAfter(()
-> requestDataFromAuthenticatedSeedNode(peerNodeAddress, connection), 100, TimeUnit.MILLISECONDS);
}
});
}
// 5. Step after authentication to first seed node we request again the data
protected void requestDataFromAuthenticatedSeedNode(NodeAddress peerNodeAddress, Connection connection) {
Log.traceCall(peerNodeAddress.toString());
stopRequestDataFromAuthenticatedSeedNodeTimer();
// We have to request the data again as we might have missed pushed data in the meantime
SettableFuture<Connection> future = networkNode.sendMessage(connection, new DataRequest());
Futures.addCallback(future, new FutureCallback<Connection>() {
@Override
public void onSuccess(@Nullable Connection connection) {
log.info("requestDataFromAuthenticatedSeedNode from " + peerNodeAddress + " succeeded.");
}
@Override
public void onFailure(@NotNull Throwable throwable) {
log.warn("requestDataFromAuthenticatedSeedNode from " + peerNodeAddress + " failed. " +
"Exception:" + throwable.getMessage()
+ "\nWe will try again to request data from any of our seed nodes.");
// We will try again to request data from any of our seed nodes.
if (seedNodeNodeAddresses != null && !seedNodeNodeAddresses.isEmpty())
requestData(seedNodeNodeAddresses);
else
log.error("seedNodeAddresses is null or empty. That must not happen. seedNodeAddresses="
+ seedNodeNodeAddresses);
}
});
}
private void stopRequestDataTimer() {
if (requestDataTimer != null) {
requestDataTimer.cancel();
requestDataTimer = null;
} }
} }
private void stopRequestDataWithPersistedPeersTimer() { private void stopDataRequestTimeoutTimer() {
if (requestDataWithPersistedPeersTimer != null) { if (dataRequestTimeoutTimer != null) {
requestDataWithPersistedPeersTimer.cancel(); dataRequestTimeoutTimer.cancel();
requestDataWithPersistedPeersTimer = null; dataRequestTimeoutTimer = null;
}
}
private void stopRequestDataFromAuthenticatedSeedNodeTimer() {
if (requestDataFromAuthenticatedSeedNodeTimer != null) {
requestDataFromAuthenticatedSeedNodeTimer.cancel();
requestDataFromAuthenticatedSeedNodeTimer = null;
} }
} }
} }

View file

@ -1,86 +0,0 @@
package io.bitsquare.p2p.peers;
import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.network.NetworkNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkArgument;
public class SeedNodePeerManager extends PeerManager {
private static final Logger log = LoggerFactory.getLogger(SeedNodePeerManager.class);
public SeedNodePeerManager(NetworkNode networkNode) {
super(networkNode, null);
}
public void authenticateToSeedNode() {
Log.traceCall();
checkArgument(seedNodeAddressesOptional.isPresent(),
"seedNodeAddresses must be set before calling authenticateToSeedNode");
checkArgument(!seedNodeAddressesOptional.get().isEmpty(),
"seedNodeAddresses must not be empty");
remainingSeedNodes.addAll(seedNodeAddressesOptional.get());
NodeAddress peerNodeAddress = getAndRemoveRandomAddress(remainingSeedNodes);
authenticateToFirstSeedNode(peerNodeAddress);
startCheckSeedNodeConnectionTask();
}
@Override
protected void createDbStorage(File storageDir) {
// Do nothing.
// The seed node does not store persisted peers in the local db
}
@Override
protected void initPersistedPeers() {
// Do nothing.
// The seed node does not store persisted peers in the local db
}
@Override
protected void onFirstSeedNodeAuthenticated() {
// If we are seed node we want to first connect to all other seed nodes before connecting to the reported peers.
authenticateToRemainingSeedNode();
}
@Override
protected void onRemainingSeedNodeAuthenticated() {
// If we are seed node we want to first connect to all other seed nodes before connecting to the reported peers.
authenticateToRemainingSeedNode();
}
@Override
protected void handleNoSeedNodesAvailableCase() {
Log.traceCall();
log.info("We don't have more seed nodes available. " +
"We authenticate to reported peers and try again after a random pause with the seed nodes which failed or if " +
"none available with the reported peers.");
boolean reportedPeersAvailableCalled = false;
if (reportedPeersAvailable()) {
authenticateToRemainingReportedPeer();
reportedPeersAvailableCalled = true;
}
resetRemainingSeedNodes();
if (!remainingSeedNodes.isEmpty()) {
if (authenticateToRemainingSeedNodeTimer == null)
authenticateToRemainingSeedNodeTimer = UserThread.runAfterRandomDelay(() -> authenticateToRemainingSeedNode(),
10, 20, TimeUnit.SECONDS);
} else if (!reportedPeersAvailableCalled) {
if (authenticateToRemainingReportedPeerTimer == null)
authenticateToRemainingReportedPeerTimer = UserThread.runAfterRandomDelay(() -> authenticateToRemainingReportedPeer(),
10, 20, TimeUnit.SECONDS);
}
}
}

View file

@ -1,30 +0,0 @@
package io.bitsquare.p2p.peers;
import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.network.Connection;
import io.bitsquare.p2p.network.NetworkNode;
import io.bitsquare.p2p.storage.P2PDataStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
public class SeedNodeRequestDataManager extends RequestDataManager {
private static final Logger log = LoggerFactory.getLogger(SeedNodeRequestDataManager.class);
public SeedNodeRequestDataManager(NetworkNode networkNode, P2PDataStorage dataStorage, PeerManager peerManager) {
super(networkNode, dataStorage, peerManager);
}
@Override
public void onPeerAuthenticated(NodeAddress peerNodeAddress, Connection connection) {
//TODO not clear which use case is handles here...
if (dataStorage.getMap().isEmpty()) {
if (requestDataFromAuthenticatedSeedNodeTimer == null)
requestDataFromAuthenticatedSeedNodeTimer = UserThread.runAfterRandomDelay(()
-> requestDataFromAuthenticatedSeedNode(peerNodeAddress, connection), 2, 5, TimeUnit.SECONDS);
}
super.onPeerAuthenticated(peerNodeAddress, connection);
}
}

View file

@ -1,32 +0,0 @@
package io.bitsquare.p2p.peers.messages.auth;
import io.bitsquare.app.Version;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.peers.ReportedPeer;
import java.util.HashSet;
public final class AuthenticationChallenge extends AuthenticationMessage {
// That object is sent over the wire, so we need to take care of version compatibility.
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
public final long requesterNonce;
public final long responderNonce;
public final HashSet<ReportedPeer> reportedPeers;
public AuthenticationChallenge(NodeAddress senderNodeAddress, long requesterNonce, long responderNonce, HashSet<ReportedPeer> reportedPeers) {
super(senderNodeAddress);
this.requesterNonce = requesterNonce;
this.responderNonce = responderNonce;
this.reportedPeers = reportedPeers;
}
@Override
public String toString() {
return "AuthenticationChallenge{" +
", requesterNonce=" + requesterNonce +
", responderNonce=" + responderNonce +
", reportedPeers=" + reportedPeers +
super.toString() + "} ";
}
}

View file

@ -1,30 +0,0 @@
package io.bitsquare.p2p.peers.messages.auth;
import io.bitsquare.app.Version;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.peers.ReportedPeer;
import java.util.HashSet;
public final class AuthenticationFinalResponse extends AuthenticationMessage {
// That object is sent over the wire, so we need to take care of version compatibility.
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
public final long responderNonce;
public final HashSet<ReportedPeer> reportedPeers;
public AuthenticationFinalResponse(NodeAddress senderNodeAddress, long responderNonce, HashSet<ReportedPeer> reportedPeers) {
super(senderNodeAddress);
this.responderNonce = responderNonce;
this.reportedPeers = reportedPeers;
}
@Override
public String toString() {
return "AuthenticationResponse{" +
"address=" + senderNodeAddress +
", responderNonce=" + responderNonce +
", reportedPeers=" + reportedPeers +
super.toString() + "} ";
}
}

View file

@ -1,27 +0,0 @@
package io.bitsquare.p2p.peers.messages.auth;
import io.bitsquare.app.Version;
import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress;
public abstract class AuthenticationMessage implements Message {
private final int networkId = Version.getNetworkId();
public final NodeAddress senderNodeAddress;
public AuthenticationMessage(NodeAddress senderNodeAddress) {
this.senderNodeAddress = senderNodeAddress;
}
@Override
public int networkId() {
return networkId;
}
@Override
public String toString() {
return ", address=" + (senderNodeAddress != null ? senderNodeAddress.toString() : "") +
", networkId=" + networkId +
'}';
}
}

View file

@ -1,19 +0,0 @@
package io.bitsquare.p2p.peers.messages.auth;
import io.bitsquare.app.Version;
import io.bitsquare.p2p.NodeAddress;
public final class AuthenticationRejection extends AuthenticationMessage {
// That object is sent over the wire, so we need to take care of version compatibility.
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
public AuthenticationRejection(NodeAddress senderNodeAddress) {
super(senderNodeAddress);
}
@Override
public String toString() {
return "AuthenticationReject{" +
super.toString() + "} ";
}
}

View file

@ -1,24 +0,0 @@
package io.bitsquare.p2p.peers.messages.auth;
import io.bitsquare.app.Version;
import io.bitsquare.p2p.NodeAddress;
public final class AuthenticationRequest extends AuthenticationMessage {
// That object is sent over the wire, so we need to take care of version compatibility.
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
public final long requesterNonce;
public AuthenticationRequest(NodeAddress senderNodeAddress, long requesterNonce) {
super(senderNodeAddress);
this.requesterNonce = requesterNonce;
}
@Override
public String toString() {
return "AuthenticationRequest{" +
"senderAddress=" + senderNodeAddress +
", requesterNonce=" + requesterNonce +
super.toString() + "} ";
}
}

View file

@ -2,14 +2,20 @@ package io.bitsquare.p2p.peers.messages.data;
import io.bitsquare.app.Version; import io.bitsquare.app.Version;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress;
import javax.annotation.Nullable;
public final class DataRequest implements Message { public final class DataRequest implements Message {
// That object is sent over the wire, so we need to take care of version compatibility. // That object is sent over the wire, so we need to take care of version compatibility.
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION; private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
private final int networkId = Version.getNetworkId(); private final int networkId = Version.getNetworkId();
@Nullable
public final NodeAddress senderNodeAddress;
public DataRequest() { public DataRequest(@Nullable NodeAddress senderNodeAddress) {
this.senderNodeAddress = senderNodeAddress;
} }
@Override @Override
@ -20,7 +26,8 @@ public final class DataRequest implements Message {
@Override @Override
public String toString() { public String toString() {
return "GetDataRequest{" + return "GetDataRequest{" +
"networkId=" + networkId + "senderNodeAddress=" + senderNodeAddress +
", networkId=" + networkId +
'}'; '}';
} }
} }

View file

@ -7,7 +7,6 @@ import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.P2PServiceListener; import io.bitsquare.p2p.P2PServiceListener;
import io.bitsquare.p2p.SeedNodeP2PService;
import io.bitsquare.p2p.peers.PeerManager; import io.bitsquare.p2p.peers.PeerManager;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -26,10 +25,10 @@ import static com.google.common.base.Preconditions.checkArgument;
public class SeedNode { public class SeedNode {
private static final Logger log = LoggerFactory.getLogger(SeedNode.class); private static final Logger log = LoggerFactory.getLogger(SeedNode.class);
private NodeAddress mySeedNodeNodeAddress = new NodeAddress("localhost:8001"); private NodeAddress mySeedNodeAddress = new NodeAddress("localhost:8001");
private boolean useLocalhost = false; private boolean useLocalhost = false;
private Set<NodeAddress> progArgSeedNodes; private Set<NodeAddress> progArgSeedNodes;
private SeedNodeP2PService seedNodeP2PService; private P2PService seedNodeP2PService;
private boolean stopped; private boolean stopped;
private final String defaultUserDataDir; private final String defaultUserDataDir;
@ -54,7 +53,7 @@ public class SeedNode {
if (args.length > 0) { if (args.length > 0) {
String arg0 = args[0]; String arg0 = args[0];
checkArgument(arg0.contains(":") && arg0.split(":").length == 2 && arg0.split(":")[1].length() > 3, "Wrong program argument: " + arg0); checkArgument(arg0.contains(":") && arg0.split(":").length == 2 && arg0.split(":")[1].length() > 3, "Wrong program argument: " + arg0);
mySeedNodeNodeAddress = new NodeAddress(arg0); mySeedNodeAddress = new NodeAddress(arg0);
if (args.length > 1) { if (args.length > 1) {
String arg1 = args[1]; String arg1 = args[1];
int networkId = Integer.parseInt(arg1); int networkId = Integer.parseInt(arg1);
@ -65,10 +64,10 @@ public class SeedNode {
String arg2 = args[2]; String arg2 = args[2];
int maxConnections = Integer.parseInt(arg2); int maxConnections = Integer.parseInt(arg2);
checkArgument(maxConnections < 1000, "maxConnections seems to be a bit too high..."); checkArgument(maxConnections < 1000, "maxConnections seems to be a bit too high...");
PeerManager.setMaxConnectionsLowPriority(maxConnections); PeerManager.setMaxConnections(maxConnections);
} else { } else {
// we keep default a higher connection size for seed nodes // we keep default a higher connection size for seed nodes
PeerManager.setMaxConnectionsLowPriority(50); PeerManager.setMaxConnections(50);
} }
if (args.length > 3) { if (args.length > 3) {
String arg3 = args[3]; String arg3 = args[3];
@ -86,7 +85,7 @@ public class SeedNode {
"Wrong program argument"); "Wrong program argument");
progArgSeedNodes.add(new NodeAddress(e)); progArgSeedNodes.add(new NodeAddress(e));
}); });
progArgSeedNodes.remove(mySeedNodeNodeAddress); progArgSeedNodes.remove(mySeedNodeAddress);
} else if (args.length > 5) { } else if (args.length > 5) {
log.error("Too many program arguments." + log.error("Too many program arguments." +
"\nProgram arguments: myAddress (incl. port) bitcoinNetworkId " + "\nProgram arguments: myAddress (incl. port) bitcoinNetworkId " +
@ -100,11 +99,11 @@ public class SeedNode {
} }
public void createAndStartP2PService(boolean useDetailedLogging) { public void createAndStartP2PService(boolean useDetailedLogging) {
createAndStartP2PService(mySeedNodeNodeAddress, useLocalhost, Version.getNetworkId(), useDetailedLogging, progArgSeedNodes, null); createAndStartP2PService(mySeedNodeAddress, useLocalhost, Version.getNetworkId(), useDetailedLogging, progArgSeedNodes, null);
} }
@VisibleForTesting @VisibleForTesting
public void createAndStartP2PService(NodeAddress mySeedNodeNodeAddress, public void createAndStartP2PService(NodeAddress mySeedNodeAddress,
boolean useLocalhost, boolean useLocalhost,
int networkId, int networkId,
boolean useDetailedLogging, boolean useDetailedLogging,
@ -113,7 +112,7 @@ public class SeedNode {
Log.traceCall(); Log.traceCall();
Path appPath = Paths.get(defaultUserDataDir, Path appPath = Paths.get(defaultUserDataDir,
"Bitsquare_seed_node_" + String.valueOf(mySeedNodeNodeAddress.getFullAddress().replace(":", "_"))); "Bitsquare_seed_node_" + String.valueOf(mySeedNodeAddress.getFullAddress().replace(":", "_")));
String logPath = Paths.get(appPath.toString(), "logs").toString(); String logPath = Paths.get(appPath.toString(), "logs").toString();
Log.setup(logPath, useDetailedLogging); Log.setup(logPath, useDetailedLogging);
@ -122,9 +121,9 @@ public class SeedNode {
SeedNodesRepository seedNodesRepository = new SeedNodesRepository(); SeedNodesRepository seedNodesRepository = new SeedNodesRepository();
if (progArgSeedNodes != null && !progArgSeedNodes.isEmpty()) { if (progArgSeedNodes != null && !progArgSeedNodes.isEmpty()) {
if (useLocalhost) if (useLocalhost)
seedNodesRepository.setLocalhostSeedNodeNodeAddresses(progArgSeedNodes); seedNodesRepository.setLocalhostSeedNodeAddresses(progArgSeedNodes);
else else
seedNodesRepository.setTorSeedNodeNodeAddresses(progArgSeedNodes); seedNodesRepository.setTorSeedNodeAddresses(progArgSeedNodes);
} }
File storageDir = Paths.get(appPath.toString(), "db").toFile(); File storageDir = Paths.get(appPath.toString(), "db").toFile();
@ -135,7 +134,8 @@ public class SeedNode {
if (torDir.mkdirs()) if (torDir.mkdirs())
log.info("Created torDir at " + torDir.getAbsolutePath()); log.info("Created torDir at " + torDir.getAbsolutePath());
seedNodeP2PService = new SeedNodeP2PService(seedNodesRepository, mySeedNodeNodeAddress, torDir, useLocalhost, networkId, storageDir); seedNodesRepository.setNodeAddressToExclude(mySeedNodeAddress);
seedNodeP2PService = new P2PService(seedNodesRepository, mySeedNodeAddress.port, torDir, useLocalhost, networkId, storageDir, null, null);
seedNodeP2PService.start(listener); seedNodeP2PService.start(listener);
} }

View file

@ -14,7 +14,7 @@ public class SeedNodesRepository {
// mainnet use port 8000 // mainnet use port 8000
// testnet use port 8001 // testnet use port 8001
// regtest use port 8002 // regtest use port 8002
private Set<NodeAddress> torSeedNodeNodeAddresses = Sets.newHashSet( private Set<NodeAddress> torSeedNodeAddresses = Sets.newHashSet(
// mainnet // mainnet
new NodeAddress("lih5zsr2bvxi24pk.onion:8000"), new NodeAddress("lih5zsr2bvxi24pk.onion:8000"),
new NodeAddress("s5xpstlooosehtxm.onion:8000"), new NodeAddress("s5xpstlooosehtxm.onion:8000"),
@ -32,7 +32,7 @@ public class SeedNodesRepository {
); );
private Set<NodeAddress> localhostSeedNodeNodeAddresses = Sets.newHashSet( private Set<NodeAddress> localhostSeedNodeAddresses = Sets.newHashSet(
// mainnet // mainnet
new NodeAddress("localhost:2000"), new NodeAddress("localhost:2000"),
new NodeAddress("localhost:3000"), new NodeAddress("localhost:3000"),
@ -48,21 +48,28 @@ public class SeedNodesRepository {
new NodeAddress("localhost:3002"), new NodeAddress("localhost:3002"),
new NodeAddress("localhost:4002") new NodeAddress("localhost:4002")
); );
private NodeAddress nodeAddressToExclude;
public Set<NodeAddress> getSeedNodeAddresses(boolean useLocalhost, int networkId) { public Set<NodeAddress> getSeedNodeAddresses(boolean useLocalhost, int networkId) {
String networkIdAsString = String.valueOf(networkId); String networkIdAsString = String.valueOf(networkId);
Set<NodeAddress> nodeAddresses = useLocalhost ? localhostSeedNodeNodeAddresses : torSeedNodeNodeAddresses; Set<NodeAddress> nodeAddresses = useLocalhost ? localhostSeedNodeAddresses : torSeedNodeAddresses;
Set<NodeAddress> filtered = nodeAddresses.stream() Set<NodeAddress> filtered = nodeAddresses.stream()
.filter(e -> String.valueOf(e.port).endsWith(networkIdAsString)).collect(Collectors.toSet()); .filter(e -> String.valueOf(e.port).endsWith(networkIdAsString))
.filter(e -> !e.equals(nodeAddressToExclude))
.collect(Collectors.toSet());
log.info("SeedNodeAddresses (useLocalhost={}) for networkId {}:\nnetworkId={}", useLocalhost, networkId, filtered); log.info("SeedNodeAddresses (useLocalhost={}) for networkId {}:\nnetworkId={}", useLocalhost, networkId, filtered);
return filtered; return filtered;
} }
public void setTorSeedNodeNodeAddresses(Set<NodeAddress> torSeedNodeNodeAddresses) { public void setTorSeedNodeAddresses(Set<NodeAddress> torSeedNodeAddresses) {
this.torSeedNodeNodeAddresses = torSeedNodeNodeAddresses; this.torSeedNodeAddresses = torSeedNodeAddresses;
} }
public void setLocalhostSeedNodeNodeAddresses(Set<NodeAddress> localhostSeedNodeNodeAddresses) { public void setLocalhostSeedNodeAddresses(Set<NodeAddress> localhostSeedNodeAddresses) {
this.localhostSeedNodeNodeAddresses = localhostSeedNodeNodeAddresses; this.localhostSeedNodeAddresses = localhostSeedNodeAddresses;
}
public void setNodeAddressToExclude(NodeAddress nodeAddress) {
this.nodeAddressToExclude = nodeAddress;
} }
} }

View file

@ -12,10 +12,9 @@ import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.network.Connection; import io.bitsquare.p2p.network.Connection;
import io.bitsquare.p2p.network.IllegalRequest;
import io.bitsquare.p2p.network.MessageListener; import io.bitsquare.p2p.network.MessageListener;
import io.bitsquare.p2p.network.NetworkNode; import io.bitsquare.p2p.network.NetworkNode;
import io.bitsquare.p2p.peers.PeerManager; import io.bitsquare.p2p.peers.Broadcaster;
import io.bitsquare.p2p.storage.data.*; import io.bitsquare.p2p.storage.data.*;
import io.bitsquare.p2p.storage.messages.AddDataMessage; import io.bitsquare.p2p.storage.messages.AddDataMessage;
import io.bitsquare.p2p.storage.messages.DataBroadcastMessage; import io.bitsquare.p2p.storage.messages.DataBroadcastMessage;
@ -41,7 +40,7 @@ public class P2PDataStorage implements MessageListener {
@VisibleForTesting @VisibleForTesting
public static int CHECK_TTL_INTERVAL = new Random().nextInt(1000) + 10 * 60 * 1000; // 10-11 min. public static int CHECK_TTL_INTERVAL = new Random().nextInt(1000) + 10 * 60 * 1000; // 10-11 min.
private final PeerManager peerManager; private final Broadcaster broadcaster;
private final Map<ByteArray, ProtectedData> map = new HashMap<>(); private final Map<ByteArray, ProtectedData> map = new HashMap<>();
private final CopyOnWriteArraySet<HashMapChangedListener> hashMapChangedListeners = new CopyOnWriteArraySet<>(); private final CopyOnWriteArraySet<HashMapChangedListener> hashMapChangedListeners = new CopyOnWriteArraySet<>();
private HashMap<ByteArray, Integer> sequenceNumberMap = new HashMap<>(); private HashMap<ByteArray, Integer> sequenceNumberMap = new HashMap<>();
@ -52,9 +51,9 @@ public class P2PDataStorage implements MessageListener {
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public P2PDataStorage(PeerManager peerManager, NetworkNode networkNode, File storageDir) { public P2PDataStorage(Broadcaster broadcaster, NetworkNode networkNode, File storageDir) {
Log.traceCall(); Log.traceCall();
this.peerManager = peerManager; this.broadcaster = broadcaster;
networkNode.addMessageListener(this); networkNode.addMessageListener(this);
@ -70,8 +69,7 @@ public class P2PDataStorage implements MessageListener {
if (persisted != null) if (persisted != null)
sequenceNumberMap = persisted; sequenceNumberMap = persisted;
removeExpiredEntriesExecutor.scheduleAtFixedRate(() -> UserThread.execute(() removeExpiredEntriesExecutor.scheduleAtFixedRate(() -> UserThread.execute(this::removeExpiredEntries), CHECK_TTL_INTERVAL, CHECK_TTL_INTERVAL, TimeUnit.SECONDS);
-> removeExpiredEntries()), CHECK_TTL_INTERVAL, CHECK_TTL_INTERVAL, TimeUnit.SECONDS);
} }
private void removeExpiredEntries() { private void removeExpiredEntries() {
@ -105,22 +103,16 @@ public class P2PDataStorage implements MessageListener {
public void onMessage(Message message, Connection connection) { public void onMessage(Message message, Connection connection) {
if (message instanceof DataBroadcastMessage) { if (message instanceof DataBroadcastMessage) {
Log.traceCall(message.toString()); Log.traceCall(message.toString());
if (connection.isAuthenticated()) { log.trace("DataBroadcastMessage received " + message + " on connection " + connection);
log.trace("ProtectedExpirableDataMessage received " + message + " on connection " + connection); connection.getPeersNodeAddressOptional().ifPresent(peersNodeAddress -> {
connection.getPeerAddressOptional().ifPresent(peerAddress -> {
if (message instanceof AddDataMessage) { if (message instanceof AddDataMessage) {
add(((AddDataMessage) message).data, peerAddress); add(((AddDataMessage) message).data, peersNodeAddress);
} else if (message instanceof RemoveDataMessage) { } else if (message instanceof RemoveDataMessage) {
remove(((RemoveDataMessage) message).data, peerAddress); remove(((RemoveDataMessage) message).data, peersNodeAddress);
} else if (message instanceof RemoveMailboxDataMessage) { } else if (message instanceof RemoveMailboxDataMessage) {
removeMailboxData(((RemoveMailboxDataMessage) message).data, peerAddress); removeMailboxData(((RemoveMailboxDataMessage) message).data, peersNodeAddress);
} }
}); });
} else {
log.warn("Connection is not authenticated yet. " +
"We don't accept storage operations from non-authenticated nodes. connection=", connection);
connection.reportIllegalRequest(IllegalRequest.NotAuthenticated);
}
} }
} }
@ -370,7 +362,7 @@ public class P2PDataStorage implements MessageListener {
private void broadcast(DataBroadcastMessage message, @Nullable NodeAddress sender) { private void broadcast(DataBroadcastMessage message, @Nullable NodeAddress sender) {
Log.traceCall(message.toString()); Log.traceCall(message.toString());
peerManager.broadcast(message, sender); broadcaster.broadcast(message, sender);
} }
private ByteArray getHashAsByteArray(ExpirablePayload payload) { private ByteArray getHashAsByteArray(ExpirablePayload payload) {

View file

@ -18,6 +18,7 @@ public class DataAndSeqNr implements Serializable {
DataAndSeqNr that = (DataAndSeqNr) o; DataAndSeqNr that = (DataAndSeqNr) o;
//noinspection SimplifiableIfStatement
if (sequenceNumber != that.sequenceNumber) return false; if (sequenceNumber != that.sequenceNumber) return false;
return !(data != null ? !data.equals(that.data) : that.data != null); return !(data != null ? !data.equals(that.data) : that.data != null);

View file

@ -32,6 +32,8 @@ import java.util.concurrent.CountDownLatch;
// need to define seed node addresses first before using tor version // need to define seed node addresses first before using tor version
// Ignored for automated tests // Ignored for automated tests
//TODO P2P network tests are outdated
@Ignore @Ignore
public class P2PServiceTest { public class P2PServiceTest {
private static final Logger log = LoggerFactory.getLogger(P2PServiceTest.class); private static final Logger log = LoggerFactory.getLogger(P2PServiceTest.class);
@ -60,7 +62,7 @@ public class P2PServiceTest {
LocalhostNetworkNode.setSimulateTorDelayTorNode(10); LocalhostNetworkNode.setSimulateTorDelayTorNode(10);
LocalhostNetworkNode.setSimulateTorDelayHiddenService(100); LocalhostNetworkNode.setSimulateTorDelayHiddenService(100);
PeerManager.setMaxConnectionsLowPriority(8); PeerManager.setMaxConnections(8);
keyRing1 = new KeyRing(new KeyStorage(dir1)); keyRing1 = new KeyRing(new KeyStorage(dir1));
keyRing2 = new KeyRing(new KeyStorage(dir2)); keyRing2 = new KeyRing(new KeyStorage(dir2));

View file

@ -95,7 +95,7 @@ public class TestUtils {
} }
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
} }
@Override @Override
@ -123,9 +123,9 @@ public class TestUtils {
SeedNodesRepository seedNodesRepository = new SeedNodesRepository(); SeedNodesRepository seedNodesRepository = new SeedNodesRepository();
if (seedNodes != null && !seedNodes.isEmpty()) { if (seedNodes != null && !seedNodes.isEmpty()) {
if (useLocalhost) if (useLocalhost)
seedNodesRepository.setLocalhostSeedNodeNodeAddresses(seedNodes); seedNodesRepository.setLocalhostSeedNodeAddresses(seedNodes);
else else
seedNodesRepository.setTorSeedNodeNodeAddresses(seedNodes); seedNodesRepository.setTorSeedNodeAddresses(seedNodes);
} }
P2PService p2PService = new P2PService(seedNodesRepository, port, new File("seed_node_" + port), useLocalhost, P2PService p2PService = new P2PService(seedNodesRepository, port, new File("seed_node_" + port), useLocalhost,
@ -148,7 +148,7 @@ public class TestUtils {
} }
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
latch.countDown(); latch.countDown();
} }

View file

@ -1,7 +1,5 @@
package io.bitsquare.p2p.network; package io.bitsquare.p2p.network;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.peers.messages.auth.AuthenticationRequest;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
@ -16,6 +14,8 @@ import java.util.concurrent.CountDownLatch;
// TorNode created. Took 6 sec. // TorNode created. Took 6 sec.
// Hidden service created. Took 40-50 sec. // Hidden service created. Took 40-50 sec.
// Connection establishment takes about 4 sec. // Connection establishment takes about 4 sec.
//TODO P2P network tests are outdated
@Ignore @Ignore
public class LocalhostNetworkNodeTest { public class LocalhostNetworkNodeTest {
private static final Logger log = LoggerFactory.getLogger(LocalhostNetworkNodeTest.class); private static final Logger log = LoggerFactory.getLogger(LocalhostNetworkNodeTest.class);
@ -77,8 +77,6 @@ public class LocalhostNetworkNodeTest {
}); });
startupLatch.await(); startupLatch.await();
node2.sendMessage(new NodeAddress("localhost", 9001), new AuthenticationRequest(new NodeAddress("localhost", 9002), 1));
node1.sendMessage(new NodeAddress("localhost", 9002), new AuthenticationRequest(new NodeAddress("localhost", 9001), 1));
msgLatch.await(); msgLatch.await();
CountDownLatch shutDownLatch = new CountDownLatch(2); CountDownLatch shutDownLatch = new CountDownLatch(2);

View file

@ -20,6 +20,7 @@ import java.util.concurrent.CountDownLatch;
// TorNode created. Took 6 sec. // TorNode created. Took 6 sec.
// Hidden service created. Took 40-50 sec. // Hidden service created. Took 40-50 sec.
// Connection establishment takes about 4 sec. // Connection establishment takes about 4 sec.
//TODO P2P network tests are outdated
@Ignore @Ignore
public class TorNetworkNodeTest { public class TorNetworkNodeTest {
private static final Logger log = LoggerFactory.getLogger(TorNetworkNodeTest.class); private static final Logger log = LoggerFactory.getLogger(TorNetworkNodeTest.class);

View file

@ -6,7 +6,10 @@ import io.bitsquare.p2p.P2PServiceListener;
import io.bitsquare.p2p.network.LocalhostNetworkNode; import io.bitsquare.p2p.network.LocalhostNetworkNode;
import io.bitsquare.p2p.peers.PeerManager; import io.bitsquare.p2p.peers.PeerManager;
import io.bitsquare.p2p.seed.SeedNode; import io.bitsquare.p2p.seed.SeedNode;
import org.junit.*; import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -19,6 +22,7 @@ import java.util.concurrent.CountDownLatch;
// Connection establishment takes about 4 sec. // Connection establishment takes about 4 sec.
// need to define seed node addresses first before using tor version // need to define seed node addresses first before using tor version
//TODO P2P network tests are outdated
@Ignore @Ignore
public class PeerManagerTest { public class PeerManagerTest {
private static final Logger log = LoggerFactory.getLogger(PeerManagerTest.class); private static final Logger log = LoggerFactory.getLogger(PeerManagerTest.class);
@ -33,7 +37,7 @@ public class PeerManagerTest {
public void setup() throws InterruptedException { public void setup() throws InterruptedException {
LocalhostNetworkNode.setSimulateTorDelayTorNode(50); LocalhostNetworkNode.setSimulateTorDelayTorNode(50);
LocalhostNetworkNode.setSimulateTorDelayHiddenService(8); LocalhostNetworkNode.setSimulateTorDelayHiddenService(8);
PeerManager.setMaxConnectionsLowPriority(100); PeerManager.setMaxConnections(100);
seedNodes = new HashSet<>(); seedNodes = new HashSet<>();
if (useLocalhost) { if (useLocalhost) {
@ -100,7 +104,7 @@ public class PeerManagerTest {
} }
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
} }
@Override @Override
@ -116,7 +120,7 @@ public class PeerManagerTest {
P2PService p2PService1 = seedNode1.getSeedNodeP2PService(); P2PService p2PService1 = seedNode1.getSeedNodeP2PService();
latch.await(); latch.await();
Thread.sleep(500); Thread.sleep(500);
Assert.assertEquals(0, p2PService1.getPeerManager().getAuthenticatedAndReportedPeers().size()); //Assert.assertEquals(0, p2PService1.getPeerManager().getAuthenticatedAndReportedPeers().size());
} }
@Test @Test
@ -151,7 +155,7 @@ public class PeerManagerTest {
} }
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
latch.countDown(); latch.countDown();
} }
@ -189,7 +193,7 @@ public class PeerManagerTest {
} }
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
latch.countDown(); latch.countDown();
} }
@ -205,8 +209,8 @@ public class PeerManagerTest {
}); });
P2PService p2PService2 = seedNode2.getSeedNodeP2PService(); P2PService p2PService2 = seedNode2.getSeedNodeP2PService();
latch.await(); latch.await();
Assert.assertEquals(1, p2PService1.getPeerManager().getAuthenticatedAndReportedPeers().size()); // Assert.assertEquals(1, p2PService1.getPeerManager().getAuthenticatedAndReportedPeers().size());
Assert.assertEquals(1, p2PService2.getPeerManager().getAuthenticatedAndReportedPeers().size()); // Assert.assertEquals(1, p2PService2.getPeerManager().getAuthenticatedAndReportedPeers().size());
} }
// @Test // @Test
@ -426,7 +430,7 @@ public class PeerManagerTest {
} }
@Override @Override
public void onFirstPeerAuthenticated() { public void onBootstrapped() {
} }
@Override @Override

View file

@ -9,6 +9,7 @@ import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.TestUtils; import io.bitsquare.p2p.TestUtils;
import io.bitsquare.p2p.mocks.MockMessage; import io.bitsquare.p2p.mocks.MockMessage;
import io.bitsquare.p2p.network.NetworkNode; import io.bitsquare.p2p.network.NetworkNode;
import io.bitsquare.p2p.peers.Broadcaster;
import io.bitsquare.p2p.peers.PeerManager; import io.bitsquare.p2p.peers.PeerManager;
import io.bitsquare.p2p.storage.data.DataAndSeqNr; import io.bitsquare.p2p.storage.data.DataAndSeqNr;
import io.bitsquare.p2p.storage.data.ExpirableMailboxPayload; import io.bitsquare.p2p.storage.data.ExpirableMailboxPayload;
@ -30,6 +31,7 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
//TODO P2P network tests are outdated
@Ignore @Ignore
public class ProtectedDataStorageTest { public class ProtectedDataStorageTest {
private static final Logger log = LoggerFactory.getLogger(ProtectedDataStorageTest.class); private static final Logger log = LoggerFactory.getLogger(ProtectedDataStorageTest.class);
@ -65,8 +67,11 @@ public class ProtectedDataStorageTest {
storageSignatureKeyPair1 = keyRing1.getSignatureKeyPair(); storageSignatureKeyPair1 = keyRing1.getSignatureKeyPair();
encryptionService1 = new EncryptionService(keyRing1); encryptionService1 = new EncryptionService(keyRing1);
networkNode1 = TestUtils.getAndStartSeedNode(8001, useClearNet, seedNodes).getSeedNodeP2PService().getNetworkNode(); networkNode1 = TestUtils.getAndStartSeedNode(8001, useClearNet, seedNodes).getSeedNodeP2PService().getNetworkNode();
peerManager1 = new PeerManager(networkNode1, new File("dummy")); peerManager1 = new PeerManager(networkNode1, null, new File("dummy"));
dataStorage1 = new P2PDataStorage(peerManager1, networkNode1, new File("dummy"));
//TODO
Broadcaster broadcaster = new Broadcaster(networkNode1);
dataStorage1 = new P2PDataStorage(broadcaster, networkNode1, new File("dummy"));
// for mailbox // for mailbox
keyRing2 = new KeyRing(new KeyStorage(dir2)); keyRing2 = new KeyRing(new KeyStorage(dir2));
@ -108,7 +113,10 @@ public class ProtectedDataStorageTest {
public void testExpirableData() throws InterruptedException, NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException, CryptoException, SignatureException, InvalidKeyException, NoSuchProviderException { public void testExpirableData() throws InterruptedException, NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException, CryptoException, SignatureException, InvalidKeyException, NoSuchProviderException {
P2PDataStorage.CHECK_TTL_INTERVAL = 10; P2PDataStorage.CHECK_TTL_INTERVAL = 10;
// CHECK_TTL_INTERVAL is used in constructor of ProtectedExpirableDataStorage so we recreate it here // CHECK_TTL_INTERVAL is used in constructor of ProtectedExpirableDataStorage so we recreate it here
dataStorage1 = new P2PDataStorage(peerManager1, networkNode1, new File("dummy"));
//TODO
Broadcaster broadcaster = new Broadcaster(networkNode1);
dataStorage1 = new P2PDataStorage(broadcaster, networkNode1, new File("dummy"));
mockData.ttl = 50; mockData.ttl = 50;
ProtectedData data = dataStorage1.getDataWithSignedSeqNr(mockData, storageSignatureKeyPair1); ProtectedData data = dataStorage1.getDataWithSignedSeqNr(mockData, storageSignatureKeyPair1);