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) {
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 (newPassword != null && newPassword.length() < 8) throw new IllegalStateException("Password must be at least 8 characters");
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() {
if (!isAccountOpen()) throw new IllegalStateException("Cannot close unopened account");
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.statistics.TradeStatisticsManager;
import bisq.core.trade.txproof.xmr.XmrTxProofService;
import bisq.network.p2p.P2PService;
import bisq.common.UserThread;
import bisq.common.app.AppModule;
import bisq.common.config.HavenoHelpFormatter;
import bisq.common.config.Config;
import bisq.common.config.ConfigException;
import bisq.common.config.HavenoHelpFormatter;
import bisq.common.crypto.IncorrectPasswordException;
import bisq.common.handlers.ResultHandler;
import bisq.common.persistence.PersistenceManager;
@ -51,8 +52,11 @@ import com.google.inject.Guice;
import com.google.inject.Injector;
import java.io.Console;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
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.
protected abstract void launchApplication();
///////////////////////////////////////////////////////////////////////////////////////////
// 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.
if (!isReadOnly && loginAccount()) {
readAllPersisted(this::startApplication);
} else {
log.warn("Running application in readonly mode");
startApplication();
}
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);
} else {
log.warn("Running application in readonly mode");
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.
*/
protected boolean loginAccount() {
protected CompletableFuture<Boolean> loginAccount() {
CompletableFuture<Boolean> result = new CompletableFuture<>();
if (accountService.accountExists()) {
log.info("Account already exists, attempting to open");
try {
accountService.openAccount(null);
result.complete(accountService.isAccountOpen());
} catch (IncorrectPasswordException ipe) {
log.info("Account password protected, password required");
result.complete(false);
}
} else if (!config.passwordRequired) {
log.info("Creating Haveno account with null password");
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.setLockedUpFundsHandler(msg -> log.info("onLockedUpFundsHandler: msg={}", msg));
bisqSetup.setShowFirstPopupIfResyncSPVRequestedHandler(() -> log.info("onShowFirstPopupIfResyncSPVRequestedHandler"));
bisqSetup.setRequestWalletPasswordHandler(aesKeyHandler -> log.info("onRequestWalletPasswordHandler"));
bisqSetup.setDisplayUpdateHandler((alert, key) -> log.info("onDisplayUpdateHandler"));
bisqSetup.setDisplayAlertHandler(alert -> log.info("onDisplayAlertHandler. alert={}", alert));
bisqSetup.setDisplayPrivateNotificationHandler(privateNotification -> log.info("onDisplayPrivateNotificationHandler. privateNotification={}", privateNotification));

View file

@ -83,8 +83,6 @@ import javafx.beans.value.ChangeListener;
import javafx.collections.SetChangeListener;
import org.bouncycastle.crypto.params.KeyParameter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
@ -160,9 +158,6 @@ public class HavenoSetup {
private Runnable showFirstPopupIfResyncSPVRequestedHandler;
@Setter
@Nullable
private Consumer<Consumer<KeyParameter>> requestWalletPasswordHandler;
@Setter
@Nullable
private Consumer<Alert> displayAlertHandler;
@Setter
@Nullable
@ -215,30 +210,30 @@ public class HavenoSetup {
@Inject
public HavenoSetup(DomainInitialisation domainInitialisation,
P2PNetworkSetup p2PNetworkSetup,
WalletAppSetup walletAppSetup,
WalletsManager walletsManager,
WalletsSetup walletsSetup,
XmrWalletService xmrWalletService,
BtcWalletService btcWalletService,
P2PService p2PService,
PrivateNotificationManager privateNotificationManager,
SignedWitnessStorageService signedWitnessStorageService,
TradeManager tradeManager,
OpenOfferManager openOfferManager,
Preferences preferences,
User user,
AlertManager alertManager,
Config config,
AccountAgeWitnessService accountAgeWitnessService,
TorSetup torSetup,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
LocalBitcoinNode localBitcoinNode,
AppStartupState appStartupState,
Socks5ProxyProvider socks5ProxyProvider,
MediationManager mediationManager,
RefundManager refundManager,
ArbitrationManager arbitrationManager) {
P2PNetworkSetup p2PNetworkSetup,
WalletAppSetup walletAppSetup,
WalletsManager walletsManager,
WalletsSetup walletsSetup,
XmrWalletService xmrWalletService,
BtcWalletService btcWalletService,
P2PService p2PService,
PrivateNotificationManager privateNotificationManager,
SignedWitnessStorageService signedWitnessStorageService,
TradeManager tradeManager,
OpenOfferManager openOfferManager,
Preferences preferences,
User user,
AlertManager alertManager,
Config config,
AccountAgeWitnessService accountAgeWitnessService,
TorSetup torSetup,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
LocalBitcoinNode localBitcoinNode,
AppStartupState appStartupState,
Socks5ProxyProvider socks5ProxyProvider,
MediationManager mediationManager,
RefundManager refundManager,
ArbitrationManager arbitrationManager) {
this.domainInitialisation = domainInitialisation;
this.p2PNetworkSetup = p2PNetworkSetup;
this.walletAppSetup = walletAppSetup;
@ -264,6 +259,8 @@ public class HavenoSetup {
this.refundManager = refundManager;
this.arbitrationManager = arbitrationManager;
xmrWalletService.setHavenoSetup(this);
MemPoolSpaceTxBroadcaster.init(socks5ProxyProvider, preferences, localBitcoinNode);
}
@ -411,9 +408,10 @@ public class HavenoSetup {
return;
}
log.warn("startupTimeout called");
if (walletsManager.areWalletsEncrypted())
walletInitialized.addListener(walletInitializedListener);
else if (displayTorNetworkSettingsHandler != null)
//TODO (niyid) This has a part to play in the display of the password prompt
// if (walletsManager.areWalletsEncrypted())
// walletInitialized.addListener(walletInitializedListener);
if (displayTorNetworkSettingsHandler != null)
displayTorNetworkSettingsHandler.accept(true);
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() {
log.info("Init wallet");
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,
spvFileCorruptedHandler,
getResyncSpvSemaphore(),
showFirstPopupIfResyncSPVRequestedHandler,
showPopupIfInvalidBtcConfigHandler,
walletPasswordHandler,
() -> {
if (allBasicServicesInitialized) {
checkForLockedUpFunds();
@ -867,5 +841,7 @@ public class HavenoSetup {
return p2PNetworkSetup.getP2pNetworkLabelId();
}
public BooleanProperty getWalletInitialized() {
return walletInitialized;
}
}

View file

@ -108,7 +108,6 @@ public class WalletAppSetup {
boolean isSpvResyncRequested,
@Nullable Runnable showFirstPopupIfResyncSPVRequestedHandler,
@Nullable Runnable showPopupIfInvalidBtcConfigHandler,
Runnable walletPasswordHandler,
Runnable downloadCompleteHandler,
Runnable walletInitializedHandler) {
log.info("Initialize WalletAppSetup with BitcoinJ version {} and hash of BitcoinJ commit {}",
@ -171,17 +170,7 @@ public class WalletAppSetup {
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 -> {
if (exception instanceof InvalidHostException && showPopupIfInvalidBtcConfigHandler != null) {

View file

@ -307,7 +307,7 @@ public class EncryptedConnectionList implements PersistableEnvelope, PersistedDa
try {
return Encryption.decrypt(encrypted, secret);
} 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.ResultHandler;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.crypto.KeyCrypter;
import org.bitcoinj.crypto.KeyCrypterScrypt;
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.CoreAccountService;
import bisq.core.api.CoreMoneroConnectionsService;
import bisq.core.app.HavenoSetup;
import bisq.core.btc.listeners.XmrBalanceListener;
import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.model.XmrAddressEntryList;
@ -92,6 +93,7 @@ public class XmrWalletService {
private final CoreMoneroConnectionsService connectionsService;
private final XmrAddressEntryList xmrAddressEntryList;
private final WalletsSetup walletsSetup;
private final File walletDir;
private final File xmrWalletFile;
private final int rpcBindPort;
@ -103,6 +105,8 @@ public class XmrWalletService {
private final Map<String, Optional<MoneroTx>> txCache = new HashMap<String, Optional<MoneroTx>>();
private boolean isShutDown = false;
private HavenoSetup havenoSetup;
@Inject
XmrWalletService(CoreAccountService accountService,
CoreMoneroConnectionsService connectionsService,
@ -148,8 +152,8 @@ public class XmrWalletService {
@Override
public void onPasswordChanged(String oldPassword, String newPassword) {
log.info(getClass() + "accountservice.onPasswordChanged()");
if (oldPassword == null) oldPassword = MONERO_WALLET_RPC_DEFAULT_PASSWORD;
if (newPassword == null) newPassword = MONERO_WALLET_RPC_DEFAULT_PASSWORD;
if (oldPassword == null || oldPassword.isEmpty()) oldPassword = MONERO_WALLET_RPC_DEFAULT_PASSWORD;
if (newPassword == null || newPassword.isEmpty()) newPassword = MONERO_WALLET_RPC_DEFAULT_PASSWORD;
changeWalletPasswords(oldPassword, newPassword);
}
});
@ -383,41 +387,41 @@ public class XmrWalletService {
// verify tx not submitted to pool
MoneroTx tx = daemon.getTx(txHash);
if (tx != null) throw new RuntimeException("Tx is already submitted");
// submit tx to pool
MoneroSubmitTxResult result = daemon.submitTxHex(txHex, true); // TODO (woodser): invert doNotRelay flag to relay for library consistency?
if (!result.isGood()) throw new RuntimeException("Failed to submit tx to daemon: " + JsonUtils.serialize(result));
tx = getTx(txHash);
// verify key images
if (keyImages != null) {
Set<String> txKeyImages = new HashSet<String>();
for (MoneroOutput input : tx.getInputs()) txKeyImages.add(input.getKeyImage().getHex());
if (!txKeyImages.equals(new HashSet<String>(keyImages))) throw new Error("Tx inputs do not match claimed key images");
}
// verify unlock height
if (tx.getUnlockHeight() != 0) throw new RuntimeException("Unlock height must be 0");
// verify trade fee
String feeAddress = HavenoUtils.getTradeFeeAddress();
MoneroCheckTx check = wallet.checkTxKey(txHash, txKey, feeAddress);
if (!check.isGood()) throw new RuntimeException("Invalid proof of trade fee");
if (!check.getReceivedAmount().equals(tradeFee)) throw new RuntimeException("Trade fee is incorrect amount, expected " + tradeFee + " but was " + check.getReceivedAmount());
// verify miner fee
BigInteger feeEstimate = getFeeEstimate(tx.getWeight());
double feeDiff = tx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); // TODO: use BigDecimal?
if (feeDiff > MINER_FEE_TOLERANCE) throw new Error("Miner fee is not within " + (MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + tx.getFee());
log.info("Trade tx fee {} is within tolerance, diff%={}", tx.getFee(), feeDiff);
// verify sufficient security deposit
check = wallet.checkTxKey(txHash, txKey, address);
if (!check.isGood()) throw new RuntimeException("Invalid proof of deposit amount");
BigInteger minSecurityDeposit = new BigDecimal(securityDeposit).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE)).toBigInteger();
BigInteger actualSecurityDeposit = check.getReceivedAmount().subtract(sendAmount);
if (actualSecurityDeposit.compareTo(minSecurityDeposit) < 0) throw new RuntimeException("Security deposit amount is not enough, needed " + minSecurityDeposit + " but was " + actualSecurityDeposit);
// verify deposit amount + miner fee within dust tolerance
BigInteger minDepositAndFee = sendAmount.add(securityDeposit).subtract(new BigDecimal(tx.getFee()).multiply(new BigDecimal(1.0 - DUST_TOLERANCE)).toBigInteger());
BigInteger actualDepositAndFee = check.getReceivedAmount().add(tx.getFee());
@ -558,6 +562,9 @@ public class XmrWalletService {
System.out.println("Monero wallet balance: " + wallet.getBalance(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
wallet.addListener(new XmrWalletListener());
}
@ -961,6 +968,10 @@ public class XmrWalletService {
log.info("\n" + tracePrefix + ":" + sb.toString());
}
public void setHavenoSetup(HavenoSetup havenoSetup) {
this.havenoSetup = havenoSetup;
}
// -------------------------------- HELPERS -------------------------------
/**

View file

@ -17,11 +17,10 @@
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.app.ConsoleInput;
import bisq.core.app.CoreModule;
import bisq.core.app.HavenoHeadlessAppMain;
import bisq.common.UserThread;
import bisq.common.app.AppModule;
@ -32,7 +31,10 @@ import bisq.common.persistence.PersistenceManager;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.Console;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
@ -43,7 +45,7 @@ import lombok.extern.slf4j.Slf4j;
import bisq.daemon.grpc.GrpcServer;
@Slf4j
public class HavenoDaemonMain extends HavenoHeadlessAppMain implements HavenoSetup.HavenoSetupListener {
public class HavenoDaemonMain extends HavenoHeadlessAppMain {
private GrpcServer grpcServer;
@ -139,53 +141,60 @@ public class HavenoDaemonMain extends HavenoHeadlessAppMain implements HavenoSet
* Start the grpcServer to allow logging in remotely.
*/
@Override
protected boolean loginAccount() {
boolean opened = super.loginAccount();
protected CompletableFuture<Boolean> loginAccount() {
CompletableFuture<Boolean> opened = super.loginAccount();
// Start rpc server in case login is coming in from rpc
grpcServer = injector.getInstance(GrpcServer.class);
if (!opened) {
// Nonblocking, we need to stop if the login occurred through rpc.
// TODO: add a mode to mask password
ConsoleInput reader = new ConsoleInput(Integer.MAX_VALUE, Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
Thread t = new Thread(() -> {
interactiveLogin(reader);
});
CompletableFuture<Boolean> inputResult = new CompletableFuture<Boolean>();
try {
if (opened.get()) {
grpcServer.start();
return opened;
} else {
// Handle asynchronous account opens.
// Will need to also close and reopen account.
AccountServiceListener accountListener = new AccountServiceListener() {
@Override public void onAccountCreated() { onLogin(); }
@Override public void onAccountOpened() { onLogin(); }
private void onLogin() {
log.info("Logged in successfully");
reader.cancel(); // closing the reader will stop all read attempts and end the interactive login thread
// Nonblocking, we need to stop if the login occurred through rpc.
// TODO: add a mode to mask password
ConsoleInput reader = new ConsoleInput(Integer.MAX_VALUE, Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
Thread t = new Thread(() -> {
interactiveLogin(reader);
});
// Handle asynchronous account opens.
// Will need to also close and reopen account.
AccountServiceListener accountListener = new AccountServiceListener() {
@Override public void onAccountCreated() { onLogin(); }
@Override public void onAccountOpened() { onLogin(); }
private void onLogin() {
log.info("Logged in successfully");
reader.cancel(); // closing the reader will stop all read attempts and end the interactive login thread
}
};
accountService.addListener(accountListener);
// start server after the listener is registered
grpcServer.start();
try {
// Wait until interactive login or rpc. Check one more time if account is open to close race condition.
if (!accountService.isAccountOpen()) {
log.info("Interactive login required");
t.start();
t.join();
}
} catch (InterruptedException e) {
// expected
}
};
accountService.addListener(accountListener);
// start server after the listener is registered
grpcServer.start();
try {
// Wait until interactive login or rpc. Check one more time if account is open to close race condition.
if (!accountService.isAccountOpen()) {
log.info("Interactive login required");
t.start();
t.join();
}
} catch (InterruptedException e) {
// expected
accountService.removeListener(accountListener);
inputResult.complete(accountService.isAccountOpen());
}
accountService.removeListener(accountListener);
opened = 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.layout.StackPane;
import javafx.geometry.Rectangle2D;
import javafx.geometry.BoundingBox;
import javafx.geometry.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
@ -128,6 +128,7 @@ public class HavenoApp extends Application implements UncaughtExceptionHandler {
}
public void startApplication(Runnable onApplicationStartedHandler) {
log.info("Running startApplication...");
try {
mainView = loadMainView(injector);
mainView.setOnApplicationStartedHandler(onApplicationStartedHandler);
@ -149,7 +150,7 @@ public class HavenoApp extends Application implements UncaughtExceptionHandler {
.show();
new Thread(() -> {
gracefulShutDownHandler.gracefulShutDown(() -> {
log.debug("App shutdown complete");
log.info("App shutdown complete");
});
}).start();
shutDownRequested = true;

View file

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

View file

@ -27,16 +27,15 @@ import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.main.MainView;
import bisq.desktop.main.account.AccountView;
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.util.Layout;
import bisq.desktop.util.validation.PasswordValidator;
import bisq.core.api.CoreAccountService;
import bisq.core.btc.wallet.WalletsManager;
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;
@ -61,6 +60,7 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
private final WalletsManager walletsManager;
private final PasswordValidator passwordValidator;
private final Navigation navigation;
private final CoreAccountService accountService;
private PasswordTextField passwordField;
private PasswordTextField repeatedPasswordField;
@ -77,10 +77,11 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
private PasswordView(WalletsManager walletsManager, PasswordValidator passwordValidator, Navigation navigation) {
private PasswordView(CoreAccountService accountService, WalletsManager walletsManager, PasswordValidator passwordValidator, Navigation navigation) {
this.walletsManager = walletsManager;
this.passwordValidator = passwordValidator;
this.navigation = navigation;
this.accountService = accountService;
}
@Override
@ -141,41 +142,38 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
deriveStatusLabel.setText(Res.get("password.deriveKey"));
busyAnimation.play();
KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt();
ScryptUtil.deriveKeyWithScrypt(keyCrypterScrypt, password, aesKey -> {
deriveStatusLabel.setText("");
busyAnimation.stop();
if (walletsManager.areWalletsEncrypted()) {
if (walletsManager.checkAESKey(aesKey)) {
walletsManager.decryptWallets(aesKey);
new Popup()
.feedback(Res.get("password.walletDecrypted"))
.show();
backupWalletAndResetFields();
} else {
pwButton.setDisable(false);
new Popup()
.warning(Res.get("password.wrongPw"))
.show();
}
} else {
try {
walletsManager.encryptWallets(keyCrypterScrypt, aesKey);
new Popup()
.feedback(Res.get("password.walletEncrypted"))
.show();
backupWalletAndResetFields();
walletsManager.clearBackup();
} catch (Throwable t) {
new Popup()
.warning(Res.get("password.walletEncryptionFailed"))
.show();
}
if (walletsManager.areWalletsEncrypted()) {
try {
accountService.changePassword(password, null);
new Popup()
.feedback(Res.get("password.walletDecrypted"))
.show();
backupWalletAndResetFields();
} catch (Throwable t) {
pwButton.setDisable(false);
new Popup()
.warning(Res.get("password.wrongPw"))
.show();
}
setText();
updatePasswordListeners();
});
} else {
try {
accountService.changePassword(accountService.getPassword(), password);
new Popup()
.feedback(Res.get("password.walletEncrypted"))
.show();
backupWalletAndResetFields();
walletsManager.clearBackup();
} catch (Throwable t) {
new Popup()
.warning(Res.get("password.walletEncryptionFailed"))
.show();
}
}
setText();
updatePasswordListeners();
deriveStatusLabel.setText("");
busyAnimation.stop();
}
private void backupWalletAndResetFields() {

View file

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

View file

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

View file

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

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
// https://stackoverflow.com/questions/20767708/how-do-you-detect-a-retina-display-in-java#20767802
public static boolean isRetina() {

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB