From e58df21bab452b50de64e710277b34c826b70684 Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Sun, 9 Mar 2025 21:02:17 +0100 Subject: [PATCH] TestNet3Params: fix difficulty transition check The difficulty transition check didn't properly account for the "20 minute rule" specific to testnet: if the time delta between a block and its previous block is more than 20 minutes, the block's difficulty target MUST reset to the minimum difficulty. Before this fix, it did not check the difficulty target at all in this case. So it wrongly assumed the difficulty target can be changed to any value. --- .../org/bitcoinj/params/TestNet3Params.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/params/TestNet3Params.java b/core/src/main/java/org/bitcoinj/params/TestNet3Params.java index 7a651c7a1..59e947f41 100644 --- a/core/src/main/java/org/bitcoinj/params/TestNet3Params.java +++ b/core/src/main/java/org/bitcoinj/params/TestNet3Params.java @@ -110,9 +110,12 @@ public class TestNet3Params extends BitcoinNetworkParams { // 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 = nextBlock.time().getEpochSecond() - prev.time().getEpochSecond(); - // 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) { + BigInteger expectedTarget; + if (timeDelta < 0 && nextBlock.getDifficultyTargetAsInteger().equals(getMaxTarget())) { + // There is an integer underflow bug in bitcoin-qt that means mindiff blocks are accepted when time + // goes backwards. + return; + } else if (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; @@ -120,13 +123,16 @@ public class TestNet3Params extends BitcoinNetworkParams { cursor.getHeight() % getInterval() != 0 && cursor.getHeader().getDifficultyTargetAsInteger().equals(getMaxTarget())) cursor = cursor.getPrev(blockStore); - BigInteger cursorTarget = cursor.getHeader().getDifficultyTargetAsInteger(); - BigInteger newTarget = nextBlock.getDifficultyTargetAsInteger(); - if (!cursorTarget.equals(newTarget)) - throw new VerificationException("Testnet block transition that is not allowed: " + - Long.toHexString(cursor.getHeader().getDifficultyTarget()) + " vs " + - Long.toHexString(nextBlock.getDifficultyTarget())); + expectedTarget = cursor.getHeader().getDifficultyTargetAsInteger(); + } else { + // 20 minute exception + expectedTarget = getMaxTarget(); } + BigInteger newTarget = nextBlock.getDifficultyTargetAsInteger(); + if (!newTarget.equals(expectedTarget)) + throw new VerificationException("Testnet block transition that is not allowed: " + + Long.toHexString(ByteUtils.encodeCompactBits(expectedTarget)) + " vs " + + Long.toHexString(nextBlock.getDifficultyTarget())); } else { super.checkDifficultyTransitions(storedPrev, nextBlock, blockStore); }