Skip to content

Commit

Permalink
Make Block a little less mutable, take bitcoinj#2
Browse files Browse the repository at this point in the history
* Move difficultyTarget and time to constructors (where possible)

NetworkParameters and subclasses:

* Move Genesis Block parameters to constructors
* Prefer static final fields (constants) to magic numbers
  • Loading branch information
msgilligan committed Sep 7, 2021
1 parent 0cb6864 commit 01eba3c
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 73 deletions.
24 changes: 11 additions & 13 deletions core/src/main/java/org/bitcoinj/core/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ public enum VerifyFlag {
protected int optimalEncodingMessageSize;

/** Special case constructor, used for the genesis node, cloneAsHeader and unit tests. */
Block(NetworkParameters params, long setVersion) {
Block(NetworkParameters params, long setVersion, long difficultyTarget, long time) {
super(params);
// Set up a few basic things. We are not complete after this though.
version = setVersion;
difficultyTarget = 0x1d07fff8L;
time = Utils.currentTimeSeconds();
this.difficultyTarget = difficultyTarget;
this.time = time;
prevBlockHash = Sha256Hash.ZERO_HASH;

length = HEADER_SIZE;
Expand Down Expand Up @@ -253,8 +253,8 @@ protected void parse() throws ProtocolException {
length = cursor - offset;
}

static Block createGenesis(NetworkParameters n) {
Block genesisBlock = new Block(n, BLOCK_VERSION_GENESIS);
static Block createGenesis(NetworkParameters n, long difficultyTarget, long genesisTime) {
Block genesisBlock = new Block(n, BLOCK_VERSION_GENESIS, difficultyTarget, genesisTime);
Transaction t = createGenesisTransaction(n, genesisTxInputScriptBytes, FIFTY_COINS, genesisTxScriptPubKeyBytes);
genesisBlock.addTransaction(t);
return genesisBlock;
Expand Down Expand Up @@ -471,9 +471,7 @@ public BigInteger getWork() throws VerificationException {
* @return new, header-only {@code Block}
*/
public Block cloneAsHeader() {
Block block = new Block(params, version);
block.difficultyTarget = difficultyTarget;
block.time = time;
Block block = new Block(params, version, this.difficultyTarget, this.time);
block.nonce = nonce;
block.prevBlockHash = prevBlockHash;
block.merkleRoot = getMerkleRoot();
Expand Down Expand Up @@ -888,7 +886,10 @@ public long getDifficultyTarget() {
return difficultyTarget;
}

/** Sets the difficulty target in compact form. */
/**
* Sets the difficulty target in compact form.
* @param compactForm difficulty target
*/
@VisibleForTesting
public void setDifficultyTarget(long compactForm) {
unCacheHeader();
Expand Down Expand Up @@ -979,8 +980,7 @@ Block createNextBlock(@Nullable final Address to, final long version,
@Nullable TransactionOutPoint prevOut, final long time,
final byte[] pubKey, final Coin coinbaseValue,
final int height) {
Block b = new Block(params, version);
b.setDifficultyTarget(difficultyTarget);
Block b = new Block(params, version, difficultyTarget, time);
b.addCoinbaseTransaction(pubKey, coinbaseValue, height);

if (to != null) {
Expand Down Expand Up @@ -1008,8 +1008,6 @@ Block createNextBlock(@Nullable final Address to, final long version,
// Don't let timestamp go backwards
if (getTimeSeconds() >= time)
b.setTime(getTimeSeconds() + 1);
else
b.setTime(time);
b.solve();
try {
b.verifyHeader();
Expand Down
18 changes: 16 additions & 2 deletions core/src/main/java/org/bitcoinj/core/NetworkParameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,22 @@ public abstract class NetworkParameters {
protected Map<Integer, Sha256Hash> checkpoints = new HashMap<>();
protected volatile transient MessageSerializer defaultSerializer = null;

protected NetworkParameters() {
genesisBlock = Block.createGenesis(this);
private NetworkParameters(BigInteger maxTarget, long difficultyTarget, long genesisTime) {
this.maxTarget = maxTarget;
genesisBlock = Block.createGenesis(this, difficultyTarget, genesisTime);
}

protected NetworkParameters(BigInteger maxTarget, long difficultyTarget, long genesisTime, long genesisNonce) {
this(maxTarget, difficultyTarget, genesisTime);
genesisBlock.setNonce(genesisNonce);
}

/**
* This constructor is for NetworkParameters for unit testing
*/
protected NetworkParameters(BigInteger maxTarget, long difficultyTarget) {
this(maxTarget, difficultyTarget, Utils.currentTimeSeconds());
genesisBlock.solve();
}

public static final int TARGET_TIMESPAN = 14 * 24 * 60 * 60; // 2 weeks per difficulty cycle, on average.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,19 @@ public abstract class AbstractBitcoinNetParams extends NetworkParameters {

private static final Logger log = LoggerFactory.getLogger(AbstractBitcoinNetParams.class);

public AbstractBitcoinNetParams() {
super();
{
interval = INTERVAL;
subsidyDecreaseBlockCount = REWARD_HALVING_INTERVAL;
}

public AbstractBitcoinNetParams(BigInteger maxTarget, long difficultyTarget, long genesisTime, long genesisNonce) {
super(maxTarget, difficultyTarget, genesisTime, genesisNonce);
}

public AbstractBitcoinNetParams(BigInteger maxTarget, long difficultyTarget) {
super(maxTarget, difficultyTarget);
}

/**
* Checks if we are at a reward halving point.
* @param previousHeight The height of the previous stored block
Expand Down
13 changes: 6 additions & 7 deletions core/src/main/java/org/bitcoinj/params/MainNetParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@

package org.bitcoinj.params;

import java.math.BigInteger;
import java.net.URI;

import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Utils;
import org.bitcoinj.net.discovery.HttpDiscovery;


import static com.google.common.base.Preconditions.checkState;

/**
Expand All @@ -34,18 +34,17 @@ public class MainNetParams extends AbstractBitcoinNetParams {
public static final int MAINNET_MAJORITY_WINDOW = 1000;
public static final int MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED = 950;
public static final int MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE = 750;
private static final long DIFFICULTY_TARGET = 0x1d00ffffL;
private static final BigInteger MAX_TARGET = Utils.decodeCompactBits(DIFFICULTY_TARGET);
private static final long GENESIS_TIME = 1231006505L;
private static final long GENESIS_NONCE = 2083236893;

public MainNetParams() {
super();
super(MAX_TARGET, DIFFICULTY_TARGET, GENESIS_TIME, GENESIS_NONCE);
id = ID_MAINNET;

targetTimespan = TARGET_TIMESPAN;
maxTarget = Utils.decodeCompactBits(0x1d00ffffL);

genesisBlock.setDifficultyTarget(0x1d00ffffL);
genesisBlock.setTime(1231006505L);
genesisBlock.setNonce(2083236893);

port = 8333;
packetMagic = 0xf9beb4d9L;
dumpedPrivateKeyHeader = 128;
Expand Down
10 changes: 4 additions & 6 deletions core/src/main/java/org/bitcoinj/params/RegTestParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,21 @@
*/
public class RegTestParams extends AbstractBitcoinNetParams {
private static final BigInteger MAX_TARGET = new BigInteger("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
private static final long DIFFICULTY_TARGET = 0x207fFFFFL;
private static final long GENESIS_TIME = 1296688602L;
private static final long GENESIS_NONCE = 2;

public RegTestParams() {
super();
super(MAX_TARGET, DIFFICULTY_TARGET, GENESIS_TIME, GENESIS_NONCE);
id = ID_REGTEST;

targetTimespan = TARGET_TIMESPAN;
maxTarget = MAX_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.
interval = Integer.MAX_VALUE;
subsidyDecreaseBlockCount = 150;

genesisBlock.setDifficultyTarget(0x207fFFFFL);
genesisBlock.setTime(1296688602L);
genesisBlock.setNonce(2);

port = 18444;
packetMagic = 0xfabfb5daL;
dumpedPrivateKeyHeader = 239;
Expand Down
11 changes: 5 additions & 6 deletions core/src/main/java/org/bitcoinj/params/TestNet3Params.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,17 @@ public class TestNet3Params extends AbstractBitcoinNetParams {
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 DIFFICULTY_TARGET = 0x1d00ffffL;
private static final BigInteger MAX_TARGET = Utils.decodeCompactBits(DIFFICULTY_TARGET);
private static final long GENESIS_TIME = 1296688602L;
private static final long GENESIS_NONCE = 414098458;

public TestNet3Params() {
super();
super(MAX_TARGET, DIFFICULTY_TARGET, GENESIS_TIME, GENESIS_NONCE);
id = ID_TESTNET;

targetTimespan = TARGET_TIMESPAN;
maxTarget = Utils.decodeCompactBits(0x1d00ffffL);

genesisBlock.setDifficultyTarget(0x1d00ffffL);
genesisBlock.setTime(1296688602L);
genesisBlock.setNonce(414098458);

port = 18333;
packetMagic = 0x0b110907;
dumpedPrivateKeyHeader = 239;
Expand Down
9 changes: 2 additions & 7 deletions core/src/main/java/org/bitcoinj/params/UnitTestParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package org.bitcoinj.params;

import org.bitcoinj.core.Block;
import org.bitcoinj.core.Utils;

import java.math.BigInteger;

Expand All @@ -30,20 +29,16 @@ public class UnitTestParams extends AbstractBitcoinNetParams {
public static final int UNITNET_MAJORITY_WINDOW = 8;
public static final int TESTNET_MAJORITY_REJECT_BLOCK_OUTDATED = 6;
public static final int TESTNET_MAJORITY_ENFORCE_BLOCK_UPGRADE = 4;
public static final BigInteger MAX_TARGET = new BigInteger("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);

public UnitTestParams() {
super();
super(MAX_TARGET, Block.EASIEST_DIFFICULTY_TARGET);
id = ID_UNITTESTNET;

targetTimespan = 200000000; // 6 years. Just a very big number.
maxTarget = new BigInteger("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
interval = 10;
subsidyDecreaseBlockCount = 100;

genesisBlock.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
genesisBlock.setTime(Utils.currentTimeSeconds());
genesisBlock.solve();

port = 18333;
packetMagic = 0x0b110907;
dumpedPrivateKeyHeader = 239;
Expand Down
18 changes: 6 additions & 12 deletions core/src/test/java/org/bitcoinj/core/BlockChainTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,17 +186,15 @@ public void badDifficulty() throws Exception {
assertTrue(testNetChain.add(getBlock1()));
Block b2 = getBlock2();
assertTrue(testNetChain.add(b2));
Block bad = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS);
// 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.
Block bad = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS, Block.EASIEST_DIFFICULTY_TARGET, 1279242649);
// Merkle root can be anything here, doesn't matter.
bad.setMerkleRoot(Sha256Hash.wrap("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
Expand Down Expand Up @@ -397,23 +395,19 @@ public void coinbaseTransactionAvailability() throws Exception {

// Some blocks from the test net.
private static Block getBlock2() throws Exception {
Block b2 = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS);
Block b2 = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS, 0x1d00ffff, 1296688946L);
b2.setMerkleRoot(Sha256Hash.wrap("20222eb90f5895556926c112bb5aa0df4ab5abc3107e21a6950aec3b2e3541e2"));
b2.setNonce(875942400L);
b2.setTime(1296688946L);
b2.setDifficultyTarget(0x1d00ffff);
b2.setPrevBlockHash(Sha256Hash.wrap("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206"));
assertEquals("000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820", b2.getHashAsString());
b2.verifyHeader();
return b2;
}

private static Block getBlock1() throws Exception {
Block b1 = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS);
Block b1 = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS, 0x1d00ffff, 1296688928);
b1.setMerkleRoot(Sha256Hash.wrap("f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba"));
b1.setNonce(1924588547);
b1.setTime(1296688928);
b1.setDifficultyTarget(0x1d00ffff);
b1.setPrevBlockHash(Sha256Hash.wrap("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
assertEquals("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206", b1.getHashAsString());
b1.verifyHeader();
Expand Down
4 changes: 1 addition & 3 deletions core/src/test/java/org/bitcoinj/core/BlockTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,7 @@ public byte[] bitcoinSerialize() {

@Test
public void testGenesisBlock() {
Block genesisBlock = Block.createGenesis(MainNetParams.get());
genesisBlock.setDifficultyTarget(0x1d00ffffL);
genesisBlock.setTime(1231006505L);
Block genesisBlock = Block.createGenesis(MainNetParams.get(), 0x1d00ffffL, 1231006505L);
genesisBlock.setNonce(2083236893);
assertEquals(Sha256Hash.wrap("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"), genesisBlock.getHash());
}
Expand Down
12 changes: 3 additions & 9 deletions core/src/test/java/org/bitcoinj/core/FullBlockTestGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -906,10 +906,9 @@ public boolean add(Rule element) {
TransactionOutPointWithValue out14 = spendableOutputs.poll();

// A valid block created exactly like b44 to make sure the creation itself works
Block b44 = new Block(params, Block.BLOCK_VERSION_GENESIS);
Block b44 = new Block(params, Block.BLOCK_VERSION_GENESIS, b43.block.getDifficultyTarget(), b43.block.getTimeSeconds() + 1);
byte[] outScriptBytes = ScriptBuilder.createP2PKOutputScript(ECKey.fromPublicOnly(coinbaseOutKeyPubKey)).getProgram();
{
b44.setDifficultyTarget(b43.block.getDifficultyTarget());
b44.addCoinbaseTransaction(coinbaseOutKeyPubKey, ZERO, chainHeadHeight + 15);

Transaction t = new Transaction(params);
Expand All @@ -922,17 +921,15 @@ public boolean add(Rule element) {
b44.addTransaction(t);

b44.setPrevBlockHash(b43.getHash());
b44.setTime(b43.block.getTimeSeconds() + 1);
}
b44.solve();
blocks.add(new BlockAndValidity(b44, true, false, b44.getHash(), chainHeadHeight + 15, "b44"));

TransactionOutPointWithValue out15 = spendableOutputs.poll();

// A block with a non-coinbase as the first tx
Block b45 = new Block(params, Block.BLOCK_VERSION_GENESIS);
Block b45 = new Block(params, Block.BLOCK_VERSION_GENESIS, b44.getDifficultyTarget(), b44.getTimeSeconds() + 1);
{
b45.setDifficultyTarget(b44.getDifficultyTarget());
//b45.addCoinbaseTransaction(pubKey, coinbaseValue);

Transaction t = new Transaction(params);
Expand All @@ -950,20 +947,17 @@ public boolean add(Rule element) {
b45.addTransaction(t, false);

b45.setPrevBlockHash(b44.getHash());
b45.setTime(b44.getTimeSeconds() + 1);
}
b45.solve();
blocks.add(new BlockAndValidity(b45, false, true, b44.getHash(), chainHeadHeight + 15, "b45"));

// A block with no txn
Block b46 = new Block(params, Block.BLOCK_VERSION_GENESIS);
Block b46 = new Block(params, Block.BLOCK_VERSION_GENESIS, b44.getDifficultyTarget(), b44.getTimeSeconds() + 1);
{
b46.transactions = new ArrayList<>();
b46.setDifficultyTarget(b44.getDifficultyTarget());
b46.setMerkleRoot(Sha256Hash.ZERO_HASH);

b46.setPrevBlockHash(b44.getHash());
b46.setTime(b44.getTimeSeconds() + 1);
}
b46.solve();
blocks.add(new BlockAndValidity(b46, false, true, b44.getHash(), chainHeadHeight + 15, "b46"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,17 @@

package org.bitcoinj.params;

import org.bitcoinj.core.Block;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Utils;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class AbstractBitcoinNetParamsTest {
private final AbstractBitcoinNetParams BITCOIN_PARAMS = new AbstractBitcoinNetParams() {
@Override
public String getPaymentProtocolId() {
return null;
}
};
private final AbstractBitcoinNetParams BITCOIN_PARAMS = new AbstractBitcoinNetParamsSubClass();

@Test
public void isDifficultyTransitionPoint() {
Expand Down Expand Up @@ -58,4 +55,16 @@ public void getBlockInflation() {
assertEquals(Coin.FIFTY_COINS.div(2), BITCOIN_PARAMS.getBlockInflation(210000));
assertEquals(Coin.FIFTY_COINS.div(2), BITCOIN_PARAMS.getBlockInflation(210001));
}

static class AbstractBitcoinNetParamsSubClass extends AbstractBitcoinNetParams {

AbstractBitcoinNetParamsSubClass() {
super(UnitTestParams.MAX_TARGET, Block.EASIEST_DIFFICULTY_TARGET);
}

@Override
public String getPaymentProtocolId() {
return null;
}
}
}

0 comments on commit 01eba3c

Please sign in to comment.