Skip to content

Commit

Permalink
Add some Javadocs and clean up the watching key API a tiny bit. Defau…
Browse files Browse the repository at this point in the history
…lt creation time is now BIP32 standardisation time.
  • Loading branch information
mikehearn committed May 29, 2014
1 parent 9ff7a91 commit 42bfbb9
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 9 deletions.
3 changes: 2 additions & 1 deletion HD Wallets TODO.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
- Switch to the tree format agreed on with slush/stick
- Switch to the tree format agreed on with slush/stick.
- Store the account key creation time for an HD hierarchy.
- Support seeds up to 512 bits in size. Test compatibility with greenaddress, at least for some keys.
- Make Wallet notify key chains when a key has been observed in a transaction.
- Make DeterministicKeyChain auto-extend when a key was observed, to keep a gap limit in place.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public class DeterministicHierarchy implements Serializable {
// Keep track of how many child keys each node has. This is kind of weak.
private final Map<ImmutableList<ChildNumber>, ChildNumber> lastChildNumbers = Maps.newHashMap();

public static final int BIP32_STANDARDISATION_TIME_SECS = 1369267200;

/**
* 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
private DeterministicHierarchy hierarchy;
private DeterministicKey rootKey;
private DeterministicSeed seed;

// Ignored if seed != null. Useful for watching hierarchies.
private long creationTimeSeconds = MnemonicCode.BIP39_STANDARDISATION_TIME_SECS;

// Paths through the key tree. External keys are ones that are communicated to other parties. Internal keys are
Expand Down Expand Up @@ -133,7 +135,11 @@ public DeterministicKeyChain(byte[] seed, long seedCreationTimeSecs) {
this(new DeterministicSeed(seed, seedCreationTimeSecs));
}

public DeterministicKeyChain(DeterministicSeed seed) {
/**
* Creates a deterministic key chain starting from the given seed. All keys yielded by this chain will be the same
* if the starting seed is the same.
*/
protected DeterministicKeyChain(DeterministicSeed seed) {
this(seed, null);
}

Expand All @@ -155,10 +161,19 @@ public DeterministicKeyChain(DeterministicKey watchingKey) {
this(watchingKey, Utils.currentTimeSeconds());
}

/**
* Creates a key chain that watches the given account key. The creation time is taken to be the time that BIP 32
* was standardised: most likely, you can optimise by selecting a more accurate creation time for your key and
* using the other watch method.
*/
public static DeterministicKeyChain watch(DeterministicKey accountKey) {
return new DeterministicKeyChain(accountKey);
return watch(accountKey, DeterministicHierarchy.BIP32_STANDARDISATION_TIME_SECS);
}

/**
* Creates a key chain that watches the given account key, and assumes there are no transactions involving it until
* the given time (this is an optimisation for chain scanning purposes).
*/
public static DeterministicKeyChain watch(DeterministicKey accountKey, long seedCreationTimeSecs) {
return new DeterministicKeyChain(accountKey, seedCreationTimeSecs);
}
Expand Down
11 changes: 8 additions & 3 deletions core/src/main/java/com/google/bitcoin/wallet/KeyChainGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,16 @@ public KeyChainGroup(DeterministicSeed seed) {
* This HAS to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
*/
public KeyChainGroup(DeterministicKey watchKey) {
this(null, ImmutableList.of(new DeterministicKeyChain(watchKey)), null);
this(null, ImmutableList.of(DeterministicKeyChain.watch(watchKey)), null);
}

public KeyChainGroup(DeterministicKey watchKey, long creationTimeSeconds) {
this(null, ImmutableList.of(new DeterministicKeyChain(watchKey, creationTimeSeconds)), null);
/**
* Creates a keychain group with no basic chain, and an HD chain that is watching the given watching key which
* was assumed to be first used at the given UNIX time.
* This HAS to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
*/
public KeyChainGroup(DeterministicKey watchKey, long creationTimeSecondsSecs) {
this(null, ImmutableList.of(DeterministicKeyChain.watch(watchKey, creationTimeSecondsSecs)), null);
}

// Used for deserialization.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.google.bitcoin.wallet;

import com.google.bitcoin.core.*;
import com.google.bitcoin.crypto.DeterministicHierarchy;
import com.google.bitcoin.crypto.DeterministicKey;
import com.google.bitcoin.params.UnitTestParams;
import com.google.bitcoin.store.UnreadableWalletException;
Expand Down Expand Up @@ -228,8 +229,8 @@ public void watchingChain() throws UnreadableWalletException {
final String pub58 = watchingKey.serializePubB58();
assertEquals("xpub68KFnj3bqUx1s7mHejLDBPywCAKdJEu1b49uniEEn2WSbHmZ7xbLqFTjJbtx1LUcAt1DwhoqWHmo2s5WMJp6wi38CiF2hYD49qVViKVvAoi", pub58);
watchingKey = DeterministicKey.deserializeB58(null, pub58);
chain = new DeterministicKeyChain(watchingKey);
assertEquals(Utils.currentTimeSeconds(), chain.getEarliestKeyCreationTime());
chain = DeterministicKeyChain.watch(watchingKey);
assertEquals(DeterministicHierarchy.BIP32_STANDARDISATION_TIME_SECS, chain.getEarliestKeyCreationTime());
chain.setLookaheadSize(10);

assertEquals(key1.getPubKeyPoint(), chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).getPubKeyPoint());
Expand All @@ -254,7 +255,7 @@ public void watchingChain() throws UnreadableWalletException {
@Test(expected = IllegalStateException.class)
public void watchingCannotEncrypt() throws Exception {
final DeterministicKey accountKey = chain.getKeyByPath(DeterministicKeyChain.ACCOUNT_ZERO_PATH);
chain = new DeterministicKeyChain(accountKey.getPubOnly());
chain = DeterministicKeyChain.watch(accountKey.getPubOnly());
chain = chain.toEncrypted("this doesn't make any sense");
}

Expand Down

0 comments on commit 42bfbb9

Please sign in to comment.