dont lose labels when rescanning, even if app is restarted

This commit is contained in:
Craig Raw 2021-03-01 15:27:54 +02:00
parent 3bd2f69157
commit 6a58e8a799
7 changed files with 169 additions and 60 deletions

2
drongo

@ -1 +1 @@
Subproject commit 08acfe5ba16ed54ebbdcb8177cea88e4c53bda77 Subproject commit faa8f71313ff102c9611f8a9265511029654a83c

View file

@ -569,7 +569,7 @@ public class AppController implements Initializable {
File walletFile = Storage.getWalletFile(nameAndBirthDate.getName()); File walletFile = Storage.getWalletFile(nameAndBirthDate.getName());
Storage storage = new Storage(walletFile); Storage storage = new Storage(walletFile);
Wallet wallet = new Wallet(nameAndBirthDate.getName(), PolicyType.SINGLE, ScriptType.P2WPKH, nameAndBirthDate.getBirthDate()); Wallet wallet = new Wallet(nameAndBirthDate.getName(), PolicyType.SINGLE, ScriptType.P2WPKH, nameAndBirthDate.getBirthDate());
addWalletTabOrWindow(storage, wallet, false); addWalletTabOrWindow(storage, wallet, null, false);
} }
} }
@ -594,13 +594,13 @@ public class AppController implements Initializable {
Storage storage = new Storage(file); Storage storage = new Storage(file);
FileType fileType = IOUtils.getFileType(file); FileType fileType = IOUtils.getFileType(file);
if(FileType.JSON.equals(fileType)) { if(FileType.JSON.equals(fileType)) {
Wallet wallet = storage.loadWallet(); Storage.WalletBackupAndKey walletBackupAndKey = storage.loadWallet();
checkWalletNetwork(wallet); checkWalletNetwork(walletBackupAndKey.wallet);
restorePublicKeysFromSeed(wallet, null); restorePublicKeysFromSeed(walletBackupAndKey.wallet, null);
if(!wallet.isValid()) { if(!walletBackupAndKey.wallet.isValid()) {
throw new IllegalStateException("Wallet file is not valid."); throw new IllegalStateException("Wallet file is not valid.");
} }
addWalletTabOrWindow(storage, wallet, forceSameWindow); addWalletTabOrWindow(storage, walletBackupAndKey.wallet, walletBackupAndKey.backupWallet, forceSameWindow);
} else if(FileType.BINARY.equals(fileType)) { } else if(FileType.BINARY.equals(fileType)) {
WalletPasswordDialog dlg = new WalletPasswordDialog(file.getName(), WalletPasswordDialog.PasswordRequirement.LOAD); WalletPasswordDialog dlg = new WalletPasswordDialog(file.getName(), WalletPasswordDialog.PasswordRequirement.LOAD);
Optional<SecureString> optionalPassword = dlg.showAndWait(); Optional<SecureString> optionalPassword = dlg.showAndWait();
@ -612,15 +612,15 @@ public class AppController implements Initializable {
Storage.LoadWalletService loadWalletService = new Storage.LoadWalletService(storage, password); Storage.LoadWalletService loadWalletService = new Storage.LoadWalletService(storage, password);
loadWalletService.setOnSucceeded(workerStateEvent -> { loadWalletService.setOnSucceeded(workerStateEvent -> {
EventManager.get().post(new StorageEvent(storage.getWalletFile(), TimedEvent.Action.END, "Done")); EventManager.get().post(new StorageEvent(storage.getWalletFile(), TimedEvent.Action.END, "Done"));
Storage.WalletAndKey walletAndKey = loadWalletService.getValue(); Storage.WalletBackupAndKey walletBackupAndKey = loadWalletService.getValue();
try { try {
checkWalletNetwork(walletAndKey.wallet); checkWalletNetwork(walletBackupAndKey.wallet);
restorePublicKeysFromSeed(walletAndKey.wallet, walletAndKey.key); restorePublicKeysFromSeed(walletBackupAndKey.wallet, walletBackupAndKey.key);
addWalletTabOrWindow(storage, walletAndKey.wallet, forceSameWindow); addWalletTabOrWindow(storage, walletBackupAndKey.wallet, walletBackupAndKey.backupWallet, forceSameWindow);
} catch(Exception e) { } catch(Exception e) {
showErrorDialog("Error Opening Wallet", e.getMessage()); showErrorDialog("Error Opening Wallet", e.getMessage());
} finally { } finally {
walletAndKey.key.clear(); walletBackupAndKey.key.clear();
} }
}); });
loadWalletService.setOnFailed(workerStateEvent -> { loadWalletService.setOnFailed(workerStateEvent -> {
@ -779,7 +779,7 @@ public class AppController implements Initializable {
if(password.isPresent()) { if(password.isPresent()) {
if(password.get().length() == 0) { if(password.get().length() == 0) {
storage.setEncryptionPubKey(Storage.NO_PASSWORD_KEY); storage.setEncryptionPubKey(Storage.NO_PASSWORD_KEY);
addWalletTabOrWindow(storage, wallet, false); addWalletTabOrWindow(storage, wallet, null, false);
} else { } else {
Storage.KeyDerivationService keyDerivationService = new Storage.KeyDerivationService(storage, password.get()); Storage.KeyDerivationService keyDerivationService = new Storage.KeyDerivationService(storage, password.get());
keyDerivationService.setOnSucceeded(workerStateEvent -> { keyDerivationService.setOnSucceeded(workerStateEvent -> {
@ -792,7 +792,7 @@ public class AppController implements Initializable {
key = new Key(encryptionFullKey.getPrivKeyBytes(), storage.getKeyDeriver().getSalt(), EncryptionType.Deriver.ARGON2); key = new Key(encryptionFullKey.getPrivKeyBytes(), storage.getKeyDeriver().getSalt(), EncryptionType.Deriver.ARGON2);
wallet.encrypt(key); wallet.encrypt(key);
storage.setEncryptionPubKey(encryptionPubKey); storage.setEncryptionPubKey(encryptionPubKey);
addWalletTabOrWindow(storage, wallet, false); addWalletTabOrWindow(storage, wallet, null, false);
} finally { } finally {
encryptionFullKey.clear(); encryptionFullKey.clear();
if(key != null) { if(key != null) {
@ -856,12 +856,13 @@ public class AppController implements Initializable {
WalletTabData walletTabData = (WalletTabData) tabData; WalletTabData walletTabData = (WalletTabData) tabData;
Wallet wallet = walletTabData.getWallet(); Wallet wallet = walletTabData.getWallet();
Wallet pastWallet = wallet.copy(); Wallet pastWallet = wallet.copy();
walletTabData.getStorage().backupTempWallet();
wallet.clearHistory(); wallet.clearHistory();
EventManager.get().post(new WalletSettingsChangedEvent(wallet, pastWallet, walletTabData.getStorage().getWalletFile())); EventManager.get().post(new WalletSettingsChangedEvent(wallet, pastWallet, walletTabData.getStorage().getWalletFile()));
} }
} }
public void addWalletTabOrWindow(Storage storage, Wallet wallet, boolean forceSameWindow) { public void addWalletTabOrWindow(Storage storage, Wallet wallet, Wallet backupWallet, boolean forceSameWindow) {
Window existingWalletWindow = AppServices.get().getWindowForWallet(storage); Window existingWalletWindow = AppServices.get().getWindowForWallet(storage);
if(existingWalletWindow instanceof Stage) { if(existingWalletWindow instanceof Stage) {
Stage existingWalletStage = (Stage)existingWalletWindow; Stage existingWalletStage = (Stage)existingWalletWindow;
@ -876,13 +877,13 @@ public class AppController implements Initializable {
AppController appController = AppServices.newAppWindow(stage); AppController appController = AppServices.newAppWindow(stage);
stage.toFront(); stage.toFront();
stage.setX(AppServices.get().getWalletWindowMaxX() + 30); stage.setX(AppServices.get().getWalletWindowMaxX() + 30);
appController.addWalletTab(storage, wallet); appController.addWalletTab(storage, wallet, backupWallet);
} else { } else {
addWalletTab(storage, wallet); addWalletTab(storage, wallet, backupWallet);
} }
} }
public void addWalletTab(Storage storage, Wallet wallet) { public void addWalletTab(Storage storage, Wallet wallet, Wallet backupWallet) {
try { try {
String name = storage.getWalletFile().getName(); String name = storage.getWalletFile().getName();
if(name.endsWith(".json")) { if(name.endsWith(".json")) {
@ -901,7 +902,7 @@ public class AppController implements Initializable {
EventManager.get().post(new WalletOpeningEvent(storage, wallet)); 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. //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); WalletForm walletForm = new WalletForm(storage, wallet, backupWallet);
EventManager.get().register(walletForm); EventManager.get().register(walletForm);
controller.setWalletForm(walletForm); controller.setWalletForm(walletForm);

View file

@ -84,17 +84,16 @@ public class CoinTreeTable extends TreeTableView<Entry> {
WalletBirthDateDialog dlg = new WalletBirthDateDialog(wallet.getBirthDate()); WalletBirthDateDialog dlg = new WalletBirthDateDialog(wallet.getBirthDate());
Optional<Date> optDate = dlg.showAndWait(); Optional<Date> optDate = dlg.showAndWait();
if(optDate.isPresent()) { if(optDate.isPresent()) {
Wallet pastWallet = wallet.copy();
wallet.setBirthDate(optDate.get());
Storage storage = AppServices.get().getOpenWallets().get(wallet); Storage storage = AppServices.get().getOpenWallets().get(wallet);
if(storage != null) { Wallet pastWallet = wallet.copy();
storage.backupTempWallet();
wallet.setBirthDate(optDate.get());
//Trigger background save of birthdate //Trigger background save of birthdate
EventManager.get().post(new WalletDataChangedEvent(wallet)); EventManager.get().post(new WalletDataChangedEvent(wallet));
//Trigger full wallet rescan //Trigger full wallet rescan
wallet.clearHistory(); wallet.clearHistory();
EventManager.get().post(new WalletSettingsChangedEvent(wallet, pastWallet, storage.getWalletFile())); EventManager.get().post(new WalletSettingsChangedEvent(wallet, pastWallet, storage.getWalletFile()));
} }
}
}); });
if(wallet.getBirthDate() == null) { if(wallet.getBirthDate() == null) {
hyperlink.setText("Scan for previous transactions?"); hyperlink.setText("Scan for previous transactions?");

View file

@ -10,13 +10,14 @@ import com.sparrowwallet.drongo.crypto.*;
import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.drongo.protocol.Sha256Hash;
import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.MnemonicException;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.drongo.wallet.WalletNode; import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.sparrow.MainApp; import com.sparrowwallet.sparrow.MainApp;
import javafx.concurrent.Service; import javafx.concurrent.Service;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import org.controlsfx.tools.Platform; import org.controlsfx.tools.Platform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*; import java.io.*;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@ -26,14 +27,18 @@ import java.security.SecureRandom;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.*; import java.util.zip.*;
import static com.sparrowwallet.drongo.crypto.Argon2KeyDeriver.SPRW1_PARAMETERS; import static com.sparrowwallet.drongo.crypto.Argon2KeyDeriver.SPRW1_PARAMETERS;
public class Storage { public class Storage {
private static final Logger log = LoggerFactory.getLogger(Storage.class);
public static final ECKey NO_PASSWORD_KEY = ECKey.fromPublicOnly(ECKey.fromPrivate(Utils.hexToBytes("885e5a09708a167ea356a252387aa7c4893d138d632e296df8fbf5c12798bd28"))); public static final ECKey NO_PASSWORD_KEY = ECKey.fromPublicOnly(ECKey.fromPrivate(Utils.hexToBytes("885e5a09708a167ea356a252387aa7c4893d138d632e296df8fbf5c12798bd28")));
private static final DateFormat BACKUP_DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss"); private static final DateFormat BACKUP_DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss");
private static final Pattern DATE_PATTERN = Pattern.compile(".+-([0-9]{14}?).*");
public static final String SPARROW_DIR = ".sparrow"; public static final String SPARROW_DIR = ".sparrow";
public static final String WINDOWS_SPARROW_DIR = "Sparrow"; public static final String WINDOWS_SPARROW_DIR = "Sparrow";
@ -41,6 +46,7 @@ public class Storage {
public static final String WALLETS_BACKUP_DIR = "backup"; public static final String WALLETS_BACKUP_DIR = "backup";
public static final String HEADER_MAGIC_1 = "SPRW1"; public static final String HEADER_MAGIC_1 = "SPRW1";
private static final int BINARY_HEADER_LENGTH = 28; private static final int BINARY_HEADER_LENGTH = 28;
public static final String TEMP_BACKUP_EXTENSION = "tmp";
private File walletFile; private File walletFile;
private final Gson gson; private final Gson gson;
@ -81,17 +87,49 @@ public class Storage {
return gsonBuilder.setPrettyPrinting().disableHtmlEscaping().create(); return gsonBuilder.setPrettyPrinting().disableHtmlEscaping().create();
} }
public Wallet loadWallet() throws IOException { public WalletBackupAndKey loadWallet() throws IOException {
Reader reader = new FileReader(walletFile); Wallet wallet = loadWallet(walletFile);
Wallet backupWallet = null;
File[] backups = getBackups("json." + TEMP_BACKUP_EXTENSION);
if(backups.length > 0) {
try {
backupWallet = loadWallet(backups[0]);
} catch(Exception e) {
log.error("Error loading backup wallet " + TEMP_BACKUP_EXTENSION, e);
}
}
encryptionPubKey = NO_PASSWORD_KEY;
return new WalletBackupAndKey(wallet, backupWallet, null);
}
public Wallet loadWallet(File jsonFile) throws IOException {
Reader reader = new FileReader(jsonFile);
Wallet wallet = gson.fromJson(reader, Wallet.class); Wallet wallet = gson.fromJson(reader, Wallet.class);
reader.close(); reader.close();
encryptionPubKey = NO_PASSWORD_KEY;
return wallet; return wallet;
} }
public WalletAndKey loadWallet(CharSequence password) throws IOException, StorageException { public WalletBackupAndKey loadWallet(CharSequence password) throws IOException, StorageException {
InputStream fileStream = new FileInputStream(walletFile); WalletAndKey walletAndKey = loadWallet(walletFile, password);
WalletAndKey backupAndKey = new WalletAndKey(null, null);
File[] backups = getBackups(TEMP_BACKUP_EXTENSION, "json." + TEMP_BACKUP_EXTENSION);
if(backups.length > 0) {
try {
backupAndKey = loadWallet(backups[0], password);
} catch(Exception e) {
log.error("Error loading backup wallet " + TEMP_BACKUP_EXTENSION, e);
}
}
return new WalletBackupAndKey(walletAndKey.wallet, backupAndKey.wallet, walletAndKey.key);
}
public WalletAndKey loadWallet(File encryptedFile, CharSequence password) throws IOException, StorageException {
InputStream fileStream = new FileInputStream(encryptedFile);
ECKey encryptionKey = getEncryptionKey(password, fileStream); ECKey encryptionKey = getEncryptionKey(password, fileStream);
InputStream inputStream = new InflaterInputStream(new ECIESInputStream(fileStream, encryptionKey, getEncryptionMagic())); InputStream inputStream = new InflaterInputStream(new ECIESInputStream(fileStream, encryptionKey, getEncryptionMagic()));
@ -168,6 +206,18 @@ public class Storage {
} }
public void backupWallet() throws IOException { public void backupWallet() throws IOException {
backupWallet(null);
}
public void backupTempWallet() {
try {
backupWallet(TEMP_BACKUP_EXTENSION);
} catch(IOException e) {
log.error("Error creating ." + TEMP_BACKUP_EXTENSION + " backup wallet", e);
}
}
public void backupWallet(String extension) throws IOException {
File backupDir = getWalletsBackupDir(); File backupDir = getWalletsBackupDir();
Date backupDate = new Date(); Date backupDate = new Date();
@ -180,20 +230,50 @@ public class Storage {
backupName += dateSuffix; backupName += dateSuffix;
} }
if(extension != null) {
backupName += "." + extension;
}
File backupFile = new File(backupDir, backupName); File backupFile = new File(backupDir, backupName);
Files.copy(walletFile, backupFile); Files.copy(walletFile, backupFile);
} }
public void deleteBackups() { public void deleteBackups() {
deleteBackups(null);
}
public void deleteBackups(String extension) {
File[] backups = getBackups(extension);
for(File backup : backups) {
backup.delete();
}
}
private File[] getBackups(String extension) {
return getBackups(extension, null);
}
private File[] getBackups(String extension, String notExtension) {
File backupDir = getWalletsBackupDir(); File backupDir = getWalletsBackupDir();
File[] unencryptedBackups = backupDir.listFiles((dir, name) -> { File[] backups = backupDir.listFiles((dir, name) -> {
int dotIndex = name.lastIndexOf('.'); return name.startsWith(Files.getNameWithoutExtension(walletFile.getName()) + "-") &&
return name.startsWith(walletFile.getName() + "-") && name.substring(walletFile.getName().length() + 1, dotIndex > -1 ? dotIndex : name.length()).matches("[0-9]+"); getBackupDate(name) != null &&
(extension == null || name.endsWith("." + extension)) &&
(notExtension == null || !name.endsWith("." + notExtension));
}); });
for(File unencryptedBackup : unencryptedBackups) { Arrays.sort(backups, Comparator.comparing(o -> getBackupDate(((File)o).getName())).reversed());
unencryptedBackup.delete();
return backups;
} }
private String getBackupDate(String backupFileName) {
Matcher matcher = DATE_PATTERN.matcher(backupFileName);
if(matcher.matches()) {
return matcher.group(1);
}
return null;
} }
public ECKey getEncryptionPubKey() { public ECKey getEncryptionPubKey() {
@ -250,6 +330,8 @@ public class Storage {
} }
keyDeriver = new Argon2KeyDeriver(salt); keyDeriver = new Argon2KeyDeriver(salt);
} else if(inputStream != null) {
inputStream.skip(BINARY_HEADER_LENGTH);
} }
return keyDeriver; return keyDeriver;
@ -477,7 +559,16 @@ public class Storage {
} }
} }
public static class LoadWalletService extends Service<WalletAndKey> { public static class WalletBackupAndKey extends WalletAndKey {
public final Wallet backupWallet;
public WalletBackupAndKey(Wallet wallet, Wallet backupWallet, Key key) {
super(wallet, key);
this.backupWallet = backupWallet;
}
}
public static class LoadWalletService extends Service<WalletBackupAndKey> {
private final Storage storage; private final Storage storage;
private final SecureString password; private final SecureString password;
@ -487,12 +578,12 @@ public class Storage {
} }
@Override @Override
protected Task<WalletAndKey> createTask() { protected Task<WalletBackupAndKey> createTask() {
return new Task<>() { return new Task<>() {
protected WalletAndKey call() throws IOException, StorageException, MnemonicException { protected WalletBackupAndKey call() throws IOException, StorageException {
WalletAndKey walletAndKey = storage.loadWallet(password); WalletBackupAndKey walletBackupAndKey = storage.loadWallet(password);
password.clear(); password.clear();
return walletAndKey; return walletBackupAndKey;
} }
}; };
} }

View file

@ -443,14 +443,14 @@ public class SettingsController extends WalletFormController implements Initiali
return; return;
} }
walletForm.getWallet().encrypt(key);
walletForm.getStorage().setEncryptionPubKey(encryptionPubKey);
walletForm.saveAndRefresh();
if(dlg.isDeleteBackups()) { if(dlg.isDeleteBackups()) {
walletForm.deleteBackups(); walletForm.deleteBackups();
} }
walletForm.getWallet().encrypt(key);
walletForm.getStorage().setEncryptionPubKey(encryptionPubKey);
walletForm.saveAndRefresh();
if(requirement == WalletPasswordDialog.PasswordRequirement.UPDATE_NEW || requirement == WalletPasswordDialog.PasswordRequirement.UPDATE_EMPTY) { if(requirement == WalletPasswordDialog.PasswordRequirement.UPDATE_NEW || requirement == WalletPasswordDialog.PasswordRequirement.UPDATE_EMPTY) {
EventManager.get().post(new RequestOpenWalletsEvent()); EventManager.get().post(new RequestOpenWalletsEvent());
} }

View file

@ -17,7 +17,7 @@ public class SettingsWalletForm extends WalletForm {
private Wallet walletCopy; private Wallet walletCopy;
public SettingsWalletForm(Storage storage, Wallet currentWallet) { public SettingsWalletForm(Storage storage, Wallet currentWallet) {
super(storage, currentWallet); super(storage, currentWallet, null, false);
this.walletCopy = currentWallet.copy(); this.walletCopy = currentWallet.copy();
} }
@ -38,10 +38,13 @@ public class SettingsWalletForm extends WalletForm {
@Override @Override
public void saveAndRefresh() throws IOException { public void saveAndRefresh() throws IOException {
Wallet pastWallet = wallet.copy(); Wallet pastWallet = null;
boolean refreshAll = isRefreshNecessary(wallet, walletCopy); boolean refreshAll = isRefreshNecessary(wallet, walletCopy);
if(refreshAll) { if(refreshAll) {
pastWallet = wallet.copy();
save(); //Save here for the temp backup in case password has been changed
getStorage().backupTempWallet();
walletCopy.clearNodes(); walletCopy.clearNodes();
} }

View file

@ -34,10 +34,20 @@ public class WalletForm {
private final List<NodeEntry> accountEntries = new ArrayList<>(); private final List<NodeEntry> accountEntries = new ArrayList<>();
private final List<Set<WalletNode>> walletTransactionNodes = new ArrayList<>(); private final List<Set<WalletNode>> walletTransactionNodes = new ArrayList<>();
public WalletForm(Storage storage, Wallet currentWallet) { public WalletForm(Storage storage, Wallet currentWallet, Wallet backupWallet) {
this(storage, currentWallet, backupWallet, true);
}
public WalletForm(Storage storage, Wallet currentWallet, Wallet backupWallet, boolean refreshHistory) {
this.storage = storage; this.storage = storage;
this.wallet = currentWallet; this.wallet = currentWallet;
refreshHistory(AppServices.getCurrentBlockHeight(), null);
//Unencrypted wallets load before isConnected is true, waiting for the ConnectionEvent to refresh history - save the backup for this event
savedPastWallet = backupWallet;
if(refreshHistory) {
refreshHistory(AppServices.getCurrentBlockHeight(), backupWallet);
}
} }
public Wallet getWallet() { public Wallet getWallet() {
@ -66,6 +76,7 @@ public class WalletForm {
public void saveAndRefresh() throws IOException { public void saveAndRefresh() throws IOException {
Wallet pastWallet = wallet.copy(); Wallet pastWallet = wallet.copy();
storage.backupTempWallet();
wallet.clearHistory(); wallet.clearHistory();
save(); save();
refreshHistory(AppServices.getCurrentBlockHeight(), pastWallet); refreshHistory(AppServices.getCurrentBlockHeight(), pastWallet);
@ -106,31 +117,36 @@ public class WalletForm {
wallet.setStoredBlockHeight(blockHeight); wallet.setStoredBlockHeight(blockHeight);
} }
boolean labelsChanged = false;
if(pastWallet != null) { if(pastWallet != null) {
copyLabels(pastWallet); labelsChanged = copyLabels(pastWallet);
} }
notifyIfChanged(blockHeight, previousWallet); notifyIfChanged(blockHeight, previousWallet, labelsChanged);
} }
private void copyLabels(Wallet pastWallet) { private boolean copyLabels(Wallet pastWallet) {
wallet.getNode(KeyPurpose.RECEIVE).copyLabels(pastWallet.getNode(KeyPurpose.RECEIVE)); boolean changed = wallet.getNode(KeyPurpose.RECEIVE).copyLabels(pastWallet.getNode(KeyPurpose.RECEIVE));
wallet.getNode(KeyPurpose.CHANGE).copyLabels(pastWallet.getNode(KeyPurpose.CHANGE)); changed |= wallet.getNode(KeyPurpose.CHANGE).copyLabels(pastWallet.getNode(KeyPurpose.CHANGE));
for(Map.Entry<Sha256Hash, BlockTransaction> txEntry : wallet.getTransactions().entrySet()) { for(Map.Entry<Sha256Hash, BlockTransaction> txEntry : wallet.getTransactions().entrySet()) {
BlockTransaction pastBlockTransaction = pastWallet.getTransactions().get(txEntry.getKey()); BlockTransaction pastBlockTransaction = pastWallet.getTransactions().get(txEntry.getKey());
if(pastBlockTransaction != null && txEntry.getValue() != null && txEntry.getValue().getLabel() == null && pastBlockTransaction.getLabel() != null) { if(pastBlockTransaction != null && txEntry.getValue() != null && txEntry.getValue().getLabel() == null && pastBlockTransaction.getLabel() != null) {
txEntry.getValue().setLabel(pastBlockTransaction.getLabel()); txEntry.getValue().setLabel(pastBlockTransaction.getLabel());
} changed = true;
} }
} }
private void notifyIfChanged(Integer blockHeight, Wallet previousWallet) { storage.deleteBackups(Storage.TEMP_BACKUP_EXTENSION);
return changed;
}
private void notifyIfChanged(Integer blockHeight, Wallet previousWallet, boolean labelsChanged) {
List<WalletNode> historyChangedNodes = new ArrayList<>(); List<WalletNode> historyChangedNodes = new ArrayList<>();
historyChangedNodes.addAll(getHistoryChangedNodes(previousWallet.getNode(KeyPurpose.RECEIVE).getChildren(), wallet.getNode(KeyPurpose.RECEIVE).getChildren())); 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())); historyChangedNodes.addAll(getHistoryChangedNodes(previousWallet.getNode(KeyPurpose.CHANGE).getChildren(), wallet.getNode(KeyPurpose.CHANGE).getChildren()));
boolean changed = false; boolean changed = labelsChanged;
if(!historyChangedNodes.isEmpty()) { if(!historyChangedNodes.isEmpty()) {
Platform.runLater(() -> EventManager.get().post(new WalletHistoryChangedEvent(wallet, storage, historyChangedNodes))); Platform.runLater(() -> EventManager.get().post(new WalletHistoryChangedEvent(wallet, storage, historyChangedNodes)));
changed = true; changed = true;
@ -254,7 +270,6 @@ public class WalletForm {
EventManager.get().post(new WalletNodesChangedEvent(wallet)); 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 //It is necessary to save the past wallet because the actual copying of the past labels only occurs on a later ConnectionEvent with bwt
//The savedPastWallet variable can be removed once bwt supports dynamic loading of wallets without needing to disconnect/reconnect
if(Config.get().getServerType() == ServerType.BITCOIN_CORE) { if(Config.get().getServerType() == ServerType.BITCOIN_CORE) {
savedPastWallet = event.getPastWallet(); savedPastWallet = event.getPastWallet();
} }