mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-07-29 11:01:22 +00:00
Another rewrite of the re-org handling:
- Split the unit tests for this into a separate file - Add more tests for double spends, reversal of external spends and more edge cases - Handle the case where transactions should be resurrected after a re-org - Handle the case where transactions are overridden by double spends Should address [some of] the points Miron raised during his review. There are likely still bugs but it's a lot closer to correct than before.
This commit is contained in:
@@ -24,10 +24,8 @@ import java.math.BigInteger;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
// Tests still to write:
|
||||
// - Fragmented chains can be joined together.
|
||||
// - Longest testNetChain is selected based on total difficulty not length.
|
||||
// - Many more ...
|
||||
// NOTE: Handling of chain splits/reorgs are in ChainSplitTests.
|
||||
|
||||
public class BlockChainTest {
|
||||
private static final NetworkParameters testNet = NetworkParameters.testNet();
|
||||
private BlockChain testNetChain;
|
||||
@@ -88,140 +86,6 @@ public class BlockChainTest {
|
||||
assertEquals(chain.getChainHead().getHeader(), b3.cloneAsHeader());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForking1() throws Exception {
|
||||
// Check that if the block chain forks, we end up using the right chain. Only tests inbound transactions
|
||||
// (receiving coins). Checking that we understand reversed spends is in testForking2.
|
||||
|
||||
// TODO: Change this test to not use coinbase transactions as they are special (maturity rules).
|
||||
final boolean[] reorgHappened = new boolean[1];
|
||||
reorgHappened[0] = false;
|
||||
wallet.addEventListener(new WalletEventListener() {
|
||||
@Override
|
||||
public void onReorganize() {
|
||||
reorgHappened[0] = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Start by building a couple of blocks on top of the genesis block.
|
||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo);
|
||||
Block b2 = b1.createNextBlock(coinbaseTo);
|
||||
assertTrue(chain.add(b1));
|
||||
assertTrue(chain.add(b2));
|
||||
assertFalse(reorgHappened[0]);
|
||||
// We got two blocks which generated 50 coins each, to us.
|
||||
assertEquals("100.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
// We now have the following chain:
|
||||
// genesis -> b1 -> b2
|
||||
//
|
||||
// so fork like this:
|
||||
//
|
||||
// genesis -> b1 -> b2
|
||||
// \-> b3
|
||||
//
|
||||
// Nothing should happen at this point. We saw b2 first so it takes priority.
|
||||
Block b3 = b1.createNextBlock(someOtherGuy);
|
||||
assertTrue(chain.add(b3));
|
||||
assertFalse(reorgHappened[0]); // No re-org took place.
|
||||
assertEquals("100.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
// Now we add another block to make the alternative chain longer.
|
||||
assertTrue(chain.add(b3.createNextBlock(someOtherGuy)));
|
||||
assertTrue(reorgHappened[0]); // Re-org took place.
|
||||
reorgHappened[0] = false;
|
||||
//
|
||||
// genesis -> b1 -> b2
|
||||
// \-> b3 -> b4
|
||||
//
|
||||
// We lost some coins! b2 is no longer a part of the best chain so our balance should drop to 50 again.
|
||||
assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
// ... and back to the first chain.
|
||||
Block b5 = b2.createNextBlock(coinbaseTo);
|
||||
Block b6 = b5.createNextBlock(coinbaseTo);
|
||||
assertTrue(chain.add(b5));
|
||||
assertTrue(chain.add(b6));
|
||||
//
|
||||
// genesis -> b1 -> b2 -> b5 -> b6
|
||||
// \-> b3 -> b4
|
||||
//
|
||||
assertTrue(reorgHappened[0]);
|
||||
assertEquals("200.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForking2() throws Exception {
|
||||
// Check that if the chain forks and new coins are received in the alternate chain our balance goes up.
|
||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(someOtherGuy);
|
||||
Block b2 = b1.createNextBlock(someOtherGuy);
|
||||
assertTrue(chain.add(b1));
|
||||
assertTrue(chain.add(b2));
|
||||
// genesis -> b1 -> b2
|
||||
// \-> b3 -> b4
|
||||
assertEquals(BigInteger.ZERO, wallet.getBalance());
|
||||
Block b3 = b1.createNextBlock(coinbaseTo);
|
||||
Block b4 = b3.createNextBlock(someOtherGuy);
|
||||
assertTrue(chain.add(b3));
|
||||
assertTrue(chain.add(b4));
|
||||
assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForking3() throws Exception {
|
||||
// Check that we can handle our own spends being rolled back by a fork.
|
||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo);
|
||||
chain.add(b1);
|
||||
assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
Address dest = new ECKey().toAddress(unitTestParams);
|
||||
Transaction spend = wallet.createSend(dest, Utils.toNanoCoins(10, 0));
|
||||
wallet.confirmSend(spend);
|
||||
// Waiting for confirmation ...
|
||||
assertEquals(BigInteger.ZERO, wallet.getBalance());
|
||||
Block b2 = b1.createNextBlock(someOtherGuy);
|
||||
b2.addTransaction(spend);
|
||||
b2.solve();
|
||||
chain.add(b2);
|
||||
assertEquals(Utils.toNanoCoins(40, 0), wallet.getBalance());
|
||||
// genesis -> b1 (receive coins) -> b2 (spend coins)
|
||||
// \-> b3 -> b4
|
||||
Block b3 = b1.createNextBlock(someOtherGuy);
|
||||
Block b4 = b3.createNextBlock(someOtherGuy);
|
||||
chain.add(b3);
|
||||
chain.add(b4);
|
||||
// b4 causes a re-org that should make our spend go inactive. Because the inputs are already spent our balance
|
||||
// drops to zero again.
|
||||
assertEquals(BigInteger.ZERO, wallet.getBalance());
|
||||
// Not pending .... we don't know if our spend will EVER become active again (if there's an attack it may not).
|
||||
assertEquals(0, wallet.getPendingTransactions().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForking4() throws Exception {
|
||||
// Check that we can handle external spends on an inactive chain becoming active. An external spend is where
|
||||
// we see a transaction that spends our own coins but we did not broadcast it ourselves. This happens when
|
||||
// keys are being shared between wallets.
|
||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo);
|
||||
chain.add(b1);
|
||||
assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
Address dest = new ECKey().toAddress(unitTestParams);
|
||||
Transaction spend = wallet.createSend(dest, Utils.toNanoCoins(50, 0));
|
||||
// We do NOT confirm the spend here. That means it's not considered to be pending because createSend is
|
||||
// stateless. For our purposes it is as if some other program with our keys created the tx.
|
||||
//
|
||||
// genesis -> b1 (receive 50) --> b2
|
||||
// \-> b3 (external spend) -> b4
|
||||
Block b2 = b1.createNextBlock(someOtherGuy);
|
||||
chain.add(b2);
|
||||
Block b3 = b1.createNextBlock(someOtherGuy);
|
||||
b3.addTransaction(spend);
|
||||
b3.solve();
|
||||
chain.add(b3);
|
||||
// The external spend is not active yet.
|
||||
assertEquals(Utils.toNanoCoins(50, 0), wallet.getBalance());
|
||||
Block b4 = b3.createNextBlock(someOtherGuy);
|
||||
chain.add(b4);
|
||||
// The external spend is now active.
|
||||
assertEquals(Utils.toNanoCoins(0, 0), wallet.getBalance());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifficultyTransitions() throws Exception {
|
||||
// Add a bunch of blocks in a loop until we reach a difficulty transition point. The unit test params have an
|
||||
|
271
tests/com/google/bitcoin/core/ChainSplitTests.java
Normal file
271
tests/com/google/bitcoin/core/ChainSplitTests.java
Normal file
@@ -0,0 +1,271 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.bitcoin.core;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ChainSplitTests {
|
||||
private NetworkParameters unitTestParams;
|
||||
private Wallet wallet;
|
||||
private BlockChain chain;
|
||||
private Address coinbaseTo;
|
||||
private Address someOtherGuy;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
unitTestParams = NetworkParameters.unitTests();
|
||||
wallet = new Wallet(unitTestParams);
|
||||
wallet.addKey(new ECKey());
|
||||
chain = new BlockChain(unitTestParams, wallet, new MemoryBlockStore(unitTestParams));
|
||||
coinbaseTo = wallet.keychain.get(0).toAddress(unitTestParams);
|
||||
someOtherGuy = new ECKey().toAddress(unitTestParams);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForking1() throws Exception {
|
||||
// Check that if the block chain forks, we end up using the right chain. Only tests inbound transactions
|
||||
// (receiving coins). Checking that we understand reversed spends is in testForking2.
|
||||
|
||||
// TODO: Change this test to not use coinbase transactions as they are special (maturity rules).
|
||||
final boolean[] reorgHappened = new boolean[1];
|
||||
reorgHappened[0] = false;
|
||||
wallet.addEventListener(new WalletEventListener() {
|
||||
@Override
|
||||
public void onReorganize() {
|
||||
reorgHappened[0] = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Start by building a couple of blocks on top of the genesis block.
|
||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo);
|
||||
Block b2 = b1.createNextBlock(coinbaseTo);
|
||||
assertTrue(chain.add(b1));
|
||||
assertTrue(chain.add(b2));
|
||||
assertFalse(reorgHappened[0]);
|
||||
// We got two blocks which generated 50 coins each, to us.
|
||||
assertEquals("100.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
// We now have the following chain:
|
||||
// genesis -> b1 -> b2
|
||||
//
|
||||
// so fork like this:
|
||||
//
|
||||
// genesis -> b1 -> b2
|
||||
// \-> b3
|
||||
//
|
||||
// Nothing should happen at this point. We saw b2 first so it takes priority.
|
||||
Block b3 = b1.createNextBlock(someOtherGuy);
|
||||
assertTrue(chain.add(b3));
|
||||
assertFalse(reorgHappened[0]); // No re-org took place.
|
||||
assertEquals("100.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
// Now we add another block to make the alternative chain longer.
|
||||
assertTrue(chain.add(b3.createNextBlock(someOtherGuy)));
|
||||
assertTrue(reorgHappened[0]); // Re-org took place.
|
||||
reorgHappened[0] = false;
|
||||
//
|
||||
// genesis -> b1 -> b2
|
||||
// \-> b3 -> b4
|
||||
//
|
||||
// We lost some coins! b2 is no longer a part of the best chain so our available balance should drop to 50.
|
||||
assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
// ... and back to the first chain.
|
||||
Block b5 = b2.createNextBlock(coinbaseTo);
|
||||
Block b6 = b5.createNextBlock(coinbaseTo);
|
||||
assertTrue(chain.add(b5));
|
||||
assertTrue(chain.add(b6));
|
||||
//
|
||||
// genesis -> b1 -> b2 -> b5 -> b6
|
||||
// \-> b3 -> b4
|
||||
//
|
||||
assertTrue(reorgHappened[0]);
|
||||
assertEquals("200.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForking2() throws Exception {
|
||||
// Check that if the chain forks and new coins are received in the alternate chain our balance goes up
|
||||
// after the re-org takes place.
|
||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(someOtherGuy);
|
||||
Block b2 = b1.createNextBlock(someOtherGuy);
|
||||
assertTrue(chain.add(b1));
|
||||
assertTrue(chain.add(b2));
|
||||
// genesis -> b1 -> b2
|
||||
// \-> b3 -> b4
|
||||
assertEquals(BigInteger.ZERO, wallet.getBalance());
|
||||
Block b3 = b1.createNextBlock(coinbaseTo);
|
||||
Block b4 = b3.createNextBlock(someOtherGuy);
|
||||
assertTrue(chain.add(b3));
|
||||
assertEquals(BigInteger.ZERO, wallet.getBalance());
|
||||
assertTrue(chain.add(b4));
|
||||
assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForking3() throws Exception {
|
||||
// Check that we can handle our own spends being rolled back by a fork.
|
||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo);
|
||||
chain.add(b1);
|
||||
assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
Address dest = new ECKey().toAddress(unitTestParams);
|
||||
Transaction spend = wallet.createSend(dest, Utils.toNanoCoins(10, 0));
|
||||
wallet.confirmSend(spend);
|
||||
// Waiting for confirmation ...
|
||||
assertEquals(BigInteger.ZERO, wallet.getBalance());
|
||||
Block b2 = b1.createNextBlock(someOtherGuy);
|
||||
b2.addTransaction(spend);
|
||||
b2.solve();
|
||||
chain.add(b2);
|
||||
assertEquals(Utils.toNanoCoins(40, 0), wallet.getBalance());
|
||||
// genesis -> b1 (receive coins) -> b2 (spend coins)
|
||||
// \-> b3 -> b4
|
||||
Block b3 = b1.createNextBlock(someOtherGuy);
|
||||
Block b4 = b3.createNextBlock(someOtherGuy);
|
||||
chain.add(b3);
|
||||
chain.add(b4);
|
||||
// b4 causes a re-org that should make our spend go inactive. Because the inputs are already spent our
|
||||
// available balance drops to zero again.
|
||||
assertEquals(BigInteger.ZERO, wallet.getBalance(Wallet.BalanceType.AVAILABLE));
|
||||
// We estimate that it'll make it back into the block chain (we know we won't double spend).
|
||||
// assertEquals(Utils.toNanoCoins(40, 0), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForking4() throws Exception {
|
||||
// Check that we can handle external spends on an inactive chain becoming active. An external spend is where
|
||||
// we see a transaction that spends our own coins but we did not broadcast it ourselves. This happens when
|
||||
// keys are being shared between wallets.
|
||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo);
|
||||
chain.add(b1);
|
||||
assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
Address dest = new ECKey().toAddress(unitTestParams);
|
||||
Transaction spend = wallet.createSend(dest, Utils.toNanoCoins(50, 0));
|
||||
// We do NOT confirm the spend here. That means it's not considered to be pending because createSend is
|
||||
// stateless. For our purposes it is as if some other program with our keys created the tx.
|
||||
//
|
||||
// genesis -> b1 (receive 50) --> b2
|
||||
// \-> b3 (external spend) -> b4
|
||||
Block b2 = b1.createNextBlock(someOtherGuy);
|
||||
chain.add(b2);
|
||||
Block b3 = b1.createNextBlock(someOtherGuy);
|
||||
b3.addTransaction(spend);
|
||||
b3.solve();
|
||||
chain.add(b3);
|
||||
// The external spend is not active yet.
|
||||
assertEquals(Utils.toNanoCoins(50, 0), wallet.getBalance());
|
||||
Block b4 = b3.createNextBlock(someOtherGuy);
|
||||
chain.add(b4);
|
||||
// The external spend is now active.
|
||||
assertEquals(Utils.toNanoCoins(0, 0), wallet.getBalance());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleSpendOnFork() throws Exception {
|
||||
// Check what happens when a re-org happens and one of our confirmed transactions becomes invalidated by a
|
||||
// double spend on the new best chain.
|
||||
|
||||
final boolean[] eventCalled = new boolean[1];
|
||||
wallet.addEventListener(new WalletEventListener() {
|
||||
@Override
|
||||
public void onDeadTransaction(Transaction deadTx, Transaction replacementTx) {
|
||||
eventCalled[0] = true;
|
||||
}
|
||||
});
|
||||
|
||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo);
|
||||
chain.add(b1);
|
||||
|
||||
Transaction t1 = wallet.createSend(someOtherGuy, Utils.toNanoCoins(10, 0));
|
||||
Address yetAnotherGuy = new ECKey().toAddress(unitTestParams);
|
||||
Transaction t2 = wallet.createSend(yetAnotherGuy, Utils.toNanoCoins(20, 0));
|
||||
wallet.confirmSend(t1);
|
||||
// Receive t1 as confirmed by the network.
|
||||
Block b2 = b1.createNextBlock(new ECKey().toAddress(unitTestParams));
|
||||
b2.addTransaction(t1);
|
||||
b2.solve();
|
||||
chain.add(b2);
|
||||
|
||||
// Now we make a double spend become active after a re-org.
|
||||
Block b3 = b1.createNextBlock(new ECKey().toAddress(unitTestParams));
|
||||
b3.addTransaction(t2);
|
||||
b3.solve();
|
||||
chain.add(b3); // Side chain.
|
||||
Block b4 = b3.createNextBlock(new ECKey().toAddress(unitTestParams));
|
||||
chain.add(b4); // New best chain.
|
||||
|
||||
// Should have seen a double spend.
|
||||
assertTrue(eventCalled[0]);
|
||||
assertEquals(Utils.toNanoCoins(30, 0), wallet.getBalance());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleSpendOnForkPending() throws Exception {
|
||||
// Check what happens when a re-org happens and one of our UNconfirmed transactions becomes invalidated by a
|
||||
// double spend on the new best chain.
|
||||
|
||||
final boolean[] eventCalled = new boolean[1];
|
||||
wallet.addEventListener(new WalletEventListener() {
|
||||
@Override
|
||||
public void onDeadTransaction(Transaction deadTx, Transaction replacementTx) {
|
||||
eventCalled[0] = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Start with 50 coins.
|
||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo);
|
||||
chain.add(b1);
|
||||
|
||||
Transaction t1 = wallet.createSend(someOtherGuy, Utils.toNanoCoins(10, 0));
|
||||
Address yetAnotherGuy = new ECKey().toAddress(unitTestParams);
|
||||
Transaction t2 = wallet.createSend(yetAnotherGuy, Utils.toNanoCoins(20, 0));
|
||||
wallet.confirmSend(t1);
|
||||
// t1 is still pending ...
|
||||
Block b2 = b1.createNextBlock(new ECKey().toAddress(unitTestParams));
|
||||
chain.add(b2);
|
||||
assertEquals(Utils.toNanoCoins(0, 0), wallet.getBalance());
|
||||
assertEquals(Utils.toNanoCoins(40, 0), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
|
||||
|
||||
// Now we make a double spend become active after a re-org.
|
||||
// genesis -> b1 -> b2 [t1 pending]
|
||||
// \-> b3 (t2) -> b4
|
||||
Block b3 = b1.createNextBlock(new ECKey().toAddress(unitTestParams));
|
||||
b3.addTransaction(t2);
|
||||
b3.solve();
|
||||
chain.add(b3); // Side chain.
|
||||
Block b4 = b3.createNextBlock(new ECKey().toAddress(unitTestParams));
|
||||
chain.add(b4); // New best chain.
|
||||
|
||||
// Should have seen a double spend against the pending pool.
|
||||
assertTrue(eventCalled[0]);
|
||||
assertEquals(Utils.toNanoCoins(30, 0), wallet.getBalance());
|
||||
|
||||
// ... and back to our own parallel universe.
|
||||
Block b5 = b2.createNextBlock(new ECKey().toAddress(unitTestParams));
|
||||
chain.add(b5);
|
||||
Block b6 = b5.createNextBlock(new ECKey().toAddress(unitTestParams));
|
||||
chain.add(b6);
|
||||
// genesis -> b1 -> b2 -> b5 -> b6 [t1 pending]
|
||||
// \-> b3 [t2 inactive] -> b4
|
||||
assertEquals(Utils.toNanoCoins(0, 0), wallet.getBalance());
|
||||
assertEquals(Utils.toNanoCoins(40, 0), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
|
||||
}
|
||||
}
|
@@ -23,6 +23,7 @@ import java.math.BigInteger;
|
||||
|
||||
import static com.google.bitcoin.core.Utils.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class WalletTest {
|
||||
@@ -41,16 +42,17 @@ public class WalletTest {
|
||||
blockStore = new MemoryBlockStore(params);
|
||||
}
|
||||
|
||||
private static byte fakeHashCounter = 0;
|
||||
private Transaction createFakeTx(BigInteger nanocoins, Address to) {
|
||||
Transaction t = new Transaction(params);
|
||||
TransactionOutput o1 = new TransactionOutput(params, nanocoins, to, t);
|
||||
TransactionOutput o1 = new TransactionOutput(params, t, nanocoins, to);
|
||||
t.addOutput(o1);
|
||||
// t1 is not a valid transaction - it has no inputs. Nonetheless, if we set it up with a fake hash it'll be
|
||||
// valid enough for these tests.
|
||||
byte[] hash = new byte[32];
|
||||
hash[0] = fakeHashCounter++;
|
||||
t.setFakeHashForTesting(new Sha256Hash(hash));
|
||||
// Make a previous tx simply to send us sufficient coins. This prev tx is not really valid but it doesn't
|
||||
// matter for our purposes.
|
||||
Transaction prevTx = new Transaction(params);
|
||||
TransactionOutput prevOut = new TransactionOutput(params, prevTx, nanocoins, to);
|
||||
prevTx.addOutput(prevOut);
|
||||
// Connect it.
|
||||
t.addInput(prevOut);
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -152,8 +154,11 @@ public class WalletTest {
|
||||
Transaction spend = wallet.createSend(new ECKey().toAddress(params), v3);
|
||||
wallet.confirmSend(spend);
|
||||
|
||||
// Balance should be 0.50 because the change output is pending confirmation by the network.
|
||||
assertEquals(toNanoCoins(0, 50), wallet.getBalance());
|
||||
// Available and estimated balances should not be the same. We don't check the exact available balance here
|
||||
// because it depends on the coin selection algorithm.
|
||||
assertEquals(toNanoCoins(4, 50), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
|
||||
assertFalse(wallet.getBalance(Wallet.BalanceType.AVAILABLE).equals(
|
||||
wallet.getBalance(Wallet.BalanceType.ESTIMATED)));
|
||||
|
||||
// Now confirm the transaction by including it into a block.
|
||||
StoredBlock b3 = createFakeBlock(spend).storedBlock;
|
||||
@@ -161,8 +166,7 @@ public class WalletTest {
|
||||
|
||||
// Change is confirmed. We started with 5.50 so we should have 4.50 left.
|
||||
BigInteger v4 = toNanoCoins(4, 50);
|
||||
assertEquals(bitcoinValueToFriendlyString(v4),
|
||||
bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
assertEquals(v4, wallet.getBalance(Wallet.BalanceType.AVAILABLE));
|
||||
}
|
||||
|
||||
// Intuitively you'd expect to be able to create a transaction with identical inputs and outputs and get an
|
||||
|
Reference in New Issue
Block a user