<commit_before>/**
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.bitcoin.core;

import java.math.BigInteger;
import java.util.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A BlockChain holds a series of {@link Block} objects, links them together, and knows how to verify that the
 * chain follows the rules of the {@link NetworkParameters} for this chain.<p>
 *
 * A BlockChain requires a {@link Wallet} to receive transactions that it finds during the initial download. However,
 * if you don't care about this, you can just pass in an empty wallet and nothing bad will happen.<p>
 *
 * A newly constructed BlockChain is empty. To fill it up, use a {@link Peer} object to download the chain from the
 * network.<p>
 *
 * <b>Notes</b><p>
 *
 * The 'chain' can actually be a tree although in normal operation it can be thought of as a simple list. In such a
 * situation there are multiple stories of the economy competing to become the one true consensus. This can happen
 * naturally when two miners solve a block within a few seconds of each other, or it can happen when the chain is
 * under attack.<p>
 *
 * A reference to the head block of every chain is stored. If you can reach the genesis block by repeatedly walking
 * through the prevBlock pointers, then we say this is a full chain. If you cannot reach the genesis block we say it is
 * an orphan chain.<p>
 *
 * Orphan chains can occur when blocks are solved and received during the initial block chain download,
 * or if we connect to a peer that doesn't send us blocks in order.
 */
public class BlockChain {
    private static final Logger log = LoggerFactory.getLogger(BlockChain.class);

    /** Keeps a map of block hashes to StoredBlocks. */
    protected BlockStore blockStore;

    /**
     * Tracks the top of the best known chain.<p>
     *
     * Following this one down to the genesis block produces the story of the economy from the creation of BitCoin
     * until the present day. The chain head can change if a new set of blocks is received that results in a chain of
     * greater work than the one obtained by following this one down. In that case a reorganize is triggered,
     * potentially invalidating transactions in our wallet.
     */
    protected StoredBlock chainHead;

    protected final NetworkParameters params;
    protected final Wallet wallet;

    // Holds blocks that we have received but can't plug into the chain yet, eg because they were created whilst we
    // were downloading the block chain.
    private final ArrayList<Block> unconnectedBlocks = new ArrayList<Block>();

    /**
     * Constructs a BlockChain connected to the given wallet and store. To obtain a {@link Wallet} you can construct
     * one from scratch, or you can deserialize a saved wallet from disk using {@link Wallet#loadFromFile(java.io.File)}
     * <p>
     *
     * For the store you can use a {@link MemoryBlockStore} if you don't care about saving the downloaded data, or a
     * {@link BoundedOverheadBlockStore} if you'd like to ensure fast startup the next time you run the program.
     */
    public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) {
        try {
            this.blockStore = blockStore;
            chainHead = blockStore.getChainHead();
            log.info("chain head is:\n{}", chainHead.getHeader());
        } catch (BlockStoreException e) {
            throw new RuntimeException(e);
        }

        this.params = params;
        this.wallet = wallet;
    }

    /**
     * Processes a received block and tries to add it to the chain. If there's something wrong with the block an
     * exception is thrown. If the block is OK but cannot be connected to the chain at this time, returns false.
     * If the block can be connected to the chain, returns true.
     */
    public synchronized boolean add(Block block) throws VerificationException, ScriptException {
        try {
            return add(block, true);
        } catch (BlockStoreException e) {
            // TODO: Figure out a better way to propagate this exception to the user.
            throw new RuntimeException(e);
        }
    }

    // Stat counters.
    private long statsLastTime = System.currentTimeMillis();
    private long statsBlocksAdded;

    private synchronized boolean add(Block block, boolean tryConnecting)
            throws BlockStoreException, VerificationException, ScriptException {
        if (System.currentTimeMillis() - statsLastTime > 1000) {
            // More than a second passed since last stats logging.
            log.info("{} blocks per second", statsBlocksAdded);
            statsLastTime = System.currentTimeMillis();
            statsBlocksAdded = 0;
        }
        // We check only the chain head for double adds here to avoid potentially expensive block chain misses.
        if (block.equals(chainHead.getHeader())) {
            // Duplicate add of the block at the top of the chain, can be a natural artifact of the download process.
            return true;
        }

        // Prove the block is internally valid: hash is lower than target, merkle root is correct and so on.
        try {
            block.verify();
        } catch (VerificationException e) {
            log.error("Failed to verify block:", e);
            log.error(block.toString());
            throw e;
        }

        // Try linking it to a place in the currently known blocks.
        StoredBlock storedPrev = blockStore.get(block.getPrevBlockHash());

        if (storedPrev == null) {
            // We can't find the previous block. Probably we are still in the process of downloading the chain and a
            // block was solved whilst we were doing it. We put it to one side and try to connect it later when we
            // have more blocks.
            log.warn("Block does not connect: {}", block.getHashAsString());
            unconnectedBlocks.add(block);
            return false;
        } else {
            // It connects to somewhere on the chain. Not necessarily the top of the best known chain.
            //
            // Create a new StoredBlock from this block. It will throw away the transaction data so when block goes
            // out of scope we will reclaim the used memory.
            StoredBlock newStoredBlock = storedPrev.build(block);
            checkDifficultyTransitions(storedPrev, newStoredBlock);
            blockStore.put(newStoredBlock);
            // block.transactions may be null here if we received only a header and not a full block. This does not
            // happen currently but might in future if getheaders is implemented.
            connectBlock(newStoredBlock, storedPrev, block.transactions);
        }

        if (tryConnecting)
            tryConnectingUnconnected();

        statsBlocksAdded++;
        return true;
    }

    private void connectBlock(StoredBlock newStoredBlock, StoredBlock storedPrev, List<Transaction> newTransactions)
            throws BlockStoreException, VerificationException {
        if (storedPrev.equals(chainHead)) {
            // This block connects to the best known block, it is a normal continuation of the system.
            setChainHead(newStoredBlock);
            log.info("Chain is now {} blocks high", chainHead.getHeight());
            if (newTransactions != null)
                sendTransactionsToWallet(newStoredBlock, NewBlockType.BEST_CHAIN, newTransactions);
        } else {
            // This block connects to somewhere other than the top of the best known chain. We treat these differently.
            //
            // Note that we send the transactions to the wallet FIRST, even if we're about to re-organize this block
            // to become the new best chain head. This simplifies handling of the re-org in the Wallet class.
            boolean haveNewBestChain = newStoredBlock.moreWorkThan(chainHead);
            if (haveNewBestChain) {
                log.info("Block is causing a re-organize");
            } else {
                StoredBlock splitPoint = findSplit(newStoredBlock, chainHead);
                String splitPointHash =
                        splitPoint != null ? splitPoint.getHeader().getHashAsString() : "?";
                log.info("Block forks the chain at {}, but it did not cause a reorganize:\n{}",
                          splitPointHash, newStoredBlock);
            }
<commit_msg>Don't output an info log for every block downloaded.<commit_after>/**
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.bitcoin.core;

import java.math.BigInteger;
import java.util.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A BlockChain holds a series of {@link Block} objects, links them together, and knows how to verify that the
 * chain follows the rules of the {@link NetworkParameters} for this chain.<p>
 *
 * A BlockChain requires a {@link Wallet} to receive transactions that it finds during the initial download. However,
 * if you don't care about this, you can just pass in an empty wallet and nothing bad will happen.<p>
 *
 * A newly constructed BlockChain is empty. To fill it up, use a {@link Peer} object to download the chain from the
 * network.<p>
 *
 * <b>Notes</b><p>
 *
 * The 'chain' can actually be a tree although in normal operation it can be thought of as a simple list. In such a
 * situation there are multiple stories of the economy competing to become the one true consensus. This can happen
 * naturally when two miners solve a block within a few seconds of each other, or it can happen when the chain is
 * under attack.<p>
 *
 * A reference to the head block of every chain is stored. If you can reach the genesis block by repeatedly walking
 * through the prevBlock pointers, then we say this is a full chain. If you cannot reach the genesis block we say it is
 * an orphan chain.<p>
 *
 * Orphan chains can occur when blocks are solved and received during the initial block chain download,
 * or if we connect to a peer that doesn't send us blocks in order.
 */
public class BlockChain {
    private static final Logger log = LoggerFactory.getLogger(BlockChain.class);

    /** Keeps a map of block hashes to StoredBlocks. */
    protected BlockStore blockStore;

    /**
     * Tracks the top of the best known chain.<p>
     *
     * Following this one down to the genesis block produces the story of the economy from the creation of BitCoin
     * until the present day. The chain head can change if a new set of blocks is received that results in a chain of
     * greater work than the one obtained by following this one down. In that case a reorganize is triggered,
     * potentially invalidating transactions in our wallet.
     */
    protected StoredBlock chainHead;

    protected final NetworkParameters params;
    protected final Wallet wallet;

    // Holds blocks that we have received but can't plug into the chain yet, eg because they were created whilst we
    // were downloading the block chain.
    private final ArrayList<Block> unconnectedBlocks = new ArrayList<Block>();

    /**
     * Constructs a BlockChain connected to the given wallet and store. To obtain a {@link Wallet} you can construct
     * one from scratch, or you can deserialize a saved wallet from disk using {@link Wallet#loadFromFile(java.io.File)}
     * <p>
     *
     * For the store you can use a {@link MemoryBlockStore} if you don't care about saving the downloaded data, or a
     * {@link BoundedOverheadBlockStore} if you'd like to ensure fast startup the next time you run the program.
     */
    public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) {
        try {
            this.blockStore = blockStore;
            chainHead = blockStore.getChainHead();
            log.info("chain head is:\n{}", chainHead.getHeader());
        } catch (BlockStoreException e) {
            throw new RuntimeException(e);
        }

        this.params = params;
        this.wallet = wallet;
    }

    /**
     * Processes a received block and tries to add it to the chain. If there's something wrong with the block an
     * exception is thrown. If the block is OK but cannot be connected to the chain at this time, returns false.
     * If the block can be connected to the chain, returns true.
     */
    public synchronized boolean add(Block block) throws VerificationException, ScriptException {
        try {
            return add(block, true);
        } catch (BlockStoreException e) {
            // TODO: Figure out a better way to propagate this exception to the user.
            throw new RuntimeException(e);
        }
    }

    // Stat counters.
    private long statsLastTime = System.currentTimeMillis();
    private long statsBlocksAdded;

    private synchronized boolean add(Block block, boolean tryConnecting)
            throws BlockStoreException, VerificationException, ScriptException {
        if (System.currentTimeMillis() - statsLastTime > 1000) {
            // More than a second passed since last stats logging.
            log.info("{} blocks per second", statsBlocksAdded);
            statsLastTime = System.currentTimeMillis();
            statsBlocksAdded = 0;
        }
        // We check only the chain head for double adds here to avoid potentially expensive block chain misses.
        if (block.equals(chainHead.getHeader())) {
            // Duplicate add of the block at the top of the chain, can be a natural artifact of the download process.
            return true;
        }

        // Prove the block is internally valid: hash is lower than target, merkle root is correct and so on.
        try {
            block.verify();
        } catch (VerificationException e) {
            log.error("Failed to verify block:", e);
            log.error(block.toString());
            throw e;
        }

        // Try linking it to a place in the currently known blocks.
        StoredBlock storedPrev = blockStore.get(block.getPrevBlockHash());

        if (storedPrev == null) {
            // We can't find the previous block. Probably we are still in the process of downloading the chain and a
            // block was solved whilst we were doing it. We put it to one side and try to connect it later when we
            // have more blocks.
            log.warn("Block does not connect: {}", block.getHashAsString());
            unconnectedBlocks.add(block);
            return false;
        } else {
            // It connects to somewhere on the chain. Not necessarily the top of the best known chain.
            //
            // Create a new StoredBlock from this block. It will throw away the transaction data so when block goes
            // out of scope we will reclaim the used memory.
            StoredBlock newStoredBlock = storedPrev.build(block);
            checkDifficultyTransitions(storedPrev, newStoredBlock);
            blockStore.put(newStoredBlock);
            // block.transactions may be null here if we received only a header and not a full block. This does not
            // happen currently but might in future if getheaders is implemented.
            connectBlock(newStoredBlock, storedPrev, block.transactions);
        }

        if (tryConnecting)
            tryConnectingUnconnected();

        statsBlocksAdded++;
        return true;
    }

    private void connectBlock(StoredBlock newStoredBlock, StoredBlock storedPrev, List<Transaction> newTransactions)
            throws BlockStoreException, VerificationException {
        if (storedPrev.equals(chainHead)) {
            // This block connects to the best known block, it is a normal continuation of the system.
            setChainHead(newStoredBlock);
            log.trace("Chain is now {} blocks high", chainHead.getHeight());
            if (newTransactions != null)
                sendTransactionsToWallet(newStoredBlock, NewBlockType.BEST_CHAIN, newTransactions);
        } else {
            // This block connects to somewhere other than the top of the best known chain. We treat these differently.
            //
            // Note that we send the transactions to the wallet FIRST, even if we're about to re-organize this block
            // to become the new best chain head. This simplifies handling of the re-org in the Wallet class.
            boolean haveNewBestChain = newStoredBlock.moreWorkThan(chainHead);
            if (haveNewBestChain) {
                log.info("Block is causing a re-organize");
            } else {
                StoredBlock splitPoint = findSplit(newStoredBlock, chainHead);
                String splitPointHash =
                        splitPoint != null ? splitPoint.getHeader().getHashAsString() : "?";
                log.info("Block forks the chain at {}, but it did not cause a reorganize:\n{}",
                          splitPointHash, newStoredBlock);
            }

{'fraction_non_alphanumeric': 0.06783612745642278, 'fraction_numerical': 0.0016653713778172533, 'mean_word_length': 2.575823739579198, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 15, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5649328', 'n_tokens_mistral': 4430, 'n_tokens_neox': 4321, 'n_words': 2090}