Example #1
0
 @Test
 public void testWork() throws Exception {
   BigInteger work = params.getGenesisBlock().getWork();
   // This number is printed by Bitcoin Core at startup as the calculated value of chainWork on
   // testnet:
   //
   // SetBestChain: new best=00000007199508e34a9f  height=0  work=536879104
   assertEquals(BigInteger.valueOf(536879104L), work);
 }
  @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
      }
    }
  }
Example #4
0
 @Test
 public void testUpdateLength() {
   NetworkParameters params = UnitTestParams.get();
   Block block =
       params
           .getGenesisBlock()
           .createNextBlockWithCoinbase(
               Block.BLOCK_VERSION_GENESIS, new ECKey().getPubKey(), Block.BLOCK_HEIGHT_GENESIS);
   assertEquals(block.bitcoinSerialize().length, block.length);
   final int origBlockLen = block.length;
   Transaction tx = new Transaction(params);
   // this is broken until the transaction has > 1 input + output (which is required anyway...)
   // assertTrue(tx.length == tx.bitcoinSerialize().length && tx.length == 8);
   byte[] outputScript = new byte[10];
   Arrays.fill(outputScript, (byte) ScriptOpCodes.OP_FALSE);
   tx.addOutput(new TransactionOutput(params, null, Coin.SATOSHI, outputScript));
   tx.addInput(
       new TransactionInput(
           params,
           null,
           new byte[] {(byte) ScriptOpCodes.OP_FALSE},
           new TransactionOutPoint(params, 0, Sha256Hash.of(new byte[] {1}))));
   int origTxLength = 8 + 2 + 8 + 1 + 10 + 40 + 1 + 1;
   assertEquals(tx.bitcoinSerialize().length, tx.length);
   assertEquals(origTxLength, tx.length);
   block.addTransaction(tx);
   assertEquals(block.bitcoinSerialize().length, block.length);
   assertEquals(origBlockLen + tx.length, block.length);
   block
       .getTransactions()
       .get(1)
       .getInputs()
       .get(0)
       .setScriptBytes(new byte[] {(byte) ScriptOpCodes.OP_FALSE, (byte) ScriptOpCodes.OP_FALSE});
   assertEquals(block.length, origBlockLen + tx.length);
   assertEquals(tx.length, origTxLength + 1);
   block.getTransactions().get(1).getInputs().get(0).setScriptBytes(new byte[] {});
   assertEquals(block.length, block.bitcoinSerialize().length);
   assertEquals(block.length, origBlockLen + tx.length);
   assertEquals(tx.length, origTxLength - 1);
   block
       .getTransactions()
       .get(1)
       .addInput(
           new TransactionInput(
               params,
               null,
               new byte[] {(byte) ScriptOpCodes.OP_FALSE},
               new TransactionOutPoint(params, 0, Sha256Hash.of(new byte[] {1}))));
   assertEquals(block.length, origBlockLen + tx.length);
   assertEquals(tx.length, origTxLength + 41); // - 1 + 40 + 1 + 1
 }
  @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 coinbaseTxns() throws Exception {
   // Covers issue 420 where the outpoint index of a coinbase tx input was being mis-serialized.
   Block b =
       params
           .getGenesisBlock()
           .createNextBlockWithCoinbase(
               Block.BLOCK_VERSION_GENESIS,
               myKey.getPubKey(),
               FIFTY_COINS,
               Block.BLOCK_HEIGHT_GENESIS);
   Transaction coinbase = b.getTransactions().get(0);
   assertTrue(coinbase.isCoinBase());
   BlockChain chain = new BlockChain(params, myWallet, new MemoryBlockStore(params));
   assertTrue(chain.add(b));
   // Wallet now has a coinbase tx in it.
   assertEquals(1, myWallet.getTransactions(true).size());
   assertTrue(myWallet.getTransaction(coinbase.getHash()).isCoinBase());
   Wallet wallet2 = roundTrip(myWallet);
   assertEquals(1, wallet2.getTransactions(true).size());
   assertTrue(wallet2.getTransaction(coinbase.getHash()).isCoinBase());
 }
  @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) {
    }
  }
  @Test
  public void testAppearedAtChainHeightDepthAndWorkDone() throws Exception {
    // Test the TransactionConfidence appearedAtChainHeight, depth and workDone field are stored.

    BlockChain chain = new BlockChain(params, myWallet, new MemoryBlockStore(params));

    final ArrayList<Transaction> txns = new ArrayList<Transaction>(2);
    myWallet.addEventListener(
        new AbstractWalletEventListener() {
          @Override
          public void onCoinsReceived(
              Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
            txns.add(tx);
          }
        });

    // Start by building two blocks on top of the genesis block.
    Block b1 = params.getGenesisBlock().createNextBlock(myAddress);
    BigInteger work1 = b1.getWork();
    assertTrue(work1.signum() > 0);

    Block b2 = b1.createNextBlock(myAddress);
    BigInteger work2 = b2.getWork();
    assertTrue(work2.signum() > 0);

    assertTrue(chain.add(b1));
    assertTrue(chain.add(b2));

    // We now have the following chain:
    //     genesis -> b1 -> b2

    // Check the transaction confidence levels are correct before wallet roundtrip.
    Threading.waitForUserCode();
    assertEquals(2, txns.size());

    TransactionConfidence confidence0 = txns.get(0).getConfidence();
    TransactionConfidence confidence1 = txns.get(1).getConfidence();

    assertEquals(1, confidence0.getAppearedAtChainHeight());
    assertEquals(2, confidence1.getAppearedAtChainHeight());

    assertEquals(2, confidence0.getDepthInBlocks());
    assertEquals(1, confidence1.getDepthInBlocks());

    // Roundtrip the wallet and check it has stored the depth and workDone.
    Wallet rebornWallet = roundTrip(myWallet);

    Set<Transaction> rebornTxns = rebornWallet.getTransactions(false);
    assertEquals(2, rebornTxns.size());

    // The transactions are not guaranteed to be in the same order so sort them to be in chain
    // height order if required.
    Iterator<Transaction> it = rebornTxns.iterator();
    Transaction txA = it.next();
    Transaction txB = it.next();

    Transaction rebornTx0, rebornTx1;
    if (txA.getConfidence().getAppearedAtChainHeight() == 1) {
      rebornTx0 = txA;
      rebornTx1 = txB;
    } else {
      rebornTx0 = txB;
      rebornTx1 = txA;
    }

    TransactionConfidence rebornConfidence0 = rebornTx0.getConfidence();
    TransactionConfidence rebornConfidence1 = rebornTx1.getConfidence();

    assertEquals(1, rebornConfidence0.getAppearedAtChainHeight());
    assertEquals(2, rebornConfidence1.getAppearedAtChainHeight());

    assertEquals(2, rebornConfidence0.getDepthInBlocks());
    assertEquals(1, rebornConfidence1.getDepthInBlocks());
  }