diff --git a/src/com/google/bitcoin/core/Block.java b/src/com/google/bitcoin/core/Block.java index ba864b17..c11bd951 100644 --- a/src/com/google/bitcoin/core/Block.java +++ b/src/com/google/bitcoin/core/Block.java @@ -35,25 +35,28 @@ import static com.google.bitcoin.core.Utils.*; public class Block extends Message { private static final long serialVersionUID = 2738848929966035281L; + /** How many bytes are required to represent a block header. */ + public static final int HEADER_SIZE = 80; + static final long ALLOWED_TIME_DRIFT = 2 * 60 * 60; // Same value as official client. + /** A value for difficultyTarget (nBits) that allows half of all possible hash solutions. Used in unit testing. */ static final long EASIEST_DIFFICULTY_TARGET = 0x207fFFFFL; // For unit testing. If not zero, use this instead of the current time. static long fakeClock = 0; - private long version; private byte[] prevBlockHash; private byte[] merkleRoot; private long time; private long difficultyTarget; // "nBits" + private long nonce; /** If null, it means this object holds only the headers. */ List transactions; - /** Stores the hash of the block. If null, getHash() will recalculate it. */ - transient private byte[] hash; + private transient byte[] hash; /** Special case constructor, used for the genesis node and unit tests. */ Block(NetworkParameters params) { @@ -429,6 +432,9 @@ public class Block extends Message { this.hash = null; } + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Unit testing related methods. + static private int coinbaseCounter; /** Adds a coinbase transaction to the block. This exists for unit tests. */ void addCoinbaseTransaction(Address to) { @@ -445,4 +451,24 @@ public class Block extends Message { coinbase.outputs.add(new TransactionOutput(params, Utils.toNanoCoins(50, 0), to)); transactions.add(coinbase); } + + /** Returns a solved block that builds on top of this one. This exists for unit tests. */ + Block createNextBlock(Address to, long time) { + Block b = new Block(params); + b.setDifficultyTarget(difficultyTarget); + b.addCoinbaseTransaction(to); + b.setPrevBlockHash(getHash()); + b.setTime(time); + b.solve(); + try { + b.verify(); + } catch (VerificationException e) { + throw new RuntimeException(e); // Cannot happen. + } + return b; + } + + Block createNextBlock(Address to) { + return createNextBlock(to, System.currentTimeMillis() / 1000); + } } diff --git a/tests/com/google/bitcoin/core/BlockChainTest.java b/tests/com/google/bitcoin/core/BlockChainTest.java index 833cc1f7..dac40a4f 100644 --- a/tests/com/google/bitcoin/core/BlockChainTest.java +++ b/tests/com/google/bitcoin/core/BlockChainTest.java @@ -71,27 +71,11 @@ public class BlockChainTest { assertTrue(testNetChain.add(b2)); } - private Block createNextBlock(Address to, Block prev) throws VerificationException { - return createNextBlock(to, prev, Block.EASIEST_DIFFICULTY_TARGET, System.currentTimeMillis() / 1000); - } - - private Block createNextBlock(Address to, Block prev, long difficultyTarget, - long time) throws VerificationException { - Block b = new Block(prev.params); - b.setDifficultyTarget(difficultyTarget); - b.addCoinbaseTransaction(to); - b.setPrevBlockHash(prev.getHash()); - b.setTime(time); - b.solve(); - b.verify(); - return b; - } - @Test public void testUnconnectedBlocks() throws Exception { - Block b1 = createNextBlock(coinbaseTo, unitTestParams.genesisBlock); - Block b2 = createNextBlock(coinbaseTo, b1); - Block b3 = createNextBlock(coinbaseTo, b2); + Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo); + Block b2 = b1.createNextBlock(coinbaseTo); + Block b3 = b2.createNextBlock(coinbaseTo); // Connected. assertTrue(chain.add(b1)); // Unconnected. @@ -111,8 +95,8 @@ public class BlockChainTest { } }); - Block b1 = createNextBlock(coinbaseTo, unitTestParams.genesisBlock); - Block b2 = createNextBlock(coinbaseTo, b1); + Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo); + Block b2 = b1.createNextBlock(coinbaseTo); assertTrue(chain.add(b1)); assertTrue(chain.add(b2)); assertFalse(flags[0]); @@ -128,12 +112,12 @@ public class BlockChainTest { // // Nothing should happen at this point. We saw b2 first so it takes priority. Address someOtherGuy = new ECKey().toAddress(unitTestParams); - Block b3 = createNextBlock(someOtherGuy, b1); + Block b3 = b1.createNextBlock(someOtherGuy); assertTrue(chain.add(b3)); assertFalse(flags[0]); // No re-org took place. assertEquals("100.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance())); // Now we add another block to make the alternative chain longer. - assertTrue(chain.add(createNextBlock(someOtherGuy, b3))); + assertTrue(chain.add(b3.createNextBlock(someOtherGuy))); assertTrue(flags[0]); // Re-org took place. flags[0] = false; // @@ -145,8 +129,8 @@ public class BlockChainTest { // These tests do not pass currently, as wallet handling of re-orgs isn't implemented. assertEquals("50.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance())); // ... and back to the first testNetChain - Block b5 = createNextBlock(coinbaseTo, b2); - Block b6 = createNextBlock(coinbaseTo, b5); + Block b5 = b2.createNextBlock(coinbaseTo); + Block b6 = b5.createNextBlock(coinbaseTo); assertTrue(chain.add(b5)); assertTrue(chain.add(b6)); // @@ -164,7 +148,7 @@ public class BlockChainTest { Block prev = unitTestParams.genesisBlock; Block.fakeClock = System.currentTimeMillis() / 1000; for (int i = 0; i < unitTestParams.interval - 1; i++) { - Block newBlock = createNextBlock(coinbaseTo, prev, Block.EASIEST_DIFFICULTY_TARGET, Block.fakeClock); + Block newBlock = prev.createNextBlock(coinbaseTo, Block.fakeClock); assertTrue(chain.add(newBlock)); prev = newBlock; // The fake chain should seem to be "fast" for the purposes of difficulty calculations. @@ -172,13 +156,15 @@ public class BlockChainTest { } // Now add another block that has no difficulty adjustment, it should be rejected. try { - chain.add(createNextBlock(coinbaseTo, prev)); + chain.add(prev.createNextBlock(coinbaseTo)); fail(); } catch (VerificationException e) { } // Create a new block with the right difficulty target given our blistering speed relative to the huge amount // of time it's supposed to take (set in the unit test network parameters). - Block b = createNextBlock(coinbaseTo, prev, 0x201fFFFFL, Block.fakeClock); + Block b = prev.createNextBlock(coinbaseTo, Block.fakeClock); + b.setDifficultyTarget(0x201fFFFFL); + b.solve(); assertTrue(chain.add(b)); // Successfully traversed a difficulty transition period. }