mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Merge pull request #6330 from jmacxx/network_status_indicator
Feature: P2P network status indicator
This commit is contained in:
commit
8929567725
@ -94,7 +94,6 @@ public class BisqHeadlessApp implements HeadlessApp {
|
||||
bisqSetup.setShowPopupIfInvalidBtcConfigHandler(() -> log.error("onShowPopupIfInvalidBtcConfigHandler"));
|
||||
bisqSetup.setRevolutAccountsUpdateHandler(revolutAccountList -> log.info("setRevolutAccountsUpdateHandler: revolutAccountList={}", revolutAccountList));
|
||||
bisqSetup.setQubesOSInfoHandler(() -> log.info("setQubesOSInfoHandler"));
|
||||
bisqSetup.setFirewallIssueHandler(() -> log.info("setFirewallIssueHandler"));
|
||||
bisqSetup.setDownGradePreventionHandler(lastVersion -> log.info("Downgrade from version {} to version {} is not supported",
|
||||
lastVersion, Version.VERSION));
|
||||
bisqSetup.setDaoRequiresRestartHandler(() -> {
|
||||
|
@ -198,9 +198,6 @@ public class BisqSetup {
|
||||
private Runnable qubesOSInfoHandler;
|
||||
@Setter
|
||||
@Nullable
|
||||
private Runnable firewallIssueHandler;
|
||||
@Setter
|
||||
@Nullable
|
||||
private Runnable daoRequiresRestartHandler;
|
||||
@Setter
|
||||
@Nullable
|
||||
@ -217,7 +214,6 @@ public class BisqSetup {
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private MonadicBinding<Boolean> p2pNetworkAndWalletInitialized;
|
||||
private final List<BisqSetupListener> bisqSetupListeners = new ArrayList<>();
|
||||
private int failedSelfPings = 0;
|
||||
|
||||
@Inject
|
||||
public BisqSetup(DomainInitialisation domainInitialisation,
|
||||
@ -338,7 +334,7 @@ public class BisqSetup {
|
||||
maybeShowLocalhostRunningInfo();
|
||||
maybeShowAccountSigningStateInfo();
|
||||
maybeShowTorAddressUpgradeInformation();
|
||||
checkTorFirewall();
|
||||
checkInboundConnections();
|
||||
}
|
||||
|
||||
|
||||
@ -662,30 +658,34 @@ public class BisqSetup {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have inbound connections. If not, try to ping ourselves.
|
||||
* If Bisq cannot connect to its own onion address through Tor, display
|
||||
* an informative message to let the user know to configure their firewall else
|
||||
* their offers will not be reachable.
|
||||
* In rare cases a self ping can fail (thanks Tor), so we retry up to 3 times at random intervals in hope of success.
|
||||
* Repeat this test hourly.
|
||||
*/
|
||||
private void checkTorFirewall() {
|
||||
private void checkInboundConnections() {
|
||||
NodeAddress onionAddress = p2PService.getNetworkNode().nodeAddressProperty().get();
|
||||
if (onionAddress == null || !onionAddress.getFullAddress().contains("onion")) {
|
||||
return;
|
||||
}
|
||||
privateNotificationManager.sendPing(onionAddress, stringResult -> {
|
||||
log.info(stringResult);
|
||||
if (stringResult.contains("failed")) {
|
||||
// the self-ping failed: after 3 failures notify the user and stop trying
|
||||
if (++failedSelfPings >= 3) {
|
||||
if (firewallIssueHandler != null) {
|
||||
firewallIssueHandler.run();
|
||||
}
|
||||
} else {
|
||||
// retry self ping after a random delay
|
||||
UserThread.runAfter(this::checkTorFirewall, new Random().nextInt((int) STARTUP_TIMEOUT_MINUTES), TimeUnit.MINUTES);
|
||||
|
||||
if (p2PService.getNetworkNode().upTime() > TimeUnit.HOURS.toMillis(1) &&
|
||||
p2PService.getNetworkNode().getInboundConnectionCount() == 0) {
|
||||
// we've been online a while and did not find any inbound connections; lets try the self-ping check
|
||||
log.info("no recent inbound connections found, starting the self-ping test");
|
||||
privateNotificationManager.sendPing(onionAddress, stringResult -> {
|
||||
log.info(stringResult);
|
||||
if (stringResult.contains("failed")) {
|
||||
getP2PNetworkStatusIconId().set("flashing:image-yellow_circle");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// schedule another inbound connection check for later
|
||||
int nextCheckInMinutes = 30 + new Random().nextInt(30);
|
||||
log.debug("next inbound connections check in {} minutes", nextCheckInMinutes);
|
||||
UserThread.runAfter(this::checkInboundConnections, nextCheckInMinutes, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
private void maybeShowSecurityRecommendation() {
|
||||
@ -828,6 +828,10 @@ public class BisqSetup {
|
||||
return p2PNetworkSetup.getP2PNetworkIconId();
|
||||
}
|
||||
|
||||
public StringProperty getP2PNetworkStatusIconId() {
|
||||
return p2PNetworkSetup.getP2PNetworkStatusIconId();
|
||||
}
|
||||
|
||||
public BooleanProperty getUpdatedDataReceived() {
|
||||
return p2PNetworkSetup.getUpdatedDataReceived();
|
||||
}
|
||||
|
@ -65,6 +65,8 @@ public class P2PNetworkSetup {
|
||||
@Getter
|
||||
final StringProperty p2PNetworkIconId = new SimpleStringProperty();
|
||||
@Getter
|
||||
final StringProperty p2PNetworkStatusIconId = new SimpleStringProperty();
|
||||
@Getter
|
||||
final BooleanProperty splashP2PNetworkAnimationVisible = new SimpleBooleanProperty(true);
|
||||
@Getter
|
||||
final StringProperty p2pNetworkLabelId = new SimpleStringProperty("footer-pane");
|
||||
@ -127,10 +129,12 @@ public class P2PNetworkSetup {
|
||||
p2PService.getNetworkNode().addConnectionListener(new ConnectionListener() {
|
||||
@Override
|
||||
public void onConnection(Connection connection) {
|
||||
updateNetworkStatusIndicator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) {
|
||||
updateNetworkStatusIndicator();
|
||||
// We only check at seed nodes as they are running the latest version
|
||||
// Other disconnects might be caused by peers running an older version
|
||||
if (connection.getConnectionState().isSeedNode() &&
|
||||
@ -243,4 +247,14 @@ public class P2PNetworkSetup {
|
||||
!(payload instanceof ProofOfWorkPayload);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateNetworkStatusIndicator() {
|
||||
if (p2PService.getNetworkNode().getInboundConnectionCount() > 0) {
|
||||
p2PNetworkStatusIconId.set("image-green_circle");
|
||||
} else if (p2PService.getNetworkNode().getOutboundConnectionCount() > 0) {
|
||||
p2PNetworkStatusIconId.set("image-yellow_circle");
|
||||
} else {
|
||||
p2PNetworkStatusIconId.set("image-alert-round");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -293,7 +293,7 @@ mainView.networkWarning.clockWatcher=Your computer was asleep for {0} seconds. \
|
||||
Standby mode has been known to cause trades to fail. \
|
||||
In order to operate correctly Bisq requires that standby mode be disabled in your computer's settings.
|
||||
mainView.version.update=(Update available)
|
||||
|
||||
mainView.status.connections=Inbound connections: {0}\nOutbound connections: {1}
|
||||
|
||||
####################################################################
|
||||
# MarketView
|
||||
@ -3126,9 +3126,15 @@ popup.info.shutDownWithTradeInit={0}\n\
|
||||
This trade has not finished initializing; shutting down now will probably make it corrupted. Please wait a minute and try again.
|
||||
popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\n\
|
||||
Please make sure your Bisq qube is setup according to our Setup Guide at [HYPERLINK:https://bisq.wiki/Running_Bisq_on_Qubes].
|
||||
popup.info.firewallSetupInfo=It appears this machine blocks incoming Tor connections. \
|
||||
This can happen in VM environments such as Qubes/VirtualBox/Whonix. \n\n\
|
||||
Please set up your environment to accept incoming Tor connections, otherwise no-one will be able to take your offers.
|
||||
popup.info.p2pStatusIndicator.red={0}\n\n\
|
||||
Your node has no connection to the P2P network. Bisq cannot operate in this state. \
|
||||
See [HYPERLINK:https://bisq.wiki/Network_status_indicator] for more information.
|
||||
popup.info.p2pStatusIndicator.yellow={0}\n\n\
|
||||
Your node has no inbound Tor connections. Bisq will function ok, but if this state persists for several hours it may be an indication of connectivity problems. \
|
||||
See [HYPERLINK:https://bisq.wiki/Network_status_indicator] for more information.
|
||||
popup.info.p2pStatusIndicator.green={0}\n\n\
|
||||
Good news, your P2P connection state looks healthy! \
|
||||
[HYPERLINK:https://bisq.wiki/Network_status_indicator]
|
||||
popup.warn.downGradePrevention=Downgrade from version {0} to version {1} is not supported. Please use the latest Bisq version.
|
||||
popup.warn.daoRequiresRestart=There was a problem with synchronizing the DAO state. You have to restart the application to fix the issue.
|
||||
|
||||
|
@ -21,6 +21,10 @@
|
||||
-fx-image: url("../../images/green_circle.png");
|
||||
}
|
||||
|
||||
#image-yellow_circle {
|
||||
-fx-image: url("../../images/yellow_circle.png");
|
||||
}
|
||||
|
||||
#image-blue_circle {
|
||||
-fx-image: url("../../images/blue_circle.png");
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import bisq.desktop.main.market.offerbook.OfferBookChartView;
|
||||
import bisq.desktop.main.offer.BuyOfferView;
|
||||
import bisq.desktop.main.offer.SellOfferView;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.overlays.windows.TorNetworkSettingsWindow;
|
||||
import bisq.desktop.main.portfolio.PortfolioView;
|
||||
import bisq.desktop.main.settings.SettingsView;
|
||||
import bisq.desktop.main.shared.PriceFeedComboBoxItem;
|
||||
@ -60,6 +61,10 @@ import com.jfoenix.controls.JFXBadge;
|
||||
import com.jfoenix.controls.JFXComboBox;
|
||||
import com.jfoenix.controls.JFXProgressBar;
|
||||
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
@ -92,6 +97,8 @@ import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
|
||||
import javafx.util.Duration;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
@ -156,17 +163,20 @@ public class MainView extends InitializableView<StackPane, MainViewModel>
|
||||
private Label btcSplashInfo;
|
||||
private Popup p2PNetworkWarnMsgPopup, btcNetworkWarnMsgPopup;
|
||||
private final DaoStateMonitoringService daoStateMonitoringService;
|
||||
private final TorNetworkSettingsWindow torNetworkSettingsWindow;
|
||||
|
||||
@Inject
|
||||
public MainView(MainViewModel model,
|
||||
CachingViewLoader viewLoader,
|
||||
Navigation navigation,
|
||||
Transitions transitions,
|
||||
TorNetworkSettingsWindow torNetworkSettingsWindow,
|
||||
DaoStateMonitoringService daoStateMonitoringService) {
|
||||
super(model);
|
||||
this.viewLoader = viewLoader;
|
||||
this.navigation = navigation;
|
||||
MainView.transitions = transitions;
|
||||
this.torNetworkSettingsWindow = torNetworkSettingsWindow;
|
||||
this.daoStateMonitoringService = daoStateMonitoringService;
|
||||
}
|
||||
|
||||
@ -631,6 +641,9 @@ public class MainView extends InitializableView<StackPane, MainViewModel>
|
||||
splashP2PNetworkIcon.setVisible(false);
|
||||
splashP2PNetworkIcon.setManaged(false);
|
||||
HBox.setMargin(splashP2PNetworkIcon, new Insets(0, 0, 5, 0));
|
||||
splashP2PNetworkIcon.setOnMouseClicked(e -> {
|
||||
torNetworkSettingsWindow.show();
|
||||
});
|
||||
|
||||
Timer showTorNetworkSettingsTimer = UserThread.runAfter(() -> {
|
||||
showTorNetworkSettingsButton.setVisible(true);
|
||||
@ -772,6 +785,40 @@ public class MainView extends InitializableView<StackPane, MainViewModel>
|
||||
p2PNetworkWarnMsgPopup.hide();
|
||||
}
|
||||
});
|
||||
p2PNetworkIcon.setOnMouseClicked(e -> {
|
||||
torNetworkSettingsWindow.show();
|
||||
});
|
||||
|
||||
ImageView p2PNetworkStatusIcon = new ImageView();
|
||||
setRightAnchor(p2PNetworkStatusIcon, 30d);
|
||||
setBottomAnchor(p2PNetworkStatusIcon, 7d);
|
||||
Tooltip p2pNetworkStatusToolTip = new Tooltip();
|
||||
Tooltip.install(p2PNetworkStatusIcon, p2pNetworkStatusToolTip);
|
||||
p2PNetworkStatusIcon.setOnMouseEntered(e -> p2pNetworkStatusToolTip.setText(model.getP2pConnectionSummary()));
|
||||
Timeline flasher = new Timeline(
|
||||
new KeyFrame(Duration.seconds(0.5), e -> p2PNetworkStatusIcon.setOpacity(0.2)),
|
||||
new KeyFrame(Duration.seconds(1.0), e -> p2PNetworkStatusIcon.setOpacity(1))
|
||||
);
|
||||
flasher.setCycleCount(Animation.INDEFINITE);
|
||||
model.getP2PNetworkStatusIconId().addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue.equalsIgnoreCase("flashing:image-yellow_circle")) {
|
||||
p2PNetworkStatusIcon.setId("image-yellow_circle");
|
||||
flasher.play();
|
||||
} else {
|
||||
p2PNetworkStatusIcon.setId(newValue);
|
||||
flasher.stop();
|
||||
p2PNetworkStatusIcon.setOpacity(1);
|
||||
}
|
||||
});
|
||||
p2PNetworkStatusIcon.setOnMouseClicked(e -> {
|
||||
if (p2PNetworkStatusIcon.getId().equalsIgnoreCase("image-alert-round")) {
|
||||
new Popup().warning(Res.get("popup.info.p2pStatusIndicator.red", model.getP2pConnectionSummary())).show();
|
||||
} else if (p2PNetworkStatusIcon.getId().equalsIgnoreCase("image-yellow_circle")) {
|
||||
new Popup().information(Res.get("popup.info.p2pStatusIndicator.yellow", model.getP2pConnectionSummary())).show();
|
||||
} else {
|
||||
new Popup().information(Res.get("popup.info.p2pStatusIndicator.green", model.getP2pConnectionSummary())).show();
|
||||
}
|
||||
});
|
||||
|
||||
model.getUpdatedDataReceived().addListener((observable, oldValue, newValue) -> {
|
||||
p2PNetworkIcon.setOpacity(1);
|
||||
@ -785,10 +832,10 @@ public class MainView extends InitializableView<StackPane, MainViewModel>
|
||||
VBox vBox = new VBox();
|
||||
vBox.setAlignment(Pos.CENTER_RIGHT);
|
||||
vBox.getChildren().addAll(p2PNetworkLabel, p2pNetworkProgressBar);
|
||||
setRightAnchor(vBox, 33d);
|
||||
setRightAnchor(vBox, 53d);
|
||||
setBottomAnchor(vBox, 5d);
|
||||
|
||||
return new AnchorPane(separator, btcInfoLabel, versionBox, vBox, p2PNetworkIcon) {{
|
||||
return new AnchorPane(separator, btcInfoLabel, versionBox, vBox, p2PNetworkStatusIcon, p2PNetworkIcon) {{
|
||||
setId("footer-pane");
|
||||
setMinHeight(30);
|
||||
setMaxHeight(30);
|
||||
|
@ -475,15 +475,6 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
|
||||
.show();
|
||||
}
|
||||
});
|
||||
bisqSetup.setFirewallIssueHandler(() -> {
|
||||
String key = "firewallSetupInfo";
|
||||
if (preferences.showAgain(key)) {
|
||||
new Popup().information(Res.get("popup.info.firewallSetupInfo"))
|
||||
.closeButtonText(Res.get("shared.iUnderstand"))
|
||||
.dontShowAgainId(key)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
|
||||
bisqSetup.setDownGradePreventionHandler(lastVersion -> {
|
||||
new Popup().warning(Res.get("popup.warn.downGradePrevention", lastVersion, Version.VERSION))
|
||||
@ -829,6 +820,10 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
|
||||
return bisqSetup.getP2PNetworkIconId();
|
||||
}
|
||||
|
||||
StringProperty getP2PNetworkStatusIconId() {
|
||||
return bisqSetup.getP2PNetworkStatusIconId();
|
||||
}
|
||||
|
||||
BooleanProperty getUpdatedDataReceived() {
|
||||
return bisqSetup.getUpdatedDataReceived();
|
||||
}
|
||||
@ -896,4 +891,10 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
|
||||
overlay.show();
|
||||
}
|
||||
}
|
||||
|
||||
public String getP2pConnectionSummary() {
|
||||
return Res.get("mainView.status.connections",
|
||||
p2PService.getNetworkNode().getInboundConnectionCount(),
|
||||
p2PService.getNetworkNode().getOutboundConnectionCount());
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ public class TorNetworkSettingsWindow extends Overlay<TorNetworkSettingsWindow>
|
||||
headLine = Res.get("torNetworkSettingWindow.header");
|
||||
|
||||
width = 1068;
|
||||
|
||||
rowIndex = 0;
|
||||
createGridPane();
|
||||
gridPane.getColumnConstraints().get(0).setHalignment(HPos.LEFT);
|
||||
|
||||
|
BIN
desktop/src/main/resources/images/yellow_circle.png
Normal file
BIN
desktop/src/main/resources/images/yellow_circle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 510 B |
@ -45,6 +45,7 @@ import java.net.Socket;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@ -513,4 +514,22 @@ public abstract class NetworkNode implements MessageListener {
|
||||
.map(Connection::getCapabilities)
|
||||
.findAny();
|
||||
}
|
||||
|
||||
public long upTime() {
|
||||
// how long Bisq has been running with at least one connection
|
||||
// uptime is relative to last all connections lost event
|
||||
long earliestConnection = new Date().getTime();
|
||||
for (Connection connection : outBoundConnections) {
|
||||
earliestConnection = Math.min(earliestConnection, connection.getStatistic().getCreationDate().getTime());
|
||||
}
|
||||
return new Date().getTime() - earliestConnection;
|
||||
}
|
||||
|
||||
public int getInboundConnectionCount() {
|
||||
return inBoundConnections.size();
|
||||
}
|
||||
|
||||
public int getOutboundConnectionCount() {
|
||||
return outBoundConnections.size();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user