mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
dont lose labels when rescanning, even if app is restarted
This commit is contained in:
parent
3bd2f69157
commit
6a58e8a799
7 changed files with 169 additions and 60 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 08acfe5ba16ed54ebbdcb8177cea88e4c53bda77
|
Subproject commit faa8f71313ff102c9611f8a9265511029654a83c
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -84,16 +84,15 @@ 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();
|
||||||
//Trigger background save of birthdate
|
storage.backupTempWallet();
|
||||||
EventManager.get().post(new WalletDataChangedEvent(wallet));
|
wallet.setBirthDate(optDate.get());
|
||||||
//Trigger full wallet rescan
|
//Trigger background save of birthdate
|
||||||
wallet.clearHistory();
|
EventManager.get().post(new WalletDataChangedEvent(wallet));
|
||||||
EventManager.get().post(new WalletSettingsChangedEvent(wallet, pastWallet, storage.getWalletFile()));
|
//Trigger full wallet rescan
|
||||||
}
|
wallet.clearHistory();
|
||||||
|
EventManager.get().post(new WalletSettingsChangedEvent(wallet, pastWallet, storage.getWalletFile()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if(wallet.getBirthDate() == null) {
|
if(wallet.getBirthDate() == null) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storage.deleteBackups(Storage.TEMP_BACKUP_EXTENSION);
|
||||||
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyIfChanged(Integer blockHeight, Wallet previousWallet) {
|
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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue