mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
improve wallet descriptor and add output descriptor retrieval
This commit is contained in:
parent
e65f1ef3cc
commit
b8c3bf1bea
9 changed files with 166 additions and 15 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 08c159ebadee78af391e83d91b6a453a28acbf3e
|
Subproject commit 488752c142765bacd0373390faccbdb11b47487a
|
|
@ -1,6 +1,9 @@
|
||||||
package com.sparrowwallet.sparrow;
|
package com.sparrowwallet.sparrow;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
import com.sparrowwallet.drongo.protocol.ScriptChunk;
|
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 com.sparrowwallet.sparrow.control.ScriptArea;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
@ -39,4 +42,42 @@ public abstract class BaseController {
|
||||||
protected String describeScriptChunk(ScriptChunk chunk) {
|
protected String describeScriptChunk(ScriptChunk chunk) {
|
||||||
return chunk.toString();
|
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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.protocol.Script;
|
import com.sparrowwallet.drongo.protocol.Script;
|
||||||
import com.sparrowwallet.drongo.protocol.ScriptChunk;
|
import com.sparrowwallet.drongo.protocol.ScriptChunk;
|
||||||
import com.sparrowwallet.sparrow.transaction.ScriptContextMenu;
|
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import org.controlsfx.control.decoration.Decorator;
|
import org.controlsfx.control.decoration.Decorator;
|
||||||
import org.controlsfx.control.decoration.GraphicDecoration;
|
import org.controlsfx.control.decoration.GraphicDecoration;
|
||||||
|
|
|
@ -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.Script;
|
||||||
import com.sparrowwallet.drongo.protocol.ScriptChunk;
|
import com.sparrowwallet.drongo.protocol.ScriptChunk;
|
||||||
|
@ -33,7 +33,7 @@ public class ScriptContextMenu extends ContextMenu {
|
||||||
});
|
});
|
||||||
|
|
||||||
getItems().add(copyvalue);
|
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 -> {
|
area.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> {
|
||||||
hoverChunk = null;
|
hoverChunk = null;
|
|
@ -13,6 +13,7 @@ import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||||
import com.sparrowwallet.sparrow.AppController;
|
import com.sparrowwallet.sparrow.AppController;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.control.CopyableLabel;
|
import com.sparrowwallet.sparrow.control.CopyableLabel;
|
||||||
|
import com.sparrowwallet.sparrow.control.DescriptorArea;
|
||||||
import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
|
import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
|
||||||
import com.sparrowwallet.sparrow.event.SettingsChangedEvent;
|
import com.sparrowwallet.sparrow.event.SettingsChangedEvent;
|
||||||
import com.sparrowwallet.sparrow.event.StorageEvent;
|
import com.sparrowwallet.sparrow.event.StorageEvent;
|
||||||
|
@ -46,7 +47,7 @@ public class SettingsController extends WalletFormController implements Initiali
|
||||||
private ComboBox<PolicyType> policyType;
|
private ComboBox<PolicyType> policyType;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TextField spendingMiniscript;
|
private DescriptorArea descriptor;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private ComboBox<ScriptType> scriptType;
|
private ComboBox<ScriptType> scriptType;
|
||||||
|
@ -160,6 +161,8 @@ public class SettingsController extends WalletFormController implements Initiali
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initializeDescriptorField(descriptor);
|
||||||
|
|
||||||
revert.setOnAction(event -> {
|
revert.setOnAction(event -> {
|
||||||
keystoreTabs.getTabs().removeAll(keystoreTabs.getTabs());
|
keystoreTabs.getTabs().removeAll(keystoreTabs.getTabs());
|
||||||
totalKeystores.unbind();
|
totalKeystores.unbind();
|
||||||
|
@ -224,6 +227,7 @@ public class SettingsController extends WalletFormController implements Initiali
|
||||||
KeystoreController controller = keystoreLoader.getController();
|
KeystoreController controller = keystoreLoader.getController();
|
||||||
controller.setKeystore(getWalletForm(), keystore);
|
controller.setKeystore(getWalletForm(), keystore);
|
||||||
tab.textProperty().bind(controller.getLabel().textProperty());
|
tab.textProperty().bind(controller.getLabel().textProperty());
|
||||||
|
tab.setUserData(keystore);
|
||||||
|
|
||||||
controller.getValidationSupport().validationResultProperty().addListener((o, oldValue, result) -> {
|
controller.getValidationSupport().validationResultProperty().addListener((o, oldValue, result) -> {
|
||||||
if(result.getErrors().isEmpty()) {
|
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
|
@Subscribe
|
||||||
public void update(SettingsChangedEvent event) {
|
public void update(SettingsChangedEvent event) {
|
||||||
Wallet wallet = event.getWallet();
|
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()));
|
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);
|
revert.setDisable(false);
|
||||||
apply.setDisable(!wallet.isValid());
|
apply.setDisable(!wallet.isValid());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
.descriptor-text { -fx-fill: #000000 }
|
||||||
|
.descriptor-error { -fx-fill: #ca1243 }
|
||||||
|
|
|
@ -10,8 +10,4 @@
|
||||||
-fx-alignment: center-left;
|
-fx-alignment: center-left;
|
||||||
}
|
}
|
||||||
|
|
||||||
#spendingMiniscript {
|
|
||||||
-fx-font-size: 13px;
|
|
||||||
-fx-font-family: 'Roboto Mono';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
<?import com.sparrowwallet.sparrow.control.CopyableLabel?>
|
<?import com.sparrowwallet.sparrow.control.CopyableLabel?>
|
||||||
<?import com.sparrowwallet.drongo.policy.PolicyType?>
|
<?import com.sparrowwallet.drongo.policy.PolicyType?>
|
||||||
<?import com.sparrowwallet.drongo.protocol.ScriptType?>
|
<?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>
|
<center>
|
||||||
<GridPane hgap="10.0" vgap="10.0">
|
<GridPane hgap="10.0" vgap="10.0">
|
||||||
<padding>
|
<padding>
|
||||||
|
@ -59,7 +61,7 @@
|
||||||
<Form GridPane.columnIndex="1" GridPane.rowIndex="0">
|
<Form GridPane.columnIndex="1" GridPane.rowIndex="0">
|
||||||
<Fieldset inputGrow="SOMETIMES" text="" fx:id="multisigFieldset">
|
<Fieldset inputGrow="SOMETIMES" text="" fx:id="multisigFieldset">
|
||||||
<Field text="Cosigners:">
|
<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>
|
||||||
<Field text="M of N:">
|
<Field text="M of N:">
|
||||||
<CopyableLabel fx:id="multisigLowLabel" />
|
<CopyableLabel fx:id="multisigLowLabel" />
|
||||||
|
@ -70,9 +72,13 @@
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
<Form GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="1">
|
<Form GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="1">
|
||||||
<Fieldset inputGrow="SOMETIMES" text="Spending Policy">
|
<Fieldset inputGrow="SOMETIMES" text="Script Policy">
|
||||||
<Field text="Miniscript:">
|
<Field text="Descriptor:">
|
||||||
<TextField fx:id="spendingMiniscript" editable="false" />
|
<VirtualizedScrollPane hbarPolicy="NEVER" vbarPolicy="NEVER">
|
||||||
|
<content>
|
||||||
|
<DescriptorArea fx:id="descriptor" editable="false" styleClass="uneditable-codearea" prefHeight="27" maxHeight="27" />
|
||||||
|
</content>
|
||||||
|
</VirtualizedScrollPane>
|
||||||
</Field>
|
</Field>
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
Loading…
Reference in a new issue