mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
terminal - add account
This commit is contained in:
parent
8f165b05c7
commit
273f3043fb
7 changed files with 223 additions and 9 deletions
|
@ -5,6 +5,7 @@ import com.googlecode.lanterna.TerminalSize;
|
|||
import com.googlecode.lanterna.TextColor;
|
||||
import com.googlecode.lanterna.gui2.*;
|
||||
import com.googlecode.lanterna.screen.Screen;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.event.*;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
|
@ -128,6 +129,7 @@ public class SparrowTextGui extends MultiWindowTextGUI {
|
|||
} else if(event.getTimeMills() < 0) {
|
||||
getGUIThread().invokeLater(() -> {
|
||||
statusLabel.setText(event.getStatus());
|
||||
statusProgress.setValue(0);
|
||||
});
|
||||
} else {
|
||||
getGUIThread().invokeLater(() -> {
|
||||
|
@ -138,6 +140,7 @@ public class SparrowTextGui extends MultiWindowTextGUI {
|
|||
new KeyFrame(Duration.millis(event.getTimeMills()), e -> {
|
||||
getGUIThread().invokeLater(() -> {
|
||||
statusLabel.setText("");
|
||||
statusProgress.setValue(0);
|
||||
});
|
||||
}, new KeyValue(progressProperty, 1))
|
||||
);
|
||||
|
@ -163,4 +166,13 @@ public class SparrowTextGui extends MultiWindowTextGUI {
|
|||
walletHistoryFinished(new WalletHistoryFinishedEvent(event.getWallet()));
|
||||
statusUpdated(new StatusEvent("Error retrieving wallet history" + (Config.get().getServerType() == ServerType.PUBLIC_ELECTRUM_SERVER ? ", trying another server..." : "")));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void childWalletsAdded(ChildWalletsAddedEvent event) {
|
||||
if(!event.getChildWallets().isEmpty()) {
|
||||
for(Wallet childWallet : event.getChildWallets()) {
|
||||
SparrowTerminal.addWallet(event.getStorage(), childWallet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class TerminalInteractionServices implements InteractionServices {
|
|||
private Optional<ButtonType> showMessageDialog(String title, String content, ButtonType[] buttons) {
|
||||
String formattedContent = formatLines(content, 50);
|
||||
|
||||
MessageDialogBuilder builder = new MessageDialogBuilder().setTitle(title).setText(formattedContent);
|
||||
MessageDialogBuilder builder = new MessageDialogBuilder().setTitle(title).setText("\n" + formattedContent);
|
||||
for(ButtonType buttonType : buttons) {
|
||||
builder.addButton(getButton(buttonType));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
package com.sparrowwallet.sparrow.terminal.wallet;
|
||||
|
||||
import com.googlecode.lanterna.TerminalSize;
|
||||
import com.googlecode.lanterna.gui2.*;
|
||||
import com.googlecode.lanterna.gui2.dialogs.DialogWindow;
|
||||
import com.sparrowwallet.drongo.wallet.StandardAccount;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.whirlpool.WhirlpoolServices;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class AddAccountDialog extends DialogWindow {
|
||||
private ComboBox<DisplayStandardAccount> standardAccounts;
|
||||
private StandardAccount standardAccount;
|
||||
|
||||
public AddAccountDialog(Wallet wallet) {
|
||||
super("Add Account");
|
||||
|
||||
setHints(List.of(Hint.CENTERED));
|
||||
|
||||
Panel mainPanel = new Panel();
|
||||
mainPanel.setLayoutManager(new GridLayout(2).setHorizontalSpacing(5).setVerticalSpacing(1));
|
||||
|
||||
mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
|
||||
mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
|
||||
|
||||
mainPanel.addComponent(new Label("Account to add"));
|
||||
standardAccounts = new ComboBox<>();
|
||||
mainPanel.addComponent(standardAccounts);
|
||||
|
||||
List<Integer> existingIndexes = new ArrayList<>();
|
||||
Wallet masterWallet = wallet.isMasterWallet() ? wallet : wallet.getMasterWallet();
|
||||
existingIndexes.add(masterWallet.getAccountIndex());
|
||||
for(Wallet childWallet : masterWallet.getChildWallets()) {
|
||||
if(!childWallet.isNested()) {
|
||||
existingIndexes.add(childWallet.getAccountIndex());
|
||||
}
|
||||
}
|
||||
|
||||
List<StandardAccount> availableAccounts = new ArrayList<>();
|
||||
for(StandardAccount standardAccount : StandardAccount.values()) {
|
||||
if(!existingIndexes.contains(standardAccount.getAccountNumber()) && !StandardAccount.WHIRLPOOL_ACCOUNTS.contains(standardAccount)) {
|
||||
availableAccounts.add(standardAccount);
|
||||
}
|
||||
}
|
||||
|
||||
if(WhirlpoolServices.canWalletMix(masterWallet) && !masterWallet.isWhirlpoolMasterWallet()) {
|
||||
availableAccounts.add(StandardAccount.WHIRLPOOL_PREMIX);
|
||||
}
|
||||
|
||||
availableAccounts.stream().map(DisplayStandardAccount::new).forEach(standardAccounts::addItem);
|
||||
|
||||
Panel buttonPanel = new Panel();
|
||||
buttonPanel.setLayoutManager(new GridLayout(2).setHorizontalSpacing(1));
|
||||
buttonPanel.addComponent(new Button("Cancel", this::onCancel));
|
||||
Button okButton = new Button("Add Account", this::addAccount).setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.CENTER, GridLayout.Alignment.CENTER, true, false));
|
||||
buttonPanel.addComponent(okButton);
|
||||
|
||||
mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
|
||||
|
||||
buttonPanel.setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.END, GridLayout.Alignment.CENTER, false, false)).addTo(mainPanel);
|
||||
setComponent(mainPanel);
|
||||
}
|
||||
|
||||
private void addAccount() {
|
||||
standardAccount = standardAccounts.getSelectedItem().account;
|
||||
close();
|
||||
}
|
||||
|
||||
private void onCancel() {
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardAccount showDialog(WindowBasedTextGUI textGUI) {
|
||||
super.showDialog(textGUI);
|
||||
return standardAccount;
|
||||
}
|
||||
|
||||
private static class DisplayStandardAccount {
|
||||
private final StandardAccount account;
|
||||
|
||||
public DisplayStandardAccount(StandardAccount standardAccount) {
|
||||
this.account = standardAccount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if(StandardAccount.WHIRLPOOL_ACCOUNTS.contains(account)) {
|
||||
return "Whirlpool Accounts";
|
||||
}
|
||||
|
||||
return account.getName();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -191,7 +191,7 @@ public class Bip39Dialog extends NewWalletDialog {
|
|||
}
|
||||
|
||||
private static final class WordNumberDialog extends DialogWindow {
|
||||
ComboBox<Integer> wordCount;
|
||||
private final ComboBox<Integer> wordCount;
|
||||
private Integer numberOfWords;
|
||||
|
||||
public WordNumberDialog() {
|
||||
|
|
|
@ -63,6 +63,7 @@ public class LoadWallet implements Runnable {
|
|||
|
||||
String password = builder.build().showDialog(SparrowTerminal.get().getGui());
|
||||
if(password == null) {
|
||||
SparrowTerminal.get().getGui().removeWindow(loadingDialog);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,25 +8,29 @@ import com.sparrowwallet.drongo.OutputDescriptor;
|
|||
import com.sparrowwallet.drongo.SecureString;
|
||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||
import com.sparrowwallet.drongo.crypto.EncryptionType;
|
||||
import com.sparrowwallet.drongo.crypto.InvalidPasswordException;
|
||||
import com.sparrowwallet.drongo.crypto.Key;
|
||||
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
||||
import com.sparrowwallet.drongo.wallet.StandardAccount;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.RequestOpenWalletsEvent;
|
||||
import com.sparrowwallet.sparrow.event.StorageEvent;
|
||||
import com.sparrowwallet.sparrow.event.TimedEvent;
|
||||
import com.sparrowwallet.sparrow.event.*;
|
||||
import com.sparrowwallet.sparrow.io.Storage;
|
||||
import com.sparrowwallet.sparrow.io.StorageException;
|
||||
import com.sparrowwallet.sparrow.terminal.SparrowTerminal;
|
||||
import com.sparrowwallet.sparrow.wallet.Function;
|
||||
import com.sparrowwallet.sparrow.wallet.WalletForm;
|
||||
import com.sparrowwallet.sparrow.whirlpool.WhirlpoolServices;
|
||||
import javafx.application.Platform;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
|
||||
import static com.sparrowwallet.sparrow.AppServices.showSuccessDialog;
|
||||
|
||||
public class SettingsDialog extends WalletDialog {
|
||||
private static final Logger log = LoggerFactory.getLogger(SettingsDialog.class);
|
||||
|
@ -70,7 +74,11 @@ public class SettingsDialog extends WalletDialog {
|
|||
buttonPanel.addComponent(new Button("Back", () -> onBack(Function.SETTINGS)));
|
||||
buttonPanel.addComponent(new Button("Advanced", this::showAdvanced).setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.CENTER, GridLayout.Alignment.CENTER, true, false)));
|
||||
|
||||
if(getWalletForm().getMasterWallet().getKeystores().stream().allMatch(ks -> ks.getSource() == KeystoreSource.SW_SEED)) {
|
||||
mainPanel.addComponent(new Button("Add Account", this::showAddAccount));
|
||||
} else {
|
||||
mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
|
||||
}
|
||||
|
||||
buttonPanel.setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.END, GridLayout.Alignment.CENTER,false,false)).addTo(mainPanel);
|
||||
setComponent(mainPanel);
|
||||
|
@ -85,6 +93,18 @@ public class SettingsDialog extends WalletDialog {
|
|||
}
|
||||
}
|
||||
|
||||
private void showAddAccount() {
|
||||
Wallet openWallet = AppServices.get().getOpenWallets().entrySet().stream().filter(entry -> getWalletForm().getWalletFile().equals(entry.getValue().getWalletFile())).map(Map.Entry::getKey).findFirst().orElseThrow();
|
||||
Wallet masterWallet = openWallet.isMasterWallet() ? openWallet : openWallet.getMasterWallet();
|
||||
|
||||
AddAccountDialog addAccountDialog = new AddAccountDialog(masterWallet);
|
||||
StandardAccount standardAccount = addAccountDialog.showDialog(SparrowTerminal.get().getGui());
|
||||
|
||||
if(standardAccount != null) {
|
||||
addAccount(masterWallet, standardAccount);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveWallet(boolean changePassword, boolean suggestChangePassword) {
|
||||
WalletForm walletForm = getWalletForm();
|
||||
ECKey existingPubKey = walletForm.getStorage().getEncryptionPubKey();
|
||||
|
@ -178,6 +198,86 @@ public class SettingsDialog extends WalletDialog {
|
|||
}
|
||||
}
|
||||
|
||||
private void addAccount(Wallet masterWallet, StandardAccount standardAccount) {
|
||||
if(masterWallet.isEncrypted()) {
|
||||
String walletId = getWalletForm().getWalletId();
|
||||
|
||||
TextInputDialogBuilder builder = new TextInputDialogBuilder().setTitle("Wallet Password");
|
||||
builder.setDescription("Enter the wallet password:");
|
||||
builder.setPasswordInput(true);
|
||||
|
||||
String password = builder.build().showDialog(SparrowTerminal.get().getGui());
|
||||
if(password != null) {
|
||||
Platform.runLater(() -> {
|
||||
Storage.KeyDerivationService keyDerivationService = new Storage.KeyDerivationService(getWalletForm().getStorage(), new SecureString(password), true);
|
||||
keyDerivationService.setOnSucceeded(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Done"));
|
||||
ECKey encryptionFullKey = keyDerivationService.getValue();
|
||||
Key key = new Key(encryptionFullKey.getPrivKeyBytes(), getWalletForm().getStorage().getKeyDeriver().getSalt(), EncryptionType.Deriver.ARGON2);
|
||||
encryptionFullKey.clear();
|
||||
masterWallet.decrypt(key);
|
||||
addAndEncryptAccount(masterWallet, standardAccount, key);
|
||||
});
|
||||
keyDerivationService.setOnFailed(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Failed"));
|
||||
if(keyDerivationService.getException() instanceof InvalidPasswordException) {
|
||||
showErrorDialog("Invalid Password", "The wallet password was invalid.");
|
||||
} else {
|
||||
log.error("Error deriving wallet key", keyDerivationService.getException());
|
||||
}
|
||||
});
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.START, "Decrypting wallet..."));
|
||||
keyDerivationService.start();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
Platform.runLater(() -> addAndSaveAccount(masterWallet, standardAccount));
|
||||
}
|
||||
}
|
||||
|
||||
private void addAndEncryptAccount(Wallet masterWallet, StandardAccount standardAccount, Key key) {
|
||||
try {
|
||||
addAndSaveAccount(masterWallet, standardAccount);
|
||||
} finally {
|
||||
masterWallet.encrypt(key);
|
||||
for(Wallet childWallet : masterWallet.getChildWallets()) {
|
||||
if(!childWallet.isNested() && !childWallet.isEncrypted()) {
|
||||
childWallet.encrypt(key);
|
||||
}
|
||||
}
|
||||
key.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void addAndSaveAccount(Wallet masterWallet, StandardAccount standardAccount) {
|
||||
if(StandardAccount.WHIRLPOOL_ACCOUNTS.contains(standardAccount)) {
|
||||
WhirlpoolServices.prepareWhirlpoolWallet(masterWallet, getWalletForm().getWalletId(), getWalletForm().getStorage());
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> showSuccessDialog("Added Accounts", "Whirlpool Accounts have been successfully added."));
|
||||
} else {
|
||||
Wallet childWallet = masterWallet.addChildWallet(standardAccount);
|
||||
EventManager.get().post(new ChildWalletsAddedEvent(getWalletForm().getStorage(), masterWallet, childWallet));
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> showSuccessDialog("Added Account", standardAccount.getName() + " has been successfully added."));
|
||||
}
|
||||
|
||||
saveChildWallets(masterWallet);
|
||||
}
|
||||
|
||||
private void saveChildWallets(Wallet masterWallet) {
|
||||
for(Wallet childWallet : masterWallet.getChildWallets()) {
|
||||
if(!childWallet.isNested()) {
|
||||
Storage storage = getWalletForm().getStorage();
|
||||
if(!storage.isPersisted(childWallet)) {
|
||||
try {
|
||||
storage.saveWallet(childWallet);
|
||||
} catch(Exception e) {
|
||||
log.error("Error saving wallet", e);
|
||||
showErrorDialog("Error saving wallet " + childWallet.getName(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> splitString(String stringToSplit, int maxLength) {
|
||||
String text = stringToSplit;
|
||||
List<String> lines = new ArrayList<>();
|
||||
|
|
|
@ -8,6 +8,8 @@ import com.sparrowwallet.sparrow.io.Storage;
|
|||
import com.sparrowwallet.sparrow.terminal.SparrowTerminal;
|
||||
import com.sparrowwallet.sparrow.wallet.WalletForm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class WalletAccountsDialog extends DialogWindow {
|
||||
|
@ -24,7 +26,9 @@ public class WalletAccountsDialog extends DialogWindow {
|
|||
|
||||
actions = new ActionListBox();
|
||||
|
||||
for(Wallet wallet : masterWallet.getAllWallets()) {
|
||||
List<Wallet> allWallets = new ArrayList<>(masterWallet.getAllWallets());
|
||||
Collections.sort(allWallets);
|
||||
for(Wallet wallet : allWallets) {
|
||||
actions.addItem(wallet.getDisplayName(), () -> {
|
||||
close();
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> {
|
||||
|
|
Loading…
Reference in a new issue