private boolean inMainChain(Block block) { Block b = this.lastBlock; while (b != null && b.getBlockNo() > block.getBlockNo()) { b = AbstractDb.blockProvider.getBlock(b.getBlockPrev()); } return b != null && Arrays.equals(b.getBlockHash(), block.getBlockHash()); }
private Block getSameParent(Block block1, Block block2) { Block b1 = block1; Block b2 = block2; while (b1 != null && b2 != null && !Arrays.equals(b1.getBlockHash(), b2.getBlockHash())) { if (b1.getBlockNo() == 0 || b1.getBlockNo() >= b2.getBlockNo()) { b1 = AbstractDb.blockProvider.getBlock(b1.getBlockPrev()); } if (b1.getBlockNo() < b2.getBlockNo()) { b2 = AbstractDb.blockProvider.getBlock(b2.getBlockPrev()); } } return b1; }
public int relayedBlockHeadersForMainChain(List<Block> blocks) { if (blocks == null || blocks.size() == 0) { return 0; } ArrayList<Block> blocksToAdd = new ArrayList<Block>(); Block prev = getLastBlock(); if (prev == null) { log.warn("pre block is null"); return 0; } for (int i = 0; i < blocks.size(); i++) { Block block = blocks.get(i); if (!Arrays.equals(prev.getBlockHash(), block.getBlockPrev())) { Block alreadyIn = getBlock(block.getBlockHash()); if (alreadyIn != null) { log.debug("Block is already in, No." + alreadyIn.getBlockNo()); continue; } else { this.singleBlocks.put(block.getBlockHash(), block); break; } } block.setBlockNo(prev.getBlockNo() + 1); try { block.verifyDifficultyFromPreviousBlock(prev); } catch (Exception e) { e.printStackTrace(); break; } block.setMain(true); blocksToAdd.add(block); prev = block; } if (blocksToAdd.size() > 0) { addBlocks(blocksToAdd); lastBlock = blocksToAdd.get(blocksToAdd.size() - 1); } return blocksToAdd.size(); }
public boolean rollbackBlock(int blockNo) { log.warn("block chain roll back to " + blockNo); if (blockNo > this.lastBlock.getBlockNo()) return false; int delta = this.lastBlock.getBlockNo() - blockNo; if (delta >= BitherjSettings.BLOCK_DIFFICULTY_INTERVAL || delta >= this.getBlockCount()) return false; List<Block> blocks = AbstractDb.blockProvider.getBlocksFrom(blockNo); // DDLogWarn(@"roll back block from %d to %d", self.lastBlock.height, blockNo); for (Block block : blocks) { AbstractDb.blockProvider.removeBlock(block.getBlockHash()); if (block.isMain()) { AbstractDb.txProvider.unConfirmTxByBlockNo(block.getBlockNo()); } } this.lastBlock = AbstractDb.blockProvider.getLastBlock(); return true; }
public List<byte[]> getBlockLocatorArray() { // append 10 most recent block hashes, descending, then continue appending, doubling the step // back each time, // finishing with the genesis block (top, -1, -2, -3, -4, -5, -6, -7, -8, -9, -11, -15, -23, // -39, -71, -135, ..., 0) ArrayList<byte[]> locators = new ArrayList<byte[]>(); int step = 1, start = 0; Block b = this.lastBlock; while (b != null && b.getBlockNo() > 0) { locators.add(b.getBlockHash()); if (++start >= 10) step *= 2; for (int i = 0; b != null && i < step; i++) { b = AbstractDb.blockProvider.getMainChainBlock(b.getBlockPrev()); } } locators.add(BitherjSettings.GENESIS_BLOCK_HASH); return locators; }
public int relayedBlocks(List<Block> blocks) throws VerificationException { if (blocks == null || blocks.size() == 0) { return 0; } Block prev = null; Block first = blocks.get(0); int rollbackBlockNo = 0; if (Arrays.equals(first.getBlockPrev(), this.getLastBlock().getBlockHash())) { prev = this.getLastBlock(); } else if (AbstractDb.blockProvider.getMainChainBlock(first.getBlockPrev()) != null) { prev = this.getSameParent(first, this.getLastBlock()); rollbackBlockNo = prev.getBlockNo(); } if (prev == null) { return 0; } for (Block block : blocks) { if (!Arrays.equals(block.getBlockPrev(), prev.getBlockHash())) { return 0; } block.setBlockNo(prev.getBlockNo() + 1); try { int transitionTime = 0; if (block.getBlockNo() % BitherjSettings.BLOCK_DIFFICULTY_INTERVAL == 0) { // We need to find a block far back in the chain. It's OK that this is expensive because // it only occurs every // two weeks after the initial block chain download. long now = System.currentTimeMillis(); Block cursor = first; for (int i = 0; i < BitherjSettings.BLOCK_DIFFICULTY_INTERVAL - block.getBlockNo() + first.getBlockNo(); i++) { if (cursor == null) { // This should never happen. If it does, it means we are following an incorrect or // busted chain. throw new VerificationException( "Difficulty transition point but we did not find a way back to the genesis block."); } cursor = getBlock(cursor.getBlockPrev()); } long elapsed = System.currentTimeMillis() - now; if (elapsed > 50) log.info("Difficulty transition traversal took {}msec", elapsed); transitionTime = cursor.getBlockTime(); } block.verifyDifficultyFromPreviousBlock(prev, transitionTime); } catch (Exception e) { e.printStackTrace(); return 0; } block.setMain(true); prev = block; } if (rollbackBlockNo > 0) { this.rollbackBlock(rollbackBlockNo); } this.addBlocks(blocks); for (Block block : blocks) { AbstractDb.txProvider.confirmTx(block.getBlockNo(), block.getTxHashes()); } this.lastBlock = blocks.get(blocks.size() - 1); return blocks.size(); }
/* * if result is true, means the block is in main chain, if result is false, means the block is single * or orphan. * */ public boolean relayedBlock(Block block) throws VerificationException { Block prev = AbstractDb.blockProvider.getBlock(block.getBlockPrev()); if (prev == null) { log.debug("prev block is null, prev hash is : " + Utils.hashToString(block.getBlockPrev())); // DDLogDebug(@"%@:%d relayed orphan block %@, previous %@, last block is %@, // height %d", peer.host, peer.port, // block.blockHash, block.prevBlock, self.lastBlock.blockHash, // self.lastBlock.height); // ignore orphans older than one week ago // if (block.blockTime - NSTimeIntervalSince1970 < [NSDate // timeIntervalSinceReferenceDate] - ONE_WEEK) return; this.singleBlocks.put(block.getBlockPrev(), block); return false; // // call get blocks, unless we already did with the previous block, or we're // still downloading the chain // if (self.lastBlock.height >= peer.lastBlock && ![self.lastOrphan.blockHash // isEqual:block.prevBlock]) { // DDLogDebug(@"%@:%d calling getblocks", peer.host, peer.port); // [peer sendGetBlocksMessageWithLocators:[self blockLocatorArray] // andHashStop:nil]; // } } block.setBlockNo(prev.getBlockNo() + 1); // TODO // int transitionTime = 0; // // hit a difficulty transition, find previous transition time // if ((block.getBlockNo() % BitherjSettings.BLOCK_DIFFICULTY_INTERVAL) == 0) { // Block b = block; // for (int i = 0; b != null && i < BitherjSettings.BLOCK_DIFFICULTY_INTERVAL; i++) { // b = DbHelper.blockProvider.getBlock(b.getBlockPrev()); // } // transitionTime = b.getBlockTime(); // } // verify block difficulty block.verifyDifficultyFromPreviousBlock(prev); // if (!block.verifyDifficultyFromPreviousBlock(prev)) { // callback(block, NO); // return; // } boolean result = false; if (Arrays.equals(block.getBlockPrev(), this.lastBlock.getBlockHash())) { this.extendMainChain(block); result = true; } else if (this.inMainChain(block)) { result = true; } else { if (block.getBlockNo() <= BitherjSettings.BITCOIN_REFERENCE_BLOCK_HEIGHT) { log.debug("block is too old"); return false; } if (block.getBlockNo() <= this.lastBlock.getBlockNo()) { this.addOrphan(block); log.debug("block is orphan"); return false; } if (block.getBlockNo() > this.lastBlock.getBlockNo()) { Block b = this.getSameParent(block, this.lastBlock); this.rollbackBlock(b.getBlockNo()); log.debug("roll back block from" + b.getBlockNo()); } } if (!result) log.debug("block is not in main chain"); return result; }