mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2025-11-05 11:56:37 +00:00
support sending to and displaying bip353 human readable names and include dnssec proof in associated psbts
This commit is contained in:
parent
9dcf210762
commit
5f62523710
29 changed files with 351 additions and 94 deletions
2
drongo
2
drongo
|
|
@ -1 +1 @@
|
|||
Subproject commit 2a456dd6027937705be7f6153b5a0a0eea757377
|
||||
Subproject commit 58cc096f8e5a1274945a252907100c1dc051a996
|
||||
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
|
||||
.id, .fixed-width {
|
||||
-fx-font-size: 13px;
|
||||
-fx-font-family: 'Roboto Mono';
|
||||
-fx-font-family: 'Fragment Mono Regular';
|
||||
}
|
||||
|
||||
.form-separator {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#fingerprint, #derivation, #xpub {
|
||||
-fx-font-size: 13px;
|
||||
-fx-font-family: 'Roboto Mono';
|
||||
-fx-font-family: 'Fragment Mono Regular';
|
||||
}
|
||||
|
||||
#type {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
BIN
src/main/resources/font/FragmentMono-Italic.ttf
Normal file
BIN
src/main/resources/font/FragmentMono-Italic.ttf
Normal file
Binary file not shown.
BIN
src/main/resources/font/FragmentMono-Regular.ttf
Normal file
BIN
src/main/resources/font/FragmentMono-Regular.ttf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
11
src/main/resources/image/bitcoin-character-invert.svg
Normal file
11
src/main/resources/image/bitcoin-character-invert.svg
Normal 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 |
11
src/main/resources/image/bitcoin-character.svg
Normal file
11
src/main/resources/image/bitcoin-character.svg
Normal 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 |
|
|
@ -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"/>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue