Class KeyChainGroup

java.lang.Object
org.bitcoinj.wallet.KeyChainGroup
All Implemented Interfaces:
KeyBag

public class KeyChainGroup extends Object implements KeyBag

A KeyChainGroup is used by the Wallet and manages: a BasicKeyChain object (which will normally be empty), and zero or more DeterministicKeyChains. The last added deterministic keychain is always the default active keychain, that's the one we normally derive keys and addresses from.

There can be active keychains for each output script type. However this class almost entirely only works on the default active keychain (see getActiveKeyChain()). The other active keychains (see getActiveKeyChain(ScriptType, long)) are meant as fallback for if a sender doesn't understand a certain new script type (e.g. P2WPKH which comes with the new Bech32 address format). Active keychains share the same seed, so that upgrading the wallet (see upgradeToDeterministic(ScriptType, KeyChainGroupStructure, long, KeyParameter)) to understand a new script type doesn't require a fresh backup.

If a key rotation time is set, it may be necessary to add a new DeterministicKeyChain with a fresh seed and also preserve the old one, so funds can be swept from the rotating keys. In this case, there may be more than one deterministic chain. The latest chain is called the active chain and is where new keys are served from.

The wallet delegates most key management tasks to this class. It is not thread safe and requires external locking, i.e. by the wallet lock. The group then in turn delegates most operations to the key chain objects, combining their responses together when necessary.

Deterministic key chains have a concept of a lookahead size and threshold. Please see the discussion in the class docs for DeterministicKeyChain for more information on this topic.

  • Field Details

  • Method Details

    • createBasic

      public static KeyChainGroup createBasic(NetworkParameters params)
      Creates a keychain group with just a basic chain. No deterministic chains will be created automatically.
    • builder

      public static KeyChainGroup.Builder builder(NetworkParameters params)
    • builder

      public static KeyChainGroup.Builder builder(NetworkParameters params, KeyChainGroupStructure structure)
    • isSupportsDeterministicChains

      public boolean isSupportsDeterministicChains()
      Returns true if it contains any deterministic keychain or one could be created.
    • addAndActivateHDChain

      public void addAndActivateHDChain(DeterministicKeyChain chain)
      Adds an HD chain to the chains list, and make it the default chain (from which keys are issued). Useful for adding a complex pre-configured keychain, such as a married wallet.
    • currentKey

      public DeterministicKey currentKey(KeyChain.KeyPurpose purpose)
      Returns a key that hasn't been seen in a transaction yet, and which is suitable for displaying in a wallet user interface as "a convenient key to receive funds on" when the purpose parameter is KeyChain.KeyPurpose.RECEIVE_FUNDS. The returned key is stable until it's actually seen in a pending or confirmed transaction, at which point this method will start returning a different key (for each purpose independently).

      This method is not supposed to be used for married keychains and will throw UnsupportedOperationException if the active chain is married. For married keychains use currentAddress(KeyChain.KeyPurpose) to get a proper P2SH address

    • currentAddress

      public Address currentAddress(KeyChain.KeyPurpose purpose)
      Returns address for a currentKey(KeyChain.KeyPurpose)
    • freshKey

      public DeterministicKey freshKey(KeyChain.KeyPurpose purpose)
      Returns a key that has not been returned by this method before (fresh). You can think of this as being a newly created key, although the notion of "create" is not really valid for a DeterministicKeyChain. When the parameter is KeyChain.KeyPurpose.RECEIVE_FUNDS the returned key is suitable for being put into a receive coins wizard type UI. You should use this when the user is definitely going to hand this key out to someone who wishes to send money.

      This method is not supposed to be used for married keychains and will throw UnsupportedOperationException if the active chain is married. For married keychains use freshAddress(KeyChain.KeyPurpose) to get a proper P2SH address

    • freshKeys

      public List<DeterministicKey> freshKeys(KeyChain.KeyPurpose purpose, int numberOfKeys)
      Returns a key/s that have not been returned by this method before (fresh). You can think of this as being newly created key/s, although the notion of "create" is not really valid for a DeterministicKeyChain. When the parameter is KeyChain.KeyPurpose.RECEIVE_FUNDS the returned key is suitable for being put into a receive coins wizard type UI. You should use this when the user is definitely going to hand this key out to someone who wishes to send money.

      This method is not supposed to be used for married keychains and will throw UnsupportedOperationException if the active chain is married. For married keychains use freshAddress(KeyChain.KeyPurpose) to get a proper P2SH address

    • freshAddress

      public Address freshAddress(KeyChain.KeyPurpose purpose, Script.ScriptType outputScriptType, long keyRotationTimeSecs)

      Returns a fresh address for a given KeyChain.KeyPurpose and of a given Script.ScriptType.

      This method is meant for when you really need a fallback address. Normally, you should be using freshAddress(KeyChain.KeyPurpose) or currentAddress(KeyChain.KeyPurpose).

    • freshAddress

      public Address freshAddress(KeyChain.KeyPurpose purpose)
      Returns address for a freshKey(KeyChain.KeyPurpose)
    • getActiveKeyChains

      public List<DeterministicKeyChain> getActiveKeyChains(long keyRotationTimeSecs)
      Returns the key chains that are used for generation of fresh/current keys, in the order of how they were added. The default active chain will come last in the list.
    • getActiveKeyChain

      public final DeterministicKeyChain getActiveKeyChain(Script.ScriptType outputScriptType, long keyRotationTimeSecs)
      Returns the key chain that's used for generation of fresh/current keys of the given type. If it's not the default type and no active chain for this type exists, null is returned. No upgrade or downgrade is tried.
    • getActiveKeyChain

      public final DeterministicKeyChain getActiveKeyChain()
      Returns the key chain that's used for generation of default fresh/current keys. This is always the newest deterministic chain. If no deterministic chain is present but imported keys instead, a deterministic upgrate is tried.
    • mergeActiveKeyChains

      public final void mergeActiveKeyChains(KeyChainGroup from, long keyRotationTimeSecs)
      Merge all active chains from the given keychain group into this keychain group.
    • getLookaheadSize

      public int getLookaheadSize()
      Gets the current lookahead size being used for ALL deterministic key chains. See DeterministicKeyChain.setLookaheadSize(int) for more information.
    • getLookaheadThreshold

      public int getLookaheadThreshold()
      Gets the current lookahead threshold being used for ALL deterministic key chains. See DeterministicKeyChain.setLookaheadThreshold(int) for more information.
    • importKeys

      public int importKeys(List<ECKey> keys)
      Imports the given keys into the basic chain, creating it if necessary.
    • importKeys

      public int importKeys(ECKey... keys)
      Imports the given keys into the basic chain, creating it if necessary.
    • checkPassword

      public boolean checkPassword(CharSequence password)
    • checkAESKey

      public boolean checkAESKey(org.bouncycastle.crypto.params.KeyParameter aesKey)
    • importKeysAndEncrypt

      public int importKeysAndEncrypt(List<ECKey> keys, org.bouncycastle.crypto.params.KeyParameter aesKey)
      Imports the given unencrypted keys into the basic chain, encrypting them along the way with the given key.
    • findRedeemDataFromScriptHash

      @Nullable public RedeemData findRedeemDataFromScriptHash(byte[] scriptHash)
      Description copied from interface: KeyBag
      Locates a redeem data (redeem script and keys) from the keychain given the hash of the script. This is needed when finding out which key and script we need to use to locally sign a P2SH transaction input. It is assumed that wallet should not have more than one private key for a single P2SH tx for security reasons. Returns RedeemData object or null if no such data was found.
      Specified by:
      findRedeemDataFromScriptHash in interface KeyBag
    • markP2SHAddressAsUsed

      public void markP2SHAddressAsUsed(LegacyAddress address)
    • findKeyFromPubKeyHash

      @Nullable public ECKey findKeyFromPubKeyHash(byte[] pubKeyHash, @Nullable Script.ScriptType scriptType)
      Description copied from interface: KeyBag
      Locates a keypair from the keychain given the hash of the public key, and (optionally) by usage for a specific script type. This is needed when finding out which key we need to use to redeem a transaction output.
      Specified by:
      findKeyFromPubKeyHash in interface KeyBag
      Parameters:
      pubKeyHash - hash of the keypair to look for
      scriptType - only look for given usage (currently Script.ScriptType.P2PKH or Script.ScriptType.P2WPKH) or null if we don't care
      Returns:
      found key or null if no such key was found.
    • markPubKeyHashAsUsed

      public void markPubKeyHashAsUsed(byte[] pubKeyHash)
      Mark the DeterministicKeys as used, if they match the pubKeyHash See DeterministicKeyChain.markKeyAsUsed(DeterministicKey) for more info on this.
    • hasKey

      public boolean hasKey(ECKey key)
    • findKeyFromPubKey

      @Nullable public ECKey findKeyFromPubKey(byte[] pubKey)
      Description copied from interface: KeyBag
      Locates a keypair from the keychain given the raw public key bytes.
      Specified by:
      findKeyFromPubKey in interface KeyBag
      Returns:
      ECKey or null if no such key was found.
    • markPubKeyAsUsed

      public void markPubKeyAsUsed(byte[] pubkey)
      Mark the DeterministicKeys as used, if they match the pubkey See DeterministicKeyChain.markKeyAsUsed(DeterministicKey) for more info on this.
    • numKeys

      public int numKeys()
      Returns the number of keys managed by this group, including the lookahead buffers.
    • removeImportedKey

      public boolean removeImportedKey(ECKey key)
      Removes a key that was imported into the basic key chain. You cannot remove deterministic keys.
      Throws:
      IllegalArgumentException - if the key is deterministic.
    • isMarried

      public final boolean isMarried()
      Whether the active keychain is married. A keychain is married when it vends P2SH addresses from multiple keychains in a multisig relationship.
      See Also:
    • encrypt

      public void encrypt(KeyCrypter keyCrypter, org.bouncycastle.crypto.params.KeyParameter aesKey)
      Encrypt the keys in the group using the KeyCrypter and the AES key. A good default KeyCrypter to use is KeyCrypterScrypt.
      Throws:
      KeyCrypterException - Thrown if the wallet encryption fails for some reason, leaving the group unchanged.
      DeterministicUpgradeRequiredException - Thrown if there are random keys but no HD chain.
    • decrypt

      public void decrypt(org.bouncycastle.crypto.params.KeyParameter aesKey)
      Decrypt the keys in the group using the previously given key crypter and the AES key. A good default KeyCrypter to use is KeyCrypterScrypt.
      Throws:
      KeyCrypterException - Thrown if the wallet decryption fails for some reason, leaving the group unchanged.
    • isEncrypted

      public boolean isEncrypted()
      Returns true if the group is encrypted.
    • isWatching

      public boolean isWatching()
      Returns whether this chain has only watching keys (unencrypted keys with no private part). Mixed chains are forbidden.
      Throws:
      IllegalStateException - if there are no keys, or if there is a mix between watching and non-watching keys.
    • getKeyCrypter

      @Nullable public KeyCrypter getKeyCrypter()
      Returns the key crypter or null if the group is not encrypted.
    • getImportedKeys

      public List<ECKey> getImportedKeys()
      Returns a list of the non-deterministic keys that have been imported into the wallet, or the empty list if none.
    • getEarliestKeyCreationTime

      public long getEarliestKeyCreationTime()
    • getBloomFilterElementCount

      public int getBloomFilterElementCount()
    • getBloomFilter

      public BloomFilter getBloomFilter(int size, double falsePositiveRate, long nTweak)
    • isRequiringUpdateAllBloomFilter

      public boolean isRequiringUpdateAllBloomFilter()
    • addEventListener

      public void addEventListener(KeyChainEventListener listener)
      Adds a listener for events that are run when keys are added, on the user thread.
    • addEventListener

      public void addEventListener(KeyChainEventListener listener, Executor executor)
      Adds a listener for events that are run when keys are added, on the given executor.
    • removeEventListener

      public boolean removeEventListener(KeyChainEventListener listener)
      Removes a listener for events that are run when keys are added.
    • addCurrentKeyChangeEventListener

      public void addCurrentKeyChangeEventListener(CurrentKeyChangeEventListener listener)
      Removes a listener for events that are run when a current key and/or address changes.
    • addCurrentKeyChangeEventListener

      public void addCurrentKeyChangeEventListener(CurrentKeyChangeEventListener listener, Executor executor)
      Adds a listener for events that are run when a current key and/or address changes, on the given executor.
    • removeCurrentKeyChangeEventListener

      public boolean removeCurrentKeyChangeEventListener(CurrentKeyChangeEventListener listener)
      Removes a listener for events that are run when a current key and/or address changes.
    • serializeToProtobuf

      public List<Protos.Key> serializeToProtobuf()
      Returns a list of key protobufs obtained by merging the chains.
    • fromProtobufUnencrypted

      public static KeyChainGroup fromProtobufUnencrypted(NetworkParameters params, List<Protos.Key> keys, KeyChainFactory factory) throws UnreadableWalletException
      Throws:
      UnreadableWalletException
    • fromProtobufEncrypted

      public static KeyChainGroup fromProtobufEncrypted(NetworkParameters params, List<Protos.Key> keys, KeyCrypter crypter, KeyChainFactory factory) throws UnreadableWalletException
      Throws:
      UnreadableWalletException
    • upgradeToDeterministic

      public void upgradeToDeterministic(Script.ScriptType preferredScriptType, KeyChainGroupStructure structure, long keyRotationTimeSecs, @Nullable org.bouncycastle.crypto.params.KeyParameter aesKey) throws DeterministicUpgradeRequiresPassword, AllRandomKeysRotating

      This method will upgrade the wallet along the following path: Basic --> P2PKH --> P2WPKH

      It won't skip any steps in that upgrade path because the user might be restoring from a backup and still expects money on the P2PKH chain.

      It will extract and reuse the seed from the current wallet, so that a fresh backup isn't required after upgrading. If coming from a basic chain containing only random keys this means it will pick the oldest non-rotating private key as a seed.

      Note that for upgrading an encrypted wallet, the decryption key is needed. In future, we could skip that requirement for a P2PKH --> P2WPKH upgrade and just clone the encryped seed, but currently the key is needed even for that.

      Parameters:
      preferredScriptType - desired script type for the active keychain
      structure - keychain group structure to derive an account path from
      keyRotationTimeSecs - If non-zero, UNIX time for which keys created before this are assumed to be compromised or weak, those keys will not be used for deterministic upgrade.
      aesKey - If non-null, the encryption key the keychain is encrypted under. If the keychain is encrypted and this is not supplied, an exception is thrown letting you know you should ask the user for their password, turn it into a key, and then try again.
      Throws:
      IllegalStateException - if there is already a deterministic key chain present or if there are no random keys (i.e. this is not an upgrade scenario), or if aesKey is provided but the wallet is not encrypted.
      IllegalArgumentException - if the rotation time specified excludes all keys.
      DeterministicUpgradeRequiresPassword - if the key chain group is encrypted and you should provide the users encryption key.
      AllRandomKeysRotating
    • isDeterministicUpgradeRequired

      public boolean isDeterministicUpgradeRequired(Script.ScriptType preferredScriptType, long keyRotationTimeSecs)
      Returns true if a call to upgradeToDeterministic(ScriptType, KeyChainGroupStructure, long, KeyParameter) is required in order to have an active deterministic keychain of the desired script type.
    • toString

      public String toString(boolean includeLookahead, boolean includePrivateKeys, @Nullable org.bouncycastle.crypto.params.KeyParameter aesKey)
    • getDeterministicKeyChains

      public List<DeterministicKeyChain> getDeterministicKeyChains()
      Returns a copy of the current list of chains.
    • getCombinedKeyLookaheadEpochs

      public int getCombinedKeyLookaheadEpochs()
      Returns a counter that increases (by an arbitrary amount) each time new keys have been calculated due to lookahead and thus the Bloom filter that was previously calculated has become stale.