support sending to and displaying bip353 human readable names and include dnssec proof in associated psbts

This commit is contained in:
Craig Raw 2025-07-24 14:36:11 +02:00
parent 9dcf210762
commit 5f62523710
29 changed files with 351 additions and 94 deletions

2
drongo

@ -1 +1 @@
Subproject commit 2a456dd6027937705be7f6153b5a0a0eea757377
Subproject commit 58cc096f8e5a1274945a252907100c1dc051a996

View file

@ -1233,7 +1233,7 @@ public class AppServices {
}
public static Font getMonospaceFont() {
return Font.font("Roboto Mono", 13);
return Font.font("Fragment Mono Regular", 13);
}
public static boolean isOnWayland() {

View file

@ -113,8 +113,8 @@ public class SparrowDesktop extends Application {
private void initializeFonts() {
GlyphFontRegistry.register(new FontAwesome5());
GlyphFontRegistry.register(new FontAwesome5Brands());
Font.loadFont(AppServices.class.getResourceAsStream("/font/RobotoMono-Regular.ttf"), 13);
Font.loadFont(AppServices.class.getResourceAsStream("/font/RobotoMono-Italic.ttf"), 11);
Font.loadFont(AppServices.class.getResourceAsStream("/font/FragmentMono-Regular.ttf"), 13);
Font.loadFont(AppServices.class.getResourceAsStream("/font/FragmentMono-Italic.ttf"), 11);
if(OsType.getCurrent() == OsType.MACOS) {
Font.loadFont(AppServices.class.getResourceAsStream("/font/LiberationSans-Regular.ttf"), 13);
}

View file

@ -6,10 +6,16 @@ import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.input.Clipboard;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import org.controlsfx.control.textfield.CustomTextField;
import java.util.List;
public class ComboBoxTextField extends CustomTextField {
private final ObjectProperty<ComboBox<?>> comboProperty = new SimpleObjectProperty<>();
@ -68,4 +74,53 @@ public class ComboBoxTextField extends CustomTextField {
public void setComboProperty(ComboBox<?> comboProperty) {
this.comboProperty.set(comboProperty);
}
public ContextMenu getCustomContextMenu(List<MenuItem> customItems) {
return new CustomContextMenu(customItems);
}
public class CustomContextMenu extends ContextMenu {
public CustomContextMenu(List<MenuItem> customItems) {
super();
setFont(null);
MenuItem undo = new MenuItem("Undo");
undo.setOnAction(_ -> undo());
MenuItem redo = new MenuItem("Redo");
redo.setOnAction(_ -> redo());
MenuItem cut = new MenuItem("Cut");
cut.setOnAction(_ -> cut());
MenuItem copy = new MenuItem("Copy");
copy.setOnAction(_ -> copy());
MenuItem paste = new MenuItem("Paste");
paste.setOnAction(_ -> paste());
MenuItem delete = new MenuItem("Delete");
delete.setOnAction(_ -> deleteText(getSelection()));
MenuItem selectAll = new MenuItem("Select All");
selectAll.setOnAction(_ -> selectAll());
getItems().addAll(undo, redo, new SeparatorMenuItem(), cut, copy, paste, delete, new SeparatorMenuItem(), selectAll);
getItems().addAll(customItems);
setOnShowing(_ -> {
boolean hasSelection = getSelection().getLength() > 0;
boolean hasText = getText() != null && !getText().isEmpty();
boolean clipboardHasContent = Clipboard.getSystemClipboard().hasString();
undo.setDisable(!isUndoable());
redo.setDisable(!isRedoable());
cut.setDisable(!isEditable() || !hasSelection);
copy.setDisable(!hasSelection);
paste.setDisable(!isEditable() || !clipboardHasContent);
delete.setDisable(!hasSelection);
selectAll.setDisable(!hasText);
});
}
}
}

View file

@ -4,6 +4,8 @@ import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.OsType;
import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.bip47.PaymentCode;
import com.sparrowwallet.drongo.dns.DnsPayment;
import com.sparrowwallet.drongo.dns.DnsPaymentCache;
import com.sparrowwallet.drongo.protocol.Sha256Hash;
import com.sparrowwallet.drongo.protocol.TransactionOutput;
import com.sparrowwallet.drongo.uri.BitcoinURI;
@ -693,9 +695,10 @@ public class TransactionDiagram extends GridPane {
Wallet toWallet = walletTx.getToWallet(AppServices.get().getOpenWallets().keySet(), payment);
WalletNode toNode = walletTx.getWallet() != null && !walletTx.getWallet().isBip47() ? walletTx.getAddressNodeMap().get(payment.getAddress()) : null;
Wallet toBip47Wallet = getBip47SendWallet(payment);
DnsPayment dnsPayment = DnsPaymentCache.getDnsPayment(payment.getAddress());
Tooltip recipientTooltip = new Tooltip((toWallet == null ? (toNode != null ? "Consolidate " : "Pay ") : "Receive ")
+ getSatsValue(payment.getAmount()) + " sats to "
+ (payment instanceof AdditionalPayment ? (isExpanded() ? "\n" : "(click to expand)\n") + payment : (toWallet == null ? (payment.getLabel() == null ? (toNode != null ? toNode : (toBip47Wallet == null ? "external address" : toBip47Wallet.getDisplayName())) : payment.getLabel()) : toWallet.getFullDisplayName()) + "\n" + payment.getAddress().toString())
+ (payment instanceof AdditionalPayment ? (isExpanded() ? "\n" : "(click to expand)\n") + payment : (toWallet == null ? (dnsPayment == null ? (payment.getLabel() == null ? (toNode != null ? toNode : (toBip47Wallet == null ? "external address" : toBip47Wallet.getDisplayName())) : payment.getLabel()) : dnsPayment.toString()) : toWallet.getFullDisplayName()) + "\n" + payment.getAddress().toString())
+ (walletTx.isDuplicateAddress(payment) ? " (Duplicate)" : ""));
recipientTooltip.getStyleClass().add("recipient-label");
recipientTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY));

View file

@ -1,5 +1,7 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.dns.DnsPayment;
import com.sparrowwallet.drongo.dns.DnsPaymentCache;
import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
@ -206,7 +208,9 @@ public class TransactionDiagramLabel extends HBox {
WalletNode toNode = walletTx.getWallet() != null && !walletTx.getWallet().isBip47() ? walletTx.getAddressNodeMap().get(payment.getAddress()) : null;
Glyph glyph = GlyphUtils.getOutputGlyph(transactionDiagram.getWalletTransaction(), payment);
String text = (toWallet == null ? (toNode != null ? "Consolidate " : "Pay ") : "Receive ") + transactionDiagram.getSatsValue(payment.getAmount()) + " sats to " + payment.getAddress().toString();
DnsPayment dnsPayment = DnsPaymentCache.getDnsPayment(payment.getAddress());
String recipient = dnsPayment == null ? payment.getAddress().toString() : dnsPayment.toString();
String text = (toWallet == null ? (toNode != null ? "Consolidate " : "Pay ") : "Receive ") + transactionDiagram.getSatsValue(payment.getAmount()) + " sats to " + recipient;
return getOutputLabel(glyph, text);
}
@ -240,7 +244,7 @@ public class TransactionDiagramLabel extends HBox {
icon.setGraphic(glyph);
CopyableLabel label = new CopyableLabel();
label.setFont(Font.font("Roboto Mono Italic", 13));
label.setFont(Font.font("Fragment Mono Italic", 13));
label.setText(text);
HBox output = new HBox(5);

View file

@ -40,6 +40,10 @@ public class Payjoin {
this.wallet = wallet;
this.psbt = psbt;
if(payjoinURI.getAddress() == null) {
throw new IllegalArgumentException("Payjoin URI must have an address");
}
for(PSBTInput psbtInput : psbt.getPsbtInputs()) {
if(psbtInput.getUtxo() == null) {
throw new IllegalArgumentException("Original PSBT for payjoin transaction must have non_witness_utxo or witness_utxo fields for all inputs");

View file

@ -180,6 +180,9 @@ public class HeadersController extends TransactionFormController implements Init
@FXML
private CopyableLabel blockTimestamp;
@FXML
private Form blockchainSpacerForm;
@FXML
private Form signingWalletForm;
@ -451,6 +454,7 @@ public class HeadersController extends TransactionFormController implements Init
headersForm.setWalletTransaction(getWalletTransaction(headersForm.getInputTransactions()));
blockchainForm.managedProperty().bind(blockchainForm.visibleProperty());
blockchainSpacerForm.managedProperty().bind(blockchainForm.managedProperty());
signingWalletForm.managedProperty().bind(signingWalletForm.visibleProperty());
sigHashForm.managedProperty().bind(sigHashForm.visibleProperty());

View file

@ -9,15 +9,13 @@ import com.sparrowwallet.drongo.address.P2PKHAddress;
import com.sparrowwallet.drongo.bip47.InvalidPaymentCodeException;
import com.sparrowwallet.drongo.bip47.PaymentCode;
import com.sparrowwallet.drongo.crypto.ECKey;
import com.sparrowwallet.drongo.dns.DnsPaymentCache;
import com.sparrowwallet.drongo.protocol.ScriptType;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.protocol.TransactionOutput;
import com.sparrowwallet.drongo.uri.BitcoinURI;
import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.CurrencyRate;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.*;
import com.sparrowwallet.sparrow.control.*;
import com.sparrowwallet.sparrow.event.*;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
@ -25,6 +23,8 @@ import com.sparrowwallet.sparrow.io.CardApi;
import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.io.Storage;
import com.sparrowwallet.sparrow.net.ExchangeSource;
import com.sparrowwallet.drongo.dns.DnsPayment;
import com.sparrowwallet.drongo.dns.DnsPaymentResolver;
import com.sparrowwallet.sparrow.paynym.PayNym;
import com.sparrowwallet.sparrow.paynym.PayNymDialog;
import javafx.application.Platform;
@ -36,21 +36,32 @@ import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.layout.HBox;
import org.controlsfx.glyphfont.Glyph;
import org.controlsfx.validation.ValidationResult;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;
import org.girod.javafx.svgimage.SVGImage;
import org.girod.javafx.svgimage.SVGLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.TimeoutException;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -131,6 +142,8 @@ public class PaymentController extends WalletFormController implements Initializ
private final ObjectProperty<PayNym> payNymProperty = new SimpleObjectProperty<>();
private final ObjectProperty<DnsPayment> dnsPaymentProperty = new SimpleObjectProperty<>();
private static final Wallet payNymWallet = new Wallet() {
@Override
public String getFullDisplayName() {
@ -145,6 +158,76 @@ public class PaymentController extends WalletFormController implements Initializ
}
};
private final ChangeListener<String> addressListener = new ChangeListener<>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
address.leftProperty().set(null);
if(payNymProperty.get() != null && !newValue.equals(payNymProperty.get().nymName())) {
payNymProperty.set(null);
}
if(dnsPaymentProperty.get() != null && !newValue.equals(dnsPaymentProperty.get().hrn())) {
dnsPaymentProperty.set(null);
}
try {
BitcoinURI bitcoinURI = new BitcoinURI(newValue);
Platform.runLater(() -> updateFromURI(bitcoinURI));
return;
} catch(Exception e) {
//ignore, not a URI
}
String dnsPaymentHrn = getDnsPaymentHrn(newValue);
if(dnsPaymentHrn != null) {
DnsPaymentService dnsPaymentService = new DnsPaymentService(dnsPaymentHrn);
dnsPaymentService.setOnSucceeded(_ -> dnsPaymentService.getValue().ifPresent(dnsPayment -> setDnsPayment(dnsPayment)));
dnsPaymentService.setOnFailed(failEvent -> {
if(failEvent.getSource().getException() != null && !(failEvent.getSource().getException().getCause() instanceof TimeoutException)) {
AppServices.showErrorDialog("Validation failed for " + dnsPaymentHrn, failEvent.getSource().getException().getMessage());
}
});
dnsPaymentService.start();
return;
}
if(sendController.getWalletForm().getWallet().hasPaymentCode()) {
try {
PaymentCode paymentCode = new PaymentCode(newValue);
Wallet recipientBip47Wallet = sendController.getWalletForm().getWallet().getChildWallet(paymentCode, sendController.getWalletForm().getWallet().getScriptType());
if(recipientBip47Wallet == null && sendController.getWalletForm().getWallet().getScriptType() != ScriptType.P2PKH) {
recipientBip47Wallet = sendController.getWalletForm().getWallet().getChildWallet(paymentCode, ScriptType.P2PKH);
}
if(recipientBip47Wallet != null) {
PayNym payNym = PayNym.fromWallet(recipientBip47Wallet);
Platform.runLater(() -> setPayNym(payNym));
} else if(!paymentCode.equals(sendController.getWalletForm().getWallet().getPaymentCode())) {
ButtonType previewType = new ButtonType("Preview Transaction", ButtonBar.ButtonData.YES);
Optional<ButtonType> optButton = AppServices.showAlertDialog("Send notification transaction?", "This payment code is not yet linked with a notification transaction. Send a notification transaction?", Alert.AlertType.CONFIRMATION, ButtonType.CANCEL, previewType);
if(optButton.isPresent() && optButton.get() == previewType) {
Payment payment = new Payment(paymentCode.getNotificationAddress(), "Link " + paymentCode.toAbbreviatedString(), MINIMUM_P2PKH_OUTPUT_SATS, false);
Platform.runLater(() -> EventManager.get().post(new SpendUtxoEvent(sendController.getWalletForm().getWallet(), List.of(payment), List.of(new byte[80]), paymentCode)));
} else {
Platform.runLater(() -> address.setText(""));
}
}
} catch(Exception e) {
//ignore, not a payment code
}
}
revalidateAmount();
maxButton.setDisable(!isMaxButtonEnabled());
sendController.updateTransaction();
if(validationSupport != null) {
validationSupport.setErrorDecorationEnabled(true);
}
}
};
@Override
public void initialize(URL location, ResourceBundle resources) {
EventManager.get().register(this);
@ -210,6 +293,24 @@ public class PaymentController extends WalletFormController implements Initializ
revalidateAmount();
});
dnsPaymentProperty.addListener((observable, oldValue, dnsPayment) -> {
if(dnsPayment != null) {
MenuItem copyMenuItem = new MenuItem("Copy URI");
copyMenuItem.setOnAction(e -> {
ClipboardContent content = new ClipboardContent();
content.putString(dnsPayment.bitcoinURI().toURIString());
Clipboard.getSystemClipboard().setContent(content);
});
address.setContextMenu(address.getCustomContextMenu(List.of(copyMenuItem)));
} else {
address.setContextMenu(address.getCustomContextMenu(Collections.emptyList()));
}
revalidateAmount();
maxButton.setDisable(!isMaxButtonEnabled());
sendController.updateTransaction();
});
address.setTextFormatter(new TextFormatter<>(change -> {
String controlNewText = change.getControlNewText();
if(!controlNewText.equals(controlNewText.trim())) {
@ -222,55 +323,8 @@ public class PaymentController extends WalletFormController implements Initializ
return change;
}));
address.textProperty().addListener((observable, oldValue, newValue) -> {
address.leftProperty().set(null);
if(payNymProperty.get() != null && !newValue.equals(payNymProperty.get().nymName())) {
payNymProperty.set(null);
}
try {
BitcoinURI bitcoinURI = new BitcoinURI(newValue);
Platform.runLater(() -> updateFromURI(bitcoinURI));
return;
} catch(Exception e) {
//ignore, not a URI
}
if(sendController.getWalletForm().getWallet().hasPaymentCode()) {
try {
PaymentCode paymentCode = new PaymentCode(newValue);
Wallet recipientBip47Wallet = sendController.getWalletForm().getWallet().getChildWallet(paymentCode, sendController.getWalletForm().getWallet().getScriptType());
if(recipientBip47Wallet == null && sendController.getWalletForm().getWallet().getScriptType() != ScriptType.P2PKH) {
recipientBip47Wallet = sendController.getWalletForm().getWallet().getChildWallet(paymentCode, ScriptType.P2PKH);
}
if(recipientBip47Wallet != null) {
PayNym payNym = PayNym.fromWallet(recipientBip47Wallet);
Platform.runLater(() -> setPayNym(payNym));
} else if(!paymentCode.equals(sendController.getWalletForm().getWallet().getPaymentCode())) {
ButtonType previewType = new ButtonType("Preview Transaction", ButtonBar.ButtonData.YES);
Optional<ButtonType> optButton = AppServices.showAlertDialog("Send notification transaction?", "This payment code is not yet linked with a notification transaction. Send a notification transaction?", Alert.AlertType.CONFIRMATION, ButtonType.CANCEL, previewType);
if(optButton.isPresent() && optButton.get() == previewType) {
Payment payment = new Payment(paymentCode.getNotificationAddress(), "Link " + paymentCode.toAbbreviatedString(), MINIMUM_P2PKH_OUTPUT_SATS, false);
Platform.runLater(() -> EventManager.get().post(new SpendUtxoEvent(sendController.getWalletForm().getWallet(), List.of(payment), List.of(new byte[80]), paymentCode)));
} else {
Platform.runLater(() -> address.setText(""));
}
}
} catch(Exception e) {
//ignore, not a payment code
}
}
revalidateAmount();
maxButton.setDisable(!isMaxButtonEnabled());
sendController.updateTransaction();
if(validationSupport != null) {
validationSupport.setErrorDecorationEnabled(true);
}
});
address.textProperty().addListener(addressListener);
address.setContextMenu(address.getCustomContextMenu(Collections.emptyList()));
label.textProperty().addListener((observable, oldValue, newValue) -> {
maxButton.setDisable(!isMaxButtonEnabled());
@ -328,6 +382,23 @@ public class PaymentController extends WalletFormController implements Initializ
}
}
public void setDnsPayment(DnsPayment dnsPayment) {
if(dnsPayment.bitcoinURI().getAddress() == null) {
AppServices.showWarningDialog("No Address Provided", "The DNS payment instruction for " + dnsPayment.hrn() + " resolved correctly but did not contain a Bitcoin address.");
return;
}
DnsPaymentCache.putDnsPayment(dnsPayment.bitcoinURI().getAddress(), dnsPayment);
dnsPaymentProperty.set(dnsPayment);
address.setText(dnsPayment.hrn());
revalidate(address, addressListener);
address.leftProperty().set(getBitcoinCharacter());
if(label.getText().isEmpty()) {
label.setText("To " + dnsPayment);
}
label.requestFocus();
}
private void updateOpenWallets() {
updateOpenWallets(AppServices.get().getOpenWallets().keySet());
}
@ -399,6 +470,11 @@ public class PaymentController extends WalletFormController implements Initializ
}
private Address getRecipientAddress() throws InvalidAddressException {
DnsPayment dnsPayment = dnsPaymentProperty.get();
if(dnsPayment != null) {
return dnsPayment.bitcoinURI().getAddress();
}
PayNym payNym = payNymProperty.get();
if(payNym == null) {
return Address.fromString(address.getText());
@ -422,6 +498,25 @@ public class PaymentController extends WalletFormController implements Initializ
throw new InvalidAddressException();
}
private String getDnsPaymentHrn(String value) {
String hrn = value;
if(value.endsWith(".")) {
return null;
}
if(hrn.startsWith("")) {
hrn = hrn.substring(1);
}
String[] addressParts = hrn.split("@");
if(addressParts.length == 2 && addressParts[1].indexOf('.') > -1 && addressParts[1].substring(addressParts[1].indexOf('.') + 1).length() > 1 &&
StandardCharsets.US_ASCII.newEncoder().canEncode(hrn)) {
return hrn;
}
return null;
}
private Wallet getWalletForPayNym(PayNym payNym) throws InvalidPaymentCodeException {
Wallet masterWallet = sendController.getWalletForm().getMasterWallet();
return masterWallet.getChildWallet(new PaymentCode(payNym.paymentCode().toString()), payNym.segwit() ? ScriptType.P2WPKH : ScriptType.P2PKH);
@ -565,6 +660,7 @@ public class PaymentController extends WalletFormController implements Initializ
dustAmountProperty.set(false);
payNymProperty.set(null);
dnsPaymentProperty.set(null);
}
public void setMaxInput(ActionEvent event) {
@ -625,7 +721,7 @@ public class PaymentController extends WalletFormController implements Initializ
setRecipientValueSats(bitcoinURI.getAmount());
setFiatAmount(AppServices.getFiatCurrencyExchangeRate(), bitcoinURI.getAmount());
}
if(bitcoinURI.getPayjoinUrl() != null) {
if(bitcoinURI.getAddress() != null && bitcoinURI.getPayjoinUrl() != null) {
AppServices.addPayjoinURI(bitcoinURI);
}
sendController.updateTransaction();
@ -676,10 +772,33 @@ public class PaymentController extends WalletFormController implements Initializ
public static Glyph getPayNymGlyph() {
Glyph payNymGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.ROBOT);
payNymGlyph.getStyleClass().add("paynym-icon");
payNymGlyph.setFontSize(12);
payNymGlyph.setFontSize(10);
return payNymGlyph;
}
public static Node getBitcoinCharacter() {
try {
URL url;
if(Config.get().getTheme() == Theme.DARK) {
url = AppServices.class.getResource("/image/bitcoin-character-invert.svg");
} else {
url = AppServices.class.getResource("/image/bitcoin-character.svg");
}
if(url != null) {
SVGImage svgImage = SVGLoader.load(url);
HBox hBox = new HBox();
hBox.setAlignment(Pos.CENTER);
hBox.getChildren().add(svgImage);
hBox.setPadding(new Insets(0, 2, 0, 4));
return hBox;
}
} catch(Exception e) {
log.error("Could not load bitcoin character");
}
return null;
}
public static Glyph getNfcCardGlyph() {
Glyph nfcCardGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.WIFI);
nfcCardGlyph.getStyleClass().add("nfccard-icon");
@ -723,4 +842,23 @@ public class PaymentController extends WalletFormController implements Initializ
public void openWallets(OpenWalletsEvent event) {
updateOpenWallets(event.getWallets());
}
private static class DnsPaymentService extends Service<Optional<DnsPayment>> {
private final String hrn;
public DnsPaymentService(String hrn) {
this.hrn = hrn;
}
@Override
protected Task<Optional<DnsPayment>> createTask() {
return new Task<>() {
@Override
protected Optional<DnsPayment> call() throws Exception {
DnsPaymentResolver resolver = new DnsPaymentResolver(hrn);
return resolver.resolve();
}
};
}
}
}

View file

@ -46,7 +46,7 @@
.id, .fixed-width {
-fx-font-size: 13px;
-fx-font-family: 'Roboto Mono';
-fx-font-family: 'Fragment Mono Regular';
}
.form-separator {

View file

@ -29,7 +29,7 @@
.virtualized-scroll-pane .code-area, .uneditable-codearea {
-fx-font-size: 13px;
-fx-font-family: 'Roboto Mono';
-fx-font-family: 'Fragment Mono Regular';
-fx-padding: 4;
-fx-fill: -fx-text-inner-color;
}

View file

@ -44,7 +44,7 @@
#transactionDiagram .input-label, #transactionDiagram .recipient-label, #transactionDiagram .change-label, #transactionDiagram .fee-tooltip, #transactionDiagram .transaction-tooltip {
-fx-font-size: 13px;
-fx-font-family: 'Roboto Mono';
-fx-font-family: 'Fragment Mono Regular';
}
#transactionDiagram .fee-warning-icon {

View file

@ -41,6 +41,8 @@
</columnConstraints>
<rowConstraints>
<RowConstraints />
<RowConstraints />
<RowConstraints vgrow="SOMETIMES" />
</rowConstraints>
<Form GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2">
<Fieldset text="Transaction" inputGrow="SOMETIMES" wrapWidth="620">
@ -74,9 +76,11 @@
<TabPane side="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="2" styleClass="headers-tabs">
<Tab text="Overview" closable="false">
<VBox spacing="8">
<VBox spacing="8" alignment="CENTER">
<Region VBox.vgrow="SOMETIMES" />
<TransactionDiagram fx:id="transactionDiagram" maxWidth="700" final="true"/>
<TransactionDiagramLabel fx:id="transactionDiagramLabel" maxWidth="640" prefWidth="640" />
<Region VBox.vgrow="SOMETIMES" />
</VBox>
</Tab>
<Tab text="Detail" closable="false">
@ -176,7 +180,12 @@
<Separator GridPane.columnIndex="0" GridPane.rowIndex="5" GridPane.columnSpan="2" styleClass="form-separator"/>
<DynamicForm fx:id="blockchainForm" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="6">
<GridPane GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="6">
<columnConstraints>
<ColumnConstraints percentWidth="80" />
<ColumnConstraints percentWidth="20" />
</columnConstraints>
<DynamicForm fx:id="blockchainForm" GridPane.columnIndex="0" GridPane.rowIndex="0">
<Fieldset text="Blockchain" inputGrow="SOMETIMES">
<Field text="Status:">
<Label fx:id="blockStatus" contentDisplay="RIGHT" graphicTextGap="5" />
@ -189,6 +198,23 @@
</Field>
</Fieldset>
</DynamicForm>
<Form fx:id="blockchainSpacerForm" GridPane.columnIndex="1" GridPane.rowIndex="0" visible="false">
<Fieldset text="Spacer" inputGrow="SOMETIMES">
<VBox>
<ProgressBar styleClass="signatures-progress-bar" maxWidth="Infinity" minHeight="50" prefHeight="50" progress="0" />
</VBox>
<VBox>
<HBox styleClass="signatures-buttons" spacing="20">
<Button HBox.hgrow="ALWAYS" textAlignment="CENTER" text="Spacer" contentDisplay="TOP" wrapText="true">
<graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="20" icon="SEARCH" />
</graphic>
</Button>
</HBox>
</VBox>
</Fieldset>
</Form>
</GridPane>
<Form fx:id="signingWalletForm" GridPane.columnIndex="0" GridPane.rowIndex="6">
<Fieldset text="Signatures" inputGrow="SOMETIMES" styleClass="relaxedLabelFieldSet">
@ -236,7 +262,7 @@
<Fieldset text="Signatures" inputGrow="SOMETIMES">
<VBox>
<SignaturesProgressBar fx:id="signaturesProgressBar" />
<ProgressBar fx:id="broadcastProgressBar" maxWidth="Infinity" prefHeight="50" />
<ProgressBar fx:id="broadcastProgressBar" maxWidth="Infinity" minHeight="50" prefHeight="50" />
</VBox>
<VBox>
<HBox fx:id="signButtonBox" styleClass="signatures-buttons" spacing="20">

View file

@ -23,7 +23,7 @@
.chart-legend-item {
-fx-font-size: 13px;
-fx-font-family: 'Roboto Mono';
-fx-font-family: 'Fragment Mono Regular';
}
.default-color0.chart-pie { -fx-pie-color: #ca1243 }

View file

@ -23,7 +23,7 @@
.chart-legend-item {
-fx-font-size: 13;
-fx-font-family: 'Roboto Mono';
-fx-font-family: 'Fragment Mono Regular';
}
.default-color7.chart-pie { -fx-pie-color: #0184bc }

View file

@ -1,7 +1,7 @@
#txhex {
-fx-background-color: -fx-control-inner-background;
-fx-font-size: 13px;
-fx-font-family: 'Roboto Mono';
-fx-font-family: 'Fragment Mono Regular';
-fx-padding: 2;
color-0: #ca1243;
color-1: #d75f00;

View file

@ -15,7 +15,7 @@
#fingerprint, #derivation, #xpub {
-fx-font-size: 13px;
-fx-font-family: 'Roboto Mono';
-fx-font-family: 'Fragment Mono Regular';
}
#type {

View file

@ -21,7 +21,7 @@
<Insets top="10.0" bottom="10.0" />
</padding>
<columnConstraints>
<ColumnConstraints prefWidth="410" />
<ColumnConstraints prefWidth="410" hgrow="SOMETIMES" />
<ColumnConstraints prefWidth="200" />
<ColumnConstraints prefWidth="105" />
</columnConstraints>

View file

@ -26,7 +26,7 @@
<Insets left="25.0" right="25.0" top="25.0" />
</padding>
<columnConstraints>
<ColumnConstraints prefWidth="620" />
<ColumnConstraints prefWidth="620" hgrow="SOMETIMES" />
<ColumnConstraints prefWidth="140" />
</columnConstraints>
<rowConstraints>

View file

@ -71,7 +71,7 @@
#transactionDiagram .input-label, #transactionDiagram .recipient-label, #transactionDiagram .change-label, #transactionDiagram .fee-tooltip, #transactionDiagram .transaction-tooltip {
-fx-font-size: 13px;
-fx-font-family: 'Roboto Mono';
-fx-font-family: 'Fragment Mono Regular';
}
#transactionDiagram .fee-warning-icon {

View file

@ -36,7 +36,7 @@
<Insets left="25.0" right="25.0" top="25.0" />
</padding>
<columnConstraints>
<ColumnConstraints prefWidth="410" />
<ColumnConstraints prefWidth="410" hgrow="SOMETIMES" />
<ColumnConstraints prefWidth="200" />
<ColumnConstraints prefWidth="140" />
</columnConstraints>
@ -152,9 +152,9 @@
<RecentBlocksView fx:id="recentBlocksView" styleClass="feeRatesChart" AnchorPane.topAnchor="10" AnchorPane.leftAnchor="74" translateY="30" minHeight="135"/>
</AnchorPane>
</GridPane>
<AnchorPane>
<TransactionDiagram fx:id="transactionDiagram" maxWidth="700" AnchorPane.leftAnchor="100" />
</AnchorPane>
<StackPane VBox.vgrow="SOMETIMES">
<TransactionDiagram fx:id="transactionDiagram" maxWidth="700" />
</StackPane>
</VBox>
</center>
<bottom>

View file

@ -31,7 +31,7 @@
.address-cell, .utxo-row.entry-cell {
-fx-font-size: 13px;
-fx-font-family: 'Roboto Mono';
-fx-font-family: 'Fragment Mono Regular';
}
.cell > .hyperlink {
@ -149,7 +149,7 @@
.address-text-field {
-fx-font-size: 13px;
-fx-font-family: 'Roboto Mono';
-fx-font-family: 'Fragment Mono Regular';
}
.unconfirmed-row {

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="7px" height="13px" viewBox="0 0 7 13" version="1.1">
<g id="surface1">
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(255, 255, 255);fill-opacity:1;" d="M 3.84375 1.777344 L 3.867188 1.371094 C 3.382812 1.339844 3.144531 1.339844 2.78125 1.339844 L 0.410156 1.339844 C 0.300781 1.339844 0.195312 1.386719 0.121094 1.460938 C 0.0429688 1.539062 0 1.644531 0 1.75 L 0 11.121094 C 0 11.226562 0.0429688 11.332031 0.121094 11.410156 C 0.195312 11.484375 0.300781 11.527344 0.410156 11.527344 L 3.023438 11.527344 C 3.351562 11.527344 3.625 11.527344 3.984375 11.5 L 3.949219 11.09375 L 3.976562 11.5 C 4.753906 11.457031 5.496094 11.1875 6.054688 10.699219 C 6.335938 10.457031 6.566406 10.15625 6.726562 9.808594 C 6.886719 9.460938 6.976562 9.0625 6.976562 8.632812 C 6.976562 7.921875 6.777344 7.257812 6.34375 6.746094 C 6.125 6.488281 5.855469 6.273438 5.535156 6.109375 C 5.21875 5.945312 4.851562 5.832031 4.445312 5.773438 L 4.386719 6.175781 L 4.449219 6.582031 C 5.097656 6.480469 5.65625 6.183594 6.039062 5.722656 C 6.421875 5.265625 6.625 4.660156 6.621094 3.992188 C 6.621094 3.617188 6.554688 3.261719 6.417969 2.949219 C 6.21875 2.476562 5.867188 2.089844 5.425781 1.824219 C 4.984375 1.558594 4.453125 1.410156 3.871094 1.371094 L 3.867188 1.371094 L 3.84375 1.777344 L 3.8125 2.1875 C 4.453125 2.230469 4.949219 2.433594 5.28125 2.734375 C 5.445312 2.886719 5.574219 3.0625 5.660156 3.269531 C 5.75 3.476562 5.800781 3.714844 5.800781 3.992188 C 5.800781 4.503906 5.652344 4.902344 5.40625 5.199219 C 5.160156 5.492188 4.804688 5.695312 4.324219 5.773438 C 4.121094 5.804688 3.972656 5.976562 3.976562 6.179688 C 3.976562 6.382812 4.125 6.554688 4.324219 6.582031 C 4.65625 6.632812 4.929688 6.71875 5.15625 6.835938 C 5.5 7.015625 5.738281 7.253906 5.902344 7.550781 C 6.066406 7.847656 6.152344 8.210938 6.152344 8.636719 C 6.152344 8.953125 6.089844 9.226562 5.980469 9.464844 C 5.8125 9.824219 5.539062 10.109375 5.183594 10.320312 C 4.828125 10.527344 4.394531 10.65625 3.925781 10.683594 L 3.917969 10.683594 C 3.59375 10.710938 3.351562 10.710938 3.023438 10.710938 L 0.820312 10.710938 L 0.820312 2.160156 L 2.777344 2.160156 C 3.148438 2.160156 3.347656 2.160156 3.816406 2.1875 L 3.84375 1.777344 L 3.8125 2.1875 Z M 3.84375 1.777344 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(255, 255, 255);fill-opacity:1;" d="M 4.460938 5.808594 L 0.5 5.808594 C 0.273438 5.808594 0.0898438 5.988281 0.0898438 6.214844 C 0.0898438 6.441406 0.273438 6.625 0.5 6.625 L 4.460938 6.625 C 4.691406 6.625 4.875 6.441406 4.875 6.214844 C 4.875 5.988281 4.691406 5.808594 4.460938 5.808594 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(255, 255, 255);fill-opacity:1;" d="M 0.96875 0.410156 L 0.96875 1.6875 C 0.96875 1.914062 1.152344 2.097656 1.378906 2.097656 C 1.605469 2.097656 1.789062 1.914062 1.789062 1.6875 L 1.789062 0.410156 C 1.789062 0.183594 1.605469 0 1.378906 0 C 1.152344 0 0.96875 0.183594 0.96875 0.410156 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(255, 255, 255);fill-opacity:1;" d="M 2.820312 0.410156 L 2.820312 1.6875 C 2.820312 1.914062 3.003906 2.097656 3.230469 2.097656 C 3.457031 2.097656 3.640625 1.914062 3.640625 1.6875 L 3.640625 0.410156 C 3.640625 0.183594 3.457031 0 3.230469 0 C 3.003906 0 2.820312 0.183594 2.820312 0.410156 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(255, 255, 255);fill-opacity:1;" d="M 0.851562 11.300781 L 0.851562 12.578125 C 0.851562 12.804688 1.035156 12.988281 1.261719 12.988281 C 1.488281 12.988281 1.671875 12.804688 1.671875 12.578125 L 1.671875 11.300781 C 1.671875 11.074219 1.488281 10.890625 1.261719 10.890625 C 1.035156 10.890625 0.851562 11.074219 0.851562 11.300781 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(255, 255, 255);fill-opacity:1;" d="M 2.699219 11.300781 L 2.699219 12.578125 C 2.699219 12.804688 2.882812 12.988281 3.113281 12.988281 C 3.339844 12.988281 3.523438 12.804688 3.523438 12.578125 L 3.523438 11.300781 C 3.523438 11.074219 3.339844 10.890625 3.113281 10.890625 C 2.886719 10.890625 2.699219 11.074219 2.699219 11.300781 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="7px" height="13px" viewBox="0 0 7 13" version="1.1">
<g id="surface1">
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(14.117648%,14.117648%,14.117648%);fill-opacity:1;" d="M 3.84375 1.777344 L 3.867188 1.371094 C 3.382812 1.339844 3.144531 1.339844 2.78125 1.339844 L 0.410156 1.339844 C 0.300781 1.339844 0.195312 1.386719 0.121094 1.460938 C 0.0429688 1.539062 0 1.644531 0 1.75 L 0 11.121094 C 0 11.226562 0.0429688 11.332031 0.121094 11.410156 C 0.195312 11.484375 0.300781 11.527344 0.410156 11.527344 L 3.023438 11.527344 C 3.351562 11.527344 3.625 11.527344 3.984375 11.5 L 3.949219 11.09375 L 3.976562 11.5 C 4.753906 11.457031 5.496094 11.1875 6.054688 10.699219 C 6.335938 10.457031 6.566406 10.15625 6.726562 9.808594 C 6.886719 9.460938 6.976562 9.0625 6.976562 8.632812 C 6.976562 7.921875 6.777344 7.257812 6.34375 6.746094 C 6.125 6.488281 5.855469 6.273438 5.535156 6.109375 C 5.21875 5.945312 4.851562 5.832031 4.445312 5.773438 L 4.386719 6.175781 L 4.449219 6.582031 C 5.097656 6.480469 5.65625 6.183594 6.039062 5.722656 C 6.421875 5.265625 6.625 4.660156 6.621094 3.992188 C 6.621094 3.617188 6.554688 3.261719 6.417969 2.949219 C 6.21875 2.476562 5.867188 2.089844 5.425781 1.824219 C 4.984375 1.558594 4.453125 1.410156 3.871094 1.371094 L 3.867188 1.371094 L 3.84375 1.777344 L 3.8125 2.1875 C 4.453125 2.230469 4.949219 2.433594 5.28125 2.734375 C 5.445312 2.886719 5.574219 3.0625 5.660156 3.269531 C 5.75 3.476562 5.800781 3.714844 5.800781 3.992188 C 5.800781 4.503906 5.652344 4.902344 5.40625 5.199219 C 5.160156 5.492188 4.804688 5.695312 4.324219 5.773438 C 4.121094 5.804688 3.972656 5.976562 3.976562 6.179688 C 3.976562 6.382812 4.125 6.554688 4.324219 6.582031 C 4.65625 6.632812 4.929688 6.71875 5.15625 6.835938 C 5.5 7.015625 5.738281 7.253906 5.902344 7.550781 C 6.066406 7.847656 6.152344 8.210938 6.152344 8.636719 C 6.152344 8.953125 6.089844 9.226562 5.980469 9.464844 C 5.8125 9.824219 5.539062 10.109375 5.183594 10.320312 C 4.828125 10.527344 4.394531 10.65625 3.925781 10.683594 L 3.917969 10.683594 C 3.59375 10.710938 3.351562 10.710938 3.023438 10.710938 L 0.820312 10.710938 L 0.820312 2.160156 L 2.777344 2.160156 C 3.148438 2.160156 3.347656 2.160156 3.816406 2.1875 L 3.84375 1.777344 L 3.8125 2.1875 Z M 3.84375 1.777344 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(14.117648%,14.117648%,14.117648%);fill-opacity:1;" d="M 4.460938 5.808594 L 0.5 5.808594 C 0.273438 5.808594 0.0898438 5.988281 0.0898438 6.214844 C 0.0898438 6.441406 0.273438 6.625 0.5 6.625 L 4.460938 6.625 C 4.691406 6.625 4.875 6.441406 4.875 6.214844 C 4.875 5.988281 4.691406 5.808594 4.460938 5.808594 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(14.117648%,14.117648%,14.117648%);fill-opacity:1;" d="M 0.96875 0.410156 L 0.96875 1.6875 C 0.96875 1.914062 1.152344 2.097656 1.378906 2.097656 C 1.605469 2.097656 1.789062 1.914062 1.789062 1.6875 L 1.789062 0.410156 C 1.789062 0.183594 1.605469 0 1.378906 0 C 1.152344 0 0.96875 0.183594 0.96875 0.410156 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(14.117648%,14.117648%,14.117648%);fill-opacity:1;" d="M 2.820312 0.410156 L 2.820312 1.6875 C 2.820312 1.914062 3.003906 2.097656 3.230469 2.097656 C 3.457031 2.097656 3.640625 1.914062 3.640625 1.6875 L 3.640625 0.410156 C 3.640625 0.183594 3.457031 0 3.230469 0 C 3.003906 0 2.820312 0.183594 2.820312 0.410156 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(14.117648%,14.117648%,14.117648%);fill-opacity:1;" d="M 0.851562 11.300781 L 0.851562 12.578125 C 0.851562 12.804688 1.035156 12.988281 1.261719 12.988281 C 1.488281 12.988281 1.671875 12.804688 1.671875 12.578125 L 1.671875 11.300781 C 1.671875 11.074219 1.488281 10.890625 1.261719 10.890625 C 1.035156 10.890625 0.851562 11.074219 0.851562 11.300781 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(14.117648%,14.117648%,14.117648%);fill-opacity:1;" d="M 2.699219 11.300781 L 2.699219 12.578125 C 2.699219 12.804688 2.882812 12.988281 3.113281 12.988281 C 3.339844 12.988281 3.523438 12.804688 3.523438 12.578125 L 3.523438 11.300781 C 3.523438 11.074219 3.339844 10.890625 3.113281 10.890625 C 2.886719 10.890625 2.699219 11.074219 2.699219 11.300781 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -36,6 +36,7 @@
<logger name="org.springframework.web.HttpLogging" level="OFF" />
<logger name="org.springframework.web.socket.sockjs.client.SockJsClient" level="OFF" />
<logger name="org.springframework.web.socket.sockjs.client.DefaultTransportRequest" level="OFF" />
<logger name="org.xbill.DNS.dnssec.DnsSecVerifier" level="ERROR" />
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>