diff --git a/drongo b/drongo index 698f8b08..dfd947cb 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 698f8b08a170712a55fd6824a3d37c32b2d0e798 +Subproject commit dfd947cb69ee26de3a8f2fbaf19966c7e2ff7fe1 diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index 68ec821a..2288e602 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -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); } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java b/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java index e57cabf5..f82dc30c 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java @@ -414,7 +414,8 @@ public class EntryCell extends TreeTableCell 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) { diff --git a/src/main/java/com/sparrowwallet/sparrow/control/MessageSignDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/MessageSignDialog.java index 360d333a..3aa467dc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/MessageSignDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/MessageSignDialog.java @@ -295,8 +295,8 @@ public class MessageSignDialog extends Dialog { } 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 { 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 { 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 { } } + 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 { 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 { 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 { 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;