diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/StandardAccount.java b/src/main/java/com/sparrowwallet/drongo/wallet/StandardAccount.java index 5c3be3d..238498c 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/StandardAccount.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/StandardAccount.java @@ -16,9 +16,9 @@ public enum StandardAccount { ACCOUNT_7("Account #7", new ChildNumber(7, true)), ACCOUNT_8("Account #8", new ChildNumber(8, true)), ACCOUNT_9("Account #9", new ChildNumber(9, true)), - WHIRLPOOL_PREMIX("Premix", new ChildNumber(2147483645, true), ScriptType.P2WPKH), - WHIRLPOOL_POSTMIX("Postmix", new ChildNumber(2147483646, true), ScriptType.P2WPKH), - WHIRLPOOL_BADBANK("Badbank", new ChildNumber(2147483644, true), ScriptType.P2WPKH); + WHIRLPOOL_PREMIX("Premix", new ChildNumber(2147483645, true), ScriptType.P2WPKH, null), + WHIRLPOOL_POSTMIX("Postmix", new ChildNumber(2147483646, true), ScriptType.P2WPKH, Wallet.DEFAULT_LOOKAHEAD * 2), + WHIRLPOOL_BADBANK("Badbank", new ChildNumber(2147483644, true), ScriptType.P2WPKH, null); public static final List WHIRLPOOL_ACCOUNTS = List.of(WHIRLPOOL_PREMIX, WHIRLPOOL_POSTMIX, WHIRLPOOL_BADBANK); public static final List WHIRLPOOL_MIX_ACCOUNTS = List.of(WHIRLPOOL_PREMIX, WHIRLPOOL_POSTMIX); @@ -27,17 +27,20 @@ public enum StandardAccount { this.name = name; this.childNumber = childNumber; this.requiredScriptType = null; + this.minimumGapLimit = null; } - StandardAccount(String name, ChildNumber childNumber, ScriptType requiredScriptType) { + StandardAccount(String name, ChildNumber childNumber, ScriptType requiredScriptType, Integer minimumGapLimit) { this.name = name; this.childNumber = childNumber; this.requiredScriptType = requiredScriptType; + this.minimumGapLimit = minimumGapLimit; } private final String name; private final ChildNumber childNumber; private final ScriptType requiredScriptType; + private final Integer minimumGapLimit; public String getName() { return name; @@ -55,6 +58,10 @@ public enum StandardAccount { return requiredScriptType; } + public Integer getMinimumGapLimit() { + return minimumGapLimit; + } + @Override public String toString() { return name; diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java index 8069392..b1c04f1 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java @@ -22,7 +22,7 @@ import java.util.stream.Collectors; import static com.sparrowwallet.drongo.protocol.ScriptType.*; import static com.sparrowwallet.drongo.protocol.Transaction.WITNESS_SCALE_FACTOR; -public class Wallet extends Persistable { +public class Wallet extends Persistable implements Comparable { public static final int DEFAULT_LOOKAHEAD = 20; public static final String ALLOW_DERIVATIONS_MATCHING_OTHER_SCRIPT_TYPES_PROPERTY = "com.sparrowwallet.allowDerivationsMatchingOtherScriptTypes"; @@ -89,11 +89,7 @@ public class Wallet extends Persistable { throw new IllegalStateException("Cannot add child wallet to existing child wallet"); } - if(!childWallet.containsPrivateKeys()) { - throw new IllegalStateException("Cannot derive child wallet xpub without private keys"); - } - - if(childWallet.isEncrypted()) { + if(childWallet.containsPrivateKeys() && childWallet.isEncrypted()) { throw new IllegalStateException("Cannot derive child wallet xpub from encrypted wallet"); } @@ -102,7 +98,7 @@ public class Wallet extends Persistable { childWallet.purposeNodes.clear(); childWallet.transactions.clear(); childWallet.storedBlockHeight = null; - childWallet.gapLimit = null; + childWallet.gapLimit = standardAccount.getMinimumGapLimit(); childWallet.birthDate = null; if(standardAccount.getRequiredScriptType() != null) { @@ -111,16 +107,25 @@ public class Wallet extends Persistable { for(Keystore keystore : childWallet.getKeystores()) { List derivation = standardAccount.getRequiredScriptType() != null ? standardAccount.getRequiredScriptType().getDefaultDerivation() : keystore.getKeyDerivation().getDerivation(); - List childDerivation = new ArrayList<>(derivation.subList(0, derivation.size() - 1)); - childDerivation.add(standardAccount.getChildNumber()); + List childDerivation; + if(childWallet.getScriptType().getAccount(KeyDerivation.writePath(derivation)) > -1) { + childDerivation = childWallet.getScriptType().getDefaultDerivation(standardAccount.getChildNumber().num()); + } else { + childDerivation = derivation.isEmpty() ? new ArrayList<>() : new ArrayList<>(derivation.subList(0, derivation.size() - 1)); + childDerivation.add(standardAccount.getChildNumber()); + } - try { - Keystore derivedKeystore = Keystore.fromSeed(keystore.getSeed(), childDerivation); - keystore.setKeyDerivation(derivedKeystore.getKeyDerivation()); - keystore.setExtendedPublicKey(derivedKeystore.getExtendedPublicKey()); - keystore.getSeed().setPassphrase(keystore.getSeed().getPassphrase()); - } catch(Exception e) { - throw new IllegalStateException("Cannot derive keystore for " + standardAccount + " account", e); + if(keystore.hasPrivateKey()) { + try { + Keystore derivedKeystore = keystore.hasSeed() ? Keystore.fromSeed(keystore.getSeed(), childDerivation) : Keystore.fromMasterPrivateExtendedKey(keystore.getMasterPrivateExtendedKey(), childDerivation); + keystore.setKeyDerivation(derivedKeystore.getKeyDerivation()); + keystore.setExtendedPublicKey(derivedKeystore.getExtendedPublicKey()); + } catch(Exception e) { + throw new IllegalStateException("Cannot derive keystore for " + standardAccount + " account", e); + } + } else { + keystore.setKeyDerivation(new KeyDerivation(null, KeyDerivation.writePath(childDerivation))); + keystore.setExtendedPublicKey(null); } } @@ -154,6 +159,11 @@ public class Wallet extends Persistable { int keystoreAccount = getScriptType().getAccount(keystore.getKeyDerivation().getDerivationPath()); if(keystoreAccount != -1 && (index == -1 || keystoreAccount == index)) { index = keystoreAccount; + } else if(!keystore.getKeyDerivation().getDerivation().isEmpty()) { + keystoreAccount = keystore.getKeyDerivation().getDerivation().get(keystore.getKeyDerivation().getDerivation().size() - 1).num(); + if(index == -1 || keystoreAccount == index) { + index = keystoreAccount; + } } } } @@ -175,7 +185,7 @@ public class Wallet extends Persistable { } public boolean isWhirlpoolChildWallet() { - return !isMasterWallet() && StandardAccount.WHIRLPOOL_ACCOUNTS.contains(getStandardAccountType()); + return !isMasterWallet() && getStandardAccountType() != null && StandardAccount.WHIRLPOOL_ACCOUNTS.contains(getStandardAccountType()); } public boolean isWhirlpoolMixWallet() { @@ -1458,6 +1468,15 @@ public class Wallet extends Persistable { } } + @Override + public int compareTo(Wallet other) { + if(getStandardAccountType() != null && other.getStandardAccountType() != null) { + return getStandardAccountType().ordinal() - other.getStandardAccountType().ordinal(); + } + + return getAccountIndex() - other.getAccountIndex(); + } + @Override public String toString() { return getFullName();