@Test
public void coinbaseTransactionAvailability() throws Exception {

    // Check that a coinbase transaction is only available to spend after NetworkParameters.getSpendableCoinbaseDepth() blocks.
    // Create a second wallet to receive the coinbase spend.
    Wallet wallet2 = new Wallet(unitTestParams);
    ECKey receiveKey = new ECKey();
    wallet2.addKey(receiveKey);
    chain.addWallet(wallet2);
    Address addressToSendTo = receiveKey.toAddress(unitTestParams);
    // Create a block, sending the coinbase to the coinbaseTo address (which is in the wallet).
    Block b1 = unitTestParams.getGenesisBlock().createNextBlockWithCoinbase(wallet.getKeys().get(0).getPubKey());
    chain.add(b1);
    // Check a transaction has been received.
    assertNotNull(coinbaseTransaction);
    // The coinbase tx is not yet available to spend.
    assertEquals(BigInteger.ZERO, wallet.getBalance());
    assertEquals(wallet.getBalance(BalanceType.ESTIMATED), Utils.toNanoCoins(50, 0));
    assertTrue(!coinbaseTransaction.isMature());
    // Attempt to spend the coinbase - this should fail as the coinbase is not mature yet.
    try {
        wallet.createSend(addressToSendTo, Utils.toNanoCoins(49, 0));
        fail();
    } catch (InsufficientMoneyException e) {
    }
    // Check that the coinbase is unavailable to spend for the next spendableCoinbaseDepth - 2 blocks.
    for (int i = 0; i < unitTestParams.getSpendableCoinbaseDepth() - 2; i++) {
        // Non relevant tx - just for fake block creation.
        Transaction tx2 = createFakeTx(unitTestParams, Utils.toNanoCoins(1, 0),
            new ECKey().toAddress(unitTestParams));
        Block b2 = createFakeBlock(blockStore, tx2).block;
        chain.add(b2);
        // Wallet still does not have the coinbase transaction available for spend.
        assertEquals(BigInteger.ZERO, wallet.getBalance());
        assertEquals(wallet.getBalance(BalanceType.ESTIMATED), Utils.toNanoCoins(50, 0));
        // The coinbase transaction is still not mature.
        assertTrue(!coinbaseTransaction.isMature());
        // Attempt to spend the coinbase - this should fail.
        try {
            wallet.createSend(addressToSendTo, Utils.toNanoCoins(49, 0));
            fail();
        } catch (InsufficientMoneyException e) {
        }
    }
    // Give it one more block - should now be able to spend coinbase transaction. Non relevant tx.
    Transaction tx3 = createFakeTx(unitTestParams, Utils.toNanoCoins(1, 0), new ECKey().toAddress(unitTestParams));
    Block b3 = createFakeBlock(blockStore, tx3).block;
    chain.add(b3);
    // Wallet now has the coinbase transaction available for spend.
    assertEquals(wallet.getBalance(), Utils.toNanoCoins(50, 0));
    assertEquals(wallet.getBalance(BalanceType.ESTIMATED), Utils.toNanoCoins(50, 0));
    assertTrue(coinbaseTransaction.isMature());
    // Create a spend with the coinbase BTC to the address in the second wallet - this should now succeed.
    Transaction coinbaseSend2 = wallet.createSend(addressToSendTo, Utils.toNanoCoins(49, 0));
    assertNotNull(coinbaseSend2);
    // Commit the coinbaseSpend to the first wallet and check the balances decrement.
    wallet.commitTx(coinbaseSend2);
    assertEquals(wallet.getBalance(BalanceType.ESTIMATED), Utils.toNanoCoins(1, 0));
    // Available balance is zero as change has not been received from a block yet.
    assertEquals(wallet.getBalance(BalanceType.AVAILABLE), Utils.toNanoCoins(0, 0));
    // Give it one more block - change from coinbaseSpend should now be available in the first wallet.
    Block b4 = createFakeBlock(blockStore, coinbaseSend2).block;
    chain.add(b4);
    assertEquals(wallet.getBalance(BalanceType.AVAILABLE), Utils.toNanoCoins(1, 0));
    // Check the balances in the second wallet.
    assertEquals(wallet2.getBalance(BalanceType.ESTIMATED), Utils.toNanoCoins(49, 0));
    assertEquals(wallet2.getBalance(BalanceType.AVAILABLE), Utils.toNanoCoins(49, 0));
}