@Test
 public void fourPeers() throws Exception {
   InboundMessageQueuer[] channels = {
     connectPeer(1), connectPeer(2), connectPeer(3), connectPeer(4)
   };
   Transaction tx = new Transaction(params);
   TransactionBroadcast broadcast = new TransactionBroadcast(peerGroup, tx);
   ListenableFuture<Transaction> future = broadcast.broadcast();
   assertFalse(future.isDone());
   // We expect two peers to receive a tx message, and at least one of the others must announce for
   // the future to
   // complete successfully.
   Message[] messages = {
     (Message) outbound(channels[0]),
     (Message) outbound(channels[1]),
     (Message) outbound(channels[2]),
     (Message) outbound(channels[3])
   };
   // 0 and 3 are randomly selected to receive the broadcast.
   assertEquals(tx, messages[0]);
   assertEquals(tx, messages[3]);
   assertNull(messages[1]);
   assertNull(messages[2]);
   Threading.waitForUserCode();
   assertFalse(future.isDone());
   inbound(channels[1], InventoryMessage.with(tx));
   pingAndWait(channels[1]);
   Threading.waitForUserCode();
   assertTrue(future.isDone());
 }
  @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 = FakeTxBuilder.makeSolvedTestBlock(blockStore, address);
    inbound(p1, b1);
    pingAndWait(p1);
    assertNull(outbound(p1));
    assertEquals(FIFTY_COINS, 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, COIN);
    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;
    {
      peerGroup.waitForJobQueue();
      Message m = outbound(p1);
      // Hack: bloom filters are recalculated asynchronously to sending transactions to avoid lock
      // inversion, so we might or might not get the filter/mempool message first or second.
      while (!(m instanceof Transaction)) m = outbound(p1);
      t1 = (Transaction) m;
    }
    assertNotNull(t1);
    // 49 BTC in change.
    assertEquals(valueOf(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 = FakeTxBuilder.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, valueOf(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. No extra Bloom filter because no change address was
    // needed.
    assertEquals(t3.getHash(), ((Transaction) outbound(p1)).getHash());
  }
  @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());
  }