mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-07 14:54:15 +00:00
Catch up with new testnet rules. Resolves issue 164.
This commit is contained in:
parent
2f904e7a37
commit
cf30280767
@ -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
|
// 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.
|
// have more blocks.
|
||||||
checkState(tryConnecting, "bug in tryConnectingUnconnected");
|
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);
|
unconnectedBlocks.add(block);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@ -207,7 +207,6 @@ public class BlockChain {
|
|||||||
// Create a new StoredBlock from this block. It will throw away the transaction data so when block goes
|
// 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.
|
// out of scope we will reclaim the used memory.
|
||||||
StoredBlock newStoredBlock = storedPrev.build(block);
|
StoredBlock newStoredBlock = storedPrev.build(block);
|
||||||
if (params.checkBlockDifficulty)
|
|
||||||
checkDifficultyTransitions(storedPrev, newStoredBlock);
|
checkDifficultyTransitions(storedPrev, newStoredBlock);
|
||||||
blockStore.put(newStoredBlock);
|
blockStore.put(newStoredBlock);
|
||||||
connectBlock(newStoredBlock, storedPrev, block.transactions);
|
connectBlock(newStoredBlock, storedPrev, block.transactions);
|
||||||
@ -406,6 +405,9 @@ public class BlockChain {
|
|||||||
} while (blocksConnectedThisRound > 0);
|
} while (blocksConnectedThisRound > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// February 16th 2012
|
||||||
|
private static Date testnetDiffDate = new Date(1329264000000L);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throws an exception if the blocks difficulty is not correct.
|
* Throws an exception if the blocks difficulty is not correct.
|
||||||
*/
|
*/
|
||||||
@ -413,8 +415,18 @@ public class BlockChain {
|
|||||||
throws BlockStoreException, VerificationException {
|
throws BlockStoreException, VerificationException {
|
||||||
Block prev = storedPrev.getHeader();
|
Block prev = storedPrev.getHeader();
|
||||||
Block next = storedNext.getHeader();
|
Block next = storedNext.getHeader();
|
||||||
|
|
||||||
// Is this supposed to be a difficulty transition point?
|
// Is this supposed to be a difficulty transition point?
|
||||||
if ((storedPrev.getHeight() + 1) % params.interval != 0) {
|
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.
|
// No ... so check the difficulty didn't actually change.
|
||||||
if (next.getDifficultyTarget() != prev.getDifficultyTarget())
|
if (next.getDifficultyTarget() != prev.getDifficultyTarget())
|
||||||
throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.getHeight() +
|
throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.getHeight() +
|
||||||
@ -435,7 +447,7 @@ public class BlockChain {
|
|||||||
}
|
}
|
||||||
cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
|
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();
|
Block blockIntervalAgo = cursor.getHeader();
|
||||||
int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds());
|
int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds());
|
||||||
@ -450,7 +462,7 @@ public class BlockChain {
|
|||||||
newDifficulty = newDifficulty.divide(BigInteger.valueOf(params.targetTimespan));
|
newDifficulty = newDifficulty.divide(BigInteger.valueOf(params.targetTimespan));
|
||||||
|
|
||||||
if (newDifficulty.compareTo(params.proofOfWorkLimit) > 0) {
|
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;
|
newDifficulty = params.proofOfWorkLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,6 +478,30 @@ public class BlockChain {
|
|||||||
receivedDifficulty.toString(16) + " vs " + newDifficulty.toString(16));
|
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
|
* 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.
|
* transactions for which it is relevant.
|
||||||
|
@ -24,10 +24,12 @@ import java.math.BigInteger;
|
|||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
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.
|
* evolves there may be more. You can create your own as long as they don't conflict.
|
||||||
*/
|
*/
|
||||||
public class NetworkParameters implements Serializable {
|
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
|
* 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
|
* 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;
|
public int targetTimespan;
|
||||||
/**
|
/**
|
||||||
@ -94,11 +96,6 @@ public class NetworkParameters implements Serializable {
|
|||||||
* signatures using it.
|
* signatures using it.
|
||||||
*/
|
*/
|
||||||
public byte[] alertSigningKey;
|
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
|
* 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;
|
return genesisBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
static private final int TARGET_TIMESPAN = 14 * 24 * 60 * 60; // 2 weeks per difficulty cycle, on average.
|
public static 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.
|
public static final int TARGET_SPACING = 10 * 60; // 10 minutes per block.
|
||||||
static private final int INTERVAL = TARGET_TIMESPAN / TARGET_SPACING;
|
public static final int INTERVAL = TARGET_TIMESPAN / TARGET_SPACING;
|
||||||
|
|
||||||
/** Sets up the given NetworkParameters with testnet values. */
|
/** Sets up the given NetworkParameters with testnet values. */
|
||||||
private static NetworkParameters createTestNet(NetworkParameters n) {
|
private static NetworkParameters createTestNet(NetworkParameters n) {
|
||||||
// Genesis hash is 0000000224b1593e3ff16a0e3b61285bbc393a39f78c8aa48c456142671f7110
|
// 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 = Utils.decodeCompactBits(0x1d0fffffL);
|
||||||
n.proofOfWorkLimit = new BigInteger("0000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
|
|
||||||
n.packetMagic = 0xfabfb5daL;
|
n.packetMagic = 0xfabfb5daL;
|
||||||
n.port = 18333;
|
n.port = 18333;
|
||||||
n.addressHeader = 111;
|
n.addressHeader = 111;
|
||||||
@ -158,7 +154,6 @@ public class NetworkParameters implements Serializable {
|
|||||||
n.genesisBlock.setDifficultyTarget(0x1d07fff8L);
|
n.genesisBlock.setDifficultyTarget(0x1d07fff8L);
|
||||||
n.genesisBlock.setNonce(384568319);
|
n.genesisBlock.setNonce(384568319);
|
||||||
n.id = ID_TESTNET;
|
n.id = ID_TESTNET;
|
||||||
n.checkBlockDifficulty = false;
|
|
||||||
String genesisHash = n.genesisBlock.getHashAsString();
|
String genesisHash = n.genesisBlock.getHashAsString();
|
||||||
checkState(genesisHash.equals("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"),
|
checkState(genesisHash.equals("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"),
|
||||||
genesisHash);
|
genesisHash);
|
||||||
@ -174,7 +169,7 @@ public class NetworkParameters implements Serializable {
|
|||||||
/** The primary BitCoin chain created by Satoshi. */
|
/** The primary BitCoin chain created by Satoshi. */
|
||||||
public static NetworkParameters prodNet() {
|
public static NetworkParameters prodNet() {
|
||||||
NetworkParameters n = new NetworkParameters();
|
NetworkParameters n = new NetworkParameters();
|
||||||
n.proofOfWorkLimit = new BigInteger("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
|
n.proofOfWorkLimit = Utils.decodeCompactBits(0x1d00ffffL);
|
||||||
n.port = 8333;
|
n.port = 8333;
|
||||||
n.packetMagic = 0xf9beb4d9L;
|
n.packetMagic = 0xf9beb4d9L;
|
||||||
n.addressHeader = 0;
|
n.addressHeader = 0;
|
||||||
@ -188,7 +183,6 @@ public class NetworkParameters implements Serializable {
|
|||||||
n.genesisBlock.setTime(1231006505L);
|
n.genesisBlock.setTime(1231006505L);
|
||||||
n.genesisBlock.setNonce(2083236893);
|
n.genesisBlock.setNonce(2083236893);
|
||||||
n.id = ID_PRODNET;
|
n.id = ID_PRODNET;
|
||||||
n.checkBlockDifficulty = true;
|
|
||||||
String genesisHash = n.genesisBlock.getHashAsString();
|
String genesisHash = n.genesisBlock.getHashAsString();
|
||||||
checkState(genesisHash.equals("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
|
checkState(genesisHash.equals("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
|
||||||
genesisHash);
|
genesisHash);
|
||||||
@ -204,7 +198,6 @@ public class NetworkParameters implements Serializable {
|
|||||||
n.genesisBlock.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
|
n.genesisBlock.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
|
||||||
n.interval = 10;
|
n.interval = 10;
|
||||||
n.targetTimespan = 200000000; // 6 years. Just a very big number.
|
n.targetTimespan = 200000000; // 6 years. Just a very big number.
|
||||||
n.checkBlockDifficulty = true;
|
|
||||||
n.id = "com.google.bitcoin.unittest";
|
n.id = "com.google.bitcoin.unittest";
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -315,7 +315,7 @@ public class Peer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processBlock(Block m) throws IOException {
|
private void processBlock(Block m) throws IOException {
|
||||||
log.info("Received broadcast block {}", m.getHashAsString());
|
log.debug("Received broadcast block {}", m.getHashAsString());
|
||||||
try {
|
try {
|
||||||
// Was this block requested by getBlock()?
|
// Was this block requested by getBlock()?
|
||||||
synchronized (pendingGetBlockFutures) {
|
synchronized (pendingGetBlockFutures) {
|
||||||
|
@ -64,7 +64,6 @@ public class BlockChainTest {
|
|||||||
|
|
||||||
coinbaseTo = wallet.keychain.get(0).toAddress(unitTestParams);
|
coinbaseTo = wallet.keychain.get(0).toAddress(unitTestParams);
|
||||||
|
|
||||||
testNet.checkBlockDifficulty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user