3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-07 23:03:04 +00:00

Add height info to FullBlockTests, to verify the tests are correct.

This commit is contained in:
Matt Corallo 2013-04-22 16:03:40 -04:00
parent 9ddbcb88b9
commit 0390bc9371
3 changed files with 146 additions and 117 deletions

View File

@ -157,6 +157,9 @@ public class BitcoindComparisonTool {
} else if (!chain.getChainHead().getHeader().getHash().equals(block.hashChainTipAfterBlock)) { } else if (!chain.getChainHead().getHeader().getHash().equals(block.hashChainTipAfterBlock)) {
log.error("New block head didn't match the correct value after block \"" + block.blockName + "\""); log.error("New block head didn't match the correct value after block \"" + block.blockName + "\"");
invalidBlocks++; invalidBlocks++;
} else if (chain.getChainHead().getHeight() != block.heightAfterBlock) {
log.error("New block head didn't match the correct height after block " + block.blockName);
invalidBlocks++;
} }
bitcoind.sendMessage(block.block); bitcoind.sendMessage(block.block);

View File

@ -17,15 +17,25 @@ class BlockAndValidity {
boolean connects; boolean connects;
boolean throwsException; boolean throwsException;
Sha256Hash hashChainTipAfterBlock; Sha256Hash hashChainTipAfterBlock;
int heightAfterBlock;
String blockName; String blockName;
public BlockAndValidity(Block block, boolean connects, boolean throwsException, Sha256Hash hashChainTipAfterBlock, String blockName) {
public BlockAndValidity(Map<Sha256Hash, Integer> blockToHeightMap, Block block, boolean connects, boolean throwsException, Sha256Hash hashChainTipAfterBlock, int heightAfterBlock, String blockName) {
if (connects && throwsException) if (connects && throwsException)
throw new RuntimeException("A block cannot connect if an exception was thrown while adding it."); throw new RuntimeException("A block cannot connect if an exception was thrown while adding it.");
this.block = block; this.block = block;
this.connects = connects; this.connects = connects;
this.throwsException = throwsException; this.throwsException = throwsException;
this.hashChainTipAfterBlock = hashChainTipAfterBlock; this.hashChainTipAfterBlock = hashChainTipAfterBlock;
this.heightAfterBlock = heightAfterBlock;
this.blockName = blockName; this.blockName = blockName;
// Double-check that we are always marking any given block at the same height
Integer height = blockToHeightMap.get(hashChainTipAfterBlock);
if (height != null)
Preconditions.checkState(height == heightAfterBlock);
else
blockToHeightMap.put(hashChainTipAfterBlock, heightAfterBlock);
} }
} }
@ -55,6 +65,9 @@ public class FullBlockTestGenerator {
private ECKey coinbaseOutKey; private ECKey coinbaseOutKey;
private byte[] coinbaseOutKeyPubKey; private byte[] coinbaseOutKeyPubKey;
// Used to double-check that we are always using the right next-height
private Map<Sha256Hash, Integer> blockToHeightMap = new HashMap<Sha256Hash, Integer>();
public FullBlockTestGenerator(NetworkParameters params) { public FullBlockTestGenerator(NetworkParameters params) {
this.params = params; this.params = params;
coinbaseOutKey = new ECKey(); coinbaseOutKey = new ECKey();
@ -70,14 +83,14 @@ public class FullBlockTestGenerator {
int chainHeadHeight = 1; int chainHeadHeight = 1;
Block chainHead = params.genesisBlock.createNextBlockWithCoinbase(coinbaseOutKeyPubKey); Block chainHead = params.genesisBlock.createNextBlockWithCoinbase(coinbaseOutKeyPubKey);
blocks.add(new BlockAndValidity(chainHead, true, false, chainHead.getHash(), "Initial Block")); blocks.add(new BlockAndValidity(blockToHeightMap, chainHead, true, false, chainHead.getHash(), 1, "Initial Block"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, chainHead.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, chainHead.getTransactions().get(0).getHash()),
Utils.toNanoCoins(50, 0), chainHead.getTransactions().get(0).getOutputs().get(0).getScriptPubKey())); Utils.toNanoCoins(50, 0), chainHead.getTransactions().get(0).getOutputs().get(0).getScriptPubKey()));
for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) { for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
chainHead = chainHead.createNextBlockWithCoinbase(coinbaseOutKeyPubKey); chainHead = chainHead.createNextBlockWithCoinbase(coinbaseOutKeyPubKey);
chainHeadHeight++; chainHeadHeight++;
blocks.add(new BlockAndValidity(chainHead, true, false, chainHead.getHash(), "Initial Block chain output generation")); blocks.add(new BlockAndValidity(blockToHeightMap, chainHead, true, false, chainHead.getHash(), i+1, "Initial Block chain output generation"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, chainHead.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, chainHead.getTransactions().get(0).getHash()),
Utils.toNanoCoins(50, 0), chainHead.getTransactions().get(0).getOutputs().get(0).getScriptPubKey())); Utils.toNanoCoins(50, 0), chainHead.getTransactions().get(0).getOutputs().get(0).getScriptPubKey()));
@ -85,17 +98,17 @@ public class FullBlockTestGenerator {
// Start by building a couple of blocks on top of the genesis block. // Start by building a couple of blocks on top of the genesis block.
Block b1 = createNextBlock(chainHead, chainHeadHeight + 1, spendableOutputs.poll(), null); Block b1 = createNextBlock(chainHead, chainHeadHeight + 1, spendableOutputs.poll(), null);
blocks.add(new BlockAndValidity(b1, true, false, b1.getHash(), "b1")); blocks.add(new BlockAndValidity(blockToHeightMap, b1, true, false, b1.getHash(), chainHeadHeight + 1, "b1"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b1.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b1.getTransactions().get(0).getHash()),
b1.getTransactions().get(0).getOutputs().get(0).getValue(), b1.getTransactions().get(0).getOutputs().get(0).getValue(),
b1.getTransactions().get(0).getOutputs().get(0).getScriptPubKey())); b1.getTransactions().get(0).getOutputs().get(0).getScriptPubKey()));
TransactionOutPointWithValue out1 = spendableOutputs.poll(); TransactionOutPointWithValue out1 = spendableOutputs.poll(); Preconditions.checkState(out1 != null);
Block b2 = createNextBlock(b1, chainHeadHeight + 2, out1, null); Block b2 = createNextBlock(b1, chainHeadHeight + 2, out1, null);
blocks.add(new BlockAndValidity(b2, true, false, b2.getHash(), "b2")); blocks.add(new BlockAndValidity(blockToHeightMap, b2, true, false, b2.getHash(), chainHeadHeight + 2, "b2"));
// Make sure nothing funky happens if we try to re-add b2 // Make sure nothing funky happens if we try to re-add b2
blocks.add(new BlockAndValidity(b2, true, false, b2.getHash(), "b2")); blocks.add(new BlockAndValidity(blockToHeightMap, b2, true, false, b2.getHash(), chainHeadHeight + 2, "b2"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b2.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b2.getTransactions().get(0).getHash()),
b2.getTransactions().get(0).getOutputs().get(0).getValue(), b2.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -110,21 +123,21 @@ public class FullBlockTestGenerator {
// //
// Nothing should happen at this point. We saw b2 first so it takes priority. // Nothing should happen at this point. We saw b2 first so it takes priority.
Block b3 = createNextBlock(b1, chainHeadHeight + 2, out1, null); Block b3 = createNextBlock(b1, chainHeadHeight + 2, out1, null);
blocks.add(new BlockAndValidity(b3, true, false, b2.getHash(), "b3")); blocks.add(new BlockAndValidity(blockToHeightMap, b3, true, false, b2.getHash(), chainHeadHeight + 2, "b3"));
// Make sure nothing breaks if we add b3 twice // Make sure nothing breaks if we add b3 twice
blocks.add(new BlockAndValidity(b3, true, false, b2.getHash(), "b3")); blocks.add(new BlockAndValidity(blockToHeightMap, b3, true, false, b2.getHash(), chainHeadHeight + 2, "b3"));
// Now we add another block to make the alternative chain longer. // Now we add another block to make the alternative chain longer.
TransactionOutPointWithValue out2 = spendableOutputs.poll(); TransactionOutPointWithValue out2 = spendableOutputs.poll(); Preconditions.checkState(out2 != null);
Block b4 = createNextBlock(b3, chainHeadHeight + 3, out2, null); Block b4 = createNextBlock(b3, chainHeadHeight + 3, out2, null);
blocks.add(new BlockAndValidity(b4, true, false, b4.getHash(), "b4")); blocks.add(new BlockAndValidity(blockToHeightMap, b4, true, false, b4.getHash(), chainHeadHeight + 3, "b4"));
// //
// genesis -> b1 (0) -> b2 (1) // genesis -> b1 (0) -> b2 (1)
// \-> b3 (1) -> b4 (2) // \-> b3 (1) -> b4 (2)
// //
// ... and back to the first chain. // ... and back to the first chain.
Block b5 = createNextBlock(b2, chainHeadHeight + 3, out2, null); Block b5 = createNextBlock(b2, chainHeadHeight + 3, out2, null);
blocks.add(new BlockAndValidity(b5, true, false, b4.getHash(), "b5")); blocks.add(new BlockAndValidity(blockToHeightMap, b5, true, false, b4.getHash(), chainHeadHeight + 3, "b5"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b5.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b5.getTransactions().get(0).getHash()),
b5.getTransactions().get(0).getOutputs().get(0).getValue(), b5.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -133,7 +146,7 @@ public class FullBlockTestGenerator {
TransactionOutPointWithValue out3 = spendableOutputs.poll(); TransactionOutPointWithValue out3 = spendableOutputs.poll();
Block b6 = createNextBlock(b5, chainHeadHeight + 4, out3, null); Block b6 = createNextBlock(b5, chainHeadHeight + 4, out3, null);
blocks.add(new BlockAndValidity(b6, true, false, b6.getHash(), "b6")); blocks.add(new BlockAndValidity(blockToHeightMap, b6, true, false, b6.getHash(), chainHeadHeight + 4, "b6"));
// //
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
// \-> b3 (1) -> b4 (2) // \-> b3 (1) -> b4 (2)
@ -145,12 +158,12 @@ public class FullBlockTestGenerator {
// \-> b3 (1) -> b4 (2) // \-> b3 (1) -> b4 (2)
// //
Block b7 = createNextBlock(b5, chainHeadHeight + 5, out2, null); Block b7 = createNextBlock(b5, chainHeadHeight + 5, out2, null);
blocks.add(new BlockAndValidity(b7, true, false, b6.getHash(), "b7")); blocks.add(new BlockAndValidity(blockToHeightMap, b7, true, false, b6.getHash(), chainHeadHeight + 4, "b7"));
TransactionOutPointWithValue out4 = spendableOutputs.poll(); TransactionOutPointWithValue out4 = spendableOutputs.poll();
Block b8 = createNextBlock(b7, chainHeadHeight + 6, out4, null); Block b8 = createNextBlock(b7, chainHeadHeight + 6, out4, null);
blocks.add(new BlockAndValidity(b8, false, true, b6.getHash(), "b8")); blocks.add(new BlockAndValidity(blockToHeightMap, b8, false, true, b6.getHash(), chainHeadHeight + 4, "b8"));
// Try to create a block that has too much fee // Try to create a block that has too much fee
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -158,7 +171,7 @@ public class FullBlockTestGenerator {
// \-> b3 (1) -> b4 (2) // \-> b3 (1) -> b4 (2)
// //
Block b9 = createNextBlock(b6, chainHeadHeight + 5, out4, BigInteger.valueOf(1)); Block b9 = createNextBlock(b6, chainHeadHeight + 5, out4, BigInteger.valueOf(1));
blocks.add(new BlockAndValidity(b9, false, true, b6.getHash(), "b9")); blocks.add(new BlockAndValidity(blockToHeightMap, b9, false, true, b6.getHash(), chainHeadHeight + 4, "b9"));
// Create a fork that ends in a block with too much fee (the one that causes the reorg) // Create a fork that ends in a block with too much fee (the one that causes the reorg)
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -166,10 +179,10 @@ public class FullBlockTestGenerator {
// \-> b3 (1) -> b4 (2) // \-> b3 (1) -> b4 (2)
// //
Block b10 = createNextBlock(b5, chainHeadHeight + 4, out3, null); Block b10 = createNextBlock(b5, chainHeadHeight + 4, out3, null);
blocks.add(new BlockAndValidity(b10, true, false, b6.getHash(), "b10")); blocks.add(new BlockAndValidity(blockToHeightMap, b10, true, false, b6.getHash(), chainHeadHeight + 4, "b10"));
Block b11 = createNextBlock(b10, chainHeadHeight + 5, out4, BigInteger.valueOf(1)); Block b11 = createNextBlock(b10, chainHeadHeight + 5, out4, BigInteger.valueOf(1));
blocks.add(new BlockAndValidity(b11, false, true, b6.getHash(), "b11")); blocks.add(new BlockAndValidity(blockToHeightMap, b11, false, true, b6.getHash(), chainHeadHeight + 4, "b11"));
// Try again, but with a valid fork first // Try again, but with a valid fork first
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -184,9 +197,9 @@ public class FullBlockTestGenerator {
b12.getTransactions().get(0).getOutputs().get(0).getScriptPubKey())); b12.getTransactions().get(0).getOutputs().get(0).getScriptPubKey()));
Block b13 = createNextBlock(b12, chainHeadHeight + 5, out4, null); Block b13 = createNextBlock(b12, chainHeadHeight + 5, out4, null);
blocks.add(new BlockAndValidity(b13, false, false, b6.getHash(), "b13")); blocks.add(new BlockAndValidity(blockToHeightMap, b13, false, false, b6.getHash(), chainHeadHeight + 4, "b13"));
// Make sure we dont die if an orphan gets added twice // Make sure we dont die if an orphan gets added twice
blocks.add(new BlockAndValidity(b13, false, false, b6.getHash(), "b13")); blocks.add(new BlockAndValidity(blockToHeightMap, b13, false, false, b6.getHash(), chainHeadHeight + 4, "b13"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b13.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b13.getTransactions().get(0).getHash()),
b13.getTransactions().get(0).getOutputs().get(0).getValue(), b13.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -198,11 +211,11 @@ public class FullBlockTestGenerator {
// This will be "validly" added, though its actually invalid, it will just be marked orphan // This will be "validly" added, though its actually invalid, it will just be marked orphan
// and will be discarded when an attempt is made to reorg to it. // and will be discarded when an attempt is made to reorg to it.
// TODO: Use a WeakReference to check that it is freed properly after the reorg // TODO: Use a WeakReference to check that it is freed properly after the reorg
blocks.add(new BlockAndValidity(b14, false, false, b6.getHash(), "b14")); blocks.add(new BlockAndValidity(blockToHeightMap, b14, false, false, b6.getHash(), chainHeadHeight + 4, "b14"));
// Make sure we dont die if an orphan gets added twice // Make sure we dont die if an orphan gets added twice
blocks.add(new BlockAndValidity(b14, false, false, b6.getHash(), "b14")); blocks.add(new BlockAndValidity(blockToHeightMap, b14, false, false, b6.getHash(), chainHeadHeight + 4, "b14"));
blocks.add(new BlockAndValidity(b12, false, true, b13.getHash(), "b12")); blocks.add(new BlockAndValidity(blockToHeightMap, b12, false, true, b13.getHash(), chainHeadHeight + 5, "b12"));
// Add a block with MAX_BLOCK_SIGOPS and one with one more sigop // Add a block with MAX_BLOCK_SIGOPS and one with one more sigop
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -226,7 +239,7 @@ public class FullBlockTestGenerator {
} }
b15.solve(); b15.solve();
blocks.add(new BlockAndValidity(b15, true, false, b15.getHash(), "b15")); blocks.add(new BlockAndValidity(blockToHeightMap, b15, true, false, b15.getHash(), chainHeadHeight + 6, "b15"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b15.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b15.getTransactions().get(0).getHash()),
b15.getTransactions().get(0).getOutputs().get(0).getValue(), b15.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -251,7 +264,7 @@ public class FullBlockTestGenerator {
} }
b16.solve(); b16.solve();
blocks.add(new BlockAndValidity(b16, false, true, b15.getHash(), "b16")); blocks.add(new BlockAndValidity(blockToHeightMap, b16, false, true, b15.getHash(), chainHeadHeight + 6, "b16"));
// Attempt to spend a transaction created on a different fork // Attempt to spend a transaction created on a different fork
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -268,7 +281,7 @@ public class FullBlockTestGenerator {
b17.addTransaction(tx); b17.addTransaction(tx);
} }
b17.solve(); b17.solve();
blocks.add(new BlockAndValidity(b17, false, true, b15.getHash(), "b17")); blocks.add(new BlockAndValidity(blockToHeightMap, b17, false, true, b15.getHash(), chainHeadHeight + 6, "b17"));
// Attempt to spend a transaction created on a different fork (on a fork this time) // Attempt to spend a transaction created on a different fork (on a fork this time)
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -286,10 +299,10 @@ public class FullBlockTestGenerator {
b18.addTransaction(tx); b18.addTransaction(tx);
} }
b18.solve(); b18.solve();
blocks.add(new BlockAndValidity(b18, true, false, b15.getHash(), "b17")); blocks.add(new BlockAndValidity(blockToHeightMap, b18, true, false, b15.getHash(), chainHeadHeight + 6, "b17"));
Block b19 = createNextBlock(b18, chainHeadHeight + 7, out6, null); Block b19 = createNextBlock(b18, chainHeadHeight + 7, out6, null);
blocks.add(new BlockAndValidity(b19, false, true, b15.getHash(), "b19")); blocks.add(new BlockAndValidity(blockToHeightMap, b19, false, true, b15.getHash(), chainHeadHeight + 6, "b19"));
// Attempt to spend a coinbase at depth too low // Attempt to spend a coinbase at depth too low
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -299,7 +312,7 @@ public class FullBlockTestGenerator {
TransactionOutPointWithValue out7 = spendableOutputs.poll(); TransactionOutPointWithValue out7 = spendableOutputs.poll();
Block b20 = createNextBlock(b15, chainHeadHeight + 7, out7, null); Block b20 = createNextBlock(b15, chainHeadHeight + 7, out7, null);
blocks.add(new BlockAndValidity(b20, false, true, b15.getHash(), "b20")); blocks.add(new BlockAndValidity(blockToHeightMap, b20, false, true, b15.getHash(), chainHeadHeight + 6, "b20"));
// Attempt to spend a coinbase at depth too low (on a fork this time) // Attempt to spend a coinbase at depth too low (on a fork this time)
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -308,9 +321,9 @@ public class FullBlockTestGenerator {
// \-> b3 (1) -> b4 (2) // \-> b3 (1) -> b4 (2)
// //
Block b21 = createNextBlock(b13, chainHeadHeight + 6, out6, null); Block b21 = createNextBlock(b13, chainHeadHeight + 6, out6, null);
blocks.add(new BlockAndValidity(b21, true, false, b15.getHash(), "b21")); blocks.add(new BlockAndValidity(blockToHeightMap, b21, true, false, b15.getHash(), chainHeadHeight + 6, "b21"));
Block b22 = createNextBlock(b21, chainHeadHeight + 7, out5, null); Block b22 = createNextBlock(b21, chainHeadHeight + 7, out5, null);
blocks.add(new BlockAndValidity(b22, false, true, b15.getHash(), "b22")); blocks.add(new BlockAndValidity(blockToHeightMap, b22, false, true, b15.getHash(), chainHeadHeight + 6, "b22"));
// Create a block on either side of MAX_BLOCK_SIZE and make sure its accepted/rejected // Create a block on either side of MAX_BLOCK_SIZE and make sure its accepted/rejected
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -331,7 +344,7 @@ public class FullBlockTestGenerator {
b23.addTransaction(tx); b23.addTransaction(tx);
} }
b23.solve(); b23.solve();
blocks.add(new BlockAndValidity(b23, true, false, b23.getHash(), "b23")); blocks.add(new BlockAndValidity(blockToHeightMap, b23, true, false, b23.getHash(), chainHeadHeight + 7, "b23"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b23.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b23.getTransactions().get(0).getHash()),
b23.getTransactions().get(0).getOutputs().get(0).getValue(), b23.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -350,11 +363,11 @@ public class FullBlockTestGenerator {
b24.addTransaction(tx); b24.addTransaction(tx);
} }
b24.solve(); b24.solve();
blocks.add(new BlockAndValidity(b24, false, true, b23.getHash(), "b24")); blocks.add(new BlockAndValidity(blockToHeightMap, b24, false, true, b23.getHash(), chainHeadHeight + 7, "b24"));
// Extend the b24 chain to make sure bitcoind isn't accepting b24 // Extend the b24 chain to make sure bitcoind isn't accepting b24
Block b25 = createNextBlock(b24, chainHeadHeight + 8, out7, null); Block b25 = createNextBlock(b24, chainHeadHeight + 8, out7, null);
blocks.add(new BlockAndValidity(b25, false, false, b23.getHash(), "b25")); blocks.add(new BlockAndValidity(blockToHeightMap, b25, false, false, b23.getHash(), chainHeadHeight + 7, "b25"));
// Create blocks with a coinbase input script size out of range // Create blocks with a coinbase input script size out of range
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) // genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -367,11 +380,11 @@ public class FullBlockTestGenerator {
b26.getTransactions().get(0).getInputs().get(0).setScriptBytes(new byte[] {0}); b26.getTransactions().get(0).getInputs().get(0).setScriptBytes(new byte[] {0});
b26.setMerkleRoot(null); b26.setMerkleRoot(null);
b26.solve(); b26.solve();
blocks.add(new BlockAndValidity(b26, false, true, b23.getHash(), "b26")); blocks.add(new BlockAndValidity(blockToHeightMap, b26, false, true, b23.getHash(), chainHeadHeight + 7, "b26"));
// Extend the b26 chain to make sure bitcoind isn't accepting b26 // Extend the b26 chain to make sure bitcoind isn't accepting b26
Block b27 = createNextBlock(b26, chainHeadHeight + 8, out7, null); Block b27 = createNextBlock(b26, chainHeadHeight + 8, out7, null);
blocks.add(new BlockAndValidity(b27, false, false, b23.getHash(), "b27")); blocks.add(new BlockAndValidity(blockToHeightMap, b27, false, false, b23.getHash(), chainHeadHeight + 7, "b27"));
Block b28 = createNextBlock(b15, chainHeadHeight + 7, out6, null); Block b28 = createNextBlock(b15, chainHeadHeight + 7, out6, null);
{ {
@ -381,11 +394,11 @@ public class FullBlockTestGenerator {
} }
b28.setMerkleRoot(null); b28.setMerkleRoot(null);
b28.solve(); b28.solve();
blocks.add(new BlockAndValidity(b28, false, true, b23.getHash(), "b28")); blocks.add(new BlockAndValidity(blockToHeightMap, b28, false, true, b23.getHash(), chainHeadHeight + 7, "b28"));
// Extend the b28 chain to make sure bitcoind isn't accepting b28 // Extend the b28 chain to make sure bitcoind isn't accepting b28
Block b29 = createNextBlock(b28, chainHeadHeight + 8, out7, null); Block b29 = createNextBlock(b28, chainHeadHeight + 8, out7, null);
blocks.add(new BlockAndValidity(b29, false, false, b23.getHash(), "b29")); blocks.add(new BlockAndValidity(blockToHeightMap, b29, false, false, b23.getHash(), chainHeadHeight + 7, "b29"));
Block b30 = createNextBlock(b23, chainHeadHeight + 8, out7, null); Block b30 = createNextBlock(b23, chainHeadHeight + 8, out7, null);
{ {
@ -395,7 +408,7 @@ public class FullBlockTestGenerator {
} }
b30.setMerkleRoot(null); b30.setMerkleRoot(null);
b30.solve(); b30.solve();
blocks.add(new BlockAndValidity(b30, true, false, b30.getHash(), "b30")); blocks.add(new BlockAndValidity(blockToHeightMap, b30, true, false, b30.getHash(), chainHeadHeight + 8, "b30"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b30.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b30.getTransactions().get(0).getHash()),
b30.getTransactions().get(0).getOutputs().get(0).getValue(), b30.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -427,7 +440,7 @@ public class FullBlockTestGenerator {
} }
b31.solve(); b31.solve();
blocks.add(new BlockAndValidity(b31, true, false, b31.getHash(), "b31")); blocks.add(new BlockAndValidity(blockToHeightMap, b31, true, false, b31.getHash(), chainHeadHeight + 9, "b31"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b31.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b31.getTransactions().get(0).getHash()),
b31.getTransactions().get(0).getOutputs().get(0).getValue(), b31.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -454,7 +467,7 @@ public class FullBlockTestGenerator {
} }
b32.solve(); b32.solve();
blocks.add(new BlockAndValidity(b32, false, true, b31.getHash(), "b32")); blocks.add(new BlockAndValidity(blockToHeightMap, b32, false, true, b31.getHash(), chainHeadHeight + 9, "b32"));
Block b33 = createNextBlock(b31, chainHeadHeight + 10, out9, null); Block b33 = createNextBlock(b31, chainHeadHeight + 10, out9, null);
@ -474,7 +487,7 @@ public class FullBlockTestGenerator {
} }
b33.solve(); b33.solve();
blocks.add(new BlockAndValidity(b33, true, false, b33.getHash(), "b33")); blocks.add(new BlockAndValidity(blockToHeightMap, b33, true, false, b33.getHash(), chainHeadHeight + 10, "b33"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b33.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b33.getTransactions().get(0).getHash()),
b33.getTransactions().get(0).getOutputs().get(0).getValue(), b33.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -501,7 +514,7 @@ public class FullBlockTestGenerator {
} }
b34.solve(); b34.solve();
blocks.add(new BlockAndValidity(b34, false, true, b33.getHash(), "b34")); blocks.add(new BlockAndValidity(blockToHeightMap, b34, false, true, b33.getHash(), chainHeadHeight + 10, "b34"));
Block b35 = createNextBlock(b33, chainHeadHeight + 11, out10, null); Block b35 = createNextBlock(b33, chainHeadHeight + 11, out10, null);
@ -521,7 +534,7 @@ public class FullBlockTestGenerator {
} }
b35.solve(); b35.solve();
blocks.add(new BlockAndValidity(b35, true, false, b35.getHash(), "b35")); blocks.add(new BlockAndValidity(blockToHeightMap, b35, true, false, b35.getHash(), chainHeadHeight + 11, "b35"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b35.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b35.getTransactions().get(0).getHash()),
b35.getTransactions().get(0).getOutputs().get(0).getValue(), b35.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -546,7 +559,7 @@ public class FullBlockTestGenerator {
} }
b36.solve(); b36.solve();
blocks.add(new BlockAndValidity(b36, false, true, b35.getHash(), "b36")); blocks.add(new BlockAndValidity(blockToHeightMap, b36, false, true, b35.getHash(), chainHeadHeight + 11, "b36"));
// Check spending of a transaction in a block which failed to connect // Check spending of a transaction in a block which failed to connect
// (test block store transaction abort handling, not that it should get this far if that's broken...) // (test block store transaction abort handling, not that it should get this far if that's broken...)
@ -555,7 +568,7 @@ public class FullBlockTestGenerator {
// \-> b37 (11) // \-> b37 (11)
// \-> b38 (11) // \-> b38 (11)
// //
Block b37 = createNextBlock(b35, chainHeadHeight + 10, out11, null); Block b37 = createNextBlock(b35, chainHeadHeight + 12, out11, null);
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, BigInteger.valueOf(1), new byte[] {})); tx.addOutput(new TransactionOutput(params, tx, BigInteger.valueOf(1), new byte[] {}));
@ -563,9 +576,9 @@ public class FullBlockTestGenerator {
b37.addTransaction(tx); b37.addTransaction(tx);
} }
b37.solve(); b37.solve();
blocks.add(new BlockAndValidity(b37, false, true, b35.getHash(), "b37")); blocks.add(new BlockAndValidity(blockToHeightMap, b37, false, true, b35.getHash(), chainHeadHeight + 11, "b37"));
Block b38 = createNextBlock(b35, chainHeadHeight + 10, out11, null); Block b38 = createNextBlock(b35, chainHeadHeight + 12, out11, null);
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, BigInteger.valueOf(1), new byte[] {})); tx.addOutput(new TransactionOutput(params, tx, BigInteger.valueOf(1), new byte[] {}));
@ -576,7 +589,7 @@ public class FullBlockTestGenerator {
b38.addTransaction(tx); b38.addTransaction(tx);
} }
b38.solve(); b38.solve();
blocks.add(new BlockAndValidity(b38, false, true, b35.getHash(), "b38")); blocks.add(new BlockAndValidity(blockToHeightMap, b38, false, true, b35.getHash(), chainHeadHeight + 11, "b38"));
// Check P2SH SigOp counting // Check P2SH SigOp counting
// 13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b41 (12) // 13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b41 (12)
@ -585,7 +598,7 @@ public class FullBlockTestGenerator {
// Create some P2SH outputs that will require 6 sigops to spend // Create some P2SH outputs that will require 6 sigops to spend
byte[] b39p2shScriptPubKey; byte[] b39p2shScriptPubKey;
int b39numP2SHOutputs = 0, b39sigOpsPerOutput = 6; int b39numP2SHOutputs = 0, b39sigOpsPerOutput = 6;
Block b39 = createNextBlock(b35, chainHeadHeight + 10, null, null); Block b39 = createNextBlock(b35, chainHeadHeight + 12, null, null);
{ {
ByteArrayOutputStream p2shScriptPubKey = new UnsafeByteArrayOutputStream(); ByteArrayOutputStream p2shScriptPubKey = new UnsafeByteArrayOutputStream();
try { try {
@ -646,7 +659,7 @@ public class FullBlockTestGenerator {
} }
} }
b39.solve(); b39.solve();
blocks.add(new BlockAndValidity(b39, true, false, b39.getHash(), "b39")); blocks.add(new BlockAndValidity(blockToHeightMap, b39, true, false, b39.getHash(), chainHeadHeight + 12, "b39"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b39.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b39.getTransactions().get(0).getHash()),
b39.getTransactions().get(0).getOutputs().get(0).getValue(), b39.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -654,7 +667,7 @@ public class FullBlockTestGenerator {
TransactionOutPointWithValue out12 = spendableOutputs.poll(); TransactionOutPointWithValue out12 = spendableOutputs.poll();
Block b40 = createNextBlock(b39, chainHeadHeight + 11, out12, null); Block b40 = createNextBlock(b39, chainHeadHeight + 13, out12, null);
{ {
int sigOps = 0; int sigOps = 0;
for (Transaction tx : b40.transactions) { for (Transaction tx : b40.transactions) {
@ -714,11 +727,11 @@ public class FullBlockTestGenerator {
b40.addTransaction(tx); b40.addTransaction(tx);
} }
b40.solve(); b40.solve();
blocks.add(new BlockAndValidity(b40, false, true, b39.getHash(), "b40")); blocks.add(new BlockAndValidity(blockToHeightMap, b40, false, true, b39.getHash(), chainHeadHeight + 12, "b40"));
Block b41 = null; Block b41 = null;
if (addExpensiveBlocks) { if (addExpensiveBlocks) {
b41 = createNextBlock(b39, chainHeadHeight + 11, out12, null); b41 = createNextBlock(b39, chainHeadHeight + 13, out12, null);
{ {
int sigOps = 0; int sigOps = 0;
for (Transaction tx : b41.transactions) { for (Transaction tx : b41.transactions) {
@ -791,15 +804,15 @@ public class FullBlockTestGenerator {
b41.addTransaction(tx); b41.addTransaction(tx);
} }
b41.solve(); b41.solve();
blocks.add(new BlockAndValidity(b41, true, false, b41.getHash(), "b41")); blocks.add(new BlockAndValidity(blockToHeightMap, b41, true, false, b41.getHash(), chainHeadHeight + 13, "b41"));
} }
// Fork off of b39 to create a constant base again // Fork off of b39 to create a constant base again
// b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) // b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13)
// \-> b41 (12) // \-> b41 (12)
// //
Block b42 = createNextBlock(b39, chainHeadHeight + 11, out12, null); Block b42 = createNextBlock(b39, chainHeadHeight + 13, out12, null);
blocks.add(new BlockAndValidity(b42, true, false, b41 == null ? b42.getHash() : b41.getHash(), "b42")); blocks.add(new BlockAndValidity(blockToHeightMap, b42, true, false, b41 == null ? b42.getHash() : b41.getHash(), chainHeadHeight + 13, "b42"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b42.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b42.getTransactions().get(0).getHash()),
b42.getTransactions().get(0).getOutputs().get(0).getValue(), b42.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -807,8 +820,8 @@ public class FullBlockTestGenerator {
TransactionOutPointWithValue out13 = spendableOutputs.poll(); TransactionOutPointWithValue out13 = spendableOutputs.poll();
Block b43 = createNextBlock(b42, chainHeadHeight + 12, out13, null); Block b43 = createNextBlock(b42, chainHeadHeight + 14, out13, null);
blocks.add(new BlockAndValidity(b43, true, false, b43.getHash(), "b43")); blocks.add(new BlockAndValidity(blockToHeightMap, b43, true, false, b43.getHash(), chainHeadHeight + 14, "b43"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b43.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b43.getTransactions().get(0).getHash()),
b43.getTransactions().get(0).getOutputs().get(0).getValue(), b43.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -840,7 +853,7 @@ public class FullBlockTestGenerator {
b44.setTime(b43.getTimeSeconds() + 1); b44.setTime(b43.getTimeSeconds() + 1);
} }
b44.solve(); b44.solve();
blocks.add(new BlockAndValidity(b44, true, false, b44.getHash(), "b44")); blocks.add(new BlockAndValidity(blockToHeightMap, b44, true, false, b44.getHash(), chainHeadHeight + 15, "b44"));
TransactionOutPointWithValue out15 = spendableOutputs.poll(); TransactionOutPointWithValue out15 = spendableOutputs.poll();
@ -868,7 +881,7 @@ public class FullBlockTestGenerator {
b45.setTime(b44.getTimeSeconds() + 1); b45.setTime(b44.getTimeSeconds() + 1);
} }
b45.solve(); b45.solve();
blocks.add(new BlockAndValidity(b45, false, true, b44.getHash(), "b45")); blocks.add(new BlockAndValidity(blockToHeightMap, b45, false, true, b44.getHash(), chainHeadHeight + 15, "b45"));
// A block with no txn // A block with no txn
Block b46 = new Block(params); Block b46 = new Block(params);
@ -881,10 +894,10 @@ public class FullBlockTestGenerator {
b46.setTime(b44.getTimeSeconds() + 1); b46.setTime(b44.getTimeSeconds() + 1);
} }
b46.solve(); b46.solve();
blocks.add(new BlockAndValidity(b46, false, true, b44.getHash(), "b46")); blocks.add(new BlockAndValidity(blockToHeightMap, b46, false, true, b44.getHash(), chainHeadHeight + 15, "b46"));
// A block with invalid work // A block with invalid work
Block b47 = createNextBlock(b44, chainHeadHeight + 14, out15, null); Block b47 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
{ {
try { try {
// Inverse solve // Inverse solve
@ -900,32 +913,32 @@ public class FullBlockTestGenerator {
throw new RuntimeException(e); // Cannot happen. throw new RuntimeException(e); // Cannot happen.
} }
} }
blocks.add(new BlockAndValidity(b47, false, true, b44.getHash(), "b47")); blocks.add(new BlockAndValidity(blockToHeightMap, b47, false, true, b44.getHash(), chainHeadHeight + 15, "b47"));
// Block with timestamp > 2h in the future // Block with timestamp > 2h in the future
Block b48 = createNextBlock(b44, chainHeadHeight + 14, out15, null); Block b48 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
b48.setTime(Utils.now().getTime() / 1000 + 60*60*3); b48.setTime(Utils.now().getTime() / 1000 + 60*60*3);
b48.solve(); b48.solve();
blocks.add(new BlockAndValidity(b48, false, true, b44.getHash(), "b48")); blocks.add(new BlockAndValidity(blockToHeightMap, b48, false, true, b44.getHash(), chainHeadHeight + 15, "b48"));
// Block with invalid merkle hash // Block with invalid merkle hash
Block b49 = createNextBlock(b44, chainHeadHeight + 14, out15, null); Block b49 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
b49.setMerkleRoot(Sha256Hash.ZERO_HASH); b49.setMerkleRoot(Sha256Hash.ZERO_HASH);
b49.solve(); b49.solve();
blocks.add(new BlockAndValidity(b49, false, true, b44.getHash(), "b49")); blocks.add(new BlockAndValidity(blockToHeightMap, b49, false, true, b44.getHash(), chainHeadHeight + 15, "b49"));
// Block with incorrect POW limit // Block with incorrect POW limit
Block b50 = createNextBlock(b44, chainHeadHeight + 14, out15, null); Block b50 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
{ {
long diffTarget = b44.getDifficultyTarget(); long diffTarget = b44.getDifficultyTarget();
diffTarget &= 0xFFBFFFFF; // Make difficulty one bit harder diffTarget &= 0xFFBFFFFF; // Make difficulty one bit harder
b50.setDifficultyTarget(diffTarget); b50.setDifficultyTarget(diffTarget);
} }
b50.solve(); b50.solve();
blocks.add(new BlockAndValidity(b50, false, true, b44.getHash(), "b50")); blocks.add(new BlockAndValidity(blockToHeightMap, b50, false, true, b44.getHash(), chainHeadHeight + 15, "b50"));
// A block with two coinbase txn // A block with two coinbase txn
Block b51 = createNextBlock(b44, chainHeadHeight + 14, out15, null); Block b51 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
{ {
Transaction coinbase = new Transaction(params); Transaction coinbase = new Transaction(params);
coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) 0xff, 110, 1})); coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) 0xff, 110, 1}));
@ -933,10 +946,10 @@ public class FullBlockTestGenerator {
b51.addTransaction(coinbase, false); b51.addTransaction(coinbase, false);
} }
b51.solve(); b51.solve();
blocks.add(new BlockAndValidity(b51, false, true, b44.getHash(), "b51")); blocks.add(new BlockAndValidity(blockToHeightMap, b51, false, true, b44.getHash(), chainHeadHeight + 15, "b51"));
// A block with duplicate txn // A block with duplicate txn
Block b52 = createNextBlock(b44, chainHeadHeight + 14, out15, null); Block b52 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, BigInteger.valueOf(1), new byte[] {})); tx.addOutput(new TransactionOutput(params, tx, BigInteger.valueOf(1), new byte[] {}));
@ -947,31 +960,31 @@ public class FullBlockTestGenerator {
b52.addTransaction(tx); b52.addTransaction(tx);
} }
b52.solve(); b52.solve();
blocks.add(new BlockAndValidity(b52, false, true, b44.getHash(), "b52")); blocks.add(new BlockAndValidity(blockToHeightMap, b52, false, true, b44.getHash(), chainHeadHeight + 15, "b52"));
// Test block timestamp // Test block timestamp
// -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) // -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)
// \-> b54 (15) // \-> b54 (15)
// \-> b44 (14) // \-> b44 (14)
// //
Block b53 = createNextBlock(b43, chainHeadHeight + 13, out14, null); Block b53 = createNextBlock(b43, chainHeadHeight + 15, out14, null);
blocks.add(new BlockAndValidity(b53, true, false, b44.getHash(), "b53")); blocks.add(new BlockAndValidity(blockToHeightMap, b53, true, false, b44.getHash(), chainHeadHeight + 15, "b53"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b53.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b53.getTransactions().get(0).getHash()),
b53.getTransactions().get(0).getOutputs().get(0).getValue(), b53.getTransactions().get(0).getOutputs().get(0).getValue(),
b53.getTransactions().get(0).getOutputs().get(0).getScriptPubKey())); b53.getTransactions().get(0).getOutputs().get(0).getScriptPubKey()));
// Block with invalid timestamp // Block with invalid timestamp
Block b54 = createNextBlock(b53, chainHeadHeight + 14, out15, null); Block b54 = createNextBlock(b53, chainHeadHeight + 16, out15, null);
b54.setTime(b35.getTimeSeconds() - 1); b54.setTime(b35.getTimeSeconds() - 1);
b54.solve(); b54.solve();
blocks.add(new BlockAndValidity(b54, false, true, b44.getHash(), "b54")); blocks.add(new BlockAndValidity(blockToHeightMap, b54, false, true, b44.getHash(), chainHeadHeight + 15, "b54"));
// Block with valid timestamp // Block with valid timestamp
Block b55 = createNextBlock(b53, chainHeadHeight + 14, out15, null); Block b55 = createNextBlock(b53, chainHeadHeight + 16, out15, null);
b55.setTime(b35.getTimeSeconds()); b55.setTime(b35.getTimeSeconds());
b55.solve(); b55.solve();
blocks.add(new BlockAndValidity(b55, true, false, b55.getHash(), "b55")); blocks.add(new BlockAndValidity(blockToHeightMap, b55, true, false, b55.getHash(), chainHeadHeight + 16, "b55"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b55.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b55.getTransactions().get(0).getHash()),
b55.getTransactions().get(0).getOutputs().get(0).getValue(), b55.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -983,7 +996,7 @@ public class FullBlockTestGenerator {
// //
TransactionOutPointWithValue out16 = spendableOutputs.poll(); TransactionOutPointWithValue out16 = spendableOutputs.poll();
Block b57 = createNextBlock(b55, chainHeadHeight + 15, out16, null); Block b57 = createNextBlock(b55, chainHeadHeight + 17, out16, null);
Transaction b56txToDuplicate; Transaction b56txToDuplicate;
{ {
b56txToDuplicate = new Transaction(params); b56txToDuplicate = new Transaction(params);
@ -1003,9 +1016,9 @@ public class FullBlockTestGenerator {
} }
b56.addTransaction(b56txToDuplicate); b56.addTransaction(b56txToDuplicate);
Preconditions.checkState(b56.getHash().equals(b57.getHash())); Preconditions.checkState(b56.getHash().equals(b57.getHash()));
blocks.add(new BlockAndValidity(b56, false, true, b55.getHash(), "b56")); blocks.add(new BlockAndValidity(blockToHeightMap, b56, false, true, b55.getHash(), chainHeadHeight + 16, "b56"));
blocks.add(new BlockAndValidity(b57, true, false, b57.getHash(), "b57")); blocks.add(new BlockAndValidity(blockToHeightMap, b57, true, false, b57.getHash(), chainHeadHeight + 17, "b57"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b57.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b57.getTransactions().get(0).getHash()),
b57.getTransactions().get(0).getOutputs().get(0).getValue(), b57.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -1018,7 +1031,7 @@ public class FullBlockTestGenerator {
TransactionOutPointWithValue out17 = spendableOutputs.poll(); TransactionOutPointWithValue out17 = spendableOutputs.poll();
// tx with prevout.n out of range // tx with prevout.n out of range
Block b58 = createNextBlock(b57, chainHeadHeight + 16, out17, null); Block b58 = createNextBlock(b57, chainHeadHeight + 18, out17, null);
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, BigInteger.ZERO, new byte[] {})); tx.addOutput(new TransactionOutput(params, tx, BigInteger.ZERO, new byte[] {}));
@ -1027,10 +1040,10 @@ public class FullBlockTestGenerator {
b58.addTransaction(tx); b58.addTransaction(tx);
} }
b58.solve(); b58.solve();
blocks.add(new BlockAndValidity(b58, false, true, b57.getHash(), "b58")); blocks.add(new BlockAndValidity(blockToHeightMap, b58, false, true, b57.getHash(), chainHeadHeight + 17, "b58"));
// tx with output value > input value out of range // tx with output value > input value out of range
Block b59 = createNextBlock(b57, chainHeadHeight + 16, out17, null); Block b59 = createNextBlock(b57, chainHeadHeight + 18, out17, null);
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, tx.addOutput(new TransactionOutput(params, tx,
@ -1040,10 +1053,10 @@ public class FullBlockTestGenerator {
b59.addTransaction(tx); b59.addTransaction(tx);
} }
b59.solve(); b59.solve();
blocks.add(new BlockAndValidity(b59, false, true, b57.getHash(), "b59")); blocks.add(new BlockAndValidity(blockToHeightMap, b59, false, true, b57.getHash(), chainHeadHeight + 17, "b59"));
Block b60 = createNextBlock(b57, chainHeadHeight + 16, out17, null); Block b60 = createNextBlock(b57, chainHeadHeight + 18, out17, null);
blocks.add(new BlockAndValidity(b60, true, false, b60.getHash(), "b60")); blocks.add(new BlockAndValidity(blockToHeightMap, b60, true, false, b60.getHash(), chainHeadHeight + 18, "b60"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b60.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b60.getTransactions().get(0).getHash()),
b60.getTransactions().get(0).getOutputs().get(0).getValue(), b60.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -1055,21 +1068,22 @@ public class FullBlockTestGenerator {
// //
TransactionOutPointWithValue out18 = spendableOutputs.poll(); TransactionOutPointWithValue out18 = spendableOutputs.poll();
Block b61 = createNextBlock(b60, chainHeadHeight + 17, out18, null); Block b61 = createNextBlock(b60, chainHeadHeight + 19, out18, null);
{ {
byte[] scriptBytes = b61.getTransactions().get(0).getInputs().get(0).getScriptBytes(); byte[] scriptBytes = b61.getTransactions().get(0).getInputs().get(0).getScriptBytes();
scriptBytes[0]--; // createNextBlock will increment the first script byte on each new block scriptBytes[0]--; // createNextBlock will increment the first script byte on each new block
b61.getTransactions().get(0).getInputs().get(0).setScriptBytes(scriptBytes); b61.getTransactions().get(0).getInputs().get(0).setScriptBytes(scriptBytes);
b61.unCache(); b61.unCache();
Preconditions.checkState(b61.getTransactions().get(0).equals(b60.getTransactions().get(0)));
} }
b61.solve(); b61.solve();
blocks.add(new BlockAndValidity(b61, false, true, b60.getHash(), "b61")); blocks.add(new BlockAndValidity(blockToHeightMap, b61, false, true, b60.getHash(), chainHeadHeight + 18, "b61"));
// Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests) // Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests)
// -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) // -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
// \-> b62 (18) // \-> b62 (18)
// //
Block b62 = createNextBlock(b60, chainHeadHeight + 17, null, null); Block b62 = createNextBlock(b60, chainHeadHeight + 19, null, null);
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
tx.setLockTime(0xffffffffL); tx.setLockTime(0xffffffffL);
@ -1079,20 +1093,20 @@ public class FullBlockTestGenerator {
Preconditions.checkState(!tx.isFinal(chainHeadHeight + 17, b62.getTimeSeconds())); Preconditions.checkState(!tx.isFinal(chainHeadHeight + 17, b62.getTimeSeconds()));
} }
b62.solve(); b62.solve();
blocks.add(new BlockAndValidity(b62, false, true, b60.getHash(), "b62")); blocks.add(new BlockAndValidity(blockToHeightMap, b62, false, true, b60.getHash(), chainHeadHeight + 18, "b62"));
// Test a non-final coinbase is also rejected // Test a non-final coinbase is also rejected
// -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) // -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
// \-> b63 (-) // \-> b63 (-)
// //
Block b63 = createNextBlock(b60, chainHeadHeight + 17, null, null); Block b63 = createNextBlock(b60, chainHeadHeight + 19, null, null);
{ {
b63.getTransactions().get(0).setLockTime(0xffffffffL); b63.getTransactions().get(0).setLockTime(0xffffffffL);
b63.getTransactions().get(0).getInputs().get(0).setSequenceNumber(0xDEADBEEF); b63.getTransactions().get(0).getInputs().get(0).setSequenceNumber(0xDEADBEEF);
Preconditions.checkState(!b63.getTransactions().get(0).isFinal(chainHeadHeight + 17, b63.getTimeSeconds())); Preconditions.checkState(!b63.getTransactions().get(0).isFinal(chainHeadHeight + 17, b63.getTimeSeconds()));
} }
b63.solve(); b63.solve();
blocks.add(new BlockAndValidity(b63, false, true, b60.getHash(), "b63")); blocks.add(new BlockAndValidity(blockToHeightMap, b63, false, true, b60.getHash(), chainHeadHeight + 18, "b63"));
// Check that a block which is (when properly encoded) <= MAX_BLOCK_SIZE is accepted // Check that a block which is (when properly encoded) <= MAX_BLOCK_SIZE is accepted
// Even when it is encoded with varints that make its encoded size actually > MAX_BLOCK_SIZE // Even when it is encoded with varints that make its encoded size actually > MAX_BLOCK_SIZE
@ -1100,7 +1114,7 @@ public class FullBlockTestGenerator {
// //
Block b64; Block b64;
{ {
Block b64Created = createNextBlock(b60, chainHeadHeight + 17, out18, null); Block b64Created = createNextBlock(b60, chainHeadHeight + 19, out18, null);
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
// Signature size is non-deterministic, so it may take several runs before finding any off-by-one errors // Signature size is non-deterministic, so it may take several runs before finding any off-by-one errors
byte[] outputScript = new byte[Block.MAX_BLOCK_SIZE - b64Created.getMessageSize() - 138]; byte[] outputScript = new byte[Block.MAX_BLOCK_SIZE - b64Created.getMessageSize() - 138];
@ -1133,7 +1147,7 @@ public class FullBlockTestGenerator {
Preconditions.checkState(Arrays.equals(stream.toByteArray(), b64.bitcoinSerialize())); Preconditions.checkState(Arrays.equals(stream.toByteArray(), b64.bitcoinSerialize()));
Preconditions.checkState(b64.getOptimalEncodingMessageSize() == b64Created.getMessageSize()); Preconditions.checkState(b64.getOptimalEncodingMessageSize() == b64Created.getMessageSize());
} }
blocks.add(new BlockAndValidity(b64, true, false, b64.getHash(), "b64")); blocks.add(new BlockAndValidity(blockToHeightMap, b64, true, false, b64.getHash(), chainHeadHeight + 19, "b64"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b64.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b64.getTransactions().get(0).getHash()),
b64.getTransactions().get(0).getOutputs().get(0).getValue(), b64.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -1142,9 +1156,9 @@ public class FullBlockTestGenerator {
// Spend an output created in the block itself // Spend an output created in the block itself
// -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) // -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
// //
TransactionOutPointWithValue out19 = spendableOutputs.poll(); TransactionOutPointWithValue out19 = spendableOutputs.poll(); Preconditions.checkState(out19 != null);//TODO preconditions all the way up
Block b65 = createNextBlock(b64, chainHeadHeight + 18, null, null); Block b65 = createNextBlock(b64, chainHeadHeight + 20, null, null);
{ {
Transaction tx1 = new Transaction(params); Transaction tx1 = new Transaction(params);
tx1.addOutput(new TransactionOutput(params, tx1, out19.value, new byte[]{OP_TRUE})); tx1.addOutput(new TransactionOutput(params, tx1, out19.value, new byte[]{OP_TRUE}));
@ -1157,7 +1171,7 @@ public class FullBlockTestGenerator {
b65.addTransaction(tx2); b65.addTransaction(tx2);
} }
b65.solve(); b65.solve();
blocks.add(new BlockAndValidity(b65, true, false, b65.getHash(), "b65")); blocks.add(new BlockAndValidity(blockToHeightMap, b65, true, false, b65.getHash(), chainHeadHeight + 20, "b65"));
spendableOutputs.offer(new TransactionOutPointWithValue( spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b65.getTransactions().get(0).getHash()), new TransactionOutPoint(params, 0, b65.getTransactions().get(0).getHash()),
b65.getTransactions().get(0).getOutputs().get(0).getValue(), b65.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -1167,9 +1181,9 @@ public class FullBlockTestGenerator {
// -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) // -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
// \-> b66 (20) // \-> b66 (20)
// //
TransactionOutPointWithValue out20 = spendableOutputs.poll(); TransactionOutPointWithValue out20 = spendableOutputs.poll(); Preconditions.checkState(out20 != null);
Block b66 = createNextBlock(b65, chainHeadHeight + 19, null, null); Block b66 = createNextBlock(b65, chainHeadHeight + 21, null, null);
{ {
Transaction tx1 = new Transaction(params); Transaction tx1 = new Transaction(params);
tx1.addOutput(new TransactionOutput(params, tx1, out20.value, new byte[]{OP_TRUE})); tx1.addOutput(new TransactionOutput(params, tx1, out20.value, new byte[]{OP_TRUE}));
@ -1182,13 +1196,13 @@ public class FullBlockTestGenerator {
b66.addTransaction(tx1); b66.addTransaction(tx1);
} }
b66.solve(); b66.solve();
blocks.add(new BlockAndValidity(b66, false, true, b65.getHash(), "b66")); blocks.add(new BlockAndValidity(blockToHeightMap, b66, false, true, b65.getHash(), chainHeadHeight + 20, "b66"));
// Attempt to double-spend a transaction created in a block // Attempt to double-spend a transaction created in a block
// -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) // -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
// \-> b67 (20) // \-> b67 (20)
// //
Block b67 = createNextBlock(b65, chainHeadHeight + 19, null, null); Block b67 = createNextBlock(b65, chainHeadHeight + 21, null, null);
{ {
Transaction tx1 = new Transaction(params); Transaction tx1 = new Transaction(params);
tx1.addOutput(new TransactionOutput(params, tx1, out20.value, new byte[]{OP_TRUE})); tx1.addOutput(new TransactionOutput(params, tx1, out20.value, new byte[]{OP_TRUE}));
@ -1206,13 +1220,13 @@ public class FullBlockTestGenerator {
b67.addTransaction(tx3); b67.addTransaction(tx3);
} }
b67.solve(); b67.solve();
blocks.add(new BlockAndValidity(b67, false, true, b65.getHash(), "b67")); blocks.add(new BlockAndValidity(blockToHeightMap, b67, false, true, b65.getHash(), chainHeadHeight + 20, "b67"));
// A few more tests of block subsidy // A few more tests of block subsidy
// -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) // -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
// \-> b68 (20) // \-> b68 (20)
// //
Block b68 = createNextBlock(b65, chainHeadHeight + 19, null, BigInteger.TEN); Block b68 = createNextBlock(b65, chainHeadHeight + 21, null, BigInteger.TEN);
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, out20.value.subtract(BigInteger.valueOf(9)), new byte[]{OP_TRUE})); tx.addOutput(new TransactionOutput(params, tx, out20.value.subtract(BigInteger.valueOf(9)), new byte[]{OP_TRUE}));
@ -1220,9 +1234,9 @@ public class FullBlockTestGenerator {
b68.addTransaction(tx); b68.addTransaction(tx);
} }
b68.solve(); b68.solve();
blocks.add(new BlockAndValidity(b68, false, true, b65.getHash(), "b68")); blocks.add(new BlockAndValidity(blockToHeightMap, b68, false, true, b65.getHash(), chainHeadHeight + 20, "b68"));
Block b69 = createNextBlock(b65, chainHeadHeight + 19, null, BigInteger.TEN); Block b69 = createNextBlock(b65, chainHeadHeight + 21, null, BigInteger.TEN);
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, out20.value.subtract(BigInteger.TEN), new byte[]{OP_TRUE})); tx.addOutput(new TransactionOutput(params, tx, out20.value.subtract(BigInteger.TEN), new byte[]{OP_TRUE}));
@ -1230,14 +1244,14 @@ public class FullBlockTestGenerator {
b69.addTransaction(tx); b69.addTransaction(tx);
} }
b69.solve(); b69.solve();
blocks.add(new BlockAndValidity(b69, true, false, b69.getHash(), "b69")); blocks.add(new BlockAndValidity(blockToHeightMap, b69, true, false, b69.getHash(), chainHeadHeight + 21, "b69"));
// Test spending the outpoint of a non-existent transaction // Test spending the outpoint of a non-existent transaction
// -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) // -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
// \-> b70 (21) // \-> b70 (21)
// //
TransactionOutPointWithValue out21 = spendableOutputs.poll(); TransactionOutPointWithValue out21 = spendableOutputs.poll(); Preconditions.checkState(out21 != null);
Block b70 = createNextBlock(b69, chainHeadHeight + 20, out21, null); Block b70 = createNextBlock(b69, chainHeadHeight + 22, out21, null);
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, BigInteger.ZERO, new byte[]{OP_TRUE})); tx.addOutput(new TransactionOutput(params, tx, BigInteger.ZERO, new byte[]{OP_TRUE}));
@ -1246,13 +1260,13 @@ public class FullBlockTestGenerator {
b70.addTransaction(tx); b70.addTransaction(tx);
} }
b70.solve(); b70.solve();
blocks.add(new BlockAndValidity(b70, false, true, b69.getHash(), "b70")); blocks.add(new BlockAndValidity(blockToHeightMap, b70, false, true, b69.getHash(), chainHeadHeight + 21, "b70"));
// Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks) // Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks)
// -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b71 (21) // -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b71 (21)
// \-> b71 (21) // \-> b71 (21)
// //
Block b72 = createNextBlock(b69, chainHeadHeight + 20, out21, null); Block b72 = createNextBlock(b69, chainHeadHeight + 22, out21, null);
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, BigInteger.ZERO, new byte[]{OP_TRUE})); tx.addOutput(new TransactionOutput(params, tx, BigInteger.ZERO, new byte[]{OP_TRUE}));
@ -1266,8 +1280,13 @@ public class FullBlockTestGenerator {
Block b71 = new Block(params, b72.bitcoinSerialize()); Block b71 = new Block(params, b72.bitcoinSerialize());
b71.addTransaction(b72.getTransactions().get(2)); b71.addTransaction(b72.getTransactions().get(2));
Preconditions.checkState(b71.getHash().equals(b72.getHash())); Preconditions.checkState(b71.getHash().equals(b72.getHash()));
blocks.add(new BlockAndValidity(b71, false, true, b69.getHash(), "b71")); blocks.add(new BlockAndValidity(blockToHeightMap, b71, false, true, b69.getHash(), chainHeadHeight + 21, "b71"));
blocks.add(new BlockAndValidity(b72, true, false, b72.getHash(), "b72")); blocks.add(new BlockAndValidity(blockToHeightMap, b72, true, false, b72.getHash(), chainHeadHeight + 22, "b72"));
spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b72.getTransactions().get(0).getHash()),
b72.getTransactions().get(0).getOutputs().get(0).getValue(),
b72.getTransactions().get(0).getOutputs().get(0).getScriptPubKey()));
//TODO: Explicitly address MoneyRange() checks //TODO: Explicitly address MoneyRange() checks
@ -1277,9 +1296,12 @@ public class FullBlockTestGenerator {
private Block createNextBlock(Block baseBlock, int nextBlockHeight, TransactionOutPointWithValue prevOut, private Block createNextBlock(Block baseBlock, int nextBlockHeight, TransactionOutPointWithValue prevOut,
BigInteger additionalCoinbaseValue) throws ScriptException { BigInteger additionalCoinbaseValue) throws ScriptException {
Integer height = blockToHeightMap.get(baseBlock.getHash());
if (height != null)
Preconditions.checkState(height == nextBlockHeight - 1);
BigInteger coinbaseValue = Utils.toNanoCoins(50, 0).shiftRight(nextBlockHeight / params.getSubsidyDecreaseBlockCount()) BigInteger coinbaseValue = Utils.toNanoCoins(50, 0).shiftRight(nextBlockHeight / params.getSubsidyDecreaseBlockCount())
.add((prevOut != null ? prevOut.value.subtract(BigInteger.ONE) : BigInteger.valueOf(0))) .add((prevOut != null ? prevOut.value.subtract(BigInteger.ONE) : BigInteger.ZERO))
.add(additionalCoinbaseValue == null ? BigInteger.valueOf(0) : additionalCoinbaseValue); .add(additionalCoinbaseValue == null ? BigInteger.ZERO : additionalCoinbaseValue);
Block block = baseBlock.createNextBlockWithCoinbase(coinbaseOutKeyPubKey, coinbaseValue); Block block = baseBlock.createNextBlockWithCoinbase(coinbaseOutKeyPubKey, coinbaseValue);
if (prevOut != null) { if (prevOut != null) {
Transaction t = new Transaction(params); Transaction t = new Transaction(params);

View File

@ -96,6 +96,10 @@ public class FullPrunedBlockChainTest {
log.error("New block head didn't match the correct value after block " + block.blockName); log.error("New block head didn't match the correct value after block " + block.blockName);
fail(); fail();
} }
if (chain.getChainHead().getHeight() != block.heightAfterBlock) {
log.error("New block head didn't match the correct height after block " + block.blockName);
fail();
}
} }
} }