improve wallet descriptor and add output descriptor retrieval

This commit is contained in:
Craig Raw 2020-09-09 11:18:13 +02:00
parent e65f1ef3cc
commit b8c3bf1bea
9 changed files with 166 additions and 15 deletions

2
drongo

@ -1 +1 @@
Subproject commit 08c159ebadee78af391e83d91b6a453a28acbf3e
Subproject commit 488752c142765bacd0373390faccbdb11b47487a

View file

@ -1,6 +1,9 @@
package com.sparrowwallet.sparrow;
import com.sparrowwallet.drongo.policy.PolicyType;
import com.sparrowwallet.drongo.protocol.ScriptChunk;
import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.sparrow.control.DescriptorArea;
import com.sparrowwallet.sparrow.control.ScriptArea;
import javafx.geometry.Point2D;
import javafx.scene.control.Label;
@ -39,4 +42,42 @@ public abstract class BaseController {
protected String describeScriptChunk(ScriptChunk chunk) {
return chunk.toString();
}
protected void initializeDescriptorField(DescriptorArea descriptorArea) {
Popup popup = new Popup();
Label popupMsg = new Label();
popupMsg.getStyleClass().add("tooltip");
popup.getContent().add(popupMsg);
descriptorArea.setMouseOverTextDelay(Duration.ofMillis(150));
descriptorArea.addEventHandler(MouseOverTextEvent.MOUSE_OVER_TEXT_BEGIN, e -> {
TwoDimensional.Position position = descriptorArea.getParagraph(0).getStyleSpans().offsetToPosition(e.getCharacterIndex(), Backward);
int index = descriptorArea.getWallet().getPolicyType() == PolicyType.SINGLE ? position.getMajor() - 1 : ((position.getMajor() - 1) / 2);
if(position.getMajor() > 0 && index >= 0 && index < descriptorArea.getWallet().getKeystores().size()) {
Keystore hoverKeystore = descriptorArea.getWallet().getKeystores().get(index);
Point2D pos = e.getScreenPosition();
popupMsg.setText(describeKeystore(hoverKeystore));
popup.show(descriptorArea, pos.getX(), pos.getY() + 10);
}
});
descriptorArea.addEventHandler(MouseOverTextEvent.MOUSE_OVER_TEXT_END, e -> {
popup.hide();
});
}
protected String describeKeystore(Keystore keystore) {
if(keystore.isValid()) {
StringBuilder builder = new StringBuilder();
builder.append("[");
builder.append(keystore.getKeyDerivation().getMasterFingerprint());
builder.append("/");
builder.append(keystore.getKeyDerivation().getDerivationPath().replaceFirst("^m?/", ""));
builder.append("]");
builder.append(keystore.getExtendedPublicKey().toString());
return builder.toString();
}
return "Invalid";
}
}

View file

@ -0,0 +1,89 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.OutputDescriptor;
import com.sparrowwallet.drongo.policy.PolicyType;
import com.sparrowwallet.drongo.protocol.ScriptType;
import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.Wallet;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import org.fxmisc.richtext.CodeArea;
import java.util.List;
import static com.sparrowwallet.drongo.policy.PolicyType.MULTI;
import static com.sparrowwallet.drongo.policy.PolicyType.SINGLE;
import static com.sparrowwallet.drongo.protocol.ScriptType.MULTISIG;
public class DescriptorArea extends CodeArea {
private Wallet wallet;
public void setWallet(Wallet wallet) {
clear();
this.wallet = wallet;
DescriptorContextMenu contextMenu = new DescriptorContextMenu(wallet, this);
setContextMenu(contextMenu);
PolicyType policyType = wallet.getPolicyType();
ScriptType scriptType = wallet.getScriptType();
List<Keystore> keystores = wallet.getKeystores();
int threshold = wallet.getDefaultPolicy().getNumSignaturesRequired();
if(SINGLE.equals(policyType)) {
append(scriptType.getDescriptor(), "descriptor-text");
replace(getLength(), getLength(), keystores.get(0).getScriptName(), List.of(keystores.get(0).isValid() ? "descriptor-text" : "descriptor-error", keystores.get(0).getScriptName()));
append(scriptType.getCloseDescriptor(), "descriptor-text");
}
if(MULTI.equals(policyType)) {
append(scriptType.getDescriptor(), "descriptor-text");
append(MULTISIG.getDescriptor(), "descriptor-text");
append(Integer.toString(threshold), "descriptor-text");
for(Keystore keystore : keystores) {
append(",", "descriptor-text");
replace(getLength(), getLength(), keystore.getScriptName(), List.of(keystore.isValid() ? "descriptor-text" : "descriptor-error", keystore.getScriptName()));
}
append(MULTISIG.getCloseDescriptor(), "descriptor-text");
append(scriptType.getCloseDescriptor(), "descriptor-text");
}
}
public Wallet getWallet() {
return wallet;
}
public void clear() {
super.clear();
this.wallet = null;
setDisable(false);
setContextMenu(null);
}
private static class DescriptorContextMenu extends ContextMenu {
public DescriptorContextMenu(Wallet wallet, DescriptorArea descriptorArea) {
MenuItem copyvalue = new MenuItem("Copy Value");
copyvalue.setOnAction(AE -> {
hide();
ClipboardContent content = new ClipboardContent();
content.putString(descriptorArea.getText());
Clipboard.getSystemClipboard().setContent(content);
});
getItems().add(copyvalue);
MenuItem copyOutputDescriptor = new MenuItem("Copy Output Descriptor");
copyOutputDescriptor.setOnAction(AE -> {
hide();
ClipboardContent content = new ClipboardContent();
content.putString(OutputDescriptor.getOutputDescriptor(wallet).toString(true));
Clipboard.getSystemClipboard().setContent(content);
});
getItems().add(copyOutputDescriptor);
this.setStyle("-fx-background-color: -fx-color; -fx-font-family: System; -fx-font-size: 1em;");
}
}
}

View file

@ -2,7 +2,6 @@ package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.protocol.Script;
import com.sparrowwallet.drongo.protocol.ScriptChunk;
import com.sparrowwallet.sparrow.transaction.ScriptContextMenu;
import javafx.geometry.Pos;
import org.controlsfx.control.decoration.Decorator;
import org.controlsfx.control.decoration.GraphicDecoration;

View file

@ -1,4 +1,4 @@
package com.sparrowwallet.sparrow.transaction;
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.protocol.Script;
import com.sparrowwallet.drongo.protocol.ScriptChunk;
@ -33,7 +33,7 @@ public class ScriptContextMenu extends ContextMenu {
});
getItems().add(copyvalue);
this.setStyle("-fx-background-color: -fx-color; -fx-font-family: sans-serif; -fx-font-size: 1em;");
this.setStyle("-fx-background-color: -fx-color; -fx-font-family: System; -fx-font-size: 1em;");
area.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> {
hoverChunk = null;

View file

@ -13,6 +13,7 @@ import com.sparrowwallet.drongo.wallet.WalletModel;
import com.sparrowwallet.sparrow.AppController;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.CopyableLabel;
import com.sparrowwallet.sparrow.control.DescriptorArea;
import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
import com.sparrowwallet.sparrow.event.SettingsChangedEvent;
import com.sparrowwallet.sparrow.event.StorageEvent;
@ -46,7 +47,7 @@ public class SettingsController extends WalletFormController implements Initiali
private ComboBox<PolicyType> policyType;
@FXML
private TextField spendingMiniscript;
private DescriptorArea descriptor;
@FXML
private ComboBox<ScriptType> scriptType;
@ -160,6 +161,8 @@ public class SettingsController extends WalletFormController implements Initiali
}
});
initializeDescriptorField(descriptor);
revert.setOnAction(event -> {
keystoreTabs.getTabs().removeAll(keystoreTabs.getTabs());
totalKeystores.unbind();
@ -224,6 +227,7 @@ public class SettingsController extends WalletFormController implements Initiali
KeystoreController controller = keystoreLoader.getController();
controller.setKeystore(getWalletForm(), keystore);
tab.textProperty().bind(controller.getLabel().textProperty());
tab.setUserData(keystore);
controller.getValidationSupport().validationResultProperty().addListener((o, oldValue, result) -> {
if(result.getErrors().isEmpty()) {
@ -243,6 +247,19 @@ public class SettingsController extends WalletFormController implements Initiali
}
}
@Override
protected String describeKeystore(Keystore keystore) {
if(!keystore.isValid()) {
for(Tab tab : keystoreTabs.getTabs()) {
if(tab.getUserData() == keystore && tab.getTooltip() != null) {
return tab.getTooltip().getText();
}
}
}
return super.describeKeystore(keystore);
}
@Subscribe
public void update(SettingsChangedEvent event) {
Wallet wallet = event.getWallet();
@ -253,7 +270,7 @@ public class SettingsController extends WalletFormController implements Initiali
wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), wallet.getScriptType(), wallet.getKeystores(), (int)multisigControl.getLowValue()));
}
spendingMiniscript.setText(wallet.getDefaultPolicy().getMiniscript().getScript());
descriptor.setWallet(wallet);
revert.setDisable(false);
apply.setDisable(!wallet.isValid());
}

View file

@ -0,0 +1,3 @@
.descriptor-text { -fx-fill: #000000 }
.descriptor-error { -fx-fill: #ca1243 }

View file

@ -10,8 +10,4 @@
-fx-alignment: center-left;
}
#spendingMiniscript {
-fx-font-size: 13px;
-fx-font-family: 'Roboto Mono';
}

View file

@ -9,8 +9,10 @@
<?import com.sparrowwallet.sparrow.control.CopyableLabel?>
<?import com.sparrowwallet.drongo.policy.PolicyType?>
<?import com.sparrowwallet.drongo.protocol.ScriptType?>
<?import com.sparrowwallet.sparrow.control.DescriptorArea?>
<?import org.fxmisc.flowless.VirtualizedScrollPane?>
<BorderPane stylesheets="@settings.css, @wallet.css, @../general.css" styleClass="wallet-pane" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.wallet.SettingsController">
<BorderPane stylesheets="@settings.css, @wallet.css, @../script.css, @../descriptor.css, @../general.css" styleClass="wallet-pane" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.wallet.SettingsController">
<center>
<GridPane hgap="10.0" vgap="10.0">
<padding>
@ -59,7 +61,7 @@
<Form GridPane.columnIndex="1" GridPane.rowIndex="0">
<Fieldset inputGrow="SOMETIMES" text="" fx:id="multisigFieldset">
<Field text="Cosigners:">
<RangeSlider fx:id="multisigControl" showTickMarks="true" showTickLabels="true" blockIncrement="1" min="2" max="9" lowValue="2" highValue="3" snapToTicks="true" majorTickUnit="1" minorTickCount="0" />
<RangeSlider fx:id="multisigControl" showTickMarks="true" showTickLabels="true" blockIncrement="1" min="1" max="9" highValue="3" lowValue="2" snapToTicks="true" majorTickUnit="1" minorTickCount="0" />
</Field>
<Field text="M of N:">
<CopyableLabel fx:id="multisigLowLabel" />
@ -70,9 +72,13 @@
</Form>
<Form GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="1">
<Fieldset inputGrow="SOMETIMES" text="Spending Policy">
<Field text="Miniscript:">
<TextField fx:id="spendingMiniscript" editable="false" />
<Fieldset inputGrow="SOMETIMES" text="Script Policy">
<Field text="Descriptor:">
<VirtualizedScrollPane hbarPolicy="NEVER" vbarPolicy="NEVER">
<content>
<DescriptorArea fx:id="descriptor" editable="false" styleClass="uneditable-codearea" prefHeight="27" maxHeight="27" />
</content>
</VirtualizedScrollPane>
</Field>
</Fieldset>
</Form>