@Test
  public void testGetOpenTransactionOutputs() throws Exception {
    final int UNDOABLE_BLOCKS_STORED = 10;
    store = createStore(params, UNDOABLE_BLOCKS_STORED);
    chain = new FullPrunedBlockChain(params, store);

    // Check that we aren't accidentally leaving any references
    // to the full StoredUndoableBlock's lying around (ie memory leaks)
    ECKey outKey = new ECKey();
    int height = 1;

    // Build some blocks on genesis block to create a spendable output
    Block rollingBlock =
        params
            .getGenesisBlock()
            .createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
    chain.add(rollingBlock);
    Transaction transaction = rollingBlock.getTransactions().get(0);
    TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, transaction.getHash());
    byte[] spendableOutputScriptPubKey = transaction.getOutputs().get(0).getScriptBytes();
    for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
      rollingBlock =
          rollingBlock.createNextBlockWithCoinbase(
              Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
      chain.add(rollingBlock);
    }
    rollingBlock = rollingBlock.createNextBlock(null);

    // Create bitcoin spend of 1 BTC.
    ECKey toKey = new ECKey();
    Coin amount = Coin.valueOf(100000000);
    Address address = new Address(params, toKey.getPubKeyHash());
    Coin totalAmount = Coin.ZERO;

    Transaction t = new Transaction(params);
    t.addOutput(new TransactionOutput(params, t, amount, toKey));
    t.addSignedInput(spendableOutput, new Script(spendableOutputScriptPubKey), outKey);
    rollingBlock.addTransaction(t);
    rollingBlock.solve();
    chain.add(rollingBlock);
    totalAmount = totalAmount.add(amount);

    List<UTXO> outputs = store.getOpenTransactionOutputs(Lists.newArrayList(address));
    assertNotNull(outputs);
    assertEquals("Wrong Number of Outputs", 1, outputs.size());
    UTXO output = outputs.get(0);
    assertEquals("The address is not equal", address.toString(), output.getAddress());
    assertEquals("The amount is not equal", totalAmount, output.getValue());

    outputs = null;
    output = null;
    try {
      store.close();
    } catch (Exception e) {
    }
  }
  /** Test that if the block height is missing from coinbase of a version 2 block, it's rejected. */
  @Test
  public void missingHeightFromCoinbase() throws Exception {
    final int UNDOABLE_BLOCKS_STORED = params.getMajorityEnforceBlockUpgrade() + 1;
    store = createStore(params, UNDOABLE_BLOCKS_STORED);
    try {
      chain = new FullPrunedBlockChain(params, store);
      ECKey outKey = new ECKey();
      int height = 1;
      Block chainHead = params.getGenesisBlock();

      // Build some blocks on genesis block to create a spendable output.

      // Put in just enough v1 blocks to stop the v2 blocks from forming a majority
      for (height = 1;
          height <= (params.getMajorityWindow() - params.getMajorityEnforceBlockUpgrade());
          height++) {
        chainHead =
            chainHead.createNextBlockWithCoinbase(
                Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height);
        chain.add(chainHead);
      }

      // Fill the rest of the window in with v2 blocks
      for (; height < params.getMajorityWindow(); height++) {
        chainHead =
            chainHead.createNextBlockWithCoinbase(
                Block.BLOCK_VERSION_BIP34, outKey.getPubKey(), height);
        chain.add(chainHead);
      }
      // Throw a broken v2 block in before we have a supermajority to enable
      // enforcement, which should validate as-is
      chainHead =
          chainHead.createNextBlockWithCoinbase(
              Block.BLOCK_VERSION_BIP34, outKey.getPubKey(), height * 2);
      chain.add(chainHead);
      height++;

      // Trying to add a broken v2 block should now result in rejection as
      // we have a v2 supermajority
      thrown.expect(VerificationException.CoinbaseHeightMismatch.class);
      chainHead =
          chainHead.createNextBlockWithCoinbase(
              Block.BLOCK_VERSION_BIP34, outKey.getPubKey(), height * 2);
      chain.add(chainHead);
    } catch (final VerificationException ex) {
      throw (Exception) ex.getCause();
    } finally {
      try {
        store.close();
      } catch (Exception e) {
        // Catch and drop any exception so a break mid-test doesn't result
        // in a new exception being thrown and the original lost
      }
    }
  }
  @Test
  public void testGeneratedChain() throws Exception {
    // Tests various test cases from FullBlockTestGenerator
    FullBlockTestGenerator generator = new FullBlockTestGenerator(params);
    RuleList blockList = generator.getBlocksToTest(false, false, null);

    store = createStore(params, blockList.maximumReorgBlockCount);
    chain = new FullPrunedBlockChain(params, store);

    for (Rule rule : blockList.list) {
      if (!(rule instanceof FullBlockTestGenerator.BlockAndValidity)) continue;
      FullBlockTestGenerator.BlockAndValidity block =
          (FullBlockTestGenerator.BlockAndValidity) rule;
      log.info("Testing rule " + block.ruleName + " with block hash " + block.block.getHash());
      boolean threw = false;
      try {
        if (chain.add(block.block) != block.connects) {
          log.error("Block didn't match connects flag on block " + block.ruleName);
          fail();
        }
      } catch (VerificationException e) {
        threw = true;
        if (!block.throwsException) {
          log.error("Block didn't match throws flag on block " + block.ruleName);
          throw e;
        }
        if (block.connects) {
          log.error("Block didn't match connects flag on block " + block.ruleName);
          fail();
        }
      }
      if (!threw && block.throwsException) {
        log.error("Block didn't match throws flag on block " + block.ruleName);
        fail();
      }
      if (!chain.getChainHead().getHeader().getHash().equals(block.hashChainTipAfterBlock)) {
        log.error("New block head didn't match the correct value after block " + block.ruleName);
        fail();
      }
      if (chain.getChainHead().getHeight() != block.heightAfterBlock) {
        log.error("New block head didn't match the correct height after block " + block.ruleName);
        fail();
      }
    }
    try {
      store.close();
    } catch (Exception e) {
    }
  }
  @Test
  public void skipScripts() throws Exception {
    store = createStore(params, 10);
    chain = new FullPrunedBlockChain(params, store);

    // Check that we aren't accidentally leaving any references
    // to the full StoredUndoableBlock's lying around (ie memory leaks)

    ECKey outKey = new ECKey();
    int height = 1;

    // Build some blocks on genesis block to create a spendable output
    Block rollingBlock =
        params
            .getGenesisBlock()
            .createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
    chain.add(rollingBlock);
    TransactionOutput spendableOutput = rollingBlock.getTransactions().get(0).getOutput(0);
    for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
      rollingBlock =
          rollingBlock.createNextBlockWithCoinbase(
              Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
      chain.add(rollingBlock);
    }

    rollingBlock = rollingBlock.createNextBlock(null);
    Transaction t = new Transaction(params);
    t.addOutput(new TransactionOutput(params, t, FIFTY_COINS, new byte[] {}));
    TransactionInput input = t.addInput(spendableOutput);
    // Invalid script.
    input.clearScriptBytes();
    rollingBlock.addTransaction(t);
    rollingBlock.solve();
    chain.setRunScripts(false);
    try {
      chain.add(rollingBlock);
    } catch (VerificationException e) {
      fail();
    }
    try {
      store.close();
    } catch (Exception e) {
    }
  }
  @Test
  public void testFirst100KBlocks() throws Exception {
    NetworkParameters params = MainNetParams.get();
    Context context = new Context(params);
    File blockFile = new File(getClass().getResource("first-100k-blocks.dat").getFile());
    BlockFileLoader loader = new BlockFileLoader(params, Arrays.asList(blockFile));

    store = createStore(params, 10);
    resetStore(store);
    chain = new FullPrunedBlockChain(context, store);
    for (Block block : loader) chain.add(block);
    try {
      store.close();
    } catch (Exception e) {
    }
  }
  @Test
  public void testUTXOProviderWithWallet() throws Exception {
    final int UNDOABLE_BLOCKS_STORED = 10;
    store = createStore(params, UNDOABLE_BLOCKS_STORED);
    chain = new FullPrunedBlockChain(params, store);

    // Check that we aren't accidentally leaving any references
    // to the full StoredUndoableBlock's lying around (ie memory leaks)
    ECKey outKey = new ECKey();
    int height = 1;

    // Build some blocks on genesis block to create a spendable output.
    Block rollingBlock =
        params
            .getGenesisBlock()
            .createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
    chain.add(rollingBlock);
    Transaction transaction = rollingBlock.getTransactions().get(0);
    TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, transaction.getHash());
    byte[] spendableOutputScriptPubKey = transaction.getOutputs().get(0).getScriptBytes();
    for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
      rollingBlock =
          rollingBlock.createNextBlockWithCoinbase(
              Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
      chain.add(rollingBlock);
    }
    rollingBlock = rollingBlock.createNextBlock(null);

    // Create 1 BTC spend to a key in this wallet (to ourselves).
    Wallet wallet = new Wallet(params);
    assertEquals(
        "Available balance is incorrect",
        Coin.ZERO,
        wallet.getBalance(Wallet.BalanceType.AVAILABLE));
    assertEquals(
        "Estimated balance is incorrect",
        Coin.ZERO,
        wallet.getBalance(Wallet.BalanceType.ESTIMATED));

    wallet.setUTXOProvider(store);
    ECKey toKey = wallet.freshReceiveKey();
    Coin amount = Coin.valueOf(100000000);

    Transaction t = new Transaction(params);
    t.addOutput(new TransactionOutput(params, t, amount, toKey));
    t.addSignedInput(spendableOutput, new Script(spendableOutputScriptPubKey), outKey);
    rollingBlock.addTransaction(t);
    rollingBlock.solve();
    chain.add(rollingBlock);

    // Create another spend of 1/2 the value of BTC we have available using the wallet (store coin
    // selector).
    ECKey toKey2 = new ECKey();
    Coin amount2 = amount.divide(2);
    Address address2 = new Address(params, toKey2.getPubKeyHash());
    Wallet.SendRequest req = Wallet.SendRequest.to(address2, amount2);
    wallet.completeTx(req);
    wallet.commitTx(req.tx);
    Coin fee = req.fee;

    // There should be one pending tx (our spend).
    assertEquals(
        "Wrong number of PENDING.4", 1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
    Coin totalPendingTxAmount = Coin.ZERO;
    for (Transaction tx : wallet.getPendingTransactions()) {
      totalPendingTxAmount = totalPendingTxAmount.add(tx.getValueSentToMe(wallet));
    }

    // The availbale balance should be the 0 (as we spent the 1 BTC that's pending) and estimated
    // should be 1/2 - fee BTC
    assertEquals(
        "Available balance is incorrect",
        Coin.ZERO,
        wallet.getBalance(Wallet.BalanceType.AVAILABLE));
    assertEquals(
        "Estimated balance is incorrect",
        amount2.subtract(fee),
        wallet.getBalance(Wallet.BalanceType.ESTIMATED));
    assertEquals("Pending tx amount is incorrect", amount2.subtract(fee), totalPendingTxAmount);
    try {
      store.close();
    } catch (Exception e) {
    }
  }
  @Test
  public void testFinalizedBlocks() throws Exception {
    final int UNDOABLE_BLOCKS_STORED = 10;
    store = createStore(params, UNDOABLE_BLOCKS_STORED);
    chain = new FullPrunedBlockChain(params, store);

    // Check that we aren't accidentally leaving any references
    // to the full StoredUndoableBlock's lying around (ie memory leaks)

    ECKey outKey = new ECKey();
    int height = 1;

    // Build some blocks on genesis block to create a spendable output
    Block rollingBlock =
        params
            .getGenesisBlock()
            .createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
    chain.add(rollingBlock);
    TransactionOutPoint spendableOutput =
        new TransactionOutPoint(params, 0, rollingBlock.getTransactions().get(0).getHash());
    byte[] spendableOutputScriptPubKey =
        rollingBlock.getTransactions().get(0).getOutputs().get(0).getScriptBytes();
    for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
      rollingBlock =
          rollingBlock.createNextBlockWithCoinbase(
              Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
      chain.add(rollingBlock);
    }

    WeakReference<UTXO> out =
        new WeakReference<UTXO>(
            store.getTransactionOutput(spendableOutput.getHash(), spendableOutput.getIndex()));
    rollingBlock = rollingBlock.createNextBlock(null);

    Transaction t = new Transaction(params);
    // Entirely invalid scriptPubKey
    t.addOutput(new TransactionOutput(params, t, FIFTY_COINS, new byte[] {}));
    t.addSignedInput(spendableOutput, new Script(spendableOutputScriptPubKey), outKey);
    rollingBlock.addTransaction(t);
    rollingBlock.solve();

    chain.add(rollingBlock);
    WeakReference<StoredUndoableBlock> undoBlock =
        new WeakReference<StoredUndoableBlock>(store.getUndoBlock(rollingBlock.getHash()));

    StoredUndoableBlock storedUndoableBlock = undoBlock.get();
    assertNotNull(storedUndoableBlock);
    assertNull(storedUndoableBlock.getTransactions());
    WeakReference<TransactionOutputChanges> changes =
        new WeakReference<TransactionOutputChanges>(storedUndoableBlock.getTxOutChanges());
    assertNotNull(changes.get());
    storedUndoableBlock = null; // Blank the reference so it can be GCd.

    // Create a chain longer than UNDOABLE_BLOCKS_STORED
    for (int i = 0; i < UNDOABLE_BLOCKS_STORED; i++) {
      rollingBlock = rollingBlock.createNextBlock(null);
      chain.add(rollingBlock);
    }
    // Try to get the garbage collector to run
    System.gc();
    assertNull(undoBlock.get());
    assertNull(changes.get());
    assertNull(out.get());
    try {
      store.close();
    } catch (Exception e) {
    }
  }