/**
  * Return the fingerprint of this key's parent as an int value, or zero if this key is the root
  * node of the key hierarchy. Raise an exception if the arguments are inconsistent. This method
  * exists to avoid code repetition in the constructors.
  */
 private int ascertainParentFingerprint(DeterministicKey parentKey, int parentFingerprint)
     throws IllegalArgumentException {
   if (parentFingerprint != 0) {
     if (parent != null)
       checkArgument(
           parent.getFingerprint() == parentFingerprint,
           "parent fingerprint mismatch",
           Integer.toHexString(parent.getFingerprint()),
           Integer.toHexString(parentFingerprint));
     return parentFingerprint;
   } else return 0;
 }
 /**
  * Deserialize an HD Key.
  *
  * @param parent The parent node in the given key's deterministic hierarchy.
  */
 public static DeterministicKey deserialize(
     NetworkParameters params, byte[] serializedKey, @Nullable DeterministicKey parent) {
   ByteBuffer buffer = ByteBuffer.wrap(serializedKey);
   int header = buffer.getInt();
   if (header != params.getBip32HeaderPriv() && header != params.getBip32HeaderPub())
     throw new IllegalArgumentException(
         "Unknown header bytes: " + toBase58(serializedKey).substring(0, 4));
   boolean pub = header == params.getBip32HeaderPub();
   int depth =
       buffer.get() & 0xFF; // convert signed byte to positive int since depth cannot be negative
   final int parentFingerprint = buffer.getInt();
   final int i = buffer.getInt();
   final ChildNumber childNumber = new ChildNumber(i);
   ImmutableList<ChildNumber> path;
   if (parent != null) {
     if (parentFingerprint == 0)
       throw new IllegalArgumentException("Parent was provided but this key doesn't have one");
     if (parent.getFingerprint() != parentFingerprint)
       throw new IllegalArgumentException("Parent fingerprints don't match");
     path = HDUtils.append(parent.getPath(), childNumber);
     if (path.size() != depth) throw new IllegalArgumentException("Depth does not match");
   } else {
     if (depth >= 1)
       // We have been given a key that is not a root key, yet we lack the object representing the
       // parent.
       // This can happen when deserializing an account key for a watching wallet.  In this case,
       // we assume that
       // the client wants to conceal the key's position in the hierarchy.  The path is truncated
       // at the
       // parent's node.
       path = ImmutableList.of(childNumber);
     else path = ImmutableList.of();
   }
   byte[] chainCode = new byte[32];
   buffer.get(chainCode);
   byte[] data = new byte[33];
   buffer.get(data);
   checkArgument(!buffer.hasRemaining(), "Found unexpected data in key");
   if (pub) {
     return new DeterministicKey(
         path,
         chainCode,
         new LazyECPoint(ECKey.CURVE.getCurve(), data),
         parent,
         depth,
         parentFingerprint);
   } else {
     return new DeterministicKey(
         path, chainCode, new BigInteger(1, data), parent, depth, parentFingerprint);
   }
 }
 @Override
 public DeterministicKey decrypt(KeyCrypter keyCrypter, KeyParameter aesKey)
     throws KeyCrypterException {
   checkNotNull(keyCrypter);
   // Check that the keyCrypter matches the one used to encrypt the keys, if set.
   if (this.keyCrypter != null && !this.keyCrypter.equals(keyCrypter))
     throw new KeyCrypterException(
         "The keyCrypter being used to decrypt the key is different to the one that was used to encrypt it");
   BigInteger privKey = findOrDeriveEncryptedPrivateKey(keyCrypter, aesKey);
   DeterministicKey key = new DeterministicKey(childNumberPath, chainCode, privKey, parent);
   if (!Arrays.equals(key.getPubKey(), getPubKey()))
     throw new KeyCrypterException("Provided AES key is wrong");
   if (parent == null) key.setCreationTimeSeconds(getCreationTimeSeconds());
   return key;
 }
 public DeterministicKey encrypt(
     KeyCrypter keyCrypter, KeyParameter aesKey, @Nullable DeterministicKey newParent)
     throws KeyCrypterException {
   // Same as the parent code, except we construct a DeterministicKey instead of an ECKey.
   checkNotNull(keyCrypter);
   if (newParent != null) checkArgument(newParent.isEncrypted());
   final byte[] privKeyBytes = getPrivKeyBytes();
   checkState(privKeyBytes != null, "Private key is not available");
   EncryptedData encryptedPrivateKey = keyCrypter.encrypt(privKeyBytes, aesKey);
   DeterministicKey key =
       new DeterministicKey(
           childNumberPath, chainCode, keyCrypter, pub, encryptedPrivateKey, newParent);
   if (newParent == null) key.setCreationTimeSeconds(getCreationTimeSeconds());
   return key;
 }
 /**
  * Returns this keys {@link org.bitcoinj.crypto.KeyCrypter} <b>or</b> the keycrypter of its parent
  * key.
  */
 @Override
 @Nullable
 public KeyCrypter getKeyCrypter() {
   if (keyCrypter != null) return keyCrypter;
   else if (parent != null) return parent.getKeyCrypter();
   else return null;
 }
 /** Constructs a key from its components. This is not normally something you should use. */
 public DeterministicKey(
     ImmutableList<ChildNumber> childNumberPath,
     byte[] chainCode,
     BigInteger priv,
     @Nullable DeterministicKey parent) {
   super(priv, compressPoint(ECKey.publicPointFromPrivate(priv)));
   checkArgument(chainCode.length == 32);
   this.parent = parent;
   this.childNumberPath = checkNotNull(childNumberPath);
   this.chainCode = Arrays.copyOf(chainCode, chainCode.length);
   this.depth = this.childNumberPath.size();
   this.parentFingerprint = (parent != null) ? parent.getFingerprint() : 0;
 }
 private BigInteger derivePrivateKeyDownwards(
     DeterministicKey cursor, byte[] parentalPrivateKeyBytes) {
   DeterministicKey downCursor =
       new DeterministicKey(
           cursor.childNumberPath,
           cursor.chainCode,
           cursor.pub,
           new BigInteger(1, parentalPrivateKeyBytes),
           cursor.parent);
   // Now we have to rederive the keys along the path back to ourselves. That path can be found by
   // just truncating
   // our path with the length of the parents path.
   ImmutableList<ChildNumber> path =
       childNumberPath.subList(cursor.getPath().size(), childNumberPath.size());
   for (ChildNumber num : path) {
     downCursor = HDKeyDerivation.deriveChildKey(downCursor, num);
   }
   // downCursor is now the same key as us, but with private key bytes.
   checkState(downCursor.pub.equals(pub));
   return checkNotNull(downCursor.priv);
 }
 /**
  * The creation time of a deterministic key is equal to that of its parent, unless this key is the
  * root of a tree in which case the time is stored alongside the key as per normal, see {@link
  * org.bitcoinj.core.ECKey#getCreationTimeSeconds()}.
  */
 @Override
 public long getCreationTimeSeconds() {
   if (parent != null) return parent.getCreationTimeSeconds();
   else return super.getCreationTimeSeconds();
 }
 /**
  * A deterministic key is considered to be encrypted if it has access to encrypted private key
  * bytes, OR if its parent does. The reason is because the parent would be encrypted under the
  * same key and this key knows how to rederive its own private key bytes from the parent, if
  * needed.
  */
 @Override
 public boolean isEncrypted() {
   return priv == null && (super.isEncrypted() || (parent != null && parent.isEncrypted()));
 }
 /**
  * A deterministic key is considered to be 'public key only' if it hasn't got a private key part
  * and it cannot be rederived. If the hierarchy is encrypted this returns true.
  */
 @Override
 public boolean isPubKeyOnly() {
   return super.isPubKeyOnly() && (parent == null || parent.isPubKeyOnly());
 }
 /**
  * Returns the same key with the parent pointer removed (it still knows its own path and the
  * parent fingerprint).
  *
  * <p>If this key doesn't have private key bytes stored/cached itself, but could rederive them
  * from the parent, then the new key returned by this method won't be able to do that. Thus, using
  * dropPrivateBytes().dropParent() on a regular DeterministicKey will yield a new DeterministicKey
  * that cannot sign or do other things involving the private key at all.
  */
 public DeterministicKey dropParent() {
   DeterministicKey key = new DeterministicKey(getPath(), getChainCode(), pub, priv, null);
   key.parentFingerprint = parentFingerprint;
   return key;
 }