What changed, and why it matters
This commit is a small internal cleanup: it stops converting a hardware wallet's extended public key (xpub) to a text string and back again, and instead keeps it as a typed object throughout the code. There is no visible user-facing change and no indication of a security fix.
No security action required; treat as a normal code-quality refactor.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors Hwi.getXpub() and Hwi.getXpubs() to return ExtendedKey objects rather than String descriptors. Callers in DevicePane.java are updated to remove the now-unnecessary ExtendedKey.fromDescriptor() calls. This is a type-safety/roundtrip elimination refactor with no functional change to parsing, validation, or key handling.
Changed components
src/main/java/com/sparrowwallet/sparrow/control/DevicePane.javasrc/main/java/com/sparrowwallet/sparrow/io/Hwi.javaInspect captured patch +19 / −19
diff --git a/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java b/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java
index 6ac4182..befe198 100644
--- a/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java
+++ b/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java
@@ -756,7 +756,7 @@ public class DevicePane extends TitledDescriptionPane {
Hwi.GetXpubService getXpubService = new Hwi.GetXpubService(device, passphrase.get(), derivationPath);
getXpubService.setOnSucceeded(workerStateEvent -> {
- String xpub = getXpubService.getValue();
+ ExtendedKey xpub = getXpubService.getValue();
try {
Keystore keystore = new Keystore();
@@ -764,7 +764,7 @@ public class DevicePane extends TitledDescriptionPane {
keystore.setSource(KeystoreSource.HW_USB);
keystore.setWalletModel(device.getModel());
keystore.setKeyDerivation(new KeyDerivation(device.getFingerprint(), derivationPath));
- keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(xpub));
+ keystore.setExtendedPublicKey(xpub);
importKeystore(derivation, keystore);
} catch(Exception e) {
@@ -976,9 +976,9 @@ public class DevicePane extends TitledDescriptionPane {
Hwi.GetXpubsService getXpubsService = new Hwi.GetXpubsService(device, passphrase.get(), derivationPaths);
getXpubsService.setOnSucceeded(_ -> {
- Map<Hwi.WalletType, String> accountXpubs = getXpubsService.getValue();
+ Map<Hwi.WalletType, ExtendedKey> accountXpubs = getXpubsService.getValue();
- for(Map.Entry<Hwi.WalletType, String> entry : accountXpubs.entrySet()) {
+ for(Map.Entry<Hwi.WalletType, ExtendedKey> entry : accountXpubs.entrySet()) {
try {
Wallet wallet = new Wallet(device.getModel().toDisplayString());
wallet.setPolicyType(PolicyType.SINGLE_HD);
@@ -988,7 +988,7 @@ public class DevicePane extends TitledDescriptionPane {
keystore.setSource(KeystoreSource.HW_USB);
keystore.setWalletModel(device.getModel());
keystore.setKeyDerivation(new KeyDerivation(device.getFingerprint(), derivationPaths.get(entry.getKey())));
- keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(entry.getValue()));
+ keystore.setExtendedPublicKey(entry.getValue());
wallet.getKeystores().add(keystore);
wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE_HD, entry.getKey().scriptType(), wallet.getKeystores(), 1));
if(entry.getKey().standardAccount().equals(StandardAccount.ACCOUNT_0)) {
@@ -1070,16 +1070,16 @@ public class DevicePane extends TitledDescriptionPane {
Map<StandardAccount, Keystore> importedKeystores = new LinkedHashMap<>();
Hwi.GetXpubsService getXpubsService = new Hwi.GetXpubsService(device, passphrase.get(), accountDerivationPaths);
getXpubsService.setOnSucceeded(workerStateEvent -> {
- Map<Hwi.WalletType, String> accountXpubs = getXpubsService.getValue();
+ Map<Hwi.WalletType, ExtendedKey> accountXpubs = getXpubsService.getValue();
- for(Map.Entry<Hwi.WalletType, String> entry : accountXpubs.entrySet()) {
+ for(Map.Entry<Hwi.WalletType, ExtendedKey> entry : accountXpubs.entrySet()) {
try {
Keystore keystore = new Keystore();
keystore.setLabel(device.getModel().toDisplayString());
keystore.setSource(KeystoreSource.HW_USB);
keystore.setWalletModel(device.getModel());
keystore.setKeyDerivation(new KeyDerivation(masterFingerprint, accountDerivationPaths.get(entry.getKey())));
- keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(entry.getValue()));
+ keystore.setExtendedPublicKey(entry.getValue());
importedKeystores.put(entry.getKey().standardAccount(), keystore);
} catch(Exception e) {
setError("Could not retrieve xpub", e.getMessage());
diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java b/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java
index 2122e10..d62c77c 100644
--- a/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java
+++ b/src/main/java/com/sparrowwallet/sparrow/io/Hwi.java
@@ -146,7 +146,7 @@ public class Hwi {
}
}
- public Map<WalletType, String> getXpubs(Device device, String passphrase, Map<WalletType, String> accountDerivationPaths, Map<WalletType, String> accountXpubs) throws ImportException {
+ public Map<WalletType, ExtendedKey> getXpubs(Device device, String passphrase, Map<WalletType, String> accountDerivationPaths, Map<WalletType, ExtendedKey> accountXpubs) throws ImportException {
for(Map.Entry<WalletType, String> entry : accountDerivationPaths.entrySet()) {
accountXpubs.put(entry.getKey(), getXpub(device, passphrase, entry.getValue()));
}
@@ -154,12 +154,12 @@ public class Hwi {
return accountXpubs;
}
- public String getXpub(Device device, String passphrase, String derivationPath) throws ImportException {
+ public ExtendedKey getXpub(Device device, String passphrase, String derivationPath) throws ImportException {
try {
Lark lark = getLark(passphrase);
ExtendedKey xpub = lark.getPubKeyAtPath(device.getType(), device.getPath(), derivationPath);
isPromptActive = false;
- return xpub.toString();
+ return xpub;
} catch(DeviceException e) {
throw new ImportException(e.getMessage(), e);
} catch(RuntimeException e) {
@@ -436,7 +436,7 @@ public class Hwi {
}
}
- public static class GetXpubService extends Service<String> {
+ public static class GetXpubService extends Service<ExtendedKey> {
private final Device device;
private final String passphrase;
private final String derivationPath;
@@ -448,9 +448,9 @@ public class Hwi {
}
@Override
- protected Task<String> createTask() {
+ protected Task<ExtendedKey> createTask() {
return new Task<>() {
- protected String call() throws ImportException {
+ protected ExtendedKey call() throws ImportException {
Hwi hwi = new Hwi();
return hwi.getXpub(device, passphrase, derivationPath);
}
@@ -480,7 +480,7 @@ public class Hwi {
}
}
- public static class GetXpubsService extends Service<Map<WalletType, String>> {
+ public static class GetXpubsService extends Service<Map<WalletType, ExtendedKey>> {
private final Device device;
private final String passphrase;
private final Map<WalletType, String> accountDerivationPaths;
@@ -492,13 +492,13 @@ public class Hwi {
}
@Override
- protected Task<Map<WalletType, String>> createTask() {
+ protected Task<Map<WalletType, ExtendedKey>> createTask() {
return new Task<>() {
- protected Map<WalletType, String> call() throws ImportException {
+ protected Map<WalletType, ExtendedKey> call() throws ImportException {
Hwi hwi = new Hwi();
updateProgress(0, accountDerivationPaths.size());
- ObservableMap<WalletType, String> accountXpubs = FXCollections.observableMap(new LinkedHashMap<>());
- accountXpubs.addListener((MapChangeListener<? super WalletType, ? super String>) _ -> updateProgress(accountXpubs.size(), accountDerivationPaths.size()));
+ ObservableMap<WalletType, ExtendedKey> accountXpubs = FXCollections.observableMap(new LinkedHashMap<>());
+ accountXpubs.addListener((MapChangeListener<? super WalletType, ? super ExtendedKey>) _ -> updateProgress(accountXpubs.size(), accountDerivationPaths.size()));
return hwi.getXpubs(device, passphrase, accountDerivationPaths, accountXpubs);
}
};
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.