This commit is contained in:
Andreas Schildbach 2025-03-11 15:09:13 +00:00 committed by GitHub
commit c5396ec5d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 423 additions and 111 deletions

View file

@ -0,0 +1,169 @@
/*
* Copyright by the original author or authors.
*
* 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 org.bitcoinj.base;
import org.bitcoinj.base.internal.ByteUtils;
import java.math.BigInteger;
import java.time.Duration;
import java.util.Objects;
import static org.bitcoinj.base.internal.Preconditions.checkArgument;
/**
* The target difficulty is a value which a header hash must be equal to or below in order for that header to be a
* valid part of the block chain.
* <p>
* Difficulty values cannot be negative. A high value means low difficulty, and a low value means high difficulty.
* <p>
* The value is stored in only 32 bits of space, so it uses a less precise compact form, or "nBits". Think of it as a
* base-256 version of the scientific notation, consisting of a 1 byte exponent and 3 bytes of mantissa. That form is
* used for storing the value in this class as well, so when constructing from an integer value, be prepared to lose
* precision.
*/
public class Difficulty implements Comparable<Difficulty> {
/**
* Standard maximum value for difficulty target. For most chains this is declared to be "difficulty 1", because
* it is fairly easy.
*/
public static final Difficulty STANDARD_MAX_DIFFICULTY_TARGET = Difficulty.ofCompact(0x1d00ffff);
/**
* The easiest difficulty target possible, allowing (slightly less than) half of all possible hash solutions.
* This is the highest value this class can represent. Used for testing.
*/
public static final Difficulty EASIEST_DIFFICULTY_TARGET = Difficulty.ofCompact(0x207fffff);
// for bounds checking
private static final BigInteger MAX_INTEGER_VALUE = Difficulty.EASIEST_DIFFICULTY_TARGET.asInteger();
private final long compact;
private Difficulty(long compact) {
this.compact = compact;
}
/**
* Construct a difficulty from a compact form, sometimes called "nBits".
*
* @param compact compact form
* @return constructed difficulty
*/
public static Difficulty ofCompact(long compact) {
long exponent = (compact >> 24) & 0xff;
checkArgument(exponent <= 32, () ->
"exponent cannot exceed 32: " + Long.toHexString(compact));
long mantissa = compact & 0xffffff;
checkArgument(mantissa <= 0x7fffff, () ->
"sign bit 24 cannot be set: " + Long.toHexString(compact));
checkArgument(mantissa >= 0x008000, () ->
"not optimally encoded, can shift to left: " + Long.toHexString(compact));
return new Difficulty(compact);
}
/**
* Construct a difficulty from an 256-bit integer value. Because the value is stored in compact form, it will
* likely lose precision.
*
* @param value 256-bit integer value
* @return constructed difficulty
*/
public static Difficulty ofInteger(BigInteger value) {
checkArgument(value.signum() >= 0, () ->
"cannot be negative: " + value.toString(16));
checkArgument(value.compareTo(MAX_INTEGER_VALUE) <= 0, () ->
"too high: " + value.toString(16));
return new Difficulty(ByteUtils.encodeCompactBits(value));
}
/**
* Inside a block the difficulty target is represented using a compact form, sometimes called "nBits".
*
* @return difficulty target as a long in compact form
*/
public long compact() {
return compact;
}
/**
* Returns the difficulty target as a 256 bit value that can be compared to a SHA-256 hash.
*
* @return difficulty target as 256-bit integer value
*/
public BigInteger asInteger() {
return ByteUtils.decodeCompactBits(compact);
}
/**
* Adjust this difficulty so that actual time between blocks closely matches our target. This method doesn't take
* hash rate changes between intervals into account.
*
* @param actualTimespan the actual duration of the last block interval, according to the headers
* @param targetTimespan the duration of block intervals we're targetting at
* @param maxTarget make sure adjusted difficulty doesn't get any easier than this value
* @return adjusted difficulty to be used for the next interval
*/
public Difficulty adjust(Duration actualTimespan, Duration targetTimespan, Difficulty maxTarget) {
checkArgument(!actualTimespan.isNegative(), () -> "cannot be negative: " + actualTimespan);
checkArgument(!actualTimespan.isZero(), () -> "cannot be zero: " + actualTimespan);
checkArgument(!targetTimespan.isNegative(), () -> "cannot be negative: " + targetTimespan);
checkArgument(!targetTimespan.isZero(), () -> "cannot be zero: " + targetTimespan);
BigInteger adjusted = asInteger()
.multiply(BigInteger.valueOf(actualTimespan.getSeconds()))
.divide(BigInteger.valueOf(targetTimespan.getSeconds()));
if (adjusted.compareTo(maxTarget.asInteger()) > 0)
return maxTarget;
else
return Difficulty.ofInteger(adjusted);
}
/**
* Determines if the work represented by a given block hash meets or exceeds this difficulty target. The more
* leading zero bits the hash has, the more work has been put into creating it.
*
* @param blockHash block hash that represents work
* @return true if this target is met or exceeded by given work
*/
public boolean isMetByWork(Sha256Hash blockHash) {
BigInteger work = blockHash.toBigInteger();
return work.compareTo(this.asInteger()) <= 0;
}
@Override
public String toString() {
return Long.toHexString(compact);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return this.compact == ((Difficulty) o).compact;
}
@Override
public int hashCode() {
return Objects.hash(compact);
}
@Override
public int compareTo(Difficulty other) {
// This yields the same order as if we were comparing the integer
// forms, due to the optimal encoding rule of the compact format.
return Long.compare(this.compact, other.compact);
}
}

View file

@ -0,0 +1,126 @@
/*
* Copyright by the original author or authors.
*
* 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 org.bitcoinj.base;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.math.BigInteger;
import java.time.Duration;
import static org.junit.Assert.assertEquals;
@RunWith(JUnitParamsRunner.class)
public class DifficultyTest {
@Test
@Parameters(method = "testVectors")
public void compactToInteger(long compact, String expectedInteger) {
Difficulty difficulty = Difficulty.ofCompact(compact);
BigInteger integer = difficulty.asInteger();
assertEquals(expectedInteger, integer.toString(16));
}
@Test
@Parameters(method = "testVectors")
public void integerToCompact(long expectedCompact, String integerHex) {
Difficulty difficulty = Difficulty.ofInteger(new BigInteger(integerHex, 16));
long compact = difficulty.compact();
assertEquals(expectedCompact, compact);
}
private Object[] testVectors() {
return new Object[] {
// from https://en.bitcoin.it/wiki/Difficulty
new Object[] { 0x1d00ffff, "ffff0000000000000000000000000000000000000000000000000000" }, // difficulty 1
new Object[] { 0x1b0404cb, "404cb000000000000000000000000000000000000000000000000" },
// from https://developer.bitcoin.org/reference/block_chain.html#target-nbits
new Object[] { 0x02008000, "80" },
new Object[] { 0x05009234, "92340000" },
new Object[] { 0x04123456, "12345600" },
};
}
@Test
public void exponentZero_mantissaIsShiftedOutOfExistance() {
assertEquals("0", Difficulty.ofCompact(0x00778899).asInteger().toString(16));
}
@Test
public void exponentOne_mantissaIsLoosingTwoBytes() {
assertEquals("77", Difficulty.ofCompact(0x01778899).asInteger().toString(16));
}
@Test
public void exponentTwo_mantissaIsLoosingOneByte() {
assertEquals("7788", Difficulty.ofCompact(0x02778899).asInteger().toString(16));
}
@Test(expected = IllegalArgumentException.class)
public void exponent_tooHigh() {
Difficulty.ofCompact(0x217fffff);
}
@Test
public void mantissa_highestPossible() {
Difficulty.ofCompact(0x007fffff);
}
@Test(expected = IllegalArgumentException.class)
public void mantissa_tooHigh() {
// the 24th bit is sign, so the value would be negative
Difficulty.ofCompact(0x00800000);
}
@Test
public void mantissa_lowestPossible() {
Difficulty.ofCompact(0x00008000);
}
@Test(expected = IllegalArgumentException.class)
public void mantissa_tooLow() {
// these bits can be encoded more optimally by shifting to the left by one
Difficulty.ofCompact(0x00007fff);
}
@Test(expected = IllegalArgumentException.class)
public void ofInteger_negativeValueNotAllowed() {
Difficulty.ofInteger(BigInteger.ONE.negate());
}
@Test(expected = IllegalArgumentException.class)
public void ofInteger_valueTooHigh() {
BigInteger easierThanEasiest = Difficulty.EASIEST_DIFFICULTY_TARGET.asInteger().add(BigInteger.ONE);
Difficulty.ofInteger(easierThanEasiest);
}
@Test
public void adjust_easiestEvenEasier_shouldNotAdjust() {
Difficulty easier = Difficulty.EASIEST_DIFFICULTY_TARGET.adjust(
Duration.ofDays(15), Duration.ofDays(10), Difficulty.EASIEST_DIFFICULTY_TARGET);
assertEquals(Difficulty.EASIEST_DIFFICULTY_TARGET, easier);
}
@Test
public void compareTo() {
Difficulty d1 = Difficulty.ofCompact(0x10771111);
Difficulty d2 = Difficulty.ofCompact(0x11111177);
assertEquals(1, d2.compareTo(d1));
}
}

View file

@ -18,6 +18,7 @@
package org.bitcoinj.core;
import com.google.common.annotations.VisibleForTesting;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.base.Network;
import org.bitcoinj.base.Sha256Hash;
import org.bitcoinj.core.listeners.NewBestBlockListener;
@ -493,9 +494,9 @@ public abstract class AbstractBlockChain {
return false;
}
BigInteger target = block.getDifficultyTargetAsInteger();
if (target.signum() <= 0 || target.compareTo(params.maxTarget) > 0)
throw new VerificationException("Difficulty target is out of range: " + target.toString());
Difficulty target = block.difficultyTarget();
if (target.compareTo(params.maxTarget) > 0)
throw new VerificationException("Difficulty target is out of range: " + target);
// If we want to verify transactions (ie we are running with full blocks), verify that block has transactions
if (shouldVerifyTransactions() && block.getTransactions() == null)

View file

@ -20,6 +20,7 @@ package org.bitcoinj.core;
import com.google.common.annotations.VisibleForTesting;
import org.bitcoinj.base.Address;
import org.bitcoinj.base.Coin;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.base.Sha256Hash;
import org.bitcoinj.base.VarInt;
import org.bitcoinj.base.internal.Buffers;
@ -29,7 +30,6 @@ import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.base.internal.ByteUtils;
import org.bitcoinj.base.internal.InternalUtils;
import org.bitcoinj.crypto.ECKey;
import org.bitcoinj.params.BitcoinNetworkParams;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.script.ScriptOpCodes;
@ -101,11 +101,12 @@ public class Block extends BaseMessage {
*/
public static final int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE / 50;
/** Standard maximum value for difficultyTarget (nBits) (Bitcoin MainNet and TestNet) */
public static final long STANDARD_MAX_DIFFICULTY_TARGET = 0x1d00ffffL;
/** A value for difficultyTarget (nBits) that allows (slightly less than) half of all possible hash solutions. Used in unit testing. */
public static final long EASIEST_DIFFICULTY_TARGET = 0x207fFFFFL;
/** @deprecated use {@link Difficulty#STANDARD_MAX_DIFFICULTY_TARGET} */
@Deprecated
public static final long STANDARD_MAX_DIFFICULTY_TARGET = Difficulty.STANDARD_MAX_DIFFICULTY_TARGET.compact();
/** @deprecated use {@link Difficulty#EASIEST_DIFFICULTY_TARGET} */
@Deprecated
public static final long EASIEST_DIFFICULTY_TARGET = Difficulty.EASIEST_DIFFICULTY_TARGET.compact();
/** Value to use if the block height is unknown */
public static final int BLOCK_HEIGHT_UNKNOWN = -1;
@ -125,7 +126,7 @@ public class Block extends BaseMessage {
private Sha256Hash prevBlockHash;
private Sha256Hash merkleRoot, witnessRoot;
private Instant time;
private long difficultyTarget; // "nBits"
private Difficulty difficultyTarget; // "nBits"
private long nonce;
// If null, it means this object holds only the headers.
@ -149,7 +150,7 @@ public class Block extends BaseMessage {
Sha256Hash prevBlockHash = Sha256Hash.read(payload);
Sha256Hash merkleRoot = Sha256Hash.read(payload);
Instant time = Instant.ofEpochSecond(ByteUtils.readUint32(payload));
long difficultyTarget = ByteUtils.readUint32(payload);
Difficulty difficultyTarget = Difficulty.ofCompact(ByteUtils.readUint32(payload));
long nonce = ByteUtils.readUint32(payload);
payload.reset(); // read again from the mark for the hash
Sha256Hash hash = Sha256Hash.wrapReversed(Sha256Hash.hashTwice(Buffers.readBytes(payload, HEADER_SIZE)));
@ -184,20 +185,20 @@ public class Block extends BaseMessage {
// Set up a few basic things. We are not complete after this though.
this(setVersion,
TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS), // convert to Bitcoin time)
0x1d07fff8L,
Difficulty.ofCompact(0x1d07fff8L),
0,
Collections.emptyList());
}
// For unit-test genesis blocks
// For testing only
Block(long setVersion, Instant time, long difficultyTarget, List<Transaction> transactions) {
Block(long setVersion, Instant time, Difficulty difficultyTarget, List<Transaction> transactions) {
this(setVersion, time, difficultyTarget, 0, transactions);
// Solve for nonce?
}
// For genesis blocks (and also unit tests)
Block(long setVersion, Instant time, long difficultyTarget, long nonce, List<Transaction> transactions) {
Block(long setVersion, Instant time, Difficulty difficultyTarget, long nonce, List<Transaction> transactions) {
this.version = setVersion;
this.time = time;
this.difficultyTarget = difficultyTarget;
@ -217,7 +218,7 @@ public class Block extends BaseMessage {
* @param transactions List of transactions including the coinbase, or {@code null} for header-only blocks
*/
public Block(long version, Sha256Hash prevBlockHash, Sha256Hash merkleRoot, Instant time,
long difficultyTarget, long nonce, @Nullable List<Transaction> transactions) {
Difficulty difficultyTarget, long nonce, @Nullable List<Transaction> transactions) {
super();
this.version = version;
this.prevBlockHash = prevBlockHash;
@ -230,14 +231,33 @@ public class Block extends BaseMessage {
null;
}
public static Block createGenesis(Instant time, long difficultyTarget) {
/** @deprecated use {@link #Block(long, Sha256Hash, Sha256Hash, Instant, Difficulty, long, List)} */
@Deprecated
public Block(long version, Sha256Hash prevBlockHash, Sha256Hash merkleRoot, Instant time,
long difficultyTarget, long nonce, @Nullable List<Transaction> transactions) {
this(version, prevBlockHash, merkleRoot, time, Difficulty.ofCompact(difficultyTarget), nonce, transactions);
}
public static Block createGenesis(Instant time, Difficulty difficultyTarget) {
return new Block(BLOCK_VERSION_GENESIS, time, difficultyTarget, genesisTransactions());
}
public static Block createGenesis(Instant time, long difficultyTarget, long nonce) {
/** @deprecated use {@link #createGenesis(Instant, Difficulty)} */
@Deprecated
public static Block createGenesis(Instant time, long difficultyTarget) {
return createGenesis(time, Difficulty.ofCompact(difficultyTarget));
}
public static Block createGenesis(Instant time, Difficulty difficultyTarget, long nonce) {
return new Block(BLOCK_VERSION_GENESIS, time, difficultyTarget, nonce, genesisTransactions());
}
/** @deprecated use {@link #createGenesis(Instant, Difficulty, long)} */
@Deprecated
public static Block createGenesis(Instant time, long difficultyTarget, long nonce) {
return createGenesis(time, Difficulty.ofCompact(difficultyTarget), nonce);
}
private static List<Transaction> genesisTransactions() {
Transaction tx = Transaction.coinbase(genesisTxInputScriptBytes);
tx.addOutput(new TransactionOutput(tx, FIFTY_COINS, genesisTxScriptPubKeyBytes));
@ -275,7 +295,7 @@ public class Block extends BaseMessage {
stream.write(prevBlockHash.serialize());
stream.write(getMerkleRoot().serialize());
ByteUtils.writeInt32LE(time.getEpochSecond(), stream);
ByteUtils.writeInt32LE(difficultyTarget, stream);
ByteUtils.writeInt32LE(difficultyTarget.compact(), stream);
ByteUtils.writeInt32LE(nonce, stream);
}
@ -365,7 +385,7 @@ public class Block extends BaseMessage {
* lower, the amount of work goes up.
*/
public BigInteger getWork() throws VerificationException {
BigInteger target = getDifficultyTargetAsInteger();
BigInteger target = difficultyTarget.asInteger();
return LARGEST_HASH.divide(target.add(BigInteger.ONE));
}
@ -437,14 +457,10 @@ public class Block extends BaseMessage {
}
}
/**
* Returns the difficulty target as a 256 bit value that can be compared to a SHA-256 hash. Inside a block the
* target is represented using a compact form.
*
* @return difficulty target as 256-bit value
*/
/** @deprecated use {@link #difficultyTarget()} then {@link Difficulty#asInteger()} */
@Deprecated
public BigInteger getDifficultyTargetAsInteger() {
return ByteUtils.decodeCompactBits(difficultyTarget);
return difficultyTarget.asInteger();
}
/** Returns true if the hash of the block is OK (lower than difficulty target). */
@ -461,14 +477,11 @@ public class Block extends BaseMessage {
//
// To prevent this attack from being possible, elsewhere we check that the difficultyTarget
// field is of the right value. This requires us to have the preceding blocks.
BigInteger target = getDifficultyTargetAsInteger();
BigInteger h = getHash().toBigInteger();
if (h.compareTo(target) > 0) {
if (!difficultyTarget.isMetByWork(getHash())) {
// Proof of work check failed!
if (throwException)
throw new VerificationException("Hash is higher than target: " + getHashAsString() + " vs "
+ target.toString(16));
+ difficultyTarget.asInteger().toString(16));
else
return false;
}
@ -728,23 +741,29 @@ public class Block extends BaseMessage {
}
/**
* Returns the difficulty of the proof of work that this block should meet encoded <b>in compact form</b>. The {@link
* Returns the difficulty of the proof of work that this block should meet. The {@link
* BlockChain} verifies that this is not too easy by looking at the length of the chain when the block is added.
* To find the actual value the hash should be compared against, use
* {@link Block#getDifficultyTargetAsInteger()}. Note that this is <b>not</b> the same as
* {@link Difficulty#asInteger()}. Note that this is <b>not</b> the same as
* the difficulty value reported by the Bitcoin "getdifficulty" RPC that you may see on various block explorers.
* That number is the result of applying a formula to the underlying difficulty to normalize the minimum to 1.
* Calculating the difficulty that way is currently unsupported.
*/
public long getDifficultyTarget() {
public Difficulty difficultyTarget() {
return difficultyTarget;
}
/** Sets the difficulty target in compact form. */
/** @deprecated use {@link #difficultyTarget()} then {@link Difficulty#compact()} */
@Deprecated
public long getDifficultyTarget() {
return difficultyTarget.compact();
}
/** Sets the difficulty target. */
// For testing only
void setDifficultyTarget(long compactForm) {
void setDifficultyTarget(Difficulty difficultyTarget) {
unCacheHeader();
this.difficultyTarget = compactForm;
this.difficultyTarget = difficultyTarget;
this.hash = null;
}

View file

@ -20,6 +20,7 @@ package org.bitcoinj.core;
import org.bitcoinj.base.Address;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.Coin;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.base.LegacyAddress;
import org.bitcoinj.base.Network;
import org.bitcoinj.base.Sha256Hash;
@ -51,7 +52,7 @@ import java.util.Objects;
public abstract class NetworkParameters {
// TODO: Seed nodes should be here as well.
protected BigInteger maxTarget;
protected Difficulty maxTarget;
protected int port;
protected int packetMagic; // Indicates message origin network and is used to seek to the next message when stream state is unknown.
protected int addressHeader;
@ -322,10 +323,16 @@ public abstract class NetworkParameters {
* Maximum target represents the easiest allowable proof of work.
* @return maximum target integer
*/
public BigInteger getMaxTarget() {
public Difficulty maxTarget() {
return maxTarget;
}
/** @deprecated use {@link #maxTarget()} then {@link Difficulty#asInteger()} */
@Deprecated
public BigInteger getMaxTarget() {
return maxTarget.asInteger();
}
/**
* Returns the 4 byte header for BIP32 wallet P2PKH - public key part.
* @return the header value

View file

@ -18,6 +18,7 @@
package org.bitcoinj.params;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.base.internal.Stopwatch;
import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.base.internal.ByteUtils;
@ -158,10 +159,10 @@ public abstract class BitcoinNetworkParams extends NetworkParameters {
if (!isDifficultyTransitionPoint(storedPrev.getHeight())) {
// No ... so check the difficulty didn't actually change.
if (nextBlock.getDifficultyTarget() != prev.getDifficultyTarget())
if (!nextBlock.difficultyTarget().equals(prev.difficultyTarget()))
throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.getHeight() +
": " + Long.toHexString(nextBlock.getDifficultyTarget()) + " vs " +
Long.toHexString(prev.getDifficultyTarget()));
": " + nextBlock.difficultyTarget() + " vs " +
prev.difficultyTarget());
return;
}
@ -187,37 +188,21 @@ public abstract class BitcoinNetworkParams extends NetworkParameters {
log.info("Difficulty transition traversal took {}", watch);
Block blockIntervalAgo = cursor.getHeader();
int timespan = (int) (prev.time().getEpochSecond() - blockIntervalAgo.time().getEpochSecond());
long timespan = prev.time().getEpochSecond() - blockIntervalAgo.time().getEpochSecond();
// Limit the adjustment step.
final int targetTimespan = this.getTargetTimespan();
final long targetTimespan = this.getTargetTimespan();
if (timespan < targetTimespan / 4)
timespan = targetTimespan / 4;
if (timespan > targetTimespan * 4)
timespan = targetTimespan * 4;
BigInteger newTarget = ByteUtils.decodeCompactBits(prev.getDifficultyTarget());
newTarget = newTarget.multiply(BigInteger.valueOf(timespan));
newTarget = newTarget.divide(BigInteger.valueOf(targetTimespan));
Difficulty newTarget = prev.difficultyTarget().adjust(
Duration.ofSeconds(timespan), Duration.ofSeconds(targetTimespan), this.maxTarget);
BigInteger maxTarget = this.getMaxTarget();
if (newTarget.compareTo(maxTarget) > 0) {
log.info("Difficulty hit proof of work limit: {} vs {}",
Long.toHexString(ByteUtils.encodeCompactBits(newTarget)),
Long.toHexString(ByteUtils.encodeCompactBits(maxTarget)));
newTarget = maxTarget;
}
int accuracyBytes = (int) (nextBlock.getDifficultyTarget() >>> 24) - 3;
long receivedTargetCompact = nextBlock.getDifficultyTarget();
// The calculated difficulty is to a higher precision than received, so reduce here.
BigInteger mask = BigInteger.valueOf(0xFFFFFFL).shiftLeft(accuracyBytes * 8);
newTarget = newTarget.and(mask);
long newTargetCompact = ByteUtils.encodeCompactBits(newTarget);
if (newTargetCompact != receivedTargetCompact)
Difficulty receivedTarget = nextBlock.difficultyTarget();
if (!newTarget.equals(receivedTarget))
throw new VerificationException("Network provided difficulty bits do not match what was calculated: " +
Long.toHexString(newTargetCompact) + " vs " + Long.toHexString(receivedTargetCompact));
newTarget + " vs " + receivedTarget);
}
@Override

View file

@ -18,7 +18,7 @@
package org.bitcoinj.params;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.internal.ByteUtils;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.core.Block;
import org.bitcoinj.base.Sha256Hash;
@ -41,7 +41,7 @@ public class MainNetParams extends BitcoinNetworkParams {
super(BitcoinNetwork.MAINNET);
targetTimespan = TARGET_TIMESPAN;
maxTarget = ByteUtils.decodeCompactBits(Block.STANDARD_MAX_DIFFICULTY_TARGET);
maxTarget = Difficulty.STANDARD_MAX_DIFFICULTY_TARGET;
port = 8333;
packetMagic = 0xf9beb4d9;
@ -134,7 +134,8 @@ public class MainNetParams extends BitcoinNetworkParams {
public Block getGenesisBlock() {
synchronized (GENESIS_HASH) {
if (genesisBlock == null) {
genesisBlock = Block.createGenesis(GENESIS_TIME, Block.STANDARD_MAX_DIFFICULTY_TARGET, GENESIS_NONCE);
genesisBlock = Block.createGenesis(GENESIS_TIME, Difficulty.STANDARD_MAX_DIFFICULTY_TARGET,
GENESIS_NONCE);
checkState(genesisBlock.getHash().equals(GENESIS_HASH), () -> "invalid genesis hash");
}
}

View file

@ -18,7 +18,7 @@
package org.bitcoinj.params;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.internal.ByteUtils;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.core.Block;
import org.bitcoinj.base.Sha256Hash;
@ -38,7 +38,7 @@ public class RegTestParams extends BitcoinNetworkParams {
super(BitcoinNetwork.REGTEST);
targetTimespan = TARGET_TIMESPAN;
maxTarget = ByteUtils.decodeCompactBits(Block.EASIEST_DIFFICULTY_TARGET);
maxTarget = Difficulty.EASIEST_DIFFICULTY_TARGET;
// Difficulty adjustments are disabled for regtest.
// By setting the block interval for difficulty adjustments to Integer.MAX_VALUE we make sure difficulty never
// changes.
@ -82,7 +82,7 @@ public class RegTestParams extends BitcoinNetworkParams {
public Block getGenesisBlock() {
synchronized (GENESIS_HASH) {
if (genesisBlock == null) {
genesisBlock = Block.createGenesis(GENESIS_TIME, Block.EASIEST_DIFFICULTY_TARGET, GENESIS_NONCE);
genesisBlock = Block.createGenesis(GENESIS_TIME, Difficulty.EASIEST_DIFFICULTY_TARGET, GENESIS_NONCE);
checkState(genesisBlock.getHash().equals(GENESIS_HASH), () ->
"invalid genesis hash");
}

View file

@ -17,7 +17,7 @@
package org.bitcoinj.params;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.internal.ByteUtils;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.core.Block;
import org.bitcoinj.base.Sha256Hash;
@ -34,7 +34,7 @@ public class SigNetParams extends BitcoinNetworkParams {
public static final int TESTNET_MAJORITY_WINDOW = 100;
public static final int TESTNET_MAJORITY_REJECT_BLOCK_OUTDATED = 75;
public static final int TESTNET_MAJORITY_ENFORCE_BLOCK_UPGRADE = 51;
private static final long GENESIS_DIFFICULTY = 0x1e0377ae;
private static final Difficulty GENESIS_DIFFICULTY = Difficulty.ofCompact(0x1e0377ae);
private static final Instant GENESIS_TIME = Instant.ofEpochSecond(1598918400);
private static final long GENESIS_NONCE = 52613770;
private static final Sha256Hash GENESIS_HASH = Sha256Hash.wrap("00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6");
@ -43,7 +43,7 @@ public class SigNetParams extends BitcoinNetworkParams {
super(BitcoinNetwork.SIGNET);
targetTimespan = TARGET_TIMESPAN;
maxTarget = ByteUtils.decodeCompactBits(Block.EASIEST_DIFFICULTY_TARGET);
maxTarget = Difficulty.EASIEST_DIFFICULTY_TARGET;
port = 38333;
packetMagic = 0x0a03cf40;

View file

@ -18,7 +18,7 @@
package org.bitcoinj.params;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.internal.ByteUtils;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.base.Sha256Hash;
@ -27,7 +27,6 @@ import org.bitcoinj.core.VerificationException;
import org.bitcoinj.store.BlockStore;
import org.bitcoinj.store.BlockStoreException;
import java.math.BigInteger;
import java.time.Instant;
import static org.bitcoinj.base.internal.Preconditions.checkState;
@ -48,7 +47,7 @@ public class TestNet3Params extends BitcoinNetworkParams {
super(BitcoinNetwork.TESTNET);
targetTimespan = TARGET_TIMESPAN;
maxTarget = ByteUtils.decodeCompactBits(Block.STANDARD_MAX_DIFFICULTY_TARGET);
maxTarget = Difficulty.STANDARD_MAX_DIFFICULTY_TARGET;
port = 18333;
packetMagic = 0x0b110907;
@ -89,7 +88,8 @@ public class TestNet3Params extends BitcoinNetworkParams {
public Block getGenesisBlock() {
synchronized (GENESIS_HASH) {
if (genesisBlock == null) {
genesisBlock = Block.createGenesis(GENESIS_TIME, Block.STANDARD_MAX_DIFFICULTY_TARGET, GENESIS_NONCE);
genesisBlock = Block.createGenesis(GENESIS_TIME, Difficulty.STANDARD_MAX_DIFFICULTY_TARGET,
GENESIS_NONCE);
checkState(genesisBlock.getHash().equals(GENESIS_HASH), () ->
"invalid genesis hash");
}
@ -118,14 +118,14 @@ public class TestNet3Params extends BitcoinNetworkParams {
StoredBlock cursor = storedPrev;
while (!cursor.getHeader().equals(getGenesisBlock()) &&
cursor.getHeight() % getInterval() != 0 &&
cursor.getHeader().getDifficultyTargetAsInteger().equals(getMaxTarget()))
cursor.getHeader().difficultyTarget().equals(maxTarget()))
cursor = cursor.getPrev(blockStore);
BigInteger cursorTarget = cursor.getHeader().getDifficultyTargetAsInteger();
BigInteger newTarget = nextBlock.getDifficultyTargetAsInteger();
Difficulty cursorTarget = cursor.getHeader().difficultyTarget();
Difficulty newTarget = nextBlock.difficultyTarget();
if (!cursorTarget.equals(newTarget))
throw new VerificationException("Testnet block transition that is not allowed: " +
Long.toHexString(cursor.getHeader().getDifficultyTarget()) + " vs " +
Long.toHexString(nextBlock.getDifficultyTarget()));
cursorTarget + " vs " +
newTarget);
}
} else {
super.checkDifficultyTransitions(storedPrev, nextBlock, blockStore);

View file

@ -18,8 +18,8 @@
package org.bitcoinj.params;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.base.internal.ByteUtils;
import org.bitcoinj.core.Block;
/**
@ -37,7 +37,7 @@ public class UnitTestParams extends BitcoinNetworkParams {
super(BitcoinNetwork.TESTNET);
targetTimespan = 200000000; // 6 years. Just a very big number.
maxTarget = ByteUtils.decodeCompactBits(Block.EASIEST_DIFFICULTY_TARGET);
maxTarget = Difficulty.EASIEST_DIFFICULTY_TARGET;
interval = 10;
subsidyDecreaseBlockCount = 100;
@ -73,7 +73,7 @@ public class UnitTestParams extends BitcoinNetworkParams {
public Block getGenesisBlock() {
synchronized (this) {
if (genesisBlock == null) {
genesisBlock = Block.createGenesis(TimeUtils.currentTime(), Block.EASIEST_DIFFICULTY_TARGET);
genesisBlock = Block.createGenesis(TimeUtils.currentTime(), Difficulty.EASIEST_DIFFICULTY_TARGET);
}
}
return genesisBlock;

View file

@ -20,6 +20,7 @@ package org.bitcoinj.core;
import org.bitcoinj.base.Address;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.Coin;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.base.ScriptType;
import org.bitcoinj.base.Sha256Hash;
import org.bitcoinj.base.internal.TimeUtils;
@ -208,7 +209,7 @@ public class BlockChainTest {
Block prev = chain.getChainHead().getHeader();
Instant newTime = prev.time().plus(Duration.ofMinutes(10));
Block newBlock = prev.createNextBlock(null, 1, newTime, 1);
newBlock.setDifficultyTarget(newBlock.getDifficultyTarget() + 10);
newBlock.setDifficultyTarget(Difficulty.ofCompact(newBlock.difficultyTarget().compact() + 10));
assertTrue(chain.add(newBlock));
}
@ -227,7 +228,7 @@ public class BlockChainTest {
// 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);
bad.setDifficultyTarget(Difficulty.EASIEST_DIFFICULTY_TARGET);
try {
testNetChain.add(bad);
// The difficulty target above should be rejected on the grounds of being easier than the networks
@ -418,7 +419,7 @@ public class BlockChainTest {
Sha256Hash.wrap("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206"), // prev
Sha256Hash.wrap("20222eb90f5895556926c112bb5aa0df4ab5abc3107e21a6950aec3b2e3541e2"), // merkle
Instant.ofEpochSecond(1296688946L),
0x1d00ffff,
Difficulty.STANDARD_MAX_DIFFICULTY_TARGET,
875942400L,
null);
assertEquals("000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820", b2.getHashAsString());
@ -431,7 +432,7 @@ public class BlockChainTest {
Sha256Hash.wrap("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"), // prev
Sha256Hash.wrap("f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba"), // merkle
Instant.ofEpochSecond(1296688928),
0x1d00ffff,
Difficulty.STANDARD_MAX_DIFFICULTY_TARGET,
1924588547,
null);
assertEquals("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206", b1.getHashAsString());

View file

@ -20,6 +20,7 @@ package org.bitcoinj.core;
import com.google.common.io.ByteStreams;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.Coin;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.base.ScriptType;
import org.bitcoinj.base.Sha256Hash;
import org.bitcoinj.base.VarInt;
@ -91,7 +92,7 @@ public class BlockTest {
}
private static class TweakableTestNet3Params extends TestNet3Params {
public void setMaxTarget(BigInteger limit) {
public void setMaxTarget(Difficulty limit) {
maxTarget = limit;
}
}
@ -100,7 +101,7 @@ public class BlockTest {
public void testProofOfWork() {
// This params accepts any difficulty target.
final TweakableTestNet3Params TWEAK_TESTNET = new TweakableTestNet3Params();
TWEAK_TESTNET.setMaxTarget(ByteUtils.decodeCompactBits(Block.EASIEST_DIFFICULTY_TARGET));
TWEAK_TESTNET.setMaxTarget(Difficulty.EASIEST_DIFFICULTY_TARGET);
Block block = TWEAK_TESTNET.getDefaultSerializer().makeBlock(ByteBuffer.wrap(block700000Bytes));
block.setNonce(12346);
try {
@ -111,7 +112,7 @@ public class BlockTest {
}
// Blocks contain their own difficulty target. The BlockChain verification mechanism is what stops real blocks
// from containing artificially weak difficulties.
block.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
block.setDifficultyTarget(Difficulty.EASIEST_DIFFICULTY_TARGET);
// Now it should pass.
Block.verify(TWEAK_TESTNET, block, Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
// Break the nonce again at the lower difficulty level so we can try solving for it.
@ -312,15 +313,15 @@ public class BlockTest {
@Test
public void parseBlockWithHugeDeclaredTransactionsSize() {
Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true));
Block block = new Block(1, Sha256Hash.ZERO_HASH, Sha256Hash.ZERO_HASH, Instant.ofEpochSecond(1), 1, 1,
new ArrayList<Transaction>()) {
Block block = new Block(1, Sha256Hash.ZERO_HASH, Sha256Hash.ZERO_HASH, Instant.ofEpochSecond(1),
Difficulty.EASIEST_DIFFICULTY_TARGET, 1, new ArrayList<Transaction>()) {
@Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
ByteUtils.writeInt32LE(getVersion(), stream);
stream.write(getPrevBlockHash().serialize());
stream.write(getMerkleRoot().serialize());
ByteUtils.writeInt32LE(time().getEpochSecond(), stream);
ByteUtils.writeInt32LE(getDifficultyTarget(), stream);
ByteUtils.writeInt32LE(difficultyTarget().compact(), stream);
ByteUtils.writeInt32LE(getNonce(), stream);
stream.write(VarInt.of(Integer.MAX_VALUE).serialize());
@ -338,7 +339,7 @@ public class BlockTest {
@Test
public void testGenesisBlock() {
Block genesisBlock = Block.createGenesis(Instant.ofEpochSecond(1231006505L),
0x1d00ffffL,
Difficulty.ofCompact(0x1d00ffff),
2083236893);
assertEquals(Sha256Hash.wrap("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"), genesisBlock.getHash());
}

View file

@ -17,6 +17,7 @@
package org.bitcoinj.core;
import org.bitcoinj.base.Coin;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.base.Sha256Hash;
import org.bitcoinj.base.VarInt;
import org.bitcoinj.base.internal.TimeUtils;
@ -903,7 +904,7 @@ public class FullBlockTestGenerator {
Block b44 = new Block(Block.BLOCK_VERSION_GENESIS);
byte[] outScriptBytes = ScriptBuilder.createP2PKOutputScript(ECKey.fromPublicOnly(coinbaseOutKeyPubKey)).program();
{
b44.setDifficultyTarget(b43.block.getDifficultyTarget());
b44.setDifficultyTarget(b43.block.difficultyTarget());
b44.addCoinbaseTransaction(coinbaseOutKeyPubKey, ZERO, chainHeadHeight + 15);
Transaction t = new Transaction();
@ -926,7 +927,7 @@ public class FullBlockTestGenerator {
// A block with a non-coinbase as the first tx
Block b45 = new Block(Block.BLOCK_VERSION_GENESIS);
{
b45.setDifficultyTarget(b44.getDifficultyTarget());
b45.setDifficultyTarget(b44.difficultyTarget());
//b45.addCoinbaseTransaction(pubKey, coinbaseValue);
Transaction t = new Transaction();
@ -953,7 +954,7 @@ public class FullBlockTestGenerator {
Block b46 = new Block(Block.BLOCK_VERSION_GENESIS);
{
b46.transactions = new ArrayList<>();
b46.setDifficultyTarget(b44.getDifficultyTarget());
b46.setDifficultyTarget(b44.difficultyTarget());
b46.setMerkleRoot(Sha256Hash.ZERO_HASH);
b46.setPrevBlockHash(b44.getHash());
@ -967,10 +968,9 @@ public class FullBlockTestGenerator {
{
try {
// Inverse solve
BigInteger target = b47.block.getDifficultyTargetAsInteger();
Difficulty target = b47.block.difficultyTarget();
while (true) {
BigInteger h = b47.getHash().toBigInteger();
if (h.compareTo(target) > 0) // if invalid
if (!target.isMetByWork(b47.getHash()))
break;
// increment the nonce and try again.
b47.block.setNonce(b47.block.getNonce() + 1);
@ -998,9 +998,9 @@ public class FullBlockTestGenerator {
// Block with incorrect POW limit
NewBlock b50 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
{
long diffTarget = b44.getDifficultyTarget();
diffTarget &= 0xFFBFFFFF; // Make difficulty one bit harder
b50.block.setDifficultyTarget(diffTarget);
Difficulty target = b44.difficultyTarget();
target = Difficulty.ofCompact(target.compact() & 0xFFBFFFFF); // Make difficulty one bit harder
b50.block.setDifficultyTarget(target);
}
b50.solve();
blocks.add(new BlockAndValidity(b50, false, true, b44.getHash(), chainHeadHeight + 15, "b50"));

View file

@ -177,7 +177,7 @@ public class ParseByteCacheTest {
bRef = (Block) serializerRef.deserialize(ByteBuffer.wrap(blockBytes));
// retrieve a value from header
b1.getDifficultyTarget();
b1.difficultyTarget();
// does it still match ref block?
serDeser(serializer, b1, bos.toByteArray(), null, null);
@ -187,7 +187,7 @@ public class ParseByteCacheTest {
bRef = (Block) serializerRef.deserialize(ByteBuffer.wrap(blockBytes));
// retrieve a value from a child and header
b1.getDifficultyTarget();
b1.difficultyTarget();
b1.getTransactions();
if (b1.getTransactions().size() > 0) {

View file

@ -18,6 +18,7 @@ package org.bitcoinj.core;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.bitcoinj.base.Difficulty;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -43,7 +44,7 @@ public class StoredBlockTest {
private static final BigInteger TOO_LARGE_WORK_V2 = new BigInteger(/* 33 bytes */
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
// Just an arbitrary block
private static final Block BLOCK = Block.createGenesis(Instant.now(), Block.EASIEST_DIFFICULTY_TARGET);
private static final Block BLOCK = Block.createGenesis(Instant.now(), Difficulty.EASIEST_DIFFICULTY_TARGET);
private Object[] vectors_serializeCompact_pass() {
return new Object[] {

View file

@ -18,6 +18,7 @@
package org.bitcoinj.store;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.Difficulty;
import org.bitcoinj.base.ScriptType;
import org.bitcoinj.base.Address;
import org.bitcoinj.base.internal.PlatformUtils;
@ -162,8 +163,8 @@ public class SPVBlockStoreTest {
Stopwatch watch = Stopwatch.start();
for (int i = 0; i < ITERATIONS; i++) {
// Using i as the nonce so that the block hashes are different.
Block block = new Block(0, Sha256Hash.ZERO_HASH, Sha256Hash.ZERO_HASH, Instant.EPOCH, 0, i,
Collections.emptyList());
Block block = new Block(0, Sha256Hash.ZERO_HASH, Sha256Hash.ZERO_HASH, Instant.EPOCH,
Difficulty.EASIEST_DIFFICULTY_TARGET, i, Collections.emptyList());
StoredBlock b = new StoredBlock(block, BigInteger.ZERO, i);
store.put(b);
store.setChainHead(b);