Remove authentication for P2P network, UI improvements

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

View File

@ -26,7 +26,7 @@ import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.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) {

View File

@ -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);
}

View File

@ -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);

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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();
}
}

View File

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

View File

@ -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"/>

View File

@ -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>>() {

View File

@ -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>

View File

@ -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.");
}
}
}

View File

@ -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();
}
}

View File

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

View File

@ -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();
}
}

View File

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

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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)");
});
}

View File

@ -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);
});

View File

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

View File

@ -1,7 +1,7 @@
package io.bitsquare.p2p;
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();
}

View File

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

View File

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

View File

@ -8,6 +8,7 @@ import com.google.inject.name.Named;
import io.bitsquare.app.Log;
import io.bitsquare.app.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.");
}
}

View File

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

View File

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

View File

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

View File

@ -10,6 +10,9 @@ import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.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 +
'}';
}

View File

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

View File

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

View File

@ -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);
});
});
}

View File

@ -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();

View File

@ -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();

View File

@ -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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -10,6 +10,7 @@ import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.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

View File

@ -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;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,14 +2,20 @@ package io.bitsquare.p2p.peers.messages.data;
import io.bitsquare.app.Version;
import io.bitsquare.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 +
'}';
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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));

View File

@ -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();
}

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);