private void addToBasicChain(DeterministicKey key) { simpleKeyChain.importKeys(ImmutableList.of(key)); }
/** * Pre-generate enough keys to reach the lookahead size. You can call this if you need to explicitly invoke * the lookahead procedure, but it's normally unnecessary as it will be done automatically when needed. */ public void maybeLookAhead() { lock.lock(); try { List<DeterministicKey> keys = maybeLookAhead(externalKey, issuedExternalKeys); keys.addAll(maybeLookAhead(internalKey, issuedInternalKeys)); // Batch add all keys at once so there's only one event listener invocation, as this will be listened to // by the wallet and used to rebuild/broadcast the Bloom filter. That's expensive so we don't want to do // it more often than necessary. simpleKeyChain.importKeys(keys); } finally { lock.unlock(); } }
private List<DeterministicKey> getDeterministicKeys(int numberOfKeys, DeterministicKey parentKey, int index) { lock.lock(); try { // Optimization: potentially do a very quick key generation for just the number of keys we need if we // didn't already create them, ignoring the configured lookahead size. This ensures we'll be able to // retrieve the keys in the following loop, but if we're totally fresh and didn't get a chance to // calculate the lookahead keys yet, this will not block waiting to calculate 100+ EC point multiplies. // On slow/crappy Android phones looking ahead 100 keys can take ~5 seconds but the OS will kill us // if we block for just one second on the UI thread. Because UI threads may need an address in order // to render the screen, we need getKeys to be fast even if the wallet is totally brand new and lookahead // didn't happen yet. // // It's safe to do this because when a network thread tries to calculate a Bloom filter, we'll go ahead // and calculate the full lookahead zone there, so network requests will always use the right amount. List<DeterministicKey> lookahead = maybeLookAhead(parentKey, index, 0, 0); simpleKeyChain.importKeys(lookahead); List<DeterministicKey> keys = new ArrayList<DeterministicKey>(numberOfKeys); for (int i = 0; i < numberOfKeys; i++) { ImmutableList<ChildNumber> path = HDUtils.append(parentKey.getPath(), new ChildNumber(index - numberOfKeys + i, false)); keys.add(hierarchy.get(path, false, false)); } return keys; } finally { lock.unlock(); } }
@Override public SimpleHDKeyChain toDecrypted(KeyParameter aesKey) { checkState(getKeyCrypter() != null, "Key chain not encrypted"); checkState(rootKey.isEncrypted(), "Root key not encrypted"); DeterministicKey decKey = rootKey.decrypt(getKeyCrypter(), aesKey); SimpleHDKeyChain chain = new SimpleHDKeyChain(decKey); // 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 : simpleKeyChain.getKeys()) { DeterministicKey key = (DeterministicKey) eckey; if (!isLeaf(key)) continue; // Not a leaf key. checkState(key.isEncrypted(), "Key is not encrypted"); DeterministicKey parent = chain.hierarchy.get(checkNotNull(key.getParent(), "Key has null parent").getPath(), false, false); // Clone the key to the new decrypted hierarchy. key = new DeterministicKey(key.getPubOnly(), parent); chain.hierarchy.putKey(key); chain.simpleKeyChain.importKeys(key); } chain.issuedExternalKeys = issuedExternalKeys; chain.issuedInternalKeys = issuedInternalKeys; return chain; }