Replace WalletEventListener.onDeadTransaction with a generic confidence changed callb...
[elbandi:fedoracoinj.git] / tests / com / google / bitcoin / core / WalletTest.java
1 /**
2  * Copyright 2011 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.google.bitcoin.core;
18
19 import com.google.bitcoin.store.BlockStore;
20 import com.google.bitcoin.store.MemoryBlockStore;
21 import com.google.bitcoin.store.WalletProtobufSerializer;
22 import com.google.bitcoin.utils.BriefLogFormatter;
23 import org.junit.Before;
24 import org.junit.Test;
25
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.InputStream;
29 import java.math.BigInteger;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Set;
33
34 import static com.google.bitcoin.core.TestUtils.createFakeBlock;
35 import static com.google.bitcoin.core.TestUtils.createFakeTx;
36 import static com.google.bitcoin.core.Utils.bitcoinValueToFriendlyString;
37 import static com.google.bitcoin.core.Utils.toNanoCoins;
38 import static org.junit.Assert.*;
39
40 public class WalletTest {
41     static final NetworkParameters params = NetworkParameters.unitTests();
42
43     private Address myAddress;
44     private Wallet wallet;
45     private BlockStore blockStore;
46     private ECKey myKey;
47
48     @Before
49     public void setUp() throws Exception {
50         myKey = new ECKey();
51         myAddress = myKey.toAddress(params);
52         wallet = new Wallet(params);
53         wallet.addKey(myKey);
54         blockStore = new MemoryBlockStore(params);
55
56         BriefLogFormatter.init();
57     }
58
59     @Test
60     public void basicSpending() throws Exception {
61         // We'll set up a wallet that receives a coin, then sends a coin of lesser value and keeps the change.
62         BigInteger v1 = Utils.toNanoCoins(1, 0);
63         Transaction t1 = createFakeTx(params, v1, myAddress);
64
65         wallet.receiveFromBlock(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
66         assertEquals(v1, wallet.getBalance());
67         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
68         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL));
69
70         ECKey k2 = new ECKey();
71         BigInteger v2 = toNanoCoins(0, 50);
72         Transaction t2 = wallet.createSend(k2.toAddress(params), v2);
73         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
74         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL));
75
76         // Do some basic sanity checks.
77         assertEquals(1, t2.getInputs().size());
78         assertEquals(myAddress, t2.getInputs().get(0).getScriptSig().getFromAddress());
79         assertEquals(t2.getConfidence().getConfidenceType(), TransactionConfidence.ConfidenceType.NOT_SEEN_IN_CHAIN);
80
81         // We have NOT proven that the signature is correct!
82
83         wallet.commitTx(t2);
84         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
85         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.SPENT));
86         assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.ALL));
87     }
88
89     @Test
90     public void customTransactionSpending() throws Exception {
91         // We'll set up a wallet that receives a coin, then sends a coin of lesser value and keeps the change.
92         BigInteger v1 = Utils.toNanoCoins(3, 0);
93         Transaction t1 = createFakeTx(params, v1, myAddress);
94
95         wallet.receiveFromBlock(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
96         assertEquals(v1, wallet.getBalance());
97         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
98         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL));
99
100         ECKey k2 = new ECKey();
101         Address a2 = k2.toAddress(params);
102         BigInteger v2 = toNanoCoins(0, 50);
103         BigInteger v3 = toNanoCoins(0, 75);
104         BigInteger v4 = toNanoCoins(1, 25);
105
106         Transaction t2 = new Transaction(params);
107         t2.addOutput(v2, a2);
108         t2.addOutput(v3, a2);
109         t2.addOutput(v4, a2);
110         boolean complete = wallet.completeTx(t2);
111
112         // Do some basic sanity checks.
113         assertTrue(complete);
114         assertEquals(1, t2.getInputs().size());
115         assertEquals(myAddress, t2.getInputs().get(0).getScriptSig().getFromAddress());
116         assertEquals(t2.getConfidence().getConfidenceType(), TransactionConfidence.ConfidenceType.NOT_SEEN_IN_CHAIN);
117
118         // We have NOT proven that the signature is correct!
119
120         wallet.commitTx(t2);
121         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
122         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.SPENT));
123         assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.ALL));
124     }
125
126     @Test
127     public void sideChain() throws Exception {
128         // The wallet receives a coin on the main chain, then on a side chain. Only main chain counts towards balance.
129         BigInteger v1 = Utils.toNanoCoins(1, 0);
130         Transaction t1 = createFakeTx(params, v1, myAddress);
131
132         wallet.receiveFromBlock(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
133         assertEquals(v1, wallet.getBalance());
134         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
135         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL));
136
137         BigInteger v2 = toNanoCoins(0, 50);
138         Transaction t2 = createFakeTx(params, v2, myAddress);
139         wallet.receiveFromBlock(t2, null, BlockChain.NewBlockType.SIDE_CHAIN);
140         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.INACTIVE));
141         assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.ALL));
142
143         assertEquals(v1, wallet.getBalance());
144     }
145
146     @Test
147     public void balance() throws Exception {
148         // Receive 5 coins then half a coin.
149         BigInteger v1 = toNanoCoins(5, 0);
150         BigInteger v2 = toNanoCoins(0, 50);
151         Transaction t1 = createFakeTx(params, v1, myAddress);
152         Transaction t2 = createFakeTx(params, v2, myAddress);
153         StoredBlock b1 = createFakeBlock(params, blockStore, t1).storedBlock;
154         StoredBlock b2 = createFakeBlock(params, blockStore, t2).storedBlock;
155         BigInteger expected = toNanoCoins(5, 50);
156         assertEquals(0, wallet.getPoolSize(WalletTransaction.Pool.ALL));
157         wallet.receiveFromBlock(t1, b1, BlockChain.NewBlockType.BEST_CHAIN);
158         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
159         wallet.receiveFromBlock(t2, b2, BlockChain.NewBlockType.BEST_CHAIN);
160         assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
161         assertEquals(expected, wallet.getBalance());
162
163         // Now spend one coin.
164         BigInteger v3 = toNanoCoins(1, 0);
165         Transaction spend = wallet.createSend(new ECKey().toAddress(params), v3);
166         wallet.commitTx(spend);
167         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
168
169         // Available and estimated balances should not be the same. We don't check the exact available balance here
170         // because it depends on the coin selection algorithm.
171         assertEquals(toNanoCoins(4, 50), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
172         assertFalse(wallet.getBalance(Wallet.BalanceType.AVAILABLE).equals(
173                     wallet.getBalance(Wallet.BalanceType.ESTIMATED)));
174
175         // Now confirm the transaction by including it into a block.
176         StoredBlock b3 = createFakeBlock(params, blockStore, spend).storedBlock;
177         wallet.receiveFromBlock(spend, b3, BlockChain.NewBlockType.BEST_CHAIN);
178
179         // Change is confirmed. We started with 5.50 so we should have 4.50 left.
180         BigInteger v4 = toNanoCoins(4, 50);
181         assertEquals(v4, wallet.getBalance(Wallet.BalanceType.AVAILABLE));
182     }
183
184     // Intuitively you'd expect to be able to create a transaction with identical inputs and outputs and get an
185     // identical result to the official client. However the signatures are not deterministic - signing the same data
186     // with the same key twice gives two different outputs. So we cannot prove bit-for-bit compatibility in this test
187     // suite.
188
189     @Test
190     public void blockChainCatchup() throws Exception {
191         // Test that we correctly process transactions arriving from the chain, with callbacks for inbound and outbound.
192         final BigInteger bigints[] = new BigInteger[4];
193         final Transaction txn[] = new Transaction[2];
194         wallet.addEventListener(new AbstractWalletEventListener() {
195             @Override
196             public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
197                 super.onCoinsReceived(wallet, tx, prevBalance, newBalance);
198                 bigints[0] = prevBalance;
199                 bigints[1] = newBalance;
200                 txn[0] = tx;
201             }
202
203             @Override
204             public void onCoinsSent(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
205                 super.onCoinsSent(wallet, tx, prevBalance, newBalance);
206                 bigints[2] = prevBalance;
207                 bigints[3] = newBalance;
208                 txn[1] = tx;
209             }
210         });
211         
212         // Receive some money.
213         BigInteger oneCoin = Utils.toNanoCoins(1, 0);
214         Transaction tx1 = createFakeTx(params, oneCoin, myAddress);
215         StoredBlock b1 = createFakeBlock(params, blockStore, tx1).storedBlock;
216         wallet.receiveFromBlock(tx1, b1, BlockChain.NewBlockType.BEST_CHAIN);
217         assertEquals(null, txn[1]);  // onCoinsSent not called.
218         assertEquals(txn[0].getHash(), tx1.getHash());
219         assertEquals(BigInteger.ZERO, bigints[0]);
220         assertEquals(oneCoin, bigints[1]);
221         // Send 0.10 to somebody else.
222         Transaction send1 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 10), myAddress);
223         // Pretend it makes it into the block chain, our wallet state is cleared but we still have the keys, and we
224         // want to get back to our previous state. We can do this by just not confirming the transaction as
225         // createSend is stateless.
226         StoredBlock b2 = createFakeBlock(params, blockStore, send1).storedBlock;
227         wallet.receiveFromBlock(send1, b2, BlockChain.NewBlockType.BEST_CHAIN);
228         assertEquals(bitcoinValueToFriendlyString(wallet.getBalance()), "0.90");
229         assertEquals(txn[1].getHash(), send1.getHash());
230         assertEquals(bitcoinValueToFriendlyString(bigints[2]), "1.00");
231         assertEquals(bitcoinValueToFriendlyString(bigints[3]), "0.90");
232         // And we do it again after the catchup.
233         Transaction send2 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 10), myAddress);
234         // What we'd really like to do is prove the official client would accept it .... no such luck unfortunately.
235         wallet.commitTx(send2);
236         StoredBlock b3 = createFakeBlock(params, blockStore, send2).storedBlock;
237         wallet.receiveFromBlock(send2, b3, BlockChain.NewBlockType.BEST_CHAIN);
238         assertEquals(bitcoinValueToFriendlyString(wallet.getBalance()), "0.80");
239     }
240
241     @Test
242     public void balances() throws Exception {
243         BigInteger nanos = Utils.toNanoCoins(1, 0);
244         Transaction tx1 = createFakeTx(params, nanos, myAddress);
245         wallet.receiveFromBlock(tx1, null, BlockChain.NewBlockType.BEST_CHAIN);
246         assertEquals(nanos, tx1.getValueSentToMe(wallet, true));
247         // Send 0.10 to somebody else.
248         Transaction send1 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 10), myAddress);
249         // Reserialize.
250         Transaction send2 = new Transaction(params, send1.bitcoinSerialize());
251         assertEquals(nanos, send2.getValueSentFromMe(wallet));
252     }
253
254     @Test
255     public void transactions() throws Exception {
256         // This test covers a bug in which Transaction.getValueSentFromMe was calculating incorrectly.
257         Transaction tx = createFakeTx(params, Utils.toNanoCoins(1, 0), myAddress);
258         // Now add another output (ie, change) that goes to some other address.
259         Address someOtherGuy = new ECKey().toAddress(params);
260         TransactionOutput output = new TransactionOutput(params, tx, Utils.toNanoCoins(0, 5), someOtherGuy);
261         tx.addOutput(output);
262         // Note that tx is no longer valid: it spends more than it imports. However checking transactions balance
263         // correctly isn't possible in SPV mode because value is a property of outputs not inputs. Without all
264         // transactions you can't check they add up.
265         wallet.receiveFromBlock(tx, null, BlockChain.NewBlockType.BEST_CHAIN);
266         // Now the other guy creates a transaction which spends that change.
267         Transaction tx2 = new Transaction(params);
268         tx2.addInput(output);
269         tx2.addOutput(new TransactionOutput(params, tx2, Utils.toNanoCoins(0, 5), myAddress));
270         // tx2 doesn't send any coins from us, even though the output is in the wallet.
271         assertEquals(Utils.toNanoCoins(0, 0), tx2.getValueSentFromMe(wallet));
272     }
273
274     @Test
275     public void bounce() throws Exception {
276         // This test covers bug 64 (False double spends). Check that if we create a spend and it's immediately sent
277         // back to us, this isn't considered as a double spend.
278         BigInteger coin1 = Utils.toNanoCoins(1, 0);
279         BigInteger coinHalf = Utils.toNanoCoins(0, 50);
280         // Start by giving us 1 coin.
281         Transaction inbound1 = createFakeTx(params, coin1, myAddress);
282         wallet.receiveFromBlock(inbound1, null, BlockChain.NewBlockType.BEST_CHAIN);
283         // Send half to some other guy. Sending only half then waiting for a confirm is important to ensure the tx is
284         // in the unspent pool, not pending or spent.
285         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
286         assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL));
287         Address someOtherGuy = new ECKey().toAddress(params);
288         Transaction outbound1 = wallet.createSend(someOtherGuy, coinHalf);
289         wallet.commitTx(outbound1);
290         wallet.receiveFromBlock(outbound1, null, BlockChain.NewBlockType.BEST_CHAIN);
291         // That other guy gives us the coins right back.
292         Transaction inbound2 = new Transaction(params);
293         inbound2.addOutput(new TransactionOutput(params, inbound2, coinHalf, myAddress));
294         inbound2.addInput(outbound1.getOutputs().get(0));
295         wallet.receiveFromBlock(inbound2, null, BlockChain.NewBlockType.BEST_CHAIN);
296         assertEquals(coin1, wallet.getBalance());
297     }
298
299     @Test
300     public void finneyAttack() throws Exception {
301         // A Finney attack is where a miner includes a transaction spending coins to themselves but does not
302         // broadcast it. When they find a solved block, they hold it back temporarily whilst they buy something with
303         // those same coins. After purchasing, they broadcast the block thus reversing the transaction. It can be
304         // done by any miner for products that can be bought at a chosen time and very quickly (as every second you
305         // withold your block means somebody else might find it first, invalidating your work).
306         //
307         // Test that we handle the attack correctly: a double spend on the chain moves transactions from pending to dead.
308         // This needs to work both for transactions we create, and that we receive from others.
309         final Transaction[] eventDead = new Transaction[1];
310         final Transaction[] eventReplacement = new Transaction[1];
311         wallet.addEventListener(new AbstractWalletEventListener() {
312             @Override
313             public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
314                 super.onTransactionConfidenceChanged(wallet, tx);
315                 if (tx.getConfidence().getConfidenceType() ==
316                         TransactionConfidence.ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND) {
317                     eventDead[0] = tx;
318                     eventReplacement[0] = tx.getConfidence().getOverridingTransaction();
319                 }
320             }
321         });
322
323         // Receive 1 BTC.
324         BigInteger nanos = Utils.toNanoCoins(1, 0);
325         Transaction t1 = createFakeTx(params, nanos, myAddress);
326         wallet.receiveFromBlock(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
327         // Create a send to a merchant.
328         Transaction send1 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 50));
329         // Create a double spend.
330         Transaction send2 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 50));
331         // Broadcast send1.
332         wallet.commitTx(send1);
333         // Receive a block that overrides it.
334         wallet.receiveFromBlock(send2, null, BlockChain.NewBlockType.BEST_CHAIN);
335         assertEquals(send1, eventDead[0]);
336         assertEquals(send2, eventReplacement[0]);
337         assertEquals(TransactionConfidence.ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND,
338                      send1.getConfidence().getConfidenceType());
339         
340         // Receive 10 BTC.
341         nanos = Utils.toNanoCoins(10, 0);
342
343         // Create a double spending tx.
344         Transaction t2 = new Transaction(params);
345         TransactionOutput o1 = new TransactionOutput(params, t2, nanos, myAddress);
346         t2.addOutput(o1);
347         Transaction prevTx = new Transaction(params);
348         Address someBadGuy = new ECKey().toAddress(params);
349         TransactionOutput prevOut = new TransactionOutput(params, prevTx, nanos, someBadGuy);
350         prevTx.addOutput(prevOut);
351         // Connect it.
352         t2.addInput(prevOut);
353         wallet.receivePending(t2);
354         assertEquals(TransactionConfidence.ConfidenceType.NOT_SEEN_IN_CHAIN, t2.getConfidence().getConfidenceType());
355         // Receive a tx from a block that overrides it.
356         Transaction t3 = new Transaction(params);
357         TransactionOutput o3 = new TransactionOutput(params, t3, nanos, someBadGuy);
358         t3.addOutput(o3);
359         t3.addInput(prevOut);
360         wallet.receiveFromBlock(t3, null, BlockChain.NewBlockType.BEST_CHAIN);
361         assertEquals(TransactionConfidence.ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND, 
362                      t2.getConfidence().getConfidenceType());
363         assertEquals(t3, t2.getConfidence().getOverridingTransaction());
364     }
365
366     @Test
367     public void pending1() throws Exception {
368         // Check that if we receive a pending transaction that is then confirmed, we are notified as appropriate.
369         final BigInteger nanos = Utils.toNanoCoins(1, 0);
370         final Transaction t1 = createFakeTx(params, nanos, myAddress);
371
372         // First one is "called" second is "pending".
373         final boolean[] flags = new boolean[2];
374         final Transaction[] notifiedTx = new Transaction[1];
375         wallet.addEventListener(new AbstractWalletEventListener() {
376             @Override
377             public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
378                 // Check we got the expected transaction.
379                 assertEquals(tx, t1);
380                 // Check that it's considered to be pending inclusion in the block chain.
381                 assertEquals(prevBalance, BigInteger.ZERO);
382                 assertEquals(newBalance, nanos);
383                 flags[0] = true;
384                 flags[1] = tx.isPending();
385                 notifiedTx[0] = tx;
386             }
387         });
388
389         wallet.receivePending(t1);
390         assertTrue(flags[0]);
391         assertTrue(flags[1]);   // is pending
392         flags[0] = false;
393         // Check we don't get notified if we receive it again.
394         wallet.receivePending(t1);
395         assertFalse(flags[0]);
396         // Now check again, that we should NOT be notified when we receive it via a block (we were already notified).
397         // However the confidence should be updated.
398         // Make a fresh copy of the tx to ensure we're testing realistically.
399         flags[0] = flags[1] = false;
400         notifiedTx[0].getConfidence().addEventListener(new TransactionConfidence.Listener() {
401             public void onConfidenceChanged(Transaction tx) {
402                 flags[1] = true;
403             }
404         });
405         assertEquals(TransactionConfidence.ConfidenceType.NOT_SEEN_IN_CHAIN,
406                 notifiedTx[0].getConfidence().getConfidenceType());
407         final Transaction t1Copy = new Transaction(params, t1.bitcoinSerialize());
408         wallet.receiveFromBlock(t1Copy, createFakeBlock(params, blockStore, t1Copy).storedBlock,
409                 BlockChain.NewBlockType.BEST_CHAIN);
410         assertFalse(flags[0]);
411         assertTrue(flags[1]);
412         assertEquals(TransactionConfidence.ConfidenceType.BUILDING, notifiedTx[0].getConfidence().getConfidenceType());
413         // Check we don't get notified about an irrelevant transaction.
414         flags[0] = false;
415         flags[1] = false;
416         Transaction irrelevant = createFakeTx(params, nanos, new ECKey().toAddress(params));
417         wallet.receivePending(irrelevant);
418         assertFalse(flags[0]);
419     }
420
421     @Test
422     public void pending2() throws Exception {
423         // Check that if we receive a pending tx we did not send, it updates our spent flags correctly.
424         final Transaction txn[] = new Transaction[1];
425         final BigInteger bigints[] = new BigInteger[2];
426         wallet.addEventListener(new AbstractWalletEventListener() {
427             @Override
428             public void onCoinsSent(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
429                 txn[0] = tx;
430                 bigints[0] = prevBalance;
431                 bigints[1] = newBalance;
432             }
433         });
434         // Receive some coins.
435         BigInteger nanos = Utils.toNanoCoins(1, 0);
436         Transaction t1 = createFakeTx(params, nanos, myAddress);
437         StoredBlock b1 = createFakeBlock(params, blockStore, t1).storedBlock;
438         wallet.receiveFromBlock(t1, b1, BlockChain.NewBlockType.BEST_CHAIN);
439         assertEquals(nanos, wallet.getBalance());
440         // Create a spend with them, but don't commit it (ie it's from somewhere else but using our keys). This TX
441         // will have change as we don't spend our entire balance.
442         BigInteger halfNanos = Utils.toNanoCoins(0, 50);
443         Transaction t2 = wallet.createSend(new ECKey().toAddress(params), halfNanos);
444         // Now receive it as pending.
445         wallet.receivePending(t2);
446         // We received an onCoinsSent() callback.
447         assertEquals(t2, txn[0]);
448         assertEquals(nanos, bigints[0]);
449         assertEquals(halfNanos, bigints[1]);
450         // Our balance is now 0.50 BTC
451         assertEquals(halfNanos, wallet.getBalance(Wallet.BalanceType.ESTIMATED));
452     }
453
454     @Test
455     public void pending3() throws Exception {
456         // Check that if we receive a pending tx, and it's overridden by a double spend from the main chain, we
457         // are notified that it's dead. This should work even if the pending tx inputs are NOT ours, ie, they don't
458         // connect to anything.
459         BigInteger nanos = Utils.toNanoCoins(1, 0);
460
461         // Create two transactions that share the same input tx.
462         Address badGuy = new ECKey().toAddress(params);
463         Transaction doubleSpentTx = new Transaction(params);
464         TransactionOutput doubleSpentOut = new TransactionOutput(params, doubleSpentTx, nanos, badGuy);
465         doubleSpentTx.addOutput(doubleSpentOut);
466         Transaction t1 = new Transaction(params);
467         TransactionOutput o1 = new TransactionOutput(params, t1, nanos, myAddress);
468         t1.addOutput(o1);
469         t1.addInput(doubleSpentOut);
470         Transaction t2 = new Transaction(params);
471         TransactionOutput o2 = new TransactionOutput(params, t2, nanos, badGuy);
472         t2.addOutput(o2);
473         t2.addInput(doubleSpentOut);
474
475         final Transaction[] called = new Transaction[2];
476         wallet.addEventListener(new AbstractWalletEventListener() {
477             public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
478                 called[0] = tx;
479             }
480
481             @Override
482             public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
483                 super.onTransactionConfidenceChanged(wallet, tx);
484                 if (tx.getConfidence().getConfidenceType() == 
485                         TransactionConfidence.ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND) {
486                     called[0] = tx;
487                     called[1] = tx.getConfidence().getOverridingTransaction();
488                 }
489             }
490         });
491
492         assertEquals(BigInteger.ZERO, wallet.getBalance());
493         wallet.receivePending(t1);
494         assertEquals(t1, called[0]);
495         assertEquals(nanos, wallet.getBalance(Wallet.BalanceType.ESTIMATED));
496         // Now receive a double spend on the main chain.
497         called[0] = called[1] = null;
498         wallet.receiveFromBlock(t2, createFakeBlock(params, blockStore, t2).storedBlock, BlockChain.NewBlockType.BEST_CHAIN);
499         assertEquals(BigInteger.ZERO, wallet.getBalance());
500         assertEquals(t1, called[0]); // dead
501         assertEquals(t2, called[1]); // replacement
502     }
503
504     @Test
505     public void transactionsList() throws Exception {
506         // Check the wallet can give us an ordered list of all received transactions.
507         Utils.rollMockClock(0);
508         // Receive a coin.
509         Transaction tx1 = createFakeTx(params, Utils.toNanoCoins(1, 0), myAddress);
510         StoredBlock b1 = createFakeBlock(params, blockStore, tx1).storedBlock;
511         wallet.receiveFromBlock(tx1, b1, BlockChain.NewBlockType.BEST_CHAIN);
512         // Receive half a coin 10 minutes later.
513         Utils.rollMockClock(60 * 10);
514         Transaction tx2 = createFakeTx(params, Utils.toNanoCoins(0, 5), myAddress);
515         StoredBlock b2 = createFakeBlock(params, blockStore, tx1).storedBlock;
516         wallet.receiveFromBlock(tx2, b2, BlockChain.NewBlockType.BEST_CHAIN);
517         // Check we got them back in order.
518         List<Transaction> transactions = wallet.getTransactionsByTime();
519         assertEquals(tx2, transactions.get(0));
520         assertEquals(tx1,  transactions.get(1));
521         assertEquals(2, transactions.size());
522         // Check we get only the last transaction if we request a subrage.
523         transactions = wallet.getRecentTransactions(1, false);
524         assertEquals(1, transactions.size());
525         assertEquals(tx2,  transactions.get(0));
526
527         // Create a spend five minutes later.
528         Utils.rollMockClock(60 * 5);
529         Transaction tx3 = wallet.createSend(new ECKey().toAddress(params), Utils.toNanoCoins(0, 5));
530         // Does not appear in list yet.
531         assertEquals(2, wallet.getTransactionsByTime().size());
532         wallet.commitTx(tx3);
533         // Now it does.
534         transactions = wallet.getTransactionsByTime();
535         assertEquals(3, transactions.size());
536         assertEquals(tx3, transactions.get(0));
537
538         // Verify we can handle the case of older wallets in which the timestamp is null (guessed from the
539         // block appearances list).
540         tx1.updatedAt = null;
541         tx3.updatedAt = null;
542         // Check we got them back in order.
543         transactions = wallet.getTransactionsByTime();
544         assertEquals(tx2,  transactions.get(0));
545         assertEquals(3, transactions.size());
546     }
547
548     @Test
549     public void keyCreationTime() throws Exception {
550         wallet = new Wallet(params);
551         long now = Utils.rollMockClock(0).getTime() / 1000;  // Fix the mock clock.
552         // No keys returns current time.
553         assertEquals(now, wallet.getEarliestKeyCreationTime());
554         Utils.rollMockClock(60);
555         wallet.addKey(new ECKey());
556         assertEquals(now + 60, wallet.getEarliestKeyCreationTime());
557         Utils.rollMockClock(60);
558         wallet.addKey(new ECKey());
559         assertEquals(now + 60, wallet.getEarliestKeyCreationTime());
560     }
561     
562     @Test
563     public void transactionAppearsInMigration() throws Exception {
564         // Test migration from appearsIn to appearsInHashes
565         Transaction tx1 = createFakeTx(params, Utils.toNanoCoins(1, 0), myAddress);
566         StoredBlock b1 = createFakeBlock(params, blockStore, tx1).storedBlock;
567         tx1.appearsIn = new HashSet<StoredBlock>();
568         tx1.appearsIn.add(b1);
569         assertEquals(1, tx1.getAppearsInHashes().size());
570         assertTrue(tx1.getAppearsInHashes().contains(b1.getHeader().getHash()));
571         assertNull(tx1.appearsIn);
572     }
573
574     @Test
575     public void oldWalletsDeserialize() throws Exception {
576         // Check that the Wallet class fills out tx confidences as best it can when loading old wallets. The new
577         // API provides a superset of the info that used to be available so it's impossible to do a complete
578         // migration but we can do some.
579         //
580         // TODO: This test does not check migration of dead or pending transactions.
581         InputStream stream = getClass().getResourceAsStream("old1.wallet");
582         wallet = Wallet.loadFromFileStream(stream);
583         Set<Transaction> transactions = wallet.getTransactions(true, true);
584         assertEquals(91, transactions.size());
585         Transaction tx = wallet.unspent.get(new Sha256Hash("5649c63ad55002ce2f39d1d4744996ebaccc1d15e0491c9e8d60eb3720dabebd"));
586         assertEquals(tx.getAppearsInHashes().iterator().next(), new Sha256Hash("00000000019380f5aef28393827737f55a1cf8abb51a36d46ab6f2db0a5b9cb8"));
587         assertEquals(TransactionConfidence.ConfidenceType.BUILDING, tx.getConfidence().getConfidenceType());
588         assertEquals(42814, tx.getConfidence().getAppearedAtChainHeight());
589
590         // Re-serialize the wallet. Make sure it's all still there.
591         ByteArrayOutputStream bios = new ByteArrayOutputStream();
592         wallet.saveToFileStream(bios);
593         wallet = Wallet.loadFromFileStream(new ByteArrayInputStream(bios.toByteArray()));
594         assertEquals(91, transactions.size());
595         tx = wallet.unspent.get(new Sha256Hash("5649c63ad55002ce2f39d1d4744996ebaccc1d15e0491c9e8d60eb3720dabebd"));
596         assertEquals(tx.getAppearsInHashes().iterator().next(), new Sha256Hash("00000000019380f5aef28393827737f55a1cf8abb51a36d46ab6f2db0a5b9cb8"));
597         assertEquals(TransactionConfidence.ConfidenceType.BUILDING, tx.getConfidence().getConfidenceType());
598         assertEquals(42814, tx.getConfidence().getAppearedAtChainHeight());
599
600         // Now check we can serialize old wallets to protocol buffers. Covers bug 134.
601         bios.reset();
602         WalletProtobufSerializer.writeWallet(wallet, bios);
603
604     }
605
606     // Support for offline spending is tested in PeerGroupTest
607 }