diff --git a/lark b/lark index a6371b74..45e7a2f9 160000 --- a/lark +++ b/lark @@ -1 +1 @@ -Subproject commit a6371b74df4deb788b4e745b47ce95115641984d +Subproject commit 45e7a2f97eaf15fc31ef151194703f0f368e264c diff --git a/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java b/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java index 29a69264..6ac41823 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java @@ -12,6 +12,7 @@ import com.sparrowwallet.drongo.policy.PolicyType; import com.sparrowwallet.drongo.protocol.ScriptType; import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.drongo.psbt.PSBT; +import com.sparrowwallet.drongo.silentpayments.SilentPaymentScanAddress; import com.sparrowwallet.drongo.wallet.*; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; @@ -730,13 +731,21 @@ public class DevicePane extends TitledDescriptionPane { } } - importXpub(derivation); + importKey(derivation); }); enumerateService.setOnFailed(workerStateEvent -> { setError("Error", enumerateService.getException().getMessage()); importButton.setDisable(false); }); enumerateService.start(); + } else { + importKey(derivation); + } + } + + private void importKey(List derivation) { + if(wallet != null && wallet.getPolicyType() == PolicyType.SINGLE_SP) { + importSpscan(derivation); } else { importXpub(derivation); } @@ -771,6 +780,35 @@ public class DevicePane extends TitledDescriptionPane { getXpubService.start(); } + private void importSpscan(List derivation) { + String derivationPath = KeyDerivation.writePath(derivation); + + Hwi.GetSpscanService getSpscanService = new Hwi.GetSpscanService(device, passphrase.get(), derivationPath); + getSpscanService.setOnSucceeded(workerStateEvent -> { + SilentPaymentScanAddress spscan = getSpscanService.getValue(); + + try { + Keystore keystore = new Keystore(); + keystore.setLabel(device.getModel().toDisplayString()); + keystore.setSource(KeystoreSource.HW_USB); + keystore.setWalletModel(device.getModel()); + keystore.setKeyDerivation(new KeyDerivation(device.getFingerprint(), derivationPath)); + keystore.setSilentPaymentScanAddress(spscan); + + importKeystore(derivation, keystore); + } catch(Exception e) { + setError("Could not retrieve spscan", e.getMessage()); + } + }); + getSpscanService.setOnFailed(workerStateEvent -> { + setError("Could not retrieve spscan", getSpscanService.getException().getMessage()); + importButton.setDisable(false); + }); + setDescription("Importing..."); + showHideLink.setVisible(false); + getSpscanService.start(); + } + private void importKeystore(List derivation, Keystore keystore) { if(wallet.getScriptType() == null) { ScriptType scriptType = Arrays.stream(ScriptType.ADDRESSABLE_TYPES).filter(type -> type.getDefaultDerivation().get(0).equals(derivation.get(0))).findFirst().orElse(ScriptType.P2PKH); @@ -1179,7 +1217,7 @@ public class DevicePane extends TitledDescriptionPane { showHideLink.setVisible(true); setExpanded(false); List importDerivation = KeyDerivation.parsePath(derivationField.getText()); - importXpub(importDerivation); + importKey(importDerivation); }); derivationField.textProperty().addListener((observable, oldValue, newValue) -> { diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java b/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java index 0b6ecc82..2122e101 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java @@ -6,6 +6,7 @@ import com.sparrowwallet.drongo.OsType; import com.sparrowwallet.drongo.OutputDescriptor; import com.sparrowwallet.drongo.protocol.ScriptType; import com.sparrowwallet.drongo.psbt.PSBT; +import com.sparrowwallet.drongo.silentpayments.SilentPaymentScanAddress; import com.sparrowwallet.drongo.wallet.StandardAccount; import com.sparrowwallet.drongo.wallet.WalletModel; import com.sparrowwallet.lark.DeviceException; @@ -167,6 +168,20 @@ public class Hwi { } } + public SilentPaymentScanAddress getSpscan(Device device, String passphrase, String derivationPath) throws ImportException { + try { + Lark lark = getLark(passphrase); + SilentPaymentScanAddress spscan = lark.getSpscanAtPath(device.getType(), device.getPath(), derivationPath); + isPromptActive = false; + return spscan; + } catch(DeviceException e) { + throw new ImportException(e.getMessage(), e); + } catch(RuntimeException e) { + log.error("Error retrieving spscan", e); + throw e; + } + } + public String displayAddress(Device device, String passphrase, ScriptType scriptType, OutputDescriptor addressDescriptor, OutputDescriptor walletDescriptor, String walletName, byte[] walletRegistration) throws DisplayAddressException { try { @@ -443,6 +458,28 @@ public class Hwi { } } + public static class GetSpscanService extends Service { + private final Device device; + private final String passphrase; + private final String derivationPath; + + public GetSpscanService(Device device, String passphrase, String derivationPath) { + this.device = device; + this.passphrase = passphrase; + this.derivationPath = derivationPath; + } + + @Override + protected Task createTask() { + return new Task<>() { + protected SilentPaymentScanAddress call() throws ImportException { + Hwi hwi = new Hwi(); + return hwi.getSpscan(device, passphrase, derivationPath); + } + }; + } + } + public static class GetXpubsService extends Service> { private final Device device; private final String passphrase; diff --git a/src/main/java/com/sparrowwallet/sparrow/io/ckcard/Tapsigner.java b/src/main/java/com/sparrowwallet/sparrow/io/ckcard/Tapsigner.java index 12ab589a..274b19d7 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/ckcard/Tapsigner.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/ckcard/Tapsigner.java @@ -54,6 +54,10 @@ public class Tapsigner implements KeystoreCardImport { @Override public Keystore getKeystore(PolicyType policyType, String pin, List derivation, StringProperty messageProperty) throws ImportException { + if(policyType == PolicyType.SINGLE_SP) { + throw new ImportException(getName() + " does not support receiving silent payments"); + } + if(pin.length() < 6) { throw new ImportException("PIN too short."); } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/keycard/Keycard.java b/src/main/java/com/sparrowwallet/sparrow/io/keycard/Keycard.java index dd9924b6..cf24fe6d 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/keycard/Keycard.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/keycard/Keycard.java @@ -54,6 +54,10 @@ public class Keycard implements KeystoreCardImport { @Override public Keystore getKeystore(PolicyType policyType, String pin, List derivation, StringProperty messageProperty) throws ImportException { + if(policyType == PolicyType.SINGLE_SP) { + throw new ImportException(getName() + " does not support receiving silent payments"); + } + if(!StringUtils.isNumeric(pin)) { throw new ImportException("PIN must be all digits."); } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/satochip/Satochip.java b/src/main/java/com/sparrowwallet/sparrow/io/satochip/Satochip.java index 29aa6b0c..788b4fc2 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/satochip/Satochip.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/satochip/Satochip.java @@ -53,6 +53,10 @@ public class Satochip implements KeystoreCardImport { @Override public Keystore getKeystore(PolicyType policyType, String pin, List derivation, StringProperty messageProperty) throws ImportException { + if(policyType == PolicyType.SINGLE_SP) { + throw new ImportException(getName() + " does not support receiving silent payments"); + } + if(pin.length() < 4) { throw new ImportException("PIN too short."); }