@Test
  public void peerGroupWalletIntegration() throws Exception {
    // Make sure we can create spends, and that they are announced. Then do the same with offline
    // mode.

    // Set up connections and block chain.
    VersionMessage ver = new VersionMessage(params, 2);
    ver.localServices = VersionMessage.NODE_NETWORK;
    InboundMessageQueuer p1 = connectPeer(1, ver);
    InboundMessageQueuer p2 = connectPeer(2);

    // Send ourselves a bit of money.
    Block b1 = TestUtils.makeSolvedTestBlock(blockStore, address);
    inbound(p1, b1);
    pingAndWait(p1);
    assertNull(outbound(p1));
    assertEquals(Utils.toNanoCoins(50, 0), wallet.getBalance());

    // Check that the wallet informs us of changes in confidence as the transaction ripples across
    // the network.
    final Transaction[] transactions = new Transaction[1];
    wallet.addEventListener(
        new AbstractWalletEventListener() {
          @Override
          public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
            transactions[0] = tx;
          }
        });

    // Now create a spend, and expect the announcement on p1.
    Address dest = new ECKey().toAddress(params);
    Wallet.SendResult sendResult = wallet.sendCoins(peerGroup, dest, Utils.toNanoCoins(1, 0));
    assertNotNull(sendResult.tx);
    Threading.waitForUserCode();
    assertFalse(sendResult.broadcastComplete.isDone());
    assertEquals(transactions[0], sendResult.tx);
    assertEquals(0, transactions[0].getConfidence().numBroadcastPeers());
    transactions[0] = null;
    Transaction t1 = (Transaction) outbound(p1);
    assertNotNull(t1);
    // 49 BTC in change.
    assertEquals(Utils.toNanoCoins(49, 0), t1.getValueSentToMe(wallet));
    // The future won't complete until it's heard back from the network on p2.
    InventoryMessage inv = new InventoryMessage(params);
    inv.addTransaction(t1);
    inbound(p2, inv);
    pingAndWait(p2);
    Threading.waitForUserCode();
    assertTrue(sendResult.broadcastComplete.isDone());
    assertEquals(transactions[0], sendResult.tx);
    assertEquals(1, transactions[0].getConfidence().numBroadcastPeers());
    // Confirm it.
    Block b2 = TestUtils.createFakeBlock(blockStore, t1).block;
    inbound(p1, b2);
    pingAndWait(p1);
    assertNull(outbound(p1));

    // Do the same thing with an offline transaction.
    peerGroup.removeWallet(wallet);
    Wallet.SendRequest req = Wallet.SendRequest.to(dest, Utils.toNanoCoins(2, 0));
    req.ensureMinRequiredFee = false;
    Transaction t3 = checkNotNull(wallet.sendCoinsOffline(req));
    assertNull(outbound(p1)); // Nothing sent.
    // Add the wallet to the peer group (simulate initialization). Transactions should be announced.
    peerGroup.addWallet(wallet);
    // Transaction announced to the first peer.
    assertEquals(t3.getHash(), ((Transaction) outbound(p1)).getHash());
  }
  @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) {
    }
  }