mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-02 12:26:45 +00:00
add lock wallet functionality
This commit is contained in:
parent
8e0b9a3ea0
commit
ea03dece72
13 changed files with 307 additions and 25 deletions
|
@ -137,6 +137,9 @@ public class AppController implements Initializable {
|
|||
@FXML
|
||||
private MenuItem minimizeToTray;
|
||||
|
||||
@FXML
|
||||
private MenuItem lockWallet;
|
||||
|
||||
@FXML
|
||||
private MenuItem refreshWallet;
|
||||
|
||||
|
@ -283,6 +286,7 @@ public class AppController implements Initializable {
|
|||
savePSBT.visibleProperty().bind(saveTransaction.visibleProperty().not());
|
||||
savePSBTBinary.disableProperty().bind(saveTransaction.visibleProperty());
|
||||
exportWallet.setDisable(true);
|
||||
lockWallet.setDisable(true);
|
||||
refreshWallet.disableProperty().bind(Bindings.or(exportWallet.disableProperty(), Bindings.or(serverToggle.disableProperty(), AppServices.onlineProperty().not())));
|
||||
sendToMany.disableProperty().bind(exportWallet.disableProperty());
|
||||
|
||||
|
@ -1140,6 +1144,13 @@ public class AppController implements Initializable {
|
|||
AppServices.get().minimizeStage((Stage)tabs.getScene().getWindow());
|
||||
}
|
||||
|
||||
public void lockWallet(ActionEvent event) {
|
||||
WalletForm selectedWalletForm = getSelectedWalletForm();
|
||||
if(selectedWalletForm != null) {
|
||||
EventManager.get().post(new WalletLockEvent(selectedWalletForm.getMasterWallet()));
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshWallet(ActionEvent event) {
|
||||
WalletForm selectedWalletForm = getSelectedWalletForm();
|
||||
if(selectedWalletForm != null) {
|
||||
|
@ -1189,7 +1200,6 @@ public class AppController implements Initializable {
|
|||
tabLabel.setGraphic(glyph);
|
||||
tabLabel.setGraphicTextGap(5.0);
|
||||
tab.setGraphic(tabLabel);
|
||||
tab.setContextMenu(getTabContextMenu(tab));
|
||||
tab.setClosable(true);
|
||||
tab.setOnCloseRequest(event -> {
|
||||
if(AppServices.getWhirlpoolServices().getWhirlpoolForMixToWallet(((WalletTabData)tab.getUserData()).getWalletForm().getWalletId()) != null) {
|
||||
|
@ -1202,13 +1212,17 @@ public class AppController implements Initializable {
|
|||
|
||||
TabPane subTabs = new TabPane();
|
||||
subTabs.setSide(Side.RIGHT);
|
||||
subTabs.getStyleClass().add("master-only");
|
||||
setSubTabsVisible(subTabs, false);
|
||||
subTabs.rotateGraphicProperty().set(true);
|
||||
tab.setContent(subTabs);
|
||||
|
||||
WalletForm walletForm = addWalletSubTab(subTabs, storage, wallet, backupWallet);
|
||||
TabData tabData = new WalletTabData(TabData.TabType.WALLET, walletForm);
|
||||
tab.setUserData(tabData);
|
||||
tab.setContextMenu(getTabContextMenu(tab));
|
||||
walletForm.lockedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
setSubTabsVisible(subTabs, !newValue && subTabs.getTabs().size() > 1);
|
||||
});
|
||||
|
||||
subTabs.getSelectionModel().selectedItemProperty().addListener((observable, old_val, selectedTab) -> {
|
||||
if(selectedTab != null) {
|
||||
|
@ -1236,8 +1250,7 @@ public class AppController implements Initializable {
|
|||
Label masterLabel = (Label)masterTab.getGraphic();
|
||||
masterLabel.setText(getAutomaticName(wallet.getMasterWallet()));
|
||||
Platform.runLater(() -> {
|
||||
subTabs.getStyleClass().remove("master-only");
|
||||
subTabs.getStyleClass().add("wallet-subtabs");
|
||||
setSubTabsVisible(subTabs, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1247,6 +1260,20 @@ public class AppController implements Initializable {
|
|||
EventManager.get().post(new WalletOpenedEvent(storage, wallet));
|
||||
}
|
||||
|
||||
private void setSubTabsVisible(TabPane subTabs, boolean visible) {
|
||||
if(visible) {
|
||||
subTabs.getStyleClass().remove("master-only");
|
||||
if(!subTabs.getStyleClass().contains("wallet-subtabs")) {
|
||||
subTabs.getStyleClass().add("wallet-subtabs");
|
||||
}
|
||||
} else {
|
||||
if(!subTabs.getStyleClass().contains("master-only")) {
|
||||
subTabs.getStyleClass().add("master-only");
|
||||
}
|
||||
subTabs.getStyleClass().remove("wallet-subtabs");
|
||||
}
|
||||
}
|
||||
|
||||
public WalletForm addWalletSubTab(TabPane subTabs, Storage storage, Wallet wallet, Wallet backupWallet) {
|
||||
try {
|
||||
Tab subTab = new Tab();
|
||||
|
@ -1485,6 +1512,18 @@ public class AppController implements Initializable {
|
|||
private ContextMenu getTabContextMenu(Tab tab) {
|
||||
ContextMenu contextMenu = new ContextMenu();
|
||||
|
||||
if(tab.getUserData() instanceof WalletTabData walletTabData) {
|
||||
MenuItem lock = new MenuItem("Lock");
|
||||
Glyph lockGlyph = new Glyph("FontAwesome", FontAwesome.Glyph.LOCK);
|
||||
lockGlyph.setFontSize(12);
|
||||
lock.setGraphic(lockGlyph);
|
||||
lock.disableProperty().bind(walletTabData.getWalletForm().lockedProperty());
|
||||
lock.setOnAction(event -> {
|
||||
EventManager.get().post(new WalletLockEvent(walletTabData.getWallet()));
|
||||
});
|
||||
contextMenu.getItems().addAll(lock);
|
||||
}
|
||||
|
||||
MenuItem close = new MenuItem("Close");
|
||||
close.setOnAction(event -> {
|
||||
tabs.getTabs().remove(tab);
|
||||
|
@ -1670,6 +1709,7 @@ public class AppController implements Initializable {
|
|||
} else {
|
||||
saveTransaction.setVisible(false);
|
||||
}
|
||||
lockWallet.setDisable(true);
|
||||
exportWallet.setDisable(true);
|
||||
showLoadingLog.setDisable(true);
|
||||
showUtxosChart.setDisable(true);
|
||||
|
@ -1679,6 +1719,7 @@ public class AppController implements Initializable {
|
|||
WalletTabData walletTabData = walletTabEvent.getWalletTabData();
|
||||
saveTransaction.setVisible(true);
|
||||
saveTransaction.setDisable(true);
|
||||
lockWallet.setDisable(walletTabData.getWalletForm().lockedProperty().get());
|
||||
exportWallet.setDisable(walletTabData.getWallet() == null || !walletTabData.getWallet().isValid());
|
||||
showLoadingLog.setDisable(false);
|
||||
showUtxosChart.setDisable(false);
|
||||
|
@ -1714,6 +1755,20 @@ public class AppController implements Initializable {
|
|||
@Subscribe
|
||||
public void newWalletTransactions(NewWalletTransactionsEvent event) {
|
||||
if(Config.get().isNotifyNewTransactions() && getOpenWallets().containsKey(event.getWallet())) {
|
||||
for(Tab tab : tabs.getTabs()) {
|
||||
if(tab.getUserData() instanceof WalletTabData) {
|
||||
TabPane subTabs = (TabPane)tab.getContent();
|
||||
for(Tab subTab : subTabs.getTabs()) {
|
||||
TabData tabData = (TabData)subTab.getUserData();
|
||||
if(tabData instanceof WalletTabData walletTabData) {
|
||||
if(walletTabData.getWallet().equals(event.getWallet()) && walletTabData.getWalletForm().lockedProperty().get()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<BlockTransaction> blockTransactions = new ArrayList<>(event.getBlockTransactions());
|
||||
List<BlockTransaction> whirlpoolTransactions = event.getWhirlpoolMixTransactions();
|
||||
blockTransactions.removeAll(whirlpoolTransactions);
|
||||
|
@ -2172,4 +2227,20 @@ public class AppController implements Initializable {
|
|||
|
||||
addWalletTab(storage, event.getChildWallet(), null);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletLock(WalletLockEvent event) {
|
||||
WalletForm selectedWalletForm = getSelectedWalletForm();
|
||||
if(selectedWalletForm != null && selectedWalletForm.getMasterWallet().equals(event.getWallet())) {
|
||||
lockWallet.setDisable(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletUnlock(WalletUnlockEvent event) {
|
||||
WalletForm selectedWalletForm = getSelectedWalletForm();
|
||||
if(selectedWalletForm != null && selectedWalletForm.getMasterWallet().equals(event.getWallet())) {
|
||||
lockWallet.setDisable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
|
||||
public class WalletLockEvent {
|
||||
private final Wallet wallet;
|
||||
|
||||
public WalletLockEvent(Wallet wallet) {
|
||||
this.wallet = wallet;
|
||||
}
|
||||
|
||||
public Wallet getWallet() {
|
||||
return wallet;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
|
||||
public class WalletUnlockEvent {
|
||||
private final Wallet wallet;
|
||||
|
||||
public WalletUnlockEvent(Wallet wallet) {
|
||||
this.wallet = wallet;
|
||||
}
|
||||
|
||||
public Wallet getWallet() {
|
||||
return wallet;
|
||||
}
|
||||
}
|
|
@ -62,6 +62,10 @@ public class Storage {
|
|||
}
|
||||
|
||||
public boolean isEncrypted() throws IOException {
|
||||
if(!walletFile.exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return persistence.isEncrypted(walletFile);
|
||||
}
|
||||
|
||||
|
@ -531,10 +535,18 @@ public class Storage {
|
|||
public static class KeyDerivationService extends Service<ECKey> {
|
||||
private final Storage storage;
|
||||
private final SecureString password;
|
||||
private final boolean verifyPassword;
|
||||
|
||||
public KeyDerivationService(Storage storage, SecureString password) {
|
||||
this.storage = storage;
|
||||
this.password = password;
|
||||
this.verifyPassword = false;
|
||||
}
|
||||
|
||||
public KeyDerivationService(Storage storage, SecureString password, boolean verifyPassword) {
|
||||
this.storage = storage;
|
||||
this.password = password;
|
||||
this.verifyPassword = verifyPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -542,7 +554,12 @@ public class Storage {
|
|||
return new Task<>() {
|
||||
protected ECKey call() throws IOException, StorageException {
|
||||
try {
|
||||
return storage.getEncryptionKey(password);
|
||||
ECKey encryptionFullKey = storage.getEncryptionKey(password);
|
||||
if(verifyPassword && !ECKey.fromPublicOnly(encryptionFullKey).equals(storage.getEncryptionPubKey())) {
|
||||
throw new InvalidPasswordException("Derived pubkey does not match stored pubkey");
|
||||
}
|
||||
|
||||
return encryptionFullKey;
|
||||
} finally {
|
||||
password.clear();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package com.sparrowwallet.sparrow.wallet;
|
||||
|
||||
public enum Function {
|
||||
TRANSACTIONS, SEND, RECEIVE, ADDRESSES, UTXOS, SETTINGS;
|
||||
TRANSACTIONS, SEND, RECEIVE, ADDRESSES, UTXOS, SETTINGS, LOCK;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.sparrowwallet.sparrow.event.*;
|
|||
import com.sparrowwallet.sparrow.io.Storage;
|
||||
import com.sparrowwallet.sparrow.io.StorageException;
|
||||
import com.sparrowwallet.sparrow.whirlpool.WhirlpoolServices;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.event.ActionEvent;
|
||||
|
@ -35,6 +36,8 @@ import java.net.URL;
|
|||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
|
||||
|
||||
public class SettingsController extends WalletFormController implements Initializable {
|
||||
private static final Logger log = LoggerFactory.getLogger(SettingsController.class);
|
||||
|
||||
|
@ -459,7 +462,7 @@ public class SettingsController extends WalletFormController implements Initiali
|
|||
WalletPasswordDialog dlg = new WalletPasswordDialog(masterWallet.getName(), WalletPasswordDialog.PasswordRequirement.LOAD);
|
||||
Optional<SecureString> password = dlg.showAndWait();
|
||||
if(password.isPresent()) {
|
||||
Storage.KeyDerivationService keyDerivationService = new Storage.KeyDerivationService(walletForm.getStorage(), password.get());
|
||||
Storage.KeyDerivationService keyDerivationService = new Storage.KeyDerivationService(walletForm.getStorage(), password.get(), true);
|
||||
keyDerivationService.setOnSucceeded(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Done"));
|
||||
ECKey encryptionFullKey = keyDerivationService.getValue();
|
||||
|
@ -482,7 +485,14 @@ public class SettingsController extends WalletFormController implements Initiali
|
|||
});
|
||||
keyDerivationService.setOnFailed(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Failed"));
|
||||
AppServices.showErrorDialog("Incorrect Password", keyDerivationService.getException().getMessage());
|
||||
if(keyDerivationService.getException() instanceof InvalidPasswordException) {
|
||||
Optional<ButtonType> optResponse = showErrorDialog("Invalid Password", "The wallet password was invalid. Try again?", ButtonType.CANCEL, ButtonType.OK);
|
||||
if(optResponse.isPresent() && optResponse.get().equals(ButtonType.OK)) {
|
||||
Platform.runLater(() -> addAccount(null));
|
||||
}
|
||||
} else {
|
||||
log.error("Error deriving wallet key", keyDerivationService.getException());
|
||||
}
|
||||
});
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.START, "Decrypting wallet..."));
|
||||
keyDerivationService.start();
|
||||
|
|
|
@ -30,6 +30,7 @@ import javafx.event.ActionEvent;
|
|||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.control.TreeItem;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
@ -47,6 +48,8 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
|
||||
|
||||
public class UtxosController extends WalletFormController implements Initializable {
|
||||
private static final Logger log = LoggerFactory.getLogger(UtxosController.class);
|
||||
|
||||
|
@ -234,7 +237,7 @@ public class UtxosController extends WalletFormController implements Initializab
|
|||
WalletPasswordDialog dlg = new WalletPasswordDialog(wallet.getMasterName(), WalletPasswordDialog.PasswordRequirement.LOAD);
|
||||
Optional<SecureString> password = dlg.showAndWait();
|
||||
if(password.isPresent()) {
|
||||
Storage.KeyDerivationService keyDerivationService = new Storage.KeyDerivationService(walletForm.getStorage(), password.get());
|
||||
Storage.KeyDerivationService keyDerivationService = new Storage.KeyDerivationService(walletForm.getStorage(), password.get(), true);
|
||||
keyDerivationService.setOnSucceeded(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Done"));
|
||||
ECKey encryptionFullKey = keyDerivationService.getValue();
|
||||
|
@ -259,7 +262,14 @@ public class UtxosController extends WalletFormController implements Initializab
|
|||
});
|
||||
keyDerivationService.setOnFailed(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Failed"));
|
||||
AppServices.showErrorDialog("Incorrect Password", keyDerivationService.getException().getMessage());
|
||||
if(keyDerivationService.getException() instanceof InvalidPasswordException) {
|
||||
Optional<ButtonType> optResponse = showErrorDialog("Invalid Password", "The wallet password was invalid. Try again?", ButtonType.CANCEL, ButtonType.OK);
|
||||
if(optResponse.isPresent() && optResponse.get().equals(ButtonType.OK)) {
|
||||
Platform.runLater(() -> previewPremix(tx0Preview, utxoEntries));
|
||||
}
|
||||
} else {
|
||||
log.error("Error deriving wallet key", keyDerivationService.getException());
|
||||
}
|
||||
});
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.START, "Decrypting wallet..."));
|
||||
keyDerivationService.start();
|
||||
|
|
|
@ -1,33 +1,54 @@
|
|||
package com.sparrowwallet.sparrow.wallet;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.sparrowwallet.drongo.SecureString;
|
||||
import com.sparrowwallet.drongo.crypto.InvalidPasswordException;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.ReceiveActionEvent;
|
||||
import com.sparrowwallet.sparrow.event.SendActionEvent;
|
||||
import com.sparrowwallet.sparrow.event.WalletAddressesChangedEvent;
|
||||
import com.sparrowwallet.sparrow.event.*;
|
||||
import com.sparrowwallet.sparrow.io.Storage;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Toggle;
|
||||
import javafx.scene.control.ToggleButton;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.controlsfx.control.textfield.CustomPasswordField;
|
||||
import org.controlsfx.control.textfield.TextFields;
|
||||
import org.controlsfx.glyphfont.FontAwesome;
|
||||
import org.controlsfx.glyphfont.Glyph;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
|
||||
|
||||
public class WalletController extends WalletFormController implements Initializable {
|
||||
private static final Logger log = LoggerFactory.getLogger(WalletController.class);
|
||||
|
||||
@FXML
|
||||
private StackPane walletPane;
|
||||
|
||||
@FXML
|
||||
private VBox walletMenuBox;
|
||||
|
||||
@FXML
|
||||
private ToggleGroup walletMenu;
|
||||
|
||||
private BorderPane lockPane;
|
||||
|
||||
private final BooleanProperty walletEncryptedProperty = new SimpleBooleanProperty(false);
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
EventManager.get().register(this);
|
||||
|
@ -47,7 +68,7 @@ public class WalletController extends WalletFormController implements Initializa
|
|||
if(walletFunction.getUserData().equals(function)) {
|
||||
existing = true;
|
||||
walletFunction.setViewOrder(0);
|
||||
} else {
|
||||
} else if(function != Function.LOCK) {
|
||||
walletFunction.setViewOrder(1);
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +99,9 @@ public class WalletController extends WalletFormController implements Initializa
|
|||
toggleButton.managedProperty().bind(toggleButton.visibleProperty());
|
||||
}
|
||||
|
||||
walletMenuBox.managedProperty().bind(walletMenuBox.visibleProperty());
|
||||
walletMenuBox.visibleProperty().bind(getWalletForm().lockedProperty().not());
|
||||
|
||||
configure(walletForm.getWallet());
|
||||
}
|
||||
|
||||
|
@ -111,6 +135,76 @@ public class WalletController extends WalletFormController implements Initializa
|
|||
});
|
||||
}
|
||||
|
||||
private void initializeLockScreen() {
|
||||
lockPane = new BorderPane();
|
||||
lockPane.setUserData(Function.LOCK);
|
||||
lockPane.getStyleClass().add("wallet-pane");
|
||||
VBox vBox = new VBox(20);
|
||||
vBox.setAlignment(Pos.CENTER);
|
||||
Glyph lock = new Glyph("FontAwesome", FontAwesome.Glyph.LOCK);
|
||||
lock.setFontSize(80);
|
||||
vBox.getChildren().add(lock);
|
||||
Label label = new Label("Enter password to unlock:");
|
||||
label.managedProperty().bind(label.visibleProperty());
|
||||
label.visibleProperty().bind(walletEncryptedProperty);
|
||||
CustomPasswordField passwordField = (CustomPasswordField)TextFields.createClearablePasswordField();
|
||||
passwordField.setMaxWidth(300);
|
||||
passwordField.managedProperty().bind(passwordField.visibleProperty());
|
||||
passwordField.visibleProperty().bind(walletEncryptedProperty);
|
||||
passwordField.setOnAction(event -> {
|
||||
unlockWallet(passwordField);
|
||||
});
|
||||
Button unlockButton = new Button("Unlock");
|
||||
unlockButton.setPrefWidth(300);
|
||||
unlockButton.setOnAction(event -> {
|
||||
unlockWallet(passwordField);
|
||||
});
|
||||
vBox.getChildren().addAll(label, passwordField, unlockButton);
|
||||
StackPane stackPane = new StackPane();
|
||||
stackPane.getChildren().add(vBox);
|
||||
lockPane.setCenter(stackPane);
|
||||
walletPane.getChildren().add(lockPane);
|
||||
}
|
||||
|
||||
private void unlockWallet(CustomPasswordField passwordField) {
|
||||
if(walletEncryptedProperty.get()) {
|
||||
String walletId = walletForm.getWalletId();
|
||||
SecureString password = new SecureString(passwordField.getText());
|
||||
Storage.KeyDerivationService keyDerivationService = new Storage.KeyDerivationService(walletForm.getStorage(), password, true);
|
||||
keyDerivationService.setOnSucceeded(workerStateEvent -> {
|
||||
passwordField.clear();
|
||||
password.clear();
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Done"));
|
||||
unlockWallet();
|
||||
});
|
||||
keyDerivationService.setOnFailed(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Failed"));
|
||||
if(keyDerivationService.getException() instanceof InvalidPasswordException) {
|
||||
showErrorDialog("Invalid Password", "The wallet password was invalid.");
|
||||
} else {
|
||||
log.error("Error deriving wallet key", keyDerivationService.getException());
|
||||
}
|
||||
});
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.START, "Decrypting wallet..."));
|
||||
keyDerivationService.start();
|
||||
} else {
|
||||
unlockWallet();
|
||||
}
|
||||
}
|
||||
|
||||
private void unlockWallet() {
|
||||
Wallet masterWallet = getWalletForm().getWallet().isMasterWallet() ? getWalletForm().getWallet() : getWalletForm().getWallet().getMasterWallet();
|
||||
EventManager.get().post(new WalletUnlockEvent(masterWallet));
|
||||
}
|
||||
|
||||
private void updateWalletEncryptedStatus() {
|
||||
try {
|
||||
walletEncryptedProperty.set(getWalletForm().getStorage().isEncrypted());
|
||||
} catch(IOException e) {
|
||||
log.warn("Error determining if wallet is locked", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletAddressesChanged(WalletAddressesChangedEvent event) {
|
||||
if(event.getWalletId().equals(walletForm.getWalletId())) {
|
||||
|
@ -118,6 +212,13 @@ public class WalletController extends WalletFormController implements Initializa
|
|||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletSettingsChanged(WalletSettingsChangedEvent event) {
|
||||
if(event.getWalletId().equals(walletForm.getWalletId())) {
|
||||
Platform.runLater(this::updateWalletEncryptedStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void receiveAction(ReceiveActionEvent event) {
|
||||
if(event.getWallet().equals(walletForm.getWallet())) {
|
||||
|
@ -131,4 +232,27 @@ public class WalletController extends WalletFormController implements Initializa
|
|||
selectFunction(Function.SEND);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletLock(WalletLockEvent event) {
|
||||
if(event.getWallet().equals(walletForm.getMasterWallet())) {
|
||||
if(lockPane == null) {
|
||||
updateWalletEncryptedStatus();
|
||||
initializeLockScreen();
|
||||
}
|
||||
|
||||
getWalletForm().setLocked(true);
|
||||
lockPane.setViewOrder(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletUnlock(WalletUnlockEvent event) {
|
||||
if(event.getWallet().equals(walletForm.getMasterWallet())) {
|
||||
getWalletForm().setLocked(false);
|
||||
if(lockPane != null) {
|
||||
lockPane.setViewOrder(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ import com.sparrowwallet.sparrow.net.ElectrumServer;
|
|||
import com.sparrowwallet.sparrow.io.Storage;
|
||||
import com.sparrowwallet.sparrow.net.ServerType;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.util.Duration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -37,6 +39,8 @@ public class WalletForm {
|
|||
|
||||
private ElectrumServer.TransactionMempoolService transactionMempoolService;
|
||||
|
||||
private final BooleanProperty lockedProperty = new SimpleBooleanProperty(false);
|
||||
|
||||
public WalletForm(Storage storage, Wallet currentWallet, Wallet backupWallet) {
|
||||
this(storage, currentWallet, backupWallet, true);
|
||||
}
|
||||
|
@ -58,6 +62,10 @@ public class WalletForm {
|
|||
return wallet;
|
||||
}
|
||||
|
||||
public Wallet getMasterWallet() {
|
||||
return wallet.isMasterWallet() ? wallet : wallet.getMasterWallet();
|
||||
}
|
||||
|
||||
public Storage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
@ -298,6 +306,14 @@ public class WalletForm {
|
|||
return walletUtxosEntry;
|
||||
}
|
||||
|
||||
public BooleanProperty lockedProperty() {
|
||||
return lockedProperty;
|
||||
}
|
||||
|
||||
public void setLocked(boolean locked) {
|
||||
this.lockedProperty.set(locked);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletDataChanged(WalletDataChangedEvent event) {
|
||||
if(event.getWallet().equals(wallet)) {
|
||||
|
|
|
@ -177,12 +177,15 @@ public class SparrowDataSource extends WalletResponseDataSource {
|
|||
static Wallet getWallet(String zpub) {
|
||||
return AppServices.get().getOpenWallets().keySet().stream()
|
||||
.filter(wallet -> {
|
||||
List<ExtendedKey.Header> headers = ExtendedKey.Header.getHeaders(Network.get());
|
||||
ExtendedKey.Header header = headers.stream().filter(head -> head.getDefaultScriptType().equals(wallet.getScriptType()) && !head.isPrivateKey()).findFirst().orElse(ExtendedKey.Header.xpub);
|
||||
ExtendedKey extPubKey = wallet.getKeystores().get(0).getExtendedPublicKey();
|
||||
return extPubKey.toString(header).equals(zpub);
|
||||
try {
|
||||
List<ExtendedKey.Header> headers = ExtendedKey.Header.getHeaders(Network.get());
|
||||
ExtendedKey.Header header = headers.stream().filter(head -> head.getDefaultScriptType().equals(wallet.getScriptType()) && !head.isPrivateKey()).findFirst().orElse(ExtendedKey.Header.xpub);
|
||||
ExtendedKey extPubKey = wallet.getKeystores().get(0).getExtendedPublicKey();
|
||||
return extPubKey.toString(header).equals(zpub);
|
||||
} catch(Exception e) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.filter(Wallet::isValid)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
<CheckMenuItem fx:id="showTxHex" mnemonicParsing="false" text="Show Transaction Hex" onAction="#showTxHex"/>
|
||||
<SeparatorMenuItem />
|
||||
<MenuItem fx:id="minimizeToTray" mnemonicParsing="false" text="Minimize to System Tray" accelerator="Shortcut+Y" onAction="#minimizeToTray"/>
|
||||
<MenuItem fx:id="lockWallet" mnemonicParsing="false" text="Lock Wallet" accelerator="Shortcut+L" onAction="#lockWallet"/>
|
||||
<MenuItem fx:id="refreshWallet" mnemonicParsing="false" text="Refresh Wallet" accelerator="Shortcut+R" onAction="#refreshWallet"/>
|
||||
</items>
|
||||
</Menu>
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="EXCLAMATION_TRIANGLE" styleClass="future-warning" />
|
||||
</graphic>
|
||||
<tooltip>
|
||||
<Tooltip text="Future block specified - transaction will not be mined until this block"/>
|
||||
<Tooltip text="Future block specified - transaction cannot be broadcast until this block height"/>
|
||||
</tooltip>
|
||||
</Label>
|
||||
</Field>
|
||||
|
@ -99,7 +99,7 @@
|
|||
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="EXCLAMATION_TRIANGLE" styleClass="future-warning" />
|
||||
</graphic>
|
||||
<tooltip>
|
||||
<Tooltip text="Future date specified - transaction will not be mined until this date"/>
|
||||
<Tooltip text="Future date specified - transaction cannot be broadcast until this date"/>
|
||||
</tooltip>
|
||||
</Label>
|
||||
</Field>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<BorderPane stylesheets="@wallet.css, @../general.css" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.sparrowwallet.sparrow.wallet.WalletController">
|
||||
<left>
|
||||
<VBox styleClass="list-menu">
|
||||
<VBox fx:id="walletMenuBox" styleClass="list-menu">
|
||||
<ToggleButton VBox.vgrow="ALWAYS" text="Transactions" contentDisplay="TOP" styleClass="list-item" maxHeight="Infinity">
|
||||
<toggleGroup>
|
||||
<ToggleGroup fx:id="walletMenu" />
|
||||
|
|
Loading…
Reference in a new issue