mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-25 23:39:26 -05:00
Update SeedWordsView.java
Summaries: - Restored lost code parts. - Added UI updates - Using char for memory safe seed usage
This commit is contained in:
parent
650481526d
commit
1933cc8b28
@ -53,6 +53,18 @@ import static haveno.desktop.util.FormBuilder.addTitledGroupBg;
|
|||||||
import static haveno.desktop.util.FormBuilder.addTopLabelDatePicker;
|
import static haveno.desktop.util.FormBuilder.addTopLabelDatePicker;
|
||||||
import static haveno.desktop.util.FormBuilder.addTopLabelTextArea;
|
import static haveno.desktop.util.FormBuilder.addTopLabelTextArea;
|
||||||
import haveno.desktop.util.Layout;
|
import haveno.desktop.util.Layout;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
import java.util.List;
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
@ -68,14 +80,7 @@ import net.glxn.qrgen.image.ImageType;
|
|||||||
import org.bitcoinj.crypto.MnemonicCode;
|
import org.bitcoinj.crypto.MnemonicCode;
|
||||||
import org.bitcoinj.crypto.MnemonicException;
|
import org.bitcoinj.crypto.MnemonicException;
|
||||||
import org.bitcoinj.wallet.DeterministicSeed;
|
import org.bitcoinj.wallet.DeterministicSeed;
|
||||||
|
//import static javafx.beans.binding.Bindings.createBooleanBinding;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@FxmlView
|
@FxmlView
|
||||||
public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
||||||
@ -94,7 +99,7 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
|||||||
private final SimpleBooleanProperty seedWordsValid = new SimpleBooleanProperty(false);
|
private final SimpleBooleanProperty seedWordsValid = new SimpleBooleanProperty(false);
|
||||||
private ChangeListener<String> seedWordsTextAreaChangeListener;
|
private ChangeListener<String> seedWordsTextAreaChangeListener;
|
||||||
private final BooleanProperty seedWordsEdited = new SimpleBooleanProperty();
|
private final BooleanProperty seedWordsEdited = new SimpleBooleanProperty();
|
||||||
private String seedWordText;
|
private char[] seedWordText;
|
||||||
private LocalDate walletCreationDate;
|
private LocalDate walletCreationDate;
|
||||||
|
|
||||||
private ImageView qrCodeImageView;
|
private ImageView qrCodeImageView;
|
||||||
@ -138,8 +143,25 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
|||||||
datePicker = addTopLabelDatePicker(root, ++gridRow, Res.get("seed.date"), 10).second;
|
datePicker = addTopLabelDatePicker(root, ++gridRow, Res.get("seed.date"), 10).second;
|
||||||
datePicker.setMouseTransparent(true);
|
datePicker.setMouseTransparent(true);
|
||||||
|
|
||||||
|
// TODO: to re-enable restore functionality:
|
||||||
|
// - uncomment code throughout this file
|
||||||
|
// - support getting wallet's restore height
|
||||||
|
// - support translating between date and restore height
|
||||||
|
// - clear XmrAddressEntries which are incompatible with new wallet and other tests
|
||||||
|
// - update mnemonic validation and restore calls
|
||||||
|
|
||||||
|
// addTitledGroupBg(root, ++gridRow, 3, Res.get("seed.restore.title"), Layout.GROUP_DISTANCE);
|
||||||
|
// seedWordsTextArea = addTopLabelTextArea(root, gridRow, Res.get("seed.seedWords"), "", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
|
||||||
|
// seedWordsTextArea.getStyleClass().add("wallet-seed-words");
|
||||||
|
// seedWordsTextArea.setPrefHeight(40);
|
||||||
|
// seedWordsTextArea.setMaxHeight(40);
|
||||||
|
|
||||||
|
// restoreDatePicker = addTopLabelDatePicker(root, ++gridRow, Res.get("seed.date"), 10).second;
|
||||||
|
// restoreButton = addPrimaryActionButtonAFterGroup(root, ++gridRow, Res.get("seed.restore"));
|
||||||
|
|
||||||
addTitledGroupBg(root, ++gridRow, 1, Res.get("shared.information"), Layout.GROUP_DISTANCE);
|
addTitledGroupBg(root, ++gridRow, 1, Res.get("shared.information"), Layout.GROUP_DISTANCE);
|
||||||
addMultilineLabel(root, gridRow, Res.get("account.seed.info"), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
|
addMultilineLabel(root, gridRow, Res.get("account.seed.info"),
|
||||||
|
Layout.FIRST_ROW_AND_GROUP_DISTANCE);
|
||||||
|
|
||||||
seedWordsValidChangeListener = (observable, oldValue, newValue) -> {
|
seedWordsValidChangeListener = (observable, oldValue, newValue) -> {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
@ -163,6 +185,22 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activate() {
|
public void activate() {
|
||||||
|
// seedWordsValid.addListener(seedWordsValidChangeListener);
|
||||||
|
// seedWordsTextArea.textProperty().addListener(seedWordsTextAreaChangeListener);
|
||||||
|
// restoreButton.disableProperty().bind(createBooleanBinding(() -> !seedWordsValid.get() || !seedWordsEdited.get(),
|
||||||
|
// seedWordsValid, seedWordsEdited));
|
||||||
|
|
||||||
|
// restoreButton.setOnAction(e -> {
|
||||||
|
// new Popup().information(Res.get("account.seed.restore.info"))
|
||||||
|
// .closeButtonText(Res.get("shared.cancel"))
|
||||||
|
// .actionButtonText(Res.get("account.seed.restore.ok"))
|
||||||
|
// .onAction(this::onRestore)
|
||||||
|
// .show();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// seedWordsTextArea.getStyleClass().remove("validation-error");
|
||||||
|
// restoreDatePicker.getStyleClass().remove("validation-error");
|
||||||
|
|
||||||
String key = "showBackupWarningAtSeedPhrase";
|
String key = "showBackupWarningAtSeedPhrase";
|
||||||
if (DontShowAgainLookup.showAgain(key)) {
|
if (DontShowAgainLookup.showAgain(key)) {
|
||||||
new Popup().warning(Res.get("account.seed.backup.warning"))
|
new Popup().warning(Res.get("account.seed.backup.warning"))
|
||||||
@ -181,43 +219,143 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
|||||||
if (xmrWalletService.isWalletEncrypted()) {
|
if (xmrWalletService.isWalletEncrypted()) {
|
||||||
askForPassword();
|
askForPassword();
|
||||||
} else {
|
} else {
|
||||||
initSeedWords(xmrWalletService.getWallet().getSeed());
|
String key = "showSeedWordsWarning";
|
||||||
showSeedScreen();
|
if (DontShowAgainLookup.showAgain(key)) {
|
||||||
|
new Popup().warning(Res.get("account.seed.warn.noPw.msg"))
|
||||||
|
.actionButtonText(Res.get("account.seed.warn.noPw.yes"))
|
||||||
|
.onAction(() -> {
|
||||||
|
DontShowAgainLookup.dontShowAgain(key, true);
|
||||||
|
initSeedWords(xmrWalletService.getWallet().getSeed());
|
||||||
|
Platform.runLater(this::showSeedScreen);
|
||||||
|
})
|
||||||
|
.closeButtonText(Res.get("shared.no"))
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
initSeedWords(xmrWalletService.getWallet().getSeed());
|
||||||
|
Platform.runLater(this::showSeedScreen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSeedScreen() {
|
@Override
|
||||||
displaySeedWordsTextArea.setText(seedWordText);
|
protected void deactivate() {
|
||||||
walletCreationDate = Instant.ofEpochSecond(xmrWalletService.getWalletCreationDate()).atZone(ZoneId.systemDefault()).toLocalDate();
|
clearSensitiveData();
|
||||||
datePicker.setValue(walletCreationDate);
|
datePicker.setValue(null);
|
||||||
|
|
||||||
// Generate QR code from the seed words and display it
|
// seedWordsValid.removeListener(seedWordsValidChangeListener);
|
||||||
generateAndDisplayQRCode(seedWordText);
|
// seedWordsTextArea.textProperty().removeListener(seedWordsTextAreaChangeListener);
|
||||||
|
// restoreButton.disableProperty().unbind();
|
||||||
|
// restoreButton.setOnAction(null);
|
||||||
|
|
||||||
|
// seedWordsTextArea.setText("");
|
||||||
|
|
||||||
|
// restoreDatePicker.setValue(null);
|
||||||
|
|
||||||
|
// seedWordsTextArea.getStyleClass().remove("validation-error");
|
||||||
|
// restoreDatePicker.getStyleClass().remove("validation-error");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearSensitiveData() {
|
||||||
|
if (seedWordText != null) {
|
||||||
|
// Overwrite seed words in memory before clearing
|
||||||
|
java.util.Arrays.fill(seedWordText, ' ');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateAndDisplayQRCode(String seedWords) {
|
private void generateAndDisplayQRCode(String seedWords) {
|
||||||
// Generate QR Code using the net.glxn.qrgen library
|
Platform.runLater(() -> {
|
||||||
ByteArrayInputStream qrCodeStream = new ByteArrayInputStream(
|
// Generate QR Code using the net.glxn.qrgen library
|
||||||
QRCode.from(seedWords).to(ImageType.PNG).stream().toByteArray()
|
ByteArrayInputStream qrCodeStream = new ByteArrayInputStream(
|
||||||
);
|
QRCode.from(seedWords).to(ImageType.PNG).stream().toByteArray()
|
||||||
Image qrCodeImage = new Image(qrCodeStream);
|
);
|
||||||
qrCodeImageView.setImage(qrCodeImage);
|
Image qrCodeImage = new Image(qrCodeStream);
|
||||||
|
qrCodeImageView.setImage(qrCodeImage);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void askForPassword() {
|
private void askForPassword() {
|
||||||
walletPasswordWindow.headLine(Res.get("account.seed.enterPw")).onSuccess(() -> {
|
walletPasswordWindow.headLine(Res.get("account.seed.enterPw")).onSuccess(() -> {
|
||||||
initSeedWords(xmrWalletService.getWallet().getSeed());
|
initSeedWords(xmrWalletService.getWallet().getSeed());
|
||||||
showSeedScreen();
|
Platform.runLater(this::showSeedScreen);
|
||||||
}).hideForgotPasswordButton().show();
|
}).hideForgotPasswordButton().show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initSeedWords(String seed) {
|
private void initSeedWords(String seed) {
|
||||||
seedWordText = seed;
|
seedWordText = seed.toCharArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void showSeedScreen() {
|
||||||
protected void deactivate() {
|
displaySeedWordsTextArea.setText(new String(seedWordText));
|
||||||
displaySeedWordsTextArea.setText("");
|
walletCreationDate = Instant.ofEpochSecond(xmrWalletService.getWalletCreationDate()).atZone(ZoneId.systemDefault()).toLocalDate();
|
||||||
datePicker.setValue(null);
|
datePicker.setValue(walletCreationDate);
|
||||||
|
generateAndDisplayQRCode(new String(seedWordText));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onRestore() {
|
||||||
|
if (walletsManager.hasPositiveBalance()) {
|
||||||
|
new Popup().warning(Res.get("seed.warn.walletNotEmpty.msg"))
|
||||||
|
.actionButtonText(Res.get("seed.warn.walletNotEmpty.restore"))
|
||||||
|
.onAction(this::checkIfEncrypted)
|
||||||
|
.closeButtonText(Res.get("seed.warn.walletNotEmpty.emptyWallet"))
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
checkIfEncrypted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkIfEncrypted() {
|
||||||
|
if (walletsManager.areWalletsEncrypted()) {
|
||||||
|
new Popup().information(Res.get("seed.warn.notEncryptedAnymore"))
|
||||||
|
.closeButtonText(Res.get("shared.no"))
|
||||||
|
.actionButtonText(Res.get("shared.yes"))
|
||||||
|
.onAction(this::doRestoreDateCheck)
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
doRestoreDateCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doRestoreDateCheck() {
|
||||||
|
if (restoreDatePicker.getValue() == null) {
|
||||||
|
// Provide feedback when attempting to restore a wallet from seed words without specifying a date
|
||||||
|
new Popup().information(Res.get("seed.warn.walletDateEmpty"))
|
||||||
|
.closeButtonText(Res.get("shared.no"))
|
||||||
|
.actionButtonText(Res.get("shared.yes"))
|
||||||
|
.onAction(this::doRestore)
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
doRestore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalDate getWalletDate() {
|
||||||
|
LocalDate walletDate = restoreDatePicker.getValue();
|
||||||
|
// Even though no current Haveno wallet could have been created before the v0.5 release date (2017.06.28),
|
||||||
|
// the user may want to import from a seed generated by another wallet.
|
||||||
|
// So use when the BIP39 standard was finalised (2013.10.09) as the oldest possible wallet date.
|
||||||
|
LocalDate oldestWalletDate = LocalDate.ofInstant(
|
||||||
|
Instant.ofEpochMilli(MnemonicCode.BIP39_STANDARDISATION_TIME_SECS * 1000),
|
||||||
|
TimeZone.getDefault().toZoneId());
|
||||||
|
if (walletDate == null) {
|
||||||
|
// No date was specified, perhaps the user doesn't know the wallet date
|
||||||
|
walletDate = oldestWalletDate;
|
||||||
|
} else if (walletDate.isBefore(oldestWalletDate)) {
|
||||||
|
walletDate = oldestWalletDate;
|
||||||
|
} else if (walletDate.isAfter(LocalDate.now())) {
|
||||||
|
walletDate = LocalDate.now();
|
||||||
|
}
|
||||||
|
return walletDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doRestore() {
|
||||||
|
LocalDate walletDate = getWalletDate();
|
||||||
|
// We subtract 1 day to be sure to not have any issues with timezones. Even if we can be sure that the timezone
|
||||||
|
// is handled correctly it could be that the user created the wallet in one timezone and make a restore at
|
||||||
|
// a different timezone which could lead in the worst case that he miss the first day of the wallet transactions.
|
||||||
|
LocalDateTime localDateTime = walletDate.atStartOfDay().minusDays(1);
|
||||||
|
long date = localDateTime.toEpochSecond(ZoneOffset.UTC);
|
||||||
|
|
||||||
|
DeterministicSeed seed = new DeterministicSeed(List.of(new String(seedWordText).split(" ")), null, "", date);
|
||||||
|
SharedPresentation.restoreSeedWords(walletsManager, openOfferManager, seed, storageDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user