Block: add new constructors to reduce mutability

* New constructors and static factory methods
* Update code to use the new constructors
* Remove `public` from `@VisibleForTesting` setters where possible
This commit is contained in:
Sean Gilligan 2023-08-08 14:11:43 -07:00 committed by Andreas Schildbach
parent 734993db68
commit 16ac8751bb
8 changed files with 61 additions and 54 deletions

View file

@ -51,6 +51,7 @@ import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import static org.bitcoinj.base.Coin.FIFTY_COINS;
import static org.bitcoinj.base.Sha256Hash.hashTwice;
@ -178,13 +179,32 @@ public class Block extends BaseMessage {
return transactions;
}
/** Special case constructor, used for the genesis node, cloneAsHeader and unit tests. */
/** Special case constructor, used for unit tests. */
@VisibleForTesting
Block(long setVersion) {
// Set up a few basic things. We are not complete after this though.
version = setVersion;
difficultyTarget = 0x1d07fff8L;
time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS); // convert to Bitcoin time
prevBlockHash = Sha256Hash.ZERO_HASH;
this(setVersion,
TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS), // convert to Bitcoin time)
0x1d07fff8L,
0,
Collections.emptyList());
}
// For unit-test genesis blocks
@VisibleForTesting
Block(long setVersion, Instant time, long 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) {
this.version = setVersion;
this.time = time;
this.difficultyTarget = difficultyTarget;
this.nonce = nonce;
this.prevBlockHash = Sha256Hash.ZERO_HASH;
this.transactions = new ArrayList<>(Objects.requireNonNull(transactions));
}
/**
@ -229,12 +249,18 @@ public class Block extends BaseMessage {
transactions);
}
public static Block createGenesis() {
Block genesisBlock = new Block(BLOCK_VERSION_GENESIS);
public static Block createGenesis(Instant time, long difficultyTarget) {
return new Block(BLOCK_VERSION_GENESIS, time, difficultyTarget, genesisTransactions());
}
public static Block createGenesis(Instant time, long difficultyTarget, long nonce) {
return new Block(BLOCK_VERSION_GENESIS, time, difficultyTarget, nonce, genesisTransactions());
}
private static List<Transaction> genesisTransactions() {
Transaction tx = Transaction.coinbase(genesisTxInputScriptBytes);
tx.addOutput(new TransactionOutput(tx, FIFTY_COINS, genesisTxScriptPubKeyBytes));
genesisBlock.addTransaction(tx);
return genesisBlock;
return Collections.singletonList(tx);
}
// A script containing the difficulty bits and the following message:
@ -367,14 +393,8 @@ public class Block extends BaseMessage {
* @return new, header-only {@code Block}
*/
public Block cloneAsHeader() {
Block block = new Block(version);
block.difficultyTarget = difficultyTarget;
block.time = time;
block.nonce = nonce;
block.prevBlockHash = prevBlockHash;
block.merkleRoot = getMerkleRoot();
Block block = new Block(version, prevBlockHash, getMerkleRoot(), time, difficultyTarget, nonce, null);
block.hash = getHash();
block.transactions = null;
return block;
}
@ -729,7 +749,7 @@ public class Block extends BaseMessage {
}
@VisibleForTesting
public void setTime(Instant time) {
void setTime(Instant time) {
unCacheHeader();
this.time = time.truncatedTo(ChronoUnit.SECONDS); // convert to Bitcoin time
this.hash = null;
@ -750,7 +770,7 @@ public class Block extends BaseMessage {
/** Sets the difficulty target in compact form. */
@VisibleForTesting
public void setDifficultyTarget(long compactForm) {
void setDifficultyTarget(long compactForm) {
unCacheHeader();
this.difficultyTarget = compactForm;
this.hash = null;
@ -766,7 +786,7 @@ public class Block extends BaseMessage {
/** Sets the nonce and clears any cached data. */
@VisibleForTesting
public void setNonce(long nonce) {
void setNonce(long nonce) {
unCacheHeader();
this.nonce = nonce;
this.hash = null;

View file

@ -134,10 +134,7 @@ public class MainNetParams extends BitcoinNetworkParams {
public Block getGenesisBlock() {
synchronized (GENESIS_HASH) {
if (genesisBlock == null) {
genesisBlock = Block.createGenesis();
genesisBlock.setDifficultyTarget(Block.STANDARD_MAX_DIFFICULTY_TARGET);
genesisBlock.setTime(GENESIS_TIME);
genesisBlock.setNonce(GENESIS_NONCE);
genesisBlock = Block.createGenesis(GENESIS_TIME, Block.STANDARD_MAX_DIFFICULTY_TARGET, GENESIS_NONCE);
checkState(genesisBlock.getHash().equals(GENESIS_HASH), () -> "invalid genesis hash");
}
}

View file

@ -82,10 +82,7 @@ public class RegTestParams extends BitcoinNetworkParams {
public Block getGenesisBlock() {
synchronized (GENESIS_HASH) {
if (genesisBlock == null) {
genesisBlock = Block.createGenesis();
genesisBlock.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
genesisBlock.setTime(GENESIS_TIME);
genesisBlock.setNonce(GENESIS_NONCE);
genesisBlock = Block.createGenesis(GENESIS_TIME, Block.EASIEST_DIFFICULTY_TARGET, GENESIS_NONCE);
checkState(genesisBlock.getHash().equals(GENESIS_HASH), () ->
"invalid genesis hash");
}

View file

@ -79,10 +79,7 @@ public class SigNetParams extends BitcoinNetworkParams {
public Block getGenesisBlock() {
synchronized (GENESIS_HASH) {
if (genesisBlock == null) {
genesisBlock = Block.createGenesis();
genesisBlock.setDifficultyTarget(GENESIS_DIFFICULTY);
genesisBlock.setTime(GENESIS_TIME);
genesisBlock.setNonce(GENESIS_NONCE);
genesisBlock = Block.createGenesis(GENESIS_TIME, GENESIS_DIFFICULTY, GENESIS_NONCE);
checkState(genesisBlock.getHash().equals(GENESIS_HASH), () ->
"invalid genesis hash");
}

View file

@ -88,10 +88,7 @@ public class TestNet3Params extends BitcoinNetworkParams {
public Block getGenesisBlock() {
synchronized (GENESIS_HASH) {
if (genesisBlock == null) {
genesisBlock = Block.createGenesis();
genesisBlock.setDifficultyTarget(Block.STANDARD_MAX_DIFFICULTY_TARGET);
genesisBlock.setTime(GENESIS_TIME);
genesisBlock.setNonce(GENESIS_NONCE);
genesisBlock = Block.createGenesis(GENESIS_TIME, Block.STANDARD_MAX_DIFFICULTY_TARGET, GENESIS_NONCE);
checkState(genesisBlock.getHash().equals(GENESIS_HASH), () ->
"invalid genesis hash");
}

View file

@ -73,9 +73,7 @@ public class UnitTestParams extends BitcoinNetworkParams {
public Block getGenesisBlock() {
synchronized (this) {
if (genesisBlock == null) {
genesisBlock = Block.createGenesis();
genesisBlock.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
genesisBlock.setTime(TimeUtils.currentTime());
genesisBlock = Block.createGenesis(TimeUtils.currentTime(), Block.EASIEST_DIFFICULTY_TARGET);
}
}
return genesisBlock;

View file

@ -414,24 +414,26 @@ public class BlockChainTest {
// Some blocks from the test net.
private static Block getBlock2() throws Exception {
Block b2 = new Block(Block.BLOCK_VERSION_GENESIS);
b2.setMerkleRoot(Sha256Hash.wrap("20222eb90f5895556926c112bb5aa0df4ab5abc3107e21a6950aec3b2e3541e2"));
b2.setNonce(875942400L);
b2.setTime(Instant.ofEpochSecond(1296688946L));
b2.setDifficultyTarget(0x1d00ffff);
b2.setPrevBlockHash(Sha256Hash.wrap("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206"));
Block b2 = new Block(Block.BLOCK_VERSION_GENESIS,
Sha256Hash.wrap("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206"), // prev
Sha256Hash.wrap("20222eb90f5895556926c112bb5aa0df4ab5abc3107e21a6950aec3b2e3541e2"), // merkle
Instant.ofEpochSecond(1296688946L),
0x1d00ffff,
875942400L,
null);
assertEquals("000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820", b2.getHashAsString());
Block.verifyHeader(b2);
return b2;
}
private static Block getBlock1() throws Exception {
Block b1 = new Block(Block.BLOCK_VERSION_GENESIS);
b1.setMerkleRoot(Sha256Hash.wrap("f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba"));
b1.setNonce(1924588547);
b1.setTime(Instant.ofEpochSecond(1296688928));
b1.setDifficultyTarget(0x1d00ffff);
b1.setPrevBlockHash(Sha256Hash.wrap("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
Block b1 = new Block(Block.BLOCK_VERSION_GENESIS,
Sha256Hash.wrap("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"), // prev
Sha256Hash.wrap("f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba"), // merkle
Instant.ofEpochSecond(1296688928),
0x1d00ffff,
1924588547,
null);
assertEquals("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206", b1.getHashAsString());
Block.verifyHeader(b1);
return b1;

View file

@ -336,10 +336,9 @@ public class BlockTest {
@Test
public void testGenesisBlock() {
Block genesisBlock = Block.createGenesis();
genesisBlock.setDifficultyTarget(0x1d00ffffL);
genesisBlock.setTime(Instant.ofEpochSecond(1231006505L));
genesisBlock.setNonce(2083236893);
Block genesisBlock = Block.createGenesis(Instant.ofEpochSecond(1231006505L),
0x1d00ffffL,
2083236893);
assertEquals(Sha256Hash.wrap("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"), genesisBlock.getHash());
}
}