3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-30 23:02:15 +00:00

Catch up with new testnet rules. Resolves issue 164.

This commit is contained in:
Mike Hearn 2012-04-07 23:28:18 +02:00
parent 2f904e7a37
commit cf30280767
4 changed files with 52 additions and 24 deletions

View File

@ -198,7 +198,7 @@ public class BlockChain {
// block was solved whilst we were doing it. We put it to one side and try to connect it later when we
// have more blocks.
checkState(tryConnecting, "bug in tryConnectingUnconnected");
log.warn("Block does not connect: {}", block.getHashAsString());
log.warn("Block does not connect: {} prev {}", block.getHashAsString(), block.getPrevBlockHash());
unconnectedBlocks.add(block);
return false;
} else {
@ -207,8 +207,7 @@ public class BlockChain {
// Create a new StoredBlock from this block. It will throw away the transaction data so when block goes
// out of scope we will reclaim the used memory.
StoredBlock newStoredBlock = storedPrev.build(block);
if (params.checkBlockDifficulty)
checkDifficultyTransitions(storedPrev, newStoredBlock);
checkDifficultyTransitions(storedPrev, newStoredBlock);
blockStore.put(newStoredBlock);
connectBlock(newStoredBlock, storedPrev, block.transactions);
}
@ -406,6 +405,9 @@ public class BlockChain {
} while (blocksConnectedThisRound > 0);
}
// February 16th 2012
private static Date testnetDiffDate = new Date(1329264000000L);
/**
* Throws an exception if the blocks difficulty is not correct.
*/
@ -413,8 +415,18 @@ public class BlockChain {
throws BlockStoreException, VerificationException {
Block prev = storedPrev.getHeader();
Block next = storedNext.getHeader();
// Is this supposed to be a difficulty transition point?
if ((storedPrev.getHeight() + 1) % params.interval != 0) {
// TODO: Refactor this hack after 0.5 is released and we stop supporting deserialization compatibility.
// This should be a method of the NetworkParameters, which should in turn be using singletons and a subclass
// for each network type. Then each network can define its own difficulty transition rules.
if (params.getId().equals(NetworkParameters.ID_TESTNET) && next.getTime().after(testnetDiffDate)) {
checkTestnetDifficulty(storedPrev, prev, next);
return;
}
// No ... so check the difficulty didn't actually change.
if (next.getDifficultyTarget() != prev.getDifficultyTarget())
throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.getHeight() +
@ -435,7 +447,7 @@ public class BlockChain {
}
cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
}
log.debug("Difficulty transition traversal took {}msec", System.currentTimeMillis() - now);
log.info("Difficulty transition traversal took {}msec", System.currentTimeMillis() - now);
Block blockIntervalAgo = cursor.getHeader();
int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds());
@ -450,7 +462,7 @@ public class BlockChain {
newDifficulty = newDifficulty.divide(BigInteger.valueOf(params.targetTimespan));
if (newDifficulty.compareTo(params.proofOfWorkLimit) > 0) {
log.debug("Difficulty hit proof of work limit: {}", newDifficulty.toString(16));
log.info("Difficulty hit proof of work limit: {}", newDifficulty.toString(16));
newDifficulty = params.proofOfWorkLimit;
}
@ -466,6 +478,30 @@ public class BlockChain {
receivedDifficulty.toString(16) + " vs " + newDifficulty.toString(16));
}
private void checkTestnetDifficulty(StoredBlock storedPrev, Block prev, Block next) throws VerificationException, BlockStoreException {
// After 15th February 2012 the rules on the testnet change to avoid people running up the difficulty
// and then leaving, making it too hard to mine a block. On non-difficulty transition points, easy
// blocks are allowed if there has been a span of 20 minutes without one.
final long timeDelta = next.getTimeSeconds() - prev.getTimeSeconds();
// There is an integer underflow bug in bitcoin-qt that means mindiff blocks are accepted when time
// goes backwards.
if (timeDelta >= 0 && timeDelta <= NetworkParameters.TARGET_SPACING * 2) {
// Walk backwards until we find a block that doesn't have the easiest proof of work, then check
// that difficulty is equal to that one.
StoredBlock cursor = storedPrev;
while (!cursor.getHeader().equals(params.genesisBlock) &&
cursor.getHeight() % params.interval != 0 &&
cursor.getHeader().getDifficultyTargetAsInteger().equals(params.proofOfWorkLimit))
cursor = cursor.getPrev(blockStore);
BigInteger cursorDifficulty = cursor.getHeader().getDifficultyTargetAsInteger();
BigInteger newDifficulty = next.getDifficultyTargetAsInteger();
if (!cursorDifficulty.equals(newDifficulty))
throw new VerificationException("Testnet block transition that is not allowed: " +
Long.toHexString(cursor.getHeader().getDifficultyTarget()) + " vs " +
Long.toHexString(next.getDifficultyTarget()));
}
}
/**
* For the transactions in the given block, update the txToWalletMap such that each wallet maps to a list of
* transactions for which it is relevant.

View File

@ -24,10 +24,12 @@ import java.math.BigInteger;
import static com.google.common.base.Preconditions.checkState;
// TODO: Refactor this after we stop supporting serialization compatibility to use subclasses and singletons.
/**
* NetworkParameters contains the data needed for working with an instantiation of a BitCoin chain.
* NetworkParameters contains the data needed for working with an instantiation of a Bitcoin chain.<p>
*
* Currently there are only two, the production chain and the test chain. But in future as BitCoin
* Currently there are only two, the production chain and the test chain. But in future as Bitcoin
* evolves there may be more. You can create your own as long as they don't conflict.
*/
public class NetworkParameters implements Serializable {
@ -86,7 +88,7 @@ public class NetworkParameters implements Serializable {
/**
* How much time in seconds is supposed to pass between "interval" blocks. If the actual elapsed time is
* significantly different from this value, the network difficulty formula will produce a different value. Both
* test and production BitCoin networks use 2 weeks (1209600 seconds).
* test and production Bitcoin networks use 2 weeks (1209600 seconds).
*/
public int targetTimespan;
/**
@ -94,11 +96,6 @@ public class NetworkParameters implements Serializable {
* signatures using it.
*/
public byte[] alertSigningKey;
/**
* Whether to check block difficulty on this network. The rules for testnet have changed and become
* quite complicated, so don't bother verifying them. It's only a useful security check on the main network.
*/
public boolean checkBlockDifficulty;
/**
* See getId(). This may be null for old deserialized wallets. In that case we derive it heuristically
@ -136,15 +133,14 @@ public class NetworkParameters implements Serializable {
return genesisBlock;
}
static private final int TARGET_TIMESPAN = 14 * 24 * 60 * 60; // 2 weeks per difficulty cycle, on average.
static private final int TARGET_SPACING = 10 * 60; // 10 minutes per block.
static private final int INTERVAL = TARGET_TIMESPAN / TARGET_SPACING;
public static final int TARGET_TIMESPAN = 14 * 24 * 60 * 60; // 2 weeks per difficulty cycle, on average.
public static final int TARGET_SPACING = 10 * 60; // 10 minutes per block.
public static final int INTERVAL = TARGET_TIMESPAN / TARGET_SPACING;
/** Sets up the given NetworkParameters with testnet values. */
private static NetworkParameters createTestNet(NetworkParameters n) {
// Genesis hash is 0000000224b1593e3ff16a0e3b61285bbc393a39f78c8aa48c456142671f7110
// The proof of work limit has to start with 00, as otherwise the value will be interpreted as negative.
n.proofOfWorkLimit = new BigInteger("0000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
n.proofOfWorkLimit = Utils.decodeCompactBits(0x1d0fffffL);
n.packetMagic = 0xfabfb5daL;
n.port = 18333;
n.addressHeader = 111;
@ -158,7 +154,6 @@ public class NetworkParameters implements Serializable {
n.genesisBlock.setDifficultyTarget(0x1d07fff8L);
n.genesisBlock.setNonce(384568319);
n.id = ID_TESTNET;
n.checkBlockDifficulty = false;
String genesisHash = n.genesisBlock.getHashAsString();
checkState(genesisHash.equals("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"),
genesisHash);
@ -174,7 +169,7 @@ public class NetworkParameters implements Serializable {
/** The primary BitCoin chain created by Satoshi. */
public static NetworkParameters prodNet() {
NetworkParameters n = new NetworkParameters();
n.proofOfWorkLimit = new BigInteger("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
n.proofOfWorkLimit = Utils.decodeCompactBits(0x1d00ffffL);
n.port = 8333;
n.packetMagic = 0xf9beb4d9L;
n.addressHeader = 0;
@ -188,7 +183,6 @@ public class NetworkParameters implements Serializable {
n.genesisBlock.setTime(1231006505L);
n.genesisBlock.setNonce(2083236893);
n.id = ID_PRODNET;
n.checkBlockDifficulty = true;
String genesisHash = n.genesisBlock.getHashAsString();
checkState(genesisHash.equals("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
genesisHash);
@ -204,7 +198,6 @@ public class NetworkParameters implements Serializable {
n.genesisBlock.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
n.interval = 10;
n.targetTimespan = 200000000; // 6 years. Just a very big number.
n.checkBlockDifficulty = true;
n.id = "com.google.bitcoin.unittest";
return n;
}

View File

@ -315,7 +315,7 @@ public class Peer {
}
private void processBlock(Block m) throws IOException {
log.info("Received broadcast block {}", m.getHashAsString());
log.debug("Received broadcast block {}", m.getHashAsString());
try {
// Was this block requested by getBlock()?
synchronized (pendingGetBlockFutures) {

View File

@ -64,7 +64,6 @@ public class BlockChainTest {
coinbaseTo = wallet.keychain.get(0).toAddress(unitTestParams);
testNet.checkBlockDifficulty = true;
}
@Test