mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-22 19:49:32 +00:00
support password prompt in legacy ui
Co-authored-by: niyid <neeyeed@gmail.com>
This commit is contained in:
parent
99653fe40d
commit
4dde53f0e8
19 changed files with 357 additions and 272 deletions
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
readAllPersisted(this::startApplication);
|
loginFuture.whenComplete((result, throwable) -> {
|
||||||
} else {
|
if (throwable != null) {
|
||||||
log.warn("Running application in readonly mode");
|
log.error("Error logging in to account", throwable);
|
||||||
startApplication();
|
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.
|
* @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;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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
|
||||||
|
@ -215,30 +210,30 @@ public class HavenoSetup {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public HavenoSetup(DomainInitialisation domainInitialisation,
|
public HavenoSetup(DomainInitialisation domainInitialisation,
|
||||||
P2PNetworkSetup p2PNetworkSetup,
|
P2PNetworkSetup p2PNetworkSetup,
|
||||||
WalletAppSetup walletAppSetup,
|
WalletAppSetup walletAppSetup,
|
||||||
WalletsManager walletsManager,
|
WalletsManager walletsManager,
|
||||||
WalletsSetup walletsSetup,
|
WalletsSetup walletsSetup,
|
||||||
XmrWalletService xmrWalletService,
|
XmrWalletService xmrWalletService,
|
||||||
BtcWalletService btcWalletService,
|
BtcWalletService btcWalletService,
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
PrivateNotificationManager privateNotificationManager,
|
PrivateNotificationManager privateNotificationManager,
|
||||||
SignedWitnessStorageService signedWitnessStorageService,
|
SignedWitnessStorageService signedWitnessStorageService,
|
||||||
TradeManager tradeManager,
|
TradeManager tradeManager,
|
||||||
OpenOfferManager openOfferManager,
|
OpenOfferManager openOfferManager,
|
||||||
Preferences preferences,
|
Preferences preferences,
|
||||||
User user,
|
User user,
|
||||||
AlertManager alertManager,
|
AlertManager alertManager,
|
||||||
Config config,
|
Config config,
|
||||||
AccountAgeWitnessService accountAgeWitnessService,
|
AccountAgeWitnessService accountAgeWitnessService,
|
||||||
TorSetup torSetup,
|
TorSetup torSetup,
|
||||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
||||||
LocalBitcoinNode localBitcoinNode,
|
LocalBitcoinNode localBitcoinNode,
|
||||||
AppStartupState appStartupState,
|
AppStartupState appStartupState,
|
||||||
Socks5ProxyProvider socks5ProxyProvider,
|
Socks5ProxyProvider socks5ProxyProvider,
|
||||||
MediationManager mediationManager,
|
MediationManager mediationManager,
|
||||||
RefundManager refundManager,
|
RefundManager refundManager,
|
||||||
ArbitrationManager arbitrationManager) {
|
ArbitrationManager arbitrationManager) {
|
||||||
this.domainInitialisation = domainInitialisation;
|
this.domainInitialisation = domainInitialisation;
|
||||||
this.p2PNetworkSetup = p2PNetworkSetup;
|
this.p2PNetworkSetup = p2PNetworkSetup;
|
||||||
this.walletAppSetup = walletAppSetup;
|
this.walletAppSetup = walletAppSetup;
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
walletInitializedHandler.run();
|
||||||
if (walletsManager.areWalletsEncrypted() && !coreContext.isApiUser()) {
|
|
||||||
walletPasswordHandler.run();
|
|
||||||
} else {
|
|
||||||
if (isSpvResyncRequested && !coreContext.isApiUser()) {
|
|
||||||
if (showFirstPopupIfResyncSPVRequestedHandler != null)
|
|
||||||
showFirstPopupIfResyncSPVRequestedHandler.run();
|
|
||||||
} else {
|
|
||||||
walletInitializedHandler.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
exception -> {
|
exception -> {
|
||||||
if (exception instanceof InvalidHostException && showPopupIfInvalidBtcConfigHandler != null) {
|
if (exception instanceof InvalidHostException && showPopupIfInvalidBtcConfigHandler != null) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -383,41 +387,41 @@ public class XmrWalletService {
|
||||||
// verify tx not submitted to pool
|
// verify tx not submitted to pool
|
||||||
MoneroTx tx = daemon.getTx(txHash);
|
MoneroTx tx = daemon.getTx(txHash);
|
||||||
if (tx != null) throw new RuntimeException("Tx is already submitted");
|
if (tx != null) throw new RuntimeException("Tx is already submitted");
|
||||||
|
|
||||||
// submit tx to pool
|
// submit tx to pool
|
||||||
MoneroSubmitTxResult result = daemon.submitTxHex(txHex, true); // TODO (woodser): invert doNotRelay flag to relay for library consistency?
|
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));
|
if (!result.isGood()) throw new RuntimeException("Failed to submit tx to daemon: " + JsonUtils.serialize(result));
|
||||||
tx = getTx(txHash);
|
tx = getTx(txHash);
|
||||||
|
|
||||||
// verify key images
|
// verify key images
|
||||||
if (keyImages != null) {
|
if (keyImages != null) {
|
||||||
Set<String> txKeyImages = new HashSet<String>();
|
Set<String> txKeyImages = new HashSet<String>();
|
||||||
for (MoneroOutput input : tx.getInputs()) txKeyImages.add(input.getKeyImage().getHex());
|
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");
|
if (!txKeyImages.equals(new HashSet<String>(keyImages))) throw new Error("Tx inputs do not match claimed key images");
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify unlock height
|
// verify unlock height
|
||||||
if (tx.getUnlockHeight() != 0) throw new RuntimeException("Unlock height must be 0");
|
if (tx.getUnlockHeight() != 0) throw new RuntimeException("Unlock height must be 0");
|
||||||
|
|
||||||
// verify trade fee
|
// verify trade fee
|
||||||
String feeAddress = HavenoUtils.getTradeFeeAddress();
|
String feeAddress = HavenoUtils.getTradeFeeAddress();
|
||||||
MoneroCheckTx check = wallet.checkTxKey(txHash, txKey, feeAddress);
|
MoneroCheckTx check = wallet.checkTxKey(txHash, txKey, feeAddress);
|
||||||
if (!check.isGood()) throw new RuntimeException("Invalid proof of trade fee");
|
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());
|
if (!check.getReceivedAmount().equals(tradeFee)) throw new RuntimeException("Trade fee is incorrect amount, expected " + tradeFee + " but was " + check.getReceivedAmount());
|
||||||
|
|
||||||
// verify miner fee
|
// verify miner fee
|
||||||
BigInteger feeEstimate = getFeeEstimate(tx.getWeight());
|
BigInteger feeEstimate = getFeeEstimate(tx.getWeight());
|
||||||
double feeDiff = tx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); // TODO: use BigDecimal?
|
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());
|
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);
|
log.info("Trade tx fee {} is within tolerance, diff%={}", tx.getFee(), feeDiff);
|
||||||
|
|
||||||
// verify sufficient security deposit
|
// verify sufficient security deposit
|
||||||
check = wallet.checkTxKey(txHash, txKey, address);
|
check = wallet.checkTxKey(txHash, txKey, address);
|
||||||
if (!check.isGood()) throw new RuntimeException("Invalid proof of deposit amount");
|
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 minSecurityDeposit = new BigDecimal(securityDeposit).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE)).toBigInteger();
|
||||||
BigInteger actualSecurityDeposit = check.getReceivedAmount().subtract(sendAmount);
|
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);
|
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
|
// 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 minDepositAndFee = sendAmount.add(securityDeposit).subtract(new BigDecimal(tx.getFee()).multiply(new BigDecimal(1.0 - DUST_TOLERANCE)).toBigInteger());
|
||||||
BigInteger actualDepositAndFee = check.getReceivedAmount().add(tx.getFee());
|
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 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 -------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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,53 +141,60 @@ 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>();
|
||||||
// Nonblocking, we need to stop if the login occurred through rpc.
|
try {
|
||||||
// TODO: add a mode to mask password
|
if (opened.get()) {
|
||||||
ConsoleInput reader = new ConsoleInput(Integer.MAX_VALUE, Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
|
grpcServer.start();
|
||||||
Thread t = new Thread(() -> {
|
return opened;
|
||||||
interactiveLogin(reader);
|
} else {
|
||||||
});
|
|
||||||
|
|
||||||
// Handle asynchronous account opens.
|
// Nonblocking, we need to stop if the login occurred through rpc.
|
||||||
// Will need to also close and reopen account.
|
// TODO: add a mode to mask password
|
||||||
AccountServiceListener accountListener = new AccountServiceListener() {
|
ConsoleInput reader = new ConsoleInput(Integer.MAX_VALUE, Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
|
||||||
@Override public void onAccountCreated() { onLogin(); }
|
Thread t = new Thread(() -> {
|
||||||
@Override public void onAccountOpened() { onLogin(); }
|
interactiveLogin(reader);
|
||||||
private void onLogin() {
|
});
|
||||||
log.info("Logged in successfully");
|
|
||||||
reader.cancel(); // closing the reader will stop all read attempts and end the interactive login thread
|
// 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
|
accountService.removeListener(accountListener);
|
||||||
grpcServer.start();
|
inputResult.complete(accountService.isAccountOpen());
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
accountService.removeListener(accountListener);
|
inputResult.completeExceptionally(e);
|
||||||
opened = accountService.isAccountOpen();
|
|
||||||
} else {
|
|
||||||
grpcServer.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return opened;
|
return inputResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,41 +142,38 @@ 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();
|
if (walletsManager.areWalletsEncrypted()) {
|
||||||
ScryptUtil.deriveKeyWithScrypt(keyCrypterScrypt, password, aesKey -> {
|
try {
|
||||||
deriveStatusLabel.setText("");
|
accountService.changePassword(password, null);
|
||||||
busyAnimation.stop();
|
new Popup()
|
||||||
|
.feedback(Res.get("password.walletDecrypted"))
|
||||||
if (walletsManager.areWalletsEncrypted()) {
|
.show();
|
||||||
if (walletsManager.checkAESKey(aesKey)) {
|
backupWalletAndResetFields();
|
||||||
walletsManager.decryptWallets(aesKey);
|
} catch (Throwable t) {
|
||||||
new Popup()
|
pwButton.setDisable(false);
|
||||||
.feedback(Res.get("password.walletDecrypted"))
|
new Popup()
|
||||||
.show();
|
.warning(Res.get("password.wrongPw"))
|
||||||
backupWalletAndResetFields();
|
.show();
|
||||||
} 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setText();
|
} else {
|
||||||
updatePasswordListeners();
|
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() {
|
private void backupWalletAndResetFields() {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"));
|
hide();
|
||||||
ScryptUtil.deriveKeyWithScrypt(keyCrypterScrypt, password, aesKey -> {
|
} catch (IncorrectPasswordException e2) {
|
||||||
if (walletsManager.checkAESKey(aesKey)) {
|
busyAnimation.stop();
|
||||||
if (aesKeyHandler != null)
|
deriveStatusLabel.setText("");
|
||||||
aesKeyHandler.onAesKey(aesKey);
|
new Popup()
|
||||||
|
.warning(Res.get("password.wrongPw"))
|
||||||
hide();
|
.onClose(this::blurAgain).show();
|
||||||
} 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.");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
BIN
desktop/src/main/resources/images/lock.png
Normal file
BIN
desktop/src/main/resources/images/lock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
Loading…
Reference in a new issue