optionally show output descriptor qr export as bbqr, update coldcard import and export instructions

This commit is contained in:
Craig Raw 2024-05-09 12:20:54 +02:00
parent f6ff92865b
commit daf320f36b
10 changed files with 63 additions and 19 deletions

2
drongo

@ -1 +1 @@
Subproject commit 60ac42800222a487651b93529796d31e5a954b99 Subproject commit 33bf35e3c4fdaed6e6b0d598efaa3f8f0406c266

View file

@ -3,13 +3,14 @@ package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.hummingbird.UR; import com.sparrowwallet.hummingbird.UR;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
import com.sparrowwallet.sparrow.io.PdfUtils; import com.sparrowwallet.sparrow.io.PdfUtils;
import com.sparrowwallet.sparrow.io.bbqr.BBQR;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.control.Button; import javafx.scene.control.Button;
public class DescriptorQRDisplayDialog extends QRDisplayDialog { public class DescriptorQRDisplayDialog extends QRDisplayDialog {
public DescriptorQRDisplayDialog(String walletName, String outputDescriptor, UR ur) { public DescriptorQRDisplayDialog(String walletName, String outputDescriptor, UR ur, BBQR bbqr, boolean selectBbqrButton) {
super(ur); super(ur, bbqr, false, false, selectBbqrButton);
DialogPane dialogPane = getDialogPane(); DialogPane dialogPane = getDialogPane();
final ButtonType pdfButtonType = new javafx.scene.control.ButtonType("Save PDF...", ButtonBar.ButtonData.HELP_2); final ButtonType pdfButtonType = new javafx.scene.control.ButtonType("Save PDF...", ButtonBar.ButtonData.HELP_2);
@ -19,7 +20,7 @@ public class DescriptorQRDisplayDialog extends QRDisplayDialog {
pdfButton.setGraphicTextGap(5); pdfButton.setGraphicTextGap(5);
pdfButton.setGraphic(getGlyph(FontAwesome5.Glyph.FILE_PDF)); pdfButton.setGraphic(getGlyph(FontAwesome5.Glyph.FILE_PDF));
pdfButton.addEventFilter(ActionEvent.ACTION, event -> { pdfButton.addEventFilter(ActionEvent.ACTION, event -> {
PdfUtils.saveOutputDescriptor(walletName, outputDescriptor, ur); PdfUtils.saveOutputDescriptor(walletName, outputDescriptor, ur, isUseBbqrEncoding() ? bbqr : null);
event.consume(); event.consume();
}); });
} }

View file

@ -171,11 +171,18 @@ public class FileWalletExportPane extends TitledDescriptionPane {
} else if(exporter instanceof Bip129) { } else if(exporter instanceof Bip129) {
UR ur = UR.fromBytes(outputStream.toByteArray()); UR ur = UR.fromBytes(outputStream.toByteArray());
BBQR bbqr = new BBQR(BBQRType.UNICODE, outputStream.toByteArray()); BBQR bbqr = new BBQR(BBQRType.UNICODE, outputStream.toByteArray());
qrDisplayDialog = new QRDisplayDialog(ur, bbqr, false, true, false); qrDisplayDialog = new QRDisplayDialog(ur, bbqr, false, false, false);
} else if(exporter instanceof Descriptor) { } else if(exporter instanceof Descriptor) {
boolean addBbqrOption = exportWallet.getKeystores().stream().anyMatch(keystore -> keystore.getWalletModel().showBbqr());
boolean selectBbqrOption = exportWallet.getKeystores().stream().allMatch(keystore -> keystore.getWalletModel().selectBbqr());
OutputDescriptor outputDescriptor = OutputDescriptor.getOutputDescriptor(exportWallet, KeyPurpose.DEFAULT_PURPOSES, null); OutputDescriptor outputDescriptor = OutputDescriptor.getOutputDescriptor(exportWallet, KeyPurpose.DEFAULT_PURPOSES, null);
CryptoOutput cryptoOutput = getCryptoOutput(exportWallet); CryptoOutput cryptoOutput = getCryptoOutput(exportWallet);
qrDisplayDialog = new DescriptorQRDisplayDialog(exportWallet.getFullDisplayName(), outputDescriptor.toString(true), cryptoOutput.toUR()); BBQR bbqr = addBbqrOption ? new BBQR(BBQRType.UNICODE, outputDescriptor.toString(true).getBytes(StandardCharsets.UTF_8)) : null;
qrDisplayDialog = new DescriptorQRDisplayDialog(exportWallet.getFullDisplayName(), outputDescriptor.toString(true), cryptoOutput.toUR(), bbqr, selectBbqrOption);
} else if(exporter.getClass().equals(ColdcardMultisig.class)) {
UR ur = UR.fromBytes(outputStream.toByteArray());
BBQR bbqr = new BBQR(BBQRType.UNICODE, outputStream.toByteArray());
qrDisplayDialog = new QRDisplayDialog(ur, bbqr, false, false, true);
} else { } else {
qrDisplayDialog = new QRDisplayDialog(outputStream.toString(StandardCharsets.UTF_8)); qrDisplayDialog = new QRDisplayDialog(outputStream.toString(StandardCharsets.UTF_8));
} }

View file

@ -259,6 +259,10 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
} }
} }
public boolean isUseBbqrEncoding() {
return useBbqrEncoding;
}
private void setUseBbqrEncoding(boolean useBbqrEncoding) { private void setUseBbqrEncoding(boolean useBbqrEncoding) {
if(useBbqrEncoding) { if(useBbqrEncoding) {
this.useBbqrEncoding = true; this.useBbqrEncoding = true;

View file

@ -111,7 +111,7 @@ public class ColdcardMultisig implements WalletImport, KeystoreFileImport, Walle
@Override @Override
public String getKeystoreImportDescription(int account) { public String getKeystoreImportDescription(int account) {
return "Import file created by using the Settings > Multisig Wallets > Export XPUB > " + account + " feature on your Coldcard."; return "Import file or QR created by using Advanced/Tools > Export Wallet > Sparrow Wallet" + (account > 0 ? " > 1 > " + account : "") + " on your Coldcard. For older firmware use Settings > Multisig Wallets > Export XPUB > " + account + ".";
} }
@Override @Override
@ -186,7 +186,7 @@ public class ColdcardMultisig implements WalletImport, KeystoreFileImport, Walle
@Override @Override
public String getWalletImportDescription() { public String getWalletImportDescription() {
return "Import file created by using the Settings > Multisig Wallets > [Wallet Detail] > Coldcard Export feature on your Coldcard."; return "Import file or QR created by using Settings > Multisig Wallets > [Wallet Detail] > Coldcard Export on your Coldcard.";
} }
@Override @Override
@ -239,7 +239,7 @@ public class ColdcardMultisig implements WalletImport, KeystoreFileImport, Walle
@Override @Override
public String getWalletExportDescription() { public String getWalletExportDescription() {
return "Export file that can be read by your Coldcard using the Settings > Multisig Wallets > Import from File feature."; return "Export file or QR that can be read by your Coldcard using Settings > Multisig Wallets > Import from File or QR.";
} }
@Override @Override

View file

@ -30,7 +30,7 @@ public class ColdcardSinglesig implements KeystoreFileImport, WalletImport {
@Override @Override
public String getKeystoreImportDescription(int account) { public String getKeystoreImportDescription(int account) {
return "Import file created by using the Advanced > MicroSD > Export Wallet > Generic JSON > " + account + " feature on your Coldcard. Note this requires firmware version 3.1.3 or later."; return "Import file or QR created by using Advanced/Tools > Export Wallet > Sparrow Wallet" + (account > 0 ? " > 1 > " + account : "") + " on your Coldcard. For older firmware use Advanced > MicroSD > Export Wallet > Generic JSON > " + account + ".";
} }
@Override @Override

View file

@ -22,6 +22,9 @@ import com.sparrowwallet.drongo.protocol.ScriptType;
import com.sparrowwallet.hummingbird.UR; import com.sparrowwallet.hummingbird.UR;
import com.sparrowwallet.hummingbird.UREncoder; import com.sparrowwallet.hummingbird.UREncoder;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.io.bbqr.BBQR;
import com.sparrowwallet.sparrow.io.bbqr.BBQREncoder;
import com.sparrowwallet.sparrow.io.bbqr.BBQREncoding;
import javafx.embed.swing.SwingFXUtils; import javafx.embed.swing.SwingFXUtils;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import javafx.stage.Stage; import javafx.stage.Stage;
@ -39,7 +42,7 @@ public class PdfUtils {
private static final int QR_WIDTH = 480; private static final int QR_WIDTH = 480;
private static final int QR_HEIGHT = 480; private static final int QR_HEIGHT = 480;
public static void saveOutputDescriptor(String walletName, String outputDescriptor, UR ur) { public static void saveOutputDescriptor(String walletName, String outputDescriptor, UR ur, BBQR bbqr) {
Stage window = new Stage(); Stage window = new Stage();
FileChooser fileChooser = new FileChooser(); FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Save PDF"); fileChooser.setTitle("Save PDF");
@ -56,9 +59,22 @@ public class PdfUtils {
Chunk title = new Chunk("Output descriptor for " + walletName, titleFont); Chunk title = new Chunk("Output descriptor for " + walletName, titleFont);
document.add(title); document.add(title);
String fragment = null;
if(bbqr != null) {
BBQREncoder bbqrEncoder = new BBQREncoder(bbqr.type(), BBQREncoding.ZLIB, bbqr.data(), 2000, 0);
if(bbqrEncoder.isSinglePart()) {
fragment = bbqrEncoder.nextPart();
}
}
if(fragment == null) {
UREncoder urEncoder = new UREncoder(ur, 2000, 10, 0); UREncoder urEncoder = new UREncoder(ur, 2000, 10, 0);
String fragment = urEncoder.nextPart();
if(urEncoder.isSinglePart()) { if(urEncoder.isSinglePart()) {
fragment = urEncoder.nextPart();
}
}
if(fragment != null) {
Image image = Image.getInstance(SwingFXUtils.fromFXImage(getQrCode(fragment), null), Color.WHITE); Image image = Image.getInstance(SwingFXUtils.fromFXImage(getQrCode(fragment), null), Color.WHITE);
document.add(image); document.add(image);
} }

View file

@ -905,9 +905,9 @@ public class HeadersController extends TransactionFormController implements Init
toggleButton.setSelected(false); toggleButton.setSelected(false);
//TODO: Remove once Cobo Vault support has been removed //TODO: Remove once Cobo Vault support has been removed
boolean addLegacyEncodingOption = headersForm.getSigningWallet().getKeystores().stream().anyMatch(keystore -> keystore.getWalletModel().equals(WalletModel.COBO_VAULT)); boolean addLegacyEncodingOption = headersForm.getSigningWallet().getKeystores().stream().anyMatch(keystore -> keystore.getWalletModel().showLegacyQR());
boolean addBbqrOption = headersForm.getSigningWallet().getKeystores().stream().anyMatch(keystore -> keystore.getWalletModel().equals(WalletModel.COLDCARD) || keystore.getSource().equals(KeystoreSource.SW_WATCH) || keystore.getSource().equals(KeystoreSource.SW_SEED)); boolean addBbqrOption = headersForm.getSigningWallet().getKeystores().stream().anyMatch(keystore -> keystore.getWalletModel().showBbqr());
boolean selectBbqrOption = headersForm.getSigningWallet().getKeystores().stream().allMatch(keystore -> keystore.getWalletModel().equals(WalletModel.COLDCARD)); boolean selectBbqrOption = headersForm.getSigningWallet().getKeystores().stream().allMatch(keystore -> keystore.getWalletModel().selectBbqr());
//Don't include non witness utxo fields for segwit wallets when displaying the PSBT as a QR - it can add greatly to the time required for scanning //Don't include non witness utxo fields for segwit wallets when displaying the PSBT as a QR - it can add greatly to the time required for scanning
boolean includeNonWitnessUtxos = !Arrays.asList(ScriptType.WITNESS_TYPES).contains(headersForm.getSigningWallet().getScriptType()); boolean includeNonWitnessUtxos = !Arrays.asList(ScriptType.WITNESS_TYPES).contains(headersForm.getSigningWallet().getScriptType());

View file

@ -5,12 +5,16 @@ import com.sparrowwallet.hummingbird.UR;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
import com.sparrowwallet.sparrow.io.PdfUtils; import com.sparrowwallet.sparrow.io.PdfUtils;
import com.sparrowwallet.sparrow.io.bbqr.BBQR;
import com.sparrowwallet.sparrow.io.bbqr.BBQRType;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority; import javafx.scene.layout.Priority;
import org.controlsfx.glyphfont.Glyph; import org.controlsfx.glyphfont.Glyph;
import java.nio.charset.StandardCharsets;
public class MultisigBackupDialog extends Dialog<String> { public class MultisigBackupDialog extends Dialog<String> {
private final Wallet wallet; private final Wallet wallet;
private final String descriptor; private final String descriptor;
@ -71,7 +75,12 @@ public class MultisigBackupDialog extends Dialog<String> {
final ButtonBar.ButtonData buttonData = buttonType.getButtonData(); final ButtonBar.ButtonData buttonData = buttonType.getButtonData();
ButtonBar.setButtonData(pdfButton, buttonData); ButtonBar.setButtonData(pdfButton, buttonData);
pdfButton.setOnAction(event -> { pdfButton.setOnAction(event -> {
PdfUtils.saveOutputDescriptor(wallet.getFullDisplayName(), descriptor, ur); BBQR bbqr = null;
if(wallet.getKeystores().stream().allMatch(keystore -> keystore.getWalletModel().selectBbqr())) {
bbqr = new BBQR(BBQRType.UNICODE, descriptor.getBytes(StandardCharsets.UTF_8));
}
PdfUtils.saveOutputDescriptor(wallet.getFullDisplayName(), descriptor, ur, bbqr);
}); });
button = pdfButton; button = pdfButton;

View file

@ -18,6 +18,8 @@ import com.sparrowwallet.sparrow.event.*;
import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.io.Storage; import com.sparrowwallet.sparrow.io.Storage;
import com.sparrowwallet.sparrow.io.StorageException; import com.sparrowwallet.sparrow.io.StorageException;
import com.sparrowwallet.sparrow.io.bbqr.BBQR;
import com.sparrowwallet.sparrow.io.bbqr.BBQRType;
import com.sparrowwallet.sparrow.net.ElectrumServer; import com.sparrowwallet.sparrow.net.ElectrumServer;
import com.sparrowwallet.sparrow.net.ServerType; import com.sparrowwallet.sparrow.net.ServerType;
import javafx.application.Platform; import javafx.application.Platform;
@ -37,6 +39,7 @@ import tornadofx.control.Fieldset;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -376,8 +379,12 @@ public class SettingsController extends WalletFormController implements Initiali
return; return;
} }
boolean addBbqrOption = walletForm.getWallet().getKeystores().stream().anyMatch(keystore -> keystore.getWalletModel().showBbqr());
boolean selectBbqrOption = walletForm.getWallet().getKeystores().stream().allMatch(keystore -> keystore.getWalletModel().selectBbqr());
UR cryptoOutputUR = cryptoOutput.toUR(); UR cryptoOutputUR = cryptoOutput.toUR();
QRDisplayDialog qrDisplayDialog = new DescriptorQRDisplayDialog(walletForm.getWallet().getFullDisplayName(), outputDescriptor.toString(true), cryptoOutputUR); BBQR bbqr = addBbqrOption ? new BBQR(BBQRType.UNICODE, outputDescriptor.toString(true).getBytes(StandardCharsets.UTF_8)) : null;
QRDisplayDialog qrDisplayDialog = new DescriptorQRDisplayDialog(walletForm.getWallet().getFullDisplayName(), outputDescriptor.toString(true), cryptoOutputUR, bbqr, selectBbqrOption);
qrDisplayDialog.initOwner(showDescriptorQR.getScene().getWindow()); qrDisplayDialog.initOwner(showDescriptorQR.getScene().getWindow());
qrDisplayDialog.showAndWait(); qrDisplayDialog.showAndWait();
} }