mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-23 20:36:44 +00:00
terminal - create watch only wallet
This commit is contained in:
parent
8eb092a8d6
commit
8f165b05c7
12 changed files with 341 additions and 100 deletions
|
@ -1634,6 +1634,10 @@ public class ElectrumServer {
|
|||
}
|
||||
|
||||
private List<StandardAccount> getStandardAccounts(Wallet wallet) {
|
||||
if(!wallet.getKeystores().stream().allMatch(Keystore::hasMasterPrivateKey)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<StandardAccount> accounts = new ArrayList<>();
|
||||
for(StandardAccount account : StandardAccount.values()) {
|
||||
if(account != StandardAccount.ACCOUNT_0 && (!StandardAccount.WHIRLPOOL_ACCOUNTS.contains(account) || wallet.getScriptType() == ScriptType.P2WPKH)) {
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package com.sparrowwallet.sparrow.terminal;
|
||||
|
||||
import com.googlecode.lanterna.TerminalSize;
|
||||
import com.googlecode.lanterna.graphics.ThemeDefinition;
|
||||
import com.googlecode.lanterna.gui2.ComponentRenderer;
|
||||
import com.googlecode.lanterna.gui2.ProgressBar;
|
||||
import com.googlecode.lanterna.gui2.TextGUIGraphics;
|
||||
|
||||
public class BackgroundProgressBarRenderer implements ComponentRenderer<ProgressBar> {
|
||||
@Override
|
||||
public TerminalSize getPreferredSize(ProgressBar component) {
|
||||
int preferredWidth = component.getPreferredWidth();
|
||||
if(preferredWidth > 0) {
|
||||
return new TerminalSize(preferredWidth, 1);
|
||||
} else {
|
||||
return new TerminalSize(10, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawComponent(TextGUIGraphics graphics, ProgressBar component) {
|
||||
TerminalSize size = graphics.getSize();
|
||||
if(size.getRows() == 0 || size.getColumns() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ThemeDefinition themeDefinition = component.getThemeDefinition();
|
||||
int columnOfProgress = (int)(component.getProgress() * size.getColumns());
|
||||
for(int row = 0; row < size.getRows(); row++) {
|
||||
graphics.applyThemeStyle(themeDefinition.getActive());
|
||||
for(int column = 0; column < size.getColumns(); column++) {
|
||||
if(column < columnOfProgress) {
|
||||
graphics.setCharacter(column, row, themeDefinition.getCharacter("FILLER", ' '));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ import com.sparrowwallet.sparrow.terminal.preferences.ServerStatusDialog;
|
|||
import com.sparrowwallet.sparrow.terminal.preferences.ServerTypeDialog;
|
||||
import com.sparrowwallet.sparrow.terminal.wallet.Bip39Dialog;
|
||||
import com.sparrowwallet.sparrow.terminal.wallet.LoadWallet;
|
||||
import com.sparrowwallet.sparrow.terminal.wallet.WatchOnlyDialog;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
@ -31,6 +32,10 @@ public class MasterActionListBox extends ActionListBox {
|
|||
if(Config.get().getRecentWalletFiles() != null) {
|
||||
for(int i = 0; i < Config.get().getRecentWalletFiles().size() && i < MAX_RECENT_WALLETS; i++) {
|
||||
File recentWalletFile = Config.get().getRecentWalletFiles().get(i);
|
||||
if(!recentWalletFile.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Storage storage = new Storage(recentWalletFile);
|
||||
|
||||
Optional<Wallet> optWallet = AppServices.get().getOpenWallets().entrySet().stream()
|
||||
|
@ -92,7 +97,7 @@ public class MasterActionListBox extends ActionListBox {
|
|||
TextInputDialogBuilder newWalletNameBuilder = new TextInputDialogBuilder();
|
||||
newWalletNameBuilder.setTitle("Create Wallet");
|
||||
newWalletNameBuilder.setDescription("Enter a name for the wallet");
|
||||
newWalletNameBuilder.setValidator(content -> Storage.walletExists(content) ? "Wallet already exists" : null);
|
||||
newWalletNameBuilder.setValidator(content -> content.isEmpty() ? "Please enter a name" : (Storage.walletExists(content) ? "Wallet already exists" : null));
|
||||
String walletName = newWalletNameBuilder.build().showDialog(SparrowTerminal.get().getGui());
|
||||
|
||||
ActionListDialogBuilder newBuilder = new ActionListDialogBuilder();
|
||||
|
@ -103,8 +108,8 @@ public class MasterActionListBox extends ActionListBox {
|
|||
bip39Dialog.showDialog(SparrowTerminal.get().getGui());
|
||||
});
|
||||
newBuilder.addAction("Watch Only", () -> {
|
||||
//OutputDescriptorDialog outputDescriptorDialog = new OutputDescriptorDialog(walletName);
|
||||
|
||||
WatchOnlyDialog watchOnlyDialog = new WatchOnlyDialog(walletName);
|
||||
watchOnlyDialog.showDialog(SparrowTerminal.get().getGui());
|
||||
});
|
||||
newBuilder.build().showDialog(SparrowTerminal.get().getGui());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package com.sparrowwallet.sparrow.terminal;
|
||||
|
||||
import com.googlecode.lanterna.TerminalSize;
|
||||
import com.googlecode.lanterna.gui2.EmptySpace;
|
||||
import com.googlecode.lanterna.gui2.Label;
|
||||
import com.googlecode.lanterna.gui2.LinearLayout;
|
||||
import com.googlecode.lanterna.gui2.Panel;
|
||||
import com.googlecode.lanterna.gui2.dialogs.DialogWindow;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class ModalDialog extends DialogWindow {
|
||||
public ModalDialog(String walletName, String description) {
|
||||
super(walletName);
|
||||
|
||||
setHints(List.of(Hint.CENTERED));
|
||||
setFixedSize(new TerminalSize(30, 5));
|
||||
|
||||
Panel mainPanel = new Panel();
|
||||
mainPanel.setLayoutManager(new LinearLayout());
|
||||
mainPanel.addComponent(new EmptySpace(), LinearLayout.createLayoutData(LinearLayout.Alignment.Beginning, LinearLayout.GrowPolicy.CanGrow));
|
||||
|
||||
Label label = new Label(description);
|
||||
mainPanel.addComponent(label, LinearLayout.createLayoutData(LinearLayout.Alignment.Center));
|
||||
|
||||
mainPanel.addComponent(new EmptySpace(), LinearLayout.createLayoutData(LinearLayout.Alignment.Beginning, LinearLayout.GrowPolicy.CanGrow));
|
||||
|
||||
setComponent(mainPanel);
|
||||
}
|
||||
}
|
|
@ -103,6 +103,14 @@ public class SparrowTerminal extends Application {
|
|||
if(instance != null) {
|
||||
instance.freeLock();
|
||||
}
|
||||
|
||||
List<File> recentWalletFiles = Config.get().getRecentWalletFiles();
|
||||
if(recentWalletFiles != null && !recentWalletFiles.isEmpty()) {
|
||||
Set<File> openedWalletFiles = new LinkedHashSet<>(recentWalletFiles);
|
||||
openedWalletFiles.removeIf(file -> walletData.values().stream().noneMatch(data -> data.getWalletForm().getWalletFile().equals(file)));
|
||||
openedWalletFiles.addAll(Config.get().getRecentWalletFiles().subList(0, Math.min(3, recentWalletFiles.size())));
|
||||
Config.get().setRecentWalletFiles(new ArrayList<>(openedWalletFiles));
|
||||
}
|
||||
} catch(Exception e) {
|
||||
log.error("Could not stop terminal screen", e);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ import javafx.beans.property.DoubleProperty;
|
|||
import javafx.beans.property.SimpleDoubleProperty;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class SparrowTextGui extends MultiWindowTextGUI {
|
||||
private final BasicWindow mainWindow;
|
||||
|
||||
|
@ -45,7 +47,7 @@ public class SparrowTextGui extends MultiWindowTextGUI {
|
|||
this.statusLabel = new Label("").addTo(statusBar);
|
||||
this.statusProgress = new ProgressBar(0, 100, 10);
|
||||
statusBar.addComponent(statusProgress, GridLayout.createLayoutData(GridLayout.Alignment.END, GridLayout.Alignment.CENTER, true, false));
|
||||
statusProgress.setVisible(false);
|
||||
statusProgress.setRenderer(new BackgroundProgressBarRenderer());
|
||||
statusProgress.setLabelFormat(null);
|
||||
progressProperty.addListener((observable, oldValue, newValue) -> statusProgress.setValue((int) (newValue.doubleValue() * 100)));
|
||||
|
||||
|
@ -55,8 +57,10 @@ public class SparrowTextGui extends MultiWindowTextGUI {
|
|||
getMainWindow().addWindowListener(new WindowListenerAdapter() {
|
||||
@Override
|
||||
public void onResized(Window window, TerminalSize oldSize, TerminalSize newSize) {
|
||||
titleBar.invalidate();
|
||||
statusBar.invalidate();
|
||||
if(!Objects.equals(oldSize, newSize)) {
|
||||
titleBar.invalidate();
|
||||
statusBar.invalidate();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -119,25 +123,21 @@ public class SparrowTextGui extends MultiWindowTextGUI {
|
|||
if(event.getTimeMills() == 0) {
|
||||
getGUIThread().invokeLater(() -> {
|
||||
statusLabel.setText("");
|
||||
statusProgress.setVisible(false);
|
||||
statusProgress.setValue(0);
|
||||
});
|
||||
} else if(event.getTimeMills() < 0) {
|
||||
getGUIThread().invokeLater(() -> {
|
||||
statusLabel.setText(event.getStatus());
|
||||
statusProgress.setVisible(false);
|
||||
});
|
||||
} else {
|
||||
getGUIThread().invokeLater(() -> {
|
||||
statusLabel.setText(event.getStatus());
|
||||
statusProgress.setVisible(true);
|
||||
});
|
||||
statusTimeline = new Timeline(
|
||||
new KeyFrame(Duration.ZERO, new KeyValue(progressProperty, 0)),
|
||||
new KeyFrame(Duration.millis(event.getTimeMills()), e -> {
|
||||
getGUIThread().invokeLater(() -> {
|
||||
statusLabel.setText("");
|
||||
statusProgress.setVisible(false);
|
||||
});
|
||||
}, new KeyValue(progressProperty, 1))
|
||||
);
|
||||
|
|
|
@ -28,21 +28,16 @@ public class Bip39Dialog extends NewWalletDialog {
|
|||
|
||||
private final Bip39 importer = new Bip39();
|
||||
|
||||
private Wallet wallet;
|
||||
|
||||
private final String walletName;
|
||||
private final ComboBox<DisplayScriptType> scriptType;
|
||||
private final TextBox seedWords;
|
||||
private final TextBox passphrase;
|
||||
private final Button createWallet;
|
||||
|
||||
public Bip39Dialog(String walletName) {
|
||||
super("Create BIP39 Wallet - " + walletName);
|
||||
super("Create BIP39 Wallet - " + walletName, walletName);
|
||||
|
||||
setHints(List.of(Hint.CENTERED));
|
||||
|
||||
this.walletName = walletName;
|
||||
|
||||
Panel mainPanel = new Panel();
|
||||
mainPanel.setLayoutManager(new GridLayout(2).setHorizontalSpacing(5).setVerticalSpacing(1));
|
||||
|
||||
|
@ -152,35 +147,15 @@ public class Bip39Dialog extends NewWalletDialog {
|
|||
return lines;
|
||||
}
|
||||
|
||||
private void createWallet() {
|
||||
close();
|
||||
|
||||
try {
|
||||
wallet = getWallet();
|
||||
discoverAndSaveWallet(wallet);
|
||||
} catch(ImportException e) {
|
||||
log.error("Cannot import wallet", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Wallet getWallet() throws ImportException {
|
||||
@Override
|
||||
protected List<Wallet> getWallets() throws ImportException {
|
||||
Wallet wallet = new Wallet(walletName);
|
||||
wallet.setPolicyType(PolicyType.SINGLE);
|
||||
wallet.setScriptType(scriptType.getSelectedItem().scriptType);
|
||||
Keystore keystore = importer.getKeystore(wallet.getScriptType().getDefaultDerivation(), getWords(), passphrase.getText());
|
||||
wallet.getKeystores().add(keystore);
|
||||
wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, wallet.getScriptType(), wallet.getKeystores(), 1));
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private void onCancel() {
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wallet showDialog(WindowBasedTextGUI textGUI) {
|
||||
super.showDialog(textGUI);
|
||||
return wallet;
|
||||
return List.of(wallet);
|
||||
}
|
||||
|
||||
private static final class DisplayScriptType {
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.sparrowwallet.sparrow.event.*;
|
|||
import com.sparrowwallet.sparrow.io.Storage;
|
||||
import com.sparrowwallet.sparrow.io.StorageException;
|
||||
import com.sparrowwallet.sparrow.io.WalletAndKey;
|
||||
import com.sparrowwallet.sparrow.terminal.ModalDialog;
|
||||
import com.sparrowwallet.sparrow.terminal.SparrowTerminal;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.ButtonType;
|
||||
|
@ -26,11 +27,11 @@ import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
|
|||
public class LoadWallet implements Runnable {
|
||||
private static final Logger log = LoggerFactory.getLogger(LoadWallet.class);
|
||||
private final Storage storage;
|
||||
private final LoadingDialog loadingDialog;
|
||||
private final ModalDialog loadingDialog;
|
||||
|
||||
public LoadWallet(Storage storage) {
|
||||
this.storage = storage;
|
||||
this.loadingDialog = new LoadingDialog(storage);
|
||||
this.loadingDialog = new ModalDialog(storage.getWalletName(null), "Loading...");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -47,6 +48,7 @@ public class LoadWallet implements Runnable {
|
|||
openWallet(storage, walletAndKey);
|
||||
});
|
||||
loadWalletService.setOnFailed(workerStateEvent -> {
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> SparrowTerminal.get().getGui().removeWindow(loadingDialog));
|
||||
Throwable exception = workerStateEvent.getSource().getException();
|
||||
if(exception instanceof StorageException) {
|
||||
showErrorDialog("Error Opening Wallet", exception.getMessage());
|
||||
|
@ -73,6 +75,7 @@ public class LoadWallet implements Runnable {
|
|||
});
|
||||
loadWalletService.setOnFailed(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(storage.getWalletId(null), TimedEvent.Action.END, "Failed"));
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> SparrowTerminal.get().getGui().removeWindow(loadingDialog));
|
||||
Throwable exception = loadWalletService.getException();
|
||||
if(exception instanceof InvalidPasswordException) {
|
||||
Optional<ButtonType> optResponse = showErrorDialog("Invalid Password", "The wallet password was invalid. Try again?", ButtonType.CANCEL, ButtonType.OK);
|
||||
|
@ -130,24 +133,4 @@ public class LoadWallet implements Runnable {
|
|||
return new WalletActionsDialog(storage.getWalletId(masterWallet));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LoadingDialog extends DialogWindow {
|
||||
public LoadingDialog(Storage storage) {
|
||||
super(storage.getWalletName(null));
|
||||
|
||||
setHints(List.of(Hint.CENTERED));
|
||||
setFixedSize(new TerminalSize(30, 5));
|
||||
|
||||
Panel mainPanel = new Panel();
|
||||
mainPanel.setLayoutManager(new LinearLayout());
|
||||
mainPanel.addComponent(new EmptySpace(), LinearLayout.createLayoutData(LinearLayout.Alignment.Beginning, LinearLayout.GrowPolicy.CanGrow));
|
||||
|
||||
Label label = new Label("Loading...");
|
||||
mainPanel.addComponent(label, LinearLayout.createLayoutData(LinearLayout.Alignment.Center));
|
||||
|
||||
mainPanel.addComponent(new EmptySpace(), LinearLayout.createLayoutData(LinearLayout.Alignment.Beginning, LinearLayout.GrowPolicy.CanGrow));
|
||||
|
||||
setComponent(mainPanel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
package com.sparrowwallet.sparrow.terminal.wallet;
|
||||
|
||||
import com.googlecode.lanterna.TerminalSize;
|
||||
import com.googlecode.lanterna.gui2.EmptySpace;
|
||||
import com.googlecode.lanterna.gui2.Label;
|
||||
import com.googlecode.lanterna.gui2.LinearLayout;
|
||||
import com.googlecode.lanterna.gui2.Panel;
|
||||
import com.googlecode.lanterna.gui2.*;
|
||||
import com.googlecode.lanterna.gui2.dialogs.DialogWindow;
|
||||
import com.googlecode.lanterna.gui2.dialogs.TextInputDialogBuilder;
|
||||
import com.sparrowwallet.drongo.SecureString;
|
||||
|
@ -17,9 +13,11 @@ import com.sparrowwallet.sparrow.AppServices;
|
|||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.StorageEvent;
|
||||
import com.sparrowwallet.sparrow.event.TimedEvent;
|
||||
import com.sparrowwallet.sparrow.io.ImportException;
|
||||
import com.sparrowwallet.sparrow.io.Storage;
|
||||
import com.sparrowwallet.sparrow.io.StorageException;
|
||||
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||
import com.sparrowwallet.sparrow.terminal.ModalDialog;
|
||||
import com.sparrowwallet.sparrow.terminal.SparrowTerminal;
|
||||
import javafx.application.Platform;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -27,37 +25,78 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
|
||||
|
||||
public class NewWalletDialog extends DialogWindow {
|
||||
public abstract class NewWalletDialog extends DialogWindow {
|
||||
private static final Logger log = LoggerFactory.getLogger(NewWalletDialog.class);
|
||||
|
||||
public NewWalletDialog(String title) {
|
||||
protected Wallet wallet;
|
||||
|
||||
protected final String walletName;
|
||||
|
||||
public NewWalletDialog(String title, String walletName) {
|
||||
super(title);
|
||||
this.walletName = walletName;
|
||||
}
|
||||
|
||||
protected void discoverAndSaveWallet(Wallet wallet) {
|
||||
if(AppServices.onlineProperty().get()) {
|
||||
discoverAccounts(wallet);
|
||||
} else {
|
||||
saveWallet(wallet);
|
||||
protected void createWallet() {
|
||||
close();
|
||||
|
||||
try {
|
||||
discoverAndSaveWallet(getWallets());
|
||||
} catch(ImportException e) {
|
||||
log.error("Cannot import wallet", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverAccounts(Wallet wallet) {
|
||||
DiscoveringDialog discoveringDialog = new DiscoveringDialog(wallet);
|
||||
/**
|
||||
* Returns a list of wallets for discovery.
|
||||
* If no existing wallets are discovered, the first wallet is used.
|
||||
*
|
||||
* @return a list of wallet candidates
|
||||
*/
|
||||
protected abstract List<Wallet> getWallets() throws ImportException;
|
||||
|
||||
protected void onCancel() {
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wallet showDialog(WindowBasedTextGUI textGUI) {
|
||||
super.showDialog(textGUI);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
protected void discoverAndSaveWallet(List<Wallet> wallets) {
|
||||
if(wallets.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(AppServices.onlineProperty().get()) {
|
||||
discoverAccounts(wallets);
|
||||
} else {
|
||||
saveWallet(wallets.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverAccounts(List<Wallet> wallets) {
|
||||
ModalDialog discoveringDialog = new ModalDialog(walletName, "Discovering accounts...");
|
||||
SparrowTerminal.get().getGui().addWindow(discoveringDialog);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
ElectrumServer.WalletDiscoveryService walletDiscoveryService = new ElectrumServer.WalletDiscoveryService(List.of(wallet));
|
||||
ElectrumServer.WalletDiscoveryService walletDiscoveryService = new ElectrumServer.WalletDiscoveryService(wallets);
|
||||
walletDiscoveryService.setOnSucceeded(successEvent -> {
|
||||
Optional<Wallet> optWallet = walletDiscoveryService.getValue();
|
||||
wallet = optWallet.orElseGet(() -> wallets.get(0));
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> {
|
||||
SparrowTerminal.get().getGui().removeWindow(discoveringDialog);
|
||||
saveWallet(wallet);
|
||||
});
|
||||
});
|
||||
walletDiscoveryService.setOnFailed(failedEvent -> {
|
||||
wallet = wallets.get(0);
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> {
|
||||
SparrowTerminal.get().getGui().removeWindow(discoveringDialog);
|
||||
saveWallet(wallet);
|
||||
|
@ -77,6 +116,9 @@ public class NewWalletDialog extends DialogWindow {
|
|||
|
||||
String password = builder.build().showDialog(SparrowTerminal.get().getGui());
|
||||
if(password != null) {
|
||||
ModalDialog savingDialog = new ModalDialog(walletName, "Saving wallet...");
|
||||
SparrowTerminal.get().getGui().addWindow(savingDialog);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
if(password.length() == 0) {
|
||||
try {
|
||||
|
@ -91,7 +133,10 @@ public class NewWalletDialog extends DialogWindow {
|
|||
SparrowTerminal.addWallet(storage, childWallet);
|
||||
}
|
||||
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> LoadWallet.getOpeningDialog(storage, wallet).showDialog(SparrowTerminal.get().getGui()));
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> {
|
||||
SparrowTerminal.get().getGui().removeWindow(savingDialog);
|
||||
LoadWallet.getOpeningDialog(storage, wallet).showDialog(SparrowTerminal.get().getGui());
|
||||
});
|
||||
} catch(IOException | StorageException | MnemonicException e) {
|
||||
log.error("Error saving imported wallet", e);
|
||||
}
|
||||
|
@ -120,7 +165,10 @@ public class NewWalletDialog extends DialogWindow {
|
|||
SparrowTerminal.addWallet(storage, childWallet);
|
||||
}
|
||||
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> LoadWallet.getOpeningDialog(storage, wallet).showDialog(SparrowTerminal.get().getGui()));
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> {
|
||||
SparrowTerminal.get().getGui().removeWindow(savingDialog);
|
||||
LoadWallet.getOpeningDialog(storage, wallet).showDialog(SparrowTerminal.get().getGui());
|
||||
});
|
||||
} catch(IOException | StorageException | MnemonicException e) {
|
||||
log.error("Error saving imported wallet", e);
|
||||
} finally {
|
||||
|
@ -131,6 +179,7 @@ public class NewWalletDialog extends DialogWindow {
|
|||
}
|
||||
});
|
||||
keyDerivationService.setOnFailed(workerStateEvent -> {
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> SparrowTerminal.get().getGui().removeWindow(savingDialog));
|
||||
EventManager.get().post(new StorageEvent(Storage.getWalletFile(wallet.getName()).getAbsolutePath(), TimedEvent.Action.END, "Failed"));
|
||||
showErrorDialog("Error encrypting wallet", keyDerivationService.getException().getMessage());
|
||||
});
|
||||
|
@ -141,23 +190,4 @@ public class NewWalletDialog extends DialogWindow {
|
|||
}
|
||||
}
|
||||
|
||||
private static final class DiscoveringDialog extends DialogWindow {
|
||||
public DiscoveringDialog(Wallet wallet) {
|
||||
super(wallet.getName());
|
||||
|
||||
setHints(List.of(Hint.CENTERED));
|
||||
setFixedSize(new TerminalSize(30, 5));
|
||||
|
||||
Panel mainPanel = new Panel();
|
||||
mainPanel.setLayoutManager(new LinearLayout());
|
||||
mainPanel.addComponent(new EmptySpace(), LinearLayout.createLayoutData(LinearLayout.Alignment.Beginning, LinearLayout.GrowPolicy.CanGrow));
|
||||
|
||||
Label label = new Label("Discovering Accounts...");
|
||||
mainPanel.addComponent(label, LinearLayout.createLayoutData(LinearLayout.Alignment.Center));
|
||||
|
||||
mainPanel.addComponent(new EmptySpace(), LinearLayout.createLayoutData(LinearLayout.Alignment.Beginning, LinearLayout.GrowPolicy.CanGrow));
|
||||
|
||||
setComponent(mainPanel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -181,10 +181,10 @@ public class SettingsDialog extends WalletDialog {
|
|||
public static List<String> splitString(String stringToSplit, int maxLength) {
|
||||
String text = stringToSplit;
|
||||
List<String> lines = new ArrayList<>();
|
||||
while(text.length() > maxLength) {
|
||||
while(text.length() >= maxLength) {
|
||||
int breakAt = maxLength - 1;
|
||||
lines.add(text.substring(0, breakAt));
|
||||
text = text.substring(breakAt + 1);
|
||||
text = text.substring(breakAt);
|
||||
}
|
||||
|
||||
lines.add(text);
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
package com.sparrowwallet.sparrow.terminal.wallet;
|
||||
|
||||
import com.googlecode.lanterna.TerminalPosition;
|
||||
import com.googlecode.lanterna.TerminalSize;
|
||||
import com.googlecode.lanterna.gui2.*;
|
||||
import com.sparrowwallet.drongo.ExtendedKey;
|
||||
import com.sparrowwallet.drongo.KeyDerivation;
|
||||
import com.sparrowwallet.drongo.OutputDescriptor;
|
||||
import com.sparrowwallet.drongo.policy.Policy;
|
||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||
import com.sparrowwallet.sparrow.io.ImportException;
|
||||
import com.sparrowwallet.sparrow.terminal.SparrowTerminal;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.sparrowwallet.sparrow.wallet.KeystoreController.DEFAULT_WATCH_ONLY_FINGERPRINT;
|
||||
|
||||
public class WatchOnlyDialog extends NewWalletDialog {
|
||||
private static final Logger log = LoggerFactory.getLogger(WatchOnlyDialog.class);
|
||||
|
||||
private final TextBox descriptor;
|
||||
private final Button importWallet;
|
||||
|
||||
public WatchOnlyDialog(String walletName) {
|
||||
super("Create Watch Only Wallet - " + walletName, walletName);
|
||||
|
||||
setHints(List.of(Hint.CENTERED));
|
||||
|
||||
Panel mainPanel = new Panel();
|
||||
mainPanel.setLayoutManager(new GridLayout(2).setVerticalSpacing(0));
|
||||
|
||||
mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
|
||||
mainPanel.addComponent(new EmptySpace(TerminalSize.ZERO));
|
||||
|
||||
TerminalSize screenSize = SparrowTerminal.get().getScreen().getTerminalSize();
|
||||
int descriptorWidth = Math.min(Math.max(20, screenSize.getColumns() - 20), 120);
|
||||
|
||||
mainPanel.addComponent(new Label("Output descriptor or xpub"));
|
||||
mainPanel.addComponent(new EmptySpace(TerminalSize.ZERO));
|
||||
|
||||
descriptor = new TextBox(new TerminalSize(descriptorWidth, 10));
|
||||
mainPanel.addComponent(descriptor);
|
||||
mainPanel.addComponent(new EmptySpace(TerminalSize.ZERO));
|
||||
|
||||
Panel buttonPanel = new Panel();
|
||||
buttonPanel.setLayoutManager(new GridLayout(2).setHorizontalSpacing(1));
|
||||
buttonPanel.addComponent(new Button("Cancel", this::onCancel));
|
||||
importWallet = new Button("Import Wallet", this::createWallet).setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.CENTER, GridLayout.Alignment.CENTER, true, false));
|
||||
importWallet.setEnabled(false);
|
||||
buttonPanel.addComponent(importWallet);
|
||||
|
||||
mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
|
||||
mainPanel.addComponent(new EmptySpace(TerminalSize.ZERO));
|
||||
|
||||
buttonPanel.setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.END, GridLayout.Alignment.CENTER,false,false)).addTo(mainPanel);
|
||||
mainPanel.addComponent(new EmptySpace(TerminalSize.ZERO));
|
||||
|
||||
setComponent(mainPanel);
|
||||
|
||||
descriptor.setTextChangeListener((newText, changedByUserInteraction) -> {
|
||||
String line = newText.replaceAll("\\s+", "");
|
||||
try {
|
||||
OutputDescriptor.getOutputDescriptor(line);
|
||||
importWallet.setEnabled(true);
|
||||
} catch(Exception e1) {
|
||||
try {
|
||||
ExtendedKey.fromDescriptor(line);
|
||||
importWallet.setEnabled(true);
|
||||
} catch(Exception e2) {
|
||||
importWallet.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
if(changedByUserInteraction) {
|
||||
List<String> lines = splitString(newText, descriptorWidth);
|
||||
String splitText = lines.stream().reduce((s1, s2) -> s1 + "\n" + s2).get();
|
||||
if(!newText.equals(splitText)) {
|
||||
descriptor.setText(splitText);
|
||||
|
||||
TerminalPosition pos = descriptor.getCaretPosition();
|
||||
if(pos.getRow() == lines.size() - 2 && pos.getColumn() == lines.get(lines.size() - 2).length()) {
|
||||
descriptor.setCaretPosition(lines.size() - 1, lines.get(lines.size() - 1).length());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Wallet> getWallets() throws ImportException {
|
||||
try {
|
||||
return getWalletFromXpub();
|
||||
} catch(Exception e1) {
|
||||
try {
|
||||
return getWalletFromOutputDescriptor();
|
||||
} catch(Exception e2) {
|
||||
log.error("Could not determine wallet from descriptor: " + descriptor.getText(), e2);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<Wallet> getWalletFromXpub() {
|
||||
ExtendedKey xpub = ExtendedKey.fromDescriptor(descriptor.getText().replaceAll("\\s+", ""));
|
||||
ExtendedKey.Header header = ExtendedKey.Header.fromExtendedKey(descriptor.getText());
|
||||
|
||||
Set<ScriptType> scriptTypes = new LinkedHashSet<>();
|
||||
scriptTypes.add(ScriptType.P2WPKH);
|
||||
scriptTypes.add(header.getDefaultScriptType());
|
||||
scriptTypes.addAll(ScriptType.getAddressableScriptTypes(PolicyType.SINGLE));
|
||||
|
||||
List<Wallet> wallets = new ArrayList<>();
|
||||
for(ScriptType scriptType : scriptTypes) {
|
||||
Wallet wallet = new Wallet(walletName);
|
||||
wallet.setPolicyType(PolicyType.SINGLE);
|
||||
wallet.setScriptType(scriptType);
|
||||
|
||||
Keystore keystore = new Keystore();
|
||||
keystore.setSource(KeystoreSource.SW_WATCH);
|
||||
keystore.setWalletModel(WalletModel.SPARROW);
|
||||
keystore.setKeyDerivation(new KeyDerivation(DEFAULT_WATCH_ONLY_FINGERPRINT, scriptType.getDefaultDerivationPath()));
|
||||
keystore.setExtendedPublicKey(xpub);
|
||||
wallet.makeLabelsUnique(keystore);
|
||||
wallet.getKeystores().add(keystore);
|
||||
|
||||
wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), wallet.getScriptType(), wallet.getKeystores(), 1));
|
||||
wallets.add(wallet);
|
||||
}
|
||||
|
||||
return wallets;
|
||||
}
|
||||
|
||||
private List<Wallet> getWalletFromOutputDescriptor() {
|
||||
OutputDescriptor outputDescriptor = OutputDescriptor.getOutputDescriptor(descriptor.getText().replaceAll("\\s+", ""));
|
||||
Wallet wallet = outputDescriptor.toWallet();
|
||||
wallet.setName(walletName);
|
||||
return List.of(wallet);
|
||||
}
|
||||
|
||||
private List<String> splitString(String stringToSplit, int maxLength) {
|
||||
String text = stringToSplit.replaceAll("\\s+", "");
|
||||
if(stringToSplit.endsWith("\n")) {
|
||||
text += "\n";
|
||||
}
|
||||
|
||||
List<String> lines = new ArrayList<>();
|
||||
while(text.length() >= maxLength) {
|
||||
int breakAt = maxLength - 1;
|
||||
lines.add(text.substring(0, breakAt));
|
||||
text = text.substring(breakAt);
|
||||
}
|
||||
|
||||
if(text.equals("\n")) {
|
||||
text = "";
|
||||
}
|
||||
|
||||
lines.add(text);
|
||||
return lines;
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ import java.util.stream.Collectors;
|
|||
public class KeystoreController extends WalletFormController implements Initializable {
|
||||
private static final Logger log = LoggerFactory.getLogger(KeystoreController.class);
|
||||
|
||||
private static final String DEFAULT_WATCH_ONLY_FINGERPRINT = "00000000";
|
||||
public static final String DEFAULT_WATCH_ONLY_FINGERPRINT = "00000000";
|
||||
|
||||
private Keystore keystore;
|
||||
|
||||
|
|
Loading…
Reference in a new issue