Merging and adapting old bridges UI code to new bisq structure

Had to merge by hand because of the many changes in the new bisq
structure. Also removed the 'default bridges' concept because it's
discouraged to do that.
Open issues: UI screen is still WIP, and bitcoinj SOCKS proxy error
which makes that we can't connect to bitcoin nodes anymore.

Signed-off-by: Mike Rosseel <mike@eon-consult.be>
This commit is contained in:
Mike Rosseel 2017-11-02 15:50:06 +01:00
parent af39d00e6b
commit 0ec4b5edac
No known key found for this signature in database
GPG key ID: 1D9920AF2A8E6BF0
19 changed files with 688 additions and 491 deletions

View file

@ -1257,6 +1257,7 @@ message PreferencesPayload {
bool use_animations = 31; bool use_animations = 31;
PaymentAccount selectedPayment_account_for_createOffer = 32; PaymentAccount selectedPayment_account_for_createOffer = 32;
bool pay_fee_in_Btc = 33; bool pay_fee_in_Btc = 33;
repeated string bridge_addresses = 34;
} }

View file

@ -167,6 +167,11 @@ public class AppSetupWithP2P extends AppSetup {
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
log.error(throwable.toString()); log.error(throwable.toString());
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
return p2pNetworkInitialized; return p2pNetworkInitialized;

View file

@ -112,6 +112,11 @@ public class TradeStatisticsMigrationTool {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
} }
} }

View file

@ -10,6 +10,7 @@ import io.bisq.core.btc.BaseCurrencyNetwork;
import io.bisq.core.btc.BtcOptionKeys; import io.bisq.core.btc.BtcOptionKeys;
import io.bisq.core.btc.Restrictions; import io.bisq.core.btc.Restrictions;
import io.bisq.core.payment.PaymentAccount; import io.bisq.core.payment.PaymentAccount;
import io.bisq.network.BridgeProvider;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.beans.property.LongProperty; import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
@ -175,6 +176,7 @@ public final class Preferences implements PersistedDataHost {
setPreferredTradeCurrency(preferredTradeCurrency); setPreferredTradeCurrency(preferredTradeCurrency);
setFiatCurrencies(prefPayload.getFiatCurrencies()); setFiatCurrencies(prefPayload.getFiatCurrencies());
setCryptoCurrencies(prefPayload.getCryptoCurrencies()); setCryptoCurrencies(prefPayload.getCryptoCurrencies());
setBridgeAddresses(prefPayload.getBridgeAddresses());
} else { } else {
prefPayload = new PreferencesPayload(); prefPayload = new PreferencesPayload();
@ -474,6 +476,13 @@ public final class Preferences implements PersistedDataHost {
storage.queueUpForSave(prefPayload, 1); storage.queueUpForSave(prefPayload, 1);
} }
public void setBridgeAddresses(List<String> bridgeAddresses) {
prefPayload.setBridgeAddresses(bridgeAddresses);
BridgeProvider.setBridges(bridgeAddresses);
// We call that before shutdown so we dont want a delay here
storage.queueUpForSave(prefPayload, 1);
}
// Only used from PB but keep it explicit as maybe it get used from the client and then we want to persist // Only used from PB but keep it explicit as maybe it get used from the client and then we want to persist
public void setPeerTagMap(Map<String, String> peerTagMap) { public void setPeerTagMap(Map<String, String> peerTagMap) {
prefPayload.setPeerTagMap(peerTagMap); prefPayload.setPeerTagMap(peerTagMap);
@ -650,5 +659,7 @@ public final class Preferences implements PersistedDataHost {
void setDontShowAgainMap(Map<String, Boolean> dontShowAgainMap); void setDontShowAgainMap(Map<String, Boolean> dontShowAgainMap);
void setPeerTagMap(Map<String, String> peerTagMap); void setPeerTagMap(Map<String, String> peerTagMap);
void setBridgeAddresses(List<String> bridgeAddresses);
} }
} }

View file

@ -12,6 +12,7 @@ import io.bisq.core.btc.Restrictions;
import io.bisq.core.payment.PaymentAccount; import io.bisq.core.payment.PaymentAccount;
import io.bisq.core.proto.CoreProtoResolver; import io.bisq.core.proto.CoreProtoResolver;
import io.bisq.generated.protobuffer.PB; import io.bisq.generated.protobuffer.PB;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -21,6 +22,7 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
@Data @Data
@AllArgsConstructor
public final class PreferencesPayload implements PersistableEnvelope { public final class PreferencesPayload implements PersistableEnvelope {
private String userLanguage; private String userLanguage;
private Country userCountry; private Country userCountry;
@ -64,6 +66,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
@Nullable @Nullable
private PaymentAccount selectedPaymentAccountForCreateOffer; private PaymentAccount selectedPaymentAccountForCreateOffer;
private boolean payFeeInBtc = true; private boolean payFeeInBtc = true;
@Nullable
private List<String> bridgeAddresses = new ArrayList<>();
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -78,74 +82,6 @@ public final class PreferencesPayload implements PersistableEnvelope {
// PROTO BUFFER // PROTO BUFFER
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private PreferencesPayload(String userLanguage,
Country userCountry,
List<FiatCurrency> fiatCurrencies,
List<CryptoCurrency> cryptoCurrencies,
BlockChainExplorer blockChainExplorerMainNet,
BlockChainExplorer blockChainExplorerTestNet,
BlockChainExplorer bsqBlockChainExplorer,
@Nullable String backupDirectory,
boolean autoSelectArbitrators,
Map<String, Boolean> dontShowAgainMap,
boolean tacAccepted,
boolean useTorForBitcoinJ,
boolean showOwnOffersInOfferBook,
@Nullable TradeCurrency preferredTradeCurrency,
long withdrawalTxFeeInBytes,
boolean useCustomWithdrawalTxFee,
double maxPriceDistanceInPercent,
@Nullable String offerBookChartScreenCurrencyCode,
@Nullable String tradeChartsScreenCurrencyCode,
@Nullable String buyScreenCurrencyCode,
@Nullable String sellScreenCurrencyCode,
int tradeStatisticsTickUnitIndex,
boolean resyncSpvRequested,
boolean sortMarketCurrenciesNumerically,
boolean usePercentageBasedPrice,
Map<String, String> peerTagMap,
String bitcoinNodes,
List<String> ignoreTradersList,
String directoryChooserPath,
long buyerSecurityDepositAsLong,
boolean useAnimations,
@Nullable PaymentAccount selectedPaymentAccountForCreateOffer,
boolean payFeeInBtc) {
this.userLanguage = userLanguage;
this.userCountry = userCountry;
this.fiatCurrencies = fiatCurrencies;
this.cryptoCurrencies = cryptoCurrencies;
this.blockChainExplorerMainNet = blockChainExplorerMainNet;
this.blockChainExplorerTestNet = blockChainExplorerTestNet;
this.bsqBlockChainExplorer = bsqBlockChainExplorer;
this.backupDirectory = backupDirectory;
this.autoSelectArbitrators = autoSelectArbitrators;
this.dontShowAgainMap = dontShowAgainMap;
this.tacAccepted = tacAccepted;
this.useTorForBitcoinJ = useTorForBitcoinJ;
this.showOwnOffersInOfferBook = showOwnOffersInOfferBook;
this.preferredTradeCurrency = preferredTradeCurrency;
this.withdrawalTxFeeInBytes = withdrawalTxFeeInBytes;
this.useCustomWithdrawalTxFee = useCustomWithdrawalTxFee;
this.maxPriceDistanceInPercent = maxPriceDistanceInPercent;
this.offerBookChartScreenCurrencyCode = offerBookChartScreenCurrencyCode;
this.tradeChartsScreenCurrencyCode = tradeChartsScreenCurrencyCode;
this.buyScreenCurrencyCode = buyScreenCurrencyCode;
this.sellScreenCurrencyCode = sellScreenCurrencyCode;
this.tradeStatisticsTickUnitIndex = tradeStatisticsTickUnitIndex;
this.resyncSpvRequested = resyncSpvRequested;
this.sortMarketCurrenciesNumerically = sortMarketCurrenciesNumerically;
this.usePercentageBasedPrice = usePercentageBasedPrice;
this.peerTagMap = peerTagMap;
this.bitcoinNodes = bitcoinNodes;
this.ignoreTradersList = ignoreTradersList;
this.directoryChooserPath = directoryChooserPath;
this.buyerSecurityDepositAsLong = buyerSecurityDepositAsLong;
this.useAnimations = useAnimations;
this.selectedPaymentAccountForCreateOffer = selectedPaymentAccountForCreateOffer;
this.payFeeInBtc = payFeeInBtc;
}
@Override @Override
public Message toProtoMessage() { public Message toProtoMessage() {
PB.PreferencesPayload.Builder builder = PB.PreferencesPayload.newBuilder() PB.PreferencesPayload.Builder builder = PB.PreferencesPayload.newBuilder()
@ -178,8 +114,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
.setDirectoryChooserPath(directoryChooserPath) .setDirectoryChooserPath(directoryChooserPath)
.setBuyerSecurityDepositAsLong(buyerSecurityDepositAsLong) .setBuyerSecurityDepositAsLong(buyerSecurityDepositAsLong)
.setUseAnimations(useAnimations) .setUseAnimations(useAnimations)
.setPayFeeInBtc(payFeeInBtc); .setPayFeeInBtc(payFeeInBtc)
.addAllBridgeAddresses(bridgeAddresses);
Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory); Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory);
Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((PB.TradeCurrency) e.toProtoMessage())); Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((PB.TradeCurrency) e.toProtoMessage()));
Optional.ofNullable(offerBookChartScreenCurrencyCode).ifPresent(builder::setOfferBookChartScreenCurrencyCode); Optional.ofNullable(offerBookChartScreenCurrencyCode).ifPresent(builder::setOfferBookChartScreenCurrencyCode);
@ -188,7 +124,6 @@ public final class PreferencesPayload implements PersistableEnvelope {
Optional.ofNullable(sellScreenCurrencyCode).ifPresent(builder::setSellScreenCurrencyCode); Optional.ofNullable(sellScreenCurrencyCode).ifPresent(builder::setSellScreenCurrencyCode);
Optional.ofNullable(selectedPaymentAccountForCreateOffer).ifPresent( Optional.ofNullable(selectedPaymentAccountForCreateOffer).ifPresent(
account -> builder.setSelectedPaymentAccountForCreateOffer(selectedPaymentAccountForCreateOffer.toProtoMessage())); account -> builder.setSelectedPaymentAccountForCreateOffer(selectedPaymentAccountForCreateOffer.toProtoMessage()));
return PB.PersistableEnvelope.newBuilder().setPreferencesPayload(builder).build(); return PB.PersistableEnvelope.newBuilder().setPreferencesPayload(builder).build();
} }
@ -237,6 +172,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
proto.getBuyerSecurityDepositAsLong(), proto.getBuyerSecurityDepositAsLong(),
proto.getUseAnimations(), proto.getUseAnimations(),
paymentAccount, paymentAccount,
proto.getPayFeeInBtc()); proto.getPayFeeInBtc(),
proto.getBridgeAddressesList());
} }
} }

View file

@ -72,12 +72,14 @@ import io.bisq.gui.components.BalanceWithConfirmationTextField;
import io.bisq.gui.components.TxIdTextField; import io.bisq.gui.components.TxIdTextField;
import io.bisq.gui.main.overlays.notifications.NotificationCenter; import io.bisq.gui.main.overlays.notifications.NotificationCenter;
import io.bisq.gui.main.overlays.popups.Popup; import io.bisq.gui.main.overlays.popups.Popup;
import io.bisq.gui.main.overlays.windows.AddBridgeEntriesWindow;
import io.bisq.gui.main.overlays.windows.DisplayAlertMessageWindow; import io.bisq.gui.main.overlays.windows.DisplayAlertMessageWindow;
import io.bisq.gui.main.overlays.windows.TacWindow; import io.bisq.gui.main.overlays.windows.TacWindow;
import io.bisq.gui.main.overlays.windows.WalletPasswordWindow; import io.bisq.gui.main.overlays.windows.WalletPasswordWindow;
import io.bisq.gui.main.overlays.windows.downloadupdate.DisplayUpdateDownloadWindow; import io.bisq.gui.main.overlays.windows.downloadupdate.DisplayUpdateDownloadWindow;
import io.bisq.gui.util.BSFormatter; import io.bisq.gui.util.BSFormatter;
import io.bisq.gui.util.GUIUtil; import io.bisq.gui.util.GUIUtil;
import io.bisq.gui.util.Transitions;
import io.bisq.network.crypto.DecryptedDataTuple; import io.bisq.network.crypto.DecryptedDataTuple;
import io.bisq.network.crypto.EncryptionService; import io.bisq.network.crypto.EncryptionService;
import io.bisq.network.p2p.BootstrapListener; import io.bisq.network.p2p.BootstrapListener;
@ -485,6 +487,13 @@ public class MainViewModel implements ViewModel {
bootstrapWarning.set(Res.get("mainView.bootstrapWarning.bootstrappingToP2PFailed")); bootstrapWarning.set(Res.get("mainView.bootstrapWarning.bootstrappingToP2PFailed"));
p2pNetworkLabelId.set("splash-error-state-msg"); p2pNetworkLabelId.set("splash-error-state-msg");
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
AddBridgeEntriesWindow addBridgeEntriesWindow = new AddBridgeEntriesWindow(preferences)
.onAction(resultHandler::run);
UserThread.execute(addBridgeEntriesWindow::show);
}
}); });
return p2pNetworkInitialized; return p2pNetworkInitialized;

View file

@ -0,0 +1,173 @@
/*
* This file is part of bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
/** This file is part of bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.gui.main.overlays.windows;
import io.bisq.common.util.Tuple2;
import io.bisq.common.util.Utilities;
import io.bisq.core.alert.Alert;
import io.bisq.core.user.Preferences;
import io.bisq.gui.main.overlays.Overlay;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import lombok.extern.slf4j.Slf4j;
import javax.inject.Inject;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import static io.bisq.gui.util.FormBuilder.addLabel;
import static io.bisq.gui.util.FormBuilder.addLabelTextArea;
@Slf4j
public class AddBridgeEntriesWindow extends Overlay<AddBridgeEntriesWindow> {
private TextArea bridgeEntriesTextArea;
private final Preferences preferences;
@Inject
public AddBridgeEntriesWindow(Preferences preferences) {
this.preferences = preferences;
type = Type.Attention;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public API
///////////////////////////////////////////////////////////////////////////////////////////
public void show() {
if (headLine == null)
headLine = "Add Tor bridge entries";
width = 900;
createGridPane();
addHeadLine();
addSeparator();
addContent();
addCloseButton();
applyStyles();
display();
}
protected void addCloseButton() {
closeButton = new Button(closeButtonText == null ? "Close" : closeButtonText);
closeButton.setOnAction(event -> doClose());
if (actionHandlerOptional.isPresent() || actionButtonText != null) {
actionButton = new Button("Save and retry");
actionButton.setDefaultButton(true);
//TODO app wide focus
//actionButton.requestFocus();
actionButton.setOnAction(event -> save());
Button urlButton = new Button("Open Tor project web page");
urlButton.setOnAction(event -> {
try {
Utilities.openURI(URI.create("https://bridges.torproject.org/bridges"));
} catch (IOException e) {
e.printStackTrace();
}
});
Pane spacer = new Pane();
HBox hBox = new HBox();
hBox.setSpacing(10);
hBox.getChildren().addAll(spacer, closeButton, urlButton, actionButton);
HBox.setHgrow(spacer, Priority.ALWAYS);
GridPane.setHalignment(hBox, HPos.RIGHT);
GridPane.setRowIndex(hBox, rowIndex);
GridPane.setColumnSpan(hBox, 2);
GridPane.setMargin(hBox, new Insets(buttonDistance, 0, 0, 0));
gridPane.getChildren().add(hBox);
} else if (!hideCloseButton) {
closeButton.setDefaultButton(true);
GridPane.setHalignment(closeButton, HPos.RIGHT);
GridPane.setMargin(closeButton, new Insets(buttonDistance, 0, 0, 0));
GridPane.setRowIndex(closeButton, rowIndex);
GridPane.setColumnIndex(closeButton, 1);
gridPane.getChildren().add(closeButton);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Protected
///////////////////////////////////////////////////////////////////////////////////////////
@Override
protected void setupKeyHandler(Scene scene) {
if (!hideCloseButton) {
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE) {
e.consume();
doClose();
} else if (e.getCode() == KeyCode.ENTER) {
e.consume();
save();
}
});
}
}
private void addContent() {
Label label = addLabel(gridPane, ++rowIndex, "We could not connect to the Tor network.\n" +
"If Tor is blocked at your internet provider, you can try to add Tor bridge address entries from the Tor project:\n" +
"https://bridges.torproject.org/bridges\n\n" +
"Add one address entry in each line.\n");
GridPane.setColumnIndex(label, 0);
GridPane.setColumnSpan(label, 2);
GridPane.setHalignment(label, HPos.LEFT);
Tuple2<Label, TextArea> labelTextAreaTuple2 = addLabelTextArea(gridPane, rowIndex, "Bridge entries:", "");
bridgeEntriesTextArea = labelTextAreaTuple2.second;
}
private void save() {
if (!bridgeEntriesTextArea.getText().isEmpty()) {
List<String> list = Arrays.asList(bridgeEntriesTextArea.getText().split("\\n"));
preferences.setBridgeAddresses(list);
actionHandlerOptional.ifPresent(Runnable::run);
hide();
}
}
}

View file

@ -9,11 +9,11 @@
<root level="TRACE"> <root level="TRACE">
<appender-ref ref="CONSOLE_APPENDER"/> <appender-ref ref="CONSOLE_APPENDER"/>
</root> </root>
<!--
<logger name="com.neemre.btcdcli4j" level="WARN"/> <logger name="com.neemre.btcdcli4j" level="WARN"/>
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="INFO"/> <logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="INFO"/>
<logger name="org.bitcoinj.core.AbstractBlockChain" level="WARN"/> <logger name="org.bitcoinj.core.AbstractBlockChain" level="WARN"/>
<logger name="org.bitcoinj.net.BlockingClient" level="WARN"/> <logger name="org.bitcoinj.net.BlockingClient" level="WARN"/>
<logger name="org.bitcoinj.core.PeerGroup" level="WARN"/> <logger name="org.bitcoinj.core.PeerGroup" level="WARN"/>
@ -23,10 +23,11 @@
<logger name="org.bitcoinj.core.listeners.DownloadProgressTracker" level="WARN"/> <logger name="org.bitcoinj.core.listeners.DownloadProgressTracker" level="WARN"/>
<logger name="org.bitcoinj.core.PeerSocketHandler" level="WARN"/> <logger name="org.bitcoinj.core.PeerSocketHandler" level="WARN"/>
<logger name="org.bitcoinj.net.NioClientManager" level="WARN"/> <logger name="org.bitcoinj.net.NioClientManager" level="WARN"/>
-->
<!-- We get too many errors logged from connection issues--> <!-- We get too many errors logged from connection issues
<logger name="org.bitcoinj.net.BlockingClient" level="OFF"/> <logger name="org.bitcoinj.net.BlockingClient" level="OFF"/>
-->
<!-- <!--
<logger name="org.bitcoinj.net.ConnectionHandler" level="WARN"/> <logger name="org.bitcoinj.net.ConnectionHandler" level="WARN"/>

View file

@ -0,0 +1,23 @@
package io.bisq.network;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* This class exists because the TorNetworkNode in module 'network' can't access the preferences
* in 'core' directly, so we use this provider.
*/
@Slf4j
public class BridgeProvider {
@Setter
@Getter
static List<String> bridges = new ArrayList<>();
}

View file

@ -28,4 +28,8 @@ public abstract class BootstrapListener implements P2PServiceListener {
@Override @Override
abstract public void onBootstrapComplete(); abstract public void onBootstrapComplete();
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
} }

View file

@ -272,6 +272,12 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
p2pServiceListeners.stream().forEach(e -> e.onSetupFailed(throwable)); p2pServiceListeners.stream().forEach(e -> e.onSetupFailed(throwable));
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
Log.traceCall();
p2pServiceListeners.stream().forEach(e -> e.onRequestCustomBridges(resultHandler));
}
// Called from networkReadyBinding // Called from networkReadyBinding
private void onNetworkReady() { private void onNetworkReady() {
Log.traceCall(); Log.traceCall();

View file

@ -7,4 +7,6 @@ public interface SetupListener {
@SuppressWarnings("unused") @SuppressWarnings("unused")
void onSetupFailed(Throwable throwable); void onSetupFailed(Throwable throwable);
void onRequestCustomBridges(Runnable resultHandler);
} }

View file

@ -11,6 +11,7 @@ import io.bisq.common.app.Log;
import io.bisq.common.proto.network.NetworkProtoResolver; import io.bisq.common.proto.network.NetworkProtoResolver;
import io.bisq.common.storage.FileUtil; import io.bisq.common.storage.FileUtil;
import io.bisq.common.util.Utilities; import io.bisq.common.util.Utilities;
import io.bisq.network.BridgeProvider;
import io.bisq.network.p2p.NodeAddress; import io.bisq.network.p2p.NodeAddress;
import io.bisq.network.p2p.Utils; import io.bisq.network.p2p.Utils;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
@ -31,7 +32,7 @@ import java.nio.file.Paths;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
@ -41,6 +42,7 @@ public class TorNetworkNode extends NetworkNode {
private static final int MAX_RESTART_ATTEMPTS = 5; private static final int MAX_RESTART_ATTEMPTS = 5;
private static final long SHUT_DOWN_TIMEOUT_SEC = 5; private static final long SHUT_DOWN_TIMEOUT_SEC = 5;
private static final int WAIT_BEFORE_RESTART = 2000;
private HiddenServiceSocket hiddenServiceSocket; private HiddenServiceSocket hiddenServiceSocket;
private final File torDir; private final File torDir;
@ -48,8 +50,6 @@ public class TorNetworkNode extends NetworkNode {
private int restartCounter; private int restartCounter;
@SuppressWarnings("FieldCanBeLocal") @SuppressWarnings("FieldCanBeLocal")
private MonadicBinding<Boolean> allShutDown; private MonadicBinding<Boolean> allShutDown;
@Setter
private List<String> bridgeLines = null;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -76,8 +76,7 @@ public class TorNetworkNode extends NetworkNode {
createExecutorService(); createExecutorService();
// Create the tor node (takes about 6 sec.) // Create the tor node (takes about 6 sec.)
//createTorNode(torDir, (nativeTor) -> createHiddenService(Utils.findFreeSystemPort(), servicePort, bridgeLines)); createTorAndHiddenService(torDir, Utils.findFreeSystemPort(), servicePort, BridgeProvider.getBridges());
createTorAndHiddenService(torDir, Utils.findFreeSystemPort(), servicePort, bridgeLines);
} }
@Override @Override
@ -170,8 +169,25 @@ public class TorNetworkNode extends NetworkNode {
private void restartTor(String errorMessage) { private void restartTor(String errorMessage) {
Log.traceCall(); Log.traceCall();
log.warn("Restarting Tor");
restartCounter++; restartCounter++;
if (restartCounter > MAX_RESTART_ATTEMPTS) { if (restartCounter <= MAX_RESTART_ATTEMPTS) {
// If we failed we try with custom bridges
if (restartCounter == 1) {
setupListeners.stream().forEach(e -> e.onRequestCustomBridges(() -> {
log.warn("Tor restart after custom bridges.");
start(null);
}));
log.warn("We stop tor as starting tor with the default bridges failed. We request user to add custom bridges.");
shutDown(null);
} else {
shutDown(() -> UserThread.runAfter(() -> {
log.warn("We restart tor using custom bridges.");
log.warn("Bridges: " + BridgeProvider.getBridges());
start(null);
}, WAIT_BEFORE_RESTART, TimeUnit.MILLISECONDS));
}
} else {
String msg = "We tried to restart Tor " + restartCounter + String msg = "We tried to restart Tor " + restartCounter +
" times, but it continued to fail with error message:\n" + " times, but it continued to fail with error message:\n" +
errorMessage + "\n\n" + errorMessage + "\n\n" +
@ -186,99 +202,21 @@ public class TorNetworkNode extends NetworkNode {
// create tor // create tor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void createTorNode(final File torDir, final Consumer<NativeTor> resultHandler) {
Log.traceCall();
ListenableFuture<NativeTor> future = executorService.submit(() -> {
Utilities.setThreadName("TorNetworkNode:CreateTorNode");
long ts = System.currentTimeMillis();
if (torDir.mkdirs())
log.trace("Created directory for tor at {}", torDir.getAbsolutePath());
NativeTor nativeTor = null;
try {
nativeTor = new NativeTor(torDir, bridgeLines);
} catch (TorCtlException e) {
throw new Exception(e);
}
log.debug("\n\n############################################################\n" +
"TorNode created:" +
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
+ "\n############################################################\n");
return nativeTor;
});
Futures.addCallback(future, new FutureCallback<NativeTor>() {
public void onSuccess(NativeTor torNode) {
Tor.setDefault(torNode);
UserThread.execute(() -> resultHandler.accept(torNode));
}
public void onFailure(@NotNull Throwable throwable) {
UserThread.execute(() -> {
log.error("TorNode creation failed with exception: " + throwable.getMessage());
restartTor(throwable.getMessage());
});
}
});
}
private void createHiddenService(int localPort, int servicePort, List<String> bridgeLines) {
Log.traceCall();
ListenableFuture<Object> future = executorService.submit(() -> {
Utilities.setThreadName("TorNetworkNode:CreateHiddenService");
{
long ts = System.currentTimeMillis();
// TODO backup has to be taken from ./hiddenservice, not ./
hiddenServiceSocket = new HiddenServiceSocket(localPort, "hiddenservice", servicePort);
hiddenServiceSocket.addReadyListener(socket -> {
Socket con;
try {
log.info("Hidden Service " + socket + " is ready");
new Thread() {
@Override
public void run() {
try {
Log.traceCall("hiddenService created");
nodeAddressProperty.set(new NodeAddress(hiddenServiceSocket.getServiceName() + ":" + hiddenServiceSocket.getHiddenServicePort()));
startServer(socket);
UserThread.execute(() -> setupListeners.stream().forEach(SetupListener::onHiddenServicePublished));
} catch (final Exception e1) {
e1.printStackTrace();
}
}
}.start();
} catch (final Exception e) {
e.printStackTrace();
}
return null;
});
log.info("It will take some time for the HS to be reachable (~40 seconds). You will be notified about this");
return null;
}
});
Futures.addCallback(future, new FutureCallback<Object>() {
public void onSuccess(Object hiddenServiceDescriptor) {
log.debug("HiddenServiceDescriptor created. Wait for publishing.");
}
public void onFailure(@NotNull Throwable throwable) {
UserThread.execute(() -> {
log.error("Hidden service creation failed");
restartTor(throwable.getMessage());
});
}
});
}
private void createTorAndHiddenService(final File torDir, int localPort, int servicePort, List<String> bridgeLines) { private void createTorAndHiddenService(final File torDir, int localPort, int servicePort, List<String> bridgeLines) {
Log.traceCall(); Log.traceCall();
log.debug("Using bridges: {}", bridgeLines.stream().collect(Collectors.joining(",")));
if(restartCounter == 0) {
log.error("Doing fake restart to get to the bridges");
restartTor("error message here...");
return;
}
ListenableFuture<Object> future = (ListenableFuture<Object>) executorService.submit(() -> { ListenableFuture<Object> future = (ListenableFuture<Object>) executorService.submit(() -> {
try { try {
Tor.setDefault(new NativeTor(torDir, bridgeLines)); Tor.setDefault(new NativeTor(torDir, bridgeLines));
} catch (TorCtlException e) { } catch (TorCtlException e) {
log.error("Tor node creation failed", e); log.error("Tor node creation failed", e);
restartTor(e.getMessage()); restartTor(e.getMessage());
return;
} }
UserThread.execute(() -> setupListeners.stream().forEach(SetupListener::onTorNodeReady)); UserThread.execute(() -> setupListeners.stream().forEach(SetupListener::onTorNodeReady));

View file

@ -117,6 +117,10 @@ public class PeerServiceTest {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
} }
Thread.sleep(30_000); Thread.sleep(30_000);
@ -208,6 +212,11 @@ public class PeerServiceTest {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
P2PService p2PService1 = seedNode1.getSeedNodeP2PService(); P2PService p2PService1 = seedNode1.getSeedNodeP2PService();
@ -245,6 +254,11 @@ public class PeerServiceTest {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
P2PService p2PService2 = seedNode2.getSeedNodeP2PService(); P2PService p2PService2 = seedNode2.getSeedNodeP2PService();
latch.await(); latch.await();
@ -480,6 +494,10 @@ public class PeerServiceTest {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
latch.await(); latch.await();
Thread.sleep(sleepTime); Thread.sleep(sleepTime);

View file

@ -80,6 +80,11 @@ public class TestUtils {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
latch.await(); latch.await();
Thread.sleep(sleepTime); Thread.sleep(sleepTime);

View file

@ -52,6 +52,11 @@ public class LocalhostNetworkNodeTest {
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
log.debug("onSetupFailed"); log.debug("onSetupFailed");
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
LocalhostNetworkNode node2 = new LocalhostNetworkNode(9002, TestUtils.getNetworkProtoResolver()); LocalhostNetworkNode node2 = new LocalhostNetworkNode(9002, TestUtils.getNetworkProtoResolver());
@ -75,6 +80,11 @@ public class LocalhostNetworkNodeTest {
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
log.debug("onSetupFailed 2"); log.debug("onSetupFailed 2");
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
startupLatch.await(); startupLatch.await();

View file

@ -416,6 +416,11 @@ public class NetworkStressTest {
localServicesFailed.set(true); localServicesFailed.set(true);
localServicesLatch.countDown(); localServicesLatch.countDown();
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
} }
private class SeedServiceListener extends TestSetupListener implements P2PServiceListener { private class SeedServiceListener extends TestSetupListener implements P2PServiceListener {
@ -802,6 +807,11 @@ public class NetworkStressTest {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
} }
} }

View file

@ -53,6 +53,11 @@ public class TorNetworkNodeTest {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
latch.await(); latch.await();
@ -75,6 +80,11 @@ public class TorNetworkNodeTest {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
latch.await(); latch.await();
@ -127,6 +137,11 @@ public class TorNetworkNodeTest {
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
int port2 = 9002; int port2 = 9002;
@ -146,6 +161,11 @@ public class TorNetworkNodeTest {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
latch.await(); latch.await();

View file

@ -117,6 +117,11 @@ public class PeerManagerTest {
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
P2PService p2PService1 = seedNode1.getSeedNodeP2PService(); P2PService p2PService1 = seedNode1.getSeedNodeP2PService();
latch.await(); latch.await();
@ -168,6 +173,11 @@ public class PeerManagerTest {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
P2PService p2PService1 = seedNode1.getSeedNodeP2PService(); P2PService p2PService1 = seedNode1.getSeedNodeP2PService();
@ -205,6 +215,11 @@ public class PeerManagerTest {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
P2PService p2PService2 = seedNode2.getSeedNodeP2PService(); P2PService p2PService2 = seedNode2.getSeedNodeP2PService();
latch.await(); latch.await();
@ -439,6 +454,11 @@ public class PeerManagerTest {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
} }
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
}); });
latch.await(); latch.await();
Thread.sleep(sleepTime); Thread.sleep(sleepTime);