mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 18:03:12 +01:00
Remove authentication for P2P network, UI improvements
This commit is contained in:
parent
17fa8a424a
commit
2ad72938ec
@ -26,7 +26,7 @@ import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
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.P2PService;
|
||||
import io.bitsquare.p2p.storage.HashMapChangedListener;
|
||||
@ -89,7 +89,7 @@ public class ArbitratorManager {
|
||||
));
|
||||
private static final String publicKeyForTesting = "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee";
|
||||
private final boolean isDevTest;
|
||||
private FirstPeerAuthenticatedListener firstPeerAuthenticatedListener;
|
||||
private NetWorkReadyListener netWorkReadyListener;
|
||||
private ScheduledThreadPoolExecutor republishArbitratorExecutor;
|
||||
|
||||
@Inject
|
||||
@ -121,14 +121,14 @@ public class ArbitratorManager {
|
||||
if (user.getRegisteredArbitrator() != null) {
|
||||
|
||||
P2PService p2PService = arbitratorService.getP2PService();
|
||||
if (!p2PService.isAuthenticated()) {
|
||||
firstPeerAuthenticatedListener = new FirstPeerAuthenticatedListener() {
|
||||
if (!p2PService.isNetworkReady()) {
|
||||
netWorkReadyListener = new NetWorkReadyListener() {
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
public void onBootstrapped() {
|
||||
republishArbitrator();
|
||||
}
|
||||
};
|
||||
p2PService.addP2PServiceListener(firstPeerAuthenticatedListener);
|
||||
p2PService.addP2PServiceListener(netWorkReadyListener);
|
||||
|
||||
} else {
|
||||
republishArbitrator();
|
||||
@ -144,8 +144,8 @@ public class ArbitratorManager {
|
||||
}
|
||||
|
||||
private void republishArbitrator() {
|
||||
if (firstPeerAuthenticatedListener != null)
|
||||
arbitratorService.getP2PService().removeP2PServiceListener(firstPeerAuthenticatedListener);
|
||||
if (netWorkReadyListener != null)
|
||||
arbitratorService.getP2PService().removeP2PServiceListener(netWorkReadyListener);
|
||||
|
||||
Arbitrator registeredArbitrator = user.getRegisteredArbitrator();
|
||||
if (registeredArbitrator != null) {
|
||||
|
@ -26,8 +26,8 @@ import io.bitsquare.btc.exceptions.TransactionVerificationException;
|
||||
import io.bitsquare.btc.exceptions.WalletException;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.common.crypto.PubKeyRing;
|
||||
import io.bitsquare.p2p.FirstPeerAuthenticatedListener;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.NetWorkReadyListener;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.messaging.DecryptedMsgWithPubKey;
|
||||
@ -67,7 +67,7 @@ public class DisputeManager {
|
||||
private final DisputeList<Dispute> disputes;
|
||||
transient private final ObservableList<Dispute> disputesObservableList;
|
||||
private final String disputeInfo;
|
||||
private final FirstPeerAuthenticatedListener firstPeerAuthenticatedListener;
|
||||
private final NetWorkReadyListener netWorkReadyListener;
|
||||
private final CopyOnWriteArraySet<DecryptedMsgWithPubKey> decryptedMailboxMessageWithPubKeys = new CopyOnWriteArraySet<>();
|
||||
private final CopyOnWriteArraySet<DecryptedMsgWithPubKey> decryptedMailMessageWithPubKeys = new CopyOnWriteArraySet<>();
|
||||
|
||||
@ -106,22 +106,22 @@ public class DisputeManager {
|
||||
|
||||
p2PService.addDecryptedMailListener((decryptedMessageWithPubKey, senderAddress) -> {
|
||||
decryptedMailMessageWithPubKeys.add(decryptedMessageWithPubKey);
|
||||
if (p2PService.isAuthenticated())
|
||||
if (p2PService.isNetworkReady())
|
||||
applyMessages();
|
||||
});
|
||||
p2PService.addDecryptedMailboxListener((decryptedMessageWithPubKey, senderAddress) -> {
|
||||
decryptedMailboxMessageWithPubKeys.add(decryptedMessageWithPubKey);
|
||||
if (p2PService.isAuthenticated())
|
||||
if (p2PService.isNetworkReady())
|
||||
applyMessages();
|
||||
});
|
||||
|
||||
firstPeerAuthenticatedListener = new FirstPeerAuthenticatedListener() {
|
||||
netWorkReadyListener = new NetWorkReadyListener() {
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
public void onBootstrapped() {
|
||||
applyMessages();
|
||||
}
|
||||
};
|
||||
p2PService.addP2PServiceListener(firstPeerAuthenticatedListener);
|
||||
p2PService.addP2PServiceListener(netWorkReadyListener);
|
||||
}
|
||||
|
||||
private void applyMessages() {
|
||||
@ -143,7 +143,7 @@ public class DisputeManager {
|
||||
});
|
||||
decryptedMailboxMessageWithPubKeys.clear();
|
||||
|
||||
p2PService.removeP2PServiceListener(firstPeerAuthenticatedListener);
|
||||
p2PService.removeP2PServiceListener(netWorkReadyListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
package io.bitsquare.trade;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import io.bitsquare.app.Log;
|
||||
import io.bitsquare.arbitration.ArbitratorManager;
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.TradeWalletService;
|
||||
@ -26,8 +27,8 @@ import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.common.handlers.FaultHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.p2p.FirstPeerAuthenticatedListener;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.NetWorkReadyListener;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.messaging.DecryptedMailListener;
|
||||
@ -81,7 +82,7 @@ public class TradeManager {
|
||||
private final Storage<TradableList<Trade>> tradableListStorage;
|
||||
private final TradableList<Trade> trades;
|
||||
private final BooleanProperty pendingTradesInitialized = new SimpleBooleanProperty();
|
||||
private final FirstPeerAuthenticatedListener firstPeerAuthenticatedListener;
|
||||
private final NetWorkReadyListener netWorkReadyListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -99,6 +100,7 @@ public class TradeManager {
|
||||
ArbitratorManager arbitratorManager,
|
||||
P2PService p2PService,
|
||||
@Named("storage.dir") File storageDir) {
|
||||
Log.traceCall();
|
||||
this.user = user;
|
||||
this.keyRing = keyRing;
|
||||
this.walletService = walletService;
|
||||
@ -147,14 +149,15 @@ public class TradeManager {
|
||||
}
|
||||
});
|
||||
|
||||
firstPeerAuthenticatedListener = new FirstPeerAuthenticatedListener() {
|
||||
netWorkReadyListener = new NetWorkReadyListener() {
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
public void onBootstrapped() {
|
||||
Log.traceCall("onNetworkReady");
|
||||
// give a bit delay to be sure other listeners have executed its work
|
||||
UserThread.runAfter(() -> initPendingTrades(), 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
};
|
||||
p2PService.addP2PServiceListener(firstPeerAuthenticatedListener);
|
||||
p2PService.addP2PServiceListener(netWorkReadyListener);
|
||||
}
|
||||
|
||||
|
||||
@ -163,7 +166,8 @@ public class TradeManager {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void initPendingTrades() {
|
||||
if (firstPeerAuthenticatedListener != null) p2PService.removeP2PServiceListener(firstPeerAuthenticatedListener);
|
||||
Log.traceCall();
|
||||
if (netWorkReadyListener != null) p2PService.removeP2PServiceListener(netWorkReadyListener);
|
||||
|
||||
//List<Trade> failedTrades = new ArrayList<>();
|
||||
for (Trade trade : trades) {
|
||||
@ -178,7 +182,7 @@ public class TradeManager {
|
||||
trade.updateDepositTxFromWallet(tradeWalletService);
|
||||
initTrade(trade);
|
||||
|
||||
// after we are authenticated we remove mailbox messages.
|
||||
// after network is ready we remove mailbox messages.
|
||||
DecryptedMsgWithPubKey mailboxMessage = trade.getMailboxMessage();
|
||||
if (mailboxMessage != null) {
|
||||
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.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.p2p.FirstPeerAuthenticatedListener;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.NetWorkReadyListener;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.messaging.SendMailMessageListener;
|
||||
@ -67,7 +67,7 @@ public class OpenOfferManager {
|
||||
private final TradableList<OpenOffer> openOffers;
|
||||
private final Storage<TradableList<OpenOffer>> openOffersStorage;
|
||||
private boolean shutDownRequested;
|
||||
private FirstPeerAuthenticatedListener firstPeerAuthenticatedListener;
|
||||
private NetWorkReadyListener netWorkReadyListener;
|
||||
private final Timer timer = new Timer();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -103,12 +103,12 @@ public class OpenOfferManager {
|
||||
"OpenOfferManager.ShutDownHook"));
|
||||
|
||||
// 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.
|
||||
// A basic sig check is in done also at decryption time
|
||||
Message message = decryptedMessageWithPubKey.message;
|
||||
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
|
||||
// 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.
|
||||
if (!p2PService.isAuthenticated()) {
|
||||
firstPeerAuthenticatedListener = new FirstPeerAuthenticatedListener() {
|
||||
if (!p2PService.isNetworkReady()) {
|
||||
netWorkReadyListener = new NetWorkReadyListener() {
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
public void onBootstrapped() {
|
||||
startRePublishThread();
|
||||
}
|
||||
};
|
||||
p2PService.addP2PServiceListener(firstPeerAuthenticatedListener);
|
||||
p2PService.addP2PServiceListener(netWorkReadyListener);
|
||||
|
||||
} else {
|
||||
startRePublishThread();
|
||||
@ -141,8 +141,8 @@ public class OpenOfferManager {
|
||||
}
|
||||
|
||||
private void startRePublishThread() {
|
||||
if (firstPeerAuthenticatedListener != null)
|
||||
p2PService.removeP2PServiceListener(firstPeerAuthenticatedListener);
|
||||
if (netWorkReadyListener != null)
|
||||
p2PService.removeP2PServiceListener(netWorkReadyListener);
|
||||
|
||||
long period = (long) (Offer.TTL * 0.8); // republish sufficiently before offer would expire
|
||||
TimerTask timerTask = new TimerTask() {
|
||||
|
@ -56,14 +56,14 @@ public class OfferAvailabilityProtocol {
|
||||
this.resultHandler = resultHandler;
|
||||
this.errorMessageHandler = errorMessageHandler;
|
||||
|
||||
decryptedMailListener = (decryptedMessageWithPubKey, peerAddress) -> {
|
||||
decryptedMailListener = (decryptedMessageWithPubKey, peersNodeAddress) -> {
|
||||
Message message = decryptedMessageWithPubKey.message;
|
||||
if (message instanceof OfferMessage) {
|
||||
OfferMessage offerMessage = (OfferMessage) message;
|
||||
nonEmptyStringOf(offerMessage.offerId);
|
||||
if (message instanceof OfferAvailabilityResponse
|
||||
&& 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);
|
||||
}
|
||||
}
|
||||
|
@ -49,19 +49,19 @@ public abstract class TradeProtocol {
|
||||
this.trade = trade;
|
||||
this.processModel = trade.getProcessModel();
|
||||
|
||||
decryptedMailListener = (decryptedMessageWithPubKey, peerAddress) -> {
|
||||
decryptedMailListener = (decryptedMessageWithPubKey, peersNodeAddress) -> {
|
||||
// We check the sig only as soon we have stored the peers pubKeyRing.
|
||||
PubKeyRing tradingPeerPubKeyRing = processModel.tradingPeer.getPubKeyRing();
|
||||
PublicKey signaturePubKey = decryptedMessageWithPubKey.signaturePubKey;
|
||||
if (tradingPeerPubKeyRing != null && signaturePubKey.equals(tradingPeerPubKeyRing.getSignaturePubKey())) {
|
||||
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) {
|
||||
TradeMessage tradeMessage = (TradeMessage) message;
|
||||
nonEmptyStringOf(tradeMessage.tradeId);
|
||||
|
||||
if (tradeMessage.tradeId.equals(processModel.getId())) {
|
||||
doHandleDecryptedMessage(tradeMessage, peerAddress);
|
||||
doHandleDecryptedMessage(tradeMessage, peersNodeAddress);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -113,7 +113,7 @@ public class MainViewModel implements ViewModel {
|
||||
private final User user;
|
||||
private int numBTCPeers = 0;
|
||||
private Timer checkForBtcSyncStateTimer;
|
||||
private ChangeListener<Number> numAuthenticatedPeersListener, btcNumPeersListener;
|
||||
private ChangeListener<Number> numConnectedPeersListener, btcNumPeersListener;
|
||||
private java.util.Timer numberofBtcPeersTimer;
|
||||
private java.util.Timer numberofP2PNetworkPeersTimer;
|
||||
|
||||
@ -176,8 +176,8 @@ public class MainViewModel implements ViewModel {
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
if (numAuthenticatedPeersListener != null)
|
||||
p2PService.getNumAuthenticatedPeers().removeListener(numAuthenticatedPeersListener);
|
||||
if (numConnectedPeersListener != null)
|
||||
p2PService.getNumConnectedPeers().removeListener(numConnectedPeersListener);
|
||||
|
||||
if (btcNumPeersListener != null)
|
||||
walletService.numPeersProperty().removeListener(btcNumPeersListener);
|
||||
@ -208,17 +208,17 @@ public class MainViewModel implements ViewModel {
|
||||
|
||||
@Override
|
||||
public void onRequestingDataCompleted() {
|
||||
if (p2PService.getNumAuthenticatedPeers().get() == 0) {
|
||||
if (p2PService.getNumConnectedPeers().get() == 0) {
|
||||
p2PNetworkInfo.set("Initial data received");
|
||||
} else {
|
||||
updateP2pNetworkInfoWithPeersChanged(p2PService.getNumAuthenticatedPeers().get());
|
||||
updateP2pNetworkInfoWithPeersChanged(p2PService.getNumConnectedPeers().get());
|
||||
}
|
||||
p2pNetworkInitialized.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoSeedNodeAvailable() {
|
||||
if (p2PService.getNumAuthenticatedPeers().get() == 0) {
|
||||
if (p2PService.getNumConnectedPeers().get() == 0) {
|
||||
p2PNetworkInfo.set("No seed nodes available");
|
||||
}
|
||||
p2pNetworkInitialized.set(true);
|
||||
@ -226,7 +226,7 @@ public class MainViewModel implements ViewModel {
|
||||
|
||||
@Override
|
||||
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" +
|
||||
"Please check your internet connection or try to restart the application.");
|
||||
p2PNetworkInfo.set("No seed nodes and peers available");
|
||||
@ -236,8 +236,8 @@ public class MainViewModel implements ViewModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
updateP2pNetworkInfoWithPeersChanged(p2PService.getNumAuthenticatedPeers().get());
|
||||
public void onBootstrapped() {
|
||||
updateP2pNetworkInfoWithPeersChanged(p2PService.getNumConnectedPeers().get());
|
||||
splashP2PNetworkProgress.set(1);
|
||||
}
|
||||
|
||||
@ -248,7 +248,7 @@ public class MainViewModel implements ViewModel {
|
||||
+ throwable.getMessage() + ").\n" +
|
||||
"Please check your internet connection or try to restart the application.");
|
||||
splashP2PNetworkProgress.set(0);
|
||||
if (p2PService.getNumAuthenticatedPeers().get() == 0)
|
||||
if (p2PService.getNumConnectedPeers().get() == 0)
|
||||
p2PNetworkLabelId.set("splash-error-state-msg");
|
||||
}
|
||||
});
|
||||
@ -385,14 +385,13 @@ public class MainViewModel implements ViewModel {
|
||||
.show();
|
||||
|
||||
// update nr of peers in footer
|
||||
numAuthenticatedPeersListener = (observable, oldValue, newValue) -> {
|
||||
|
||||
numConnectedPeersListener = (observable, oldValue, newValue) -> {
|
||||
if ((int) oldValue > 0 && (int) newValue == 0) {
|
||||
// give a bit of tolerance
|
||||
if (numberofP2PNetworkPeersTimer != null)
|
||||
numberofP2PNetworkPeersTimer.cancel();
|
||||
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" +
|
||||
"Maybe you lost your internet connection or your computer was in hibernate/sleep mode.");
|
||||
p2PNetworkLabelId.set("splash-error-state-msg");
|
||||
@ -408,7 +407,7 @@ public class MainViewModel implements ViewModel {
|
||||
|
||||
updateP2pNetworkInfoWithPeersChanged((int) newValue);
|
||||
};
|
||||
p2PService.getNumAuthenticatedPeers().addListener(numAuthenticatedPeersListener);
|
||||
p2PService.getNumConnectedPeers().addListener(numConnectedPeersListener);
|
||||
|
||||
// now show app
|
||||
showAppScreen.set(true);
|
||||
@ -427,8 +426,8 @@ public class MainViewModel implements ViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateP2pNetworkInfoWithPeersChanged(int numAuthenticatedPeers) {
|
||||
p2PNetworkInfo.set("Nr. of connections: " + numAuthenticatedPeers);
|
||||
private void updateP2pNetworkInfoWithPeersChanged(int numPeers) {
|
||||
p2PNetworkInfo.set("Nr. of connections: " + numPeers);
|
||||
}
|
||||
|
||||
private void displayAlertIfPresent(Alert alert) {
|
||||
|
@ -219,23 +219,23 @@ public class ArbitratorRegistrationView extends ActivatableViewAndModel<VBox, Ar
|
||||
}
|
||||
|
||||
private void onRevoke() {
|
||||
if (model.isAuthenticated()) {
|
||||
if (model.isNetworkReady()) {
|
||||
model.onRevoke(
|
||||
() -> new Popup().information("You have successfully removed your arbitrator from the P2P network.").show(),
|
||||
(errorMessage) -> new Popup().error("Could not remove arbitrator.\nError message: " + errorMessage).show());
|
||||
} else {
|
||||
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private void onRegister() {
|
||||
if (model.isAuthenticated()) {
|
||||
if (model.isNetworkReady()) {
|
||||
model.onRegister(
|
||||
() -> new Popup().information("You have successfully registered your arbitrator to the P2P network.").show(),
|
||||
(errorMessage) -> new Popup().error("Could not register arbitrator.\nError message: " + errorMessage).show());
|
||||
} else {
|
||||
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ class ArbitratorRegistrationViewModel extends ActivatableViewModel {
|
||||
revokeButtonDisabled.set(!allDataValid || myArbitratorProperty.get() == null);
|
||||
}
|
||||
|
||||
boolean isAuthenticated() {
|
||||
return p2PService.isAuthenticated();
|
||||
boolean isNetworkReady() {
|
||||
return p2PService.isNetworkReady();
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
<TableView fx:id="table" VBox.vgrow="ALWAYS">
|
||||
<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="Balance" fx:id="balanceColumn" minWidth="90" sortable="false"/>
|
||||
<TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="30" sortable="false"/>
|
||||
|
@ -50,7 +50,7 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||
@FXML
|
||||
TableView<ReservedListItem> table;
|
||||
@FXML
|
||||
TableColumn<ReservedListItem, ReservedListItem> labelColumn, addressColumn, balanceColumn, confidenceColumn;
|
||||
TableColumn<ReservedListItem, ReservedListItem> detailsColumn, addressColumn, balanceColumn, confidenceColumn;
|
||||
|
||||
private final WalletService walletService;
|
||||
private final TradeManager tradeManager;
|
||||
@ -123,8 +123,8 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||
}
|
||||
|
||||
private void setLabelColumnCellFactory() {
|
||||
labelColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
labelColumn.setCellFactory(new Callback<TableColumn<ReservedListItem, ReservedListItem>,
|
||||
detailsColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
detailsColumn.setCellFactory(new Callback<TableColumn<ReservedListItem, ReservedListItem>,
|
||||
TableCell<ReservedListItem,
|
||||
ReservedListItem>>() {
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
<TableView fx:id="table" VBox.vgrow="ALWAYS">
|
||||
<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">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="addressString"/>
|
||||
@ -37,6 +37,7 @@
|
||||
</TableColumn>
|
||||
<TableColumn text="Balance" fx:id="balanceColumn" minWidth="50" sortable="false"/>
|
||||
<TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="30" sortable="false"/>
|
||||
<TableColumn text="" fx:id="selectColumn" prefWidth="80" sortable="false"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
||||
|
@ -71,7 +71,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
@FXML
|
||||
TextField withdrawFromTextField, withdrawToTextField, amountTextField;
|
||||
@FXML
|
||||
TableColumn<WithdrawalListItem, WithdrawalListItem> labelColumn, addressColumn, balanceColumn, confidenceColumn;
|
||||
TableColumn<WithdrawalListItem, WithdrawalListItem> detailsColumn, addressColumn, balanceColumn, confidenceColumn, selectColumn;
|
||||
|
||||
private final WalletService walletService;
|
||||
private final TradeManager tradeManager;
|
||||
@ -114,6 +114,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
setAddressColumnCellFactory();
|
||||
setBalanceColumnCellFactory();
|
||||
setConfidenceColumnCellFactory();
|
||||
setSelectColumnCellFactory();
|
||||
|
||||
if (BitsquareApp.DEV_MODE)
|
||||
withdrawToTextField.setText("mxAkWWaQBqwqcYstKzqLku3kzR6pbu2zHq");
|
||||
@ -262,8 +263,8 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
}
|
||||
|
||||
private void setLabelColumnCellFactory() {
|
||||
labelColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
labelColumn.setCellFactory(new Callback<TableColumn<WithdrawalListItem, WithdrawalListItem>,
|
||||
detailsColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
detailsColumn.setCellFactory(new Callback<TableColumn<WithdrawalListItem, WithdrawalListItem>,
|
||||
TableCell<WithdrawalListItem,
|
||||
WithdrawalListItem>>() {
|
||||
|
||||
@ -272,21 +273,25 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
WithdrawalListItem> column) {
|
||||
return new TableCell<WithdrawalListItem, WithdrawalListItem>() {
|
||||
|
||||
private Hyperlink hyperlink;
|
||||
|
||||
@Override
|
||||
public void updateItem(final WithdrawalListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
hyperlink = new Hyperlink(item.getLabel());
|
||||
if (item.getAddressEntry().getOfferId() != null) {
|
||||
Tooltip tooltip = new Tooltip(item.getAddressEntry().getShortOfferId());
|
||||
Tooltip.install(hyperlink, tooltip);
|
||||
if (detailsAvailable(item)) {
|
||||
Hyperlink hyperlink = new Hyperlink(item.getLabel());
|
||||
if (item.getAddressEntry().getOfferId() != null) {
|
||||
Tooltip tooltip = new Tooltip(item.getAddressEntry().getShortOfferId());
|
||||
Tooltip.install(hyperlink, tooltip);
|
||||
|
||||
hyperlink.setOnAction(event -> openDetails(item));
|
||||
hyperlink.setOnAction(event -> openDetails(item));
|
||||
setGraphic(hyperlink);
|
||||
}
|
||||
} else {
|
||||
Label label = new Label("No info available");
|
||||
setGraphic(label);
|
||||
}
|
||||
setGraphic(hyperlink);
|
||||
|
||||
} else {
|
||||
setGraphic(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) {
|
||||
String offerId = item.getAddressEntry().getOfferId();
|
||||
Optional<Tradable> tradableOptional = closedTradableManager.getTradableById(offerId);
|
||||
@ -384,8 +425,10 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
} else if (tradable instanceof OpenOffer) {
|
||||
offerDetailsPopup.show(tradable.getOffer());
|
||||
}
|
||||
} else if (failedTradesManager.getTradeById(offerId).isPresent()) {
|
||||
tradeDetailsPopup.show(failedTradesManager.getTradeById(offerId).get());
|
||||
} 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() {
|
||||
if (model.isAuthenticated()) {
|
||||
if (model.isNetworkReady()) {
|
||||
Offer offer = model.createAndGetOffer();
|
||||
if (model.getShowPlaceOfferConfirmation()) {
|
||||
offerDetailsPopup.onPlaceOffer(o -> model.onPlaceOffer(o)).show(offer);
|
||||
@ -214,7 +214,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
}
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
@ -485,8 +485,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
return dataModel.hasAcceptedArbitrators();
|
||||
}
|
||||
|
||||
boolean isAuthenticated() {
|
||||
return p2PService.isAuthenticated();
|
||||
boolean isNetworkReady() {
|
||||
return p2PService.isNetworkReady();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -230,15 +230,15 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||
}
|
||||
|
||||
private void onTakeOffer(Offer offer) {
|
||||
if (model.isAuthenticated())
|
||||
if (model.isNetworkReady())
|
||||
offerActionHandler.onTakeOffer(offer);
|
||||
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();
|
||||
}
|
||||
|
||||
private void onRemoveOpenOffer(Offer offer) {
|
||||
if (model.isAuthenticated()) {
|
||||
if (model.isNetworkReady()) {
|
||||
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.")
|
||||
.actionButtonText("Remove offer")
|
||||
@ -246,7 +246,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||
.closeButtonText("Don't remove the offer")
|
||||
.show();
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
@ -157,8 +157,8 @@ class OfferBookViewModel extends ActivatableViewModel {
|
||||
return list;
|
||||
}
|
||||
|
||||
boolean isAuthenticated() {
|
||||
return p2PService.isAuthenticated();
|
||||
boolean isNetworkReady() {
|
||||
return p2PService.isNetworkReady();
|
||||
}
|
||||
|
||||
public TradeCurrency getTradeCurrency() {
|
||||
|
@ -73,7 +73,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
|
||||
}
|
||||
|
||||
private void onRemoveOpenOffer(OpenOffer openOffer) {
|
||||
if (model.isAuthenticated()) {
|
||||
if (model.isNetworkReady()) {
|
||||
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.")
|
||||
.actionButtonText("Remove offer")
|
||||
@ -81,7 +81,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
|
||||
.closeButtonText("Don't remove the offer")
|
||||
.show();
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel>
|
||||
return formatter.formatDateTime(item.getOffer().getDate());
|
||||
}
|
||||
|
||||
boolean isAuthenticated() {
|
||||
return p2PService.isAuthenticated();
|
||||
boolean isNetworkReady() {
|
||||
return p2PService.isNetworkReady();
|
||||
}
|
||||
}
|
||||
|
@ -219,8 +219,8 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
||||
return btcAddressValidator;
|
||||
}
|
||||
|
||||
public boolean isAuthenticated() {
|
||||
return p2PService.isAuthenticated();
|
||||
public boolean isNetworkReady() {
|
||||
return p2PService.isNetworkReady();
|
||||
}
|
||||
|
||||
// columns
|
||||
|
@ -107,7 +107,7 @@ public class ConfirmPaymentReceivedView extends TradeStepDetailsView {
|
||||
|
||||
private void onPaymentReceived(ActionEvent actionEvent) {
|
||||
log.debug("onPaymentReceived");
|
||||
if (model.isAuthenticated()) {
|
||||
if (model.isNetworkReady()) {
|
||||
Preferences preferences = model.dataModel.getPreferences();
|
||||
String key = PopupId.PAYMENT_RECEIVED;
|
||||
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
|
||||
@ -124,7 +124,7 @@ public class ConfirmPaymentReceivedView extends TradeStepDetailsView {
|
||||
confirmPaymentReceived();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ public class StartPaymentView extends TradeStepDetailsView {
|
||||
|
||||
private void onPaymentStarted(ActionEvent actionEvent) {
|
||||
log.debug("onPaymentStarted");
|
||||
if (model.isAuthenticated()) {
|
||||
if (model.isNetworkReady()) {
|
||||
String key = PopupId.PAYMENT_SENT;
|
||||
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
|
||||
new Popup().headLine("Confirmation")
|
||||
@ -154,7 +154,7 @@ public class StartPaymentView extends TradeStepDetailsView {
|
||||
confirmPaymentStarted();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@
|
||||
<ComboBox fx:id="netWorkComboBox" GridPane.rowIndex="0" GridPane.columnIndex="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"/>
|
||||
|
||||
|
||||
@ -62,8 +62,8 @@
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
|
||||
<Label fx:id="authenticatedPeersLabel" text="Authenticated peers:" GridPane.rowIndex="4"/>
|
||||
<TextArea fx:id="authenticatedPeersTextArea" GridPane.rowIndex="4" GridPane.columnIndex="1"
|
||||
<Label fx:id="p2PPeersLabel" text="Connected peers:" GridPane.rowIndex="4"/>
|
||||
<TextArea fx:id="p2PPeersTextArea" GridPane.rowIndex="4" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
|
||||
editable="false" focusTraversable="false"/>
|
||||
|
||||
<columnConstraints>
|
||||
|
@ -65,14 +65,14 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||
@FXML
|
||||
ComboBox<BitcoinNetwork> netWorkComboBox;
|
||||
@FXML
|
||||
TextArea bitcoinPeersTextArea, authenticatedPeersTextArea;
|
||||
TextArea bitcoinPeersTextArea, p2PPeersTextArea;
|
||||
@FXML
|
||||
Label bitcoinPeersLabel, authenticatedPeersLabel;
|
||||
Label bitcoinPeersLabel, p2PPeersLabel;
|
||||
|
||||
private P2PServiceListener p2PServiceListener;
|
||||
private ChangeListener<Number> numAuthenticatedPeersChangeListener;
|
||||
private ChangeListener<Number> numP2PPeersChangeListener;
|
||||
private ChangeListener<List<Peer>> bitcoinPeersChangeListener;
|
||||
private final Set<NodeAddress> seedNodeNodeAddresses;
|
||||
private final Set<NodeAddress> seedNodeAddresses;
|
||||
|
||||
@Inject
|
||||
public NetworkSettingsView(WalletService walletService, P2PService p2PService, Preferences preferences,
|
||||
@ -84,14 +84,14 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||
BitcoinNetwork bitcoinNetwork = preferences.getBitcoinNetwork();
|
||||
|
||||
boolean useLocalhost = p2PService.getNetworkNode() instanceof LocalhostNetworkNode;
|
||||
this.seedNodeNodeAddresses = seedNodesRepository.getSeedNodeAddresses(useLocalhost, bitcoinNetwork.ordinal());
|
||||
this.seedNodeAddresses = seedNodesRepository.getSeedNodeAddresses(useLocalhost, bitcoinNetwork.ordinal());
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
GridPane.setMargin(bitcoinPeersLabel, new Insets(4, 0, 0, 0));
|
||||
GridPane.setValignment(bitcoinPeersLabel, VPos.TOP);
|
||||
GridPane.setMargin(authenticatedPeersLabel, new Insets(4, 0, 0, 0));
|
||||
GridPane.setValignment(authenticatedPeersLabel, VPos.TOP);
|
||||
GridPane.setMargin(p2PPeersLabel, new Insets(4, 0, 0, 0));
|
||||
GridPane.setValignment(p2PPeersLabel, VPos.TOP);
|
||||
bitcoinPeersTextArea.setPrefRowCount(12);
|
||||
netWorkComboBox.setItems(FXCollections.observableArrayList(BitcoinNetwork.values()));
|
||||
netWorkComboBox.getSelectionModel().select(preferences.getBitcoinNetwork());
|
||||
@ -127,7 +127,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
public void onBootstrapped() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -152,9 +152,9 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||
walletService.connectedPeersProperty().addListener(bitcoinPeersChangeListener);
|
||||
updateBitcoinPeersTextArea();
|
||||
|
||||
numAuthenticatedPeersChangeListener = (observable, oldValue, newValue) -> updateAuthenticatedPeersTextArea();
|
||||
p2PService.getNumAuthenticatedPeers().addListener(numAuthenticatedPeersChangeListener);
|
||||
updateAuthenticatedPeersTextArea();
|
||||
numP2PPeersChangeListener = (observable, oldValue, newValue) -> updateP2PPeersTextArea();
|
||||
p2PService.getNumConnectedPeers().addListener(numP2PPeersChangeListener);
|
||||
updateP2PPeersTextArea();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -165,18 +165,18 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||
if (bitcoinPeersChangeListener != null)
|
||||
walletService.connectedPeersProperty().removeListener(bitcoinPeersChangeListener);
|
||||
|
||||
if (numAuthenticatedPeersChangeListener != null)
|
||||
p2PService.getNumAuthenticatedPeers().removeListener(numAuthenticatedPeersChangeListener);
|
||||
if (numP2PPeersChangeListener != null)
|
||||
p2PService.getNumConnectedPeers().removeListener(numP2PPeersChangeListener);
|
||||
}
|
||||
|
||||
private void updateAuthenticatedPeersTextArea() {
|
||||
authenticatedPeersTextArea.clear();
|
||||
p2PService.getAuthenticatedPeerNodeAddresses().stream().forEach(e -> {
|
||||
if (authenticatedPeersTextArea.getText().length() > 0)
|
||||
authenticatedPeersTextArea.appendText("\n");
|
||||
authenticatedPeersTextArea.appendText(e.getFullAddress());
|
||||
if (seedNodeNodeAddresses.contains(e))
|
||||
authenticatedPeersTextArea.appendText(" (Seed node)");
|
||||
private void updateP2PPeersTextArea() {
|
||||
p2PPeersTextArea.clear();
|
||||
p2PService.getNodeAddressesOfConnectedPeers().stream().forEach(e -> {
|
||||
if (p2PPeersTextArea.getText().length() > 0)
|
||||
p2PPeersTextArea.appendText("\n");
|
||||
p2PPeersTextArea.appendText(e.getFullAddress());
|
||||
if (seedNodeAddresses.contains(e))
|
||||
p2PPeersTextArea.appendText(" (Seed node)");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ public class Transitions {
|
||||
fade.setInterpolator(Interpolator.EASE_IN);
|
||||
fade.setOnFinished(actionEvent -> {
|
||||
((Pane) (node.getParent())).getChildren().remove(node);
|
||||
Profiler.printMsgWithTime("fadeOutAndRemove");
|
||||
//Profiler.printMsgWithTime("fadeOutAndRemove");
|
||||
if (handler != null)
|
||||
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;
|
||||
|
||||
|
||||
public abstract class FirstPeerAuthenticatedListener implements P2PServiceListener {
|
||||
public abstract class NetWorkReadyListener implements P2PServiceListener {
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
}
|
||||
@ -27,5 +27,5 @@ public abstract class FirstPeerAuthenticatedListener implements P2PServiceListen
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (port != nodeAddress.port) return false;
|
||||
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.ProgramArguments;
|
||||
import io.bitsquare.common.ByteArray;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.crypto.CryptoException;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.common.crypto.PubKeyRing;
|
||||
@ -15,7 +16,8 @@ import io.bitsquare.crypto.EncryptionService;
|
||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
||||
import io.bitsquare.p2p.messaging.*;
|
||||
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.RequestDataManager;
|
||||
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.ProtectedData;
|
||||
import io.bitsquare.p2p.storage.data.ProtectedMailboxData;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
import org.fxmisc.easybind.monadic.MonadicBinding;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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.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 final SeedNodesRepository seedNodesRepository;
|
||||
private final int port;
|
||||
private final File torDir;
|
||||
private final boolean useLocalhost;
|
||||
protected final File storageDir;
|
||||
private final Optional<EncryptionService> optionalEncryptionService;
|
||||
private final Optional<KeyRing> optionalKeyRing;
|
||||
protected final SeedNodesRepository seedNodesRepository;
|
||||
protected final int port;
|
||||
protected final File torDir;
|
||||
protected final Optional<EncryptionService> optionalEncryptionService;
|
||||
protected final Optional<KeyRing> optionalKeyRing;
|
||||
|
||||
// set in init
|
||||
protected NetworkNode networkNode;
|
||||
protected PeerManager peerManager;
|
||||
protected P2PDataStorage dataStorage;
|
||||
private NetworkNode networkNode;
|
||||
private P2PDataStorage p2PDataStorage;
|
||||
private PeerManager peerManager;
|
||||
private RequestDataManager requestDataManager;
|
||||
private PeerExchangeManager peerExchangeManager;
|
||||
|
||||
private final CopyOnWriteArraySet<DecryptedMailListener> decryptedMailListeners = new CopyOnWriteArraySet<>();
|
||||
private final CopyOnWriteArraySet<DecryptedMailboxListener> decryptedMailboxListeners = new CopyOnWriteArraySet<>();
|
||||
protected final CopyOnWriteArraySet<P2PServiceListener> p2pServiceListeners = new CopyOnWriteArraySet<>();
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private MonadicBinding<Boolean> networkReadyBinding;
|
||||
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 Set<NodeAddress> authenticatedPeerNodeAddresses = new HashSet<>();
|
||||
private final CopyOnWriteArraySet<Runnable> shutDownResultHandlers = new CopyOnWriteArraySet<>();
|
||||
protected final BooleanProperty hiddenServicePublished = new SimpleBooleanProperty();
|
||||
private final BooleanProperty requestingDataCompleted = new SimpleBooleanProperty();
|
||||
protected final BooleanProperty notAuthenticated = new SimpleBooleanProperty(true);
|
||||
private final IntegerProperty numAuthenticatedPeers = new SimpleIntegerProperty(0);
|
||||
private final Set<Runnable> shutDownResultHandlers = new CopyOnWriteArraySet<>();
|
||||
private final BooleanProperty hiddenServicePublished = new SimpleBooleanProperty();
|
||||
private final BooleanProperty preliminaryDataReceived = new SimpleBooleanProperty();
|
||||
private final IntegerProperty numConnectedPeers = new SimpleIntegerProperty(0);
|
||||
|
||||
private NodeAddress seedNodeOfInitialDataRequest;
|
||||
private volatile boolean shutDownInProgress;
|
||||
private boolean shutDownComplete;
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private MonadicBinding<Boolean> readyForAuthenticationBinding;
|
||||
private final Storage<NodeAddress> dbStorage;
|
||||
private NodeAddress myOnionNodeAddress;
|
||||
protected RequestDataManager requestDataManager;
|
||||
protected Set<NodeAddress> seedNodeNodeAddresses;
|
||||
private ChangeListener<Connection.State> stateChangeListener;
|
||||
private Subscription networkReadySubscription;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -97,89 +95,52 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
this.seedNodesRepository = seedNodesRepository;
|
||||
this.port = port;
|
||||
this.torDir = torDir;
|
||||
this.useLocalhost = useLocalhost;
|
||||
this.storageDir = storageDir;
|
||||
|
||||
optionalEncryptionService = encryptionService == null ? Optional.empty() : Optional.of(encryptionService);
|
||||
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();
|
||||
|
||||
// lets check if we have already stored our onion address
|
||||
NodeAddress persistedOnionNodeAddress = dbStorage.initAndGetPersisted("myOnionAddress");
|
||||
if (persistedOnionNodeAddress != null)
|
||||
this.myOnionNodeAddress = persistedOnionNodeAddress;
|
||||
stateChangeListener = (observable, oldValue, newValue) -> {
|
||||
Set<NodeAddress> nodeAddressesOfAllSucceededConnections = networkNode.getNodeAddressesOfSucceededConnections();
|
||||
Set<Connection> allSucceededConnections = networkNode.getSucceededConnections();
|
||||
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.addConnectionListener(this);
|
||||
networkNode.addMessageListener(this);
|
||||
|
||||
// peer group
|
||||
peerManager = getNewPeerManager();
|
||||
peerManager.setSeedNodeAddresses(seedNodeNodeAddresses);
|
||||
peerManager.addAuthenticationListener(this);
|
||||
Broadcaster broadcaster = new Broadcaster(networkNode);
|
||||
|
||||
// P2P network data storage
|
||||
dataStorage = new P2PDataStorage(peerManager, networkNode, storageDir);
|
||||
dataStorage.addHashMapChangedListener(this);
|
||||
p2PDataStorage = new P2PDataStorage(broadcaster, networkNode, storageDir);
|
||||
p2PDataStorage.addHashMapChangedListener(this);
|
||||
|
||||
// Request data manager
|
||||
requestDataManager = getNewRequestDataManager();
|
||||
requestDataManager.setRequestDataManagerListener(new RequestDataManager.Listener() {
|
||||
@Override
|
||||
public void onNoSeedNodeAvailable() {
|
||||
p2pServiceListeners.stream().forEach(e -> e.onNoSeedNodeAvailable());
|
||||
}
|
||||
Set<NodeAddress> seedNodeAddresses = seedNodesRepository.getSeedNodeAddresses(useLocalhost, networkId);
|
||||
peerManager = new PeerManager(networkNode, seedNodeAddresses, storageDir);
|
||||
|
||||
@Override
|
||||
public void onNoPeersAvailable() {
|
||||
p2pServiceListeners.stream().forEach(e -> e.onNoPeersAvailable());
|
||||
}
|
||||
requestDataManager = new RequestDataManager(networkNode, p2PDataStorage, peerManager, seedNodeAddresses);
|
||||
requestDataManager.setRequestDataManagerListener(this);
|
||||
|
||||
@Override
|
||||
public void onDataReceived(NodeAddress nodeAddress) {
|
||||
if (!requestingDataCompleted.get()) {
|
||||
seedNodeOfInitialDataRequest = nodeAddress;
|
||||
requestingDataCompleted.set(true);
|
||||
}
|
||||
p2pServiceListeners.stream().forEach(e -> e.onRequestingDataCompleted());
|
||||
}
|
||||
});
|
||||
peerManager.addAuthenticationListener(requestDataManager);
|
||||
peerExchangeManager = new PeerExchangeManager(networkNode, peerManager, seedNodeAddresses);
|
||||
|
||||
// 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 before we
|
||||
// authenticate to a seed node.
|
||||
readyForAuthenticationBinding = getNewReadyForAuthenticationBinding();
|
||||
readyForAuthenticationBinding.subscribe((observable, oldValue, newValue) -> {
|
||||
// We need to have both the initial data delivered and the hidden service published
|
||||
networkReadyBinding = EasyBind.combine(hiddenServicePublished, preliminaryDataReceived,
|
||||
(hiddenServicePublished, preliminaryDataReceived)
|
||||
-> hiddenServicePublished && preliminaryDataReceived);
|
||||
networkReadySubscription = networkReadyBinding.subscribe((observable, oldValue, 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
|
||||
@ -200,8 +161,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
|
||||
shutDownResultHandlers.add(shutDownCompleteHandler);
|
||||
|
||||
if (dataStorage != null)
|
||||
dataStorage.shutDown();
|
||||
if (p2PDataStorage != null)
|
||||
p2PDataStorage.shutDown();
|
||||
|
||||
if (peerManager != null)
|
||||
peerManager.shutDown();
|
||||
@ -209,11 +170,17 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
if (requestDataManager != null)
|
||||
requestDataManager.shutDown();
|
||||
|
||||
if (peerExchangeManager != null)
|
||||
peerExchangeManager.shutDown();
|
||||
|
||||
if (networkNode != null)
|
||||
networkNode.shutDown(() -> {
|
||||
shutDownResultHandlers.stream().forEach(e -> e.run());
|
||||
shutDownResultHandlers.stream().forEach(Runnable::run);
|
||||
shutDownComplete = true;
|
||||
});
|
||||
|
||||
if (networkReadySubscription != null)
|
||||
networkReadySubscription.unsubscribe();
|
||||
} else {
|
||||
log.debug("shutDown already in progress");
|
||||
if (shutDownComplete) {
|
||||
@ -229,16 +196,13 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
* Startup sequence:
|
||||
* <p>
|
||||
* Variant 1 (normal expected mode):
|
||||
* onTorNodeReady -> requestDataManager.requestData()
|
||||
* RequestDataManager.Listener.onDataReceived && onHiddenServicePublished -> authenticateSeedNode()
|
||||
* RequestDataManager.onPeerAddressAuthenticated -> RequestDataManager.requestDataFromAuthenticatedSeedNode()
|
||||
* onTorNodeReady -> requestDataManager.firstDataRequestFromAnySeedNode()
|
||||
* RequestDataManager.Listener.onDataReceived && onHiddenServicePublished -> onNetworkReady()
|
||||
* <p>
|
||||
* Variant 2 (no seed node available):
|
||||
* onTorNodeReady -> requestDataManager.requestData
|
||||
* RequestDataManager.Listener.onNoSeedNodeAvailable && onHiddenServicePublished -> retry after 20-30 until
|
||||
* seed node is available and data can be retrieved
|
||||
* RequestDataManager.Listener.onDataReceived && onHiddenServicePublished -> authenticateSeedNode()
|
||||
* RequestDataManager.onPeerAddressAuthenticated -> RequestDataManager.requestDataFromAuthenticatedSeedNode()
|
||||
* onTorNodeReady -> requestDataManager.firstDataRequestFromAnySeedNode
|
||||
* retry after 20-30 sec until we get at least one seed node connected
|
||||
* RequestDataManager.Listener.onDataReceived && onHiddenServicePublished -> onNetworkReady()
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -247,25 +211,20 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
Log.traceCall();
|
||||
requestDataManager.requestDataFromSeedNodes(seedNodeNodeAddresses);
|
||||
p2pServiceListeners.stream().forEach(e -> e.onTorNodeReady());
|
||||
// 1
|
||||
requestDataManager.requestPreliminaryData();
|
||||
p2pServiceListeners.stream().forEach(SetupListener::onTorNodeReady);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHiddenServicePublished() {
|
||||
Log.traceCall();
|
||||
// 3
|
||||
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);
|
||||
|
||||
p2pServiceListeners.stream().forEach(e -> e.onHiddenServicePublished());
|
||||
p2pServiceListeners.stream().forEach(SetupListener::onHiddenServicePublished);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -274,10 +233,52 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
p2pServiceListeners.stream().forEach(e -> e.onSetupFailed(throwable));
|
||||
}
|
||||
|
||||
protected void authenticateToSeedNode() {
|
||||
|
||||
// Called from networkReadyBinding
|
||||
private void onNetworkReady() {
|
||||
Log.traceCall();
|
||||
checkNotNull(seedNodeOfInitialDataRequest != null, "seedNodeOfInitialDataRequest must not be null");
|
||||
peerManager.authenticateToSeedNode(seedNodeOfInitialDataRequest);
|
||||
networkReadySubscription.unsubscribe();
|
||||
|
||||
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
|
||||
public void onConnection(Connection connection) {
|
||||
connection.getStateProperty().addListener(stateChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect(Reason reason, Connection connection) {
|
||||
Log.traceCall();
|
||||
connection.getPeerAddressOptional().ifPresent(peerAddresses -> authenticatedPeerNodeAddresses.remove(peerAddresses));
|
||||
numAuthenticatedPeers.set(authenticatedPeerNodeAddresses.size());
|
||||
connection.getStateProperty().removeListener(stateChangeListener);
|
||||
UserThread.runAfter(() -> numConnectedPeers.set(networkNode.getNodeAddressesOfSucceededConnections().size()), 1);
|
||||
}
|
||||
|
||||
@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
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -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 get too many connection attempts.
|
||||
// That is used as protection against eclipse attacks.
|
||||
connection.setConnectionPriority(ConnectionPriority.DIRECT_MSG);
|
||||
connection.setPeerType(Connection.PeerType.PEER.DIRECT_MSG_PEER);
|
||||
|
||||
log.info("\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" +
|
||||
"Decrypted SealedAndSignedMessage:\ndecryptedMsgWithPubKey={}"
|
||||
+ "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", decryptedMsgWithPubKey);
|
||||
connection.getPeerAddressOptional().ifPresent(peerAddresses ->
|
||||
connection.getPeersNodeAddressOptional().ifPresent(peersNodeAddress ->
|
||||
decryptedMailListeners.stream().forEach(
|
||||
e -> e.onMailMessage(decryptedMsgWithPubKey, peerAddresses)));
|
||||
e -> e.onMailMessage(decryptedMsgWithPubKey, peersNodeAddress)));
|
||||
} else {
|
||||
log.info("Wrong receiverAddressMaskHash. The message is not intended for us.");
|
||||
}
|
||||
@ -381,21 +363,14 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
SendMailMessageListener sendMailMessageListener) {
|
||||
Log.traceCall();
|
||||
checkNotNull(peerNodeAddress, "PeerAddress must not be null (sendEncryptedMailMessage)");
|
||||
try {
|
||||
checkAuthentication();
|
||||
if (!authenticatedPeerNodeAddresses.contains(peerNodeAddress))
|
||||
peerManager.authenticateToDirectMessagePeer(peerNodeAddress,
|
||||
() -> doSendEncryptedMailMessage(peerNodeAddress, pubKeyRing, message, sendMailMessageListener),
|
||||
() -> sendMailMessageListener.onFault());
|
||||
else
|
||||
doSendEncryptedMailMessage(peerNodeAddress, pubKeyRing, message, sendMailMessageListener);
|
||||
} catch (AuthenticationException e) {
|
||||
log.error(e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
if (isNetworkReady()) {
|
||||
doSendEncryptedMailMessage(peerNodeAddress, pubKeyRing, message, sendMailMessageListener);
|
||||
} else {
|
||||
throw new NetworkNotReadyException();
|
||||
}
|
||||
}
|
||||
|
||||
private void doSendEncryptedMailMessage(NodeAddress peerNodeAddress, PubKeyRing pubKeyRing, MailMessage message,
|
||||
private void doSendEncryptedMailMessage(NodeAddress peersNodeAddress, PubKeyRing pubKeyRing, MailMessage message,
|
||||
SendMailMessageListener sendMailMessageListener) {
|
||||
Log.traceCall();
|
||||
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={}"
|
||||
+ "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", message);
|
||||
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(
|
||||
optionalEncryptionService.get().encryptAndSign(pubKeyRing, message), peerNodeAddress.getAddressPrefixHash());
|
||||
SettableFuture<Connection> future = networkNode.sendMessage(peerNodeAddress, sealedAndSignedMessage);
|
||||
optionalEncryptionService.get().encryptAndSign(pubKeyRing, message), peersNodeAddress.getAddressPrefixHash());
|
||||
SettableFuture<Connection> future = networkNode.sendMessage(peersNodeAddress, sealedAndSignedMessage);
|
||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Connection connection) {
|
||||
sendMailMessageListener.onArrived();
|
||||
if (connection != null) {
|
||||
if (!connection.getPeersNodeAddressOptional().isPresent() && peersNodeAddress != null)
|
||||
connection.setPeersNodeAddress(peersNodeAddress);
|
||||
|
||||
connection.setPeerType(Connection.PeerType.DIRECT_MSG_PEER);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -472,27 +453,16 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
checkNotNull(peerNodeAddress, "PeerAddress must not be null (sendEncryptedMailboxMessage)");
|
||||
checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen.");
|
||||
checkArgument(!optionalKeyRing.get().getPubKeyRing().equals(peersPubKeyRing), "We got own keyring instead of that from peer");
|
||||
try {
|
||||
checkAuthentication();
|
||||
if (authenticatedPeerNodeAddresses.contains(peerNodeAddress)) {
|
||||
trySendEncryptedMailboxMessage(peerNodeAddress, peersPubKeyRing, message, sendMailboxMessageListener);
|
||||
} else {
|
||||
peerManager.authenticateToDirectMessagePeer(peerNodeAddress,
|
||||
() -> 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);
|
||||
|
||||
if (isNetworkReady()) {
|
||||
trySendEncryptedMailboxMessage(peerNodeAddress, peersPubKeyRing, message, sendMailboxMessageListener);
|
||||
} else {
|
||||
throw new NetworkNotReadyException();
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
Log.traceCall();
|
||||
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={}"
|
||||
+ "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", message);
|
||||
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(
|
||||
optionalEncryptionService.get().encryptAndSign(peersPubKeyRing, message), peerNodeAddress.getAddressPrefixHash());
|
||||
SettableFuture<Connection> future = networkNode.sendMessage(peerNodeAddress, sealedAndSignedMessage);
|
||||
optionalEncryptionService.get().encryptAndSign(peersPubKeyRing, message), peersNodeAddress.getAddressPrefixHash());
|
||||
SettableFuture<Connection> future = networkNode.sendMessage(peersNodeAddress, sealedAndSignedMessage);
|
||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Connection connection) {
|
||||
log.trace("SendEncryptedMailboxMessage onSuccess");
|
||||
sendMailboxMessageListener.onArrived();
|
||||
|
||||
if (connection != null) {
|
||||
if (!connection.getPeersNodeAddressOptional().isPresent() && peersNodeAddress != null)
|
||||
connection.setPeersNodeAddress(peersNodeAddress);
|
||||
|
||||
connection.setPeerType(Connection.PeerType.DIRECT_MSG_PEER);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -516,7 +493,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
log.trace("SendEncryptedMailboxMessage onFailure");
|
||||
log.debug(throwable.toString());
|
||||
log.info("We cannot send message to peer. Peer might be offline. We will store message in mailbox.");
|
||||
log.trace("create MailboxEntry with peerAddress " + peerNodeAddress);
|
||||
log.trace("create MailboxEntry with peerAddress " + peersNodeAddress);
|
||||
PublicKey receiverStoragePublicKey = peersPubKeyRing.getSignaturePubKey();
|
||||
addMailboxData(new ExpirableMailboxPayload(sealedAndSignedMessage,
|
||||
optionalKeyRing.get().getSignatureKeyPair().getPublic(),
|
||||
@ -535,27 +512,25 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
private void addMailboxData(ExpirableMailboxPayload expirableMailboxPayload, PublicKey receiversPublicKey) {
|
||||
Log.traceCall();
|
||||
checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen.");
|
||||
try {
|
||||
checkAuthentication();
|
||||
ProtectedMailboxData protectedMailboxData = dataStorage.getMailboxDataWithSignedSeqNr(
|
||||
expirableMailboxPayload,
|
||||
optionalKeyRing.get().getSignatureKeyPair(),
|
||||
receiversPublicKey);
|
||||
dataStorage.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) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
if (isNetworkReady()) {
|
||||
try {
|
||||
ProtectedMailboxData protectedMailboxData = p2PDataStorage.getMailboxDataWithSignedSeqNr(
|
||||
expirableMailboxPayload,
|
||||
optionalKeyRing.get().getSignatureKeyPair(),
|
||||
receiversPublicKey);
|
||||
p2PDataStorage.add(protectedMailboxData, networkNode.getNodeAddress());
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
}
|
||||
} else {
|
||||
throw new NetworkNotReadyException();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeEntryFromMailbox(DecryptedMsgWithPubKey decryptedMsgWithPubKey) {
|
||||
Log.traceCall();
|
||||
checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen.");
|
||||
try {
|
||||
checkAuthentication();
|
||||
if (isNetworkReady()) {
|
||||
if (mailboxMap.containsKey(decryptedMsgWithPubKey)) {
|
||||
ProtectedMailboxData mailboxData = mailboxMap.get(decryptedMsgWithPubKey);
|
||||
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()),
|
||||
"receiversPubKey is not matching with our key. That must not happen.");
|
||||
try {
|
||||
ProtectedMailboxData protectedMailboxData = dataStorage.getMailboxDataWithSignedSeqNr(
|
||||
ProtectedMailboxData protectedMailboxData = p2PDataStorage.getMailboxDataWithSignedSeqNr(
|
||||
expirableMailboxPayload,
|
||||
optionalKeyRing.get().getSignatureKeyPair(),
|
||||
receiversPubKey);
|
||||
dataStorage.removeMailboxData(protectedMailboxData, networkNode.getNodeAddress());
|
||||
p2PDataStorage.removeMailboxData(protectedMailboxData, networkNode.getNodeAddress());
|
||||
} catch (CryptoException e) {
|
||||
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." +
|
||||
"\ndecryptedMsgWithPubKey={}\nmailboxMap={}", decryptedMsgWithPubKey, mailboxMap);
|
||||
}
|
||||
} catch (AuthenticationException e) {
|
||||
log.error(e.getMessage());
|
||||
//TODO check if boolean return type can avoid throwing an exception
|
||||
throw new RuntimeException(e);
|
||||
} else {
|
||||
throw new NetworkNotReadyException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -605,35 +579,35 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
private boolean doAddData(ExpirablePayload expirablePayload, boolean rePublish) {
|
||||
Log.traceCall();
|
||||
checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen.");
|
||||
try {
|
||||
checkAuthentication();
|
||||
ProtectedData protectedData = dataStorage.getDataWithSignedSeqNr(expirablePayload, optionalKeyRing.get().getSignatureKeyPair());
|
||||
if (rePublish)
|
||||
return dataStorage.rePublish(protectedData, networkNode.getNodeAddress());
|
||||
else
|
||||
return dataStorage.add(protectedData, networkNode.getNodeAddress());
|
||||
} catch (AuthenticationException e) {
|
||||
log.error(e.getMessage());
|
||||
return false;
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
return false;
|
||||
if (isNetworkReady()) {
|
||||
try {
|
||||
ProtectedData protectedData = p2PDataStorage.getDataWithSignedSeqNr(expirablePayload, optionalKeyRing.get().getSignatureKeyPair());
|
||||
if (rePublish)
|
||||
return p2PDataStorage.rePublish(protectedData, networkNode.getNodeAddress());
|
||||
else
|
||||
return p2PDataStorage.add(protectedData, networkNode.getNodeAddress());
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
throw new NetworkNotReadyException();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeData(ExpirablePayload expirablePayload) {
|
||||
Log.traceCall();
|
||||
checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen.");
|
||||
try {
|
||||
checkAuthentication();
|
||||
ProtectedData protectedData = dataStorage.getDataWithSignedSeqNr(expirablePayload, optionalKeyRing.get().getSignatureKeyPair());
|
||||
return dataStorage.remove(protectedData, networkNode.getNodeAddress());
|
||||
} catch (AuthenticationException e) {
|
||||
log.error(e.getMessage());
|
||||
return false;
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
return false;
|
||||
if (isNetworkReady()) {
|
||||
try {
|
||||
ProtectedData protectedData = p2PDataStorage.getDataWithSignedSeqNr(expirablePayload, optionalKeyRing.get().getSignatureKeyPair());
|
||||
return p2PDataStorage.remove(protectedData, networkNode.getNodeAddress());
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
throw new NetworkNotReadyException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -667,7 +641,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
}
|
||||
|
||||
public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) {
|
||||
dataStorage.addHashMapChangedListener(hashMapChangedListener);
|
||||
p2PDataStorage.addHashMapChangedListener(hashMapChangedListener);
|
||||
}
|
||||
|
||||
|
||||
@ -675,33 +649,30 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public boolean isAuthenticated() {
|
||||
return !notAuthenticated.get();
|
||||
public boolean isNetworkReady() {
|
||||
log.debug("###### isNetworkReady networkReadyBinding " + networkReadyBinding.get());
|
||||
log.debug("###### isNetworkReady hiddenServicePublished.get() && preliminaryDataReceived.get() " + (hiddenServicePublished.get() && preliminaryDataReceived.get()));
|
||||
return hiddenServicePublished.get() && preliminaryDataReceived.get();
|
||||
}
|
||||
|
||||
public NetworkNode getNetworkNode() {
|
||||
return networkNode;
|
||||
}
|
||||
|
||||
public PeerManager getPeerManager() {
|
||||
return peerManager;
|
||||
}
|
||||
|
||||
public NodeAddress getAddress() {
|
||||
return networkNode.getNodeAddress();
|
||||
}
|
||||
|
||||
public Set<NodeAddress> getAuthenticatedPeerNodeAddresses() {
|
||||
return authenticatedPeerNodeAddresses;
|
||||
public Set<NodeAddress> getNodeAddressesOfConnectedPeers() {
|
||||
return networkNode.getNodeAddressesOfSucceededConnections();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ReadOnlyIntegerProperty getNumAuthenticatedPeers() {
|
||||
return numAuthenticatedPeers;
|
||||
public ReadOnlyIntegerProperty getNumConnectedPeers() {
|
||||
return numConnectedPeers;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (myOnionNodeAddress != null) {
|
||||
byte[] blurredAddressHash = myOnionNodeAddress.getAddressPrefixHash();
|
||||
if (networkNode.getNodeAddress() != null) {
|
||||
byte[] blurredAddressHash = networkNode.getNodeAddress().getAddressPrefixHash();
|
||||
return blurredAddressHash != null &&
|
||||
Arrays.equals(blurredAddressHash, sealedAndSignedMessage.addressPrefixHash);
|
||||
} else {
|
||||
@ -719,10 +690,4 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
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 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;
|
||||
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (message != null ? !message.equals(that.message) : that.message != null) return false;
|
||||
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.Utils;
|
||||
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.slf4j.Logger;
|
||||
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
|
||||
//timeout on blocking Socket operations like ServerSocket.accept() or SocketInputStream.read()
|
||||
private static final int SOCKET_TIMEOUT = 10 * 60 * 1000; // 10 min.
|
||||
private ConnectionPriority connectionPriority;
|
||||
|
||||
public static int getMaxMsgSize() {
|
||||
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 MessageListener messageListener;
|
||||
private final ConnectionListener connectionListener;
|
||||
|
||||
private final Direction direction;
|
||||
private final String portInfo;
|
||||
private final String uid = UUID.randomUUID().toString();
|
||||
private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
|
||||
@ -56,8 +84,7 @@ public class Connection implements MessageListener {
|
||||
private ObjectOutputStream objectOutputStream;
|
||||
|
||||
// mutable data, set from other threads but not changed internally.
|
||||
private Optional<NodeAddress> peerAddressOptional = Optional.empty();
|
||||
private volatile boolean isAuthenticated;
|
||||
private Optional<NodeAddress> peersNodeAddressOptional = Optional.empty();
|
||||
private volatile boolean stopped;
|
||||
|
||||
//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 ObjectProperty<State> stateProperty = new SimpleObjectProperty<>();
|
||||
private PeerType peerType;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Connection(Socket socket, MessageListener messageListener, ConnectionListener connectionListener) {
|
||||
public Connection(Socket socket, MessageListener messageListener, ConnectionListener connectionListener, Direction direction) {
|
||||
this.socket = socket;
|
||||
this.messageListener = messageListener;
|
||||
this.connectionListener = connectionListener;
|
||||
this.direction = direction;
|
||||
stateProperty.set(State.IDLE);
|
||||
|
||||
sharedSpace = new SharedSpace(this, socket);
|
||||
|
||||
@ -118,25 +151,16 @@ public class Connection implements MessageListener {
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Called form UserThread
|
||||
public void setAuthenticated() {
|
||||
Log.traceCall();
|
||||
isAuthenticated = true;
|
||||
}
|
||||
|
||||
public void setConnectionPriority(ConnectionPriority connectionPriority) {
|
||||
this.connectionPriority = connectionPriority;
|
||||
}
|
||||
|
||||
// Called form various threads
|
||||
public void sendMessage(Message message) {
|
||||
Log.traceCall();
|
||||
if (!stopped) {
|
||||
try {
|
||||
String peerAddress = peerAddressOptional.isPresent() ? peerAddressOptional.get().toString() : "null";
|
||||
String peersNodeAddress = peersNodeAddressOptional.isPresent() ? peersNodeAddressOptional.get().toString() : "null";
|
||||
log.info("\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" +
|
||||
"Write object to outputStream to peer: {} (uid={})\nmessage={}"
|
||||
+ "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", peerAddress, uid, message);
|
||||
+ "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", peersNodeAddress, uid, message);
|
||||
|
||||
Object objectToWrite;
|
||||
if (useCompression) {
|
||||
@ -170,12 +194,6 @@ public class Connection implements MessageListener {
|
||||
sharedSpace.reportIllegalRequest(illegalRequest);
|
||||
}
|
||||
|
||||
public synchronized void setPeerAddress(NodeAddress peerNodeAddress) {
|
||||
Log.traceCall();
|
||||
checkNotNull(peerNodeAddress, "peerAddress must not be null");
|
||||
peerAddressOptional = Optional.of(peerNodeAddress);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// MessageListener implementation
|
||||
@ -188,27 +206,50 @@ public class Connection implements MessageListener {
|
||||
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
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Nullable
|
||||
public synchronized NodeAddress getPeerAddress() {
|
||||
return peerAddressOptional.isPresent() ? peerAddressOptional.get() : null;
|
||||
}
|
||||
|
||||
public synchronized Optional<NodeAddress> getPeerAddressOptional() {
|
||||
return peerAddressOptional;
|
||||
public synchronized Optional<NodeAddress> getPeersNodeAddressOptional() {
|
||||
return peersNodeAddressOptional;
|
||||
}
|
||||
|
||||
public Date getLastActivityDate() {
|
||||
return sharedSpace.getLastActivityDate();
|
||||
}
|
||||
|
||||
public boolean isAuthenticated() {
|
||||
return isAuthenticated;
|
||||
}
|
||||
|
||||
public String getUid() {
|
||||
return uid;
|
||||
}
|
||||
@ -217,8 +258,20 @@ public class Connection implements MessageListener {
|
||||
return stopped;
|
||||
}
|
||||
|
||||
public ConnectionPriority getConnectionPriority() {
|
||||
return connectionPriority;
|
||||
public PeerType getPeerType() {
|
||||
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) {
|
||||
Log.traceCall(this.toString());
|
||||
if (!stopped) {
|
||||
String peerAddress = peerAddressOptional.isPresent() ? peerAddressOptional.get().toString() : "null";
|
||||
String peersNodeAddress = peersNodeAddressOptional.isPresent() ? peersNodeAddressOptional.get().toString() : "null";
|
||||
log.info("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" +
|
||||
"ShutDown connection:"
|
||||
+ "\npeerAddress=" + peerAddress
|
||||
+ "\npeersNodeAddress=" + peersNodeAddress
|
||||
+ "\nlocalPort/port=" + sharedSpace.getSocket().getLocalPort()
|
||||
+ "/" + sharedSpace.getSocket().getPort()
|
||||
+ "\nuid=" + uid
|
||||
+ "\nisAuthenticated=" + isAuthenticated
|
||||
+ "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n");
|
||||
|
||||
log.trace("ShutDown connection requested. Connection=" + this.toString());
|
||||
@ -281,7 +333,6 @@ public class Connection implements MessageListener {
|
||||
sharedSpace.stop();
|
||||
if (inputHandler != null)
|
||||
inputHandler.stop();
|
||||
isAuthenticated = false;
|
||||
}
|
||||
|
||||
private void doShutDown(@Nullable Runnable shutDownCompleteHandler) {
|
||||
@ -319,8 +370,9 @@ public class Connection implements MessageListener {
|
||||
Connection that = (Connection) o;
|
||||
|
||||
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;
|
||||
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() {
|
||||
int result = portInfo != null ? portInfo.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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Connection{" +
|
||||
"portInfo=" + portInfo +
|
||||
"peerAddress=" + peersNodeAddressOptional +
|
||||
", peerType=" + peerType +
|
||||
", direction=" + direction +
|
||||
", state=" + getState() +
|
||||
", portInfo=" + portInfo +
|
||||
", uid='" + uid + '\'' +
|
||||
", sharedSpace=" + sharedSpace.toString() +
|
||||
", peerAddress=" + peerAddressOptional +
|
||||
", isAuthenticated=" + isAuthenticated +
|
||||
", stopped=" + stopped +
|
||||
", stopped=" + stopped +
|
||||
", connectionType=" + connectionPriority +
|
||||
", 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 {
|
||||
// TODO check for needed allowed tolerance
|
||||
MaxSizeExceeded(1),
|
||||
NotAuthenticated(1),
|
||||
InvalidDataType(1),
|
||||
InvalidDataType(0),
|
||||
WrongNetworkId(0);
|
||||
|
||||
public final int maxTolerance;
|
||||
|
@ -60,7 +60,7 @@ public class LocalhostNetworkNode extends NetworkNode {
|
||||
//Tor delay simulation
|
||||
createTorNode(torNode -> {
|
||||
Log.traceCall("torNode created");
|
||||
setupListeners.stream().forEach(e -> e.onTorNodeReady());
|
||||
setupListeners.stream().forEach(SetupListener::onTorNodeReady);
|
||||
|
||||
// Create Hidden Service (takes about 40 sec.)
|
||||
createHiddenService(hiddenServiceDescriptor -> {
|
||||
@ -74,7 +74,7 @@ public class LocalhostNetworkNode extends NetworkNode {
|
||||
|
||||
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.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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);
|
||||
|
||||
public SettableFuture<Connection> sendMessage(@NotNull NodeAddress peerNodeAddress, Message message) {
|
||||
Log.traceCall("peerAddress: " + peerNodeAddress + " / message: " + message);
|
||||
checkNotNull(peerNodeAddress, "peerAddress must not be null");
|
||||
public SettableFuture<Connection> sendMessage(@NotNull NodeAddress peersNodeAddress, Message message) {
|
||||
Log.traceCall("peerAddress: " + peersNodeAddress + " / message: " + message);
|
||||
checkNotNull(peersNodeAddress, "peerAddress must not be null");
|
||||
|
||||
Optional<Connection> outboundConnectionOptional = lookupOutboundConnection(peerNodeAddress);
|
||||
Optional<Connection> outboundConnectionOptional = lookupOutboundConnection(peersNodeAddress);
|
||||
Connection connection = outboundConnectionOptional.isPresent() ? outboundConnectionOptional.get() : null;
|
||||
if (connection != null)
|
||||
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) {
|
||||
Optional<Connection> inboundConnectionOptional = lookupInboundConnection(peerNodeAddress);
|
||||
Optional<Connection> inboundConnectionOptional = lookupInboundConnection(peersNodeAddress);
|
||||
if (inboundConnectionOptional.isPresent()) connection = inboundConnectionOptional.get();
|
||||
if (connection != null)
|
||||
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);
|
||||
} else {
|
||||
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 boolean[] timeoutOccurred = new boolean[1];
|
||||
timeoutOccurred[0] = false;
|
||||
|
||||
ListenableFuture<Connection> future = executorService.submit(() -> {
|
||||
Thread.currentThread().setName("NetworkNode:SendMessage-to-" + peerNodeAddress);
|
||||
Thread.currentThread().setName("NetworkNode:SendMessage-to-" + peersNodeAddress);
|
||||
Connection newConnection = null;
|
||||
try {
|
||||
// can take a while when using tor
|
||||
Socket socket = createSocket(peerNodeAddress);
|
||||
Socket socket = createSocket(peersNodeAddress);
|
||||
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);
|
||||
|
||||
|
||||
Connection newConnection = new Connection(socket, NetworkNode.this, NetworkNode.this);
|
||||
newConnection.setPeerAddress(peerNodeAddress);
|
||||
newConnection = new Connection(socket, NetworkNode.this, NetworkNode.this, Connection.Direction.OUTBOUND);
|
||||
newConnection.setPeersNodeAddress(peersNodeAddress);
|
||||
outBoundConnections.add(newConnection);
|
||||
|
||||
log.info("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" +
|
||||
"NetworkNode created new outbound connection:"
|
||||
+ "\npeerAddress=" + peerNodeAddress
|
||||
+ "\nconnection.uid=" + newConnection.getUid()
|
||||
+ "\npeerAddress=" + peersNodeAddress
|
||||
+ "\nuid=" + newConnection.getUid()
|
||||
+ "\nmessage=" + message
|
||||
+ "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n");
|
||||
|
||||
@ -122,28 +124,17 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||
throwable.printStackTrace();
|
||||
log.error("Executing task failed. " + throwable.getMessage());
|
||||
}
|
||||
if (newConnection != null)
|
||||
newConnection.setState(Connection.State.FAILED);
|
||||
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>() {
|
||||
public void onSuccess(Connection connection) {
|
||||
UserThread.execute(() -> {
|
||||
//timer.cancel();
|
||||
connection.setState(Connection.State.SUCCEEDED);
|
||||
resultFuture.set(connection);
|
||||
});
|
||||
}
|
||||
@ -151,6 +142,13 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
UserThread.execute(() -> {
|
||||
//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);
|
||||
});
|
||||
}
|
||||
@ -171,10 +169,12 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||
final SettableFuture<Connection> resultFuture = SettableFuture.create();
|
||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||
public void onSuccess(Connection connection) {
|
||||
connection.setState(Connection.State.SUCCEEDED);
|
||||
UserThread.execute(() -> resultFuture.set(connection));
|
||||
}
|
||||
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
connection.setState(Connection.State.FAILED);
|
||||
UserThread.execute(() -> resultFuture.setException(throwable));
|
||||
}
|
||||
});
|
||||
@ -182,12 +182,30 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||
}
|
||||
|
||||
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.addAll(outBoundConnections);
|
||||
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) {
|
||||
Log.traceCall();
|
||||
log.info("Shutdown NetworkNode");
|
||||
@ -198,7 +216,7 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||
server = null;
|
||||
}
|
||||
|
||||
getAllConnections().stream().forEach(e -> e.shutDown());
|
||||
getAllConnections().stream().forEach(Connection::shutDown);
|
||||
|
||||
log.info("NetworkNode shutdown complete");
|
||||
if (shutDownCompleteHandler != null) shutDownCompleteHandler.run();
|
||||
@ -305,7 +323,6 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||
@Override
|
||||
public void onConnection(Connection connection) {
|
||||
Log.traceCall("startServerConnectionListener connection=" + connection);
|
||||
// we still have not authenticated so put it to the temp list
|
||||
inBoundConnections.add(connection);
|
||||
NetworkNode.this.onConnection(connection);
|
||||
}
|
||||
@ -329,19 +346,19 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||
executorService.submit(server);
|
||||
}
|
||||
|
||||
private Optional<Connection> lookupOutboundConnection(NodeAddress peerNodeAddress) {
|
||||
// Log.traceCall("search for " + peerAddress.toString() + " / outBoundConnections " + outBoundConnections);
|
||||
private Optional<Connection> lookupOutboundConnection(NodeAddress peersNodeAddress) {
|
||||
Log.traceCall("search for " + peersNodeAddress.toString() + " / outBoundConnections " + outBoundConnections);
|
||||
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) {
|
||||
// Log.traceCall("search for " + peerAddress.toString() + " / inBoundConnections " + inBoundConnections);
|
||||
private Optional<Connection> lookupInboundConnection(NodeAddress peersNodeAddress) {
|
||||
Log.traceCall("search for " + peersNodeAddress.toString() + " / inBoundConnections " + inBoundConnections);
|
||||
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
|
||||
abstract public NodeAddress getNodeAddress();
|
||||
|
@ -43,7 +43,7 @@ class Server implements Runnable {
|
||||
final Socket socket = serverSocket.accept();
|
||||
if (!stopped && !Thread.currentThread().isInterrupted()) {
|
||||
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" +
|
||||
"Server created new inbound connection:"
|
||||
@ -71,7 +71,7 @@ class Server implements Runnable {
|
||||
if (!stopped) {
|
||||
stopped = true;
|
||||
|
||||
connections.stream().forEach(e -> e.shutDown());
|
||||
connections.stream().forEach(Connection::shutDown);
|
||||
|
||||
try {
|
||||
serverSocket.close();
|
||||
|
@ -73,7 +73,7 @@ public class TorNetworkNode extends NetworkNode {
|
||||
Log.traceCall("torNode created");
|
||||
TorNetworkNode.this.torNetworkNode = torNode;
|
||||
|
||||
setupListeners.stream().forEach(e -> e.onTorNodeReady());
|
||||
setupListeners.stream().forEach(SetupListener::onTorNodeReady);
|
||||
|
||||
// Create Hidden Service (takes about 40 sec.)
|
||||
createHiddenService(torNode,
|
||||
@ -84,7 +84,7 @@ public class TorNetworkNode extends NetworkNode {
|
||||
TorNetworkNode.this.hiddenServiceDescriptor = hiddenServiceDescriptor;
|
||||
|
||||
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);
|
||||
|
||||
if (executorService != null) {
|
||||
executorService.submit(() -> {
|
||||
UserThread.execute(() -> {
|
||||
// We want to stay in UserThread
|
||||
super.shutDown(() -> {
|
||||
networkNodeShutDownDoneComplete = true;
|
||||
if (torShutDownComplete)
|
||||
shutDownExecutorService();
|
||||
});
|
||||
executorService.submit(() -> UserThread.execute(() -> {
|
||||
// We want to stay in UserThread
|
||||
super.shutDown(() -> {
|
||||
networkNodeShutDownDoneComplete = true;
|
||||
if (torShutDownComplete)
|
||||
shutDownExecutorService();
|
||||
});
|
||||
});
|
||||
}));
|
||||
} else {
|
||||
log.error("executorService must not be null at shutDown");
|
||||
}
|
||||
@ -188,10 +186,9 @@ public class TorNetworkNode extends NetworkNode {
|
||||
start(null);
|
||||
}, WAIT_BEFORE_RESTART, TimeUnit.MILLISECONDS));
|
||||
} else {
|
||||
String msg = "We tried to restart tor " + restartCounter
|
||||
+ " times, but we failed to get tor running. We give up now.";
|
||||
String msg = "We tried to restart Tor " + restartCounter
|
||||
+ " times, but it failed to start up. We give up now.";
|
||||
log.error(msg);
|
||||
// TODO display better error 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.NodeAddress;
|
||||
import io.bitsquare.p2p.network.Connection;
|
||||
import io.bitsquare.p2p.network.ConnectionListener;
|
||||
import io.bitsquare.p2p.network.MessageListener;
|
||||
import io.bitsquare.p2p.network.NetworkNode;
|
||||
import io.bitsquare.p2p.peers.messages.peers.GetPeersRequest;
|
||||
@ -19,47 +20,39 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PeerExchangeManager implements MessageListener {
|
||||
public class PeerExchangeManager implements MessageListener, ConnectionListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(PeerExchangeManager.class);
|
||||
|
||||
private final NetworkNode networkNode;
|
||||
private final Supplier<Set<ReportedPeer>> authenticatedAndReportedPeersSupplier;
|
||||
private final Supplier<Map<NodeAddress, Peer>> authenticatedPeersSupplier;
|
||||
private final Consumer<NodeAddress> removePeerConsumer;
|
||||
private final BiConsumer<HashSet<ReportedPeer>, Connection> addReportedPeersConsumer;
|
||||
private final PeerManager peerManager;
|
||||
private final Set<NodeAddress> seedNodeAddresses;
|
||||
private final ScheduledThreadPoolExecutor executor;
|
||||
private Timer requestReportedPeersTimer, checkForSeedNodesTimer;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public PeerExchangeManager(NetworkNode networkNode,
|
||||
Supplier<Set<ReportedPeer>> authenticatedAndReportedPeersSupplier,
|
||||
Supplier<Map<NodeAddress, Peer>> authenticatedPeersSupplier,
|
||||
Consumer<NodeAddress> removePeerConsumer,
|
||||
BiConsumer<HashSet<ReportedPeer>, Connection> addReportedPeersConsumer) {
|
||||
public PeerExchangeManager(NetworkNode networkNode, PeerManager peerManager, Set<NodeAddress> seedNodeAddresses) {
|
||||
this.networkNode = networkNode;
|
||||
this.authenticatedAndReportedPeersSupplier = authenticatedAndReportedPeersSupplier;
|
||||
this.authenticatedPeersSupplier = authenticatedPeersSupplier;
|
||||
this.removePeerConsumer = removePeerConsumer;
|
||||
this.addReportedPeersConsumer = addReportedPeersConsumer;
|
||||
this.peerManager = peerManager;
|
||||
this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
|
||||
|
||||
networkNode.addMessageListener(this);
|
||||
|
||||
executor = Utilities.getScheduledThreadPoolExecutor("PeerExchangeManager", 1, 10, 5);
|
||||
long delay = new Random().nextInt(60) + 60 * 6; // 6-7 min.
|
||||
executor.scheduleAtFixedRate(() -> UserThread.execute(() -> trySendGetPeersRequest()), delay, delay, TimeUnit.SECONDS);
|
||||
long delay = new Random().nextInt(60) + 60 * 6; // 6-7 min.
|
||||
executor.scheduleAtFixedRate(() -> UserThread.execute(() -> {
|
||||
sendGetPeersRequestToAllConnectedPeers();
|
||||
checkSeedNodes();
|
||||
}), delay, delay, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
@ -67,6 +60,28 @@ public class PeerExchangeManager implements MessageListener {
|
||||
|
||||
networkNode.removeMessageListener(this);
|
||||
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;
|
||||
HashSet<ReportedPeer> reportedPeers = getPeersRequestMessage.reportedPeers;
|
||||
log.trace("Received peers: " + reportedPeers);
|
||||
if (!connection.getPeersNodeAddressOptional().isPresent())
|
||||
connection.setPeersNodeAddress(getPeersRequestMessage.senderNodeAddress);
|
||||
|
||||
SettableFuture<Connection> future = networkNode.sendMessage(connection,
|
||||
new GetPeersResponse(new HashSet<>(authenticatedAndReportedPeersSupplier.get())));
|
||||
new GetPeersResponse(getReportedPeersHashSet(getPeersRequestMessage.senderNodeAddress)));
|
||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||
@Override
|
||||
public void onSuccess(Connection connection) {
|
||||
log.trace("GetPeersResponse sent successfully");
|
||||
|
||||
handleOnSuccess(connection, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
log.info("GetPeersResponse sending failed " + throwable.getMessage());
|
||||
removePeerConsumer.accept(getPeersRequestMessage.senderNodeAddress);
|
||||
}
|
||||
});
|
||||
addReportedPeersConsumer.accept(reportedPeers, connection);
|
||||
peerManager.addToReportedPeers(reportedPeers, connection);
|
||||
} else if (message instanceof GetPeersResponse) {
|
||||
GetPeersResponse getPeersResponse = (GetPeersResponse) message;
|
||||
HashSet<ReportedPeer> reportedPeers = getPeersResponse.reportedPeers;
|
||||
HashSet<ReportedPeer> reportedPeers = ((GetPeersResponse) message).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());
|
||||
if (!connectedPeersList.isEmpty()) {
|
||||
Log.traceCall();
|
||||
connectedPeersList.stream()
|
||||
.forEach(e -> {
|
||||
SettableFuture<Connection> future = networkNode.sendMessage(e.connection,
|
||||
new GetPeersRequest(networkNode.getNodeAddress(), new HashSet<>(authenticatedAndReportedPeersSupplier.get())));
|
||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||
@Override
|
||||
public void onSuccess(Connection connection) {
|
||||
log.trace("sendGetPeersRequest sent successfully");
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
log.info("sendGetPeersRequest sending failed " + throwable.getMessage());
|
||||
removePeerConsumer.accept(e.nodeAddress);
|
||||
}
|
||||
});
|
||||
});
|
||||
public void getReportedPeersFromFirstSeedNode(NodeAddress nodeAddress) {
|
||||
getReportedPeersFromSeedNode(nodeAddress, new ArrayList<>(seedNodeAddresses));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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>() {
|
||||
@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);
|
||||
|
||||
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.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.data.DataRequest;
|
||||
@ -25,9 +24,8 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@ -40,7 +38,11 @@ public class RequestDataManager implements MessageListener, AuthenticationListen
|
||||
|
||||
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> remainingPersistedPeers = new HashSet<>();
|
||||
private Listener listener;
|
||||
private Optional<NodeAddress> optionalConnectedSeedNodeAddress = Optional.empty();
|
||||
private Collection<NodeAddress> seedNodeNodeAddresses;
|
||||
protected Timer requestDataFromAuthenticatedSeedNodeTimer;
|
||||
private Timer requestDataTimer, requestDataWithPersistedPeersTimer;
|
||||
private boolean doNotifyNoSeedNodeAvailableListener = true;
|
||||
private Optional<NodeAddress> seedNodeOfPreliminaryDataRequest = Optional.empty();
|
||||
private final Collection<NodeAddress> seedNodeAddresses;
|
||||
private Timer requestDataFromSeedNodesTimer, requestDataFromPersistedPeersTimer, dataRequestTimeoutTimer;
|
||||
private boolean noSeedNodeAvailableListenerNotified;
|
||||
private boolean dataUpdateRequested;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public RequestDataManager(NetworkNode networkNode, P2PDataStorage dataStorage, PeerManager peerManager) {
|
||||
public RequestDataManager(NetworkNode networkNode, P2PDataStorage dataStorage, PeerManager peerManager, Set<NodeAddress> seedNodeAddresses) {
|
||||
this.networkNode = networkNode;
|
||||
this.dataStorage = dataStorage;
|
||||
this.peerManager = peerManager;
|
||||
this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
|
||||
|
||||
networkNode.addMessageListener(this);
|
||||
}
|
||||
@ -74,9 +77,9 @@ public class RequestDataManager implements MessageListener, AuthenticationListen
|
||||
|
||||
networkNode.removeMessageListener(this);
|
||||
|
||||
stopRequestDataTimer();
|
||||
stopRequestDataWithPersistedPeersTimer();
|
||||
stopRequestDataFromAuthenticatedSeedNodeTimer();
|
||||
stopRequestDataFromSeedNodesTimer();
|
||||
stopRequestDataFromPersistedPeersTimer();
|
||||
stopDataRequestTimeoutTimer();
|
||||
}
|
||||
|
||||
|
||||
@ -88,102 +91,158 @@ public class RequestDataManager implements MessageListener, AuthenticationListen
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void requestDataFromSeedNodes(Collection<NodeAddress> seedNodeNodeAddresses) {
|
||||
checkNotNull(seedNodeNodeAddresses, "requestDataFromSeedNodes: seedNodeAddresses must not be null.");
|
||||
checkArgument(!seedNodeNodeAddresses.isEmpty(), "requestDataFromSeedNodes: seedNodeAddresses must not be empty.");
|
||||
|
||||
this.seedNodeNodeAddresses = seedNodeNodeAddresses;
|
||||
requestData(seedNodeNodeAddresses);
|
||||
public void requestPreliminaryData() {
|
||||
requestDataFromPeers(seedNodeAddresses);
|
||||
}
|
||||
|
||||
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());
|
||||
checkArgument(!nodeAddresses.isEmpty(), "requestData: addresses must not be empty.");
|
||||
stopRequestDataTimer();
|
||||
checkArgument(!nodeAddresses.isEmpty(), "requestDataFromPeers: nodeAddresses must not be empty.");
|
||||
stopRequestDataFromSeedNodesTimer();
|
||||
List<NodeAddress> remainingNodeAddresses = new ArrayList<>(nodeAddresses);
|
||||
NodeAddress candidate = remainingNodeAddresses.get(new Random().nextInt(remainingNodeAddresses.size()));
|
||||
if (!peerManager.isInAuthenticationProcess(candidate)) {
|
||||
// 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());
|
||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Connection connection) {
|
||||
log.info("Send GetAllDataMessage to " + candidate + " succeeded.");
|
||||
checkArgument(!optionalConnectedSeedNodeAddress.isPresent(), "We have already a connectedSeedNode. That must not happen.");
|
||||
optionalConnectedSeedNodeAddress = Optional.of(candidate);
|
||||
|
||||
stopRequestDataTimer();
|
||||
stopRequestDataWithPersistedPeersTimer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
log.info("Send GetAllDataMessage to " + candidate + " failed. " +
|
||||
"That is expected if the node is offline. " +
|
||||
"Exception:" + throwable.getMessage());
|
||||
|
||||
if (!remainingNodeAddresses.isEmpty()) {
|
||||
log.info("There are more seed nodes available for requesting data. " +
|
||||
"We will try requestData again.");
|
||||
|
||||
ReportedPeer reportedPeer = new ReportedPeer(candidate);
|
||||
if (remainingPersistedPeers.contains(reportedPeer))
|
||||
remainingPersistedPeers.remove(reportedPeer);
|
||||
|
||||
requestData(remainingNodeAddresses);
|
||||
} else {
|
||||
log.info("There is no seed node available for requesting data. " +
|
||||
"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.");
|
||||
|
||||
requestDataWithPersistedPeers(candidate);
|
||||
requestDataWithSeedNodeAddresses();
|
||||
}
|
||||
}
|
||||
});
|
||||
} 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();
|
||||
}
|
||||
requestDataFromPeer(candidate, remainingNodeAddresses);
|
||||
}
|
||||
|
||||
private void requestDataWithSeedNodeAddresses() {
|
||||
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>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Connection connection) {
|
||||
log.info("Send DataRequest to " + nodeAddress + " succeeded.");
|
||||
|
||||
if (connection != null) {
|
||||
if (!connection.getPeersNodeAddressOptional().isPresent())
|
||||
connection.setPeersNodeAddress(nodeAddress);
|
||||
|
||||
if (connection.getPeerType() == null)
|
||||
connection.setPeerType(peerManager.isSeedNode(connection) ? Connection.PeerType.SEED_NODE : Connection.PeerType.PEER);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
log.info("Send DataRequest to " + nodeAddress + " failed. " +
|
||||
"That is expected if the node is offline. " +
|
||||
"Exception:" + throwable.getMessage());
|
||||
|
||||
if (!remainingNodeAddresses.isEmpty()) {
|
||||
log.info("There are more seed nodes available for requesting data. " +
|
||||
"We will try requestData again.");
|
||||
|
||||
ReportedPeer reportedPeer = new ReportedPeer(nodeAddress);
|
||||
if (remainingPersistedPeers.contains(reportedPeer))
|
||||
remainingPersistedPeers.remove(reportedPeer);
|
||||
|
||||
requestDataFromPeers(remainingNodeAddresses);
|
||||
} else {
|
||||
log.info("There is no seed node available for requesting data. " +
|
||||
"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.");
|
||||
|
||||
requestDataFromPersistedPeersAfterDelay(nodeAddress);
|
||||
requestDataFromSeedNodesAfterDelay();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void requestDataFromSeedNodesAfterDelay() {
|
||||
Log.traceCall();
|
||||
// We only want to notify the first time
|
||||
if (doNotifyNoSeedNodeAvailableListener) {
|
||||
doNotifyNoSeedNodeAvailableListener = false;
|
||||
if (!noSeedNodeAvailableListenerNotified) {
|
||||
noSeedNodeAvailableListenerNotified = true;
|
||||
listener.onNoSeedNodeAvailable();
|
||||
}
|
||||
if (requestDataTimer == null)
|
||||
requestDataTimer = UserThread.runAfterRandomDelay(() -> requestData(seedNodeNodeAddresses),
|
||||
|
||||
if (requestDataFromSeedNodesTimer == null)
|
||||
requestDataFromSeedNodesTimer = UserThread.runAfterRandomDelay(() -> requestDataFromPeers(seedNodeAddresses),
|
||||
10, 20, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void requestDataWithPersistedPeers(@Nullable NodeAddress failedPeer) {
|
||||
private void requestDataFromPersistedPeersAfterDelay(@Nullable NodeAddress failedPeer) {
|
||||
Log.traceCall("failedPeer=" + failedPeer);
|
||||
|
||||
stopRequestDataWithPersistedPeersTimer();
|
||||
stopRequestDataFromPersistedPeersTimer();
|
||||
|
||||
if (persistedPeers.isEmpty()) {
|
||||
persistedPeers.addAll(peerManager.getPersistedPeers());
|
||||
@ -203,117 +262,45 @@ public class RequestDataManager implements MessageListener, AuthenticationListen
|
||||
if (!persistedPeerNodeAddresses.isEmpty()) {
|
||||
log.info("We try to use persisted peers for requestData.");
|
||||
persistedPeersAvailable = true;
|
||||
requestData(persistedPeerNodeAddresses);
|
||||
requestDataFromPeers(persistedPeerNodeAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
if (!persistedPeersAvailable) {
|
||||
log.warn("No seed nodes and no persisted peers are available for requesting data.\n" +
|
||||
"We will try again after a random pause.");
|
||||
doNotifyNoSeedNodeAvailableListener = false;
|
||||
noSeedNodeAvailableListenerNotified = true;
|
||||
listener.onNoPeersAvailable();
|
||||
|
||||
// reset remainingPersistedPeers
|
||||
remainingPersistedPeers.clear();
|
||||
remainingPersistedPeers.addAll(persistedPeers);
|
||||
|
||||
if (!remainingPersistedPeers.isEmpty() && requestDataWithPersistedPeersTimer == null)
|
||||
requestDataWithPersistedPeersTimer = UserThread.runAfterRandomDelay(() ->
|
||||
requestDataWithPersistedPeers(null),
|
||||
if (!remainingPersistedPeers.isEmpty() && requestDataFromPersistedPeersTimer == null)
|
||||
requestDataFromPersistedPeersTimer = UserThread.runAfterRandomDelay(() ->
|
||||
requestDataFromPersistedPeersAfterDelay(null),
|
||||
30, 40, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// MessageListener implementation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@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 stopRequestDataFromSeedNodesTimer() {
|
||||
if (requestDataFromSeedNodesTimer != null) {
|
||||
requestDataFromSeedNodesTimer.cancel();
|
||||
requestDataFromSeedNodesTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// AuthenticationListener implementation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@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 stopRequestDataFromPersistedPeersTimer() {
|
||||
if (requestDataFromPersistedPeersTimer != null) {
|
||||
requestDataFromPersistedPeersTimer.cancel();
|
||||
requestDataFromPersistedPeersTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopRequestDataWithPersistedPeersTimer() {
|
||||
if (requestDataWithPersistedPeersTimer != null) {
|
||||
requestDataWithPersistedPeersTimer.cancel();
|
||||
requestDataWithPersistedPeersTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopRequestDataFromAuthenticatedSeedNodeTimer() {
|
||||
if (requestDataFromAuthenticatedSeedNodeTimer != null) {
|
||||
requestDataFromAuthenticatedSeedNodeTimer.cancel();
|
||||
requestDataFromAuthenticatedSeedNodeTimer = null;
|
||||
private void stopDataRequestTimeoutTimer() {
|
||||
if (dataRequestTimeoutTimer != null) {
|
||||
dataRequestTimeoutTimer.cancel();
|
||||
dataRequestTimeoutTimer = 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.p2p.Message;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class DataRequest implements Message {
|
||||
// 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 final int networkId = Version.getNetworkId();
|
||||
@Nullable
|
||||
public final NodeAddress senderNodeAddress;
|
||||
|
||||
public DataRequest() {
|
||||
public DataRequest(@Nullable NodeAddress senderNodeAddress) {
|
||||
this.senderNodeAddress = senderNodeAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -20,7 +26,8 @@ public final class DataRequest implements Message {
|
||||
@Override
|
||||
public String toString() {
|
||||
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.P2PService;
|
||||
import io.bitsquare.p2p.P2PServiceListener;
|
||||
import io.bitsquare.p2p.SeedNodeP2PService;
|
||||
import io.bitsquare.p2p.peers.PeerManager;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
@ -26,10 +25,10 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
public class SeedNode {
|
||||
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 Set<NodeAddress> progArgSeedNodes;
|
||||
private SeedNodeP2PService seedNodeP2PService;
|
||||
private P2PService seedNodeP2PService;
|
||||
private boolean stopped;
|
||||
private final String defaultUserDataDir;
|
||||
|
||||
@ -54,7 +53,7 @@ public class SeedNode {
|
||||
if (args.length > 0) {
|
||||
String arg0 = args[0];
|
||||
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) {
|
||||
String arg1 = args[1];
|
||||
int networkId = Integer.parseInt(arg1);
|
||||
@ -65,10 +64,10 @@ public class SeedNode {
|
||||
String arg2 = args[2];
|
||||
int maxConnections = Integer.parseInt(arg2);
|
||||
checkArgument(maxConnections < 1000, "maxConnections seems to be a bit too high...");
|
||||
PeerManager.setMaxConnectionsLowPriority(maxConnections);
|
||||
PeerManager.setMaxConnections(maxConnections);
|
||||
} else {
|
||||
// we keep default a higher connection size for seed nodes
|
||||
PeerManager.setMaxConnectionsLowPriority(50);
|
||||
PeerManager.setMaxConnections(50);
|
||||
}
|
||||
if (args.length > 3) {
|
||||
String arg3 = args[3];
|
||||
@ -86,7 +85,7 @@ public class SeedNode {
|
||||
"Wrong program argument");
|
||||
progArgSeedNodes.add(new NodeAddress(e));
|
||||
});
|
||||
progArgSeedNodes.remove(mySeedNodeNodeAddress);
|
||||
progArgSeedNodes.remove(mySeedNodeAddress);
|
||||
} else if (args.length > 5) {
|
||||
log.error("Too many program arguments." +
|
||||
"\nProgram arguments: myAddress (incl. port) bitcoinNetworkId " +
|
||||
@ -100,11 +99,11 @@ public class SeedNode {
|
||||
}
|
||||
|
||||
public void createAndStartP2PService(boolean useDetailedLogging) {
|
||||
createAndStartP2PService(mySeedNodeNodeAddress, useLocalhost, Version.getNetworkId(), useDetailedLogging, progArgSeedNodes, null);
|
||||
createAndStartP2PService(mySeedNodeAddress, useLocalhost, Version.getNetworkId(), useDetailedLogging, progArgSeedNodes, null);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void createAndStartP2PService(NodeAddress mySeedNodeNodeAddress,
|
||||
public void createAndStartP2PService(NodeAddress mySeedNodeAddress,
|
||||
boolean useLocalhost,
|
||||
int networkId,
|
||||
boolean useDetailedLogging,
|
||||
@ -113,7 +112,7 @@ public class SeedNode {
|
||||
Log.traceCall();
|
||||
|
||||
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();
|
||||
Log.setup(logPath, useDetailedLogging);
|
||||
@ -122,9 +121,9 @@ public class SeedNode {
|
||||
SeedNodesRepository seedNodesRepository = new SeedNodesRepository();
|
||||
if (progArgSeedNodes != null && !progArgSeedNodes.isEmpty()) {
|
||||
if (useLocalhost)
|
||||
seedNodesRepository.setLocalhostSeedNodeNodeAddresses(progArgSeedNodes);
|
||||
seedNodesRepository.setLocalhostSeedNodeAddresses(progArgSeedNodes);
|
||||
else
|
||||
seedNodesRepository.setTorSeedNodeNodeAddresses(progArgSeedNodes);
|
||||
seedNodesRepository.setTorSeedNodeAddresses(progArgSeedNodes);
|
||||
}
|
||||
|
||||
File storageDir = Paths.get(appPath.toString(), "db").toFile();
|
||||
@ -135,7 +134,8 @@ public class SeedNode {
|
||||
if (torDir.mkdirs())
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ public class SeedNodesRepository {
|
||||
// mainnet use port 8000
|
||||
// testnet use port 8001
|
||||
// regtest use port 8002
|
||||
private Set<NodeAddress> torSeedNodeNodeAddresses = Sets.newHashSet(
|
||||
private Set<NodeAddress> torSeedNodeAddresses = Sets.newHashSet(
|
||||
// mainnet
|
||||
new NodeAddress("lih5zsr2bvxi24pk.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
|
||||
new NodeAddress("localhost:2000"),
|
||||
new NodeAddress("localhost:3000"),
|
||||
@ -48,21 +48,28 @@ public class SeedNodesRepository {
|
||||
new NodeAddress("localhost:3002"),
|
||||
new NodeAddress("localhost:4002")
|
||||
);
|
||||
private NodeAddress nodeAddressToExclude;
|
||||
|
||||
public Set<NodeAddress> getSeedNodeAddresses(boolean useLocalhost, int networkId) {
|
||||
String networkIdAsString = String.valueOf(networkId);
|
||||
Set<NodeAddress> nodeAddresses = useLocalhost ? localhostSeedNodeNodeAddresses : torSeedNodeNodeAddresses;
|
||||
Set<NodeAddress> nodeAddresses = useLocalhost ? localhostSeedNodeAddresses : torSeedNodeAddresses;
|
||||
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);
|
||||
return filtered;
|
||||
}
|
||||
|
||||
public void setTorSeedNodeNodeAddresses(Set<NodeAddress> torSeedNodeNodeAddresses) {
|
||||
this.torSeedNodeNodeAddresses = torSeedNodeNodeAddresses;
|
||||
public void setTorSeedNodeAddresses(Set<NodeAddress> torSeedNodeAddresses) {
|
||||
this.torSeedNodeAddresses = torSeedNodeAddresses;
|
||||
}
|
||||
|
||||
public void setLocalhostSeedNodeNodeAddresses(Set<NodeAddress> localhostSeedNodeNodeAddresses) {
|
||||
this.localhostSeedNodeNodeAddresses = localhostSeedNodeNodeAddresses;
|
||||
public void setLocalhostSeedNodeAddresses(Set<NodeAddress> localhostSeedNodeAddresses) {
|
||||
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.NodeAddress;
|
||||
import io.bitsquare.p2p.network.Connection;
|
||||
import io.bitsquare.p2p.network.IllegalRequest;
|
||||
import io.bitsquare.p2p.network.MessageListener;
|
||||
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.messages.AddDataMessage;
|
||||
import io.bitsquare.p2p.storage.messages.DataBroadcastMessage;
|
||||
@ -41,7 +40,7 @@ public class P2PDataStorage implements MessageListener {
|
||||
@VisibleForTesting
|
||||
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 CopyOnWriteArraySet<HashMapChangedListener> hashMapChangedListeners = new CopyOnWriteArraySet<>();
|
||||
private HashMap<ByteArray, Integer> sequenceNumberMap = new HashMap<>();
|
||||
@ -52,9 +51,9 @@ public class P2PDataStorage implements MessageListener {
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public P2PDataStorage(PeerManager peerManager, NetworkNode networkNode, File storageDir) {
|
||||
public P2PDataStorage(Broadcaster broadcaster, NetworkNode networkNode, File storageDir) {
|
||||
Log.traceCall();
|
||||
this.peerManager = peerManager;
|
||||
this.broadcaster = broadcaster;
|
||||
|
||||
networkNode.addMessageListener(this);
|
||||
|
||||
@ -70,8 +69,7 @@ public class P2PDataStorage implements MessageListener {
|
||||
if (persisted != null)
|
||||
sequenceNumberMap = persisted;
|
||||
|
||||
removeExpiredEntriesExecutor.scheduleAtFixedRate(() -> UserThread.execute(()
|
||||
-> removeExpiredEntries()), CHECK_TTL_INTERVAL, CHECK_TTL_INTERVAL, TimeUnit.SECONDS);
|
||||
removeExpiredEntriesExecutor.scheduleAtFixedRate(() -> UserThread.execute(this::removeExpiredEntries), CHECK_TTL_INTERVAL, CHECK_TTL_INTERVAL, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void removeExpiredEntries() {
|
||||
@ -105,22 +103,16 @@ public class P2PDataStorage implements MessageListener {
|
||||
public void onMessage(Message message, Connection connection) {
|
||||
if (message instanceof DataBroadcastMessage) {
|
||||
Log.traceCall(message.toString());
|
||||
if (connection.isAuthenticated()) {
|
||||
log.trace("ProtectedExpirableDataMessage received " + message + " on connection " + connection);
|
||||
connection.getPeerAddressOptional().ifPresent(peerAddress -> {
|
||||
if (message instanceof AddDataMessage) {
|
||||
add(((AddDataMessage) message).data, peerAddress);
|
||||
} else if (message instanceof RemoveDataMessage) {
|
||||
remove(((RemoveDataMessage) message).data, peerAddress);
|
||||
} else if (message instanceof RemoveMailboxDataMessage) {
|
||||
removeMailboxData(((RemoveMailboxDataMessage) message).data, peerAddress);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.warn("Connection is not authenticated yet. " +
|
||||
"We don't accept storage operations from non-authenticated nodes. connection=", connection);
|
||||
connection.reportIllegalRequest(IllegalRequest.NotAuthenticated);
|
||||
}
|
||||
log.trace("DataBroadcastMessage received " + message + " on connection " + connection);
|
||||
connection.getPeersNodeAddressOptional().ifPresent(peersNodeAddress -> {
|
||||
if (message instanceof AddDataMessage) {
|
||||
add(((AddDataMessage) message).data, peersNodeAddress);
|
||||
} else if (message instanceof RemoveDataMessage) {
|
||||
remove(((RemoveDataMessage) message).data, peersNodeAddress);
|
||||
} else if (message instanceof RemoveMailboxDataMessage) {
|
||||
removeMailboxData(((RemoveMailboxDataMessage) message).data, peersNodeAddress);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,7 +362,7 @@ public class P2PDataStorage implements MessageListener {
|
||||
|
||||
private void broadcast(DataBroadcastMessage message, @Nullable NodeAddress sender) {
|
||||
Log.traceCall(message.toString());
|
||||
peerManager.broadcast(message, sender);
|
||||
broadcaster.broadcast(message, sender);
|
||||
}
|
||||
|
||||
private ByteArray getHashAsByteArray(ExpirablePayload payload) {
|
||||
|
@ -18,6 +18,7 @@ public class DataAndSeqNr implements Serializable {
|
||||
|
||||
DataAndSeqNr that = (DataAndSeqNr) o;
|
||||
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (sequenceNumber != that.sequenceNumber) return false;
|
||||
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
|
||||
// Ignored for automated tests
|
||||
|
||||
//TODO P2P network tests are outdated
|
||||
@Ignore
|
||||
public class P2PServiceTest {
|
||||
private static final Logger log = LoggerFactory.getLogger(P2PServiceTest.class);
|
||||
@ -60,7 +62,7 @@ public class P2PServiceTest {
|
||||
|
||||
LocalhostNetworkNode.setSimulateTorDelayTorNode(10);
|
||||
LocalhostNetworkNode.setSimulateTorDelayHiddenService(100);
|
||||
PeerManager.setMaxConnectionsLowPriority(8);
|
||||
PeerManager.setMaxConnections(8);
|
||||
|
||||
keyRing1 = new KeyRing(new KeyStorage(dir1));
|
||||
keyRing2 = new KeyRing(new KeyStorage(dir2));
|
||||
|
@ -95,7 +95,7 @@ public class TestUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
public void onBootstrapped() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -123,9 +123,9 @@ public class TestUtils {
|
||||
SeedNodesRepository seedNodesRepository = new SeedNodesRepository();
|
||||
if (seedNodes != null && !seedNodes.isEmpty()) {
|
||||
if (useLocalhost)
|
||||
seedNodesRepository.setLocalhostSeedNodeNodeAddresses(seedNodes);
|
||||
seedNodesRepository.setLocalhostSeedNodeAddresses(seedNodes);
|
||||
else
|
||||
seedNodesRepository.setTorSeedNodeNodeAddresses(seedNodes);
|
||||
seedNodesRepository.setTorSeedNodeAddresses(seedNodes);
|
||||
}
|
||||
|
||||
P2PService p2PService = new P2PService(seedNodesRepository, port, new File("seed_node_" + port), useLocalhost,
|
||||
@ -148,7 +148,7 @@ public class TestUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
public void onBootstrapped() {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
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.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
@ -16,6 +14,8 @@ import java.util.concurrent.CountDownLatch;
|
||||
// TorNode created. Took 6 sec.
|
||||
// Hidden service created. Took 40-50 sec.
|
||||
// Connection establishment takes about 4 sec.
|
||||
|
||||
//TODO P2P network tests are outdated
|
||||
@Ignore
|
||||
public class LocalhostNetworkNodeTest {
|
||||
private static final Logger log = LoggerFactory.getLogger(LocalhostNetworkNodeTest.class);
|
||||
@ -77,8 +77,6 @@ public class LocalhostNetworkNodeTest {
|
||||
});
|
||||
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();
|
||||
|
||||
CountDownLatch shutDownLatch = new CountDownLatch(2);
|
||||
|
@ -20,6 +20,7 @@ import java.util.concurrent.CountDownLatch;
|
||||
// TorNode created. Took 6 sec.
|
||||
// Hidden service created. Took 40-50 sec.
|
||||
// Connection establishment takes about 4 sec.
|
||||
//TODO P2P network tests are outdated
|
||||
@Ignore
|
||||
public class TorNetworkNodeTest {
|
||||
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.peers.PeerManager;
|
||||
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.LoggerFactory;
|
||||
|
||||
@ -19,6 +22,7 @@ import java.util.concurrent.CountDownLatch;
|
||||
// Connection establishment takes about 4 sec.
|
||||
|
||||
// need to define seed node addresses first before using tor version
|
||||
//TODO P2P network tests are outdated
|
||||
@Ignore
|
||||
public class PeerManagerTest {
|
||||
private static final Logger log = LoggerFactory.getLogger(PeerManagerTest.class);
|
||||
@ -33,7 +37,7 @@ public class PeerManagerTest {
|
||||
public void setup() throws InterruptedException {
|
||||
LocalhostNetworkNode.setSimulateTorDelayTorNode(50);
|
||||
LocalhostNetworkNode.setSimulateTorDelayHiddenService(8);
|
||||
PeerManager.setMaxConnectionsLowPriority(100);
|
||||
PeerManager.setMaxConnections(100);
|
||||
|
||||
seedNodes = new HashSet<>();
|
||||
if (useLocalhost) {
|
||||
@ -100,7 +104,7 @@ public class PeerManagerTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
public void onBootstrapped() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -116,7 +120,7 @@ public class PeerManagerTest {
|
||||
P2PService p2PService1 = seedNode1.getSeedNodeP2PService();
|
||||
latch.await();
|
||||
Thread.sleep(500);
|
||||
Assert.assertEquals(0, p2PService1.getPeerManager().getAuthenticatedAndReportedPeers().size());
|
||||
//Assert.assertEquals(0, p2PService1.getPeerManager().getAuthenticatedAndReportedPeers().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -151,7 +155,7 @@ public class PeerManagerTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
public void onBootstrapped() {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@ -189,7 +193,7 @@ public class PeerManagerTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
public void onBootstrapped() {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@ -205,8 +209,8 @@ public class PeerManagerTest {
|
||||
});
|
||||
P2PService p2PService2 = seedNode2.getSeedNodeP2PService();
|
||||
latch.await();
|
||||
Assert.assertEquals(1, p2PService1.getPeerManager().getAuthenticatedAndReportedPeers().size());
|
||||
Assert.assertEquals(1, p2PService2.getPeerManager().getAuthenticatedAndReportedPeers().size());
|
||||
// Assert.assertEquals(1, p2PService1.getPeerManager().getAuthenticatedAndReportedPeers().size());
|
||||
// Assert.assertEquals(1, p2PService2.getPeerManager().getAuthenticatedAndReportedPeers().size());
|
||||
}
|
||||
|
||||
// @Test
|
||||
@ -426,7 +430,7 @@ public class PeerManagerTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstPeerAuthenticated() {
|
||||
public void onBootstrapped() {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -9,6 +9,7 @@ import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.p2p.TestUtils;
|
||||
import io.bitsquare.p2p.mocks.MockMessage;
|
||||
import io.bitsquare.p2p.network.NetworkNode;
|
||||
import io.bitsquare.p2p.peers.Broadcaster;
|
||||
import io.bitsquare.p2p.peers.PeerManager;
|
||||
import io.bitsquare.p2p.storage.data.DataAndSeqNr;
|
||||
import io.bitsquare.p2p.storage.data.ExpirableMailboxPayload;
|
||||
@ -30,6 +31,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
//TODO P2P network tests are outdated
|
||||
@Ignore
|
||||
public class ProtectedDataStorageTest {
|
||||
private static final Logger log = LoggerFactory.getLogger(ProtectedDataStorageTest.class);
|
||||
@ -65,8 +67,11 @@ public class ProtectedDataStorageTest {
|
||||
storageSignatureKeyPair1 = keyRing1.getSignatureKeyPair();
|
||||
encryptionService1 = new EncryptionService(keyRing1);
|
||||
networkNode1 = TestUtils.getAndStartSeedNode(8001, useClearNet, seedNodes).getSeedNodeP2PService().getNetworkNode();
|
||||
peerManager1 = new PeerManager(networkNode1, new File("dummy"));
|
||||
dataStorage1 = new P2PDataStorage(peerManager1, networkNode1, new File("dummy"));
|
||||
peerManager1 = new PeerManager(networkNode1, null, new File("dummy"));
|
||||
|
||||
//TODO
|
||||
Broadcaster broadcaster = new Broadcaster(networkNode1);
|
||||
dataStorage1 = new P2PDataStorage(broadcaster, networkNode1, new File("dummy"));
|
||||
|
||||
// for mailbox
|
||||
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 {
|
||||
P2PDataStorage.CHECK_TTL_INTERVAL = 10;
|
||||
// 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;
|
||||
|
||||
ProtectedData data = dataStorage1.getDataWithSignedSeqNr(mockData, storageSignatureKeyPair1);
|
||||
|
Loading…
Reference in New Issue
Block a user