public class DeterministicKeyChain extends Object implements EncryptableKeyChain
A deterministic key chain is a KeyChain
that uses the
BIP 32 standard, as implemented by
DeterministicHierarchy
, to derive all the keys in the keychain from a master seed.
This type of wallet is extremely convenient and flexible. Although backing up full wallet files is always a good
idea, to recover money only the root seed needs to be preserved and that is a number small enough that it can be
written down on paper or, when represented using a BIP 39 MnemonicCode
,
dictated over the phone (possibly even memorized).
Deterministic key chains have other advantages: parts of the key tree can be selectively revealed to allow
for auditing, and new public keys can be generated without access to the private keys, yielding a highly secure
configuration for web servers which can accept payments into a wallet but not spend from them. This does not work
quite how you would expect due to a quirk of elliptic curve mathematics and the techniques used to deal with it.
A watching wallet is not instantiated using the public part of the master key as you may imagine. Instead, you
need to take the account key (first child of the master key) and provide the public part of that to the watching
wallet instead. You can do this by calling getWatchingKey()
and then serializing it with
DeterministicKey.serializePubB58()
. The resulting "xpub..." string encodes
sufficient information about the account key to create a watching chain via
DeterministicKey.deserializeB58(org.bitcoinj.crypto.DeterministicKey, String)
(with null as the first parameter) and then
DeterministicKeyChain(org.bitcoinj.crypto.DeterministicKey)
.
This class builds on DeterministicHierarchy
and
DeterministicKey
by adding support for serialization to and from protobufs,
and encryption of parts of the key tree. Internally it arranges itself as per the BIP 32 spec, with the seed being
used to derive a master key, which is then used to derive an account key, the account key is used to derive two
child keys called the internal and external keys (for change and handing out addresses respectively)
and finally the actual leaf keys that users use hanging off the end. The leaf keys are special in that they don't
internally store the private part at all, instead choosing to rederive the private key from the parent when
needed for signing. This simplifies the design for encrypted key chains.
The key chain manages a lookahead zone. This zone is required because when scanning the chain, you don't know exactly which keys might receive payments. The user may have handed out several addresses and received payments on them, but for latency reasons the block chain is requested from remote peers in bulk, meaning you must "look ahead" when calculating keys to put in the Bloom filter. The default lookahead zone is 100 keys, meaning if the user hands out more than 100 addresses and receives payment on them before the chain is next scanned, some transactions might be missed. 100 is a reasonable choice for consumer wallets running on CPU constrained devices. For industrial wallets that are receiving keys all the time, a higher value is more appropriate. Ideally DKC and the wallet would know how to adjust this value automatically, but that's not implemented at the moment.
In fact the real size of the lookahead zone is larger than requested, by default, it's one third larger. This is because the act of deriving new keys means recalculating the Bloom filters and this is an expensive operation. Thus, to ensure we don't have to recalculate on every single new key/address requested or seen we add more buffer space and only extend the lookahead zone when that buffer is exhausted. For example with a lookahead zone of 100 keys, you can request 33 keys before more keys will be calculated and the Bloom filter rebuilt and rebroadcast. But even when you are requesting the 33rd key, you will still be looking 100 keys ahead.
KeyChain.KeyPurpose
Modifier and Type | Field and Description |
---|---|
static ImmutableList<ChildNumber> |
ACCOUNT_ZERO_PATH |
static String |
DEFAULT_PASSPHRASE_FOR_MNEMONIC |
static ImmutableList<ChildNumber> |
EXTERNAL_PATH |
static ImmutableList<ChildNumber> |
INTERNAL_PATH |
Modifier | Constructor and Description |
---|---|
|
DeterministicKeyChain(byte[] entropy,
String passphrase,
long seedCreationTimeSecs)
Creates a deterministic key chain starting from the given seed.
|
|
DeterministicKeyChain(DeterministicKey watchingKey) |
|
DeterministicKeyChain(DeterministicKey watchingKey,
long creationTimeSeconds)
Creates a deterministic key chain that watches the given (public only) root key.
|
protected |
DeterministicKeyChain(DeterministicSeed seed)
Creates a deterministic key chain starting from the given seed.
|
|
DeterministicKeyChain(SecureRandom random)
Generates a new key chain with entropy selected randomly from the given
SecureRandom
object and the default entropy size. |
|
DeterministicKeyChain(SecureRandom random,
int bits)
Generates a new key chain with entropy selected randomly from the given
SecureRandom
object and of the requested size in bits. |
|
DeterministicKeyChain(SecureRandom random,
int bits,
String passphrase,
long seedCreationTimeSecs)
Generates a new key chain with entropy selected randomly from the given
SecureRandom
object and of the requested size in bits. |
Modifier and Type | Method and Description |
---|---|
void |
addEventListener(KeyChainEventListener listener)
Adds a listener for events that are run when keys are added, on the user thread.
|
void |
addEventListener(KeyChainEventListener listener,
Executor executor)
Adds a listener for events that are run when keys are added, on the given executor.
|
boolean |
checkAESKey(org.spongycastle.crypto.params.KeyParameter aesKey) |
boolean |
checkPassword(CharSequence password) |
DeterministicKey |
findKeyFromPubHash(byte[] pubkeyHash) |
DeterministicKey |
findKeyFromPubKey(byte[] pubkey) |
static List<DeterministicKeyChain> |
fromProtobuf(List<Protos.Key> keys,
KeyCrypter crypter)
Returns all the key chains found in the given list of keys.
|
long |
getEarliestKeyCreationTime()
Returns the earliest creation time of keys in this chain, in seconds since the epoch.
|
BloomFilter |
getFilter(int size,
double falsePositiveRate,
long tweak)
Gets a bloom filter that contains all of the public keys from this chain, and which will provide the given
false-positive rate if it has size elements.
|
int |
getIssuedExternalKeys()
Returns number of keys used on external path.
|
int |
getIssuedInternalKeys()
Returns number of keys used on internal path.
|
DeterministicKey |
getKey(KeyChain.KeyPurpose purpose)
Returns a freshly derived key that has not been returned by this method before.
|
protected DeterministicKey |
getKeyByPath(ChildNumber... path)
Returns the deterministic key for the given absolute path in the hierarchy.
|
protected DeterministicKey |
getKeyByPath(List<ChildNumber> path)
Returns the deterministic key for the given absolute path in the hierarchy.
|
DeterministicKey |
getKeyByPath(List<ChildNumber> path,
boolean create)
Returns the deterministic key for the given absolute path in the hierarchy, optionally creating it
|
KeyCrypter |
getKeyCrypter()
Returns the key crypter used by this key chain, or null if it's not encrypted.
|
int |
getKeyLookaheadEpoch()
Returns a counter that is incremented each time new keys are generated due to lookahead.
|
List<DeterministicKey> |
getKeys(KeyChain.KeyPurpose purpose,
int numberOfKeys)
Returns freshly derived key/s that have not been returned by this method before.
|
List<DeterministicKey> |
getLeafKeys()
Returns leaf keys issued by this chain (including lookahead zone)
|
int |
getLookaheadSize()
The number of public keys we should pre-generate on each path before they are requested by the app.
|
int |
getLookaheadThreshold()
Gets the threshold for the key pre-generation.
|
List<String> |
getMnemonicCode()
Returns a list of words that represent the seed or null if this chain is a watching chain.
|
DeterministicSeed |
getSeed()
Returns the seed or null if this chain is a watching chain.
|
DeterministicKey |
getWatchingKey()
An alias for
getKeyByPath(DeterministicKeyChain.ACCOUNT_ZERO_PATH).getPubOnly() . |
boolean |
hasKey(ECKey key)
Returns true if the given key is in the chain.
|
boolean |
isFollowing()
Return true if this keychain is following another keychain
|
DeterministicKey |
markKeyAsUsed(DeterministicKey k)
Mark the DeterministicKey as used.
|
DeterministicKey |
markPubHashAsUsed(byte[] pubkeyHash)
Mark the DeterministicKeys as used, if they match the pubkeyHash
See
markKeyAsUsed(DeterministicKey) for more info on this. |
DeterministicKey |
markPubKeyAsUsed(byte[] pubkey)
Mark the DeterministicKeys as used, if they match the pubkey
See
markKeyAsUsed(DeterministicKey) for more info on this. |
void |
maybeLookAhead()
Pre-generate enough keys to reach the lookahead size.
|
int |
numBloomFilterEntries()
Returns the number of elements this chain wishes to insert into the Bloom filter.
|
int |
numKeys()
Returns the number of keys this key chain manages.
|
int |
numLeafKeysIssued()
Returns number of leaf keys used including both internal and external paths.
|
boolean |
removeEventListener(KeyChainEventListener listener)
Removes a listener for events that are run when keys are added.
|
List<Protos.Key> |
serializeToProtobuf()
Returns a list of keys serialized to the bitcoinj protobuf format.
|
void |
setLookaheadSize(int lookaheadSize)
Sets a new lookahead size.
|
void |
setLookaheadThreshold(int num)
Sets the threshold for the key pre-generation.
|
DeterministicKeyChain |
toDecrypted(CharSequence password)
Decrypts the key chain with the given password.
|
DeterministicKeyChain |
toDecrypted(org.spongycastle.crypto.params.KeyParameter aesKey)
Decrypt the key chain with the given AES key and whatever
KeyCrypter is already set. |
DeterministicKeyChain |
toEncrypted(CharSequence password)
Takes the given password, which should be strong, derives a key from it and then invokes
EncryptableKeyChain.toEncrypted(org.bitcoinj.crypto.KeyCrypter, org.spongycastle.crypto.params.KeyParameter) with
KeyCrypterScrypt as the crypter. |
DeterministicKeyChain |
toEncrypted(KeyCrypter keyCrypter,
org.spongycastle.crypto.params.KeyParameter aesKey)
Returns a new keychain holding identical/cloned keys to this chain, but encrypted under the given key.
|
static DeterministicKeyChain |
watch(DeterministicKey accountKey)
Creates a key chain that watches the given account key.
|
static DeterministicKeyChain |
watch(DeterministicKey accountKey,
long seedCreationTimeSecs)
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).
|
static DeterministicKeyChain |
watchAndFollow(DeterministicKey watchKey)
Creates a deterministic key chain with the given watch key and that follows some other keychain.
|
public static final String DEFAULT_PASSPHRASE_FOR_MNEMONIC
public static final ImmutableList<ChildNumber> ACCOUNT_ZERO_PATH
public static final ImmutableList<ChildNumber> EXTERNAL_PATH
public static final ImmutableList<ChildNumber> INTERNAL_PATH
public DeterministicKeyChain(SecureRandom random)
SecureRandom
object and the default entropy size.public DeterministicKeyChain(SecureRandom random, int bits)
SecureRandom
object and of the requested size in bits.public DeterministicKeyChain(SecureRandom random, int bits, String passphrase, long seedCreationTimeSecs)
SecureRandom
object and of the requested size in bits. The derived seed is further protected with a user selected passphrase
(see BIP 39).public DeterministicKeyChain(byte[] entropy, String passphrase, long seedCreationTimeSecs)
protected DeterministicKeyChain(DeterministicSeed seed)
public DeterministicKeyChain(DeterministicKey watchingKey, long creationTimeSeconds)
public DeterministicKeyChain(DeterministicKey watchingKey)
public static DeterministicKeyChain watchAndFollow(DeterministicKey watchKey)
public static DeterministicKeyChain watch(DeterministicKey accountKey)
public static DeterministicKeyChain watch(DeterministicKey accountKey, long seedCreationTimeSecs)
public DeterministicKey getKey(KeyChain.KeyPurpose purpose)
public List<DeterministicKey> getKeys(KeyChain.KeyPurpose purpose, int numberOfKeys)
public DeterministicKey markKeyAsUsed(DeterministicKey k)
public DeterministicKey findKeyFromPubHash(byte[] pubkeyHash)
public DeterministicKey findKeyFromPubKey(byte[] pubkey)
@Nullable public DeterministicKey markPubHashAsUsed(byte[] pubkeyHash)
markKeyAsUsed(DeterministicKey)
for more info on this.@Nullable public DeterministicKey markPubKeyAsUsed(byte[] pubkey)
markKeyAsUsed(DeterministicKey)
for more info on this.public boolean hasKey(ECKey key)
KeyChain
protected DeterministicKey getKeyByPath(ChildNumber... path)
protected DeterministicKey getKeyByPath(List<ChildNumber> path)
public DeterministicKey getKeyByPath(List<ChildNumber> path, boolean create)
public DeterministicKey getWatchingKey()
An alias for getKeyByPath(DeterministicKeyChain.ACCOUNT_ZERO_PATH).getPubOnly()
.
Use this when you would like to create a watching key chain that follows this one, but can't spend money from it.
The returned key can be serialized and then passed into watch(org.bitcoinj.crypto.DeterministicKey)
on another system to watch the hierarchy.
public int numKeys()
KeyChain
public int numLeafKeysIssued()
public long getEarliestKeyCreationTime()
KeyChain
Returns the earliest creation time of keys in this chain, in seconds since the epoch. This can return zero
if at least one key does not have that data (was created before key timestamping was implemented). If there
are no keys in the wallet, Long.MAX_VALUE
is returned.
getEarliestKeyCreationTime
in interface KeyChain
public void addEventListener(KeyChainEventListener listener)
KeyChain
addEventListener
in interface KeyChain
public void addEventListener(KeyChainEventListener listener, Executor executor)
KeyChain
addEventListener
in interface KeyChain
public boolean removeEventListener(KeyChainEventListener listener)
KeyChain
removeEventListener
in interface KeyChain
@Nullable public List<String> getMnemonicCode()
public boolean isFollowing()
public List<Protos.Key> serializeToProtobuf()
KeyChain
serializeToProtobuf
in interface KeyChain
public static List<DeterministicKeyChain> fromProtobuf(List<Protos.Key> keys, @Nullable KeyCrypter crypter) throws UnreadableWalletException
UnreadableWalletException
public DeterministicKeyChain toEncrypted(CharSequence password)
EncryptableKeyChain
EncryptableKeyChain.toEncrypted(org.bitcoinj.crypto.KeyCrypter, org.spongycastle.crypto.params.KeyParameter)
with
KeyCrypterScrypt
as the crypter.toEncrypted
in interface EncryptableKeyChain
public DeterministicKeyChain toEncrypted(KeyCrypter keyCrypter, org.spongycastle.crypto.params.KeyParameter aesKey)
EncryptableKeyChain
toEncrypted
in interface EncryptableKeyChain
public DeterministicKeyChain toDecrypted(CharSequence password)
EncryptableKeyChain
EncryptableKeyChain.toDecrypted(org.spongycastle.crypto.params.KeyParameter)
for details.toDecrypted
in interface EncryptableKeyChain
public DeterministicKeyChain toDecrypted(org.spongycastle.crypto.params.KeyParameter aesKey)
EncryptableKeyChain
KeyCrypter
is already set. Note that if you
just want to spend money from an encrypted wallet, don't decrypt the whole thing first. Instead, set the
Wallet.SendRequest.aesKey
field before asking the wallet to build the send.toDecrypted
in interface EncryptableKeyChain
aesKey
- AES key to use (normally created using KeyCrypter#deriveKey and cached as it is time consuming to
create from a password)public boolean checkPassword(CharSequence password)
checkPassword
in interface EncryptableKeyChain
public boolean checkAESKey(org.spongycastle.crypto.params.KeyParameter aesKey)
checkAESKey
in interface EncryptableKeyChain
@Nullable public KeyCrypter getKeyCrypter()
EncryptableKeyChain
getKeyCrypter
in interface EncryptableKeyChain
public int numBloomFilterEntries()
KeyChain
KeyChain.getFilter(int, double, long)
should be at least this large.numBloomFilterEntries
in interface KeyChain
public BloomFilter getFilter(int size, double falsePositiveRate, long tweak)
KeyChain
Gets a bloom filter that contains all of the public keys from this chain, and which will provide the given
false-positive rate if it has size elements. Keep in mind that you will get 2 elements in the bloom filter for
each key in the key chain, for the public key and the hash of the public key (address form). For this reason
size should be at least 2x the result of KeyChain.numKeys()
.
This is used to generate a BloomFilter
which can be BloomFilter.merge(BloomFilter)
d with
another. It could also be used if you have a specific target for the filter's size.
See the docs for BloomFilter.BloomFilter(int, double, long)
for a brief
explanation of anonymity when using bloom filters, and for the meaning of these parameters.
public int getLookaheadSize()
The number of public keys we should pre-generate on each path before they are requested by the app. This is required so that when scanning through the chain given only a seed, we can give enough keys to the remote node via the Bloom filter such that we see transactions that are "from the future", for example transactions created by a different app that's sharing the same seed, or transactions we made before but we're replaying the chain given just the seed. The default is 100.
public void setLookaheadSize(int lookaheadSize)
getLookaheadSize()
for details on what this is. Setting a new size
that's larger than the current size will return immediately and the new size will only take effect next time
a fresh filter is requested (e.g. due to a new peer being connected). So you should set this before starting
to sync the chain, if you want to modify it. If you haven't modified the lookahead threshold manually then
it will be automatically set to be a third of the new size.public void setLookaheadThreshold(int num)
public int getLookaheadThreshold()
setLookaheadThreshold(int)
for details on what this
is. The default is a third of the lookahead size (100 / 3 == 33). If you don't modify it explicitly then this
value will always be one third of the lookahead size.public void maybeLookAhead()
public int getIssuedExternalKeys()
public int getIssuedInternalKeys()
@Nullable public DeterministicSeed getSeed()
public List<DeterministicKey> getLeafKeys()
public int getKeyLookaheadEpoch()
Copyright © 2014. All rights reserved.