@Test public void doubleSpend() throws Exception { // Check that we can serialize double spends correctly, as this is a slightly tricky case. FakeTxBuilder.DoubleSpends doubleSpends = FakeTxBuilder.createFakeDoubleSpendTxns(params, myAddress); // t1 spends to our wallet. myWallet.receivePending(doubleSpends.t1, null); // t2 rolls back t1 and spends somewhere else. myWallet.receiveFromBlock(doubleSpends.t2, null, BlockChain.NewBlockType.BEST_CHAIN, 0); Wallet wallet1 = roundTrip(myWallet); assertEquals(1, wallet1.getTransactions(true).size()); Transaction t1 = wallet1.getTransaction(doubleSpends.t1.getHash()); assertEquals(ConfidenceType.DEAD, t1.getConfidence().getConfidenceType()); assertEquals(Coin.ZERO, wallet1.getBalance()); // TODO: Wallet should store overriding transactions even if they are not wallet-relevant. // assertEquals(doubleSpends.t2, t1.getConfidence().getOverridingTransaction()); }
@Test public void retryFailedBroadcast() throws Exception { // If we create a spend, it's sent to a peer that swallows it, and the peergroup is // removed/re-added then // the tx should be broadcast again. InboundMessageQueuer p1 = connectPeer(1); connectPeer(2); // Send ourselves a bit of money. Block b1 = FakeTxBuilder.makeSolvedTestBlock(blockStore, address); inbound(p1, b1); assertNull(outbound(p1)); assertEquals(FIFTY_COINS, wallet.getBalance()); // Now create a spend, and expect the announcement on p1. Address dest = new ECKey().toAddress(params); Wallet.SendResult sendResult = wallet.sendCoins(peerGroup, dest, COIN); assertFalse(sendResult.broadcastComplete.isDone()); Transaction t1; { Message m; while (!((m = outbound(p1)) instanceof Transaction)) ; t1 = (Transaction) m; } assertFalse(sendResult.broadcastComplete.isDone()); // p1 eats it :( A bit later the PeerGroup is taken down. peerGroup.removeWallet(wallet); peerGroup.addWallet(wallet); // We want to hear about it again. Now, because we've disabled the randomness for the unit tests // it will // re-appear on p1 again. Of course in the real world it would end up with a different set of // peers and // select randomly so we get a second chance. Transaction t2 = (Transaction) outbound(p1); assertEquals(t1, t2); }
@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()); }