support password prompt in legacy ui

Co-authored-by: niyid <neeyeed@gmail.com>
This commit is contained in:
woodser 2023-02-26 09:08:10 -05:00
parent 99653fe40d
commit 4dde53f0e8
19 changed files with 357 additions and 272 deletions

View File

@ -116,6 +116,7 @@ public class CoreAccountService {
public void changePassword(String oldPassword, String newPassword) { public void changePassword(String oldPassword, String newPassword) {
if (!isAccountOpen()) throw new IllegalStateException("Cannot change password on unopened account"); if (!isAccountOpen()) throw new IllegalStateException("Cannot change password on unopened account");
if ("".equals(oldPassword)) oldPassword = null; // normalize to null
if (!StringUtils.equals(this.password, oldPassword)) throw new IllegalStateException("Incorrect password"); if (!StringUtils.equals(this.password, oldPassword)) throw new IllegalStateException("Incorrect password");
if (newPassword != null && newPassword.length() < 8) throw new IllegalStateException("Password must be at least 8 characters"); if (newPassword != null && newPassword.length() < 8) throw new IllegalStateException("Password must be at least 8 characters");
keyStorage.saveKeyRing(keyRing, oldPassword, newPassword); keyStorage.saveKeyRing(keyRing, oldPassword, newPassword);
@ -125,6 +126,12 @@ public class CoreAccountService {
} }
} }
public void verifyPassword(String password) throws IncorrectPasswordException {
if (!StringUtils.equals(this.password, password)) {
throw new IncorrectPasswordException("Incorrect password");
}
}
public void closeAccount() { public void closeAccount() {
if (!isAccountOpen()) throw new IllegalStateException("Cannot close unopened account"); if (!isAccountOpen()) throw new IllegalStateException("Cannot close unopened account");
keyRing.lockKeys(); // closed account means the keys are locked keyRing.lockKeys(); // closed account means the keys are locked

View File

@ -31,13 +31,14 @@ import bisq.core.trade.HavenoUtils;
import bisq.core.trade.TradeManager; import bisq.core.trade.TradeManager;
import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.trade.txproof.xmr.XmrTxProofService; import bisq.core.trade.txproof.xmr.XmrTxProofService;
import bisq.network.p2p.P2PService; import bisq.network.p2p.P2PService;
import bisq.common.UserThread; import bisq.common.UserThread;
import bisq.common.app.AppModule; import bisq.common.app.AppModule;
import bisq.common.config.HavenoHelpFormatter;
import bisq.common.config.Config; import bisq.common.config.Config;
import bisq.common.config.ConfigException; import bisq.common.config.ConfigException;
import bisq.common.config.HavenoHelpFormatter;
import bisq.common.crypto.IncorrectPasswordException; import bisq.common.crypto.IncorrectPasswordException;
import bisq.common.handlers.ResultHandler; import bisq.common.handlers.ResultHandler;
import bisq.common.persistence.PersistenceManager; import bisq.common.persistence.PersistenceManager;
@ -51,8 +52,11 @@ import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import java.io.Console; import java.io.Console;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -135,7 +139,6 @@ public abstract class HavenoExecutable implements GracefulShutDownHandler, Haven
// thread the application is running and we don't run into thread interference. // thread the application is running and we don't run into thread interference.
protected abstract void launchApplication(); protected abstract void launchApplication();
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// If application is a JavaFX application we need wait for onApplicationLaunched // If application is a JavaFX application we need wait for onApplicationLaunched
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -165,12 +168,25 @@ public abstract class HavenoExecutable implements GracefulShutDownHandler, Haven
}); });
// Attempt to login, subclasses should implement interactive login and or rpc login. // Attempt to login, subclasses should implement interactive login and or rpc login.
if (!isReadOnly && loginAccount()) { CompletableFuture<Boolean> loginFuture = loginAccount();
loginFuture.whenComplete((result, throwable) -> {
if (throwable != null) {
log.error("Error logging in to account", throwable);
shutDownNoPersist(null, false);
return;
}
try {
if (!isReadOnly && loginFuture.get()) {
readAllPersisted(this::startApplication); readAllPersisted(this::startApplication);
} else { } else {
log.warn("Running application in readonly mode"); log.warn("Running application in readonly mode");
startApplication(); startApplication();
} }
} catch (InterruptedException | ExecutionException e) {
log.error("An error occurred: {}", e.getMessage());
e.printStackTrace();
}
});
} }
/** /**
@ -199,19 +215,26 @@ public abstract class HavenoExecutable implements GracefulShutDownHandler, Haven
* *
* @return true if account is opened successfully. * @return true if account is opened successfully.
*/ */
protected boolean loginAccount() { protected CompletableFuture<Boolean> loginAccount() {
CompletableFuture<Boolean> result = new CompletableFuture<>();
if (accountService.accountExists()) { if (accountService.accountExists()) {
log.info("Account already exists, attempting to open"); log.info("Account already exists, attempting to open");
try { try {
accountService.openAccount(null); accountService.openAccount(null);
result.complete(accountService.isAccountOpen());
} catch (IncorrectPasswordException ipe) { } catch (IncorrectPasswordException ipe) {
log.info("Account password protected, password required"); log.info("Account password protected, password required");
result.complete(false);
} }
} else if (!config.passwordRequired) { } else if (!config.passwordRequired) {
log.info("Creating Haveno account with null password"); log.info("Creating Haveno account with null password");
accountService.createAccount(null); accountService.createAccount(null);
result.complete(accountService.isAccountOpen());
} else {
log.info("Account does not exist and password is required");
result.complete(false);
} }
return accountService.isAccountOpen(); return result;
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -83,7 +83,6 @@ public class HavenoHeadlessApp implements HeadlessApp {
bisqSetup.setChainFileLockedExceptionHandler(msg -> log.error("onChainFileLockedExceptionHandler: msg={}", msg)); bisqSetup.setChainFileLockedExceptionHandler(msg -> log.error("onChainFileLockedExceptionHandler: msg={}", msg));
bisqSetup.setLockedUpFundsHandler(msg -> log.info("onLockedUpFundsHandler: msg={}", msg)); bisqSetup.setLockedUpFundsHandler(msg -> log.info("onLockedUpFundsHandler: msg={}", msg));
bisqSetup.setShowFirstPopupIfResyncSPVRequestedHandler(() -> log.info("onShowFirstPopupIfResyncSPVRequestedHandler")); bisqSetup.setShowFirstPopupIfResyncSPVRequestedHandler(() -> log.info("onShowFirstPopupIfResyncSPVRequestedHandler"));
bisqSetup.setRequestWalletPasswordHandler(aesKeyHandler -> log.info("onRequestWalletPasswordHandler"));
bisqSetup.setDisplayUpdateHandler((alert, key) -> log.info("onDisplayUpdateHandler")); bisqSetup.setDisplayUpdateHandler((alert, key) -> log.info("onDisplayUpdateHandler"));
bisqSetup.setDisplayAlertHandler(alert -> log.info("onDisplayAlertHandler. alert={}", alert)); bisqSetup.setDisplayAlertHandler(alert -> log.info("onDisplayAlertHandler. alert={}", alert));
bisqSetup.setDisplayPrivateNotificationHandler(privateNotification -> log.info("onDisplayPrivateNotificationHandler. privateNotification={}", privateNotification)); bisqSetup.setDisplayPrivateNotificationHandler(privateNotification -> log.info("onDisplayPrivateNotificationHandler. privateNotification={}", privateNotification));

View File

@ -83,8 +83,6 @@ import javafx.beans.value.ChangeListener;
import javafx.collections.SetChangeListener; import javafx.collections.SetChangeListener;
import org.bouncycastle.crypto.params.KeyParameter;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileWriter; import java.io.FileWriter;
@ -160,9 +158,6 @@ public class HavenoSetup {
private Runnable showFirstPopupIfResyncSPVRequestedHandler; private Runnable showFirstPopupIfResyncSPVRequestedHandler;
@Setter @Setter
@Nullable @Nullable
private Consumer<Consumer<KeyParameter>> requestWalletPasswordHandler;
@Setter
@Nullable
private Consumer<Alert> displayAlertHandler; private Consumer<Alert> displayAlertHandler;
@Setter @Setter
@Nullable @Nullable
@ -264,6 +259,8 @@ public class HavenoSetup {
this.refundManager = refundManager; this.refundManager = refundManager;
this.arbitrationManager = arbitrationManager; this.arbitrationManager = arbitrationManager;
xmrWalletService.setHavenoSetup(this);
MemPoolSpaceTxBroadcaster.init(socks5ProxyProvider, preferences, localBitcoinNode); MemPoolSpaceTxBroadcaster.init(socks5ProxyProvider, preferences, localBitcoinNode);
} }
@ -411,9 +408,10 @@ public class HavenoSetup {
return; return;
} }
log.warn("startupTimeout called"); log.warn("startupTimeout called");
if (walletsManager.areWalletsEncrypted()) //TODO (niyid) This has a part to play in the display of the password prompt
walletInitialized.addListener(walletInitializedListener); // if (walletsManager.areWalletsEncrypted())
else if (displayTorNetworkSettingsHandler != null) // walletInitialized.addListener(walletInitializedListener);
if (displayTorNetworkSettingsHandler != null)
displayTorNetworkSettingsHandler.accept(true); displayTorNetworkSettingsHandler.accept(true);
log.info("Set log level for org.berndpruenster.netlayer classes to DEBUG to show more details for " + log.info("Set log level for org.berndpruenster.netlayer classes to DEBUG to show more details for " +
@ -453,35 +451,11 @@ public class HavenoSetup {
private void initWallet() { private void initWallet() {
log.info("Init wallet"); log.info("Init wallet");
havenoSetupListeners.forEach(HavenoSetupListener::onInitWallet); havenoSetupListeners.forEach(HavenoSetupListener::onInitWallet);
Runnable walletPasswordHandler = () -> {
log.info("Wallet password required");
havenoSetupListeners.forEach(HavenoSetupListener::onRequestWalletPassword);
if (p2pNetworkReady.get())
p2PNetworkSetup.setSplashP2PNetworkAnimationVisible(true);
if (requestWalletPasswordHandler != null) {
requestWalletPasswordHandler.accept(aesKey -> {
walletsManager.setAesKey(aesKey);
walletsSetup.getWalletConfig().maybeAddSegwitKeychain(walletsSetup.getWalletConfig().btcWallet(),
aesKey);
if (getResyncSpvSemaphore()) {
if (showFirstPopupIfResyncSPVRequestedHandler != null)
showFirstPopupIfResyncSPVRequestedHandler.run();
} else {
// TODO no guarantee here that the wallet is really fully initialized
// We would need a new walletInitializedButNotEncrypted state to track
// Usually init is fast and we have our wallet initialized at that state though.
walletInitialized.set(true);
}
});
}
};
walletAppSetup.init(chainFileLockedExceptionHandler, walletAppSetup.init(chainFileLockedExceptionHandler,
spvFileCorruptedHandler, spvFileCorruptedHandler,
getResyncSpvSemaphore(), getResyncSpvSemaphore(),
showFirstPopupIfResyncSPVRequestedHandler, showFirstPopupIfResyncSPVRequestedHandler,
showPopupIfInvalidBtcConfigHandler, showPopupIfInvalidBtcConfigHandler,
walletPasswordHandler,
() -> { () -> {
if (allBasicServicesInitialized) { if (allBasicServicesInitialized) {
checkForLockedUpFunds(); checkForLockedUpFunds();
@ -867,5 +841,7 @@ public class HavenoSetup {
return p2PNetworkSetup.getP2pNetworkLabelId(); return p2PNetworkSetup.getP2pNetworkLabelId();
} }
public BooleanProperty getWalletInitialized() {
return walletInitialized;
}
} }

View File

@ -108,7 +108,6 @@ public class WalletAppSetup {
boolean isSpvResyncRequested, boolean isSpvResyncRequested,
@Nullable Runnable showFirstPopupIfResyncSPVRequestedHandler, @Nullable Runnable showFirstPopupIfResyncSPVRequestedHandler,
@Nullable Runnable showPopupIfInvalidBtcConfigHandler, @Nullable Runnable showPopupIfInvalidBtcConfigHandler,
Runnable walletPasswordHandler,
Runnable downloadCompleteHandler, Runnable downloadCompleteHandler,
Runnable walletInitializedHandler) { Runnable walletInitializedHandler) {
log.info("Initialize WalletAppSetup with BitcoinJ version {} and hash of BitcoinJ commit {}", log.info("Initialize WalletAppSetup with BitcoinJ version {} and hash of BitcoinJ commit {}",
@ -171,17 +170,7 @@ public class WalletAppSetup {
walletsSetup.initialize(null, walletsSetup.initialize(null,
() -> { () -> {
// We only check one wallet as we apply encryption to all or none
if (walletsManager.areWalletsEncrypted() && !coreContext.isApiUser()) {
walletPasswordHandler.run();
} else {
if (isSpvResyncRequested && !coreContext.isApiUser()) {
if (showFirstPopupIfResyncSPVRequestedHandler != null)
showFirstPopupIfResyncSPVRequestedHandler.run();
} else {
walletInitializedHandler.run(); walletInitializedHandler.run();
}
}
}, },
exception -> { exception -> {
if (exception instanceof InvalidHostException && showPopupIfInvalidBtcConfigHandler != null) { if (exception instanceof InvalidHostException && showPopupIfInvalidBtcConfigHandler != null) {

View File

@ -307,7 +307,7 @@ public class EncryptedConnectionList implements PersistableEnvelope, PersistedDa
try { try {
return Encryption.decrypt(encrypted, secret); return Encryption.decrypt(encrypted, secret);
} catch (CryptoException e) { } catch (CryptoException e) {
throw new IllegalArgumentException("Illegal old password", e); throw new IllegalArgumentException("Incorrect password", e);
} }
} }

View File

@ -23,8 +23,6 @@ import bisq.common.crypto.ScryptUtil;
import bisq.common.handlers.ExceptionHandler; import bisq.common.handlers.ExceptionHandler;
import bisq.common.handlers.ResultHandler; import bisq.common.handlers.ResultHandler;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.crypto.KeyCrypter; import org.bitcoinj.crypto.KeyCrypter;
import org.bitcoinj.crypto.KeyCrypterScrypt; import org.bitcoinj.crypto.KeyCrypterScrypt;
import org.bitcoinj.wallet.DeterministicSeed; import org.bitcoinj.wallet.DeterministicSeed;

View File

@ -10,6 +10,7 @@ import bisq.common.util.Utilities;
import bisq.core.api.AccountServiceListener; import bisq.core.api.AccountServiceListener;
import bisq.core.api.CoreAccountService; import bisq.core.api.CoreAccountService;
import bisq.core.api.CoreMoneroConnectionsService; import bisq.core.api.CoreMoneroConnectionsService;
import bisq.core.app.HavenoSetup;
import bisq.core.btc.listeners.XmrBalanceListener; import bisq.core.btc.listeners.XmrBalanceListener;
import bisq.core.btc.model.XmrAddressEntry; import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.model.XmrAddressEntryList; import bisq.core.btc.model.XmrAddressEntryList;
@ -92,6 +93,7 @@ public class XmrWalletService {
private final CoreMoneroConnectionsService connectionsService; private final CoreMoneroConnectionsService connectionsService;
private final XmrAddressEntryList xmrAddressEntryList; private final XmrAddressEntryList xmrAddressEntryList;
private final WalletsSetup walletsSetup; private final WalletsSetup walletsSetup;
private final File walletDir; private final File walletDir;
private final File xmrWalletFile; private final File xmrWalletFile;
private final int rpcBindPort; private final int rpcBindPort;
@ -103,6 +105,8 @@ public class XmrWalletService {
private final Map<String, Optional<MoneroTx>> txCache = new HashMap<String, Optional<MoneroTx>>(); private final Map<String, Optional<MoneroTx>> txCache = new HashMap<String, Optional<MoneroTx>>();
private boolean isShutDown = false; private boolean isShutDown = false;
private HavenoSetup havenoSetup;
@Inject @Inject
XmrWalletService(CoreAccountService accountService, XmrWalletService(CoreAccountService accountService,
CoreMoneroConnectionsService connectionsService, CoreMoneroConnectionsService connectionsService,
@ -148,8 +152,8 @@ public class XmrWalletService {
@Override @Override
public void onPasswordChanged(String oldPassword, String newPassword) { public void onPasswordChanged(String oldPassword, String newPassword) {
log.info(getClass() + "accountservice.onPasswordChanged()"); log.info(getClass() + "accountservice.onPasswordChanged()");
if (oldPassword == null) oldPassword = MONERO_WALLET_RPC_DEFAULT_PASSWORD; if (oldPassword == null || oldPassword.isEmpty()) oldPassword = MONERO_WALLET_RPC_DEFAULT_PASSWORD;
if (newPassword == null) newPassword = MONERO_WALLET_RPC_DEFAULT_PASSWORD; if (newPassword == null || newPassword.isEmpty()) newPassword = MONERO_WALLET_RPC_DEFAULT_PASSWORD;
changeWalletPasswords(oldPassword, newPassword); changeWalletPasswords(oldPassword, newPassword);
} }
}); });
@ -558,6 +562,9 @@ public class XmrWalletService {
System.out.println("Monero wallet balance: " + wallet.getBalance(0)); System.out.println("Monero wallet balance: " + wallet.getBalance(0));
System.out.println("Monero wallet unlocked balance: " + wallet.getUnlockedBalance(0)); System.out.println("Monero wallet unlocked balance: " + wallet.getUnlockedBalance(0));
// notify setup that main wallet is initialized
havenoSetup.getWalletInitialized().set(true); // TODO: change to listener pattern?
// register internal listener to notify external listeners // register internal listener to notify external listeners
wallet.addListener(new XmrWalletListener()); wallet.addListener(new XmrWalletListener());
} }
@ -961,6 +968,10 @@ public class XmrWalletService {
log.info("\n" + tracePrefix + ":" + sb.toString()); log.info("\n" + tracePrefix + ":" + sb.toString());
} }
public void setHavenoSetup(HavenoSetup havenoSetup) {
this.havenoSetup = havenoSetup;
}
// -------------------------------- HELPERS ------------------------------- // -------------------------------- HELPERS -------------------------------
/** /**

View File

@ -17,11 +17,10 @@
package bisq.daemon.app; package bisq.daemon.app;
import bisq.core.app.ConsoleInput;
import bisq.core.app.HavenoHeadlessAppMain;
import bisq.core.app.HavenoSetup;
import bisq.core.api.AccountServiceListener; import bisq.core.api.AccountServiceListener;
import bisq.core.app.ConsoleInput;
import bisq.core.app.CoreModule; import bisq.core.app.CoreModule;
import bisq.core.app.HavenoHeadlessAppMain;
import bisq.common.UserThread; import bisq.common.UserThread;
import bisq.common.app.AppModule; import bisq.common.app.AppModule;
@ -32,7 +31,10 @@ import bisq.common.persistence.PersistenceManager;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.Console; import java.io.Console;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -43,7 +45,7 @@ import lombok.extern.slf4j.Slf4j;
import bisq.daemon.grpc.GrpcServer; import bisq.daemon.grpc.GrpcServer;
@Slf4j @Slf4j
public class HavenoDaemonMain extends HavenoHeadlessAppMain implements HavenoSetup.HavenoSetupListener { public class HavenoDaemonMain extends HavenoHeadlessAppMain {
private GrpcServer grpcServer; private GrpcServer grpcServer;
@ -139,13 +141,19 @@ public class HavenoDaemonMain extends HavenoHeadlessAppMain implements HavenoSet
* Start the grpcServer to allow logging in remotely. * Start the grpcServer to allow logging in remotely.
*/ */
@Override @Override
protected boolean loginAccount() { protected CompletableFuture<Boolean> loginAccount() {
boolean opened = super.loginAccount(); CompletableFuture<Boolean> opened = super.loginAccount();
// Start rpc server in case login is coming in from rpc // Start rpc server in case login is coming in from rpc
grpcServer = injector.getInstance(GrpcServer.class); grpcServer = injector.getInstance(GrpcServer.class);
if (!opened) { CompletableFuture<Boolean> inputResult = new CompletableFuture<Boolean>();
try {
if (opened.get()) {
grpcServer.start();
return opened;
} else {
// Nonblocking, we need to stop if the login occurred through rpc. // Nonblocking, we need to stop if the login occurred through rpc.
// TODO: add a mode to mask password // TODO: add a mode to mask password
ConsoleInput reader = new ConsoleInput(Integer.MAX_VALUE, Integer.MAX_VALUE, TimeUnit.MILLISECONDS); ConsoleInput reader = new ConsoleInput(Integer.MAX_VALUE, Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
@ -180,12 +188,13 @@ public class HavenoDaemonMain extends HavenoHeadlessAppMain implements HavenoSet
} }
accountService.removeListener(accountListener); accountService.removeListener(accountListener);
opened = accountService.isAccountOpen(); inputResult.complete(accountService.isAccountOpen());
} else { }
grpcServer.start(); } catch (InterruptedException | ExecutionException e) {
inputResult.completeExceptionally(e);
} }
return opened; return inputResult;
} }
/** /**

View File

@ -67,8 +67,8 @@ import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.geometry.Rectangle2D;
import javafx.geometry.BoundingBox; import javafx.geometry.BoundingBox;
import javafx.geometry.Rectangle2D;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -128,6 +128,7 @@ public class HavenoApp extends Application implements UncaughtExceptionHandler {
} }
public void startApplication(Runnable onApplicationStartedHandler) { public void startApplication(Runnable onApplicationStartedHandler) {
log.info("Running startApplication...");
try { try {
mainView = loadMainView(injector); mainView = loadMainView(injector);
mainView.setOnApplicationStartedHandler(onApplicationStartedHandler); mainView.setOnApplicationStartedHandler(onApplicationStartedHandler);
@ -149,7 +150,7 @@ public class HavenoApp extends Application implements UncaughtExceptionHandler {
.show(); .show();
new Thread(() -> { new Thread(() -> {
gracefulShutDownHandler.gracefulShutDown(() -> { gracefulShutDownHandler.gracefulShutDown(() -> {
log.debug("App shutdown complete"); log.info("App shutdown complete");
}); });
}).start(); }).start();
shutDownRequested = true; shutDownRequested = true;

View File

@ -20,6 +20,7 @@ package bisq.desktop.app;
import bisq.desktop.common.UITimer; import bisq.desktop.common.UITimer;
import bisq.desktop.common.view.guice.InjectorViewFactory; import bisq.desktop.common.view.guice.InjectorViewFactory;
import bisq.desktop.setup.DesktopPersistedDataHost; import bisq.desktop.setup.DesktopPersistedDataHost;
import bisq.desktop.util.ImageUtil;
import bisq.core.app.AvoidStandbyModeService; import bisq.core.app.AvoidStandbyModeService;
import bisq.core.app.HavenoExecutable; import bisq.core.app.HavenoExecutable;
@ -27,10 +28,25 @@ import bisq.core.app.HavenoExecutable;
import bisq.common.UserThread; import bisq.common.UserThread;
import bisq.common.app.AppModule; import bisq.common.app.AppModule;
import bisq.common.app.Version; import bisq.common.app.Version;
import bisq.common.crypto.IncorrectPasswordException;
import javafx.application.Application; import javafx.application.Application;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@ -139,4 +155,86 @@ public class HavenoAppMain extends HavenoExecutable {
// Therefore, calling this as part of onApplicationStarted() // Therefore, calling this as part of onApplicationStarted()
log.info("Using JavaFX {}", System.getProperty("javafx.version")); log.info("Using JavaFX {}", System.getProperty("javafx.version"));
} }
@Override
protected CompletableFuture<Boolean> loginAccount() {
// attempt default login
CompletableFuture<Boolean> result = super.loginAccount();
try {
if (result.get()) return result;
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException(e);
}
// login using dialog
CompletableFuture<Boolean> dialogResult = new CompletableFuture<>();
Platform.setImplicitExit(false);
Platform.runLater(() -> {
// show password dialog until account open
String errorMessage = null;
while (!accountService.isAccountOpen()) {
// create the password dialog
PasswordDialog passwordDialog = new PasswordDialog(errorMessage);
// wait for user to enter password
Optional<String> passwordResult = passwordDialog.showAndWait();
if (passwordResult.isPresent()) {
try {
accountService.openAccount(passwordResult.get());
dialogResult.complete(accountService.isAccountOpen());
} catch (IncorrectPasswordException e) {
errorMessage = "Incorrect password";
}
} else {
// if the user cancelled the dialog, complete the passwordFuture exceptionally
dialogResult.completeExceptionally(new Exception("Password dialog cancelled"));
break;
}
}
});
return dialogResult;
}
private class PasswordDialog extends Dialog<String> {
public PasswordDialog(String errorMessage) {
setTitle("Enter Password");
setHeaderText("Please enter your Haveno password:");
// Add an icon to the dialog
Stage stage = (Stage) getDialogPane().getScene().getWindow();
stage.getIcons().add(ImageUtil.getImageByPath("lock.png"));
// Create the password field
PasswordField passwordField = new PasswordField();
passwordField.setPromptText("Password");
// Create the error message field
Label errorMessageField = new Label(errorMessage);
errorMessageField.setTextFill(Color.color(1, 0, 0));
// Set the dialog content
VBox vbox = new VBox(10);
vbox.getChildren().addAll(new ImageView(ImageUtil.getImageByPath("logo_splash.png")), passwordField, errorMessageField);
getDialogPane().setContent(vbox);
// Add OK and Cancel buttons
ButtonType okButton = new ButtonType("OK", ButtonBar.ButtonData.OK_DONE);
ButtonType cancelButton = new ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE);
getDialogPane().getButtonTypes().addAll(okButton, cancelButton);
// Convert the result to a string when the OK button is clicked
setResultConverter(buttonType -> {
if (buttonType == okButton) {
return passwordField.getText();
} else {
new Thread(() -> HavenoApp.getShutDownHandler().run()).start();
return null;
}
});
}
}
} }

View File

@ -18,7 +18,6 @@
package bisq.desktop.main; package bisq.desktop.main;
import bisq.desktop.Navigation; import bisq.desktop.Navigation;
import bisq.desktop.app.HavenoApp;
import bisq.desktop.common.model.ViewModel; import bisq.desktop.common.model.ViewModel;
import bisq.desktop.components.TxIdTextField; import bisq.desktop.components.TxIdTextField;
import bisq.desktop.main.account.AccountView; import bisq.desktop.main.account.AccountView;
@ -109,7 +108,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener { public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener {
private final HavenoSetup bisqSetup; private final HavenoSetup havenoSetup;
private final CoreMoneroConnectionsService connectionService; private final CoreMoneroConnectionsService connectionService;
private final User user; private final User user;
private final BalancePresentation balancePresentation; private final BalancePresentation balancePresentation;
@ -154,7 +153,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
public MainViewModel(HavenoSetup bisqSetup, public MainViewModel(HavenoSetup havenoSetup,
CoreMoneroConnectionsService connectionService, CoreMoneroConnectionsService connectionService,
XmrWalletService xmrWalletService, XmrWalletService xmrWalletService,
User user, User user,
@ -179,7 +178,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
TorNetworkSettingsWindow torNetworkSettingsWindow, TorNetworkSettingsWindow torNetworkSettingsWindow,
CorruptedStorageFileHandler corruptedStorageFileHandler, CorruptedStorageFileHandler corruptedStorageFileHandler,
Navigation navigation) { Navigation navigation) {
this.bisqSetup = bisqSetup; this.havenoSetup = havenoSetup;
this.connectionService = connectionService; this.connectionService = connectionService;
this.user = user; this.user = user;
this.balancePresentation = balancePresentation; this.balancePresentation = balancePresentation;
@ -211,7 +210,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
GUIUtil.setPreferences(preferences); GUIUtil.setPreferences(preferences);
setupHandlers(); setupHandlers();
bisqSetup.addHavenoSetupListener(this); havenoSetup.addHavenoSetupListener(this);
} }
@ -303,7 +302,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
} }
void onOpenDownloadWindow() { void onOpenDownloadWindow() {
bisqSetup.displayAlertIfPresent(user.getDisplayedAlert(), true); havenoSetup.displayAlertIfPresent(user.getDisplayedAlert(), true);
} }
void setPriceFeedComboBoxItem(PriceFeedComboBoxItem item) { void setPriceFeedComboBoxItem(PriceFeedComboBoxItem item) {
@ -316,34 +315,30 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void setupHandlers() { private void setupHandlers() {
bisqSetup.setDisplayTacHandler(acceptedHandler -> UserThread.runAfter(() -> { havenoSetup.setDisplayTacHandler(acceptedHandler -> UserThread.runAfter(() -> {
//noinspection FunctionalExpressionCanBeFolded //noinspection FunctionalExpressionCanBeFolded
tacWindow.onAction(acceptedHandler::run).show(); tacWindow.onAction(acceptedHandler::run).show();
}, 1)); }, 1));
bisqSetup.setDisplayTorNetworkSettingsHandler(show -> { havenoSetup.setDisplayTorNetworkSettingsHandler(show -> {
if (show) { if (show) {
torNetworkSettingsWindow.show(); torNetworkSettingsWindow.show();
} else if (torNetworkSettingsWindow.isDisplayed()) { } else if (torNetworkSettingsWindow.isDisplayed()) {
torNetworkSettingsWindow.hide(); torNetworkSettingsWindow.hide();
} }
}); });
bisqSetup.setSpvFileCorruptedHandler(msg -> new Popup().warning(msg) havenoSetup.setSpvFileCorruptedHandler(msg -> new Popup().warning(msg)
.actionButtonText(Res.get("settings.net.reSyncSPVChainButton")) .actionButtonText(Res.get("settings.net.reSyncSPVChainButton"))
.onAction(() -> GUIUtil.reSyncSPVChain(preferences)) .onAction(() -> GUIUtil.reSyncSPVChain(preferences))
.show()); .show());
bisqSetup.setChainFileLockedExceptionHandler(msg -> new Popup().warning(msg) havenoSetup.setChainFileLockedExceptionHandler(msg -> new Popup().warning(msg)
.useShutDownButton() .useShutDownButton()
.show()); .show());
bisqSetup.setLockedUpFundsHandler(msg -> new Popup().width(850).warning(msg).show()); havenoSetup.setLockedUpFundsHandler(msg -> new Popup().width(850).warning(msg).show());
bisqSetup.setShowFirstPopupIfResyncSPVRequestedHandler(this::showFirstPopupIfResyncSPVRequested); havenoSetup.setShowFirstPopupIfResyncSPVRequestedHandler(this::showFirstPopupIfResyncSPVRequested);
bisqSetup.setRequestWalletPasswordHandler(aesKeyHandler -> walletPasswordWindow
.onAesKey(aesKeyHandler::accept)
.onClose(() -> HavenoApp.getShutDownHandler().run())
.show());
bisqSetup.setDisplayUpdateHandler((alert, key) -> new DisplayUpdateDownloadWindow(alert, config) havenoSetup.setDisplayUpdateHandler((alert, key) -> new DisplayUpdateDownloadWindow(alert, config)
.actionButtonText(Res.get("displayUpdateDownloadWindow.button.downloadLater")) .actionButtonText(Res.get("displayUpdateDownloadWindow.button.downloadLater"))
.onAction(() -> { .onAction(() -> {
preferences.dontShowAgain(key, false); // update later preferences.dontShowAgain(key, false); // update later
@ -353,19 +348,19 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
preferences.dontShowAgain(key, true); // ignore update preferences.dontShowAgain(key, true); // ignore update
}) })
.show()); .show());
bisqSetup.setDisplayAlertHandler(alert -> new DisplayAlertMessageWindow() havenoSetup.setDisplayAlertHandler(alert -> new DisplayAlertMessageWindow()
.alertMessage(alert) .alertMessage(alert)
.closeButtonText(Res.get("shared.close")) .closeButtonText(Res.get("shared.close"))
.onClose(() -> user.setDisplayedAlert(alert)) .onClose(() -> user.setDisplayedAlert(alert))
.show()); .show());
bisqSetup.setDisplayPrivateNotificationHandler(privateNotification -> havenoSetup.setDisplayPrivateNotificationHandler(privateNotification ->
new Popup().headLine(Res.get("popup.privateNotification.headline")) new Popup().headLine(Res.get("popup.privateNotification.headline"))
.attention(privateNotification.getMessage()) .attention(privateNotification.getMessage())
.onClose(privateNotificationManager::removePrivateNotification) .onClose(privateNotificationManager::removePrivateNotification)
.useIUnderstandButton() .useIUnderstandButton()
.show()); .show());
bisqSetup.setDisplaySecurityRecommendationHandler(key -> {}); havenoSetup.setDisplaySecurityRecommendationHandler(key -> {});
bisqSetup.setDisplayLocalhostHandler(key -> { havenoSetup.setDisplayLocalhostHandler(key -> {
if (!DevEnv.isDevMode()) { if (!DevEnv.isDevMode()) {
Popup popup = new Popup().backgroundInfo(Res.get("popup.bitcoinLocalhostNode.msg")) Popup popup = new Popup().backgroundInfo(Res.get("popup.bitcoinLocalhostNode.msg"))
.dontShowAgainId(key); .dontShowAgainId(key);
@ -373,31 +368,31 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
popupQueue.add(popup); popupQueue.add(popup);
} }
}); });
bisqSetup.setDisplaySignedByArbitratorHandler(key -> accountPresentation.showOneTimeAccountSigningPopup( havenoSetup.setDisplaySignedByArbitratorHandler(key -> accountPresentation.showOneTimeAccountSigningPopup(
key, "popup.accountSigning.signedByArbitrator")); key, "popup.accountSigning.signedByArbitrator"));
bisqSetup.setDisplaySignedByPeerHandler(key -> accountPresentation.showOneTimeAccountSigningPopup( havenoSetup.setDisplaySignedByPeerHandler(key -> accountPresentation.showOneTimeAccountSigningPopup(
key, "popup.accountSigning.signedByPeer", String.valueOf(SignedWitnessService.SIGNER_AGE_DAYS))); key, "popup.accountSigning.signedByPeer", String.valueOf(SignedWitnessService.SIGNER_AGE_DAYS)));
bisqSetup.setDisplayPeerLimitLiftedHandler(key -> accountPresentation.showOneTimeAccountSigningPopup( havenoSetup.setDisplayPeerLimitLiftedHandler(key -> accountPresentation.showOneTimeAccountSigningPopup(
key, "popup.accountSigning.peerLimitLifted")); key, "popup.accountSigning.peerLimitLifted"));
bisqSetup.setDisplayPeerSignerHandler(key -> accountPresentation.showOneTimeAccountSigningPopup( havenoSetup.setDisplayPeerSignerHandler(key -> accountPresentation.showOneTimeAccountSigningPopup(
key, "popup.accountSigning.peerSigner")); key, "popup.accountSigning.peerSigner"));
bisqSetup.setWrongOSArchitectureHandler(msg -> new Popup().warning(msg).show()); havenoSetup.setWrongOSArchitectureHandler(msg -> new Popup().warning(msg).show());
bisqSetup.setRejectedTxErrorMessageHandler(msg -> new Popup().width(850).warning(msg).show()); havenoSetup.setRejectedTxErrorMessageHandler(msg -> new Popup().width(850).warning(msg).show());
bisqSetup.setShowPopupIfInvalidBtcConfigHandler(this::showPopupIfInvalidBtcConfig); havenoSetup.setShowPopupIfInvalidBtcConfigHandler(this::showPopupIfInvalidBtcConfig);
bisqSetup.setRevolutAccountsUpdateHandler(revolutAccountList -> { havenoSetup.setRevolutAccountsUpdateHandler(revolutAccountList -> {
// We copy the array as we will mutate it later // We copy the array as we will mutate it later
showRevolutAccountUpdateWindow(new ArrayList<>(revolutAccountList)); showRevolutAccountUpdateWindow(new ArrayList<>(revolutAccountList));
}); });
bisqSetup.setAmazonGiftCardAccountsUpdateHandler(amazonGiftCardAccountList -> { havenoSetup.setAmazonGiftCardAccountsUpdateHandler(amazonGiftCardAccountList -> {
// We copy the array as we will mutate it later // We copy the array as we will mutate it later
showAmazonGiftCardAccountUpdateWindow(new ArrayList<>(amazonGiftCardAccountList)); showAmazonGiftCardAccountUpdateWindow(new ArrayList<>(amazonGiftCardAccountList));
}); });
bisqSetup.setOsxKeyLoggerWarningHandler(() -> { }); havenoSetup.setOsxKeyLoggerWarningHandler(() -> { });
bisqSetup.setQubesOSInfoHandler(() -> { havenoSetup.setQubesOSInfoHandler(() -> {
String key = "qubesOSSetupInfo"; String key = "qubesOSSetupInfo";
if (preferences.showAgain(key)) { if (preferences.showAgain(key)) {
new Popup().information(Res.get("popup.info.qubesOSSetupInfo")) new Popup().information(Res.get("popup.info.qubesOSSetupInfo"))
@ -407,14 +402,14 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
} }
}); });
bisqSetup.setDownGradePreventionHandler(lastVersion -> { havenoSetup.setDownGradePreventionHandler(lastVersion -> {
new Popup().warning(Res.get("popup.warn.downGradePrevention", lastVersion, Version.VERSION)) new Popup().warning(Res.get("popup.warn.downGradePrevention", lastVersion, Version.VERSION))
.useShutDownButton() .useShutDownButton()
.hideCloseButton() .hideCloseButton()
.show(); .show();
}); });
bisqSetup.setTorAddressUpgradeHandler(() -> new Popup().information(Res.get("popup.info.torMigration.msg")) havenoSetup.setTorAddressUpgradeHandler(() -> new Popup().information(Res.get("popup.info.torMigration.msg"))
.actionButtonTextWithGoTo("navigation.account.backup") .actionButtonTextWithGoTo("navigation.account.backup")
.onAction(() -> { .onAction(() -> {
navigation.setReturnPath(navigation.getCurrentPath()); navigation.setReturnPath(navigation.getCurrentPath());
@ -430,9 +425,9 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
.warning(Res.get("popup.error.takeOfferRequestFailed", errorMessage)) .warning(Res.get("popup.error.takeOfferRequestFailed", errorMessage))
.show()); .show());
bisqSetup.getBtcSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress()); havenoSetup.getBtcSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress());
bisqSetup.setFilterWarningHandler(warning -> new Popup().warning(warning).show()); havenoSetup.setFilterWarningHandler(warning -> new Popup().warning(warning).show());
this.footerVersionInfo.setValue("v" + Version.VERSION); this.footerVersionInfo.setValue("v" + Version.VERSION);
this.getNewVersionAvailableProperty().addListener((observable, oldValue, newValue) -> { this.getNewVersionAvailableProperty().addListener((observable, oldValue, newValue) -> {
@ -538,10 +533,10 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
private void showFirstPopupIfResyncSPVRequested() { private void showFirstPopupIfResyncSPVRequested() {
Popup firstPopup = new Popup(); Popup firstPopup = new Popup();
firstPopup.information(Res.get("settings.net.reSyncSPVAfterRestart")).show(); firstPopup.information(Res.get("settings.net.reSyncSPVAfterRestart")).show();
if (bisqSetup.getBtcSyncProgress().get() == 1) { if (havenoSetup.getBtcSyncProgress().get() == 1) {
showSecondPopupIfResyncSPVRequested(firstPopup); showSecondPopupIfResyncSPVRequested(firstPopup);
} else { } else {
bisqSetup.getBtcSyncProgress().addListener((observable, oldValue, newValue) -> { havenoSetup.getBtcSyncProgress().addListener((observable, oldValue, newValue) -> {
if ((double) newValue == 1) if ((double) newValue == 1)
showSecondPopupIfResyncSPVRequested(firstPopup); showSecondPopupIfResyncSPVRequested(firstPopup);
}); });
@ -596,7 +591,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
} }
private void updateBtcSyncProgress() { private void updateBtcSyncProgress() {
final DoubleProperty btcSyncProgress = bisqSetup.getBtcSyncProgress(); final DoubleProperty btcSyncProgress = havenoSetup.getBtcSyncProgress();
combinedSyncProgress.set(btcSyncProgress.doubleValue()); combinedSyncProgress.set(btcSyncProgress.doubleValue());
} }
@ -635,7 +630,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
BooleanProperty getNewVersionAvailableProperty() { BooleanProperty getNewVersionAvailableProperty() {
return bisqSetup.getNewVersionAvailableProperty(); return havenoSetup.getNewVersionAvailableProperty();
} }
StringProperty getNumOpenSupportTickets() { StringProperty getNumOpenSupportTickets() {
@ -670,7 +665,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
// Wallet // Wallet
StringProperty getBtcInfo() { StringProperty getBtcInfo() {
final StringProperty combinedInfo = new SimpleStringProperty(); final StringProperty combinedInfo = new SimpleStringProperty();
combinedInfo.bind(bisqSetup.getBtcInfo()); combinedInfo.bind(havenoSetup.getBtcInfo());
return combinedInfo; return combinedInfo;
} }
@ -685,44 +680,44 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
} }
StringProperty getWalletServiceErrorMsg() { StringProperty getWalletServiceErrorMsg() {
return bisqSetup.getWalletServiceErrorMsg(); return havenoSetup.getWalletServiceErrorMsg();
} }
StringProperty getBtcSplashSyncIconId() { StringProperty getBtcSplashSyncIconId() {
return bisqSetup.getBtcSplashSyncIconId(); return havenoSetup.getBtcSplashSyncIconId();
} }
BooleanProperty getUseTorForBTC() { BooleanProperty getUseTorForBTC() {
return bisqSetup.getUseTorForBTC(); return havenoSetup.getUseTorForBTC();
} }
// P2P // P2P
StringProperty getP2PNetworkInfo() { StringProperty getP2PNetworkInfo() {
return bisqSetup.getP2PNetworkInfo(); return havenoSetup.getP2PNetworkInfo();
} }
BooleanProperty getSplashP2PNetworkAnimationVisible() { BooleanProperty getSplashP2PNetworkAnimationVisible() {
return bisqSetup.getSplashP2PNetworkAnimationVisible(); return havenoSetup.getSplashP2PNetworkAnimationVisible();
} }
StringProperty getP2pNetworkWarnMsg() { StringProperty getP2pNetworkWarnMsg() {
return bisqSetup.getP2pNetworkWarnMsg(); return havenoSetup.getP2pNetworkWarnMsg();
} }
StringProperty getP2PNetworkIconId() { StringProperty getP2PNetworkIconId() {
return bisqSetup.getP2PNetworkIconId(); return havenoSetup.getP2PNetworkIconId();
} }
StringProperty getP2PNetworkStatusIconId() { StringProperty getP2PNetworkStatusIconId() {
return bisqSetup.getP2PNetworkStatusIconId(); return havenoSetup.getP2PNetworkStatusIconId();
} }
BooleanProperty getUpdatedDataReceived() { BooleanProperty getUpdatedDataReceived() {
return bisqSetup.getUpdatedDataReceived(); return havenoSetup.getUpdatedDataReceived();
} }
StringProperty getP2pNetworkLabelId() { StringProperty getP2pNetworkLabelId() {
return bisqSetup.getP2pNetworkLabelId(); return havenoSetup.getP2pNetworkLabelId();
} }
// marketPricePresentation // marketPricePresentation

View File

@ -27,16 +27,15 @@ import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.main.MainView; import bisq.desktop.main.MainView;
import bisq.desktop.main.account.AccountView; import bisq.desktop.main.account.AccountView;
import bisq.desktop.main.account.content.backup.BackupView; import bisq.desktop.main.account.content.backup.BackupView;
import bisq.desktop.main.account.content.seedwords.SeedWordsView;
import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.Layout; import bisq.desktop.util.Layout;
import bisq.desktop.util.validation.PasswordValidator; import bisq.desktop.util.validation.PasswordValidator;
import bisq.core.api.CoreAccountService;
import bisq.core.btc.wallet.WalletsManager; import bisq.core.btc.wallet.WalletsManager;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.common.crypto.ScryptUtil;
import bisq.common.util.Tuple4;
import org.bitcoinj.crypto.KeyCrypterScrypt; import bisq.common.util.Tuple4;
import javax.inject.Inject; import javax.inject.Inject;
@ -61,6 +60,7 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
private final WalletsManager walletsManager; private final WalletsManager walletsManager;
private final PasswordValidator passwordValidator; private final PasswordValidator passwordValidator;
private final Navigation navigation; private final Navigation navigation;
private final CoreAccountService accountService;
private PasswordTextField passwordField; private PasswordTextField passwordField;
private PasswordTextField repeatedPasswordField; private PasswordTextField repeatedPasswordField;
@ -77,10 +77,11 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
private PasswordView(WalletsManager walletsManager, PasswordValidator passwordValidator, Navigation navigation) { private PasswordView(CoreAccountService accountService, WalletsManager walletsManager, PasswordValidator passwordValidator, Navigation navigation) {
this.walletsManager = walletsManager; this.walletsManager = walletsManager;
this.passwordValidator = passwordValidator; this.passwordValidator = passwordValidator;
this.navigation = navigation; this.navigation = navigation;
this.accountService = accountService;
} }
@Override @Override
@ -141,19 +142,14 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
deriveStatusLabel.setText(Res.get("password.deriveKey")); deriveStatusLabel.setText(Res.get("password.deriveKey"));
busyAnimation.play(); busyAnimation.play();
KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt();
ScryptUtil.deriveKeyWithScrypt(keyCrypterScrypt, password, aesKey -> {
deriveStatusLabel.setText("");
busyAnimation.stop();
if (walletsManager.areWalletsEncrypted()) { if (walletsManager.areWalletsEncrypted()) {
if (walletsManager.checkAESKey(aesKey)) { try {
walletsManager.decryptWallets(aesKey); accountService.changePassword(password, null);
new Popup() new Popup()
.feedback(Res.get("password.walletDecrypted")) .feedback(Res.get("password.walletDecrypted"))
.show(); .show();
backupWalletAndResetFields(); backupWalletAndResetFields();
} else { } catch (Throwable t) {
pwButton.setDisable(false); pwButton.setDisable(false);
new Popup() new Popup()
.warning(Res.get("password.wrongPw")) .warning(Res.get("password.wrongPw"))
@ -161,7 +157,7 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
} }
} else { } else {
try { try {
walletsManager.encryptWallets(keyCrypterScrypt, aesKey); accountService.changePassword(accountService.getPassword(), password);
new Popup() new Popup()
.feedback(Res.get("password.walletEncrypted")) .feedback(Res.get("password.walletEncrypted"))
.show(); .show();
@ -175,7 +171,9 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
} }
setText(); setText();
updatePasswordListeners(); updatePasswordListeners();
});
deriveStatusLabel.setText("");
busyAnimation.stop();
} }
private void backupWalletAndResetFields() { private void backupWalletAndResetFields() {

View File

@ -218,7 +218,7 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
} }
private void askForPassword() { private void askForPassword() {
walletPasswordWindow.headLine(Res.get("account.seed.enterPw")).onAesKey(aesKey -> { walletPasswordWindow.headLine(Res.get("account.seed.enterPw")).onSuccess(() -> {
initSeedWords(xmrWalletService.getWallet().getMnemonic()); initSeedWords(xmrWalletService.getWallet().getMnemonic());
showSeedScreen(); showSeedScreen();
}).hideForgotPasswordButton().show(); }).hideForgotPasswordButton().show();

View File

@ -121,14 +121,7 @@ public final class BtcEmptyWalletWindow extends Overlay<BtcEmptyWalletWindow> {
emptyWalletButton.setDisable(!isBalanceSufficient && addressInputTextField.getText().length() > 0); emptyWalletButton.setDisable(!isBalanceSufficient && addressInputTextField.getText().length() > 0);
emptyWalletButton.setOnAction(e -> { emptyWalletButton.setOnAction(e -> {
if (addressInputTextField.getText().length() > 0 && isBalanceSufficient) { if (addressInputTextField.getText().length() > 0 && isBalanceSufficient) {
if (btcWalletService.isEncrypted()) { log.warn(getClass().getSimpleName() + ".addContent() needs updated for XMR");
walletPasswordWindow
.onAesKey(this::doEmptyWallet)
.onClose(this::blurAgain)
.show();
} else {
doEmptyWallet(null);
}
} }
}); });

View File

@ -29,7 +29,6 @@ import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.offer.Offer; import bisq.core.offer.Offer;
import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.support.dispute.agent.DisputeAgentLookupMap;
import bisq.core.support.dispute.arbitration.ArbitrationManager; import bisq.core.support.dispute.arbitration.ArbitrationManager;
import bisq.core.trade.Contract; import bisq.core.trade.Contract;
import bisq.core.trade.Trade; import bisq.core.trade.Trade;

View File

@ -25,18 +25,15 @@ import bisq.desktop.main.SharedPresentation;
import bisq.desktop.main.overlays.Overlay; import bisq.desktop.main.overlays.Overlay;
import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.Layout; import bisq.desktop.util.Layout;
import bisq.desktop.util.Transitions; import bisq.core.api.CoreAccountService;
import bisq.core.btc.wallet.WalletsManager; import bisq.core.btc.wallet.WalletsManager;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.offer.OpenOfferManager; import bisq.core.offer.OpenOfferManager;
import bisq.common.UserThread;
import bisq.common.config.Config; import bisq.common.config.Config;
import bisq.common.crypto.ScryptUtil; import bisq.common.crypto.IncorrectPasswordException;
import bisq.common.util.Tuple2; import bisq.common.util.Tuple2;
import org.bitcoinj.crypto.KeyCrypterScrypt;
import org.bitcoinj.crypto.MnemonicCode; import org.bitcoinj.crypto.MnemonicCode;
import org.bitcoinj.crypto.MnemonicException; import org.bitcoinj.crypto.MnemonicException;
import org.bitcoinj.wallet.DeterministicSeed; import org.bitcoinj.wallet.DeterministicSeed;
@ -65,8 +62,6 @@ import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import org.bouncycastle.crypto.params.KeyParameter;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId; import java.time.ZoneId;
@ -75,8 +70,6 @@ import java.time.ZoneOffset;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import static bisq.desktop.util.FormBuilder.addPasswordTextField; import static bisq.desktop.util.FormBuilder.addPasswordTextField;
@ -88,12 +81,13 @@ import static javafx.beans.binding.Bindings.createBooleanBinding;
@Slf4j @Slf4j
public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> { public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> {
private final CoreAccountService accountService;
private final WalletsManager walletsManager; private final WalletsManager walletsManager;
private final OpenOfferManager openOfferManager; private final OpenOfferManager openOfferManager;
private File storageDir; private File storageDir;
private Button unlockButton; private Button unlockButton;
private AesKeyHandler aesKeyHandler; private WalletPasswordHandler passwordHandler;
private PasswordTextField passwordTextField; private PasswordTextField passwordTextField;
private Button forgotPasswordButton; private Button forgotPasswordButton;
private Button restoreButton; private Button restoreButton;
@ -111,14 +105,16 @@ public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> {
// Interface // Interface
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public interface AesKeyHandler { public interface WalletPasswordHandler {
void onAesKey(KeyParameter aesKey); void onSuccess();
} }
@Inject @Inject
private WalletPasswordWindow(WalletsManager walletsManager, private WalletPasswordWindow(CoreAccountService accountService,
WalletsManager walletsManager,
OpenOfferManager openOfferManager, OpenOfferManager openOfferManager,
@Named(Config.STORAGE_DIR) File storageDir) { @Named(Config.STORAGE_DIR) File storageDir) {
this.accountService = accountService;
this.walletsManager = walletsManager; this.walletsManager = walletsManager;
this.openOfferManager = openOfferManager; this.openOfferManager = openOfferManager;
this.storageDir = storageDir; this.storageDir = storageDir;
@ -149,8 +145,8 @@ public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> {
display(); display();
} }
public WalletPasswordWindow onAesKey(AesKeyHandler aesKeyHandler) { public WalletPasswordWindow onSuccess(WalletPasswordHandler passwordHandler) {
this.aesKeyHandler = aesKeyHandler; this.passwordHandler = passwordHandler;
return this; return this;
} }
@ -213,27 +209,16 @@ public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> {
unlockButton.setOnAction(e -> { unlockButton.setOnAction(e -> {
String password = passwordTextField.getText(); String password = passwordTextField.getText();
checkArgument(password.length() < 500, Res.get("password.tooLong")); checkArgument(password.length() < 500, Res.get("password.tooLong"));
KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt(); try {
if (keyCrypterScrypt != null) { accountService.verifyPassword(password);
busyAnimation.play(); if (passwordHandler != null) passwordHandler.onSuccess();
deriveStatusLabel.setText(Res.get("password.deriveKey"));
ScryptUtil.deriveKeyWithScrypt(keyCrypterScrypt, password, aesKey -> {
if (walletsManager.checkAESKey(aesKey)) {
if (aesKeyHandler != null)
aesKeyHandler.onAesKey(aesKey);
hide(); hide();
} else { } catch (IncorrectPasswordException e2) {
busyAnimation.stop(); busyAnimation.stop();
deriveStatusLabel.setText(""); deriveStatusLabel.setText("");
new Popup()
UserThread.runAfter(() -> new Popup()
.warning(Res.get("password.wrongPw")) .warning(Res.get("password.wrongPw"))
.onClose(this::blurAgain).show(), Transitions.DEFAULT_DURATION, TimeUnit.MILLISECONDS); .onClose(this::blurAgain).show();
}
});
} else {
log.error("wallet.getKeyCrypter() is null, that must not happen.");
} }
}); });

View File

@ -70,6 +70,10 @@ public class ImageUtil {
} }
} }
public static Image getImageByPath(String imagePath) {
return getImageByUrl("/images/" + imagePath);
}
// determine if this is a MacOS retina display // determine if this is a MacOS retina display
// https://stackoverflow.com/questions/20767708/how-do-you-detect-a-retina-display-in-java#20767802 // https://stackoverflow.com/questions/20767708/how-do-you-detect-a-retina-display-in-java#20767802
public static boolean isRetina() { public static boolean isRetina() {

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB