12 changed files with 339 additions and 98 deletions
@ -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", ' ')); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue