Пример #1
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);
   }
 }
Пример #2
0
 /** Returns freshly derived key/s that have not been returned by this method before. */
 @Override
 public List<DeterministicKey> getKeys(KeyPurpose purpose, int numberOfKeys) {
   checkArgument(numberOfKeys > 0);
   lock.lock();
   try {
     DeterministicKey parentKey;
     int index;
     switch (purpose) {
         // Map both REFUND and RECEIVE_KEYS to the same branch for now. Refunds are a feature of
         // the BIP 70
         // payment protocol. Later we may wish to map it to a different branch (in a new wallet
         // version?).
         // This would allow a watching wallet to only be able to see inbound payments, but not
         // change
         // (i.e. spends) or refunds. Might be useful for auditing ...
       case RECEIVE_FUNDS:
       case REFUND:
         issuedExternalKeys += numberOfKeys;
         index = issuedExternalKeys;
         parentKey = externalKey;
         break;
       case AUTHENTICATION:
       case CHANGE:
         issuedInternalKeys += numberOfKeys;
         index = issuedInternalKeys;
         parentKey = internalKey;
         break;
       default:
         throw new UnsupportedOperationException();
     }
     // Optimization: potentially do a very quick key generation for just the number of keys we
     // need if we
     // didn't already create them, ignoring the configured lookahead size. This ensures we'll be
     // able to
     // retrieve the keys in the following loop, but if we're totally fresh and didn't get a chance
     // to
     // calculate the lookahead keys yet, this will not block waiting to calculate 100+ EC point
     // multiplies.
     // On slow/crappy Android phones looking ahead 100 keys can take ~5 seconds but the OS will
     // kill us
     // if we block for just one second on the UI thread. Because UI threads may need an address in
     // order
     // to render the screen, we need getKeys to be fast even if the wallet is totally brand new
     // and lookahead
     // didn't happen yet.
     //
     // It's safe to do this because when a network thread tries to calculate a Bloom filter, we'll
     // go ahead
     // and calculate the full lookahead zone there, so network requests will always use the right
     // amount.
     List<DeterministicKey> lookahead = maybeLookAhead(parentKey, index, 0, 0);
     basicKeyChain.importKeys(lookahead);
     List<DeterministicKey> keys = new ArrayList<DeterministicKey>(numberOfKeys);
     for (int i = 0; i < numberOfKeys; i++) {
       ImmutableList<ChildNumber> path =
           HDUtils.append(parentKey.getPath(), new ChildNumber(index - numberOfKeys + i, false));
       DeterministicKey k = hierarchy.get(path, false, false);
       // Just a last minute sanity check before we hand the key out to the app for usage. This
       // isn't inspired
       // by any real problem reports from bitcoinj users, but I've heard of cases via the
       // grapevine of
       // places that lost money due to bitflips causing addresses to not match keys. Of course in
       // an
       // environment with flaky RAM there's no real way to always win: bitflips could be
       // introduced at any
       // other layer. But as we're potentially retrieving from long term storage here, check
       // anyway.
       checkForBitFlip(k);
       keys.add(k);
     }
     return keys;
   } finally {
     lock.unlock();
   }
 }