mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-05-17 14:00:35 -04:00
Remove authentication for P2P network, UI improvements
This commit is contained in:
parent
17fa8a424a
commit
2ad72938ec
65 changed files with 1351 additions and 2275 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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>>() {
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
public class AuthenticationException extends Exception {
|
|
||||||
|
|
||||||
public AuthenticationException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
||||||
}
|
}
|
|
@ -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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,5 @@ public interface P2PServiceListener extends SetupListener {
|
||||||
|
|
||||||
void onNoPeersAvailable();
|
void onNoPeersAvailable();
|
||||||
|
|
||||||
void onFirstPeerAuthenticated();
|
void onBootstrapped();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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 +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
package io.bitsquare.p2p.peers;
|
|
||||||
|
|
||||||
public class AuthenticationException extends Exception {
|
|
||||||
|
|
||||||
public AuthenticationException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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() + "} ";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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() + "} ";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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() + "} ";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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() + "} ";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue