mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
generate new bip39 word list
This commit is contained in:
parent
6872e069a9
commit
82a113afc3
4 changed files with 97 additions and 37 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit be0c4d1176da41671c2629e65f812fd28fab202b
|
Subproject commit 0f008b8985253c4fd0025c136d0a23ac3082537f
|
|
@ -40,10 +40,10 @@ public class MainApp extends Application {
|
||||||
wallet.setPolicyType(PolicyType.SINGLE);
|
wallet.setPolicyType(PolicyType.SINGLE);
|
||||||
wallet.setScriptType(ScriptType.P2WPKH);
|
wallet.setScriptType(ScriptType.P2WPKH);
|
||||||
|
|
||||||
// KeystoreImportDialog dlg = new KeystoreImportDialog(wallet);
|
KeystoreImportDialog dlg = new KeystoreImportDialog(wallet);
|
||||||
// dlg.showAndWait();
|
dlg.showAndWait();
|
||||||
|
|
||||||
stage.show();
|
// stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
|
@ -2,17 +2,15 @@ package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.KeyDerivation;
|
import com.sparrowwallet.drongo.KeyDerivation;
|
||||||
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||||
import com.sparrowwallet.drongo.wallet.Bip39Calculator;
|
import com.sparrowwallet.drongo.wallet.Bip39MnemonicCode;
|
||||||
|
import com.sparrowwallet.drongo.wallet.DeterministicSeed;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
|
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
|
||||||
import com.sparrowwallet.sparrow.io.*;
|
import com.sparrowwallet.sparrow.io.*;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleListProperty;
|
import javafx.beans.property.SimpleListProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.value.ChangeListener;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
@ -24,7 +22,6 @@ import javafx.scene.control.*;
|
||||||
import javafx.scene.layout.*;
|
import javafx.scene.layout.*;
|
||||||
import javafx.util.Callback;
|
import javafx.util.Callback;
|
||||||
import org.controlsfx.control.textfield.AutoCompletionBinding;
|
import org.controlsfx.control.textfield.AutoCompletionBinding;
|
||||||
import org.controlsfx.control.textfield.CustomPasswordField;
|
|
||||||
import org.controlsfx.control.textfield.CustomTextField;
|
import org.controlsfx.control.textfield.CustomTextField;
|
||||||
import org.controlsfx.control.textfield.TextFields;
|
import org.controlsfx.control.textfield.TextFields;
|
||||||
import org.controlsfx.validation.ValidationResult;
|
import org.controlsfx.validation.ValidationResult;
|
||||||
|
@ -32,6 +29,7 @@ import org.controlsfx.validation.ValidationSupport;
|
||||||
import org.controlsfx.validation.Validator;
|
import org.controlsfx.validation.Validator;
|
||||||
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
|
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -42,6 +40,11 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
||||||
private SplitMenuButton enterMnemonicButton;
|
private SplitMenuButton enterMnemonicButton;
|
||||||
private SplitMenuButton importButton;
|
private SplitMenuButton importButton;
|
||||||
|
|
||||||
|
private TilePane wordsPane;
|
||||||
|
private Button verifyButton;
|
||||||
|
private Button confirmButton;
|
||||||
|
private List<String> generatedMnemonicCode;
|
||||||
|
|
||||||
private SimpleListProperty<String> wordEntriesProperty;
|
private SimpleListProperty<String> wordEntriesProperty;
|
||||||
private final SimpleStringProperty passphraseProperty = new SimpleStringProperty();
|
private final SimpleStringProperty passphraseProperty = new SimpleStringProperty();
|
||||||
|
|
||||||
|
@ -107,6 +110,7 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enterMnemonic(int numWords) {
|
private void enterMnemonic(int numWords) {
|
||||||
|
generatedMnemonicCode = null;
|
||||||
setDescription("Enter mnemonic word list");
|
setDescription("Enter mnemonic word list");
|
||||||
showHideLink.setVisible(false);
|
showHideLink.setVisible(false);
|
||||||
setContent(getMnemonicWordsEntry(numWords));
|
setContent(getMnemonicWordsEntry(numWords));
|
||||||
|
@ -117,11 +121,11 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
||||||
VBox vBox = new VBox();
|
VBox vBox = new VBox();
|
||||||
vBox.setSpacing(10);
|
vBox.setSpacing(10);
|
||||||
|
|
||||||
TilePane tilePane = new TilePane();
|
wordsPane = new TilePane();
|
||||||
tilePane.setPrefRows(numWords/3);
|
wordsPane.setPrefRows(numWords/3);
|
||||||
tilePane.setHgap(10);
|
wordsPane.setHgap(10);
|
||||||
tilePane.setVgap(10);
|
wordsPane.setVgap(10);
|
||||||
tilePane.setOrientation(Orientation.VERTICAL);
|
wordsPane.setOrientation(Orientation.VERTICAL);
|
||||||
|
|
||||||
List<String> words = new ArrayList<>();
|
List<String> words = new ArrayList<>();
|
||||||
for(int i = 0; i < numWords; i++) {
|
for(int i = 0; i < numWords; i++) {
|
||||||
|
@ -132,44 +136,95 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
||||||
wordEntriesProperty = new SimpleListProperty<>(wordEntryList);
|
wordEntriesProperty = new SimpleListProperty<>(wordEntryList);
|
||||||
for(int i = 0; i < numWords; i++) {
|
for(int i = 0; i < numWords; i++) {
|
||||||
WordEntry wordEntry = new WordEntry(i, wordEntryList);
|
WordEntry wordEntry = new WordEntry(i, wordEntryList);
|
||||||
tilePane.getChildren().add(wordEntry);
|
wordsPane.getChildren().add(wordEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
vBox.getChildren().add(tilePane);
|
vBox.getChildren().add(wordsPane);
|
||||||
|
|
||||||
AnchorPane anchorPane = new AnchorPane();
|
|
||||||
anchorPane.setPadding(new Insets(0, 32, 0, 10));
|
|
||||||
|
|
||||||
PassphraseEntry passphraseEntry = new PassphraseEntry();
|
PassphraseEntry passphraseEntry = new PassphraseEntry();
|
||||||
AnchorPane.setLeftAnchor(passphraseEntry, 0.0);
|
passphraseEntry.setPadding(new Insets(0, 32, 0, 10));
|
||||||
|
vBox.getChildren().add(passphraseEntry);
|
||||||
|
|
||||||
Button okButton = new Button("Ok");
|
AnchorPane buttonPane = new AnchorPane();
|
||||||
okButton.setPrefWidth(70);
|
buttonPane.setPadding(new Insets(0, 32, 0, 10));
|
||||||
okButton.setDisable(true);
|
|
||||||
okButton.setOnAction(event -> {
|
Button generateButton = new Button("Generate New");
|
||||||
|
generateButton.setOnAction(event -> {
|
||||||
|
generateNew();
|
||||||
|
});
|
||||||
|
buttonPane.getChildren().add(generateButton);
|
||||||
|
AnchorPane.setLeftAnchor(generateButton, 0.0);
|
||||||
|
|
||||||
|
confirmButton = new Button("Confirm Backup");
|
||||||
|
confirmButton.setOnAction(event -> {
|
||||||
|
confirmBackup();
|
||||||
|
});
|
||||||
|
confirmButton.managedProperty().bind(confirmButton.visibleProperty());
|
||||||
|
confirmButton.setVisible(false);
|
||||||
|
buttonPane.getChildren().add(confirmButton);
|
||||||
|
AnchorPane.setRightAnchor(confirmButton, 0.0);
|
||||||
|
|
||||||
|
verifyButton = new Button("Verify");
|
||||||
|
verifyButton.setDisable(true);
|
||||||
|
verifyButton.setOnAction(event -> {
|
||||||
prepareImport();
|
prepareImport();
|
||||||
});
|
});
|
||||||
|
verifyButton.managedProperty().bind(verifyButton.visibleProperty());
|
||||||
|
|
||||||
wordEntriesProperty.addListener((ListChangeListener<String>) c -> {
|
wordEntriesProperty.addListener((ListChangeListener<String>) c -> {
|
||||||
for(String word : wordEntryList) {
|
for(String word : wordEntryList) {
|
||||||
if(!WordEntry.isValid(word)) {
|
if(!WordEntry.isValid(word)) {
|
||||||
okButton.setDisable(true);
|
verifyButton.setDisable(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
okButton.setDisable(false);
|
verifyButton.setDisable(false);
|
||||||
});
|
});
|
||||||
|
buttonPane.getChildren().add(verifyButton);
|
||||||
|
AnchorPane.setRightAnchor(verifyButton, 0.0);
|
||||||
|
|
||||||
AnchorPane.setRightAnchor(okButton, 0.0);
|
vBox.getChildren().add(buttonPane);
|
||||||
|
|
||||||
anchorPane.getChildren().addAll(passphraseEntry, okButton);
|
|
||||||
vBox.getChildren().add(anchorPane);
|
|
||||||
|
|
||||||
return vBox;
|
return vBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateNew() {
|
||||||
|
setDescription("Write down word list to confirm backup");
|
||||||
|
showHideLink.setVisible(false);
|
||||||
|
|
||||||
|
int mnemonicSeedLength = wordEntriesProperty.get().size() * 11;
|
||||||
|
int entropyLength = mnemonicSeedLength - (mnemonicSeedLength/33);
|
||||||
|
|
||||||
|
DeterministicSeed deterministicSeed = new DeterministicSeed(new SecureRandom(), entropyLength, "");
|
||||||
|
generatedMnemonicCode = deterministicSeed.getMnemonicCode();
|
||||||
|
if(generatedMnemonicCode.size() != wordsPane.getChildren().size()) {
|
||||||
|
throw new IllegalStateException("Generated mnemonic words list not same size as displayed words list");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < wordsPane.getChildren().size(); i++) {
|
||||||
|
WordEntry wordEntry = (WordEntry)wordsPane.getChildren().get(i);
|
||||||
|
wordEntry.getEditor().setText(generatedMnemonicCode.get(i));
|
||||||
|
wordEntry.getEditor().setEditable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyButton.setVisible(false);
|
||||||
|
confirmButton.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void confirmBackup() {
|
||||||
|
setDescription("Confirm backup by re-entering words");
|
||||||
|
showHideLink.setVisible(false);
|
||||||
|
setContent(getMnemonicWordsEntry(wordEntriesProperty.get().size()));
|
||||||
|
setExpanded(true);
|
||||||
|
}
|
||||||
|
|
||||||
private void prepareImport() {
|
private void prepareImport() {
|
||||||
|
if(generatedMnemonicCode != null && !generatedMnemonicCode.equals(wordEntriesProperty.get())) {
|
||||||
|
setError("Import Error", "Confirmation words did not match generated mnemonic");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(importKeystore(wallet.getScriptType().getDefaultDerivation(), true)) {
|
if(importKeystore(wallet.getScriptType().getDefaultDerivation(), true)) {
|
||||||
setExpanded(false);
|
setExpanded(false);
|
||||||
enterMnemonicButton.setVisible(false);
|
enterMnemonicButton.setVisible(false);
|
||||||
|
@ -203,6 +258,7 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
||||||
|
|
||||||
private static class WordEntry extends HBox {
|
private static class WordEntry extends HBox {
|
||||||
private static List<String> wordList;
|
private static List<String> wordList;
|
||||||
|
private final TextField wordField;
|
||||||
|
|
||||||
public WordEntry(int wordNumber, ObservableList<String> wordEntryList) {
|
public WordEntry(int wordNumber, ObservableList<String> wordEntryList) {
|
||||||
super();
|
super();
|
||||||
|
@ -212,11 +268,10 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
||||||
Label label = new Label((wordNumber+1) + ".");
|
Label label = new Label((wordNumber+1) + ".");
|
||||||
label.setPrefWidth(20);
|
label.setPrefWidth(20);
|
||||||
label.setAlignment(Pos.CENTER_RIGHT);
|
label.setAlignment(Pos.CENTER_RIGHT);
|
||||||
TextField wordField = new TextField();
|
wordField = new TextField();
|
||||||
wordField.setMaxWidth(100);
|
wordField.setMaxWidth(100);
|
||||||
|
|
||||||
Bip39Calculator bip39Calculator = new Bip39Calculator();
|
wordList = Bip39MnemonicCode.INSTANCE.getWordList();
|
||||||
wordList = bip39Calculator.getWordList();
|
|
||||||
TextFields.bindAutoCompletion(wordField, new WordlistSuggestionProvider(wordList));
|
TextFields.bindAutoCompletion(wordField, new WordlistSuggestionProvider(wordList));
|
||||||
|
|
||||||
ValidationSupport validationSupport = new ValidationSupport();
|
ValidationSupport validationSupport = new ValidationSupport();
|
||||||
|
@ -233,6 +288,10 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
||||||
this.getChildren().addAll(label, wordField);
|
this.getChildren().addAll(label, wordField);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TextField getEditor() {
|
||||||
|
return wordField;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isValid(String word) {
|
public static boolean isValid(String word) {
|
||||||
return wordList.contains(word);
|
return wordList.contains(word);
|
||||||
}
|
}
|
||||||
|
@ -264,11 +323,12 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
||||||
public PassphraseEntry() {
|
public PassphraseEntry() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
setAlignment(Pos.CENTER_RIGHT);
|
setAlignment(Pos.CENTER_LEFT);
|
||||||
setSpacing(10);
|
setSpacing(10);
|
||||||
Label passphraseLabel = new Label("Passphrase:");
|
Label passphraseLabel = new Label("Passphrase:");
|
||||||
CustomTextField passphraseField = (CustomTextField) TextFields.createClearableTextField();
|
CustomTextField passphraseField = (CustomTextField) TextFields.createClearableTextField();
|
||||||
passphraseProperty.bind(passphraseField.textProperty());
|
passphraseProperty.bind(passphraseField.textProperty());
|
||||||
|
passphraseField.setPromptText("Leave blank for none");
|
||||||
|
|
||||||
getChildren().addAll(passphraseLabel, passphraseField);
|
getChildren().addAll(passphraseLabel, passphraseField);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.sparrowwallet.sparrow.io;
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||||
import com.sparrowwallet.drongo.wallet.Bip39Calculator;
|
import com.sparrowwallet.drongo.wallet.Bip39MnemonicCode;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ public class Bip39 implements KeystoreMnemonicImport {
|
||||||
@Override
|
@Override
|
||||||
public Keystore getKeystore(List<ChildNumber> derivation, List<String> mnemonicWords, String passphrase) throws ImportException {
|
public Keystore getKeystore(List<ChildNumber> derivation, List<String> mnemonicWords, String passphrase) throws ImportException {
|
||||||
try {
|
try {
|
||||||
Bip39Calculator bip39Calculator = new Bip39Calculator();
|
Bip39MnemonicCode.INSTANCE.check(mnemonicWords);
|
||||||
byte[] seed = bip39Calculator.getSeed(mnemonicWords, passphrase);
|
byte[] seed = Bip39MnemonicCode.toSeed(mnemonicWords, passphrase);
|
||||||
return Keystore.fromSeed(seed, derivation);
|
return Keystore.fromSeed(seed, derivation);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ImportException(e);
|
throw new ImportException(e);
|
||||||
|
|
Loading…
Reference in a new issue