3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-31 23:32:16 +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)) {
log.error("New block head didn't match the correct value after block \"" + block.blockName + "\"");
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);

View File

@ -17,15 +17,25 @@ class BlockAndValidity {
boolean connects;
boolean throwsException;
Sha256Hash hashChainTipAfterBlock;
int heightAfterBlock;
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)
throw new RuntimeException("A block cannot connect if an exception was thrown while adding it.");
this.block = block;
this.connects = connects;
this.throwsException = throwsException;
this.hashChainTipAfterBlock = hashChainTipAfterBlock;
this.heightAfterBlock = heightAfterBlock;
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 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) {
this.params = params;
coinbaseOutKey = new ECKey();
@ -70,14 +83,14 @@ public class FullBlockTestGenerator {
int chainHeadHeight = 1;
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(
new TransactionOutPoint(params, 0, chainHead.getTransactions().get(0).getHash()),
Utils.toNanoCoins(50, 0), chainHead.getTransactions().get(0).getOutputs().get(0).getScriptPubKey()));
for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
chainHead = chainHead.createNextBlockWithCoinbase(coinbaseOutKeyPubKey);
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(
new TransactionOutPoint(params, 0, chainHead.getTransactions().get(0).getHash()),
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.
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(
new TransactionOutPoint(params, 0, b1.getTransactions().get(0).getHash()),
b1.getTransactions().get(0).getOutputs().get(0).getValue(),
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);
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
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(
new TransactionOutPoint(params, 0, b2.getTransactions().get(0).getHash()),
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.
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
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.
TransactionOutPointWithValue out2 = spendableOutputs.poll();
TransactionOutPointWithValue out2 = spendableOutputs.poll(); Preconditions.checkState(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)
// \-> b3 (1) -> b4 (2)
//
// ... and back to the first chain.
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(
new TransactionOutPoint(params, 0, b5.getTransactions().get(0).getHash()),
b5.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -133,7 +146,7 @@ public class FullBlockTestGenerator {
TransactionOutPointWithValue out3 = spendableOutputs.poll();
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)
// \-> b3 (1) -> b4 (2)
@ -145,12 +158,12 @@ public class FullBlockTestGenerator {
// \-> b3 (1) -> b4 (2)
//
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();
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
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -158,7 +171,7 @@ public class FullBlockTestGenerator {
// \-> b3 (1) -> b4 (2)
//
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)
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -166,10 +179,10 @@ public class FullBlockTestGenerator {
// \-> b3 (1) -> b4 (2)
//
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));
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
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -184,9 +197,9 @@ public class FullBlockTestGenerator {
b12.getTransactions().get(0).getOutputs().get(0).getScriptPubKey()));
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
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(
new TransactionOutPoint(params, 0, b13.getTransactions().get(0).getHash()),
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
// 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
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
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
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -226,7 +239,7 @@ public class FullBlockTestGenerator {
}
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(
new TransactionOutPoint(params, 0, b15.getTransactions().get(0).getHash()),
b15.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -251,7 +264,7 @@ public class FullBlockTestGenerator {
}
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
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -268,7 +281,7 @@ public class FullBlockTestGenerator {
b17.addTransaction(tx);
}
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)
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -286,10 +299,10 @@ public class FullBlockTestGenerator {
b18.addTransaction(tx);
}
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);
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
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -299,7 +312,7 @@ public class FullBlockTestGenerator {
TransactionOutPointWithValue out7 = spendableOutputs.poll();
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)
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -308,9 +321,9 @@ public class FullBlockTestGenerator {
// \-> b3 (1) -> b4 (2)
//
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);
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
// genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
@ -331,7 +344,7 @@ public class FullBlockTestGenerator {
b23.addTransaction(tx);
}
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(
new TransactionOutPoint(params, 0, b23.getTransactions().get(0).getHash()),
b23.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -350,11 +363,11 @@ public class FullBlockTestGenerator {
b24.addTransaction(tx);
}
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
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
// 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.setMerkleRoot(null);
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
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);
{
@ -381,11 +394,11 @@ public class FullBlockTestGenerator {
}
b28.setMerkleRoot(null);
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
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);
{
@ -395,7 +408,7 @@ public class FullBlockTestGenerator {
}
b30.setMerkleRoot(null);
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(
new TransactionOutPoint(params, 0, b30.getTransactions().get(0).getHash()),
b30.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -427,7 +440,7 @@ public class FullBlockTestGenerator {
}
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(
new TransactionOutPoint(params, 0, b31.getTransactions().get(0).getHash()),
b31.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -454,7 +467,7 @@ public class FullBlockTestGenerator {
}
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);
@ -474,7 +487,7 @@ public class FullBlockTestGenerator {
}
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(
new TransactionOutPoint(params, 0, b33.getTransactions().get(0).getHash()),
b33.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -501,7 +514,7 @@ public class FullBlockTestGenerator {
}
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);
@ -521,7 +534,7 @@ public class FullBlockTestGenerator {
}
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(
new TransactionOutPoint(params, 0, b35.getTransactions().get(0).getHash()),
b35.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -546,7 +559,7 @@ public class FullBlockTestGenerator {
}
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
// (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)
// \-> b38 (11)
//
Block b37 = createNextBlock(b35, chainHeadHeight + 10, out11, null);
Block b37 = createNextBlock(b35, chainHeadHeight + 12, out11, null);
{
Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, BigInteger.valueOf(1), new byte[] {}));
@ -563,9 +576,9 @@ public class FullBlockTestGenerator {
b37.addTransaction(tx);
}
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);
tx.addOutput(new TransactionOutput(params, tx, BigInteger.valueOf(1), new byte[] {}));
@ -576,7 +589,7 @@ public class FullBlockTestGenerator {
b38.addTransaction(tx);
}
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
// 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
byte[] b39p2shScriptPubKey;
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();
try {
@ -646,7 +659,7 @@ public class FullBlockTestGenerator {
}
}
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(
new TransactionOutPoint(params, 0, b39.getTransactions().get(0).getHash()),
b39.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -654,7 +667,7 @@ public class FullBlockTestGenerator {
TransactionOutPointWithValue out12 = spendableOutputs.poll();
Block b40 = createNextBlock(b39, chainHeadHeight + 11, out12, null);
Block b40 = createNextBlock(b39, chainHeadHeight + 13, out12, null);
{
int sigOps = 0;
for (Transaction tx : b40.transactions) {
@ -714,11 +727,11 @@ public class FullBlockTestGenerator {
b40.addTransaction(tx);
}
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;
if (addExpensiveBlocks) {
b41 = createNextBlock(b39, chainHeadHeight + 11, out12, null);
b41 = createNextBlock(b39, chainHeadHeight + 13, out12, null);
{
int sigOps = 0;
for (Transaction tx : b41.transactions) {
@ -791,15 +804,15 @@ public class FullBlockTestGenerator {
b41.addTransaction(tx);
}
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
// b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13)
// \-> b41 (12)
//
Block b42 = createNextBlock(b39, chainHeadHeight + 11, out12, null);
blocks.add(new BlockAndValidity(b42, true, false, b41 == null ? b42.getHash() : b41.getHash(), "b42"));
Block b42 = createNextBlock(b39, chainHeadHeight + 13, out12, null);
blocks.add(new BlockAndValidity(blockToHeightMap, b42, true, false, b41 == null ? b42.getHash() : b41.getHash(), chainHeadHeight + 13, "b42"));
spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b42.getTransactions().get(0).getHash()),
b42.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -807,8 +820,8 @@ public class FullBlockTestGenerator {
TransactionOutPointWithValue out13 = spendableOutputs.poll();
Block b43 = createNextBlock(b42, chainHeadHeight + 12, out13, null);
blocks.add(new BlockAndValidity(b43, true, false, b43.getHash(), "b43"));
Block b43 = createNextBlock(b42, chainHeadHeight + 14, out13, null);
blocks.add(new BlockAndValidity(blockToHeightMap, b43, true, false, b43.getHash(), chainHeadHeight + 14, "b43"));
spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b43.getTransactions().get(0).getHash()),
b43.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -840,7 +853,7 @@ public class FullBlockTestGenerator {
b44.setTime(b43.getTimeSeconds() + 1);
}
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();
@ -868,7 +881,7 @@ public class FullBlockTestGenerator {
b45.setTime(b44.getTimeSeconds() + 1);
}
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
Block b46 = new Block(params);
@ -881,10 +894,10 @@ public class FullBlockTestGenerator {
b46.setTime(b44.getTimeSeconds() + 1);
}
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
Block b47 = createNextBlock(b44, chainHeadHeight + 14, out15, null);
Block b47 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
{
try {
// Inverse solve
@ -900,32 +913,32 @@ public class FullBlockTestGenerator {
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 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.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 b49 = createNextBlock(b44, chainHeadHeight + 14, out15, null);
Block b49 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
b49.setMerkleRoot(Sha256Hash.ZERO_HASH);
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 b50 = createNextBlock(b44, chainHeadHeight + 14, out15, null);
Block b50 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
{
long diffTarget = b44.getDifficultyTarget();
diffTarget &= 0xFFBFFFFF; // Make difficulty one bit harder
b50.setDifficultyTarget(diffTarget);
}
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
Block b51 = createNextBlock(b44, chainHeadHeight + 14, out15, null);
Block b51 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
{
Transaction coinbase = new Transaction(params);
coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) 0xff, 110, 1}));
@ -933,10 +946,10 @@ public class FullBlockTestGenerator {
b51.addTransaction(coinbase, false);
}
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
Block b52 = createNextBlock(b44, chainHeadHeight + 14, out15, null);
Block b52 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
{
Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, BigInteger.valueOf(1), new byte[] {}));
@ -947,31 +960,31 @@ public class FullBlockTestGenerator {
b52.addTransaction(tx);
}
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
// -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)
// \-> b54 (15)
// \-> b44 (14)
//
Block b53 = createNextBlock(b43, chainHeadHeight + 13, out14, null);
blocks.add(new BlockAndValidity(b53, true, false, b44.getHash(), "b53"));
Block b53 = createNextBlock(b43, chainHeadHeight + 15, out14, null);
blocks.add(new BlockAndValidity(blockToHeightMap, b53, true, false, b44.getHash(), chainHeadHeight + 15, "b53"));
spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b53.getTransactions().get(0).getHash()),
b53.getTransactions().get(0).getOutputs().get(0).getValue(),
b53.getTransactions().get(0).getOutputs().get(0).getScriptPubKey()));
// 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.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 b55 = createNextBlock(b53, chainHeadHeight + 14, out15, null);
Block b55 = createNextBlock(b53, chainHeadHeight + 16, out15, null);
b55.setTime(b35.getTimeSeconds());
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(
new TransactionOutPoint(params, 0, b55.getTransactions().get(0).getHash()),
b55.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -983,7 +996,7 @@ public class FullBlockTestGenerator {
//
TransactionOutPointWithValue out16 = spendableOutputs.poll();
Block b57 = createNextBlock(b55, chainHeadHeight + 15, out16, null);
Block b57 = createNextBlock(b55, chainHeadHeight + 17, out16, null);
Transaction b56txToDuplicate;
{
b56txToDuplicate = new Transaction(params);
@ -1003,9 +1016,9 @@ public class FullBlockTestGenerator {
}
b56.addTransaction(b56txToDuplicate);
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(
new TransactionOutPoint(params, 0, b57.getTransactions().get(0).getHash()),
b57.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -1018,7 +1031,7 @@ public class FullBlockTestGenerator {
TransactionOutPointWithValue out17 = spendableOutputs.poll();
// 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);
tx.addOutput(new TransactionOutput(params, tx, BigInteger.ZERO, new byte[] {}));
@ -1027,10 +1040,10 @@ public class FullBlockTestGenerator {
b58.addTransaction(tx);
}
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
Block b59 = createNextBlock(b57, chainHeadHeight + 16, out17, null);
Block b59 = createNextBlock(b57, chainHeadHeight + 18, out17, null);
{
Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx,
@ -1040,10 +1053,10 @@ public class FullBlockTestGenerator {
b59.addTransaction(tx);
}
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);
blocks.add(new BlockAndValidity(b60, true, false, b60.getHash(), "b60"));
Block b60 = createNextBlock(b57, chainHeadHeight + 18, out17, null);
blocks.add(new BlockAndValidity(blockToHeightMap, b60, true, false, b60.getHash(), chainHeadHeight + 18, "b60"));
spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, b60.getTransactions().get(0).getHash()),
b60.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -1055,21 +1068,22 @@ public class FullBlockTestGenerator {
//
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();
scriptBytes[0]--; // createNextBlock will increment the first script byte on each new block
b61.getTransactions().get(0).getInputs().get(0).setScriptBytes(scriptBytes);
b61.unCache();
Preconditions.checkState(b61.getTransactions().get(0).equals(b60.getTransactions().get(0)));
}
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)
// -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
// \-> b62 (18)
//
Block b62 = createNextBlock(b60, chainHeadHeight + 17, null, null);
Block b62 = createNextBlock(b60, chainHeadHeight + 19, null, null);
{
Transaction tx = new Transaction(params);
tx.setLockTime(0xffffffffL);
@ -1079,20 +1093,20 @@ public class FullBlockTestGenerator {
Preconditions.checkState(!tx.isFinal(chainHeadHeight + 17, b62.getTimeSeconds()));
}
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
// -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
// \-> 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).getInputs().get(0).setSequenceNumber(0xDEADBEEF);
Preconditions.checkState(!b63.getTransactions().get(0).isFinal(chainHeadHeight + 17, b63.getTimeSeconds()));
}
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
// 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 b64Created = createNextBlock(b60, chainHeadHeight + 17, out18, null);
Block b64Created = createNextBlock(b60, chainHeadHeight + 19, out18, null);
Transaction tx = new Transaction(params);
// 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];
@ -1133,7 +1147,7 @@ public class FullBlockTestGenerator {
Preconditions.checkState(Arrays.equals(stream.toByteArray(), b64.bitcoinSerialize()));
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(
new TransactionOutPoint(params, 0, b64.getTransactions().get(0).getHash()),
b64.getTransactions().get(0).getOutputs().get(0).getValue(),
@ -1142,9 +1156,9 @@ public class FullBlockTestGenerator {
// Spend an output created in the block itself
// -> 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);
tx1.addOutput(new TransactionOutput(params, tx1, out19.value, new byte[]{OP_TRUE}));
@ -1157,7 +1171,7 @@ public class FullBlockTestGenerator {
b65.addTransaction(tx2);
}
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(
new TransactionOutPoint(params, 0, b65.getTransactions().get(0).getHash()),
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)
// \-> 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);
tx1.addOutput(new TransactionOutput(params, tx1, out20.value, new byte[]{OP_TRUE}));
@ -1182,13 +1196,13 @@ public class FullBlockTestGenerator {
b66.addTransaction(tx1);
}
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
// -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
// \-> b67 (20)
//
Block b67 = createNextBlock(b65, chainHeadHeight + 19, null, null);
Block b67 = createNextBlock(b65, chainHeadHeight + 21, null, null);
{
Transaction tx1 = new Transaction(params);
tx1.addOutput(new TransactionOutput(params, tx1, out20.value, new byte[]{OP_TRUE}));
@ -1206,13 +1220,13 @@ public class FullBlockTestGenerator {
b67.addTransaction(tx3);
}
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
// -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (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);
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.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);
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.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
// -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
// \-> b70 (21)
//
TransactionOutPointWithValue out21 = spendableOutputs.poll();
Block b70 = createNextBlock(b69, chainHeadHeight + 20, out21, null);
TransactionOutPointWithValue out21 = spendableOutputs.poll(); Preconditions.checkState(out21 != null);
Block b70 = createNextBlock(b69, chainHeadHeight + 22, out21, null);
{
Transaction tx = new Transaction(params);
tx.addOutput(new TransactionOutput(params, tx, BigInteger.ZERO, new byte[]{OP_TRUE}));
@ -1246,13 +1260,13 @@ public class FullBlockTestGenerator {
b70.addTransaction(tx);
}
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)
// -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> 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);
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());
b71.addTransaction(b72.getTransactions().get(2));
Preconditions.checkState(b71.getHash().equals(b72.getHash()));
blocks.add(new BlockAndValidity(b71, false, true, b69.getHash(), "b71"));
blocks.add(new BlockAndValidity(b72, true, false, b72.getHash(), "b72"));
blocks.add(new BlockAndValidity(blockToHeightMap, b71, false, true, b69.getHash(), chainHeadHeight + 21, "b71"));
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
@ -1277,9 +1296,12 @@ public class FullBlockTestGenerator {
private Block createNextBlock(Block baseBlock, int nextBlockHeight, TransactionOutPointWithValue prevOut,
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())
.add((prevOut != null ? prevOut.value.subtract(BigInteger.ONE) : BigInteger.valueOf(0)))
.add(additionalCoinbaseValue == null ? BigInteger.valueOf(0) : additionalCoinbaseValue);
.add((prevOut != null ? prevOut.value.subtract(BigInteger.ONE) : BigInteger.ZERO))
.add(additionalCoinbaseValue == null ? BigInteger.ZERO : additionalCoinbaseValue);
Block block = baseBlock.createNextBlockWithCoinbase(coinbaseOutKeyPubKey, coinbaseValue);
if (prevOut != null) {
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);
fail();
}
if (chain.getChainHead().getHeight() != block.heightAfterBlock) {
log.error("New block head didn't match the correct height after block " + block.blockName);
fail();
}
}
}