diff --git a/core/src/main/java/org/bitcoinj/core/AltcoinBlock.java b/core/src/main/java/org/bitcoinj/core/AltcoinBlock.java index 1118cf2ee..8c5d93ffd 100644 --- a/core/src/main/java/org/bitcoinj/core/AltcoinBlock.java +++ b/core/src/main/java/org/bitcoinj/core/AltcoinBlock.java @@ -146,6 +146,7 @@ public String getScryptHashAsString() { } @Override + @Deprecated public Coin getBlockInflation(int height) { final AltcoinNetworkParameters altParams = (AltcoinNetworkParameters) params; return altParams.getBlockSubsidy(height); diff --git a/core/src/test/java/org/libdohj/params/AbstractDogecoinParamsTest.java b/core/src/test/java/org/libdohj/params/AbstractDogecoinParamsTest.java index 9b570eaee..d2e636aa1 100644 --- a/core/src/test/java/org/libdohj/params/AbstractDogecoinParamsTest.java +++ b/core/src/test/java/org/libdohj/params/AbstractDogecoinParamsTest.java @@ -15,13 +15,14 @@ */ package org.libdohj.params; -import org.bitcoinj.core.*; +import org.bitcoinj.core.AltcoinBlock; +import org.bitcoinj.core.Coin; +import org.bitcoinj.core.Context; import org.junit.Before; import org.junit.Test; -import org.libdohj.core.AltcoinSerializer; import java.io.IOException; - +import java.util.Map; import static org.junit.Assert.assertEquals; @@ -107,30 +108,20 @@ public void shouldCalculateDigishieldDifficultyRounding() { @Test public void shouldCalculateRetarget() throws IOException { // Do a more in-depth test for the first retarget - byte[] payload; - AltcoinSerializer serializer = (AltcoinSerializer)params.getDefaultSerializer(); - final AltcoinBlock block239; - final AltcoinBlock block479; - final AltcoinBlock block480; - final AltcoinBlock block719; - final AltcoinBlock block720; - - payload = Util.getBytes(getClass().getResourceAsStream("dogecoin_block239.bin")); - block239 = (AltcoinBlock)serializer.makeBlock(payload); - payload = Util.getBytes(getClass().getResourceAsStream("dogecoin_block479.bin")); - block479 = (AltcoinBlock)serializer.makeBlock(payload); - payload = Util.getBytes(getClass().getResourceAsStream("dogecoin_block480.bin")); - block480 = (AltcoinBlock)serializer.makeBlock(payload); - payload = Util.getBytes(getClass().getResourceAsStream("dogecoin_block719.bin")); - block719 = (AltcoinBlock)serializer.makeBlock(payload); - payload = Util.getBytes(getClass().getResourceAsStream("dogecoin_block720.bin")); - block720 = (AltcoinBlock)serializer.makeBlock(payload); - - assertEquals(Sha256Hash.wrap("f9533416310fc4484cf43405a858b06afc9763ad401d267c1835d77e7d225a4e"), block239.getHash()); - assertEquals(Sha256Hash.wrap("ed83c923b532835f6597f70def42910aa9e06880e8a19b68f6b4a787f2b4b69f"), block479.getHash()); - assertEquals(Sha256Hash.wrap("a0e6d1cdef02b394d31628c3281f67e8534bec74fda1a4294b58be80c3fdf3f3"), block480.getHash()); - assertEquals(Sha256Hash.wrap("82e56e141ccfe019d475382d9a108ef86afeb297d95443dfd7250e57af805696"), block719.getHash()); - assertEquals(Sha256Hash.wrap("6b34f1a7de1954beb0ddf100bb2b618ff0183b6ae2b4a9376721ef8e04ab3b39"), block720.getHash()); + final String[][] blocks = { + {"dogecoin_block239.bin", "f9533416310fc4484cf43405a858b06afc9763ad401d267c1835d77e7d225a4e"}, + {"dogecoin_block479.bin", "ed83c923b532835f6597f70def42910aa9e06880e8a19b68f6b4a787f2b4b69f"}, + {"dogecoin_block480.bin", "a0e6d1cdef02b394d31628c3281f67e8534bec74fda1a4294b58be80c3fdf3f3"}, + {"dogecoin_block719.bin", "82e56e141ccfe019d475382d9a108ef86afeb297d95443dfd7250e57af805696"}, + {"dogecoin_block720.bin", "6b34f1a7de1954beb0ddf100bb2b618ff0183b6ae2b4a9376721ef8e04ab3b39"} + }; + final BlockLoader loader = new BlockLoader(params); + final Map loadedBlocks = loader.loadBlocks(blocks); + final AltcoinBlock block239 = loadedBlocks.get("f9533416310fc4484cf43405a858b06afc9763ad401d267c1835d77e7d225a4e"); + final AltcoinBlock block479 = loadedBlocks.get("ed83c923b532835f6597f70def42910aa9e06880e8a19b68f6b4a787f2b4b69f"); + final AltcoinBlock block480 = loadedBlocks.get("a0e6d1cdef02b394d31628c3281f67e8534bec74fda1a4294b58be80c3fdf3f3"); + final AltcoinBlock block719 = loadedBlocks.get("82e56e141ccfe019d475382d9a108ef86afeb297d95443dfd7250e57af805696"); + final AltcoinBlock block720 = loadedBlocks.get("6b34f1a7de1954beb0ddf100bb2b618ff0183b6ae2b4a9376721ef8e04ab3b39"); assertEquals(block480.getDifficultyTarget(), params.calculateNewDifficultyTargetInner(479, block479, block480, block239)); assertEquals(block720.getDifficultyTarget(), params.calculateNewDifficultyTargetInner(719, block719, block720, block479)); diff --git a/core/src/test/java/org/libdohj/params/BlockLoader.java b/core/src/test/java/org/libdohj/params/BlockLoader.java new file mode 100644 index 000000000..4f30b718d --- /dev/null +++ b/core/src/test/java/org/libdohj/params/BlockLoader.java @@ -0,0 +1,55 @@ +package org.libdohj.params; + +import org.bitcoinj.core.AltcoinBlock; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.Util; +import org.libdohj.core.AltcoinSerializer; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +class BlockLoader { + private final NetworkParameters params; + public BlockLoader(final NetworkParameters params) { + this.params = params; + } + + protected Map loadBlocks(final String[][] blocks) throws IOException { + final AltcoinSerializer serializer = (AltcoinSerializer)params.getDefaultSerializer(); + final Map loadedBlocks = new HashMap<>(); + for (String[] row: blocks) { + final InputStream stream = getClass().getResourceAsStream(row[0]); + if (stream == null) { + throw new IOException("Failed to find resource " + row[0]); + } + final byte[] payload = Util.getBytes(stream); + final AltcoinBlock block = (AltcoinBlock)serializer.makeBlock(payload); + assertEquals(row[1], block.getHashAsString()); + loadedBlocks.put(block.getHashAsString(), block); + } + return loadedBlocks; + } + + /** + * Load all the block headers found in the given filename. + */ + protected LinkedHashMap loadAllHeaders(final String filename, final int headerSize) throws IOException { + final AltcoinSerializer serializer = (AltcoinSerializer)params.getDefaultSerializer(); + final LinkedHashMap loadedBlocks = new LinkedHashMap<>(); + final InputStream stream = getClass().getResourceAsStream(filename); + if (stream == null) { + throw new IOException("Failed to find resource " + filename); + } + final byte[] payload = new byte[headerSize]; + while (stream.read(payload) == headerSize) { + final AltcoinBlock block = (AltcoinBlock) serializer.makeBlock(payload); + loadedBlocks.put(block.getHashAsString(), block); + } + return loadedBlocks; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/libdohj/params/DogecoinMainNetParamsTest.java b/core/src/test/java/org/libdohj/params/DogecoinMainNetParamsTest.java new file mode 100644 index 000000000..ef4b2e042 --- /dev/null +++ b/core/src/test/java/org/libdohj/params/DogecoinMainNetParamsTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2015, 2021 J. Ross Nicoll + * + * 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.libdohj.params; + +import org.bitcoinj.core.*; +import org.bitcoinj.script.Script; +import org.bitcoinj.store.BlockStore; +import org.bitcoinj.store.BlockStoreException; +import org.bitcoinj.store.MemoryBlockStore; +import org.bitcoinj.wallet.Wallet; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * + * @author Ross Nicoll + */ +public class DogecoinMainNetParamsTest { + private static final DogecoinMainNetParams params = DogecoinMainNetParams.get(); + private static final BlockLoader loader = new BlockLoader(params); + private static final String BLOCK_240_HASH = "3752567a4c6085970f5b726feee3b8fc0f37ca95bb2a8daf497683b5168ec8d1"; + + @Before + public void setUp() throws Exception { + Context context = new Context(params); + } + + @Test + public void shouldHaveCorrectGenesisBlock() { + final Block genesis = params.getGenesisBlock(); + final Sha256Hash expected = Sha256Hash.wrap("1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691"); + final Sha256Hash actual = genesis.getHash(); + assertEquals(expected, actual); + } + + /** + * This connects the first n blocks - this is more of a functional test than the single block + * test, and should handle cases such as difficulty recalculation. + */ + @Test + public void shouldConnectBlocks() throws BlockStoreException, PrunedException, IOException { + final BlockStore store = new MemoryBlockStore(params); + final Wallet wallet = Wallet.createDeterministic(params, Script.ScriptType.P2PKH); + final StoredBlock storedGenesis = new StoredBlock(params.getGenesisBlock(), params.genesisBlock.getWork(), 0); + store.put(storedGenesis); + final Map loadedBlocks = loader.loadAllHeaders("dogecoin_block1-240.bin", 80); + final BlockChain blockChain = new BlockChain(params, wallet, store); + assertTrue(blockChain.add(params.getGenesisBlock())); + for (Block block: loadedBlocks.values()) { + assertTrue(blockChain.add(block)); + } + assertEquals(BLOCK_240_HASH, blockChain.getChainHead().getHeader().getHashAsString()); + } +} diff --git a/core/src/test/java/org/libdohj/params/LitecoinMainNetParamsTest.java b/core/src/test/java/org/libdohj/params/LitecoinMainNetParamsTest.java new file mode 100644 index 000000000..60b8737de --- /dev/null +++ b/core/src/test/java/org/libdohj/params/LitecoinMainNetParamsTest.java @@ -0,0 +1,116 @@ +/* + * Copyright 2015, 2021 J. Ross Nicoll + * + * 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.libdohj.params; + +import org.bitcoinj.core.*; +import org.bitcoinj.script.Script; +import org.bitcoinj.store.BlockStore; +import org.bitcoinj.store.BlockStoreException; +import org.bitcoinj.store.MemoryBlockStore; +import org.bitcoinj.wallet.Wallet; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Map; +import java.util.Objects; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Ross Nicoll + */ +public class LitecoinMainNetParamsTest { + private static final LitecoinMainNetParams params = LitecoinMainNetParams.get(); + private static final BlockLoader loader = new BlockLoader(params); + public static final String GENESIS_BLOCK_HASH = "12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2"; + public static final String BLOCK_1_HASH = "80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f"; + + @Before + public void setUp() throws Exception { + Context context = new Context(params); + } + + @Test + public void shouldCloneAsHeader() { + final Block genesis = params.getGenesisBlock(); + final Block header = genesis.cloneAsHeader(); + final Sha256Hash expected = genesis.getHash(); + final Sha256Hash actual = header.getHash(); + assertEquals(genesis.getVersion(), header.getVersion()); + assertEquals(genesis.getPrevBlockHash(), header.getPrevBlockHash()); + assertEquals(genesis.getMerkleRoot(), header.getMerkleRoot()); + assertEquals(genesis.getTime(), header.getTime()); + assertEquals(genesis.getDifficultyTarget(), header.getDifficultyTarget()); + assertEquals(genesis.getNonce(), header.getNonce()); + + assertEquals(expected, actual); + } + + @Test + public void shouldHaveCorrectGenesisBlock() { + final Block genesis = params.getGenesisBlock(); + final Sha256Hash expected = Sha256Hash.wrap(GENESIS_BLOCK_HASH); + final Sha256Hash actual = genesis.getHash(); + assertEquals(expected, actual); + } + + /** + * Test connecting just block 1, while verifying progress at a number of steps. This is designed to expose if any + * of the more fundamental functionality is broken before we try connecting several hundred headers. + */ + @Test + public void shouldConnectBlock1() throws BlockStoreException, PrunedException, IOException { + final BlockStore store = new MemoryBlockStore(params); + final Wallet wallet = Wallet.createDeterministic(params, Script.ScriptType.P2PKH); + final StoredBlock storedGenesis = new StoredBlock(params.getGenesisBlock(), params.genesisBlock.getWork(), 0); + store.put(storedGenesis); + + // Verify we actually saved the genesis block correctly + assertEquals(params.getGenesisBlock().cloneAsHeader(), store.get(Sha256Hash.wrap(GENESIS_BLOCK_HASH)).getHeader()); + + final String[][] blocks = { + {"litecoin_block1.bin", BLOCK_1_HASH}, + }; + final Map loadedBlocks = loader.loadBlocks(blocks); + final BlockChain blockChain = new BlockChain(params, wallet, store); + final Block block1 = Objects.requireNonNull(loadedBlocks.get(BLOCK_1_HASH)); + + assertEquals(Sha256Hash.wrap(GENESIS_BLOCK_HASH), block1.getPrevBlockHash()); + assertTrue(blockChain.add(params.getGenesisBlock())); + assertTrue(blockChain.add(block1)); + } + + /** + * This connects the first n blocks - this is more of a functional test than the single block + * test, and should handle cases such as difficulty recalculation. + */ + @Test + public void shouldConnectBlocks() throws BlockStoreException, PrunedException, IOException { + final BlockStore store = new MemoryBlockStore(params); + final Wallet wallet = Wallet.createDeterministic(params, Script.ScriptType.P2PKH); + final StoredBlock storedGenesis = new StoredBlock(params.getGenesisBlock(), params.genesisBlock.getWork(), 0); + store.put(storedGenesis); + final Map loadedBlocks = loader.loadAllHeaders("litecoin_block1-240.bin", 80); + final BlockChain blockChain = new BlockChain(params, wallet, store); + assertTrue(blockChain.add(params.getGenesisBlock())); + for (Block block: loadedBlocks.values()) { + assertTrue(blockChain.add(block)); + } + } +} diff --git a/core/src/test/resources/org/libdohj/params/dogecoin_block1-240.bin b/core/src/test/resources/org/libdohj/params/dogecoin_block1-240.bin new file mode 100644 index 000000000..9567f38af Binary files /dev/null and b/core/src/test/resources/org/libdohj/params/dogecoin_block1-240.bin differ diff --git a/core/src/test/resources/org/libdohj/params/litecoin_block1-240.bin b/core/src/test/resources/org/libdohj/params/litecoin_block1-240.bin new file mode 100644 index 000000000..44c540858 Binary files /dev/null and b/core/src/test/resources/org/libdohj/params/litecoin_block1-240.bin differ diff --git a/core/src/test/resources/org/libdohj/params/litecoin_block1.bin b/core/src/test/resources/org/libdohj/params/litecoin_block1.bin new file mode 100644 index 000000000..9986d00d2 Binary files /dev/null and b/core/src/test/resources/org/libdohj/params/litecoin_block1.bin differ