private void initializeHierarchyUnencrypted(DeterministicKey baseKey) { rootKey = baseKey; addToBasicChain(rootKey); hierarchy = new DeterministicHierarchy(rootKey); externalKey = hierarchy.get(EXTERNAL_PATH, true, true); internalKey = hierarchy.get(INTERNAL_PATH, true, true); addToBasicChain(externalKey); addToBasicChain(internalKey); }
/** * Extends the tree by calculating the next key that hangs off the given parent path. For example, if you pass a * path of 1/2 here and there are already keys 1/2/1 and 1/2/2 then it will derive 1/2/3. * * @param parentPath the path to the parent * @param relative whether the path is relative to the root path * @param createParent whether the parent corresponding to path should be created (with any necessary ancestors) if it doesn't exist already * @param privateDerivation whether to use private or public derivation * @return next newly created key using the child derivation funtcion * @throws IllegalArgumentException if the parent doesn't exist and createParent is false. */ public DeterministicKey deriveNextChild(ImmutableList<ChildNumber> parentPath, boolean relative, boolean createParent, boolean privateDerivation) { DeterministicKey parent = get(parentPath, relative, createParent); int nAttempts = 0; while (nAttempts++ < HDKeyDerivation.MAX_CHILD_DERIVATION_ATTEMPTS) { try { ChildNumber createChildNumber = getNextChildNumberToDerive(parent.getPath(), privateDerivation); return deriveChild(parent, createChildNumber); } catch (HDDerivationException ignore) { } } throw new HDDerivationException("Maximum number of child derivation attempts reached, this is probably an indication of a bug."); }
private DeterministicKey encryptNonLeaf(KeyParameter aesKey, SimpleHDKeyChain chain, DeterministicKey parent, ImmutableList<ChildNumber> path) { DeterministicKey key = chain.hierarchy.get(path, true, false); key = key.encrypt(checkNotNull(simpleKeyChain.getKeyCrypter(), "Chain has null KeyCrypter"), aesKey, parent); hierarchy.putKey(key); simpleKeyChain.importKey(key); return key; }
/** * Extends the tree by calculating the requested child for the given path. For example, to get the key at position * 1/2/3 you would pass 1/2 as the parent path and 3 as the child number. * * @param parentPath the path to the parent * @param relative whether the path is relative to the root path * @param createParent whether the parent corresponding to path should be created (with any necessary ancestors) if it doesn't exist already * @return the requested key. * @throws IllegalArgumentException if the parent doesn't exist and createParent is false. */ public DeterministicKey deriveChild(List<ChildNumber> parentPath, boolean relative, boolean createParent, ChildNumber createChildNumber) { return deriveChild(get(parentPath, relative, createParent), createChildNumber); }
private SimpleHDKeyChain(KeyCrypter crypter, KeyParameter aesKey, SimpleHDKeyChain chain) { checkArgument(!chain.rootKey.isEncrypted(), "Chain already encrypted"); this.issuedExternalKeys = chain.issuedExternalKeys; this.issuedInternalKeys = chain.issuedInternalKeys; this.lookaheadSize = chain.lookaheadSize; this.lookaheadThreshold = chain.lookaheadThreshold; simpleKeyChain = new SimpleKeyChain(crypter); // The first number is the "account number" but we don't use that feature. rootKey = chain.rootKey.encrypt(crypter, aesKey, null); hierarchy = new DeterministicHierarchy(rootKey); simpleKeyChain.importKey(rootKey); externalKey = encryptNonLeaf(aesKey, chain, rootKey, EXTERNAL_PATH); internalKey = encryptNonLeaf(aesKey, chain, rootKey, INTERNAL_PATH); // Now copy the (pubkey only) leaf keys across to avoid rederiving them. The private key bytes are missing // anyway so there's nothing to encrypt. for (ECKey eckey : chain.simpleKeyChain.getKeys()) { DeterministicKey key = (DeterministicKey) eckey; if (!isLeaf(key)) continue; // Not a leaf key. DeterministicKey parent = hierarchy.get(checkNotNull(key.getParent(), "Key has no parent").getPath(), false, false); // Clone the key to the new encrypted hierarchy. key = new DeterministicKey(key.getPubOnly(), parent); hierarchy.putKey(key); simpleKeyChain.importKey(key); } }
/** * Returns the root key that the {@link DeterministicHierarchy} was created with. */ public DeterministicKey getRootKey() { return get(rootPath, false, false); } }
private WalletFile createWalletFile(List<String> words) throws MnemonicException.MnemonicLengthException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException, CipherException { byte[] seeds = MnemonicCode.INSTANCE.toEntropy(words); DeterministicKey masterPrivateKey = HDKeyDerivation.createMasterPrivateKey(seeds); DeterministicHierarchy deterministicHierarchy = new DeterministicHierarchy(masterPrivateKey); DeterministicKey child = deterministicHierarchy.deriveChild(DeterministicKeyChain.BIP44_ACCOUNT_ZERO_PATH, true, true, ChildNumber.ZERO); ECKeyPair ecKeyPair = ECKeyPair.create(child.getPrivKeyBytes()); return Wallet.createLight(PASSWORD, ecKeyPair); }
/** * Pre-generate enough keys to reach the lookahead size, but only if there are more than the lookaheadThreshold to * be generated, so that the Bloom filter does not have to be regenerated that often. * * The returned mutable list of keys must be inserted into the basic key chain. */ private List<DeterministicKey> maybeLookAhead(DeterministicKey parent, int issued, int lookaheadSize, int lookaheadThreshold) { checkState(lock.isHeldByCurrentThread(), "Lock is held by another thread"); final int numChildren = hierarchy.getNumChildren(parent.getPath()); final int needed = issued + lookaheadSize + lookaheadThreshold - numChildren; if (needed <= lookaheadThreshold) return new ArrayList<DeterministicKey>(); log.info("{} keys needed for {} = {} issued + {} lookahead size + {} lookahead threshold - {} num children", needed, parent.getPathAsString(), issued, lookaheadSize, lookaheadThreshold, numChildren); List<DeterministicKey> result = new ArrayList<DeterministicKey>(needed); long now = System.currentTimeMillis(); int nextChild = numChildren; for (int i = 0; i < needed; i++) { DeterministicKey key = HDKeyDerivation.deriveThisOrNextChildKey(parent, nextChild); key = key.getPubOnly(); hierarchy.putKey(key); result.add(key); nextChild = key.getChildNumber().num() + 1; } log.info("Took {} msec", System.currentTimeMillis() - now); return result; }
private void initializeHierarchyUnencrypted(DeterministicKey baseKey) { externalParentKey = hierarchy.deriveChild(getAccountPath(), false, false, ChildNumber.ZERO); internalParentKey = hierarchy.deriveChild(getAccountPath(), false, false, ChildNumber.ONE); addToBasicChain(externalParentKey); addToBasicChain(internalParentKey); }
/** * Creates a deterministic key chain that watches the given (public only) root key. You can use this to calculate * balances and generally follow along, but spending is not possible with such a chain. Currently you can't use * this method to watch an arbitrary fragment of some other tree, this limitation may be removed in future. */ public DeterministicKeyChain(DeterministicKey watchingKey) { checkArgument(watchingKey.isPubKeyOnly(), "Private subtrees not currently supported: if you got this key from DKC.getWatchingKey() then use .dropPrivate().dropParent() on it first."); checkArgument(watchingKey.getPath().size() == getAccountPath().size(), "You can only watch an account key currently"); basicKeyChain = new BasicKeyChain(); this.seed = null; rootKey = null; addToBasicChain(watchingKey); hierarchy = new DeterministicHierarchy(watchingKey); initializeHierarchyUnencrypted(watchingKey); }
private DeterministicKey deriveChild(DeterministicKey parent, ChildNumber createChildNumber) { DeterministicKey childKey = HDKeyDerivation.deriveChildKey(parent, createChildNumber); putKey(childKey); return childKey; }
private SimpleHDKeyChain(KeyCrypter crypter, KeyParameter aesKey, SimpleHDKeyChain chain) { checkArgument(!chain.rootKey.isEncrypted(), "Chain already encrypted"); this.issuedExternalKeys = chain.issuedExternalKeys; this.issuedInternalKeys = chain.issuedInternalKeys; this.lookaheadSize = chain.lookaheadSize; this.lookaheadThreshold = chain.lookaheadThreshold; simpleKeyChain = new SimpleKeyChain(crypter); // The first number is the "account number" but we don't use that feature. rootKey = chain.rootKey.encrypt(crypter, aesKey, null); hierarchy = new DeterministicHierarchy(rootKey); simpleKeyChain.importKey(rootKey); externalKey = encryptNonLeaf(aesKey, chain, rootKey, EXTERNAL_PATH); internalKey = encryptNonLeaf(aesKey, chain, rootKey, INTERNAL_PATH); // Now copy the (pubkey only) leaf keys across to avoid rederiving them. The private key bytes are missing // anyway so there's nothing to encrypt. for (ECKey eckey : chain.simpleKeyChain.getKeys()) { DeterministicKey key = (DeterministicKey) eckey; if (!isLeaf(key)) continue; // Not a leaf key. DeterministicKey parent = hierarchy.get(checkNotNull(key.getParent(), "Key has no parent").getPath(), false, false); // Clone the key to the new encrypted hierarchy. key = new DeterministicKey(key.getPubOnly(), parent); hierarchy.putKey(key); simpleKeyChain.importKey(key); } }
/** * Extends the tree by calculating the requested child for the given path. For example, to get the key at position * 1/2/3 you would pass 1/2 as the parent path and 3 as the child number. * * @param parentPath the path to the parent * @param relative whether the path is relative to the root path * @param createParent whether the parent corresponding to path should be created (with any necessary ancestors) if it doesn't exist already * @return the requested key. * @throws IllegalArgumentException if the parent doesn't exist and createParent is false. */ public DeterministicKey deriveChild(List<ChildNumber> parentPath, boolean relative, boolean createParent, ChildNumber createChildNumber) { return deriveChild(get(parentPath, relative, createParent), createChildNumber); }
/** * Returns the root key that the {@link DeterministicHierarchy} was created with. */ public DeterministicKey getRootKey() { return get(rootPath, false, false); } }
private void testVector(int testCase) { log.info("======= Test vector {}", testCase); HDWTestVector tv = tvs[testCase]; NetworkParameters params = MainNetParams.get(); DeterministicKey masterPrivateKey = HDKeyDerivation.createMasterPrivateKey(HEX.decode(tv.seed)); assertEquals(testEncode(tv.priv), testEncode(masterPrivateKey.serializePrivB58(params))); assertEquals(testEncode(tv.pub), testEncode(masterPrivateKey.serializePubB58(params))); DeterministicHierarchy dh = new DeterministicHierarchy(masterPrivateKey); for (int i = 0; i < tv.derived.size(); i++) { HDWTestVector.DerivedTestCase tc = tv.derived.get(i); log.info("{}", tc.name); assertEquals(tc.name, String.format(Locale.US, "Test%d %s", testCase + 1, tc.getPathDescription())); int depth = tc.path.length - 1; DeterministicKey ehkey = dh.deriveChild(Arrays.asList(tc.path).subList(0, depth), false, true, tc.path[depth]); assertEquals(testEncode(tc.priv), testEncode(ehkey.serializePrivB58(params))); assertEquals(testEncode(tc.pub), testEncode(ehkey.serializePubB58(params))); } }
/** * Pre-generate enough keys to reach the lookahead size, but only if there are more than the lookaheadThreshold to * be generated, so that the Bloom filter does not have to be regenerated that often. * * The returned mutable list of keys must be inserted into the basic key chain. */ private List<DeterministicKey> maybeLookAhead(DeterministicKey parent, int issued, int lookaheadSize, int lookaheadThreshold) { checkState(lock.isHeldByCurrentThread()); final int numChildren = hierarchy.getNumChildren(parent.getPath()); final int needed = issued + lookaheadSize + lookaheadThreshold - numChildren; if (needed <= lookaheadThreshold) return new ArrayList<>(); log.info("{} keys needed for {} = {} issued + {} lookahead size + {} lookahead threshold - {} num children", needed, parent.getPathAsString(), issued, lookaheadSize, lookaheadThreshold, numChildren); List<DeterministicKey> result = new ArrayList<>(needed); final Stopwatch watch = Stopwatch.createStarted(); int nextChild = numChildren; for (int i = 0; i < needed; i++) { DeterministicKey key = HDKeyDerivation.deriveThisOrNextChildKey(parent, nextChild); key = key.dropPrivateBytes(); hierarchy.putKey(key); result.add(key); nextChild = key.getChildNumber().num() + 1; } watch.stop(); log.info("Took {}", watch); return result; }
private void initializeHierarchyUnencrypted(DeterministicKey baseKey) { externalParentKey = hierarchy.deriveChild(getAccountPath(), false, false, ChildNumber.ZERO); internalParentKey = hierarchy.deriveChild(getAccountPath(), false, false, ChildNumber.ONE); basicKeyChain.importKey(externalParentKey); basicKeyChain.importKey(internalParentKey); }
/** * Creates a deterministic key chain that watches the given (public only) root key. You can use this to calculate * balances and generally follow along, but spending is not possible with such a chain. Currently you can't use * this method to watch an arbitrary fragment of some other tree, this limitation may be removed in future. */ public DeterministicKeyChain(DeterministicKey watchingKey) { checkArgument(watchingKey.isPubKeyOnly(), "Private subtrees not currently supported: if you got this key from DKC.getWatchingKey() then use .dropPrivate().dropParent() on it first."); checkArgument(watchingKey.getPath().size() == getAccountPath().size(), "You can only watch an account key currently"); basicKeyChain = new BasicKeyChain(); this.seed = null; this.rootKey = null; basicKeyChain.importKey(watchingKey); hierarchy = new DeterministicHierarchy(watchingKey); initializeHierarchyUnencrypted(watchingKey); }
/** * Constructs a new hierarchy rooted at the given key. Note that this does not have to be the top of the tree. * You can construct a DeterministicHierarchy for a subtree of a larger tree that you may not own. */ public DeterministicHierarchy(DeterministicKey rootKey) { putKey(rootKey); rootPath = rootKey.getPath(); }
private void initializeHierarchyUnencrypted(DeterministicKey baseKey) { rootKey = baseKey; addToBasicChain(rootKey); hierarchy = new DeterministicHierarchy(rootKey); externalKey = hierarchy.get(EXTERNAL_PATH, true, true); internalKey = hierarchy.get(INTERNAL_PATH, true, true); addToBasicChain(externalKey); addToBasicChain(internalKey); }