/** Gets the median timestamp of the last 11 blocks */
  private static long getMedianTimestampOfRecentBlocks(StoredBlock storedBlock, BlockStore store)
      throws BlockStoreException {
    long[] timestamps = new long[11];
    int unused = 9;
    timestamps[10] = storedBlock.getHeader().getTimeSeconds();
    while (unused >= 0 && (storedBlock = storedBlock.getPrev(store)) != null)
      timestamps[unused--] = storedBlock.getHeader().getTimeSeconds();

    Arrays.sort(timestamps, unused + 1, 11);
    return timestamps[unused + (11 - unused) / 2];
  }
 /**
  * Returns the set of contiguous blocks between 'higher' and 'lower'. Higher is included, lower is
  * not.
  */
 private static LinkedList<StoredBlock> getPartialChain(
     StoredBlock higher, StoredBlock lower, BlockStore store) throws BlockStoreException {
   checkArgument(higher.getHeight() > lower.getHeight(), "higher and lower are reversed");
   LinkedList<StoredBlock> results = new LinkedList<StoredBlock>();
   StoredBlock cursor = higher;
   while (true) {
     results.add(cursor);
     cursor = checkNotNull(cursor.getPrev(store), "Ran off the end of the chain");
     if (cursor.equals(lower)) break;
   }
   return results;
 }
예제 #3
0
 /**
  * Locates the point in the chain at which newStoredBlock and chainHead diverge. Returns null if
  * no split point was found (ie they are part of the same chain).
  */
 private StoredBlock findSplit(StoredBlock newChainHead, StoredBlock chainHead)
     throws BlockStoreException {
   StoredBlock currentChainCursor = chainHead;
   StoredBlock newChainCursor = newChainHead;
   // Loop until we find the block both chains have in common. Example:
   //
   //    A -> B -> C -> D
   //         \--> E -> F -> G
   //
   // findSplit will return block B. chainHead = D and newChainHead = G.
   while (!currentChainCursor.equals(newChainCursor)) {
     if (currentChainCursor.getHeight() > newChainCursor.getHeight()) {
       currentChainCursor = currentChainCursor.getPrev(blockStore);
       assert currentChainCursor != null : "Attempt to follow an orphan chain";
     } else {
       newChainCursor = newChainCursor.getPrev(blockStore);
       assert newChainCursor != null : "Attempt to follow an orphan chain";
     }
   }
   return currentChainCursor;
 }
 /**
  * Locates the point in the chain at which newStoredBlock and chainHead diverge. Returns null if
  * no split point was found (ie they are not part of the same chain). Returns newChainHead or
  * chainHead if they don't actually diverge but are part of the same chain.
  */
 private static StoredBlock findSplit(
     StoredBlock newChainHead, StoredBlock oldChainHead, BlockStore store)
     throws BlockStoreException {
   StoredBlock currentChainCursor = oldChainHead;
   StoredBlock newChainCursor = newChainHead;
   // Loop until we find the block both chains have in common. Example:
   //
   //    A -> B -> C -> D
   //         \--> E -> F -> G
   //
   // findSplit will return block B. oldChainHead = D and newChainHead = G.
   while (!currentChainCursor.equals(newChainCursor)) {
     if (currentChainCursor.getHeight() > newChainCursor.getHeight()) {
       currentChainCursor = currentChainCursor.getPrev(store);
       checkNotNull(currentChainCursor, "Attempt to follow an orphan chain");
     } else {
       newChainCursor = newChainCursor.getPrev(store);
       checkNotNull(newChainCursor, "Attempt to follow an orphan chain");
     }
   }
   return currentChainCursor;
 }
예제 #5
0
 /**
  * Returns the set of contiguous blocks between 'higher' and 'lower'. Higher is included, lower is
  * not.
  */
 private List<StoredBlock> getPartialChain(StoredBlock higher, StoredBlock lower)
     throws BlockStoreException {
   assert higher.getHeight() > lower.getHeight();
   LinkedList<StoredBlock> results = new LinkedList<StoredBlock>();
   StoredBlock cursor = higher;
   while (true) {
     results.add(cursor);
     cursor = cursor.getPrev(blockStore);
     assert cursor != null : "Ran off the end of the chain";
     if (cursor.equals(lower)) break;
   }
   return results;
 }
 /**
  * Called as part of connecting a block when the new block results in a different chain having
  * higher total work.
  *
  * <p>if (shouldVerifyTransactions) Either newChainHead needs to be in the block store as a
  * FullStoredBlock, or (block != null && block.transactions != null)
  */
 private void handleNewBestChain(
     StoredBlock storedPrev, StoredBlock newChainHead, Block block, boolean expensiveChecks)
     throws BlockStoreException, VerificationException, PrunedException {
   checkState(lock.isHeldByCurrentThread());
   // This chain has overtaken the one we currently believe is best. Reorganize is required.
   //
   // Firstly, calculate the block at which the chain diverged. We only need to examine the
   // chain from beyond this block to find differences.
   StoredBlock head = getChainHead();
   final StoredBlock splitPoint = findSplit(newChainHead, head, blockStore);
   log.info("Re-organize after split at height {}", splitPoint.getHeight());
   log.info("Old chain head: {}", head.getHeader().getHashAsString());
   log.info("New chain head: {}", newChainHead.getHeader().getHashAsString());
   log.info("Split at block: {}", splitPoint.getHeader().getHashAsString());
   // Then build a list of all blocks in the old part of the chain and the new part.
   final LinkedList<StoredBlock> oldBlocks = getPartialChain(head, splitPoint, blockStore);
   final LinkedList<StoredBlock> newBlocks = getPartialChain(newChainHead, splitPoint, blockStore);
   // Disconnect each transaction in the previous main chain that is no longer in the new main
   // chain
   StoredBlock storedNewHead = splitPoint;
   if (shouldVerifyTransactions()) {
     for (StoredBlock oldBlock : oldBlocks) {
       try {
         disconnectTransactions(oldBlock);
       } catch (PrunedException e) {
         // We threw away the data we need to re-org this deep! We need to go back to a peer with
         // full
         // block contents and ask them for the relevant data then rebuild the indexs. Or we could
         // just
         // give up and ask the human operator to help get us unstuck (eg, rescan from the genesis
         // block).
         // TODO: Retry adding this block when we get a block with hash e.getHash()
         throw e;
       }
     }
     StoredBlock cursor;
     // Walk in ascending chronological order.
     for (Iterator<StoredBlock> it = newBlocks.descendingIterator(); it.hasNext(); ) {
       cursor = it.next();
       Block cursorBlock = cursor.getHeader();
       if (expensiveChecks
           && cursorBlock.getTimeSeconds()
               <= getMedianTimestampOfRecentBlocks(cursor.getPrev(blockStore), blockStore))
         throw new VerificationException("Block's timestamp is too early during reorg");
       TransactionOutputChanges txOutChanges;
       if (cursor != newChainHead || block == null) txOutChanges = connectTransactions(cursor);
       else txOutChanges = connectTransactions(newChainHead.getHeight(), block);
       storedNewHead = addToBlockStore(storedNewHead, cursorBlock.cloneAsHeader(), txOutChanges);
     }
   } else {
     // (Finally) write block to block store
     storedNewHead = addToBlockStore(storedPrev, newChainHead.getHeader());
   }
   // Now inform the listeners. This is necessary so the set of currently active transactions (that
   // we can spend)
   // can be updated to take into account the re-organize. We might also have received new coins we
   // didn't have
   // before and our previous spends might have been undone.
   for (final ListenerRegistration<BlockChainListener> registration : listeners) {
     if (registration.executor == Threading.SAME_THREAD) {
       // Short circuit the executor so we can propagate any exceptions.
       // TODO: Do we really need to do this or should it be irrelevant?
       registration.listener.reorganize(splitPoint, oldBlocks, newBlocks);
     } else {
       registration.executor.execute(
           new Runnable() {
             @Override
             public void run() {
               try {
                 registration.listener.reorganize(splitPoint, oldBlocks, newBlocks);
               } catch (VerificationException e) {
                 log.error("Block chain listener threw exception during reorg", e);
               }
             }
           });
     }
   }
   // Update the pointer to the best known block.
   setChainHead(storedNewHead);
 }