mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2026-08-09 08:14:43 +00:00
support sending to and displaying bip353 human readable names and include dnssec proof in associated psbts
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user