Add the start of unit tests covering chain splits/reorgs, along with supporting code. The tests don't pass yet so they are marked @Ignore.

This commit is contained in:
Mike Hearn 2011-03-15 13:58:52 +00:00
parent d58ad311fe
commit 5e2e48eb5a
3 changed files with 81 additions and 7 deletions

View File

@ -417,11 +417,20 @@ public class Block extends Message {
this.hash = null;
}
/** Adds a fake coinbase transaction for unit tests. */
void addFakeTransaction() {
static private int coinbaseCounter;
/** Adds a coinbase transaction to the block. This exists for unit tests. */
void addCoinbaseTransaction(Address to) {
transactions = new ArrayList<Transaction>();
Transaction coinbase = new Transaction(params);
coinbase.setFakeHashForTesting(Utils.doubleDigest("test tx".getBytes()));
// A real coinbase transaction has some stuff in the scriptSig like the extraNonce and difficulty. The
// transactions are distinguished by every TX output going to a different key.
//
// Here we will do things a bit differently so a new address isn't needed every time. We'll put a simple
// counter in the scriptSig so every transaction has a different hash. The output is also different.
// Real coinbase transactions use <pubkey> OP_CHECKSIG rather than a send to an address though there's
// nothing in the system that enforces that and both are just as valid.
coinbase.inputs.add(new TransactionInput(params, new byte[] { (byte) coinbaseCounter++ } ));
coinbase.outputs.add(new TransactionOutput(params, Utils.toNanoCoins(50, 0), to));
transactions.add(coinbase);
}
}

View File

@ -69,9 +69,8 @@ public class NetworkParameters implements Serializable {
return genesisBlock;
}
/** The test chain created by Gavin. */
public static NetworkParameters testNet() {
NetworkParameters n = new NetworkParameters();
/** Sets up the given NetworkParameters with testnet values. */
private static NetworkParameters createTestNet(NetworkParameters n) {
// Genesis hash is 0000000224b1593e3ff16a0e3b61285bbc393a39f78c8aa48c456142671f7110
n.proofOfWorkLimit = new BigInteger("0000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
n.packetMagic = 0xfabfb5daL;
@ -86,6 +85,12 @@ public class NetworkParameters implements Serializable {
return n;
}
/** The test chain created by Gavin. */
public static NetworkParameters testNet() {
NetworkParameters n = new NetworkParameters();
return createTestNet(n);
}
/** The primary BitCoin chain created by Satoshi. */
public static NetworkParameters prodNet() {
NetworkParameters n = new NetworkParameters();
@ -104,7 +109,8 @@ public class NetworkParameters implements Serializable {
/** Returns a testnet params modified to allow any difficulty target. */
static NetworkParameters unitTests() {
NetworkParameters n = NetworkParameters.testNet();
NetworkParameters n = new NetworkParameters();
n = createTestNet(n);
n.proofOfWorkLimit = new BigInteger("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
n.genesisBlock.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
return n;

View File

@ -18,6 +18,7 @@ package com.google.bitcoin.core;
import com.google.bitcoin.bouncycastle.util.encoders.Hex;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import java.math.BigInteger;
@ -62,6 +63,64 @@ public class BlockChainTest {
assertTrue(chain.add(b2));
}
private Block createNextBlock(Address to, Block prev) throws VerificationException {
Block b = new Block(prev.params);
b.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
b.addCoinbaseTransaction(to);
b.setPrevBlockHash(prev.getHash());
b.solve();
b.verify();
return b;
}
@Test @Ignore
public void testForking() throws Exception {
// Check that if the block chain forks, we end up using the right one.
NetworkParameters unitTestParams = NetworkParameters.unitTests();
Wallet wallet = new Wallet(unitTestParams);
wallet.addKey(new ECKey());
Address coinbaseTo = wallet.keychain.get(0).toAddress(unitTestParams);
// Start by building a couple of blocks on top of the genesis block.
Block b1 = createNextBlock(coinbaseTo, unitTestParams.genesisBlock);
Block b2 = createNextBlock(coinbaseTo, b1);
chain = new BlockChain(unitTestParams, wallet);
chain.add(b1);
chain.add(b2);
// 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.
Address someOtherGuy = new ECKey().toAddress(unitTestParams);
Block b3 = createNextBlock(someOtherGuy, b1);
chain.add(b3);
assertEquals("100.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
// Now we add another block to make the alternative chain longer.
chain.add(createNextBlock(someOtherGuy, b3));
//
// 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 = createNextBlock(coinbaseTo, b2);
Block b6 = createNextBlock(coinbaseTo, b5);
chain.add(b5);
chain.add(b6);
//
// genesis -> b1 -> b2 -> b5 -> b6
// \-> b3 -> b4
//
assertEquals("200.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
}
@Test
public void testBadDifficulty() throws Exception {
assertTrue(chain.add(getBlock1()));