3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-31 15:22:16 +00:00

Add more javadocs.

Delete the wallet integration code - it's good, but we won't ship it in 0.10 as it's not finished.
This commit is contained in:
Mike Hearn 2013-07-11 13:10:07 +02:00
parent 0385ab34e5
commit 50dd5af0c8
7 changed files with 81 additions and 168 deletions

View File

@ -17,10 +17,9 @@
package com.google.bitcoin.crypto.hd;
/**
* This is just a wrapper for the i (child number) as per BIP 32 with a boolean getter for the first bit and a getter
* for the actual 0-based child number.
*
* This class is immutable.
* <p>This is just a wrapper for the i (child number) as per BIP 32 with a boolean getter for the first bit and a getter
* for the actual 0-based child number. A {@link List} of these forms a <i>path</i> through a
* {@link DeterministicHierarchy}. This class is immutable.
*/
public class ChildNumber {
public static final int PRIV_BIT = 0x80000000;

View File

@ -16,7 +16,6 @@
package com.google.bitcoin.crypto.hd;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
@ -24,9 +23,19 @@ import java.io.Serializable;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Preconditions.checkArgument;
/**
* A DeterministicHierarchy calculates and keeps a whole tree (hierarchy) of keys originating from a single
* root key.
* <p>A DeterministicHierarchy calculates and keeps a whole tree (hierarchy) of keys originating from a single
* root key. This implements part of the BIP 32 specification. A deterministic key tree is useful because
* Bitcoin's privacy system require new keys to be created for each transaction, but managing all these
* keys quickly becomes unwieldy. In particular it becomes hard to back up and distribute them. By having
* a way to derive random-looking but deterministic keys we can make wallet backup simpler and gain the
* ability to hand out {@link ExtendedHierarchicKey}s to other people who can then create new addresses
* on the fly, without having to contact us.</p>
*
* <p>The hierarchy is started from a single root key, and a location in the tree is given by a path which
* is a list of {@link ChildNumber}s.</p>
*/
public class DeterministicHierarchy implements Serializable {
/**
@ -40,6 +49,10 @@ public class DeterministicHierarchy implements Serializable {
private final Map<ImmutableList<ChildNumber>, ChildNumber> lastPrivDerivedNumbers = Maps.newHashMap();
private final Map<ImmutableList<ChildNumber>, ChildNumber> lastPubDerivedNumbers = Maps.newHashMap();
/**
* 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(ExtendedHierarchicKey rootKey) {
putKey(rootKey);
rootPath = rootKey.getChildNumberPath();
@ -50,33 +63,40 @@ public class DeterministicHierarchy implements Serializable {
}
/**
* Returns a key for the given path, optionally creating it.
*
* @param path the path to the key
* @param relativePath whether the path is relative to the root path
* @param create whether the key corresponding to path should be created (with any necessary ancestors) if it doesn't exist already
* @return next newly created key using the child derivation funtcion
* @return next newly created key using the child derivation function
* @throws IllegalArgumentException if create is false and the path was not found.
*/
public ExtendedHierarchicKey get(List<ChildNumber> path, boolean relativePath, boolean create) {
ImmutableList<ChildNumber> absolutePath = relativePath
? ImmutableList.<ChildNumber>builder().addAll(rootPath).addAll(path).build()
: ImmutableList.copyOf(path);
if (!keys.containsKey(absolutePath)) {
Preconditions.checkArgument(create, "No key found for {} path {}.", relativePath ? "relative" : "absolute", path);
Preconditions.checkArgument(absolutePath.size() > 0, "Can't derive the master key: nothing to derive from.");
ExtendedHierarchicKey parent = get(absolutePath.subList(0, absolutePath.size() - 1), relativePath, create);
checkArgument(create, "No key found for {} path {}.", relativePath ? "relative" : "absolute", path);
checkArgument(absolutePath.size() > 0, "Can't derive the master key: nothing to derive from.");
ExtendedHierarchicKey parent = get(absolutePath.subList(0, absolutePath.size() - 1), relativePath, true);
putKey(HDKeyDerivation.deriveChildKey(parent, absolutePath.get(absolutePath.size() - 1)));
}
return keys.get(absolutePath);
}
/**
* 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 relativePath whether the path is relative to the root path
* @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 ExtendedHierarchicKey deriveNextChild(ImmutableList<ChildNumber> parentPath, boolean relativePath, boolean createParent, boolean privateDerivation) {
ExtendedHierarchicKey parent = get(parentPath, relativePath, createParent);
public ExtendedHierarchicKey deriveNextChild(ImmutableList<ChildNumber> parentPath, boolean relative, boolean createParent, boolean privateDerivation) {
ExtendedHierarchicKey parent = get(parentPath, relative, createParent);
int nAttempts = 0;
while (nAttempts++ < MAX_CHILD_DERIVATION_ATTEMPTS) {
try {
@ -95,8 +115,18 @@ public class DeterministicHierarchy implements Serializable {
return nextChildNumber;
}
public ExtendedHierarchicKey deriveChild(List<ChildNumber> parentPath, boolean relativePath, boolean createParent, ChildNumber createChildNumber) {
return deriveChild(get(parentPath, relativePath, createParent), createChildNumber);
/**
* 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 ExtendedHierarchicKey deriveChild(List<ChildNumber> parentPath, boolean relative, boolean createParent, ChildNumber createChildNumber) {
return deriveChild(get(parentPath, relative, createParent), createChildNumber);
}
private ExtendedHierarchicKey deriveChild(ExtendedHierarchicKey parent, ChildNumber createChildNumber) {
@ -105,6 +135,9 @@ public class DeterministicHierarchy implements Serializable {
return childKey;
}
/**
* Returns the root key that the {@link DeterministicHierarchy} was created with.
*/
public ExtendedHierarchicKey getRootKey() {
return get(rootPath, false, false);
}

View File

@ -32,7 +32,9 @@ import java.util.Arrays;
import java.util.Collections;
/**
* Extended key as per BIP 32 is a pair (key, chaincode).
* An extended key is a node in a {@link DeterministicHierarchy}. As per
* <a href="https://en.bitcoin.it/wiki/BIP_0032">the BIP 32 specification</a> it is a pair (key, chaincode). If you
* know its path in the tree you can derive more keys from this.
*/
public class ExtendedHierarchicKey implements Serializable {
public static final ChildNumber MASTER_CHILD_NUMBER = new ChildNumber(0);
@ -57,6 +59,11 @@ public class ExtendedHierarchicKey implements Serializable {
this.privateAsFieldElement = privateKeyFieldElt;
}
/**
* Returns the path through some {@link DeterministicHierarchy} which reaches this keys position in the tree.
* A path can be written as 1/2/1 which means the first child of the root, the second child of that node, then
* the first child of that node.
*/
public ImmutableList<ChildNumber> getChildNumberPath() {
return childNumberPath;
}
@ -65,18 +72,30 @@ public class ExtendedHierarchicKey implements Serializable {
return childNumberPath.size();
}
/**
* Returns the last element of the path returned by {@link com.google.bitcoin.crypto.hd.ExtendedHierarchicKey#getChildNumberPath()}
*/
public ChildNumber getChildNumber() {
return getDepth() == 0 ? MASTER_CHILD_NUMBER : childNumberPath.get(childNumberPath.size() - 1);
}
byte[] getChainCode() {
/**
* Returns the chain code associated with this key. See the specification to learn more about chain codes.
*/
public byte[] getChainCode() {
return chainCode;
}
/**
* Returns the path of this key as a human readable string starting with M to indicate the master key.
*/
public String getPath() {
return PATH_JOINER.join(Iterables.concat(Collections.singleton("M"), getChildNumberPath()));
}
/**
* Returns RIPE-MD160(SHA256(pub key bytes)).
*/
public byte[] getIdentifier() {
return Utils.sha256hash160(getPubKeyBytes());
}
@ -92,8 +111,10 @@ public class ExtendedHierarchicKey implements Serializable {
return getPubPoint().getEncoded();
}
/** Returns the first 32 bits of the result of {@link #getIdentifier()}. */
public byte[] getFingerprint() {
// todo: why is this different than armory's fingerprint? BIP 32: "The first 32 bits of the identifier are called the fingerprint."
// TODO: why is this different than armory's fingerprint? BIP 32: "The first 32 bits of the identifier are called the fingerprint."
return Arrays.copyOfRange(getIdentifier(), 0, 4);
}

View File

@ -24,6 +24,7 @@ import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Arrays;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
/**
@ -36,6 +37,10 @@ public final class HDKeyDerivation {
private static final HMac MASTER_HMAC_SHA256 = HDUtils.createHmacSha256Digest("Bitcoin seed".getBytes());
/**
* Generates a new deterministic key from the given seed, which can be any arbitrary byte array. However resist
* the temptation to use a string as the seed - any key derived from a password is likely to be weak and easily
* broken by attackers (this is not theoretical, people have had money stolen that way).
*
* @throws HDDerivationException if generated master key is invalid (private key 0 or >= n).
*/
public static ExtendedHierarchicKey createMasterPrivateKey(byte[] seed) throws HDDerivationException {
@ -114,9 +119,9 @@ public final class HDKeyDerivation {
assertNonZero(ki, "Illegal derived key: derived private key equals 0.");
keyBytes = ki.toByteArray();
} else {
assertArgument(!childNumber.isPrivateDerivation(), "Can't use private derivation with public keys only.");
checkArgument(!childNumber.isPrivateDerivation(), "Can't use private derivation with public keys only.");
ECPoint Ki = HDUtils.getEcParams().getG().multiply(ilInt).add(parent.getPubPoint());
assertArgument(!Ki.equals(HDUtils.getCurve().getInfinity()),
checkArgument(!Ki.equals(HDUtils.getCurve().getInfinity()),
"Illegal derived key: derived public key equals infinity.");
keyBytes = HDUtils.toCompressed(Ki.getEncoded());
}
@ -124,17 +129,11 @@ public final class HDKeyDerivation {
}
private static void assertNonZero(BigInteger integer, String errorMessage) {
assertArgument(!integer.equals(BigInteger.ZERO), errorMessage);
checkArgument(!integer.equals(BigInteger.ZERO), errorMessage);
}
private static void assertLessThanN(BigInteger integer, String errorMessage) {
assertArgument(integer.compareTo(HDUtils.getEcParams().getN()) < 0, errorMessage);
}
private static void assertArgument(boolean assertion, String errorMessage) {
if (!assertion) {
throw new HDDerivationException(errorMessage);
}
checkArgument(integer.compareTo(HDUtils.getEcParams().getN()) < 0, errorMessage);
}
private static class RawKeyBytes {

View File

@ -1,38 +0,0 @@
package com.google.bitcoin.crypto.hd.wallet;
import com.google.bitcoin.core.ECKey;
import java.io.Serializable;
import java.util.ArrayList;
import static com.google.common.base.Preconditions.checkState;
/**
* @author Matija Mazi <br/>
*
* The default WalletKeyGenerator implementation, creating random receiving keys and always returning the second
* existing keychain key (or first if only one).
*/
public class DefaultKeyGenerator implements WalletKeyGenerator, Serializable {
@Override
public ECKey nextReceivingKey() {
return new ECKey();
}
@Override
public ECKey nextChangeKey(ArrayList<ECKey> keychain) {
// For now let's just pick the second key in our keychain. In future we might want to do something else to
// give the user better privacy here, eg in incognito mode.
// The second key is chosen rather than the first because, by default, a wallet is created with a
// single key. If the user imports say a blockchain.info backup they typically want change to go
// to one of the imported keys
checkState(keychain.size() > 0, "Can't send value without an address to use for receiving change");
ECKey change = keychain.get(0);
if (keychain.size() > 1) {
change = keychain.get(1);
}
return change;
}
}

View File

@ -1,85 +0,0 @@
package com.google.bitcoin.crypto.hd.wallet;
import com.google.bitcoin.core.ECKey;
import com.google.bitcoin.crypto.hd.ChildNumber;
import com.google.bitcoin.crypto.hd.DeterministicHierarchy;
import com.google.bitcoin.crypto.hd.ExtendedHierarchicKey;
import com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.util.ArrayList;
/**
* @author Matija Mazi <br/>
*
* A WalletKeyGenerator, used by Wallet, that uses BIP32 for key generation: internal deterministic chain for change
* keys and external chain for receiving keys.
*/
public class DeterministicKeyGenerator implements WalletKeyGenerator, Serializable {
private static final Logger log = LoggerFactory.getLogger(DeterministicKeyGenerator.class);
public static final boolean PRIVATE_DERIVATION = true;
public static final ChildNumber EXTERNAL_CHAIN = new ChildNumber(0, PRIVATE_DERIVATION);
public static final ChildNumber INTERNAL_CHAIN = new ChildNumber(1, PRIVATE_DERIVATION);
// private final NetworkParameters params;
private final DeterministicHierarchy hierarchy;
private final ImmutableList<ChildNumber> externalChainRootPath;
private final ImmutableList<ChildNumber> internalChainRootPath;
public DeterministicKeyGenerator(ExtendedHierarchicKey rootKey) {
log.debug("DeterministicKeyGenerator.DeterministicKeyGenerator");
long start = System.currentTimeMillis();
// this.params = params;
hierarchy = new DeterministicHierarchy(rootKey);
externalChainRootPath = hierarchy.deriveChild(rootKey.getChildNumberPath(), false, false, EXTERNAL_CHAIN).getChildNumberPath();
internalChainRootPath = hierarchy.deriveChild(rootKey.getChildNumberPath(), false, false, INTERNAL_CHAIN).getChildNumberPath();
log.debug("DKG constructor took {}", System.currentTimeMillis() - start);
}
public ExtendedHierarchicKey nextExternal() {
log.debug("DeterministicKeyGenerator.nextExternal");
return hierarchy.deriveNextChild(externalChainRootPath, false, false, PRIVATE_DERIVATION);
}
public ExtendedHierarchicKey nextInternal() {
log.debug("DeterministicKeyGenerator.nextInternal");
return hierarchy.deriveNextChild(internalChainRootPath, false, false, PRIVATE_DERIVATION);
}
@Override
public ECKey nextReceivingKey() {
return nextExternal().toECKey();
}
@Override
public ECKey nextChangeKey(ArrayList<ECKey> keychain) {
return nextInternal().toECKey();
}
/*
private Address nextExternalAddress() {
return nextExternal().toECKey().toAddress(getParams());
}
private Address nextInternalAddress() {
return nextInternal().toECKey().toAddress(getParams());
}
synchronized Address getChangeAddress() {
return nextInternalAddress();
}
public ExtendedHierarchicKey getRootKey() {
return hierarchy.getRootKey();
}
public NetworkParameters getParams() {
return params;
}
*/
}

View File

@ -1,16 +0,0 @@
package com.google.bitcoin.crypto.hd.wallet;
import com.google.bitcoin.core.ECKey;
import java.util.ArrayList;
/**
* @author Matija Mazi <br/>
*
* Used by {@link com.google.bitcoin.core.Wallet} to generate receiving and change keys.
*/
public interface WalletKeyGenerator {
ECKey nextReceivingKey();
ECKey nextChangeKey(ArrayList<ECKey> keychain);
}