/** Constructs a BlockChain connected to the given list of listeners (eg, wallets) and a store. */ public AbstractBlockChain( Context context, List<BlockChainListener> listeners, BlockStore blockStore) throws BlockStoreException { this.blockStore = blockStore; chainHead = blockStore.getChainHead(); log.info("chain head is at height {}:\n{}", chainHead.getHeight(), chainHead.getHeader()); this.params = context.getParams(); this.listeners = new CopyOnWriteArrayList<ListenerRegistration<BlockChainListener>>(); for (BlockChainListener l : listeners) addListener(l, Threading.SAME_THREAD); }
// 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(); } }
private static void darkGravityWaveCheck( StoredBlock prevBlock, Block added, BlockStore store, NetworkParameters params) { StoredBlock blockReading = prevBlock; long blockTimeAverage = 0; long blockTimeAveragePrev = 0; long blockTimeCount = 0; long blockTimeSum2 = 0; long blockTimeCount2 = 0; long lastBlockTime = 0; long pastBlocksMin = 14; long pastBlocksMax = 140; long countBlocks = 0; BigInteger pastDifficultyAverage = BigInteger.valueOf(0); BigInteger pastDifficultyAveragePrev = BigInteger.valueOf(0); if (prevBlock == null || prevBlock.getHeight() == 0 || prevBlock.getHeight() < pastBlocksMin) { verifyDifficulty(prevBlock, added, params.getMaxTarget(), params); return; } for (int i = 1; blockReading.getHeight() > 0; i++) { if (i > pastBlocksMax) { break; } countBlocks++; if (countBlocks <= pastBlocksMin) { if (countBlocks == 1) { pastDifficultyAverage = blockReading.getHeader().getDifficultyTargetAsInteger(); } else { pastDifficultyAverage = blockReading .getHeader() .getDifficultyTargetAsInteger() .subtract(pastDifficultyAveragePrev) .divide(BigInteger.valueOf(countBlocks)) .add(pastDifficultyAveragePrev); } pastDifficultyAveragePrev = pastDifficultyAverage; } if (lastBlockTime > 0) { long diff = lastBlockTime - blockReading.getHeader().getTimeSeconds(); if (blockTimeCount <= pastBlocksMin) { blockTimeCount++; if (blockTimeCount == 1) { blockTimeAverage = diff; } else { blockTimeAverage = (diff - blockTimeAveragePrev) / blockTimeCount + blockTimeAveragePrev; } blockTimeAveragePrev = blockTimeAverage; } blockTimeCount2++; blockTimeSum2 += diff; } lastBlockTime = blockReading.getHeader().getTimeSeconds(); try { blockReading = store.get(blockReading.getHeader().getPrevBlockHash()); if (blockReading == null) { return; } } catch (BlockStoreException ex) { log.warn("Dark gravity wave 3 descended to start of the chain"); return; } } BigInteger bnNew = pastDifficultyAverage; if (blockTimeCount != 0 && blockTimeCount2 != 0) { double smartAverage = ((double) blockTimeAverage) * 0.7 + ((double) blockTimeSum2 / (double) blockTimeCount2) * 0.3; if (smartAverage < 1) smartAverage = 1; final int targetSpacing = params.getTargetSpacing(prevBlock.getHeader(), prevBlock.getHeight()); final double shift = targetSpacing / smartAverage; final double dCountBlocks = (double) countBlocks; double actualTimespan = dCountBlocks * ((double) targetSpacing) / shift; double targetTimespan = dCountBlocks * targetSpacing; if (actualTimespan < targetTimespan / 3) actualTimespan = targetTimespan / 3; if (actualTimespan > targetTimespan * 3) actualTimespan = targetTimespan * 3; // Retarget bnNew = bnNew.multiply(BigInteger.valueOf((long) actualTimespan)); bnNew = bnNew.divide(BigInteger.valueOf((long) targetTimespan)); } verifyDifficulty(prevBlock, added, bnNew, params); }
private static void darkGravityWave3Check( StoredBlock prevBlock, Block added, BlockStore store, NetworkParameters params) { StoredBlock blockReading = prevBlock; long actualTimespan = 0; long lastBlockTime = 0; long pastBlocksMin = 24; long pastBlocksMax = 24; long countBlocks = 0; BigInteger pastDifficultyAverage = BigInteger.ZERO; BigInteger pastDifficultyAveragePrev = BigInteger.ZERO; if (prevBlock == null || prevBlock.getHeight() == 0 || prevBlock.getHeight() < pastBlocksMin) { verifyDifficulty(prevBlock, added, params.getMaxTarget(), params); return; } for (int i = 1; blockReading.getHeight() > 0; i++) { if (i > pastBlocksMax) { break; } countBlocks++; if (countBlocks <= pastBlocksMin) { if (countBlocks == 1) { pastDifficultyAverage = blockReading.getHeader().getDifficultyTargetAsInteger(); } else { pastDifficultyAverage = pastDifficultyAveragePrev .multiply(BigInteger.valueOf(countBlocks)) .add(blockReading.getHeader().getDifficultyTargetAsInteger()) .divide(BigInteger.valueOf(countBlocks + 1)); } pastDifficultyAveragePrev = pastDifficultyAverage; } if (lastBlockTime > 0) { actualTimespan += (lastBlockTime - blockReading.getHeader().getTimeSeconds()); } lastBlockTime = blockReading.getHeader().getTimeSeconds(); try { blockReading = store.get(blockReading.getHeader().getPrevBlockHash()); if (blockReading == null) { return; } } catch (BlockStoreException ex) { log.warn("Dark gravity wave 3 descended to start of the chain"); return; } } BigInteger bnNew = pastDifficultyAverage; long targetTimespan = countBlocks * params.getTargetSpacing(prevBlock.getHeader(), prevBlock.getHeight()); if (actualTimespan < targetTimespan / 3) { actualTimespan = targetTimespan / 3; } if (actualTimespan > targetTimespan * 3) { actualTimespan = targetTimespan * 3; } // Retarget bnNew = bnNew.multiply(BigInteger.valueOf(actualTimespan)); bnNew = bnNew.divide(BigInteger.valueOf(targetTimespan)); verifyDifficulty(prevBlock, added, bnNew, params); }
private static void kimotoGravityWellCheck( StoredBlock prevBlock, Block added, BlockStore store, NetworkParameters params) throws BlockStoreException { final long blocksTargetSpacing = (long) (2.5 * 60); // 2.5 minutes int timeDaySeconds = 60 * 60 * 24; long pastSecondsMin = timeDaySeconds / 40; long pastSecondsMax = timeDaySeconds * 7; long pastBlocksMin = pastSecondsMin / blocksTargetSpacing; long pastBlocksMax = pastSecondsMax / blocksTargetSpacing; StoredBlock blockReading = prevBlock; long pastBlocksMass = 0; long pastRateActualSeconds = 0; long pastRateTargetSeconds = 0; double pastRateAdjustmentRatio = 1.0f; BigInteger pastDifficultyAverage = BigInteger.valueOf(0); BigInteger pastDifficultyAveragePrev = BigInteger.valueOf(0); double eventHorizonDeviation; double eventHorizonDeviationFast; double eventHorizonDeviationSlow; if (prevBlock == null || prevBlock.getHeight() == 0 || (long) prevBlock.getHeight() < pastBlocksMin) { verifyDifficulty(prevBlock, added, params.getMaxTarget(), params); return; } final Block prevHeader = prevBlock.getHeader(); long latestBlockTime = prevHeader.getTimeSeconds(); for (int i = 1; blockReading.getHeight() > 0; i++) { if (pastBlocksMax > 0 && i > pastBlocksMax) { break; } pastBlocksMass++; if (i == 1) { pastDifficultyAverage = blockReading.getHeader().getDifficultyTargetAsInteger(); } else { pastDifficultyAverage = (blockReading .getHeader() .getDifficultyTargetAsInteger() .subtract(pastDifficultyAveragePrev)) .divide(BigInteger.valueOf(i)) .add(pastDifficultyAveragePrev); } pastDifficultyAveragePrev = pastDifficultyAverage; if (blockReading.getHeight() > 646120 && latestBlockTime < blockReading.getHeader().getTimeSeconds()) { // eliminates the ability to go back in time latestBlockTime = blockReading.getHeader().getTimeSeconds(); } pastRateActualSeconds = prevHeader.getTimeSeconds() - blockReading.getHeader().getTimeSeconds(); pastRateTargetSeconds = blocksTargetSpacing * pastBlocksMass; if (blockReading.getHeight() > 646120) { // this should slow down the upward difficulty change if (pastRateActualSeconds < 5) { pastRateActualSeconds = 5; } } else { if (pastRateActualSeconds < 0) { pastRateActualSeconds = 0; } } if (pastRateActualSeconds != 0 && pastRateTargetSeconds != 0) { pastRateAdjustmentRatio = (double) pastRateTargetSeconds / pastRateActualSeconds; } eventHorizonDeviation = 1 + 0.7084 * Math.pow((double) pastBlocksMass / 28.2d, -1.228); eventHorizonDeviationFast = eventHorizonDeviation; eventHorizonDeviationSlow = 1 / eventHorizonDeviation; if (pastBlocksMass >= pastBlocksMin) { if (pastRateAdjustmentRatio <= eventHorizonDeviationSlow || pastRateAdjustmentRatio >= eventHorizonDeviationFast) { break; } } blockReading = store.get(blockReading.getHeader().getPrevBlockHash()); if (blockReading == null) { return; } } BigInteger newDifficulty = pastDifficultyAverage; if (pastRateActualSeconds != 0 && pastRateTargetSeconds != 0) { newDifficulty = newDifficulty.multiply(BigInteger.valueOf(pastRateActualSeconds)); newDifficulty = newDifficulty.divide(BigInteger.valueOf(pastRateTargetSeconds)); } if (newDifficulty.compareTo(params.getMaxTarget()) > 0) { log.info("Difficulty hit proof of work limit: {}", newDifficulty.toString(16)); newDifficulty = params.getMaxTarget(); } verifyDifficulty(prevBlock, added, newDifficulty, params); }