extra logging, backup existing wallet first functionality

This commit is contained in:
Craig Raw 2020-08-05 13:58:54 +02:00
parent 03abc59ed3
commit ce60fe3dc6
7 changed files with 75 additions and 3 deletions

View file

@ -430,6 +430,7 @@ public class AppController implements Initializable {
showErrorDialog("Invalid QR Code", result.error);
}
if(result.exception != null) {
log.error("Error opening webcam", result.exception);
showErrorDialog("Error opening webcam", result.exception.getMessage());
}
}
@ -474,6 +475,7 @@ public class AppController implements Initializable {
}
}
} catch(IOException e) {
log.error("Error saving transaction", e);
AppController.showErrorDialog("Error saving transaction", "Cannot write to " + file.getAbsolutePath());
}
}
@ -610,6 +612,7 @@ public class AppController implements Initializable {
if(exception instanceof InvalidPasswordException) {
showErrorDialog("Invalid Password", "The wallet password was invalid.");
} else {
log.error("Error Opening Wallet", exception);
showErrorDialog("Error Opening Wallet", exception.getMessage());
}
});

View file

@ -20,11 +20,13 @@ public class WalletPasswordDialog extends Dialog<SecureString> {
private final PasswordRequirement requirement;
private final CustomPasswordField password;
private final CustomPasswordField passwordConfirm;
private final CheckBox backupExisting;
public WalletPasswordDialog(PasswordRequirement requirement) {
this.requirement = requirement;
this.password = (CustomPasswordField)TextFields.createClearablePasswordField();
this.passwordConfirm = (CustomPasswordField)TextFields.createClearablePasswordField();
this.backupExisting = new CheckBox("Backup existing wallet first");
final DialogPane dialogPane = getDialogPane();
setTitle("Wallet Password");
@ -32,7 +34,7 @@ public class WalletPasswordDialog extends Dialog<SecureString> {
dialogPane.getStylesheets().add(AppController.class.getResource("general.css").toExternalForm());
dialogPane.getButtonTypes().addAll(ButtonType.CANCEL);
dialogPane.setPrefWidth(380);
dialogPane.setPrefHeight(250);
dialogPane.setPrefHeight(260);
Glyph lock = new Glyph("FontAwesome", FontAwesome.Glyph.LOCK);
lock.setFontSize(50);
@ -43,6 +45,11 @@ public class WalletPasswordDialog extends Dialog<SecureString> {
content.getChildren().add(password);
content.getChildren().add(passwordConfirm);
if(requirement == PasswordRequirement.UPDATE_EMPTY || requirement == PasswordRequirement.UPDATE_SET) {
content.getChildren().add(backupExisting);
backupExisting.setSelected(true);
}
dialogPane.setContent(content);
ValidationSupport validationSupport = new ValidationSupport();
@ -84,6 +91,10 @@ public class WalletPasswordDialog extends Dialog<SecureString> {
setResultConverter(dialogButton -> dialogButton == okButtonType ? new SecureString(password.getText()) : null);
}
public boolean isBackupExisting() {
return backupExisting.isSelected();
}
public enum PasswordRequirement {
LOAD("Please enter the wallet password:", "Unlock"),
UPDATE_NEW("Add a password to the wallet?\nLeave empty for none:", "No Password"),

View file

@ -1,5 +1,6 @@
package com.sparrowwallet.sparrow.io;
import com.google.common.io.Files;
import com.google.gson.*;
import com.sparrowwallet.drongo.ExtendedKey;
import com.sparrowwallet.drongo.SecureString;
@ -19,6 +20,8 @@ import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.*;
@ -27,8 +30,11 @@ import static com.sparrowwallet.drongo.crypto.Argon2KeyDeriver.SPRW1_PARAMETERS;
public class Storage {
public static final ECKey NO_PASSWORD_KEY = ECKey.fromPublicOnly(ECKey.fromPrivate(Utils.hexToBytes("885e5a09708a167ea356a252387aa7c4893d138d632e296df8fbf5c12798bd28")));
private static final DateFormat BACKUP_DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss");
public static final String SPARROW_DIR = ".sparrow";
public static final String WALLETS_DIR = "wallets";
public static final String WALLETS_BACKUP_DIR = "backup";
public static final String HEADER_MAGIC_1 = "SPRW1";
private static final int BINARY_HEADER_LENGTH = 28;
@ -157,6 +163,23 @@ public class Storage {
outputStream.write(encoded);
}
public void backupWallet() throws IOException {
File backupDir = getWalletsBackupDir();
Date backupDate = new Date();
String backupName = walletFile.getName();
String dateSuffix = "-" + BACKUP_DATE_FORMAT.format(backupDate);
int lastDot = backupName.lastIndexOf('.');
if(lastDot > 0) {
backupName = backupName.substring(0, lastDot) + dateSuffix + backupName.substring(lastDot);
} else {
backupName += dateSuffix;
}
File backupFile = new File(backupDir, backupName);
Files.copy(walletFile, backupFile);
}
public ECKey getEncryptionPubKey() {
return encryptionPubKey;
}
@ -228,6 +251,15 @@ public class Storage {
return new File(getWalletsDir(), walletName);
}
public static File getWalletsBackupDir() {
File walletsBackupDir = new File(getWalletsDir(), WALLETS_BACKUP_DIR);
if(!walletsBackupDir.exists()) {
walletsBackupDir.mkdirs();
}
return walletsBackupDir;
}
public static File getWalletsDir() {
File walletsDir = new File(getSparrowDir(), WALLETS_DIR);
if(!walletsDir.exists()) {

View file

@ -26,6 +26,8 @@ import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.controlsfx.glyphfont.Glyph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tornadofx.control.DateTimePicker;
import tornadofx.control.Field;
import tornadofx.control.Fieldset;
@ -43,6 +45,7 @@ import java.util.*;
import java.util.stream.Collectors;
public class HeadersController extends TransactionFormController implements Initializable {
private static final Logger log = LoggerFactory.getLogger(HeadersController.class);
public static final String LOCKTIME_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String BLOCK_TIMESTAMP_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss ZZZ";
public static final String UNFINALIZED_TXID_CLASS = "unfinalized-txid";
@ -561,6 +564,7 @@ public class HeadersController extends TransactionFormController implements Init
writer.print(headersForm.getPsbt().toBase64String());
}
} catch(IOException e) {
log.error("Error saving PSBT", e);
AppController.showErrorDialog("Error saving PSBT", "Cannot write to " + file.getAbsolutePath());
}
}
@ -617,6 +621,7 @@ public class HeadersController extends TransactionFormController implements Init
unencryptedWallet.sign(headersForm.getPsbt());
updateSignedKeystores(headersForm.getSigningWallet());
} catch(Exception e) {
log.warn("Failed to Sign", e);
AppController.showErrorDialog("Failed to Sign", e.getMessage());
}
}
@ -707,6 +712,7 @@ public class HeadersController extends TransactionFormController implements Init
writer.print(Utils.bytesToHex(finalTx.bitcoinSerialize()));
}
} catch(IOException e) {
log.error("Error saving transaction", e);
AppController.showErrorDialog("Error saving transaction", "Cannot write to " + file.getAbsolutePath());
}
}

View file

@ -17,7 +17,6 @@ import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
import com.sparrowwallet.sparrow.event.SettingsChangedEvent;
import com.sparrowwallet.sparrow.event.StorageEvent;
import com.sparrowwallet.sparrow.event.TimedEvent;
import com.sparrowwallet.sparrow.event.WalletSettingsChangedEvent;
import com.sparrowwallet.sparrow.io.Storage;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
@ -28,6 +27,8 @@ import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import org.controlsfx.control.RangeSlider;
import org.controlsfx.tools.Borders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tornadofx.control.Fieldset;
import java.io.IOException;
@ -39,6 +40,7 @@ import java.util.ResourceBundle;
import java.util.stream.Collectors;
public class SettingsController extends WalletFormController implements Initializable {
private static final Logger log = LoggerFactory.getLogger(SettingsController.class);
@FXML
private ComboBox<PolicyType> policyType;
@ -272,11 +274,24 @@ public class SettingsController extends WalletFormController implements Initiali
WalletPasswordDialog dlg = new WalletPasswordDialog(requirement);
Optional<SecureString> password = dlg.showAndWait();
if(password.isPresent()) {
if(dlg.isBackupExisting()) {
try {
walletForm.saveBackup();
} catch(IOException e) {
log.error("Error saving wallet backup", e);
AppController.showErrorDialog("Error saving wallet backup", e.getMessage());
revert.setDisable(false);
apply.setDisable(false);
return;
}
}
if(password.get().length() == 0) {
try {
walletForm.getStorage().setEncryptionPubKey(Storage.NO_PASSWORD_KEY);
walletForm.saveAndRefresh();
} catch (IOException e) {
log.error("Error saving wallet", e);
AppController.showErrorDialog("Error saving wallet", e.getMessage());
revert.setDisable(false);
apply.setDisable(false);
@ -304,6 +319,7 @@ public class SettingsController extends WalletFormController implements Initiali
walletForm.getStorage().setEncryptionPubKey(encryptionPubKey);
walletForm.saveAndRefresh();
} catch (Exception e) {
log.error("Error saving wallet", e);
AppController.showErrorDialog("Error saving wallet", e.getMessage());
revert.setDisable(false);
apply.setDisable(false);

View file

@ -59,6 +59,10 @@ public class WalletForm {
refreshHistory(AppController.getCurrentBlockHeight());
}
public void saveBackup() throws IOException {
storage.backupWallet();
}
public void refreshHistory(Integer blockHeight) {
Wallet previousWallet = wallet.copy();
if(wallet.isValid() && AppController.isOnline()) {

View file

@ -6,7 +6,7 @@
}
.form .wideLabelFieldSet.fieldset:horizontal .label-container {
-fx-pref-width: 160px;
-fx-pref-width: 170px;
-fx-pref-height: 25px;
}