diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index 22c4c073..7cd6a3d7 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -1283,7 +1283,7 @@ public class AppController implements Initializable { } public void importWallet(ActionEvent event) { - List selectedWalletForms = getSelectedWalletForms(); + List selectedWalletForms = getSelectedWalletForms().stream().filter(walletForm -> !walletForm.isLocked()).collect(Collectors.toList()); WalletImportDialog dlg = new WalletImportDialog(selectedWalletForms); dlg.initOwner(rootStack.getScene().getWindow()); Optional> optionalWallets = dlg.showAndWait(); @@ -1558,7 +1558,7 @@ public class AppController implements Initializable { public void sweepPrivateKey(ActionEvent event) { Wallet wallet = null; WalletForm selectedWalletForm = getSelectedWalletForm(); - if(selectedWalletForm != null && selectedWalletForm.getWallet().isValid()) { + if(selectedWalletForm != null && selectedWalletForm.getWallet().isValid() && !selectedWalletForm.isLocked()) { wallet = selectedWalletForm.getWallet(); } @@ -3439,6 +3439,7 @@ public class AppController implements Initializable { if(selectedWalletForm != null && selectedWalletForm.getMasterWallet().equals(event.getWallet())) { lockWallet.setDisable(true); exportWallet.setDisable(true); + showPayNym.setDisable(true); } lockAllWallets.setDisable(allWalletsLocked(event.getWallet())); @@ -3450,6 +3451,7 @@ public class AppController implements Initializable { if(selectedWalletForm != null && selectedWalletForm.getMasterWallet().equals(event.getWallet())) { lockWallet.setDisable(false); exportWallet.setDisable(!event.getWallet().isValid()); + showPayNym.setDisable(exportWallet.isDisable() || !selectedWalletForm.getWallet().hasPaymentCode()); lockAllWallets.setDisable(false); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/terminal/MasterActionListBox.java b/src/main/java/com/sparrowwallet/sparrow/terminal/MasterActionListBox.java index 5a9c7caa..2e9b7f14 100644 --- a/src/main/java/com/sparrowwallet/sparrow/terminal/MasterActionListBox.java +++ b/src/main/java/com/sparrowwallet/sparrow/terminal/MasterActionListBox.java @@ -97,6 +97,7 @@ public class MasterActionListBox extends ActionListBox { private static void openLoadedWallet(Storage storage, Wallet wallet) { if(SparrowTerminal.get().isLocked(storage)) { String walletId = storage.getWalletId(wallet); + int lockCount = SparrowTerminal.get().getLockCount(storage); TextInputDialogBuilder builder = new TextInputDialogBuilder().setTitle("Wallet Password"); builder.setDescription("Enter the wallet password:"); @@ -108,8 +109,9 @@ public class MasterActionListBox extends ActionListBox { Storage.KeyDerivationService keyDerivationService = new Storage.KeyDerivationService(storage, new SecureString(password), true); keyDerivationService.setOnSucceeded(workerStateEvent -> { EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Done")); - SparrowTerminal.get().unlockWallet(storage); - SparrowTerminal.get().getGuiThread().invokeLater(() -> LoadWallet.getOpeningDialog(storage, wallet).showDialog(SparrowTerminal.get().getGui())); + if(SparrowTerminal.get().unlockWallet(storage, lockCount)) { + SparrowTerminal.get().getGuiThread().invokeLater(() -> LoadWallet.getOpeningDialog(storage, wallet).showDialog(SparrowTerminal.get().getGui())); + } }); keyDerivationService.setOnFailed(workerStateEvent -> { EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Failed")); diff --git a/src/main/java/com/sparrowwallet/sparrow/terminal/SparrowTerminal.java b/src/main/java/com/sparrowwallet/sparrow/terminal/SparrowTerminal.java index ab94f63e..b78bf40b 100644 --- a/src/main/java/com/sparrowwallet/sparrow/terminal/SparrowTerminal.java +++ b/src/main/java/com/sparrowwallet/sparrow/terminal/SparrowTerminal.java @@ -41,6 +41,7 @@ public class SparrowTerminal extends Application { private final Map walletData = new HashMap<>(); private final Set lockedWallets = new HashSet<>(); + private final Map lockCounts = new HashMap<>(); private static final javafx.stage.Window DEFAULT_WINDOW = new Window() { }; @@ -157,15 +158,20 @@ public class SparrowTerminal extends Application { EventManager.get().post(new WalletOpenedEvent(storage, wallet)); } - public boolean isLocked(Storage storage) { + public synchronized boolean isLocked(Storage storage) { return lockedWallets.contains(storage.getWalletFile()); } - public void lockWallet(Storage storage) { + public synchronized void lockWallet(Storage storage) { lockedWallets.add(storage.getWalletFile()); + lockCounts.merge(storage.getWalletFile(), 1, Integer::sum); } - public void unlockWallet(Storage storage) { - lockedWallets.remove(storage.getWalletFile()); + public synchronized int getLockCount(Storage storage) { + return lockCounts.getOrDefault(storage.getWalletFile(), 0); + } + + public synchronized boolean unlockWallet(Storage storage, int lockCount) { + return lockCount == getLockCount(storage) && lockedWallets.remove(storage.getWalletFile()); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/SettingsDialog.java b/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/SettingsDialog.java index c7a93d6b..b2b156cc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/SettingsDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/SettingsDialog.java @@ -240,7 +240,7 @@ public class SettingsDialog extends WalletDialog { Key existingKey = key; key = null; SparrowTerminal.get().getGuiThread().invokeLater(() -> { - boolean saving = saveWallet(true, false); + boolean saving = !SparrowTerminal.get().isLocked(walletForm.getStorage()) && saveWallet(true, false); Platform.runLater(() -> { //If a new password is not provided, re-encrypt with the existing key rather than leaving the wallet decrypted for the session if(!saving) { diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java index 34bb53be..00e9c896 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java @@ -508,6 +508,12 @@ public class KeystoreController extends WalletFormController implements Initiali Storage.DecryptWalletService decryptWalletService = new Storage.DecryptWalletService(copy, password.get()); decryptWalletService.setOnSucceeded(workerStateEvent -> { EventManager.get().post(new StorageEvent(getWalletForm().getWalletId(), TimedEvent.Action.END, "Done")); + + if(getWalletForm().isLocked()) { + AppServices.showErrorDialog("Wallet Locked", "The wallet was locked before the keystore could be displayed."); + return; + } + Wallet decryptedWallet = decryptWalletService.getValue(); showPrivate(decryptedWallet.getKeystores().get(keystoreIndex)); }); diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java index a5cf7b97..af5f64bf 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java @@ -1046,7 +1046,12 @@ public class SettingsController extends WalletFormController implements Initiali WalletPasswordDialog dlg = new WalletPasswordDialog(null, requirement, suggestChangePassword); dlg.initOwner(apply.getScene().getWindow()); - Optional password = dlg.showAndWait(); + Optional password = walletForm.isLocked() ? Optional.empty() : dlg.showAndWait(); + if(password.isPresent() && walletForm.isLocked()) { + AppServices.showErrorDialog("Wallet Locked", "The wallet was locked before the password was entered. Unlock the wallet and apply the change again."); + password = Optional.empty(); + } + if(password.isPresent()) { if(dlg.isBackupExisting()) { try { diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsWalletForm.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsWalletForm.java index cb237e5d..cb46be47 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsWalletForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsWalletForm.java @@ -10,6 +10,7 @@ import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.event.*; import com.sparrowwallet.sparrow.io.Storage; import com.sparrowwallet.sparrow.io.StorageException; +import javafx.beans.property.BooleanProperty; import java.io.IOException; import java.util.ArrayList; @@ -46,6 +47,21 @@ public class SettingsWalletForm extends WalletForm { return appWalletForm; } + @Override + public boolean isLocked() { + return appWalletForm.isLocked(); + } + + @Override + public BooleanProperty lockedProperty() { + return appWalletForm.lockedProperty(); + } + + @Override + public void setLocked(boolean locked) { + appWalletForm.setLocked(locked); + } + @Override public void revert() { this.walletCopy = super.getWallet().copy(); diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java index f96e84ee..01af2312 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java @@ -54,6 +54,8 @@ public class WalletController extends WalletFormController implements Initializa private CustomPasswordField passwordField; + private int lockCount; + private final BooleanProperty walletEncryptedProperty = new SimpleBooleanProperty(false); private final ChangeListener lockFocusListener = new ChangeListener<>() { @@ -195,12 +197,15 @@ public class WalletController extends WalletFormController implements Initializa if(walletEncryptedProperty.get()) { String walletId = walletForm.getWalletId(); SecureString password = new SecureString(passwordField.getText()); + int lockCount = this.lockCount; 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(); + if(lockCount == this.lockCount) { + unlockWallet(); + } }); keyDerivationService.setOnFailed(workerStateEvent -> { EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Failed")); @@ -251,6 +256,7 @@ public class WalletController extends WalletFormController implements Initializa getWalletForm().setLocked(true); lockPane.setViewOrder(-1); + lockCount++; } }