mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +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) {
|
private List<StandardAccount> getStandardAccounts(Wallet wallet) {
|
||||||
|
if(!wallet.getKeystores().stream().allMatch(Keystore::hasMasterPrivateKey)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
List<StandardAccount> accounts = new ArrayList<>();
|
List<StandardAccount> accounts = new ArrayList<>();
|
||||||
for(StandardAccount account : StandardAccount.values()) {
|
for(StandardAccount account : StandardAccount.values()) {
|
||||||
if(account != StandardAccount.ACCOUNT_0 && (!StandardAccount.WHIRLPOOL_ACCOUNTS.contains(account) || wallet.getScriptType() == ScriptType.P2WPKH)) {
|
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.preferences.ServerTypeDialog;
|
||||||
import com.sparrowwallet.sparrow.terminal.wallet.Bip39Dialog;
|
import com.sparrowwallet.sparrow.terminal.wallet.Bip39Dialog;
|
||||||
import com.sparrowwallet.sparrow.terminal.wallet.LoadWallet;
|
import com.sparrowwallet.sparrow.terminal.wallet.LoadWallet;
|
||||||
|
import com.sparrowwallet.sparrow.terminal.wallet.WatchOnlyDialog;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -31,6 +32,10 @@ public class MasterActionListBox extends ActionListBox {
|
||||||
if(Config.get().getRecentWalletFiles() != null) {
|
if(Config.get().getRecentWalletFiles() != null) {
|
||||||
for(int i = 0; i < Config.get().getRecentWalletFiles().size() && i < MAX_RECENT_WALLETS; i++) {
|
for(int i = 0; i < Config.get().getRecentWalletFiles().size() && i < MAX_RECENT_WALLETS; i++) {
|
||||||
File recentWalletFile = Config.get().getRecentWalletFiles().get(i);
|
File recentWalletFile = Config.get().getRecentWalletFiles().get(i);
|
||||||
|
if(!recentWalletFile.exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Storage storage = new Storage(recentWalletFile);
|
Storage storage = new Storage(recentWalletFile);
|
||||||
|
|
||||||
Optional<Wallet> optWallet = AppServices.get().getOpenWallets().entrySet().stream()
|
Optional<Wallet> optWallet = AppServices.get().getOpenWallets().entrySet().stream()
|
||||||
|
@ -92,7 +97,7 @@ public class MasterActionListBox extends ActionListBox {
|
||||||
TextInputDialogBuilder newWalletNameBuilder = new TextInputDialogBuilder();
|
TextInputDialogBuilder newWalletNameBuilder = new TextInputDialogBuilder();
|
||||||
newWalletNameBuilder.setTitle("Create Wallet");
|
newWalletNameBuilder.setTitle("Create Wallet");
|
||||||
newWalletNameBuilder.setDescription("Enter a name for the 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());
|
String walletName = newWalletNameBuilder.build().showDialog(SparrowTerminal.get().getGui());
|
||||||
|
|
||||||
ActionListDialogBuilder newBuilder = new ActionListDialogBuilder();
|
ActionListDialogBuilder newBuilder = new ActionListDialogBuilder();
|
||||||
|
@ -103,8 +108,8 @@ public class MasterActionListBox extends ActionListBox {
|
||||||
bip39Dialog.showDialog(SparrowTerminal.get().getGui());
|
bip39Dialog.showDialog(SparrowTerminal.get().getGui());
|
||||||
});
|
});
|
||||||
newBuilder.addAction("Watch Only", () -> {
|
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());
|
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) {
|
if(instance != null) {
|
||||||
instance.freeLock();
|
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) {
|
} catch(Exception e) {
|
||||||
log.error("Could not stop terminal screen", 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.beans.property.SimpleDoubleProperty;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class SparrowTextGui extends MultiWindowTextGUI {
|
public class SparrowTextGui extends MultiWindowTextGUI {
|
||||||
private final BasicWindow mainWindow;
|
private final BasicWindow mainWindow;
|
||||||
|
|
||||||
|
@ -45,7 +47,7 @@ public class SparrowTextGui extends MultiWindowTextGUI {
|
||||||
this.statusLabel = new Label("").addTo(statusBar);
|
this.statusLabel = new Label("").addTo(statusBar);
|
||||||
this.statusProgress = new ProgressBar(0, 100, 10);
|
this.statusProgress = new ProgressBar(0, 100, 10);
|
||||||
statusBar.addComponent(statusProgress, GridLayout.createLayoutData(GridLayout.Alignment.END, GridLayout.Alignment.CENTER, true, false));
|
statusBar.addComponent(statusProgress, GridLayout.createLayoutData(GridLayout.Alignment.END, GridLayout.Alignment.CENTER, true, false));
|
||||||
statusProgress.setVisible(false);
|
statusProgress.setRenderer(new BackgroundProgressBarRenderer());
|
||||||
statusProgress.setLabelFormat(null);
|
statusProgress.setLabelFormat(null);
|
||||||
progressProperty.addListener((observable, oldValue, newValue) -> statusProgress.setValue((int) (newValue.doubleValue() * 100)));
|
progressProperty.addListener((observable, oldValue, newValue) -> statusProgress.setValue((int) (newValue.doubleValue() * 100)));
|
||||||
|
|
||||||
|
@ -55,8 +57,10 @@ public class SparrowTextGui extends MultiWindowTextGUI {
|
||||||
getMainWindow().addWindowListener(new WindowListenerAdapter() {
|
getMainWindow().addWindowListener(new WindowListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void onResized(Window window, TerminalSize oldSize, TerminalSize newSize) {
|
public void onResized(Window window, TerminalSize oldSize, TerminalSize newSize) {
|
||||||
titleBar.invalidate();
|
if(!Objects.equals(oldSize, newSize)) {
|
||||||
statusBar.invalidate();
|
titleBar.invalidate();
|
||||||
|
statusBar.invalidate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -119,25 +123,21 @@ public class SparrowTextGui extends MultiWindowTextGUI {
|
||||||
if(event.getTimeMills() == 0) {
|
if(event.getTimeMills() == 0) {
|
||||||
getGUIThread().invokeLater(() -> {
|
getGUIThread().invokeLater(() -> {
|
||||||
statusLabel.setText("");
|
statusLabel.setText("");
|
||||||
statusProgress.setVisible(false);
|
|
||||||
statusProgress.setValue(0);
|
statusProgress.setValue(0);
|
||||||
});
|
});
|
||||||
} else if(event.getTimeMills() < 0) {
|
} else if(event.getTimeMills() < 0) {
|
||||||
getGUIThread().invokeLater(() -> {
|
getGUIThread().invokeLater(() -> {
|
||||||
statusLabel.setText(event.getStatus());
|
statusLabel.setText(event.getStatus());
|
||||||
statusProgress.setVisible(false);
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
getGUIThread().invokeLater(() -> {
|
getGUIThread().invokeLater(() -> {
|
||||||
statusLabel.setText(event.getStatus());
|
statusLabel.setText(event.getStatus());
|
||||||
statusProgress.setVisible(true);
|
|
||||||
});
|
});
|
||||||
statusTimeline = new Timeline(
|
statusTimeline = new Timeline(
|
||||||
new KeyFrame(Duration.ZERO, new KeyValue(progressProperty, 0)),
|
new KeyFrame(Duration.ZERO, new KeyValue(progressProperty, 0)),
|
||||||
new KeyFrame(Duration.millis(event.getTimeMills()), e -> {
|
new KeyFrame(Duration.millis(event.getTimeMills()), e -> {
|
||||||
getGUIThread().invokeLater(() -> {
|
getGUIThread().invokeLater(() -> {
|
||||||
statusLabel.setText("");
|
statusLabel.setText("");
|
||||||
statusProgress.setVisible(false);
|
|
||||||
});
|
});
|
||||||
}, new KeyValue(progressProperty, 1))
|
}, new KeyValue(progressProperty, 1))
|
||||||
);
|
);
|
||||||
|
|
|
@ -28,21 +28,16 @@ public class Bip39Dialog extends NewWalletDialog {
|
||||||
|
|
||||||
private final Bip39 importer = new Bip39();
|
private final Bip39 importer = new Bip39();
|
||||||
|
|
||||||
private Wallet wallet;
|
|
||||||
|
|
||||||
private final String walletName;
|
|
||||||
private final ComboBox<DisplayScriptType> scriptType;
|
private final ComboBox<DisplayScriptType> scriptType;
|
||||||
private final TextBox seedWords;
|
private final TextBox seedWords;
|
||||||
private final TextBox passphrase;
|
private final TextBox passphrase;
|
||||||
private final Button createWallet;
|
private final Button createWallet;
|
||||||
|
|
||||||
public Bip39Dialog(String walletName) {
|
public Bip39Dialog(String walletName) {
|
||||||
super("Create BIP39 Wallet - " + walletName);
|
super("Create BIP39 Wallet - " + walletName, walletName);
|
||||||
|
|
||||||
setHints(List.of(Hint.CENTERED));
|
setHints(List.of(Hint.CENTERED));
|
||||||
|
|
||||||
this.walletName = walletName;
|
|
||||||
|
|
||||||
Panel mainPanel = new Panel();
|
Panel mainPanel = new Panel();
|
||||||
mainPanel.setLayoutManager(new GridLayout(2).setHorizontalSpacing(5).setVerticalSpacing(1));
|
mainPanel.setLayoutManager(new GridLayout(2).setHorizontalSpacing(5).setVerticalSpacing(1));
|
||||||
|
|
||||||
|
@ -152,35 +147,15 @@ public class Bip39Dialog extends NewWalletDialog {
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createWallet() {
|
@Override
|
||||||
close();
|
protected List<Wallet> getWallets() throws ImportException {
|
||||||
|
|
||||||
try {
|
|
||||||
wallet = getWallet();
|
|
||||||
discoverAndSaveWallet(wallet);
|
|
||||||
} catch(ImportException e) {
|
|
||||||
log.error("Cannot import wallet", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Wallet getWallet() throws ImportException {
|
|
||||||
Wallet wallet = new Wallet(walletName);
|
Wallet wallet = new Wallet(walletName);
|
||||||
wallet.setPolicyType(PolicyType.SINGLE);
|
wallet.setPolicyType(PolicyType.SINGLE);
|
||||||
wallet.setScriptType(scriptType.getSelectedItem().scriptType);
|
wallet.setScriptType(scriptType.getSelectedItem().scriptType);
|
||||||
Keystore keystore = importer.getKeystore(wallet.getScriptType().getDefaultDerivation(), getWords(), passphrase.getText());
|
Keystore keystore = importer.getKeystore(wallet.getScriptType().getDefaultDerivation(), getWords(), passphrase.getText());
|
||||||
wallet.getKeystores().add(keystore);
|
wallet.getKeystores().add(keystore);
|
||||||
wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, wallet.getScriptType(), wallet.getKeystores(), 1));
|
wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, wallet.getScriptType(), wallet.getKeystores(), 1));
|
||||||
return wallet;
|
return List.of(wallet);
|
||||||
}
|
|
||||||
|
|
||||||
private void onCancel() {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Wallet showDialog(WindowBasedTextGUI textGUI) {
|
|
||||||
super.showDialog(textGUI);
|
|
||||||
return wallet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class DisplayScriptType {
|
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.Storage;
|
||||||
import com.sparrowwallet.sparrow.io.StorageException;
|
import com.sparrowwallet.sparrow.io.StorageException;
|
||||||
import com.sparrowwallet.sparrow.io.WalletAndKey;
|
import com.sparrowwallet.sparrow.io.WalletAndKey;
|
||||||
|
import com.sparrowwallet.sparrow.terminal.ModalDialog;
|
||||||
import com.sparrowwallet.sparrow.terminal.SparrowTerminal;
|
import com.sparrowwallet.sparrow.terminal.SparrowTerminal;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
|
@ -26,11 +27,11 @@ import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
|
||||||
public class LoadWallet implements Runnable {
|
public class LoadWallet implements Runnable {
|
||||||
private static final Logger log = LoggerFactory.getLogger(LoadWallet.class);
|
private static final Logger log = LoggerFactory.getLogger(LoadWallet.class);
|
||||||
private final Storage storage;
|
private final Storage storage;
|
||||||
private final LoadingDialog loadingDialog;
|
private final ModalDialog loadingDialog;
|
||||||
|
|
||||||
public LoadWallet(Storage storage) {
|
public LoadWallet(Storage storage) {
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.loadingDialog = new LoadingDialog(storage);
|
this.loadingDialog = new ModalDialog(storage.getWalletName(null), "Loading...");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -47,6 +48,7 @@ public class LoadWallet implements Runnable {
|
||||||
openWallet(storage, walletAndKey);
|
openWallet(storage, walletAndKey);
|
||||||
});
|
});
|
||||||
loadWalletService.setOnFailed(workerStateEvent -> {
|
loadWalletService.setOnFailed(workerStateEvent -> {
|
||||||
|
SparrowTerminal.get().getGuiThread().invokeLater(() -> SparrowTerminal.get().getGui().removeWindow(loadingDialog));
|
||||||
Throwable exception = workerStateEvent.getSource().getException();
|
Throwable exception = workerStateEvent.getSource().getException();
|
||||||
if(exception instanceof StorageException) {
|
if(exception instanceof StorageException) {
|
||||||
showErrorDialog("Error Opening Wallet", exception.getMessage());
|
showErrorDialog("Error Opening Wallet", exception.getMessage());
|
||||||
|
@ -73,6 +75,7 @@ public class LoadWallet implements Runnable {
|
||||||
});
|
});
|
||||||
loadWalletService.setOnFailed(workerStateEvent -> {
|
loadWalletService.setOnFailed(workerStateEvent -> {
|
||||||
EventManager.get().post(new StorageEvent(storage.getWalletId(null), TimedEvent.Action.END, "Failed"));
|
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();
|
Throwable exception = loadWalletService.getException();
|
||||||
if(exception instanceof InvalidPasswordException) {
|
if(exception instanceof InvalidPasswordException) {
|
||||||
Optional<ButtonType> optResponse = showErrorDialog("Invalid Password", "The wallet password was invalid. Try again?", ButtonType.CANCEL, ButtonType.OK);
|
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));
|
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;
|
package com.sparrowwallet.sparrow.terminal.wallet;
|
||||||
|
|
||||||
import com.googlecode.lanterna.TerminalSize;
|
import com.googlecode.lanterna.gui2.*;
|
||||||
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 com.googlecode.lanterna.gui2.dialogs.DialogWindow;
|
||||||
import com.googlecode.lanterna.gui2.dialogs.TextInputDialogBuilder;
|
import com.googlecode.lanterna.gui2.dialogs.TextInputDialogBuilder;
|
||||||
import com.sparrowwallet.drongo.SecureString;
|
import com.sparrowwallet.drongo.SecureString;
|
||||||
|
@ -17,9 +13,11 @@ import com.sparrowwallet.sparrow.AppServices;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.StorageEvent;
|
import com.sparrowwallet.sparrow.event.StorageEvent;
|
||||||
import com.sparrowwallet.sparrow.event.TimedEvent;
|
import com.sparrowwallet.sparrow.event.TimedEvent;
|
||||||
|
import com.sparrowwallet.sparrow.io.ImportException;
|
||||||
import com.sparrowwallet.sparrow.io.Storage;
|
import com.sparrowwallet.sparrow.io.Storage;
|
||||||
import com.sparrowwallet.sparrow.io.StorageException;
|
import com.sparrowwallet.sparrow.io.StorageException;
|
||||||
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
|
import com.sparrowwallet.sparrow.terminal.ModalDialog;
|
||||||
import com.sparrowwallet.sparrow.terminal.SparrowTerminal;
|
import com.sparrowwallet.sparrow.terminal.SparrowTerminal;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -27,37 +25,78 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
|
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);
|
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);
|
super(title);
|
||||||
|
this.walletName = walletName;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void discoverAndSaveWallet(Wallet wallet) {
|
protected void createWallet() {
|
||||||
if(AppServices.onlineProperty().get()) {
|
close();
|
||||||
discoverAccounts(wallet);
|
|
||||||
} else {
|
try {
|
||||||
saveWallet(wallet);
|
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);
|
SparrowTerminal.get().getGui().addWindow(discoveringDialog);
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
ElectrumServer.WalletDiscoveryService walletDiscoveryService = new ElectrumServer.WalletDiscoveryService(List.of(wallet));
|
ElectrumServer.WalletDiscoveryService walletDiscoveryService = new ElectrumServer.WalletDiscoveryService(wallets);
|
||||||
walletDiscoveryService.setOnSucceeded(successEvent -> {
|
walletDiscoveryService.setOnSucceeded(successEvent -> {
|
||||||
|
Optional<Wallet> optWallet = walletDiscoveryService.getValue();
|
||||||
|
wallet = optWallet.orElseGet(() -> wallets.get(0));
|
||||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> {
|
SparrowTerminal.get().getGuiThread().invokeLater(() -> {
|
||||||
SparrowTerminal.get().getGui().removeWindow(discoveringDialog);
|
SparrowTerminal.get().getGui().removeWindow(discoveringDialog);
|
||||||
saveWallet(wallet);
|
saveWallet(wallet);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
walletDiscoveryService.setOnFailed(failedEvent -> {
|
walletDiscoveryService.setOnFailed(failedEvent -> {
|
||||||
|
wallet = wallets.get(0);
|
||||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> {
|
SparrowTerminal.get().getGuiThread().invokeLater(() -> {
|
||||||
SparrowTerminal.get().getGui().removeWindow(discoveringDialog);
|
SparrowTerminal.get().getGui().removeWindow(discoveringDialog);
|
||||||
saveWallet(wallet);
|
saveWallet(wallet);
|
||||||
|
@ -77,6 +116,9 @@ public class NewWalletDialog extends DialogWindow {
|
||||||
|
|
||||||
String password = builder.build().showDialog(SparrowTerminal.get().getGui());
|
String password = builder.build().showDialog(SparrowTerminal.get().getGui());
|
||||||
if(password != null) {
|
if(password != null) {
|
||||||
|
ModalDialog savingDialog = new ModalDialog(walletName, "Saving wallet...");
|
||||||
|
SparrowTerminal.get().getGui().addWindow(savingDialog);
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
if(password.length() == 0) {
|
if(password.length() == 0) {
|
||||||
try {
|
try {
|
||||||
|
@ -91,7 +133,10 @@ public class NewWalletDialog extends DialogWindow {
|
||||||
SparrowTerminal.addWallet(storage, childWallet);
|
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) {
|
} catch(IOException | StorageException | MnemonicException e) {
|
||||||
log.error("Error saving imported wallet", e);
|
log.error("Error saving imported wallet", e);
|
||||||
}
|
}
|
||||||
|
@ -120,7 +165,10 @@ public class NewWalletDialog extends DialogWindow {
|
||||||
SparrowTerminal.addWallet(storage, childWallet);
|
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) {
|
} catch(IOException | StorageException | MnemonicException e) {
|
||||||
log.error("Error saving imported wallet", e);
|
log.error("Error saving imported wallet", e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -131,6 +179,7 @@ public class NewWalletDialog extends DialogWindow {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
keyDerivationService.setOnFailed(workerStateEvent -> {
|
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"));
|
EventManager.get().post(new StorageEvent(Storage.getWalletFile(wallet.getName()).getAbsolutePath(), TimedEvent.Action.END, "Failed"));
|
||||||
showErrorDialog("Error encrypting wallet", keyDerivationService.getException().getMessage());
|
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) {
|
public static List<String> splitString(String stringToSplit, int maxLength) {
|
||||||
String text = stringToSplit;
|
String text = stringToSplit;
|
||||||
List<String> lines = new ArrayList<>();
|
List<String> lines = new ArrayList<>();
|
||||||
while(text.length() > maxLength) {
|
while(text.length() >= maxLength) {
|
||||||
int breakAt = maxLength - 1;
|
int breakAt = maxLength - 1;
|
||||||
lines.add(text.substring(0, breakAt));
|
lines.add(text.substring(0, breakAt));
|
||||||
text = text.substring(breakAt + 1);
|
text = text.substring(breakAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.add(text);
|
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 {
|
public class KeystoreController extends WalletFormController implements Initializable {
|
||||||
private static final Logger log = LoggerFactory.getLogger(KeystoreController.class);
|
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;
|
private Keystore keystore;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue