Replace WalletEventListener.onDeadTransaction with a generic confidence changed callb...
[elbandi:fedoracoinj.git] / tests / com / google / bitcoin / core / ChainSplitTests.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.MemoryBlockStore;
20 import com.google.bitcoin.utils.BriefLogFormatter;
21 import org.junit.Before;
22 import org.junit.Test;
23
24 import java.math.BigInteger;
25 import java.util.ArrayList;
26
27 import static org.junit.Assert.*;
28
29 public class ChainSplitTests {
30     private NetworkParameters unitTestParams;
31     private Wallet wallet;
32     private BlockChain chain;
33     private Address coinsTo;
34     private Address someOtherGuy;
35
36     @Before
37     public void setUp() throws Exception {
38         BriefLogFormatter.init();
39         unitTestParams = NetworkParameters.unitTests();
40         wallet = new Wallet(unitTestParams);
41         wallet.addKey(new ECKey());
42         chain = new BlockChain(unitTestParams, wallet, new MemoryBlockStore(unitTestParams));
43         coinsTo = wallet.keychain.get(0).toAddress(unitTestParams);
44         someOtherGuy = new ECKey().toAddress(unitTestParams);
45     }
46
47     @Test
48     public void testForking1() throws Exception {
49         // Check that if the block chain forks, we end up using the right chain. Only tests inbound transactions
50         // (receiving coins). Checking that we understand reversed spends is in testForking2.
51
52         final boolean[] reorgHappened = new boolean[1];
53         reorgHappened[0] = false;
54         wallet.addEventListener(new AbstractWalletEventListener() {
55             @Override
56             public void onReorganize(Wallet wallet) {
57                 reorgHappened[0] = true;
58             }
59         });
60
61         // Start by building a couple of blocks on top of the genesis block.
62         Block b1 = unitTestParams.genesisBlock.createNextBlock(coinsTo);
63         Block b2 = b1.createNextBlock(coinsTo);
64         assertTrue(chain.add(b1));
65         assertTrue(chain.add(b2));
66         assertFalse(reorgHappened[0]);
67         // We got two blocks which generated 50 coins each, to us.
68         assertEquals("100.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
69         // We now have the following chain:
70         //     genesis -> b1 -> b2
71         //
72         // so fork like this:
73         //
74         //     genesis -> b1 -> b2
75         //                  \-> b3
76         //
77         // Nothing should happen at this point. We saw b2 first so it takes priority.
78         Block b3 = b1.createNextBlock(someOtherGuy);
79         assertTrue(chain.add(b3));
80         assertFalse(reorgHappened[0]);  // No re-org took place.
81         assertEquals("100.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
82         // Now we add another block to make the alternative chain longer.
83         assertTrue(chain.add(b3.createNextBlock(someOtherGuy)));
84         assertTrue(reorgHappened[0]);  // Re-org took place.
85         reorgHappened[0] = false;
86         //
87         //     genesis -> b1 -> b2
88         //                  \-> b3 -> b4
89         //
90         // We lost some coins! b2 is no longer a part of the best chain so our available balance should drop to 50.
91         assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
92         // ... and back to the first chain.
93         Block b5 = b2.createNextBlock(coinsTo);
94         Block b6 = b5.createNextBlock(coinsTo);
95         assertTrue(chain.add(b5));
96         assertTrue(chain.add(b6));
97         //
98         //     genesis -> b1 -> b2 -> b5 -> b6
99         //                  \-> b3 -> b4
100         //
101         assertTrue(reorgHappened[0]);
102         assertEquals("200.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
103     }
104
105     @Test
106     public void testForking2() throws Exception {
107         // Check that if the chain forks and new coins are received in the alternate chain our balance goes up
108         // after the re-org takes place.
109         Block b1 = unitTestParams.genesisBlock.createNextBlock(someOtherGuy);
110         Block b2 = b1.createNextBlock(someOtherGuy);
111         assertTrue(chain.add(b1));
112         assertTrue(chain.add(b2));
113         //     genesis -> b1 -> b2
114         //                  \-> b3 -> b4
115         assertEquals(BigInteger.ZERO, wallet.getBalance());
116         Block b3 = b1.createNextBlock(coinsTo);
117         Block b4 = b3.createNextBlock(someOtherGuy);
118         assertTrue(chain.add(b3));
119         assertEquals(BigInteger.ZERO, wallet.getBalance());
120         assertTrue(chain.add(b4));
121         assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
122     }
123
124     @Test
125     public void testForking3() throws Exception {
126         // Check that we can handle our own spends being rolled back by a fork.
127         Block b1 = unitTestParams.genesisBlock.createNextBlock(coinsTo);
128         chain.add(b1);
129         assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
130         Address dest = new ECKey().toAddress(unitTestParams);
131         Transaction spend = wallet.createSend(dest, Utils.toNanoCoins(10, 0));
132         wallet.commitTx(spend);
133         // Waiting for confirmation ...
134         assertEquals(BigInteger.ZERO, wallet.getBalance());
135         Block b2 = b1.createNextBlock(someOtherGuy);
136         b2.addTransaction(spend);
137         b2.solve();
138         chain.add(b2);
139         assertEquals(Utils.toNanoCoins(40, 0), wallet.getBalance());
140         // genesis -> b1 (receive coins) -> b2 (spend coins)
141         //                               \-> b3 -> b4
142         Block b3 = b1.createNextBlock(someOtherGuy);
143         Block b4 = b3.createNextBlock(someOtherGuy);
144         chain.add(b3);
145         chain.add(b4);
146         // b4 causes a re-org that should make our spend go inactive. Because the inputs are already spent our
147         // available balance drops to zero again.
148         assertEquals(BigInteger.ZERO, wallet.getBalance(Wallet.BalanceType.AVAILABLE));
149         // We estimate that it'll make it back into the block chain (we know we won't double spend).
150         // assertEquals(Utils.toNanoCoins(40, 0), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
151     }
152
153     @Test
154     public void testForking4() throws Exception {
155         // Check that we can handle external spends on an inactive chain becoming active. An external spend is where
156         // we see a transaction that spends our own coins but we did not broadcast it ourselves. This happens when
157         // keys are being shared between wallets.
158         Block b1 = unitTestParams.genesisBlock.createNextBlock(coinsTo);
159         chain.add(b1);
160         assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
161         Address dest = new ECKey().toAddress(unitTestParams);
162         Transaction spend = wallet.createSend(dest, Utils.toNanoCoins(50, 0));
163         // We do NOT confirm the spend here. That means it's not considered to be pending because createSend is
164         // stateless. For our purposes it is as if some other program with our keys created the tx.
165         //
166         // genesis -> b1 (receive 50) --> b2
167         //                            \-> b3 (external spend) -> b4
168         Block b2 = b1.createNextBlock(someOtherGuy);
169         chain.add(b2);
170         Block b3 = b1.createNextBlock(someOtherGuy);
171         b3.addTransaction(spend);
172         b3.solve();
173         chain.add(b3);
174         // The external spend is not active yet.
175         assertEquals(Utils.toNanoCoins(50, 0), wallet.getBalance());
176         Block b4 = b3.createNextBlock(someOtherGuy);
177         chain.add(b4);
178         // The external spend is now active.
179         assertEquals(Utils.toNanoCoins(0, 0), wallet.getBalance());
180     }
181
182     @Test
183     public void testDoubleSpendOnFork() throws Exception {
184         // Check what happens when a re-org happens and one of our confirmed transactions becomes invalidated by a
185         // double spend on the new best chain.
186
187         final boolean[] eventCalled = new boolean[1];
188         wallet.addEventListener(new AbstractWalletEventListener() {
189             @Override
190             public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
191                 super.onTransactionConfidenceChanged(wallet, tx);
192                 if (tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND)
193                     eventCalled[0] = true;
194             }
195         });
196
197         Block b1 = unitTestParams.genesisBlock.createNextBlock(coinsTo);
198         chain.add(b1);
199
200         Transaction t1 = wallet.createSend(someOtherGuy, Utils.toNanoCoins(10, 0));
201         Address yetAnotherGuy = new ECKey().toAddress(unitTestParams);
202         Transaction t2 = wallet.createSend(yetAnotherGuy, Utils.toNanoCoins(20, 0));
203         wallet.commitTx(t1);
204         // Receive t1 as confirmed by the network.
205         Block b2 = b1.createNextBlock(new ECKey().toAddress(unitTestParams));
206         b2.addTransaction(t1);
207         b2.solve();
208         chain.add(b2);
209
210         // Now we make a double spend become active after a re-org.
211         Block b3 = b1.createNextBlock(new ECKey().toAddress(unitTestParams));
212         b3.addTransaction(t2);
213         b3.solve();
214         chain.add(b3);  // Side chain.
215         Block b4 = b3.createNextBlock(new ECKey().toAddress(unitTestParams));
216         chain.add(b4);  // New best chain.
217
218         // Should have seen a double spend.
219         assertTrue(eventCalled[0]);
220         assertEquals(Utils.toNanoCoins(30, 0), wallet.getBalance());
221     }
222
223     @Test
224     public void testDoubleSpendOnForkPending() throws Exception {
225         // Check what happens when a re-org happens and one of our UNconfirmed transactions becomes invalidated by a
226         // double spend on the new best chain.
227
228         final Transaction[] eventDead = new Transaction[1];
229         final Transaction[] eventReplacement = new Transaction[1];
230         wallet.addEventListener(new AbstractWalletEventListener() {
231             @Override
232             public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
233                 super.onTransactionConfidenceChanged(wallet, tx);
234                 eventDead[0] = tx;
235                 eventReplacement[0] = tx.getConfidence().getOverridingTransaction();
236             }
237         });
238
239         // Start with 50 coins.
240         Block b1 = unitTestParams.genesisBlock.createNextBlock(coinsTo);
241         chain.add(b1);
242
243         Transaction t1 = wallet.createSend(someOtherGuy, Utils.toNanoCoins(10, 0));
244         Address yetAnotherGuy = new ECKey().toAddress(unitTestParams);
245         Transaction t2 = wallet.createSend(yetAnotherGuy, Utils.toNanoCoins(20, 0));
246         wallet.commitTx(t1);
247         // t1 is still pending ...
248         Block b2 = b1.createNextBlock(new ECKey().toAddress(unitTestParams));
249         chain.add(b2);
250         assertEquals(Utils.toNanoCoins(0, 0), wallet.getBalance());
251         assertEquals(Utils.toNanoCoins(40, 0), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
252
253         // Now we make a double spend become active after a re-org.
254         // genesis -> b1 -> b2 [t1 pending]
255         //              \-> b3 (t2) -> b4
256         Block b3 = b1.createNextBlock(new ECKey().toAddress(unitTestParams));
257         b3.addTransaction(t2);
258         b3.solve();
259         chain.add(b3);  // Side chain.
260         Block b4 = b3.createNextBlock(new ECKey().toAddress(unitTestParams));
261         chain.add(b4);  // New best chain.
262
263         // Should have seen a double spend against the pending pool.
264         assertEquals(t1, eventDead[0]);
265         assertEquals(t2, eventReplacement[0]);
266         assertEquals(Utils.toNanoCoins(30, 0), wallet.getBalance());
267
268         // ... and back to our own parallel universe.
269         Block b5 = b2.createNextBlock(new ECKey().toAddress(unitTestParams));
270         chain.add(b5);
271         Block b6 = b5.createNextBlock(new ECKey().toAddress(unitTestParams));
272         chain.add(b6);
273         // genesis -> b1 -> b2 -> b5 -> b6 [t1 pending]
274         //              \-> b3 [t2 inactive] -> b4
275         assertEquals(Utils.toNanoCoins(0, 0), wallet.getBalance());
276         assertEquals(Utils.toNanoCoins(40, 0), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
277     }
278
279     @Test
280     public void txConfidenceLevels() throws Exception {
281         // Check that as the chain forks and re-orgs, the confidence data associated with each transaction is
282         // maintained correctly.
283         final ArrayList<Transaction> txns = new ArrayList<Transaction>(2);
284         wallet.addEventListener(new AbstractWalletEventListener() {
285             @Override
286             public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
287                 txns.add(tx);
288             }
289         });
290
291         // Start by building a couple of blocks on top of the genesis block.
292         Block b1 = unitTestParams.genesisBlock.createNextBlock(coinsTo);
293         Block b2 = b1.createNextBlock(coinsTo);
294         assertTrue(chain.add(b1));
295         assertTrue(chain.add(b2));
296         // Check the transaction confidence levels are correct.
297         assertEquals(2, txns.size());
298         assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight());
299         assertEquals(2, txns.get(1).getConfidence().getAppearedAtChainHeight());
300         assertEquals(1, txns.get(1).getConfidence().getDepthInBlocks(chain));
301         assertEquals(2, txns.get(0).getConfidence().getDepthInBlocks(chain));
302         assertEquals(10, txns.get(0).getConfidence().getWorkDone(chain).intValue());
303         assertEquals(6,  txns.get(1).getConfidence().getWorkDone(chain).intValue());
304         // We now have the following chain:
305         //     genesis -> b1 -> b2
306         //
307         // so fork like this:
308         //
309         //     genesis -> b1 -> b2
310         //                  \-> b3
311         //
312         // Nothing should happen at this point. We saw b2 first so it takes priority.
313         Block b3 = b1.createNextBlock(someOtherGuy);
314         assertTrue(chain.add(b3));
315         assertEquals(2, txns.size());
316         assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight());
317         assertEquals(2, txns.get(1).getConfidence().getAppearedAtChainHeight());
318         // Now we add another block to make the alternative chain longer.
319         assertTrue(chain.add(b3.createNextBlock(someOtherGuy)));
320         //
321         //     genesis -> b1 -> b2
322         //                  \-> b3 -> b4
323         //
324         assertEquals(2, txns.size());
325         assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight());
326         assertEquals(TransactionConfidence.ConfidenceType.NOT_IN_BEST_CHAIN, txns.get(1).getConfidence().getConfidenceType());
327         try {
328             txns.get(1).getConfidence().getAppearedAtChainHeight();
329             fail();
330         } catch (IllegalStateException e) {}
331         // ... and back to the first chain.
332         Block b5 = b2.createNextBlock(coinsTo);
333         Block b6 = b5.createNextBlock(coinsTo);
334         assertTrue(chain.add(b5));
335         assertTrue(chain.add(b6));
336         //
337         //     genesis -> b1 -> b2 -> b5 -> b6
338         //                  \-> b3 -> b4
339         //
340
341         // This should be enabled, once we figure out the best way to inform the user of how the wallet is changing
342         // during the re-org.
343         // assertEquals(4, txns.size());
344
345         assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight());
346         assertEquals(2, txns.get(1).getConfidence().getAppearedAtChainHeight());
347         assertEquals("200.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
348     }
349 }