From 2ca286d8260a089b073ce9094ad571c37857084f Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Wed, 9 Feb 2022 16:09:12 +0200 Subject: [PATCH] remove tmp backup approach for retaining labels over wallet restarts while refreshing, replaced by detached labels --- drongo | 2 +- .../sparrowwallet/sparrow/AppController.java | 53 +++++---- .../sparrow/control/CoinTreeTable.java | 1 - .../sparrow/io/JsonPersistence.java | 30 ++--- .../sparrowwallet/sparrow/io/Persistence.java | 6 +- .../com/sparrowwallet/sparrow/io/Sparrow.java | 8 +- .../com/sparrowwallet/sparrow/io/Storage.java | 51 +++------ ...letBackupAndKey.java => WalletAndKey.java} | 16 +-- .../sparrow/io/db/DbPersistence.java | 28 ++--- .../sparrow/wallet/NodeEntry.java | 41 ------- .../sparrow/wallet/ReceiveController.java | 5 +- .../sparrow/wallet/SettingsWalletForm.java | 11 +- .../sparrow/wallet/WalletForm.java | 103 +++--------------- .../sparrowwallet/sparrow/io/StorageTest.java | 2 +- 14 files changed, 97 insertions(+), 260 deletions(-) rename src/main/java/com/sparrowwallet/sparrow/io/{WalletBackupAndKey.java => WalletAndKey.java} (62%) diff --git a/drongo b/drongo index 78359961..f73cabad 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 78359961f369f4f8b016973529241048f2ef216d +Subproject commit f73cabad3c76c1eb28b4f02b17c9beb608ba2aa4 diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index a4dcfc77..d56c517e 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -861,7 +861,7 @@ public class AppController implements Initializable { File walletFile = Storage.getWalletFile(nameAndBirthDate.getName()); Storage storage = new Storage(walletFile); Wallet wallet = new Wallet(nameAndBirthDate.getName(), PolicyType.SINGLE, ScriptType.P2WPKH, nameAndBirthDate.getBirthDate()); - addWalletTabOrWindow(storage, wallet, null, false); + addWalletTabOrWindow(storage, wallet, false); } } @@ -888,8 +888,8 @@ public class AppController implements Initializable { if(!storage.isEncrypted()) { Storage.LoadWalletService loadWalletService = new Storage.LoadWalletService(storage); loadWalletService.setOnSucceeded(workerStateEvent -> { - WalletBackupAndKey walletBackupAndKey = loadWalletService.getValue(); - openWallet(storage, walletBackupAndKey, this, forceSameWindow); + WalletAndKey walletAndKey = loadWalletService.getValue(); + openWallet(storage, walletAndKey, this, forceSameWindow); }); loadWalletService.setOnFailed(workerStateEvent -> { Throwable exception = workerStateEvent.getSource().getException(); @@ -912,8 +912,8 @@ public class AppController implements Initializable { Storage.LoadWalletService loadWalletService = new Storage.LoadWalletService(storage, password); loadWalletService.setOnSucceeded(workerStateEvent -> { EventManager.get().post(new StorageEvent(storage.getWalletId(null), TimedEvent.Action.END, "Done")); - WalletBackupAndKey walletBackupAndKey = loadWalletService.getValue(); - openWallet(storage, walletBackupAndKey, this, forceSameWindow); + WalletAndKey walletAndKey = loadWalletService.getValue(); + openWallet(storage, walletAndKey, this, forceSameWindow); }); loadWalletService.setOnFailed(workerStateEvent -> { EventManager.get().post(new StorageEvent(storage.getWalletId(null), TimedEvent.Action.END, "Failed")); @@ -947,23 +947,23 @@ public class AppController implements Initializable { } } - private void openWallet(Storage storage, WalletBackupAndKey walletBackupAndKey, AppController appController, boolean forceSameWindow) { + private void openWallet(Storage storage, WalletAndKey walletAndKey, AppController appController, boolean forceSameWindow) { try { - checkWalletNetwork(walletBackupAndKey.getWallet()); - restorePublicKeysFromSeed(storage, walletBackupAndKey.getWallet(), walletBackupAndKey.getKey()); - if(!walletBackupAndKey.getWallet().isValid()) { + checkWalletNetwork(walletAndKey.getWallet()); + restorePublicKeysFromSeed(storage, walletAndKey.getWallet(), walletAndKey.getKey()); + if(!walletAndKey.getWallet().isValid()) { throw new IllegalStateException("Wallet file is not valid."); } - AppController walletAppController = appController.addWalletTabOrWindow(storage, walletBackupAndKey.getWallet(), walletBackupAndKey.getBackupWallet(), forceSameWindow); - for(Map.Entry entry : walletBackupAndKey.getChildWallets().entrySet()) { + AppController walletAppController = appController.addWalletTabOrWindow(storage, walletAndKey.getWallet(), forceSameWindow); + for(Map.Entry entry : walletAndKey.getChildWallets().entrySet()) { openWallet(entry.getValue(), entry.getKey(), walletAppController, true); } - Platform.runLater(() -> selectTab(walletBackupAndKey.getWallet())); + Platform.runLater(() -> selectTab(walletAndKey.getWallet())); } catch(Exception e) { log.error("Error opening wallet", e); showErrorDialog("Error Opening Wallet", e.getMessage()); } finally { - walletBackupAndKey.clear(); + walletAndKey.clear(); } } @@ -1141,13 +1141,13 @@ public class AppController implements Initializable { storage.saveWallet(wallet); checkWalletNetwork(wallet); restorePublicKeysFromSeed(storage, wallet, null); - addWalletTabOrWindow(storage, wallet, null, false); + addWalletTabOrWindow(storage, wallet, false); for(Wallet childWallet : wallet.getChildWallets()) { storage.saveWallet(childWallet); checkWalletNetwork(childWallet); restorePublicKeysFromSeed(storage, childWallet, null); - addWalletTabOrWindow(storage, childWallet, null, false); + addWalletTabOrWindow(storage, childWallet, false); } Platform.runLater(() -> selectTab(wallet)); } catch(IOException | StorageException | MnemonicException e) { @@ -1168,14 +1168,14 @@ public class AppController implements Initializable { storage.saveWallet(wallet); checkWalletNetwork(wallet); restorePublicKeysFromSeed(storage, wallet, key); - addWalletTabOrWindow(storage, wallet, null, false); + addWalletTabOrWindow(storage, wallet, false); for(Wallet childWallet : wallet.getChildWallets()) { childWallet.encrypt(key); storage.saveWallet(childWallet); checkWalletNetwork(childWallet); restorePublicKeysFromSeed(storage, childWallet, key); - addWalletTabOrWindow(storage, childWallet, null, false); + addWalletTabOrWindow(storage, childWallet, false); } Platform.runLater(() -> selectTab(wallet)); } catch(IOException | StorageException | MnemonicException e) { @@ -1389,14 +1389,13 @@ public class AppController implements Initializable { if(selectedWalletForm != null) { Wallet wallet = selectedWalletForm.getWallet(); Wallet pastWallet = wallet.copy(); - selectedWalletForm.getStorage().backupTempWallet(); wallet.clearHistory(); AppServices.clearTransactionHistoryCache(wallet); EventManager.get().post(new WalletHistoryClearedEvent(wallet, pastWallet, selectedWalletForm.getWalletId())); } } - public AppController addWalletTabOrWindow(Storage storage, Wallet wallet, Wallet backupWallet, boolean forceSameWindow) { + public AppController addWalletTabOrWindow(Storage storage, Wallet wallet, boolean forceSameWindow) { Window existingWalletWindow = AppServices.get().getWindowForWallet(storage.getWalletId(wallet)); if(existingWalletWindow instanceof Stage) { Stage existingWalletStage = (Stage)existingWalletWindow; @@ -1411,15 +1410,15 @@ public class AppController implements Initializable { AppController appController = AppServices.newAppWindow(stage); stage.toFront(); stage.setX(AppServices.get().getWalletWindowMaxX() + 30); - appController.addWalletTab(storage, wallet, backupWallet); + appController.addWalletTab(storage, wallet); return appController; } else { - addWalletTab(storage, wallet, backupWallet); + addWalletTab(storage, wallet); return this; } } - public void addWalletTab(Storage storage, Wallet wallet, Wallet backupWallet) { + public void addWalletTab(Storage storage, Wallet wallet) { if(wallet.isMasterWallet()) { String name = storage.getWalletName(wallet); if(!name.equals(wallet.getName())) { @@ -1449,7 +1448,7 @@ public class AppController implements Initializable { subTabs.rotateGraphicProperty().set(true); tab.setContent(subTabs); - WalletForm walletForm = addWalletSubTab(subTabs, storage, wallet, backupWallet); + WalletForm walletForm = addWalletSubTab(subTabs, storage, wallet); TabData tabData = new WalletTabData(TabData.TabType.WALLET, walletForm); tab.setUserData(tabData); tab.setContextMenu(getTabContextMenu(tab)); @@ -1478,7 +1477,7 @@ public class AppController implements Initializable { WalletTabData walletTabData = (WalletTabData)tabData; if(walletTabData.getWallet() == wallet.getMasterWallet()) { TabPane subTabs = (TabPane)walletTab.getContent(); - addWalletSubTab(subTabs, storage, wallet, backupWallet); + addWalletSubTab(subTabs, storage, wallet); Tab masterTab = subTabs.getTabs().stream().filter(tab -> ((WalletTabData)tab.getUserData()).getWallet().isMasterWallet()).findFirst().orElse(subTabs.getTabs().get(0)); Label masterLabel = (Label)masterTab.getGraphic(); masterLabel.setText(wallet.getMasterWallet().getLabel() != null ? wallet.getMasterWallet().getLabel() : wallet.getMasterWallet().getAutomaticName()); @@ -1507,7 +1506,7 @@ public class AppController implements Initializable { } } - public WalletForm addWalletSubTab(TabPane subTabs, Storage storage, Wallet wallet, Wallet backupWallet) { + public WalletForm addWalletSubTab(TabPane subTabs, Storage storage, Wallet wallet) { try { Tab subTab = new Tab(); subTab.setClosable(false); @@ -1525,7 +1524,7 @@ public class AppController implements Initializable { EventManager.get().post(new WalletOpeningEvent(storage, wallet)); //Note that only one WalletForm is created per wallet tab, and registered to listen for events. All wallet controllers (except SettingsController) share this instance. - WalletForm walletForm = new WalletForm(storage, wallet, backupWallet); + WalletForm walletForm = new WalletForm(storage, wallet); EventManager.get().register(walletForm); controller.setWalletForm(walletForm); @@ -2478,7 +2477,7 @@ public class AppController implements Initializable { throw new IllegalStateException("Cannot find storage for master wallet"); } - addWalletTab(storage, event.getChildWallet(), null); + addWalletTab(storage, event.getChildWallet()); } @Subscribe diff --git a/src/main/java/com/sparrowwallet/sparrow/control/CoinTreeTable.java b/src/main/java/com/sparrowwallet/sparrow/control/CoinTreeTable.java index 1a39da01..6717d51b 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/CoinTreeTable.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/CoinTreeTable.java @@ -86,7 +86,6 @@ public class CoinTreeTable extends TreeTableView { if(optDate.isPresent()) { Storage storage = AppServices.get().getOpenWallets().get(wallet); Wallet pastWallet = wallet.copy(); - storage.backupTempWallet(); wallet.setBirthDate(optDate.get()); //Trigger background save of birthdate EventManager.get().post(new WalletDataChangedEvent(wallet)); diff --git a/src/main/java/com/sparrowwallet/sparrow/io/JsonPersistence.java b/src/main/java/com/sparrowwallet/sparrow/io/JsonPersistence.java index 8b47d55d..9ac0aa73 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/JsonPersistence.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/JsonPersistence.java @@ -36,29 +36,26 @@ public class JsonPersistence implements Persistence { } @Override - public WalletBackupAndKey loadWallet(Storage storage) throws IOException, StorageException { + public WalletAndKey loadWallet(Storage storage) throws IOException, StorageException { Wallet wallet; try(Reader reader = new FileReader(storage.getWalletFile())) { wallet = gson.fromJson(reader, Wallet.class); } - Map childWallets = loadChildWallets(storage, wallet, null); - wallet.setChildWallets(childWallets.keySet().stream().map(WalletBackupAndKey::getWallet).collect(Collectors.toList())); + Map childWallets = loadChildWallets(storage, wallet, null); + wallet.setChildWallets(childWallets.keySet().stream().map(WalletAndKey::getWallet).collect(Collectors.toList())); - File backupFile = storage.getTempBackup(); - Wallet backupWallet = backupFile == null ? null : loadWallet(backupFile, null); - - return new WalletBackupAndKey(wallet, backupWallet, null, null, childWallets); + return new WalletAndKey(wallet, null, null, childWallets); } @Override - public WalletBackupAndKey loadWallet(Storage storage, CharSequence password) throws IOException, StorageException { + public WalletAndKey loadWallet(Storage storage, CharSequence password) throws IOException, StorageException { return loadWallet(storage, password, null); } @Override - public WalletBackupAndKey loadWallet(Storage storage, CharSequence password, ECKey alreadyDerivedKey) throws IOException, StorageException { + public WalletAndKey loadWallet(Storage storage, CharSequence password, ECKey alreadyDerivedKey) throws IOException, StorageException { Wallet wallet; ECKey encryptionKey; @@ -68,25 +65,22 @@ public class JsonPersistence implements Persistence { wallet = gson.fromJson(reader, Wallet.class); } - Map childWallets = loadChildWallets(storage, wallet, encryptionKey); - wallet.setChildWallets(childWallets.keySet().stream().map(WalletBackupAndKey::getWallet).collect(Collectors.toList())); + Map childWallets = loadChildWallets(storage, wallet, encryptionKey); + wallet.setChildWallets(childWallets.keySet().stream().map(WalletAndKey::getWallet).collect(Collectors.toList())); - File backupFile = storage.getTempBackup(); - Wallet backupWallet = backupFile == null ? null : loadWallet(backupFile, encryptionKey); - - return new WalletBackupAndKey(wallet, backupWallet, encryptionKey, keyDeriver, childWallets); + return new WalletAndKey(wallet, encryptionKey, keyDeriver, childWallets); } - private Map loadChildWallets(Storage storage, Wallet masterWallet, ECKey encryptionKey) throws IOException, StorageException { + private Map loadChildWallets(Storage storage, Wallet masterWallet, ECKey encryptionKey) throws IOException, StorageException { File[] walletFiles = getChildWalletFiles(storage.getWalletFile(), masterWallet); - Map childWallets = new TreeMap<>(); + Map childWallets = new TreeMap<>(); for(File childFile : walletFiles) { Wallet childWallet = loadWallet(childFile, encryptionKey); Storage childStorage = new Storage(childFile); childStorage.setEncryptionPubKey(encryptionKey == null ? Storage.NO_PASSWORD_KEY : ECKey.fromPublicOnly(encryptionKey)); childStorage.setKeyDeriver(getKeyDeriver()); childWallet.setMasterWallet(masterWallet); - childWallets.put(new WalletBackupAndKey(childWallet, null, encryptionKey, keyDeriver, Collections.emptyMap()), storage); + childWallets.put(new WalletAndKey(childWallet, encryptionKey, keyDeriver, Collections.emptyMap()), storage); } return childWallets; diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Persistence.java b/src/main/java/com/sparrowwallet/sparrow/io/Persistence.java index 4b5d7fff..61c75623 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Persistence.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Persistence.java @@ -9,9 +9,9 @@ import java.io.IOException; import java.io.OutputStream; public interface Persistence { - WalletBackupAndKey loadWallet(Storage storage) throws IOException, StorageException; - WalletBackupAndKey loadWallet(Storage storage, CharSequence password) throws IOException, StorageException; - WalletBackupAndKey loadWallet(Storage storage, CharSequence password, ECKey alreadyDerivedKey) throws IOException, StorageException; + WalletAndKey loadWallet(Storage storage) throws IOException, StorageException; + WalletAndKey loadWallet(Storage storage, CharSequence password) throws IOException, StorageException; + WalletAndKey loadWallet(Storage storage, CharSequence password, ECKey alreadyDerivedKey) throws IOException, StorageException; File storeWallet(Storage storage, Wallet wallet) throws IOException, StorageException; File storeWallet(Storage storage, Wallet wallet, ECKey encryptionPubKey) throws IOException, StorageException; void updateWallet(Storage storage, Wallet wallet) throws IOException, StorageException; diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Sparrow.java b/src/main/java/com/sparrowwallet/sparrow/io/Sparrow.java index f7357d54..15033802 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Sparrow.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Sparrow.java @@ -111,10 +111,10 @@ public class Sparrow implements WalletImport, WalletExport { if(!isEncrypted(tempFile)) { wallet = storage.loadUnencryptedWallet().getWallet(); } else { - WalletBackupAndKey walletBackupAndKey = storage.loadEncryptedWallet(password); - wallet = walletBackupAndKey.getWallet(); - wallet.decrypt(walletBackupAndKey.getKey()); - for(Map.Entry entry : walletBackupAndKey.getChildWallets().entrySet()) { + WalletAndKey walletAndKey = storage.loadEncryptedWallet(password); + wallet = walletAndKey.getWallet(); + wallet.decrypt(walletAndKey.getKey()); + for(Map.Entry entry : walletAndKey.getChildWallets().entrySet()) { entry.getKey().getWallet().decrypt(entry.getKey().getKey()); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java index dcd6b940..bc95992a 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java @@ -20,7 +20,6 @@ import java.nio.file.attribute.PosixFilePermissions; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.text.DateFormat; -import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Matcher; @@ -38,7 +37,6 @@ public class Storage { public static final String WALLETS_DIR = "wallets"; public static final String WALLETS_BACKUP_DIR = "backup"; public static final String CERTS_DIR = "certs"; - public static final String TEMP_BACKUP_PREFIX = "tmp"; public static final List RESERVED_WALLET_NAMES = List.of("temp"); private Persistence persistence; @@ -87,14 +85,14 @@ public class Storage { return ""; } - public WalletBackupAndKey loadUnencryptedWallet() throws IOException, StorageException { - WalletBackupAndKey masterWalletAndKey = persistence.loadWallet(this); + public WalletAndKey loadUnencryptedWallet() throws IOException, StorageException { + WalletAndKey masterWalletAndKey = persistence.loadWallet(this); encryptionPubKey = NO_PASSWORD_KEY; return migrateToDb(masterWalletAndKey); } - public WalletBackupAndKey loadEncryptedWallet(CharSequence password) throws IOException, StorageException { - WalletBackupAndKey masterWalletAndKey = persistence.loadWallet(this, password); + public WalletAndKey loadEncryptedWallet(CharSequence password) throws IOException, StorageException { + WalletAndKey masterWalletAndKey = persistence.loadWallet(this, password); encryptionPubKey = ECKey.fromPublicOnly(masterWalletAndKey.getEncryptionKey()); return migrateToDb(masterWalletAndKey); } @@ -136,14 +134,6 @@ public class Storage { } } - public void backupTempWallet() { - try { - backupWallet(TEMP_BACKUP_PREFIX); - } catch(IOException e) { - log.error("Error creating " + TEMP_BACKUP_PREFIX + " backup wallet", e); - } - } - private void backupWallet(String prefix) throws IOException { File backupDir = getWalletsBackupDir(); @@ -174,16 +164,6 @@ public class Storage { deleteBackups(null); } - public void deleteTempBackups(boolean forceSave) { - File[] backups = getBackups(Storage.TEMP_BACKUP_PREFIX); - if(backups.length > 0 && (forceSave || hasStartedSince(backups[0]))) { - File permanent = new File(backups[0].getParent(), backups[0].getName().substring(Storage.TEMP_BACKUP_PREFIX.length() + 1)); - backups[0].renameTo(permanent); - } - - deleteBackups(Storage.TEMP_BACKUP_PREFIX); - } - private boolean hasStartedSince(File lastBackup) { try { Date date = BACKUP_DATE_FORMAT.parse(getBackupDate(lastBackup.getName())); @@ -202,11 +182,6 @@ public class Storage { } } - public File getTempBackup() { - File[] backups = getBackups(TEMP_BACKUP_PREFIX); - return backups.length == 0 ? null : backups[0]; - } - File[] getBackups(String prefix) { File backupDir = getWalletsBackupDir(); String walletName = persistence.getWalletName(walletFile, null); @@ -232,7 +207,7 @@ public class Storage { return null; } - private WalletBackupAndKey migrateToDb(WalletBackupAndKey masterWalletAndKey) throws IOException, StorageException { + private WalletAndKey migrateToDb(WalletAndKey masterWalletAndKey) throws IOException, StorageException { if(getType() == PersistenceType.JSON) { log.info("Migrating " + masterWalletAndKey.getWallet().getName() + " from JSON to DB persistence"); masterWalletAndKey = migrateType(PersistenceType.DB, masterWalletAndKey.getWallet(), masterWalletAndKey.getEncryptionKey()); @@ -241,7 +216,7 @@ public class Storage { return masterWalletAndKey; } - private WalletBackupAndKey migrateType(PersistenceType type, Wallet wallet, ECKey encryptionKey) throws IOException, StorageException { + private WalletAndKey migrateType(PersistenceType type, Wallet wallet, ECKey encryptionKey) throws IOException, StorageException { File existingFile = walletFile; try { @@ -530,7 +505,7 @@ public class Storage { return ownerOnly; } - public static class LoadWalletService extends Service { + public static class LoadWalletService extends Service { private final Storage storage; private final SecureString password; @@ -545,19 +520,19 @@ public class Storage { } @Override - protected Task createTask() { + protected Task createTask() { return new Task<>() { - protected WalletBackupAndKey call() throws IOException, StorageException { - WalletBackupAndKey walletBackupAndKey; + protected WalletAndKey call() throws IOException, StorageException { + WalletAndKey walletAndKey; if(password != null) { - walletBackupAndKey = storage.loadEncryptedWallet(password); + walletAndKey = storage.loadEncryptedWallet(password); password.clear(); } else { - walletBackupAndKey = storage.loadUnencryptedWallet(); + walletAndKey = storage.loadUnencryptedWallet(); } - return walletBackupAndKey; + return walletAndKey; } }; } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/WalletBackupAndKey.java b/src/main/java/com/sparrowwallet/sparrow/io/WalletAndKey.java similarity index 62% rename from src/main/java/com/sparrowwallet/sparrow/io/WalletBackupAndKey.java rename to src/main/java/com/sparrowwallet/sparrow/io/WalletAndKey.java index 9bb8506f..b18d6e6c 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/WalletBackupAndKey.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/WalletAndKey.java @@ -5,16 +5,14 @@ import com.sparrowwallet.drongo.wallet.Wallet; import java.util.Map; -public class WalletBackupAndKey implements Comparable { +public class WalletAndKey implements Comparable { private final Wallet wallet; - private final Wallet backupWallet; private final ECKey encryptionKey; private final Key key; - private final Map childWallets; + private final Map childWallets; - public WalletBackupAndKey(Wallet wallet, Wallet backupWallet, ECKey encryptionKey, AsymmetricKeyDeriver keyDeriver, Map childWallets) { + public WalletAndKey(Wallet wallet, ECKey encryptionKey, AsymmetricKeyDeriver keyDeriver, Map childWallets) { this.wallet = wallet; - this.backupWallet = backupWallet; this.encryptionKey = encryptionKey; this.key = encryptionKey == null ? null : new Key(encryptionKey.getPrivKeyBytes(), keyDeriver.getSalt(), EncryptionType.Deriver.ARGON2); this.childWallets = childWallets; @@ -24,10 +22,6 @@ public class WalletBackupAndKey implements Comparable { return wallet; } - public Wallet getBackupWallet() { - return backupWallet; - } - public ECKey getEncryptionKey() { return encryptionKey; } @@ -36,7 +30,7 @@ public class WalletBackupAndKey implements Comparable { return key; } - public Map getChildWallets() { + public Map getChildWallets() { return childWallets; } @@ -50,7 +44,7 @@ public class WalletBackupAndKey implements Comparable { } @Override - public int compareTo(WalletBackupAndKey other) { + public int compareTo(WalletAndKey other) { return wallet.compareTo(other.wallet); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java b/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java index df8bda72..c22a6f67 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java @@ -66,17 +66,17 @@ public class DbPersistence implements Persistence { } @Override - public WalletBackupAndKey loadWallet(Storage storage) throws IOException, StorageException { + public WalletAndKey loadWallet(Storage storage) throws IOException, StorageException { return loadWallet(storage, null, null); } @Override - public WalletBackupAndKey loadWallet(Storage storage, CharSequence password) throws IOException, StorageException { + public WalletAndKey loadWallet(Storage storage, CharSequence password) throws IOException, StorageException { return loadWallet(storage, password, null); } @Override - public WalletBackupAndKey loadWallet(Storage storage, CharSequence password, ECKey alreadyDerivedKey) throws IOException, StorageException { + public WalletAndKey loadWallet(Storage storage, CharSequence password, ECKey alreadyDerivedKey) throws IOException, StorageException { ECKey encryptionKey = getEncryptionKey(password, storage.getWalletFile(), alreadyDerivedKey); migrate(storage, MASTER_SCHEMA, encryptionKey); @@ -87,31 +87,22 @@ public class DbPersistence implements Persistence { return walletDao.getMainWallet(MASTER_SCHEMA); }); - File backupFile = storage.getTempBackup(); - Wallet backupWallet = null; - if(backupFile != null) { - Persistence backupPersistence = PersistenceType.DB.getInstance(); - backupPersistence.setKeyDeriver(keyDeriver); - backupWallet = backupPersistence.loadWallet(new Storage(backupPersistence, backupFile), password, encryptionKey).getWallet(); - backupPersistence.close(); - } - - Map childWallets = loadChildWallets(storage, masterWallet, backupWallet, encryptionKey); - masterWallet.setChildWallets(childWallets.keySet().stream().map(WalletBackupAndKey::getWallet).collect(Collectors.toList())); + Map childWallets = loadChildWallets(storage, masterWallet, encryptionKey); + masterWallet.setChildWallets(childWallets.keySet().stream().map(WalletAndKey::getWallet).collect(Collectors.toList())); createUpdateExecutor(masterWallet); - return new WalletBackupAndKey(masterWallet, backupWallet, encryptionKey, keyDeriver, childWallets); + return new WalletAndKey(masterWallet, encryptionKey, keyDeriver, childWallets); } - private Map loadChildWallets(Storage storage, Wallet masterWallet, Wallet backupWallet, ECKey encryptionKey) throws StorageException { + private Map loadChildWallets(Storage storage, Wallet masterWallet, ECKey encryptionKey) throws StorageException { Jdbi jdbi = getJdbi(storage, getFilePassword(encryptionKey)); List schemas = jdbi.withHandle(handle -> { return handle.createQuery("show schemas").mapTo(String.class).list(); }); List childSchemas = schemas.stream().filter(schema -> schema.startsWith(WALLET_SCHEMA_PREFIX) && !schema.equals(MASTER_SCHEMA)).collect(Collectors.toList()); - Map childWallets = new TreeMap<>(); + Map childWallets = new TreeMap<>(); for(String schema : childSchemas) { migrate(storage, schema, encryptionKey); @@ -123,8 +114,7 @@ public class DbPersistence implements Persistence { childWallet.setMasterWallet(masterWallet); return childWallet; }); - Wallet backupChildWallet = backupWallet == null ? null : backupWallet.getChildWallets().stream().filter(child -> wallet.getName().equals(child.getName())).findFirst().orElse(null); - childWallets.put(new WalletBackupAndKey(wallet, backupChildWallet, encryptionKey, keyDeriver, Collections.emptyMap()), storage); + childWallets.put(new WalletAndKey(wallet, encryptionKey, keyDeriver, Collections.emptyMap()), storage); } return childWallets; diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java b/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java index a6039050..f117ece8 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java @@ -90,45 +90,4 @@ public class NodeEntry extends Entry implements Comparable { public int compareTo(NodeEntry other) { return node.compareTo(other.node); } - - public Set copyLabels(WalletNode pastNode) { - if(pastNode == null) { - return Collections.emptySet(); - } - - Set changedEntries = new LinkedHashSet<>(); - - if(node.getLabel() == null && pastNode.getLabel() != null) { - node.setLabel(pastNode.getLabel()); - labelProperty().set(pastNode.getLabel()); - changedEntries.add(this); - } - - for(Entry childEntry : getChildren()) { - if(childEntry instanceof HashIndexEntry) { - HashIndexEntry hashIndexEntry = (HashIndexEntry)childEntry; - BlockTransactionHashIndex txo = hashIndexEntry.getHashIndex(); - Optional optPastTxo = pastNode.getTransactionOutputs().stream().filter(pastTxo -> pastTxo.equals(txo)).findFirst(); - if(optPastTxo.isPresent()) { - BlockTransactionHashIndex pastTxo = optPastTxo.get(); - if(txo.getLabel() == null && pastTxo.getLabel() != null) { - txo.setLabel(pastTxo.getLabel()); - changedEntries.add(childEntry); - } - if(txo.isSpent() && pastTxo.isSpent() && txo.getSpentBy().getLabel() == null && pastTxo.getSpentBy().getLabel() != null) { - txo.getSpentBy().setLabel(pastTxo.getSpentBy().getLabel()); - changedEntries.add(childEntry); - } - } - } - - if(childEntry instanceof NodeEntry) { - NodeEntry childNodeEntry = (NodeEntry)childEntry; - Optional optPastChildNodeEntry = pastNode.getChildren().stream().filter(childNodeEntry.node::equals).findFirst(); - optPastChildNodeEntry.ifPresent(pastChildNode -> changedEntries.addAll(childNodeEntry.copyLabels(pastChildNode))); - } - } - - return changedEntries; - } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/ReceiveController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/ReceiveController.java index 67a8e147..6d70bc0f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/ReceiveController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/ReceiveController.java @@ -278,7 +278,10 @@ public class ReceiveController extends WalletFormController implements Initializ @Subscribe public void walletNodesChanged(WalletNodesChangedEvent event) { if(event.getWallet().equals(walletForm.getWallet())) { - currentEntry = null; + if(currentEntry != null) { + label.textProperty().unbindBidirectional(currentEntry.labelProperty()); + currentEntry = null; + } refreshAddress(); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsWalletForm.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsWalletForm.java index 77eb8b9e..bcaa79ce 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsWalletForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsWalletForm.java @@ -24,7 +24,7 @@ public class SettingsWalletForm extends WalletForm { private Wallet walletCopy; public SettingsWalletForm(Storage storage, Wallet currentWallet) { - super(storage, currentWallet, null, false); + super(storage, currentWallet, false); this.walletCopy = currentWallet.copy(); } @@ -51,15 +51,6 @@ public class SettingsWalletForm extends WalletForm { boolean addressChange = isAddressChange(); if(wallet.isValid()) { - //Don't create temp backup on changing addresses - there are no labels to lose - if(!addressChange) { - backgroundUpdate(); //Save existing wallet here for the temp backup in case password has been changed - this will update the password on the existing wallet - if(AppServices.isConnected()) { - //Backup the wallet so labels will survive application shutdown - getStorage().backupTempWallet(); - } - } - //Clear transaction history cache before we clear the nodes AppServices.clearTransactionHistoryCache(wallet); } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java index 3149481b..c7ca7c91 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java @@ -8,12 +8,10 @@ import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.WalletTabData; import com.sparrowwallet.sparrow.event.*; -import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.io.StorageException; import com.sparrowwallet.sparrow.net.AllHistoryChangedException; 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.ObjectProperty; @@ -26,7 +24,6 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.util.*; -import java.util.stream.Collectors; import static com.sparrowwallet.drongo.wallet.WalletNode.nodeRangesToString; @@ -35,7 +32,6 @@ public class WalletForm { private final Storage storage; protected Wallet wallet; - private Wallet savedPastWallet; private WalletTransactionsEntry walletTransactionsEntry; private WalletUtxosEntry walletUtxosEntry; @@ -47,19 +43,16 @@ public class WalletForm { private final BooleanProperty lockedProperty = new SimpleBooleanProperty(false); - public WalletForm(Storage storage, Wallet currentWallet, Wallet backupWallet) { - this(storage, currentWallet, backupWallet, true); + public WalletForm(Storage storage, Wallet currentWallet) { + this(storage, currentWallet, true); } - public WalletForm(Storage storage, Wallet currentWallet, Wallet backupWallet, boolean refreshHistory) { + public WalletForm(Storage storage, Wallet currentWallet, boolean refreshHistory) { this.storage = storage; this.wallet = currentWallet; - //Unencrypted wallets load before isConnected is true, waiting for the ConnectionEvent to refresh history - save the backup for this event - savedPastWallet = backupWallet; - if(refreshHistory && wallet.isValid()) { - refreshHistory(AppServices.getCurrentBlockHeight(), backupWallet); + refreshHistory(AppServices.getCurrentBlockHeight()); } } @@ -100,11 +93,9 @@ public class WalletForm { } public void saveAndRefresh() throws IOException, StorageException { - Wallet pastWallet = wallet.copy(); - storage.backupTempWallet(); wallet.clearHistory(); save(); - refreshHistory(AppServices.getCurrentBlockHeight(), pastWallet); + refreshHistory(AppServices.getCurrentBlockHeight()); } public void saveBackup() throws IOException { @@ -124,11 +115,11 @@ public class WalletForm { storage.deleteBackups(); } - public void refreshHistory(Integer blockHeight, Wallet pastWallet) { - refreshHistory(blockHeight, pastWallet, null); + public void refreshHistory(Integer blockHeight) { + refreshHistory(blockHeight, null); } - public void refreshHistory(Integer blockHeight, Wallet pastWallet, Set nodes) { + public void refreshHistory(Integer blockHeight, Set nodes) { Wallet previousWallet = wallet.copy(); if(wallet.isValid() && AppServices.isConnected()) { if(log.isDebugEnabled()) { @@ -139,7 +130,7 @@ public class WalletForm { historyService.setOnSucceeded(workerStateEvent -> { if(historyService.getValue()) { EventManager.get().post(new WalletHistoryFinishedEvent(wallet)); - updateWallet(blockHeight, pastWallet, previousWallet); + updateWallet(blockHeight, previousWallet); } }); historyService.setOnFailed(workerStateEvent -> { @@ -152,7 +143,7 @@ public class WalletForm { wallet.clearHistory(); AppServices.clearTransactionHistoryCache(wallet); - EventManager.get().post(new WalletHistoryClearedEvent(wallet, pastWallet == null ? previousWallet : pastWallet, getWalletId())); + EventManager.get().post(new WalletHistoryClearedEvent(wallet, previousWallet, getWalletId())); } else { if(AppServices.isConnected()) { log.error("Error retrieving wallet history", workerStateEvent.getSource().getException()); @@ -169,72 +160,20 @@ public class WalletForm { } } - private void updateWallet(Integer blockHeight, Wallet pastWallet, Wallet previousWallet) { + private void updateWallet(Integer blockHeight, Wallet previousWallet) { if(blockHeight != null) { wallet.setStoredBlockHeight(blockHeight); } - //After the wallet settings are changed, the previous wallet is copied to pastWallet and used here to copy labels from past nodes, txos and txes - Set labelChangedEntries = Collections.emptySet(); - if(pastWallet != null) { - labelChangedEntries = copyLabels(pastWallet); - copyMixData(pastWallet); - } - - notifyIfChanged(blockHeight, previousWallet, labelChangedEntries); + notifyIfChanged(blockHeight, previousWallet); } - private Set copyLabels(Wallet pastWallet) { - Set changedEntries = new LinkedHashSet<>(); - - //On a full wallet refresh, walletUtxosEntry and walletTransactionsEntry will have no children yet, but AddressesController may have created accountEntries on a walletNodesChangedEvent - //Copy nodeEntry labels - for(KeyPurpose keyPurpose : KeyPurpose.DEFAULT_PURPOSES) { - NodeEntry purposeEntry = getNodeEntry(keyPurpose); - changedEntries.addAll(purposeEntry.copyLabels(pastWallet.getNode(purposeEntry.getNode().getKeyPurpose()))); - } - - //Copy node and txo labels - for(KeyPurpose keyPurpose : KeyPurpose.DEFAULT_PURPOSES) { - if(wallet.getNode(keyPurpose).copyLabels(pastWallet.getNode(keyPurpose))) { - changedEntries.add(getWalletUtxosEntry()); - } - } - - //Copy tx labels - for(Map.Entry txEntry : wallet.getTransactions().entrySet()) { - BlockTransaction pastBlockTransaction = pastWallet.getTransactions().get(txEntry.getKey()); - if(pastBlockTransaction != null && txEntry.getValue() != null && txEntry.getValue().getLabel() == null && pastBlockTransaction.getLabel() != null) { - txEntry.getValue().setLabel(pastBlockTransaction.getLabel()); - changedEntries.add(getWalletTransactionsEntry()); - } - } - - //Force saving the backup if the current wallet has fewer transactions than the past wallet (i.e. incomplete load) - storage.deleteTempBackups(wallet.getTransactions().size() < pastWallet.getTransactions().size()); - - return changedEntries; - } - - private void copyMixData(Wallet pastWallet) { - wallet.getUtxoMixes().forEach(pastWallet.getUtxoMixes()::putIfAbsent); - } - - private void notifyIfChanged(Integer blockHeight, Wallet previousWallet, Set labelChangedEntries) { + private void notifyIfChanged(Integer blockHeight, Wallet previousWallet) { List historyChangedNodes = new ArrayList<>(); historyChangedNodes.addAll(getHistoryChangedNodes(previousWallet.getNode(KeyPurpose.RECEIVE).getChildren(), wallet.getNode(KeyPurpose.RECEIVE).getChildren())); historyChangedNodes.addAll(getHistoryChangedNodes(previousWallet.getNode(KeyPurpose.CHANGE).getChildren(), wallet.getNode(KeyPurpose.CHANGE).getChildren())); boolean changed = false; - if(!labelChangedEntries.isEmpty()) { - List eventEntries = labelChangedEntries.stream().filter(entry -> entry != getWalletTransactionsEntry() && entry != getWalletUtxosEntry()).collect(Collectors.toList()); - if(!eventEntries.isEmpty()) { - Platform.runLater(() -> EventManager.get().post(new WalletEntryLabelsChangedEvent(wallet, eventEntries))); - } - - changed = true; - } - if(!historyChangedNodes.isEmpty()) { Platform.runLater(() -> EventManager.get().post(new WalletHistoryChangedEvent(wallet, storage, historyChangedNodes))); changed = true; @@ -374,14 +313,9 @@ public class WalletForm { accountEntries.clear(); EventManager.get().post(new WalletNodesChangedEvent(wallet)); - //It is necessary to save the past wallet because the actual copying of the past labels only occurs on a later ConnectionEvent with bwt - if(Config.get().getServerType() == ServerType.BITCOIN_CORE) { - savedPastWallet = event.getPastWallet(); - } - //Clear the cache - we will need to fetch everything again AppServices.clearTransactionHistoryCache(wallet); - refreshHistory(AppServices.getCurrentBlockHeight(), event.getPastWallet()); + refreshHistory(AppServices.getCurrentBlockHeight()); } } @@ -417,14 +351,13 @@ public class WalletForm { public void newBlock(NewBlockEvent event) { //Check if wallet is valid to avoid saving wallets in initial setup if(wallet.isValid()) { - updateWallet(event.getHeight(), null, wallet.copy()); + updateWallet(event.getHeight(), wallet.copy()); } } @Subscribe public void connected(ConnectionEvent event) { - refreshHistory(event.getBlockHeight(), savedPastWallet); - savedPastWallet = null; + refreshHistory(event.getBlockHeight()); } @Subscribe @@ -437,7 +370,7 @@ public class WalletForm { WalletNode walletNode = event.getWalletNode(wallet); if(walletNode != null) { log.debug(wallet.getFullName() + " history event for node " + walletNode + " (" + event.getScriptHash() + ")"); - refreshHistory(AppServices.getCurrentBlockHeight(), null, Set.of(walletNode)); + refreshHistory(AppServices.getCurrentBlockHeight(), Set.of(walletNode)); } } } @@ -576,7 +509,7 @@ public class WalletForm { } if(!newNodes.isEmpty()) { - Platform.runLater(() -> refreshHistory(AppServices.getCurrentBlockHeight(), null, newNodes)); + Platform.runLater(() -> refreshHistory(AppServices.getCurrentBlockHeight(), newNodes)); } } } diff --git a/src/test/java/com/sparrowwallet/sparrow/io/StorageTest.java b/src/test/java/com/sparrowwallet/sparrow/io/StorageTest.java index 44dd4023..abee4d8a 100644 --- a/src/test/java/com/sparrowwallet/sparrow/io/StorageTest.java +++ b/src/test/java/com/sparrowwallet/sparrow/io/StorageTest.java @@ -23,7 +23,7 @@ public class StorageTest extends IoTest { @Test public void loadSeedWallet() throws IOException, MnemonicException, StorageException { Storage storage = new Storage(getFile("sparrow-single-seed-wallet")); - WalletBackupAndKey walletAndKey = storage.loadEncryptedWallet("pass"); + WalletAndKey walletAndKey = storage.loadEncryptedWallet("pass"); Wallet wallet = walletAndKey.getWallet(); Wallet copy = wallet.copy(); copy.decrypt(walletAndKey.getKey());