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:
parent
0385ab34e5
commit
50dd5af0c8
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user