@Test
 public void deterministicUpgradeEncrypted() throws Exception {
   group = new KeyChainGroup(params);
   final ECKey key = new ECKey();
   group.importKeys(key);
   final KeyCrypterScrypt crypter = new KeyCrypterScrypt();
   final KeyParameter aesKey = crypter.deriveKey("abc");
   assertTrue(group.isDeterministicUpgradeRequired());
   group.encrypt(crypter, aesKey);
   assertTrue(group.isDeterministicUpgradeRequired());
   try {
     group.upgradeToDeterministic(0, null);
     fail();
   } catch (DeterministicUpgradeRequiresPassword e) {
     // Expected.
   }
   group.upgradeToDeterministic(0, aesKey);
   assertFalse(group.isDeterministicUpgradeRequired());
   final DeterministicSeed deterministicSeed = group.getActiveKeyChain().getSeed();
   assertNotNull(deterministicSeed);
   assertTrue(deterministicSeed.isEncrypted());
   byte[] entropy =
       checkNotNull(group.getActiveKeyChain().toDecrypted(aesKey).getSeed()).getEntropyBytes();
   // Check we used the right key: oldest non rotating.
   byte[] truncatedBytes = Arrays.copyOfRange(key.getSecretBytes(), 0, 16);
   assertArrayEquals(entropy, truncatedBytes);
 }
  @Test
  public void deterministicUpgradeUnencrypted() throws Exception {
    // Check that a group that contains only random keys has its HD chain created using the private
    // key bytes of
    // the oldest random key, so upgrading the same wallet twice gives the same outcome.
    group = new KeyChainGroup(params);
    group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests.
    ECKey key1 = new ECKey();
    Utils.rollMockClock(86400);
    ECKey key2 = new ECKey();
    group.importKeys(key2, key1);

    List<Protos.Key> protobufs = group.serializeToProtobuf();
    group.upgradeToDeterministic(0, null);
    assertFalse(group.isDeterministicUpgradeRequired());
    DeterministicKey dkey1 = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
    DeterministicSeed seed1 = group.getActiveKeyChain().getSeed();
    assertNotNull(seed1);

    group = KeyChainGroup.fromProtobufUnencrypted(params, protobufs);
    group.upgradeToDeterministic(0, null); // Should give same result as last time.
    DeterministicKey dkey2 = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
    DeterministicSeed seed2 = group.getActiveKeyChain().getSeed();
    assertEquals(seed1, seed2);
    assertEquals(dkey1, dkey2);

    // Check we used the right (oldest) key despite backwards import order.
    byte[] truncatedBytes = Arrays.copyOfRange(key1.getSecretBytes(), 0, 16);
    assertArrayEquals(seed1.getEntropyBytes(), truncatedBytes);
  }
 @Override
 public DeterministicKeyChain toDecrypted(KeyParameter aesKey) {
   checkState(getKeyCrypter() != null, "Key chain not encrypted");
   checkState(seed != null, "Can't decrypt a watching chain");
   checkState(seed.isEncrypted());
   String passphrase = DEFAULT_PASSPHRASE_FOR_MNEMONIC; // FIXME allow non-empty passphrase
   DeterministicSeed decSeed = seed.decrypt(getKeyCrypter(), passphrase, aesKey);
   DeterministicKeyChain chain = new DeterministicKeyChain(decSeed);
   // Now double check that the keys match to catch the case where the key is wrong but padding
   // didn't catch it.
   if (!chain.getWatchingKey().getPubKeyPoint().equals(getWatchingKey().getPubKeyPoint()))
     throw new KeyCrypterException("Provided AES key is wrong");
   chain.lookaheadSize = lookaheadSize;
   // Now copy the (pubkey only) leaf keys across to avoid rederiving them. The private key bytes
   // are missing
   // anyway so there's nothing to decrypt.
   for (ECKey eckey : basicKeyChain.getKeys()) {
     DeterministicKey key = (DeterministicKey) eckey;
     if (key.getPath().size() != 3) continue; // Not a leaf key.
     checkState(key.isEncrypted());
     DeterministicKey parent =
         chain.hierarchy.get(checkNotNull(key.getParent()).getPath(), false, false);
     // Clone the key to the new decrypted hierarchy.
     key = new DeterministicKey(key.getPubOnly(), parent);
     chain.hierarchy.putKey(key);
     chain.basicKeyChain.importKey(key);
   }
   chain.issuedExternalKeys = issuedExternalKeys;
   chain.issuedInternalKeys = issuedInternalKeys;
   return chain;
 }
 DeterministicKeyChain(DeterministicSeed seed, @Nullable KeyCrypter crypter) {
   this.seed = seed;
   basicKeyChain = new BasicKeyChain(crypter);
   if (!seed.isEncrypted()) {
     rootKey = HDKeyDerivation.createMasterPrivateKey(checkNotNull(seed.getSeedBytes()));
     rootKey.setCreationTimeSeconds(seed.getCreationTimeSeconds());
     initializeHierarchyUnencrypted(rootKey);
   }
   // Else...
   // We can't initialize ourselves with just an encrypted seed, so we expected deserialization
   // code to do the
   // rest of the setup (loading the root key).
 }
 @Test
 public void deterministicUpgradeRotating() throws Exception {
   group = new KeyChainGroup(params);
   group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests.
   long now = Utils.currentTimeSeconds();
   ECKey key1 = new ECKey();
   Utils.rollMockClock(86400);
   ECKey key2 = new ECKey();
   Utils.rollMockClock(86400);
   ECKey key3 = new ECKey();
   group.importKeys(key2, key1, key3);
   group.upgradeToDeterministic(now + 10, null);
   DeterministicSeed seed = group.getActiveKeyChain().getSeed();
   assertNotNull(seed);
   // Check we used the right key: oldest non rotating.
   byte[] truncatedBytes = Arrays.copyOfRange(key2.getSecretBytes(), 0, 16);
   assertArrayEquals(seed.getEntropyBytes(), truncatedBytes);
 }
 @Override
 public DeterministicKeyChain toEncrypted(CharSequence password) {
   checkNotNull(password);
   checkArgument(password.length() > 0);
   checkState(seed != null, "Attempt to encrypt a watching chain.");
   checkState(!seed.isEncrypted());
   KeyCrypter scrypt = new KeyCrypterScrypt();
   KeyParameter derivedKey = scrypt.deriveKey(password);
   return toEncrypted(scrypt, derivedKey);
 }
  /** Returns a list of words that represent the seed or null if this chain is a watching chain. */
  @Nullable
  public List<String> getMnemonicCode() {
    if (seed == null) return null;

    lock.lock();
    try {
      return seed.getMnemonicCode();
    } finally {
      lock.unlock();
    }
  }
 @Override
 public long getEarliestKeyCreationTime() {
   return seed != null ? seed.getCreationTimeSeconds() : creationTimeSeconds;
 }