/**
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.bitcoin.core;
import com.google.bitcoin.bouncycastle.util.encoders.Hex;
import org.junit.Before;
import org.junit.Test;
import java.math.BigInteger;
import static org.junit.Assert.*;
// NOTE: Handling of chain splits/reorgs are in ChainSplitTests.
public class BlockChainTest {
private static final NetworkParameters testNet = NetworkParameters.testNet();
private BlockChain testNetChain;
private Wallet wallet;
private BlockChain chain;
private Address coinbaseTo;
private NetworkParameters unitTestParams;
private Address someOtherGuy;
@Before
public void setUp() {
testNetChain = new BlockChain(testNet, new Wallet(testNet), new MemoryBlockStore(testNet));
unitTestParams = NetworkParameters.unitTests();
wallet = new Wallet(unitTestParams);
wallet.addKey(new ECKey());
chain = new BlockChain(unitTestParams, wallet, new MemoryBlockStore(unitTestParams));
coinbaseTo = wallet.keychain.get(0).toAddress(unitTestParams);
someOtherGuy = new ECKey().toAddress(unitTestParams);
}
@Test
public void testBasicChaining() throws Exception {
// Check that we can plug a few blocks together.
// Block 1 from the testnet.
Block b1 = getBlock1();
assertTrue(testNetChain.add(b1));
// Block 2 from the testnet.
Block b2 = getBlock2();
// Let's try adding an invalid block.
long n = b2.getNonce();
try {
b2.setNonce(12345);
testNetChain.add(b2);
fail();
} catch (VerificationException e) {
b2.setNonce(n);
}
// Now it works because we reset the nonce.
assertTrue(testNetChain.add(b2));
}
@Test
public void testUnconnectedBlocks() throws Exception {
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinbaseTo);
Block b2 = b1.createNextBlock(coinbaseTo);
Block b3 = b2.createNextBlock(coinbaseTo);
// Connected.
assertTrue(chain.add(b1));
// Unconnected but stored. The head of the chain is still b1.
assertFalse(chain.add(b3));
assertEquals(chain.getChainHead().getHeader(), b1.cloneAsHeader());
// Add in the middle block.
assertTrue(chain.add(b2));
assertEquals(chain.getChainHead().getHeader(), b3.cloneAsHeader());
}
@Test
public void testDifficultyTransitions() throws Exception {
// Add a bunch of blocks in a loop until we reach a difficulty transition point. The unit test params have an
// artificially shortened period.
Block prev = unitTestParams.genesisBlock;
Block.fakeClock = System.currentTimeMillis() / 1000;
for (int i = 0; i < unitTestParams.interval - 1; i++) {
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.
Block.fakeClock += 2;
}
// Now add another block that has no difficulty adjustment, it should be rejected.
try {
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 = prev.createNextBlock(coinbaseTo, Block.fakeClock);
b.setDifficultyTarget(0x201fFFFFL);
b.solve();
assertTrue(chain.add(b));
// Successfully traversed a difficulty transition period.
}
@Test
public void testBadDifficulty() throws Exception {
assertTrue(testNetChain.add(getBlock1()));
Block b2 = getBlock2();
assertTrue(testNetChain.add(b2));
NetworkParameters params2 = NetworkParameters.testNet();
Block bad = new Block(params2);
// Merkle root can be anything here, doesn't matter.
bad.setMerkleRoot(Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
// Nonce was just some number that made the hash < difficulty limit set below, it can be anything.
bad.setNonce(140548933);
bad.setTime(1279242649);
bad.setPrevBlockHash(b2.getHash());
// We're going to make this block so easy 50% of solutions will pass, and check it gets rejected for having a
// bad difficulty target. Unfortunately the encoding mechanism means we cannot make one that accepts all
// solutions.
bad.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
try {
testNetChain.add(bad);
// The difficulty target above should be rejected on the grounds of being easier than the networks
// allowable difficulty.
fail();
} catch (VerificationException e) {
assertTrue(e.getMessage(), e.getMessage().indexOf("Difficulty target is bad") >= 0);
}
// Accept any level of difficulty now.
params2.proofOfWorkLimit = new BigInteger
("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
try {
testNetChain.add(bad);
// We should not get here as the difficulty target should not be changing at this point.
fail();
} catch (VerificationException e) {
assertTrue(e.getMessage(), e.getMessage().indexOf("Unexpected change in difficulty") >= 0);
}
// TODO: Test difficulty change is not out of range when a transition period becomes valid.
}
// Some blocks from the test net.
private Block getBlock2() throws Exception {
Block b2 = new Block(testNet);
b2.setMerkleRoot(Hex.decode("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b"));
b2.setNonce(2642058077L);
b2.setTime(1296734343L);
b2.setPrevBlockHash(Hex.decode("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604"));
assertEquals("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.getHashAsString());
b2.verify();
return b2;
}
private Block getBlock1() throws Exception {
Block b1 = new Block(testNet);
b1.setMerkleRoot(Hex.decode("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10"));
b1.setNonce(236038445);
b1.setTime(1296734340);
b1.setPrevBlockHash(Hex.decode("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"));
assertEquals("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.getHashAsString());
b1.verify();
return b1;
}
}