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; }
// filteredTxHashList contains all transactions, filteredTxn just a subset private boolean add( Block block, boolean tryConnecting, @Nullable List<Sha256Hash> filteredTxHashList, @Nullable Map<Sha256Hash, Transaction> filteredTxn) throws BlockStoreException, VerificationException, PrunedException { // TODO: Use read/write locks to ensure that during chain download properties are still low // latency. lock.lock(); try { // Quick check for duplicates to avoid an expensive check further down (in findSplit). This // can happen a lot // when connecting orphan transactions due to the dumb brute force algorithm we use. if (block.equals(getChainHead().getHeader())) { return true; } if (tryConnecting && orphanBlocks.containsKey(block.getHash())) { return false; } // If we want to verify transactions (ie we are running with full blocks), verify that block // has transactions if (shouldVerifyTransactions() && block.transactions == null) throw new VerificationException("Got a block header while running in full-block mode"); // Check for already-seen block, but only for full pruned mode, where the DB is // more likely able to handle these queries quickly. if (shouldVerifyTransactions() && blockStore.get(block.getHash()) != null) { return true; } // Does this block contain any transactions we might care about? Check this up front before // verifying the // blocks validity so we can skip the merkle root verification if the contents aren't // interesting. This saves // a lot of time for big blocks. boolean contentsImportant = shouldVerifyTransactions(); if (block.transactions != null) { contentsImportant = contentsImportant || containsRelevantTransactions(block); } // Prove the block is internally valid: hash is lower than target, etc. This only checks the // block contents // if there is a tx sending or receiving coins using an address in one of our wallets. And // those transactions // are only lightly verified: presence in a valid connecting block is taken as proof of // validity. See the // article here for more details: http://code.google.com/p/bitcoinj/wiki/SecurityModel try { block.verifyHeader(); if (contentsImportant) block.verifyTransactions(); } catch (VerificationException e) { log.error("Failed to verify block: ", e); log.error(block.getHashAsString()); throw e; } // Try linking it to a place in the currently known blocks. StoredBlock storedPrev = getStoredBlockInCurrentScope(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. checkState(tryConnecting, "bug in tryConnectingOrphans"); log.warn( "Block does not connect: {} prev {}", block.getHashAsString(), block.getPrevBlockHash()); orphanBlocks.put(block.getHash(), new OrphanBlock(block, filteredTxHashList, filteredTxn)); return false; } else { checkState(lock.isHeldByCurrentThread()); // It connects to somewhere on the chain. Not necessarily the top of the best known chain. params.checkDifficultyTransitions(storedPrev, block, blockStore); connectBlock( block, storedPrev, shouldVerifyTransactions(), filteredTxHashList, filteredTxn); } if (tryConnecting) tryConnectingOrphans(); return true; } finally { lock.unlock(); } }