apply the wallet lock to label import, sweep and show paynym, and to password prompts and unlocks completing after the wallet was locked

This commit is contained in:
Craig Raw
2026-10-01 13:34:06 +02:00
parent 17ab7f08ec
commit 26a06c9e62
8 changed files with 54 additions and 11 deletions
@@ -1283,7 +1283,7 @@ public class AppController implements Initializable {
}
public void importWallet(ActionEvent event) {
List<WalletForm> selectedWalletForms = getSelectedWalletForms();
List<WalletForm> selectedWalletForms = getSelectedWalletForms().stream().filter(walletForm -> !walletForm.isLocked()).collect(Collectors.toList());
WalletImportDialog dlg = new WalletImportDialog(selectedWalletForms);
dlg.initOwner(rootStack.getScene().getWindow());
Optional<List<Wallet>> 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);
}
}
@@ -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"));
@@ -41,6 +41,7 @@ public class SparrowTerminal extends Application {
private final Map<String, WalletData> walletData = new HashMap<>();
private final Set<File> lockedWallets = new HashSet<>();
private final Map<File, Integer> 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());
}
}
@@ -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) {
@@ -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));
});
@@ -1046,7 +1046,12 @@ public class SettingsController extends WalletFormController implements Initiali
WalletPasswordDialog dlg = new WalletPasswordDialog(null, requirement, suggestChangePassword);
dlg.initOwner(apply.getScene().getWindow());
Optional<SecureString> password = dlg.showAndWait();
Optional<SecureString> 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 {
@@ -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();
@@ -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<Boolean> 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++;
}
}