Beispiel #1
0
  public void checkConsistency() throws com.google.bitcoin.store.BlockStoreException {
    StoredBlock head = block_store.getChainHead();

    StoredBlock curr_block = head;

    Sha256Hash genisis_hash = params.getGenesisBlock().getHash();
    int checked = 0;

    while (true) {
      Sha256Hash curr_hash = curr_block.getHeader().getHash();

      if (curr_block.getHeight() % 10000 == 0) {
        System.out.println("Block: " + curr_block.getHeight());
      }
      if (!file_db.getBlockMap().containsKey(curr_hash)) {
        throw new RuntimeException("Missing block: " + curr_hash);
      }
      checked++;
      // if (checked > 20) return;

      if (curr_hash.equals(genisis_hash)) return;

      curr_block = curr_block.getPrev(block_store);
    }
  }
 @Override
 protected StoredBlock addToBlockStore(
     StoredBlock storedPrev, Block header, TransactionOutputChanges txOutChanges)
     throws BlockStoreException, VerificationException {
   StoredBlock newBlock = storedPrev.build(header);
   blockStore.put(newBlock, new StoredUndoableBlock(newBlock.getHeader().getHash(), txOutChanges));
   return newBlock;
 }
 @Override
 protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block block)
     throws BlockStoreException, VerificationException {
   StoredBlock newBlock = storedPrev.build(block);
   blockStore.put(
       newBlock, new StoredUndoableBlock(newBlock.getHeader().getHash(), block.transactions));
   return newBlock;
 }
    @Override
    public View getView(final int position, final View convertView, final ViewGroup parent) {
      final ViewGroup row;
      if (convertView == null)
        row = (ViewGroup) getLayoutInflater(null).inflate(R.layout.block_row, null);
      else row = (ViewGroup) convertView;

      final StoredBlock storedBlock = getItem(position);
      final Block header = storedBlock.getHeader();

      final TextView rowHeight = (TextView) row.findViewById(R.id.block_list_row_height);
      final int height = storedBlock.getHeight();
      rowHeight.setText(Integer.toString(height));

      final TextView rowTime = (TextView) row.findViewById(R.id.block_list_row_time);
      final long timeMs = header.getTimeSeconds() * DateUtils.SECOND_IN_MILLIS;
      rowTime.setText(
          DateUtils.getRelativeDateTimeString(
              activity, timeMs, DateUtils.MINUTE_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, 0));

      final TextView rowHash = (TextView) row.findViewById(R.id.block_list_row_hash);
      rowHash.setText(WalletUtils.formatHash(null, header.getHashAsString(), 8, 0, ' '));

      final int transactionChildCount = row.getChildCount() - ROW_BASE_CHILD_COUNT;
      int iTransactionView = 0;

      if (transactions != null) {
        final String precision =
            prefs.getString(
                Constants.PREFS_KEY_BTC_PRECISION, Constants.PREFS_DEFAULT_BTC_PRECISION);
        final int btcPrecision = precision.charAt(0) - '0';
        final int btcShift = precision.length() == 3 ? precision.charAt(2) - '0' : 0;

        transactionsAdapter.setPrecision(btcPrecision, btcShift);

        for (final Transaction tx : transactions) {
          if (tx.getAppearsInHashes().containsKey(header.getHash())) {
            final View view;
            if (iTransactionView < transactionChildCount) {
              view = row.getChildAt(ROW_INSERT_INDEX + iTransactionView);
            } else {
              view = getLayoutInflater(null).inflate(R.layout.transaction_row_oneline, null);
              row.addView(view, ROW_INSERT_INDEX + iTransactionView);
            }

            transactionsAdapter.bindView(view, tx);

            iTransactionView++;
          }
        }
      }

      final int leftoverTransactionViews = transactionChildCount - iTransactionView;
      if (leftoverTransactionViews > 0)
        row.removeViews(ROW_INSERT_INDEX + iTransactionView, leftoverTransactionViews);

      return row;
    }
  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;
  }
 /**
  * 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;
 }
Beispiel #7
0
 public synchronized void put(StoredBlock block) throws BlockStoreException {
   try {
     Sha256Hash hash = block.getHeader().getHash();
     assert blockMap.get(hash) == null : "Attempt to insert duplicate";
     // Append to the end of the file. The other fields in StoredBlock will be recalculated when
     // it's reloaded.
     byte[] bytes = block.getHeader().bitcoinSerialize();
     stream.write(bytes);
     stream.flush();
     blockMap.put(hash, block);
   } catch (IOException e) {
     throw new BlockStoreException(e);
   }
 }
Beispiel #8
0
 public synchronized void setChainHead(StoredBlock chainHead) throws BlockStoreException {
   try {
     this.chainHead = chainHead.getHeader().getHash();
     // Write out new hash to the first 32 bytes of the file past one (first byte is version
     // number).
     stream.getChannel().write(ByteBuffer.wrap(this.chainHead.getBytes()), 1);
   } catch (IOException e) {
     throw new BlockStoreException(e);
   }
 }
 /**
  * This is broken for blocks that do not pass BIP30, so all BIP30-failing blocks which are allowed
  * to fail BIP30 must be checkpointed.
  */
 @Override
 protected void disconnectTransactions(StoredBlock oldBlock)
     throws PrunedException, BlockStoreException {
   checkState(lock.isHeldByCurrentThread());
   blockStore.beginDatabaseBatchWrite();
   try {
     StoredUndoableBlock undoBlock = blockStore.getUndoBlock(oldBlock.getHeader().getHash());
     if (undoBlock == null) throw new PrunedException(oldBlock.getHeader().getHash());
     TransactionOutputChanges txOutChanges = undoBlock.getTxOutChanges();
     for (StoredTransactionOutput out : txOutChanges.txOutsSpent)
       blockStore.addUnspentTransactionOutput(out);
     for (StoredTransactionOutput out : txOutChanges.txOutsCreated)
       blockStore.removeUnspentTransactionOutput(out);
   } catch (PrunedException e) {
     blockStore.abortDatabaseBatchWrite();
     throw e;
   } catch (BlockStoreException e) {
     blockStore.abortDatabaseBatchWrite();
     throw e;
   }
 }
  /**
   * 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;
  }
  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);
      }

      // We may not have any transactions if we received only a header. That never happens today but
      // will in
      // future when getheaders is used as an optimization.
      if (newTransactions != null) {
        sendTransactionsToWallet(newStoredBlock, NewBlockType.SIDE_CHAIN, newTransactions);
      }

      if (haveNewBestChain) handleNewBestChain(newStoredBlock);
    }
  }
 /**
  * Called as part of connecting a block when the new block results in a different chain having
  * higher total work.
  */
 private void handleNewBestChain(StoredBlock newChainHead)
     throws BlockStoreException, VerificationException {
   // 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 splitPoint = findSplit(newChainHead, chainHead);
   log.info("Re-organize after split at height {}", splitPoint.getHeight());
   log.info("Old chain head: {}", chainHead.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.
   List<StoredBlock> oldBlocks = getPartialChain(chainHead, splitPoint);
   List<StoredBlock> newBlocks = getPartialChain(newChainHead, splitPoint);
   // Now inform the wallet. 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.
   wallet.reorganize(oldBlocks, newBlocks);
   // Update the pointer to the best known block.
   setChainHead(newChainHead);
 }
Beispiel #13
0
 private void createNewStore(NetworkParameters params, File file) throws BlockStoreException {
   // Create a new block store if the file wasn't found or anything went wrong whilst reading.
   blockMap.clear();
   try {
     stream = new FileOutputStream(file, false); // Do not append, create fresh.
     stream.write(1); // Version.
   } catch (IOException e1) {
     // We could not load a block store nor could we create a new one!
     throw new BlockStoreException(e1);
   }
   try {
     // Set up the genesis block. When we start out fresh, it is by definition the top of the
     // chain.
     Block genesis = params.genesisBlock.cloneAsHeader();
     StoredBlock storedGenesis = new StoredBlock(genesis, genesis.getWork(), 0);
     this.chainHead = storedGenesis.getHeader().getHash();
     stream.write(this.chainHead.getBytes());
     put(storedGenesis);
   } catch (VerificationException e1) {
     throw new RuntimeException(e1); // Cannot happen.
   } catch (IOException e) {
     throw new BlockStoreException(e);
   }
 }
 /**
  * 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;
 }
  @Test
  public void testStorage() throws Exception {
    File temp = File.createTempFile("bitcoinj-test", null, null);
    System.out.println(temp.getAbsolutePath());
    temp.deleteOnExit();

    NetworkParameters params = NetworkParameters.unitTests();
    Address to = new ECKey().toAddress(params);
    BoundedOverheadBlockStore store = new BoundedOverheadBlockStore(params, temp);
    // Check the first block in a new store is the genesis block.
    StoredBlock genesis = store.getChainHead();
    assertEquals(params.genesisBlock, genesis.getHeader());

    // Build a new block.
    StoredBlock b1 = genesis.build(genesis.getHeader().createNextBlock(to).cloneAsHeader());
    store.put(b1);
    store.setChainHead(b1);
    // Check we can get it back out again if we rebuild the store object.
    store = new BoundedOverheadBlockStore(params, temp);
    StoredBlock b2 = store.get(b1.getHeader().getHash());
    assertEquals(b1, b2);
    // Check the chain head was stored correctly also.
    assertEquals(b1, store.getChainHead());
  }
  @Override
  /** Used during reorgs to connect a block previously on a fork */
  protected synchronized TransactionOutputChanges connectTransactions(StoredBlock newBlock)
      throws VerificationException, BlockStoreException, PrunedException {
    checkState(lock.isHeldByCurrentThread());
    if (!params.passesCheckpoint(newBlock.getHeight(), newBlock.getHeader().getHash()))
      throw new VerificationException("Block failed checkpoint lockin at " + newBlock.getHeight());

    blockStore.beginDatabaseBatchWrite();
    StoredUndoableBlock block = blockStore.getUndoBlock(newBlock.getHeader().getHash());
    if (block == null) {
      // We're trying to re-org too deep and the data needed has been deleted.
      blockStore.abortDatabaseBatchWrite();
      throw new PrunedException(newBlock.getHeader().getHash());
    }
    TransactionOutputChanges txOutChanges;
    try {
      List<Transaction> transactions = block.getTransactions();
      if (transactions != null) {
        LinkedList<StoredTransactionOutput> txOutsSpent = new LinkedList<StoredTransactionOutput>();
        LinkedList<StoredTransactionOutput> txOutsCreated =
            new LinkedList<StoredTransactionOutput>();
        long sigOps = 0;
        final boolean enforcePayToScriptHash =
            newBlock.getHeader().getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME;
        if (!params.isCheckpoint(newBlock.getHeight())) {
          for (Transaction tx : transactions) {
            Sha256Hash hash = tx.getHash();
            if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size()))
              throw new VerificationException("Block failed BIP30 test!");
          }
        }
        Coin totalFees = Coin.ZERO;
        Coin coinbaseValue = null;

        if (scriptVerificationExecutor.isShutdown())
          scriptVerificationExecutor =
              Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        List<Future<VerificationException>> listScriptVerificationResults =
            new ArrayList<Future<VerificationException>>(transactions.size());
        for (final Transaction tx : transactions) {
          boolean isCoinBase = tx.isCoinBase();
          Coin valueIn = Coin.ZERO;
          Coin valueOut = Coin.ZERO;
          final List<Script> prevOutScripts = new LinkedList<Script>();
          if (!isCoinBase) {
            for (int index = 0; index < tx.getInputs().size(); index++) {
              final TransactionInput in = tx.getInputs().get(index);
              final StoredTransactionOutput prevOut =
                  blockStore.getTransactionOutput(
                      in.getOutpoint().getHash(), in.getOutpoint().getIndex());
              if (prevOut == null)
                throw new VerificationException(
                    "Attempted spend of a non-existent or already spent output!");
              if (newBlock.getHeight() - prevOut.getHeight() < params.getSpendableCoinbaseDepth())
                throw new VerificationException(
                    "Tried to spend coinbase at depth "
                        + (newBlock.getHeight() - prevOut.getHeight()));
              valueIn = valueIn.add(prevOut.getValue());
              if (enforcePayToScriptHash) {
                Script script = new Script(prevOut.getScriptBytes());
                if (script.isPayToScriptHash())
                  sigOps += Script.getP2SHSigOpCount(in.getScriptBytes());
                if (sigOps > Block.MAX_BLOCK_SIGOPS)
                  throw new VerificationException("Too many P2SH SigOps in block");
              }

              prevOutScripts.add(new Script(prevOut.getScriptBytes()));

              blockStore.removeUnspentTransactionOutput(prevOut);
              txOutsSpent.add(prevOut);
            }
          }
          Sha256Hash hash = tx.getHash();
          for (TransactionOutput out : tx.getOutputs()) {
            valueOut = valueOut.add(out.getValue());
            StoredTransactionOutput newOut =
                new StoredTransactionOutput(
                    hash,
                    out.getIndex(),
                    out.getValue(),
                    newBlock.getHeight(),
                    isCoinBase,
                    out.getScriptBytes());
            blockStore.addUnspentTransactionOutput(newOut);
            txOutsCreated.add(newOut);
          }
          // All values were already checked for being non-negative (as it is verified in
          // Transaction.verify())
          // but we check again here just for defence in depth. Transactions with zero output value
          // are OK.
          if (valueOut.signum() < 0 || valueOut.compareTo(NetworkParameters.MAX_MONEY) > 0)
            throw new VerificationException("Transaction output value out of range");
          if (isCoinBase) {
            coinbaseValue = valueOut;
          } else {
            if (valueIn.compareTo(valueOut) < 0
                || valueIn.compareTo(NetworkParameters.MAX_MONEY) > 0)
              throw new VerificationException("Transaction input value out of range");
            totalFees = totalFees.add(valueIn.subtract(valueOut));
          }

          if (!isCoinBase) {
            // Because correctlySpends modifies transactions, this must come after we are done with
            // tx
            FutureTask<VerificationException> future =
                new FutureTask<VerificationException>(
                    new Verifier(tx, prevOutScripts, enforcePayToScriptHash));
            scriptVerificationExecutor.execute(future);
            listScriptVerificationResults.add(future);
          }
        }
        if (totalFees.compareTo(NetworkParameters.MAX_MONEY) > 0
            || newBlock
                    .getHeader()
                    .getBlockInflation(newBlock.getHeight())
                    .add(totalFees)
                    .compareTo(coinbaseValue)
                < 0) throw new VerificationException("Transaction fees out of range");
        txOutChanges = new TransactionOutputChanges(txOutsCreated, txOutsSpent);
        for (Future<VerificationException> future : listScriptVerificationResults) {
          VerificationException e;
          try {
            e = future.get();
          } catch (InterruptedException thrownE) {
            throw new RuntimeException(thrownE); // Shouldn't happen
          } catch (ExecutionException thrownE) {
            log.error("Script.correctlySpends threw a non-normal exception: " + thrownE.getCause());
            throw new VerificationException(
                "Bug in Script.correctlySpends, likely script malformed in some new and interesting way.",
                thrownE);
          }
          if (e != null) throw e;
        }
      } else {
        txOutChanges = block.getTxOutChanges();
        if (!params.isCheckpoint(newBlock.getHeight()))
          for (StoredTransactionOutput out : txOutChanges.txOutsCreated) {
            Sha256Hash hash = out.getHash();
            if (blockStore.getTransactionOutput(hash, out.getIndex()) != null)
              throw new VerificationException("Block failed BIP30 test!");
          }
        for (StoredTransactionOutput out : txOutChanges.txOutsCreated)
          blockStore.addUnspentTransactionOutput(out);
        for (StoredTransactionOutput out : txOutChanges.txOutsSpent)
          blockStore.removeUnspentTransactionOutput(out);
      }
    } catch (VerificationException e) {
      scriptVerificationExecutor.shutdownNow();
      blockStore.abortDatabaseBatchWrite();
      throw e;
    } catch (BlockStoreException e) {
      scriptVerificationExecutor.shutdownNow();
      blockStore.abortDatabaseBatchWrite();
      throw e;
    }
    return txOutChanges;
  }
  public static void main(String[] args) throws Exception {
    BriefLogFormatter.init();

    // Sorted map of UNIX time of block to StoredBlock object.
    final TreeMap<Integer, StoredBlock> checkpoints = new TreeMap<Integer, StoredBlock>();

    // Configure bitcoinj to fetch only headers, not save them to disk, connect to a local fully
    // synced/validated
    // node and to save block headers that are on interval boundaries, as long as they are <1 month
    // old.
    final BlockStore store = new MemoryBlockStore(PARAMS);
    final BlockChain chain = new BlockChain(PARAMS, store);
    final PeerGroup peerGroup = new PeerGroup(PARAMS, chain);
    peerGroup.addAddress(InetAddress.getLocalHost());
    // peerGroup.addAddress(InetAddress.getByName("69.164.198.161"));
    long now = new Date().getTime() / 1000;
    peerGroup.setFastCatchupTimeSecs(now);

    final long oneMonthAgo = now - (86400 * 2);

    chain.addListener(
        new AbstractBlockChainListener() {
          @Override
          public void notifyNewBestBlock(StoredBlock block) throws VerificationException {
            int height = block.getHeight();

            if (height % CoinDefinition.getIntervalCheckpoints() == 0
                && block.getHeader().getTimeSeconds() <= oneMonthAgo) {

              //               if (height % PARAMS.getInterval() == 0 &&
              // block.getHeader().getTimeSeconds() <= oneMonthAgo) {

              System.out.println(
                  String.format(
                      "Checkpointing block %s at height %d",
                      block.getHeader().getHash(), block.getHeight()));
              checkpoints.put(height, block);
            }
          }
        },
        Threading.SAME_THREAD);

    peerGroup.startAndWait();
    peerGroup.downloadBlockChain();

    checkState(checkpoints.size() > 0);

    // Write checkpoint data out.
    final FileOutputStream fileOutputStream = new FileOutputStream(CHECKPOINTS_FILE, false);
    MessageDigest digest = MessageDigest.getInstance("SHA-256");
    final DigestOutputStream digestOutputStream = new DigestOutputStream(fileOutputStream, digest);
    digestOutputStream.on(false);
    final DataOutputStream dataOutputStream = new DataOutputStream(digestOutputStream);
    dataOutputStream.writeBytes("CHECKPOINTS 1");
    dataOutputStream.writeInt(0); // Number of signatures to read. Do this later.
    digestOutputStream.on(true);
    dataOutputStream.writeInt(checkpoints.size());
    ByteBuffer buffer = ByteBuffer.allocate(StoredBlock.COMPACT_SERIALIZED_SIZE);
    for (StoredBlock block : checkpoints.values()) {
      block.serializeCompact(buffer);
      dataOutputStream.write(buffer.array());
      buffer.position(0);
    }
    dataOutputStream.close();
    Sha256Hash checkpointsHash = new Sha256Hash(digest.digest());
    System.out.println("Hash of checkpoints data is " + checkpointsHash);
    digestOutputStream.close();
    fileOutputStream.close();

    peerGroup.stopAndWait();
    store.close();

    // Sanity check the created file.
    CheckpointManager manager =
        new CheckpointManager(PARAMS, new FileInputStream(CHECKPOINTS_FILE));
    checkState(manager.numCheckpoints() == checkpoints.size());

    /*if (PARAMS.getId() == NetworkParameters.ID_MAINNET) {
        StoredBlock test = manager.getCheckpointBefore(1390500000); // Thu Jan 23 19:00:00 CET 2014
        checkState(test.getHeight() == 280224);
        checkState(test.getHeader().getHashAsString()
                .equals("00000000000000000b5d59a15f831e1c45cb688a4db6b0a60054d49a9997fa34"));
    } else if (PARAMS.getId() == NetworkParameters.ID_TESTNET) {
        StoredBlock test = manager.getCheckpointBefore(1390500000); // Thu Jan 23 19:00:00 CET 2014
        checkState(test.getHeight() == 167328);
        checkState(test.getHeader().getHashAsString()
                .equals("0000000000035ae7d5025c2538067fe7adb1cf5d5d9c31b024137d9090ed13a9"));
    }*/

    System.out.println("Checkpoints written to '" + CHECKPOINTS_FILE.getCanonicalPath() + "'.");
  }
Beispiel #18
0
 private void load(File file) throws IOException, BlockStoreException {
   log.info("Reading block store from {}", file);
   InputStream input = null;
   try {
     input = new BufferedInputStream(new FileInputStream(file));
     // Read a version byte.
     int version = input.read();
     if (version == -1) {
       // No such file or the file was empty.
       throw new FileNotFoundException(file.getName() + " does not exist or is empty");
     }
     if (version != 1) {
       throw new BlockStoreException("Bad version number: " + version);
     }
     // Chain head pointer is the first thing in the file.
     byte[] chainHeadHash = new byte[32];
     if (input.read(chainHeadHash) < chainHeadHash.length)
       throw new BlockStoreException("Truncated block store: cannot read chain head hash");
     this.chainHead = new Sha256Hash(chainHeadHash);
     log.info("Read chain head from disk: {}", this.chainHead);
     long now = System.currentTimeMillis();
     // Rest of file is raw block headers.
     byte[] headerBytes = new byte[Block.HEADER_SIZE];
     try {
       while (true) {
         // Read a block from disk.
         if (input.read(headerBytes) < 80) {
           // End of file.
           break;
         }
         // Parse it.
         Block b = new Block(params, headerBytes);
         // Look up the previous block it connects to.
         StoredBlock prev = get(b.getPrevBlockHash());
         StoredBlock s;
         if (prev == null) {
           // First block in the stored chain has to be treated specially.
           if (b.equals(params.genesisBlock)) {
             s =
                 new StoredBlock(
                     params.genesisBlock.cloneAsHeader(), params.genesisBlock.getWork(), 0);
           } else {
             throw new BlockStoreException(
                 "Could not connect "
                     + b.getHash().toString()
                     + " to "
                     + b.getPrevBlockHash().toString());
           }
         } else {
           // Don't try to verify the genesis block to avoid upsetting the unit tests.
           b.verifyHeader();
           // Calculate its height and total chain work.
           s = prev.build(b);
         }
         // Save in memory.
         blockMap.put(b.getHash(), s);
       }
     } catch (ProtocolException e) {
       // Corrupted file.
       throw new BlockStoreException(e);
     } catch (VerificationException e) {
       // Should not be able to happen unless the file contains bad blocks.
       throw new BlockStoreException(e);
     }
     long elapsed = System.currentTimeMillis() - now;
     log.info("Block chain read complete in {}ms", elapsed);
   } finally {
     if (input != null) input.close();
   }
 }
  /** Throws an exception if the blocks difficulty is not correct. */
  private void checkDifficultyTransitions(StoredBlock storedPrev, StoredBlock storedNext)
      throws BlockStoreException, VerificationException {
    Block prev = storedPrev.getHeader();
    Block next = storedNext.getHeader();
    // Is this supposed to be a difficulty transition point?
    if ((storedPrev.getHeight() + 1) % params.interval != 0) {
      // No ... so check the difficulty didn't actually change.
      if (next.getDifficultyTarget() != prev.getDifficultyTarget())
        throw new VerificationException(
            "Unexpected change in difficulty at height "
                + storedPrev.getHeight()
                + ": "
                + Long.toHexString(next.getDifficultyTarget())
                + " vs "
                + Long.toHexString(prev.getDifficultyTarget()));
      return;
    }

    // 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();
    StoredBlock cursor = blockStore.get(prev.getHash());
    for (int i = 0; i < params.interval - 1; 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 = blockStore.get(cursor.getHeader().getPrevBlockHash());
    }
    log.info("Difficulty transition traversal took {}msec", System.currentTimeMillis() - now);

    Block blockIntervalAgo = cursor.getHeader();
    int timespan = (int) (prev.getTime() - blockIntervalAgo.getTime());
    // Limit the adjustment step.
    if (timespan < params.targetTimespan / 4) timespan = params.targetTimespan / 4;
    if (timespan > params.targetTimespan * 4) timespan = params.targetTimespan * 4;

    BigInteger newDifficulty = Utils.decodeCompactBits(blockIntervalAgo.getDifficultyTarget());
    newDifficulty = newDifficulty.multiply(BigInteger.valueOf(timespan));
    newDifficulty = newDifficulty.divide(BigInteger.valueOf(params.targetTimespan));

    if (newDifficulty.compareTo(params.proofOfWorkLimit) > 0) {
      log.warn("Difficulty hit proof of work limit: {}", newDifficulty.toString(16));
      newDifficulty = params.proofOfWorkLimit;
    }

    int accuracyBytes = (int) (next.getDifficultyTarget() >>> 24) - 3;
    BigInteger receivedDifficulty = next.getDifficultyTargetAsInteger();

    // The calculated difficulty is to a higher precision than received, so reduce here.
    BigInteger mask = BigInteger.valueOf(0xFFFFFFL).shiftLeft(accuracyBytes * 8);
    newDifficulty = newDifficulty.and(mask);

    if (newDifficulty.compareTo(receivedDifficulty) != 0)
      throw new VerificationException(
          "Network provided difficulty bits do not match what was calculated: "
              + receivedDifficulty.toString(16)
              + " vs "
              + newDifficulty.toString(16));
  }