mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-23 20:36:44 +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.setScriptType(ScriptType.P2WPKH);
|
||||
|
||||
// KeystoreImportDialog dlg = new KeystoreImportDialog(wallet);
|
||||
// dlg.showAndWait();
|
||||
KeystoreImportDialog dlg = new KeystoreImportDialog(wallet);
|
||||
dlg.showAndWait();
|
||||
|
||||
stage.show();
|
||||
// stage.show();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
|
|
@ -2,17 +2,15 @@ package com.sparrowwallet.sparrow.control;
|
|||
|
||||
import com.sparrowwallet.drongo.KeyDerivation;
|
||||
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.Wallet;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
|
||||
import com.sparrowwallet.sparrow.io.*;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleListProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
@ -24,7 +22,6 @@ import javafx.scene.control.*;
|
|||
import javafx.scene.layout.*;
|
||||
import javafx.util.Callback;
|
||||
import org.controlsfx.control.textfield.AutoCompletionBinding;
|
||||
import org.controlsfx.control.textfield.CustomPasswordField;
|
||||
import org.controlsfx.control.textfield.CustomTextField;
|
||||
import org.controlsfx.control.textfield.TextFields;
|
||||
import org.controlsfx.validation.ValidationResult;
|
||||
|
@ -32,6 +29,7 @@ import org.controlsfx.validation.ValidationSupport;
|
|||
import org.controlsfx.validation.Validator;
|
||||
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
@ -42,6 +40,11 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
|||
private SplitMenuButton enterMnemonicButton;
|
||||
private SplitMenuButton importButton;
|
||||
|
||||
private TilePane wordsPane;
|
||||
private Button verifyButton;
|
||||
private Button confirmButton;
|
||||
private List<String> generatedMnemonicCode;
|
||||
|
||||
private SimpleListProperty<String> wordEntriesProperty;
|
||||
private final SimpleStringProperty passphraseProperty = new SimpleStringProperty();
|
||||
|
||||
|
@ -107,6 +110,7 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
|||
}
|
||||
|
||||
private void enterMnemonic(int numWords) {
|
||||
generatedMnemonicCode = null;
|
||||
setDescription("Enter mnemonic word list");
|
||||
showHideLink.setVisible(false);
|
||||
setContent(getMnemonicWordsEntry(numWords));
|
||||
|
@ -117,11 +121,11 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
|||
VBox vBox = new VBox();
|
||||
vBox.setSpacing(10);
|
||||
|
||||
TilePane tilePane = new TilePane();
|
||||
tilePane.setPrefRows(numWords/3);
|
||||
tilePane.setHgap(10);
|
||||
tilePane.setVgap(10);
|
||||
tilePane.setOrientation(Orientation.VERTICAL);
|
||||
wordsPane = new TilePane();
|
||||
wordsPane.setPrefRows(numWords/3);
|
||||
wordsPane.setHgap(10);
|
||||
wordsPane.setVgap(10);
|
||||
wordsPane.setOrientation(Orientation.VERTICAL);
|
||||
|
||||
List<String> words = new ArrayList<>();
|
||||
for(int i = 0; i < numWords; i++) {
|
||||
|
@ -132,44 +136,95 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
|||
wordEntriesProperty = new SimpleListProperty<>(wordEntryList);
|
||||
for(int i = 0; i < numWords; i++) {
|
||||
WordEntry wordEntry = new WordEntry(i, wordEntryList);
|
||||
tilePane.getChildren().add(wordEntry);
|
||||
wordsPane.getChildren().add(wordEntry);
|
||||
}
|
||||
|
||||
vBox.getChildren().add(tilePane);
|
||||
|
||||
AnchorPane anchorPane = new AnchorPane();
|
||||
anchorPane.setPadding(new Insets(0, 32, 0, 10));
|
||||
vBox.getChildren().add(wordsPane);
|
||||
|
||||
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");
|
||||
okButton.setPrefWidth(70);
|
||||
okButton.setDisable(true);
|
||||
okButton.setOnAction(event -> {
|
||||
AnchorPane buttonPane = new AnchorPane();
|
||||
buttonPane.setPadding(new Insets(0, 32, 0, 10));
|
||||
|
||||
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();
|
||||
});
|
||||
verifyButton.managedProperty().bind(verifyButton.visibleProperty());
|
||||
|
||||
wordEntriesProperty.addListener((ListChangeListener<String>) c -> {
|
||||
for(String word : wordEntryList) {
|
||||
if(!WordEntry.isValid(word)) {
|
||||
okButton.setDisable(true);
|
||||
verifyButton.setDisable(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
okButton.setDisable(false);
|
||||
verifyButton.setDisable(false);
|
||||
});
|
||||
buttonPane.getChildren().add(verifyButton);
|
||||
AnchorPane.setRightAnchor(verifyButton, 0.0);
|
||||
|
||||
AnchorPane.setRightAnchor(okButton, 0.0);
|
||||
|
||||
anchorPane.getChildren().addAll(passphraseEntry, okButton);
|
||||
vBox.getChildren().add(anchorPane);
|
||||
vBox.getChildren().add(buttonPane);
|
||||
|
||||
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() {
|
||||
if(generatedMnemonicCode != null && !generatedMnemonicCode.equals(wordEntriesProperty.get())) {
|
||||
setError("Import Error", "Confirmation words did not match generated mnemonic");
|
||||
return;
|
||||
}
|
||||
|
||||
if(importKeystore(wallet.getScriptType().getDefaultDerivation(), true)) {
|
||||
setExpanded(false);
|
||||
enterMnemonicButton.setVisible(false);
|
||||
|
@ -203,6 +258,7 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
|||
|
||||
private static class WordEntry extends HBox {
|
||||
private static List<String> wordList;
|
||||
private final TextField wordField;
|
||||
|
||||
public WordEntry(int wordNumber, ObservableList<String> wordEntryList) {
|
||||
super();
|
||||
|
@ -212,11 +268,10 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
|||
Label label = new Label((wordNumber+1) + ".");
|
||||
label.setPrefWidth(20);
|
||||
label.setAlignment(Pos.CENTER_RIGHT);
|
||||
TextField wordField = new TextField();
|
||||
wordField = new TextField();
|
||||
wordField.setMaxWidth(100);
|
||||
|
||||
Bip39Calculator bip39Calculator = new Bip39Calculator();
|
||||
wordList = bip39Calculator.getWordList();
|
||||
wordList = Bip39MnemonicCode.INSTANCE.getWordList();
|
||||
TextFields.bindAutoCompletion(wordField, new WordlistSuggestionProvider(wordList));
|
||||
|
||||
ValidationSupport validationSupport = new ValidationSupport();
|
||||
|
@ -233,6 +288,10 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
|||
this.getChildren().addAll(label, wordField);
|
||||
}
|
||||
|
||||
public TextField getEditor() {
|
||||
return wordField;
|
||||
}
|
||||
|
||||
public static boolean isValid(String word) {
|
||||
return wordList.contains(word);
|
||||
}
|
||||
|
@ -264,11 +323,12 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
|||
public PassphraseEntry() {
|
||||
super();
|
||||
|
||||
setAlignment(Pos.CENTER_RIGHT);
|
||||
setAlignment(Pos.CENTER_LEFT);
|
||||
setSpacing(10);
|
||||
Label passphraseLabel = new Label("Passphrase:");
|
||||
CustomTextField passphraseField = (CustomTextField) TextFields.createClearableTextField();
|
||||
passphraseProperty.bind(passphraseField.textProperty());
|
||||
passphraseField.setPromptText("Leave blank for none");
|
||||
|
||||
getChildren().addAll(passphraseLabel, passphraseField);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.sparrowwallet.sparrow.io;
|
||||
|
||||
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.WalletModel;
|
||||
|
||||
|
@ -26,8 +26,8 @@ public class Bip39 implements KeystoreMnemonicImport {
|
|||
@Override
|
||||
public Keystore getKeystore(List<ChildNumber> derivation, List<String> mnemonicWords, String passphrase) throws ImportException {
|
||||
try {
|
||||
Bip39Calculator bip39Calculator = new Bip39Calculator();
|
||||
byte[] seed = bip39Calculator.getSeed(mnemonicWords, passphrase);
|
||||
Bip39MnemonicCode.INSTANCE.check(mnemonicWords);
|
||||
byte[] seed = Bip39MnemonicCode.toSeed(mnemonicWords, passphrase);
|
||||
return Keystore.fromSeed(seed, derivation);
|
||||
} catch (Exception e) {
|
||||
throw new ImportException(e);
|
||||
|
|
Loading…
Reference in a new issue