add bip322 message signing for silent payments wallets

This commit is contained in:
Craig Raw 2026-05-18 14:56:50 +02:00
parent e985a03a58
commit a2eb937fce
4 changed files with 47 additions and 24 deletions

2
drongo

@ -1 +1 @@
Subproject commit 698f8b08a170712a55fd6824a3d37c32b2d0e798
Subproject commit dfd947cb69ee26de3a8f2fbaf19966c7e2ff7fe1

View File

@ -1476,7 +1476,7 @@ public class AppController implements Initializable {
WalletForm selectedWalletForm = getSelectedWalletForm();
if(selectedWalletForm != null) {
Wallet wallet = selectedWalletForm.getWallet();
if(wallet.getPolicyType() == PolicyType.SINGLE_HD) {
if(wallet.getPolicyType() == PolicyType.SINGLE_HD || wallet.getPolicyType() == PolicyType.SINGLE_SP) {
//Can sign and verify
messageSignDialog = new MessageSignDialog(wallet);
}

View File

@ -414,7 +414,8 @@ public class EntryCell extends TreeTableCell<Entry, Entry> implements Confirmati
private static boolean canSignMessage(WalletNode walletNode) {
Wallet wallet = walletNode.getWallet();
return wallet.getPolicyType() == PolicyType.SINGLE_HD && (!wallet.isBip47() || walletNode.getKeyPurpose() == KeyPurpose.RECEIVE);
PolicyType policyType = wallet.getPolicyType();
return (policyType == PolicyType.SINGLE_HD || policyType == PolicyType.SINGLE_SP) && (!wallet.isBip47() || walletNode.getKeyPurpose() == KeyPurpose.RECEIVE);
}
private static boolean containsWalletOutputs(TransactionEntry transactionEntry) {

View File

@ -295,8 +295,8 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
}
private void checkWalletSigning(Wallet wallet) {
if(wallet.getKeystores().size() != 1 || wallet.getPolicyType() != PolicyType.SINGLE_HD) {
throw new IllegalArgumentException("Cannot sign messages using a non-HD wallet or a wallet with multiple keystores");
if(wallet.getKeystores().size() != 1 || (wallet.getPolicyType() != PolicyType.SINGLE_HD && wallet.getPolicyType() != PolicyType.SINGLE_SP)) {
throw new IllegalArgumentException("Cannot sign messages using this wallet type");
}
}
@ -377,18 +377,24 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
private void signUnencryptedKeystore(Wallet decryptedWallet) {
try {
Keystore keystore = decryptedWallet.getKeystores().getFirst();
ECKey privKey = keystore.getKey(walletNode);
String signatureText;
if(isBip322()) {
ScriptType scriptType = decryptedWallet.getScriptType();
signatureText = Bip322.signMessageBip322(scriptType, message.getText().trim(), privKey);
if(decryptedWallet.getPolicyType() == PolicyType.SINGLE_SP) {
ECKey spendPrivKey = keystore.getSpendPrivateKey(Collections.emptyMap());
signatureText = Bip322.signMessageBip322Sp(walletNode.getAddress(), message.getText().trim(), spendPrivKey, walletNode.getSilentPaymentTweak());
spendPrivKey.clear();
} else {
ScriptType scriptType = isElectrumSignatureFormat() ? ScriptType.P2PKH : decryptedWallet.getScriptType();
signatureText = privKey.signMessage(message.getText().trim(), scriptType);
ECKey privKey = keystore.getKey(walletNode);
if(isBip322()) {
ScriptType scriptType = decryptedWallet.getScriptType();
signatureText = Bip322.signMessageBip322(scriptType, message.getText().trim(), privKey);
} else {
ScriptType scriptType = isElectrumSignatureFormat() ? ScriptType.P2PKH : decryptedWallet.getScriptType();
signatureText = privKey.signMessage(message.getText().trim(), scriptType);
}
privKey.clear();
}
signature.clear();
signature.appendText(signatureText);
privKey.clear();
} catch(Exception e) {
log.error("Could not sign message", e);
AppServices.showErrorDialog("Could not sign message", e.getMessage());
@ -498,10 +504,7 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
private void showBip322Qr() {
Wallet signingWallet = walletNode.getWallet();
ScriptType scriptType = signingWallet.getScriptType();
PSBT psbt = Bip322.getBip322Psbt(scriptType, walletNode.getAddress(), message.getText().trim());
addBip322DerivationInfo(psbt, signingWallet);
PSBT psbt = buildBip322Psbt(signingWallet);
byte[] psbtBytes = psbt.getForExport().serialize();
CryptoPSBT cryptoPSBT = new CryptoPSBT(psbtBytes);
@ -514,6 +517,30 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
}
}
private PSBT buildBip322Psbt(Wallet signingWallet) {
if(signingWallet.getPolicyType() == PolicyType.SINGLE_SP) {
Keystore keystore = signingWallet.getKeystores().getFirst();
ECKey spendPubKey = keystore.getSilentPaymentScanAddress().getSpendKey();
KeyDerivation spendDerivation = new KeyDerivation(keystore.getKeyDerivation().getMasterFingerprint(), KeyDerivation.writePath(KeyDerivation.getBip352SpendDerivation(keystore.getKeyDerivation().getDerivation())));
return Bip322.getBip322PsbtSp(walletNode.getAddress(), message.getText().trim(), walletNode.getSilentPaymentTweak(), Map.of(spendPubKey, spendDerivation));
}
PSBT psbt = Bip322.getBip322Psbt(signingWallet.getScriptType(), walletNode.getAddress(), message.getText().trim());
addBip322DerivationInfo(psbt, signingWallet);
return psbt;
}
private String extractBip322Signature(PSBT signedPsbt) {
Wallet signingWallet = walletNode.getWallet();
if(signingWallet.getPolicyType() == PolicyType.SINGLE_SP) {
return Bip322.getBip322SignatureFromPsbtSp(signedPsbt);
}
ECKey pubKey = signingWallet.getKeystores().getFirst().getPubKey(walletNode);
return Bip322.getBip322SignatureFromPsbt(signingWallet.getScriptType(), signedPsbt, pubKey);
}
private void addBip322DerivationInfo(PSBT psbt, Wallet signingWallet) {
ScriptType scriptType = signingWallet.getScriptType();
PSBTInput psbtInput = psbt.getPsbtInputs().get(0);
@ -537,9 +564,7 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
QRScanDialog.Result result = optionalResult.get();
if(result.psbt != null) {
try {
Wallet signingWallet = walletNode.getWallet();
ECKey pubKey = signingWallet.getKeystores().get(0).getPubKey(walletNode);
String sig = Bip322.getBip322SignatureFromPsbt(signingWallet.getScriptType(), result.psbt, pubKey);
String sig = extractBip322Signature(result.psbt);
signature.clear();
signature.appendText(sig);
} catch(Exception e) {
@ -599,9 +624,7 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
private void exportBip322File() {
Wallet signingWallet = walletNode.getWallet();
ScriptType scriptType = signingWallet.getScriptType();
PSBT psbt = Bip322.getBip322Psbt(scriptType, walletNode.getAddress(), message.getText().trim());
addBip322DerivationInfo(psbt, signingWallet);
PSBT psbt = buildBip322Psbt(signingWallet);
Stage window = new Stage();
FileChooser fileChooser = new FileChooser();
@ -642,8 +665,7 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
try {
byte[] psbtBytes = Files.readAllBytes(file.toPath());
PSBT signedPsbt = new PSBT(psbtBytes, false);
ECKey pubKey = walletNode.getWallet().getKeystores().get(0).getPubKey(walletNode);
String sig = Bip322.getBip322SignatureFromPsbt(walletNode.getWallet().getScriptType(), signedPsbt, pubKey);
String sig = extractBip322Signature(signedPsbt);
signature.clear();
signature.appendText(sig);
return;