@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()); }