/* * We completed handling of a filtered block. Update false-positive estimate based * on the total number of transactions in the original block. * * count includes filtered transactions, transactions that were passed in and were relevant * and transactions that were false positives (i.e. includes all transactions in the block). */ protected void trackFilteredTransactions(int count) { // Track non-false-positives in batch. Each non-false-positive counts as // 0.0 towards the estimate. // // This is slightly off because we are applying false positive tracking before non-FP tracking, // which counts FP as if they came at the beginning of the block. Assuming uniform FP // spread in a block, this will somewhat underestimate the FP rate (5% for 1000 tx block). double alphaDecay = Math.pow(1 - FP_ESTIMATOR_ALPHA, count); // new_rate = alpha_decay * new_rate falsePositiveRate = alphaDecay * falsePositiveRate; double betaDecay = Math.pow(1 - FP_ESTIMATOR_BETA, count); // trend = beta * (new_rate - old_rate) + beta_decay * trend falsePositiveTrend = FP_ESTIMATOR_BETA * count * (falsePositiveRate - previousFalsePositiveRate) + betaDecay * falsePositiveTrend; // new_rate += alpha_decay * trend falsePositiveRate += alphaDecay * falsePositiveTrend; // Stash new_rate in old_rate previousFalsePositiveRate = falsePositiveRate; }
@Override public int receiveBytes(ByteBuffer buff) { checkArgument( buff.position() == 0 && buff.capacity() >= BitcoinSerializer.BitcoinPacketHeader.HEADER_LENGTH + 4); try { // Repeatedly try to deserialize messages until we hit a BufferUnderflowException boolean firstMessage = true; while (true) { // If we are in the middle of reading a message, try to fill that one first, before we // expect another if (largeReadBuffer != null) { // This can only happen in the first iteration checkState(firstMessage); // Read new bytes into the largeReadBuffer int bytesToGet = Math.min(buff.remaining(), largeReadBuffer.length - largeReadBufferPos); buff.get(largeReadBuffer, largeReadBufferPos, bytesToGet); largeReadBufferPos += bytesToGet; // Check the largeReadBuffer's status if (largeReadBufferPos == largeReadBuffer.length) { // ...processing a message if one is available processMessage(serializer.deserializePayload(header, ByteBuffer.wrap(largeReadBuffer))); largeReadBuffer = null; header = null; firstMessage = false; } else // ...or just returning if we don't have enough bytes yet return buff.position(); } // Now try to deserialize any messages left in buff Message message; int preSerializePosition = buff.position(); try { message = serializer.deserialize(buff); } catch (BufferUnderflowException e) { // If we went through the whole buffer without a full message, we need to use the // largeReadBuffer if (firstMessage && buff.limit() == buff.capacity()) { // ...so reposition the buffer to 0 and read the next message header buff.position(0); try { serializer.seekPastMagicBytes(buff); header = serializer.deserializeHeader(buff); // Initialize the largeReadBuffer with the next message's size and fill it with any // bytes // left in buff largeReadBuffer = new byte[header.size]; largeReadBufferPos = buff.remaining(); buff.get(largeReadBuffer, 0, largeReadBufferPos); } catch (BufferUnderflowException e1) { // If we went through a whole buffer's worth of bytes without getting a header, give // up // In cases where the buff is just really small, we could create a second // largeReadBuffer // that we use to deserialize the magic+header, but that is rather complicated when // the buff // should probably be at least that big anyway (for efficiency) throw new ProtocolException( "No magic bytes+header after reading " + buff.capacity() + " bytes"); } } else { // Reposition the buffer to its original position, which saves us from skipping messages // by // seeking past part of the magic bytes before all of them are in the buffer buff.position(preSerializePosition); } return buff.position(); } // Process our freshly deserialized message processMessage(message); firstMessage = false; } } catch (Exception e) { exceptionCaught(e); return -1; // Returning -1 also throws an IllegalStateException upstream and kills the // connection } }
/** * Returns all the key chains found in the given list of keys. Typically there will only be one, * but in the case of key rotation it can happen that there are multiple chains found. */ public static List<DeterministicKeyChain> fromProtobuf( List<Protos.Key> keys, @Nullable KeyCrypter crypter) throws UnreadableWalletException { List<DeterministicKeyChain> chains = newLinkedList(); DeterministicSeed seed = null; DeterministicKeyChain chain = null; int lookaheadSize = -1; for (Protos.Key key : keys) { final Protos.Key.Type t = key.getType(); if (t == Protos.Key.Type.DETERMINISTIC_MNEMONIC) { if (chain != null) { checkState(lookaheadSize >= 0); chain.setLookaheadSize(lookaheadSize); chain.maybeLookAhead(); chains.add(chain); chain = null; } long timestamp = key.getCreationTimestamp() / 1000; String passphrase = DEFAULT_PASSPHRASE_FOR_MNEMONIC; // FIXME allow non-empty passphrase if (key.hasSecretBytes()) { seed = new DeterministicSeed(key.getSecretBytes().toStringUtf8(), passphrase, timestamp); } else if (key.hasEncryptedData()) { EncryptedData data = new EncryptedData( key.getEncryptedData().getInitialisationVector().toByteArray(), key.getEncryptedData().getEncryptedPrivateKey().toByteArray()); seed = new DeterministicSeed(data, timestamp); } else { throw new UnreadableWalletException("Malformed key proto: " + key.toString()); } if (log.isDebugEnabled()) log.debug("Deserializing: DETERMINISTIC_MNEMONIC: {}", seed); } else if (t == Protos.Key.Type.DETERMINISTIC_KEY) { if (!key.hasDeterministicKey()) throw new UnreadableWalletException( "Deterministic key missing extra data: " + key.toString()); byte[] chainCode = key.getDeterministicKey().getChainCode().toByteArray(); // Deserialize the path through the tree. LinkedList<ChildNumber> path = newLinkedList(); for (int i : key.getDeterministicKey().getPathList()) path.add(new ChildNumber(i)); // Deserialize the public key and path. ECPoint pubkey = ECKey.CURVE.getCurve().decodePoint(key.getPublicKey().toByteArray()); final ImmutableList<ChildNumber> immutablePath = ImmutableList.copyOf(path); // Possibly create the chain, if we didn't already do so yet. boolean isWatchingAccountKey = false; boolean isFollowingKey = false; // save previous chain if any if the key is marked as following. Current key and the next // ones are to be // placed in new following key chain if (key.getDeterministicKey().getIsFollowing()) { if (chain != null) { checkState(lookaheadSize >= 0); chain.setLookaheadSize(lookaheadSize); chain.maybeLookAhead(); chains.add(chain); chain = null; seed = null; } isFollowingKey = true; } if (chain == null) { if (seed == null) { DeterministicKey accountKey = new DeterministicKey(immutablePath, chainCode, pubkey, null, null); if (!accountKey.getPath().equals(ACCOUNT_ZERO_PATH)) throw new UnreadableWalletException( "Expecting account key but found key with path: " + HDUtils.formatPath(accountKey.getPath())); chain = new DeterministicKeyChain(accountKey, isFollowingKey); isWatchingAccountKey = true; } else { chain = new DeterministicKeyChain(seed, crypter); chain.lookaheadSize = LAZY_CALCULATE_LOOKAHEAD; // If the seed is encrypted, then the chain is incomplete at this point. However, we // will load // it up below as we parse in the keys. We just need to check at the end that we've // loaded // everything afterwards. } } // Find the parent key assuming this is not the root key, and not an account key for a // watching chain. DeterministicKey parent = null; if (!path.isEmpty() && !isWatchingAccountKey) { ChildNumber index = path.removeLast(); parent = chain.hierarchy.get(path, false, false); path.add(index); } DeterministicKey detkey; if (key.hasSecretBytes()) { // Not encrypted: private key is available. final BigInteger priv = new BigInteger(1, key.getSecretBytes().toByteArray()); detkey = new DeterministicKey(immutablePath, chainCode, pubkey, priv, parent); } else { if (key.hasEncryptedData()) { Protos.EncryptedData proto = key.getEncryptedData(); EncryptedData data = new EncryptedData( proto.getInitialisationVector().toByteArray(), proto.getEncryptedPrivateKey().toByteArray()); checkNotNull(crypter, "Encountered an encrypted key but no key crypter provided"); detkey = new DeterministicKey(immutablePath, chainCode, crypter, pubkey, data, parent); } else { // No secret key bytes and key is not encrypted: either a watching key or private key // bytes // will be rederived on the fly from the parent. detkey = new DeterministicKey(immutablePath, chainCode, pubkey, null, parent); } } if (key.hasCreationTimestamp()) detkey.setCreationTimeSeconds(key.getCreationTimestamp() / 1000); if (log.isDebugEnabled()) log.debug("Deserializing: DETERMINISTIC_KEY: {}", detkey); if (!isWatchingAccountKey) { // If the non-encrypted case, the non-leaf keys (account, internal, external) have already // been // rederived and inserted at this point and the two lines below are just a no-op. In the // encrypted // case though, we can't rederive and we must reinsert, potentially building the heirarchy // object // if need be. if (path.size() == 0) { // Master key. chain.rootKey = detkey; chain.hierarchy = new DeterministicHierarchy(detkey); } else if (path.size() == 2) { if (detkey.getChildNumber().num() == 0) { chain.externalKey = detkey; chain.issuedExternalKeys = key.getDeterministicKey().getIssuedSubkeys(); lookaheadSize = Math.max(lookaheadSize, key.getDeterministicKey().getLookaheadSize()); } else if (detkey.getChildNumber().num() == 1) { chain.internalKey = detkey; chain.issuedInternalKeys = key.getDeterministicKey().getIssuedSubkeys(); } } } chain.hierarchy.putKey(detkey); chain.basicKeyChain.importKey(detkey); } } if (chain != null) { checkState(lookaheadSize >= 0); chain.setLookaheadSize(lookaheadSize); chain.maybeLookAhead(); chains.add(chain); } return chains; }