diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java b/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java index f4ccad0..ce388c8 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java @@ -168,7 +168,7 @@ public class Keystore { DeterministicKey derivedKeyPublicOnly = derivedKey.dropPrivateBytes().dropParent(); ExtendedKey xpub = new ExtendedKey(derivedKeyPublicOnly, derivedKey.getParentFingerprint(), derivation.get(derivation.size() - 1)); - keystore.setLabel(masterFingerprint); + keystore.setLabel(seed.getType().name()); keystore.setSource(KeystoreSource.SW_SEED); keystore.setWalletModel(WalletModel.SPARROW); keystore.setKeyDerivation(new KeyDerivation(masterFingerprint, KeyDerivation.writePath(derivation))); diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java index feb0709..5e768a7 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java @@ -126,6 +126,30 @@ public class Wallet { return !keystores.stream().map(Keystore::getLabel).allMatch(new HashSet<>()::add); } + public int makeLabelsUnique(Keystore newKeystore) { + int max = 0; + for(Keystore keystore : getKeystores()) { + if(newKeystore != keystore && keystore.getLabel().startsWith(newKeystore.getLabel())) { + String remainder = keystore.getLabel().substring(newKeystore.getLabel().length()); + if(remainder.length() == 0) { + max = makeLabelsUnique(keystore); + } else { + try { + int count = Integer.parseInt(remainder.trim()); + max = Math.max(max, count); + } catch (NumberFormatException e) { + //ignore, no terminating number + } + } + } + } + + max++; + newKeystore.setLabel(newKeystore.getLabel() + " " + max); + + return max; + } + public Wallet copy() { Wallet copy = new Wallet(name); copy.setPolicyType(policyType); diff --git a/src/test/java/com/sparrowwallet/drongo/wallet/Bip39MnemonicCodeTest.java b/src/test/java/com/sparrowwallet/drongo/wallet/Bip39MnemonicCodeTest.java index 3a28321..8a29b4a 100644 --- a/src/test/java/com/sparrowwallet/drongo/wallet/Bip39MnemonicCodeTest.java +++ b/src/test/java/com/sparrowwallet/drongo/wallet/Bip39MnemonicCodeTest.java @@ -93,4 +93,26 @@ public class Bip39MnemonicCodeTest { Assert.assertEquals("a652d123f421f56257391af26063e900619678b552dafd3850e699f6da0667269bbcaebb0509557481db29607caac0294b3cd337d740174cfa05f552fe9e0272", Utils.bytesToHex(seed)); } -} + + @Test + public void bip39TestVector1() throws MnemonicException { + String words = "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always"; + List wordlist = Arrays.asList(words.split(" ")); + + Bip39MnemonicCode.INSTANCE.check(wordlist); + byte[] seed = Bip39MnemonicCode.toSeed(wordlist, "TREZOR"); + + Assert.assertEquals("107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913ffb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65", Utils.bytesToHex(seed)); + } + + @Test + public void bip39TestVector2() throws MnemonicException { + String words = "gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog"; + List wordlist = Arrays.asList(words.split(" ")); + + Bip39MnemonicCode.INSTANCE.check(wordlist); + byte[] seed = Bip39MnemonicCode.toSeed(wordlist, "TREZOR"); + + Assert.assertEquals("628c3827a8823298ee685db84f55caa34b5cc195a778e52d45f59bcf75aba68e4d7590e101dc414bc1bbd5737666fbbef35d1f1903953b66624f910feef245ac", Utils.bytesToHex(seed)); + } +} \ No newline at end of file diff --git a/src/test/java/com/sparrowwallet/drongo/wallet/DeterministicSeedTest.java b/src/test/java/com/sparrowwallet/drongo/wallet/DeterministicSeedTest.java index b83d395..3c3f0bf 100644 --- a/src/test/java/com/sparrowwallet/drongo/wallet/DeterministicSeedTest.java +++ b/src/test/java/com/sparrowwallet/drongo/wallet/DeterministicSeedTest.java @@ -1,5 +1,6 @@ package com.sparrowwallet.drongo.wallet; +import com.sparrowwallet.drongo.KeyDerivation; import com.sparrowwallet.drongo.crypto.KeyDeriver; import org.junit.Assert; import org.junit.Test; @@ -16,4 +17,22 @@ public class DeterministicSeedTest { DeterministicSeed decryptedSeed = encryptedSeed.decrypt("pass"); Assert.assertEquals(words, decryptedSeed.getMnemonicString()); } + + @Test + public void testBip39Vector1() throws MnemonicException { + String words = "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always"; + + DeterministicSeed seed = new DeterministicSeed(words, "TREZOR", 0, DeterministicSeed.Type.BIP39); + Keystore keystore = Keystore.fromSeed(seed, KeyDerivation.parsePath("m/0'")); + Assert.assertEquals("xprv9s21ZrQH143K3VPCbxbUtpkh9pRG371UCLDz3BjceqP1jz7XZsQ5EnNkYAEkfeZp62cDNj13ZTEVG1TEro9sZ9grfRmcYWLBhCocViKEJae", keystore.getExtendedMasterPrivateKey().toString()); + } + + @Test + public void testBip39Vector2() throws MnemonicException { + String words = "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside"; + + DeterministicSeed seed = new DeterministicSeed(words, "TREZOR", 0, DeterministicSeed.Type.BIP39); + Keystore keystore = Keystore.fromSeed(seed, KeyDerivation.parsePath("m/0'")); + Assert.assertEquals("xprv9s21ZrQH143K2WNnKmssvZYM96VAr47iHUQUTUyUXH3sAGNjhJANddnhw3i3y3pBbRAVk5M5qUGFr4rHbEWwXgX4qrvrceifCYQJbbFDems", keystore.getExtendedMasterPrivateKey().toString()); + } } diff --git a/src/test/java/com/sparrowwallet/drongo/wallet/WalletTest.java b/src/test/java/com/sparrowwallet/drongo/wallet/WalletTest.java index 2379f0f..ba8e028 100644 --- a/src/test/java/com/sparrowwallet/drongo/wallet/WalletTest.java +++ b/src/test/java/com/sparrowwallet/drongo/wallet/WalletTest.java @@ -6,6 +6,7 @@ import com.sparrowwallet.drongo.crypto.KeyDeriver; import com.sparrowwallet.drongo.policy.Policy; import com.sparrowwallet.drongo.policy.PolicyType; import com.sparrowwallet.drongo.protocol.ScriptType; +import org.junit.Assert; import org.junit.Test; public class WalletTest { @@ -26,4 +27,33 @@ public class WalletTest { wallet.decrypt("pass"); } + + @Test + public void makeLabelsUnique() { + Wallet wallet = new Wallet(); + Keystore keystore1 = new Keystore("BIP39"); + wallet.getKeystores().add(keystore1); + + Keystore keystore2 = new Keystore("BIP39 2"); + wallet.getKeystores().add(keystore2); + + Keystore keystore3 = new Keystore("Coldcard"); + wallet.getKeystores().add(keystore3); + + Keystore keystore4 = new Keystore("Coldcard2"); + wallet.getKeystores().add(keystore4); + + Keystore keystore5 = new Keystore("Coldcard -1"); + wallet.getKeystores().add(keystore5); + + Keystore keystore = new Keystore("BIP39"); + wallet.makeLabelsUnique(keystore); + Assert.assertEquals("BIP39 3", keystore1.getLabel()); + Assert.assertEquals("BIP39 4", keystore.getLabel()); + + Keystore cckeystore = new Keystore("Coldcard"); + wallet.makeLabelsUnique(cckeystore); + Assert.assertEquals("Coldcard 3", keystore3.getLabel()); + Assert.assertEquals("Coldcard 4", cckeystore.getLabel()); + } }