From 52586edb33076644923793f46a098f436cc20ceb Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 8 May 2013 15:15:02 +0200 Subject: [PATCH] NetworkParameters: Refactor out into separate classes. Hide fields behind getters and make unit tests create anonymous subclasses to tweak them rather than overwriting global variables. Introduce a regtest params class for use in the comparison tool. Conflicts: core/src/test/java/com/google/bitcoin/core/BitcoindComparisonTool.java core/src/test/java/com/google/bitcoin/core/FullPrunedBlockChainTest.java --- .../bitcoin/core/AbstractBlockChain.java | 12 +- .../com/google/bitcoin/core/AlertMessage.java | 2 +- .../java/com/google/bitcoin/core/Block.java | 2 +- .../bitcoin/core/NetworkParameters.java | 223 +++++------------- .../java/com/google/bitcoin/core/Peer.java | 2 +- .../bitcoin/core/TCPNetworkConnection.java | 2 +- .../google/bitcoin/params/MainNetParams.java | 74 ++++++ .../google/bitcoin/params/RegTestParams.java | 58 +++++ .../google/bitcoin/params/TestNet2Params.java | 57 +++++ .../google/bitcoin/params/TestNet3Params.java | 62 +++++ .../google/bitcoin/params/UnitTestParams.java | 55 +++++ .../google/bitcoin/core/AlertMessageTest.java | 11 +- .../bitcoin/core/BitcoindComparisonTool.java | 34 +-- .../google/bitcoin/core/BlockChainTest.java | 20 +- .../core/FullPrunedBlockChainTest.java | 43 ++-- .../bitcoin/tools/BuildCheckpoints.java | 3 +- 16 files changed, 424 insertions(+), 236 deletions(-) create mode 100644 core/src/main/java/com/google/bitcoin/params/MainNetParams.java create mode 100644 core/src/main/java/com/google/bitcoin/params/RegTestParams.java create mode 100644 core/src/main/java/com/google/bitcoin/params/TestNet2Params.java create mode 100644 core/src/main/java/com/google/bitcoin/params/TestNet3Params.java create mode 100644 core/src/main/java/com/google/bitcoin/params/UnitTestParams.java diff --git a/core/src/main/java/com/google/bitcoin/core/AbstractBlockChain.java b/core/src/main/java/com/google/bitcoin/core/AbstractBlockChain.java index 02ff3761..4dfd1077 100644 --- a/core/src/main/java/com/google/bitcoin/core/AbstractBlockChain.java +++ b/core/src/main/java/com/google/bitcoin/core/AbstractBlockChain.java @@ -716,7 +716,7 @@ public abstract class AbstractBlockChain { Block prev = storedPrev.getHeader(); // Is this supposed to be a difficulty transition point? - if ((storedPrev.getHeight() + 1) % params.interval != 0) { + if ((storedPrev.getHeight() + 1) % params.getInterval() != 0) { // TODO: Refactor this hack after 0.5 is released and we stop supporting deserialization compatibility. // This should be a method of the NetworkParameters, which should in turn be using singletons and a subclass @@ -738,7 +738,7 @@ public abstract class AbstractBlockChain { // two weeks after the initial block chain download. long now = System.currentTimeMillis(); StoredBlock cursor = blockStore.get(prev.getHash()); - for (int i = 0; i < params.interval - 1; i++) { + for (int i = 0; i < params.getInterval() - 1; i++) { if (cursor == null) { // This should never happen. If it does, it means we are following an incorrect or busted chain. throw new VerificationException( @@ -763,9 +763,9 @@ public abstract class AbstractBlockChain { newDifficulty = newDifficulty.multiply(BigInteger.valueOf(timespan)); newDifficulty = newDifficulty.divide(BigInteger.valueOf(targetTimespan)); - if (newDifficulty.compareTo(params.proofOfWorkLimit) > 0) { + if (newDifficulty.compareTo(params.getProofOfWorkLimit()) > 0) { log.info("Difficulty hit proof of work limit: {}", newDifficulty.toString(16)); - newDifficulty = params.proofOfWorkLimit; + newDifficulty = params.getProofOfWorkLimit(); } int accuracyBytes = (int) (nextBlock.getDifficultyTarget() >>> 24) - 3; @@ -793,8 +793,8 @@ public abstract class AbstractBlockChain { // that difficulty is equal to that one. StoredBlock cursor = storedPrev; while (!cursor.getHeader().equals(params.getGenesisBlock()) && - cursor.getHeight() % params.interval != 0 && - cursor.getHeader().getDifficultyTargetAsInteger().equals(params.proofOfWorkLimit)) + cursor.getHeight() % params.getInterval() != 0 && + cursor.getHeader().getDifficultyTargetAsInteger().equals(params.getProofOfWorkLimit())) cursor = cursor.getPrev(blockStore); BigInteger cursorDifficulty = cursor.getHeader().getDifficultyTargetAsInteger(); BigInteger newDifficulty = next.getDifficultyTargetAsInteger(); diff --git a/core/src/main/java/com/google/bitcoin/core/AlertMessage.java b/core/src/main/java/com/google/bitcoin/core/AlertMessage.java index bfeb190e..13261ef1 100644 --- a/core/src/main/java/com/google/bitcoin/core/AlertMessage.java +++ b/core/src/main/java/com/google/bitcoin/core/AlertMessage.java @@ -114,7 +114,7 @@ public class AlertMessage extends Message { * doesn't verify, because that would allow arbitrary attackers to spam your users. */ public boolean isSignatureValid() { - return ECKey.verify(Utils.doubleDigest(content), signature, params.alertSigningKey); + return ECKey.verify(Utils.doubleDigest(content), signature, params.getAlertSigningKey()); } @Override diff --git a/core/src/main/java/com/google/bitcoin/core/Block.java b/core/src/main/java/com/google/bitcoin/core/Block.java index 4aab9ea7..e2dd91a5 100644 --- a/core/src/main/java/com/google/bitcoin/core/Block.java +++ b/core/src/main/java/com/google/bitcoin/core/Block.java @@ -865,7 +865,7 @@ public class Block extends Message { return new Date(getTimeSeconds()*1000); } - void setTime(long time) { + public void setTime(long time) { unCacheHeader(); this.time = time; this.hash = null; diff --git a/core/src/main/java/com/google/bitcoin/core/NetworkParameters.java b/core/src/main/java/com/google/bitcoin/core/NetworkParameters.java index 456f02e3..c3a89d9f 100644 --- a/core/src/main/java/com/google/bitcoin/core/NetworkParameters.java +++ b/core/src/main/java/com/google/bitcoin/core/NetworkParameters.java @@ -16,6 +16,10 @@ package com.google.bitcoin.core; +import com.google.bitcoin.params.MainNetParams; +import com.google.bitcoin.params.TestNet2Params; +import com.google.bitcoin.params.TestNet3Params; +import com.google.bitcoin.params.UnitTestParams; import com.google.bitcoin.script.Script; import com.google.bitcoin.script.ScriptOpCodes; import com.google.common.base.Objects; @@ -28,15 +32,14 @@ import java.util.HashMap; import java.util.Map; import static com.google.bitcoin.core.Utils.COIN; -import static com.google.common.base.Preconditions.checkState; /** *

NetworkParameters contains the data needed for working with an instantiation of a Bitcoin chain.

* - *

Currently there are only two, the production chain and the test chain. There is also a "unit test chain" which + *

Currently there are only two, the production chain and the test chain. There is also a "unit test chain" which * is internal to bitcoinj and can't be used on a real network. In future there may be others.

*/ -public class NetworkParameters implements Serializable { +public abstract class NetworkParameters implements Serializable { /** * The protocol version this library implements. */ @@ -58,148 +61,35 @@ public class NetworkParameters implements Serializable { // TODO: Replace with getters and then finish making all these fields final. - private final Block genesisBlock; - /** What the easiest allowable proof of work should be. */ - public /*final*/ BigInteger proofOfWorkLimit; - private final int port; - private final long packetMagic; - private final int addressHeader; - private final int dumpedPrivateKeyHeader; - /** How many blocks pass between difficulty adjustment periods. Bitcoin standardises this to be 2015. */ - public /*final*/ int interval; - private final int targetTimespan; - /** - * The key used to sign {@link AlertMessage}s. You can use {@link ECKey#verify(byte[], byte[], byte[])} to verify - * signatures using it. - */ - public /*final*/ byte[] alertSigningKey; + protected Block genesisBlock; + protected BigInteger proofOfWorkLimit; + protected int port; + protected long packetMagic; + protected int addressHeader; + protected int dumpedPrivateKeyHeader; + protected int interval; + protected int targetTimespan; + protected byte[] alertSigningKey; /** * See getId(). This may be null for old deserialized wallets. In that case we derive it heuristically * by looking at the port number. */ - private final String id; + protected String id; /** * The depth of blocks required for a coinbase transaction to be spendable. */ - private final int spendableCoinbaseDepth; - private /*final*/ int subsidyDecreaseBlockCount; + protected int spendableCoinbaseDepth; + protected int subsidyDecreaseBlockCount; - /** - * If we are running in testnet-in-a-box mode, we allow connections to nodes with 0 non-genesis blocks - */ - boolean allowEmptyPeerChains; - private final int[] acceptableAddressCodes; - private final String[] dnsSeeds; - private Map checkpoints = new HashMap(); + protected int[] acceptableAddressCodes; + protected String[] dnsSeeds; + protected Map checkpoints = new HashMap(); - private NetworkParameters(int type) { + protected NetworkParameters() { alertSigningKey = SATOSHI_KEY; genesisBlock = createGenesis(this); - if (type == 0) { - // Production. - interval = INTERVAL; - targetTimespan = TARGET_TIMESPAN; - proofOfWorkLimit = Utils.decodeCompactBits(0x1d00ffffL); - acceptableAddressCodes = new int[] { 0 }; - dumpedPrivateKeyHeader = 128; - addressHeader = 0; - port = 8333; - packetMagic = 0xf9beb4d9L; - genesisBlock.setDifficultyTarget(0x1d00ffffL); - genesisBlock.setTime(1231006505L); - genesisBlock.setNonce(2083236893); - id = ID_PRODNET; - subsidyDecreaseBlockCount = 210000; - allowEmptyPeerChains = false; - spendableCoinbaseDepth = 100; - String genesisHash = genesisBlock.getHashAsString(); - checkState(genesisHash.equals("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"), - genesisHash); - - // This contains (at a minimum) the blocks which are not BIP30 compliant. BIP30 changed how duplicate - // transactions are handled. Duplicated transactions could occur in the case where a coinbase had the same - // extraNonce and the same outputs but appeared at different heights, and greatly complicated re-org handling. - // Having these here simplifies block connection logic considerably. - checkpoints.put(91722, new Sha256Hash("00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")); - checkpoints.put(91812, new Sha256Hash("00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f")); - checkpoints.put(91842, new Sha256Hash("00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")); - checkpoints.put(91880, new Sha256Hash("00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")); - checkpoints.put(200000, new Sha256Hash("000000000000034a7dedef4a161fa058a2d67a173a90155f3a2fe6fc132e0ebf")); - - dnsSeeds = new String[] { - "seed.bitcoin.sipa.be", // Pieter Wuille - "dnsseed.bluematt.me", // Matt Corallo - "dnsseed.bitcoin.dashjr.org", // Luke Dashjr - "dnsseed.plan99.net", // Mike Hearn - }; - } else if (type == 3) { - // Testnet3 - id = ID_TESTNET; - // Genesis hash is 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943 - packetMagic = 0x0b110907; - interval = INTERVAL; - targetTimespan = TARGET_TIMESPAN; - proofOfWorkLimit = Utils.decodeCompactBits(0x1d00ffffL); - port = 18333; - addressHeader = 111; - acceptableAddressCodes = new int[] { 111 }; - dumpedPrivateKeyHeader = 239; - genesisBlock.setTime(1296688602L); - genesisBlock.setDifficultyTarget(0x1d00ffffL); - genesisBlock.setNonce(414098458); - allowEmptyPeerChains = true; - spendableCoinbaseDepth = 100; - subsidyDecreaseBlockCount = 210000; - String genesisHash = genesisBlock.getHashAsString(); - checkState(genesisHash.equals("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"), - genesisHash); - - dnsSeeds = new String[] { - "testnet-seed.bitcoin.petertodd.org", - "testnet-seed.bluematt.me" - }; - } else if (type == 2) { - id = ID_TESTNET; - packetMagic = 0xfabfb5daL; - port = 18333; - addressHeader = 111; - interval = INTERVAL; - targetTimespan = TARGET_TIMESPAN; - proofOfWorkLimit = Utils.decodeCompactBits(0x1d0fffffL); - acceptableAddressCodes = new int[] { 111 }; - dumpedPrivateKeyHeader = 239; - genesisBlock.setTime(1296688602L); - genesisBlock.setDifficultyTarget(0x1d07fff8L); - genesisBlock.setNonce(384568319); - allowEmptyPeerChains = false; - spendableCoinbaseDepth = 100; - subsidyDecreaseBlockCount = 210000; - String genesisHash = genesisBlock.getHashAsString(); - checkState(genesisHash.equals("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"), - genesisHash); - dnsSeeds = null; - } else if (type == -1) { - id = ID_UNITTESTNET; - packetMagic = 0x0b110907; - addressHeader = 111; - proofOfWorkLimit = new BigInteger("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16); - genesisBlock.setTime(System.currentTimeMillis() / 1000); - genesisBlock.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET); - genesisBlock.solve(); - port = 18333; - interval = 10; - dumpedPrivateKeyHeader = 239; - allowEmptyPeerChains = false; - targetTimespan = 200000000; // 6 years. Just a very big number. - spendableCoinbaseDepth = 5; - acceptableAddressCodes = new int[] { 111 }; - subsidyDecreaseBlockCount = 100; - dnsSeeds = null; - } else { - throw new RuntimeException(); - } } private static Block createGenesis(NetworkParameters n) { @@ -241,43 +131,29 @@ public class NetworkParameters implements Serializable { */ public static final BigInteger MAX_MONEY = new BigInteger("21000000", 10).multiply(COIN); - /** Returns whatever the latest testNet parameters are. Use this rather than the versioned equivalents. */ + /** Alias for TestNet3Params.get(), use that instead. */ public static NetworkParameters testNet() { - return testNet3(); + return TestNet3Params.get(); } - private static NetworkParameters tn2; - public synchronized static NetworkParameters testNet2() { - if (tn2 == null) { - tn2 = new NetworkParameters(2); - } - return tn2; + /** Alias for TestNet2Params.get(), use that instead. */ + public static NetworkParameters testNet2() { + return TestNet2Params.get(); } - private static NetworkParameters tn3; - public synchronized static NetworkParameters testNet3() { - if (tn3 == null) { - tn3 = new NetworkParameters(3); - } - return tn3; + /** Alias for TestNet3Params.get(), use that instead. */ + public static NetworkParameters testNet3() { + return TestNet3Params.get(); } - private static NetworkParameters pn; - /** The primary Bitcoin chain created by Satoshi. */ - public synchronized static NetworkParameters prodNet() { - if (pn == null) { - pn = new NetworkParameters(0); - } - return pn; + /** Alias for MainNetParams.get(), use that instead */ + public static NetworkParameters prodNet() { + return MainNetParams.get(); } - private static NetworkParameters ut; /** Returns a testnet params modified to allow any difficulty target. */ - public synchronized static NetworkParameters unitTests() { - if (ut == null) { - ut = new NetworkParameters(-1); - } - return ut; + public static NetworkParameters unitTests() { + return UnitTestParams.get(); } /** @@ -336,10 +212,6 @@ public class NetworkParameters implements Serializable { return subsidyDecreaseBlockCount; } - public void setSubsidyDecreaseBlockCount(int value) { - subsidyDecreaseBlockCount = value; - } - /** Returns DNS names that when resolved, give IP addresses of active peers. */ public String[] getDnsSeeds() { return dnsSeeds; @@ -401,4 +273,29 @@ public class NetworkParameters implements Serializable { public int[] getAcceptableAddressCodes() { return acceptableAddressCodes; } + + /** + * If we are running in testnet-in-a-box mode, we allow connections to nodes with 0 non-genesis blocks. + */ + public boolean allowEmptyPeerChain() { + return true; + } + + /** How many blocks pass between difficulty adjustment periods. Bitcoin standardises this to be 2015. */ + public int getInterval() { + return interval; + } + + /** What the easiest allowable proof of work should be. */ + public BigInteger getProofOfWorkLimit() { + return proofOfWorkLimit; + } + + /** + * The key used to sign {@link com.google.bitcoin.core.AlertMessage}s. You can use {@link com.google.bitcoin.core.ECKey#verify(byte[], byte[], byte[])} to verify + * signatures using it. + */ + public byte[] getAlertSigningKey() { + return alertSigningKey; + } } diff --git a/core/src/main/java/com/google/bitcoin/core/Peer.java b/core/src/main/java/com/google/bitcoin/core/Peer.java index 6a5b8362..a3eea59f 100644 --- a/core/src/main/java/com/google/bitcoin/core/Peer.java +++ b/core/src/main/java/com/google/bitcoin/core/Peer.java @@ -1253,7 +1253,7 @@ public class Peer { // chainHeight should not be zero/negative because we shouldn't have given the user a Peer that is to another // client-mode node, nor should it be unconnected. If that happens it means the user overrode us somewhere or // there is a bug in the peer management code. - checkState(params.allowEmptyPeerChains || chainHeight > 0, "Connected to peer with zero/negative chain height", chainHeight); + checkState(params.allowEmptyPeerChain() || chainHeight > 0, "Connected to peer with zero/negative chain height", chainHeight); return chainHeight - blockChain.getBestChainHeight(); } diff --git a/core/src/main/java/com/google/bitcoin/core/TCPNetworkConnection.java b/core/src/main/java/com/google/bitcoin/core/TCPNetworkConnection.java index 812d720b..d86e75d7 100644 --- a/core/src/main/java/com/google/bitcoin/core/TCPNetworkConnection.java +++ b/core/src/main/java/com/google/bitcoin/core/TCPNetworkConnection.java @@ -158,7 +158,7 @@ public class TCPNetworkConnection implements NetworkConnection { // implementations claim to have a block chain in their services field but then report a height of zero, filter // them out here. if (!versionMessage.hasBlockChain() || - (!params.allowEmptyPeerChains && versionMessage.bestHeight <= 0)) { + (!params.allowEmptyPeerChain() && versionMessage.bestHeight <= 0)) { // Shut down the channel throw new ProtocolException("Peer does not have a copy of the block chain."); } diff --git a/core/src/main/java/com/google/bitcoin/params/MainNetParams.java b/core/src/main/java/com/google/bitcoin/params/MainNetParams.java new file mode 100644 index 00000000..96acad7b --- /dev/null +++ b/core/src/main/java/com/google/bitcoin/params/MainNetParams.java @@ -0,0 +1,74 @@ +/* + * Copyright 2013 Google Inc. + * + * 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 com.google.bitcoin.params; + +import com.google.bitcoin.core.NetworkParameters; +import com.google.bitcoin.core.Sha256Hash; +import com.google.bitcoin.core.Utils; + +import static com.google.common.base.Preconditions.checkState; + +/** + * Parameters for the main production network on which people trade goods and services. + */ +public class MainNetParams extends NetworkParameters { + public MainNetParams() { + super(); + interval = INTERVAL; + targetTimespan = TARGET_TIMESPAN; + proofOfWorkLimit = Utils.decodeCompactBits(0x1d00ffffL); + acceptableAddressCodes = new int[] { 0 }; + dumpedPrivateKeyHeader = 128; + addressHeader = 0; + port = 8333; + packetMagic = 0xf9beb4d9L; + genesisBlock.setDifficultyTarget(0x1d00ffffL); + genesisBlock.setTime(1231006505L); + genesisBlock.setNonce(2083236893); + id = ID_PRODNET; + subsidyDecreaseBlockCount = 210000; + spendableCoinbaseDepth = 100; + String genesisHash = genesisBlock.getHashAsString(); + checkState(genesisHash.equals("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"), + genesisHash); + + // This contains (at a minimum) the blocks which are not BIP30 compliant. BIP30 changed how duplicate + // transactions are handled. Duplicated transactions could occur in the case where a coinbase had the same + // extraNonce and the same outputs but appeared at different heights, and greatly complicated re-org handling. + // Having these here simplifies block connection logic considerably. + checkpoints.put(91722, new Sha256Hash("00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")); + checkpoints.put(91812, new Sha256Hash("00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f")); + checkpoints.put(91842, new Sha256Hash("00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")); + checkpoints.put(91880, new Sha256Hash("00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")); + checkpoints.put(200000, new Sha256Hash("000000000000034a7dedef4a161fa058a2d67a173a90155f3a2fe6fc132e0ebf")); + + dnsSeeds = new String[] { + "seed.bitcoin.sipa.be", // Pieter Wuille + "dnsseed.bluematt.me", // Matt Corallo + "dnsseed.bitcoin.dashjr.org", // Luke Dashjr + "dnsseed.plan99.net", // Mike Hearn + }; + } + + private static MainNetParams instance; + public static synchronized MainNetParams get() { + if (instance == null) { + instance = new MainNetParams(); + } + return instance; + } +} diff --git a/core/src/main/java/com/google/bitcoin/params/RegTestParams.java b/core/src/main/java/com/google/bitcoin/params/RegTestParams.java new file mode 100644 index 00000000..ffc2e233 --- /dev/null +++ b/core/src/main/java/com/google/bitcoin/params/RegTestParams.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013 Google Inc. + * + * 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 com.google.bitcoin.params; + +import com.google.bitcoin.core.Block; + +import java.math.BigInteger; + +import static com.google.common.base.Preconditions.checkState; + +/** + * Network parameters for the regression test mode of bitcoind in which all blocks are trivially solvable. + */ +public class RegTestParams extends TestNet2Params { + private static final BigInteger PROOF_OF_WORK_LIMIT = new BigInteger("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16); + + public RegTestParams() { + interval = 10000; + proofOfWorkLimit = PROOF_OF_WORK_LIMIT; + subsidyDecreaseBlockCount = 10000; + port = 18444; + } + + @Override + public boolean allowEmptyPeerChain() { + return true; + } + + private static Block genesis; + + @Override + public Block getGenesisBlock() { + synchronized (RegTestParams.class) { + if (genesis == null) { + genesis = super.getGenesisBlock(); + genesis.setNonce(2); + genesis.setDifficultyTarget(0x207fFFFFL); + genesis.setTime(1296688602L); + checkState(genesis.getHashAsString().toLowerCase().equals("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); + } + return genesis; + } + } +} diff --git a/core/src/main/java/com/google/bitcoin/params/TestNet2Params.java b/core/src/main/java/com/google/bitcoin/params/TestNet2Params.java new file mode 100644 index 00000000..6b3b2a3c --- /dev/null +++ b/core/src/main/java/com/google/bitcoin/params/TestNet2Params.java @@ -0,0 +1,57 @@ +/* + * Copyright 2013 Google Inc. + * + * 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 com.google.bitcoin.params; + +import com.google.bitcoin.core.NetworkParameters; +import com.google.bitcoin.core.Utils; + +import static com.google.common.base.Preconditions.checkState; + +/** + * Parameters for the old version 2 testnet. This is not useful to you - it exists only because some unit tests are + * based on it. + */ +public class TestNet2Params extends NetworkParameters { + public TestNet2Params() { + super(); + id = ID_TESTNET; + packetMagic = 0xfabfb5daL; + port = 18333; + addressHeader = 111; + interval = INTERVAL; + targetTimespan = TARGET_TIMESPAN; + proofOfWorkLimit = Utils.decodeCompactBits(0x1d0fffffL); + acceptableAddressCodes = new int[] { 111 }; + dumpedPrivateKeyHeader = 239; + genesisBlock.setTime(1296688602L); + genesisBlock.setDifficultyTarget(0x1d07fff8L); + genesisBlock.setNonce(384568319); + spendableCoinbaseDepth = 100; + subsidyDecreaseBlockCount = 210000; + String genesisHash = genesisBlock.getHashAsString(); + checkState(genesisHash.equals("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")); + dnsSeeds = null; + } + + private static TestNet2Params instance; + public static synchronized TestNet2Params get() { + if (instance == null) { + instance = new TestNet2Params(); + } + return instance; + } +} diff --git a/core/src/main/java/com/google/bitcoin/params/TestNet3Params.java b/core/src/main/java/com/google/bitcoin/params/TestNet3Params.java new file mode 100644 index 00000000..0b1bdf10 --- /dev/null +++ b/core/src/main/java/com/google/bitcoin/params/TestNet3Params.java @@ -0,0 +1,62 @@ +/* + * Copyright 2013 Google Inc. + * + * 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 com.google.bitcoin.params; + +import com.google.bitcoin.core.NetworkParameters; +import com.google.bitcoin.core.Utils; + +import static com.google.common.base.Preconditions.checkState; + +/** + * Parameters for the testnet, a separate public instance of Bitcoin that has relaxed rules suitable for development + * and testing of applications and new Bitcoin versions. + */ +public class TestNet3Params extends NetworkParameters { + public TestNet3Params() { + super(); + id = ID_TESTNET; + // Genesis hash is 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943 + packetMagic = 0x0b110907; + interval = INTERVAL; + targetTimespan = TARGET_TIMESPAN; + proofOfWorkLimit = Utils.decodeCompactBits(0x1d00ffffL); + port = 18333; + addressHeader = 111; + acceptableAddressCodes = new int[] { 111 }; + dumpedPrivateKeyHeader = 239; + genesisBlock.setTime(1296688602L); + genesisBlock.setDifficultyTarget(0x1d00ffffL); + genesisBlock.setNonce(414098458); + spendableCoinbaseDepth = 100; + subsidyDecreaseBlockCount = 210000; + String genesisHash = genesisBlock.getHashAsString(); + checkState(genesisHash.equals("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943")); + + dnsSeeds = new String[] { + "testnet-seed.bitcoin.petertodd.org", + "testnet-seed.bluematt.me" + }; + } + + private static TestNet3Params instance; + public static synchronized TestNet3Params get() { + if (instance == null) { + instance = new TestNet3Params(); + } + return instance; + } +} diff --git a/core/src/main/java/com/google/bitcoin/params/UnitTestParams.java b/core/src/main/java/com/google/bitcoin/params/UnitTestParams.java new file mode 100644 index 00000000..e9b68d91 --- /dev/null +++ b/core/src/main/java/com/google/bitcoin/params/UnitTestParams.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013 Google Inc. + * + * 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 com.google.bitcoin.params; + +import com.google.bitcoin.core.Block; +import com.google.bitcoin.core.NetworkParameters; + +import java.math.BigInteger; + +/** + * Network parameters used by the bitcoinj unit tests (and potentially your own). This lets you solve a block using + * {@link com.google.bitcoin.core.Block#solve()} by setting difficulty to the easiest possible. + */ +public class UnitTestParams extends NetworkParameters { + public UnitTestParams() { + super(); + id = ID_UNITTESTNET; + packetMagic = 0x0b110907; + addressHeader = 111; + proofOfWorkLimit = new BigInteger("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16); + genesisBlock.setTime(System.currentTimeMillis() / 1000); + genesisBlock.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET); + genesisBlock.solve(); + port = 18333; + interval = 10; + dumpedPrivateKeyHeader = 239; + targetTimespan = 200000000; // 6 years. Just a very big number. + spendableCoinbaseDepth = 5; + acceptableAddressCodes = new int[] { 111 }; + subsidyDecreaseBlockCount = 100; + dnsSeeds = null; + } + + private static UnitTestParams instance; + public static synchronized UnitTestParams get() { + if (instance == null) { + instance = new UnitTestParams(); + } + return instance; + } +} diff --git a/core/src/test/java/com/google/bitcoin/core/AlertMessageTest.java b/core/src/test/java/com/google/bitcoin/core/AlertMessageTest.java index 9a6ab352..2985dd71 100644 --- a/core/src/test/java/com/google/bitcoin/core/AlertMessageTest.java +++ b/core/src/test/java/com/google/bitcoin/core/AlertMessageTest.java @@ -16,6 +16,7 @@ package com.google.bitcoin.core; +import com.google.bitcoin.params.UnitTestParams; import org.junit.Before; import org.junit.Test; import org.spongycastle.util.encoders.Hex; @@ -29,9 +30,13 @@ public class AlertMessageTest { @Before public void setUp() throws Exception { - ECKey key = new ECKey(TEST_KEY_PRIV, null); - params = NetworkParameters.unitTests(); - params.alertSigningKey = key.getPubKey(); + final ECKey key = new ECKey(TEST_KEY_PRIV, null); + params = new UnitTestParams() { + @Override + public byte[] getAlertSigningKey() { + return key.getPubKey(); + } + }; } @Test diff --git a/core/src/test/java/com/google/bitcoin/core/BitcoindComparisonTool.java b/core/src/test/java/com/google/bitcoin/core/BitcoindComparisonTool.java index 52c46773..8ff1d11d 100644 --- a/core/src/test/java/com/google/bitcoin/core/BitcoindComparisonTool.java +++ b/core/src/test/java/com/google/bitcoin/core/BitcoindComparisonTool.java @@ -16,6 +16,7 @@ package com.google.bitcoin.core; +import com.google.bitcoin.params.RegTestParams; import com.google.bitcoin.store.BlockStoreException; import com.google.bitcoin.store.FullPrunedBlockStore; import com.google.bitcoin.store.H2FullPrunedBlockStore; @@ -58,38 +59,15 @@ public class BitcoindComparisonTool { System.out.println("USAGE: bitcoinjBlockStoreLocation runLargeReorgs(1/0) [port=18444]"); boolean runLargeReorgs = Integer.parseInt(args[1]) == 1; - params = NetworkParameters.testNet2(); - /** - * The following have been changed from the default and do not match bitcoind's default. - * In order for this test to work, bitcoind should be recompiled with the same values you see here. - * You can also opt to comment out these lines to use the default, however that will cause this tool to be - * very significantly less efficient and useful (it will likely run forever trying to mine new blocks). - * - * You could also simply use git apply to apply the test-patches included with bitcoind - */ - - // bnProofOfWorkLimit set in main.cpp - params.proofOfWorkLimit = new BigInteger("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16); - // Also set hashGenesisBlock to 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206 one line up - - // constant (210000) in GetBlockValue (main.cpp) - params.setSubsidyDecreaseBlockCount(150); - - // block.nNonce/block.nBits in LoadBlockIndex not the ones under "if (fTestNet)" - params.getGenesisBlock().setNonce(2); - params.getGenesisBlock().setDifficultyTarget(0x207fFFFFL); - // Also set block.nTime = 1296688602; in the same block - + params = new RegTestParams(); + File blockFile = File.createTempFile("testBlocks", ".dat"); blockFile.deleteOnExit(); - + FullBlockTestGenerator generator = new FullBlockTestGenerator(params); BlockAndValidityList blockList = generator.getBlocksToTest(true, runLargeReorgs, blockFile); Iterator blocks = new BlockFileLoader(params, Arrays.asList(blockFile)); - - // Only needs to be set in bitcoinj - params.allowEmptyPeerChains = true; - + try { store = new H2FullPrunedBlockStore(params, args[0], blockList.maximumReorgBlockCount); ((H2FullPrunedBlockStore)store).resetStore(); @@ -104,7 +82,7 @@ public class BitcoindComparisonTool { peers.setUserAgent("BlockAcceptanceComparisonTool", "1.0"); // bitcoind MUST be on localhost or we will get banned as a DoSer - peers.addAddress(new PeerAddress(InetAddress.getByName("localhost"), args.length > 2 ? Integer.parseInt(args[2]) : 18444)); + peers.addAddress(new PeerAddress(InetAddress.getByName("localhost"), args.length > 2 ? Integer.parseInt(args[2]) : params.getPort())); final Set blocksRequested = Collections.synchronizedSet(new HashSet()); peers.addEventListener(new AbstractPeerEventListener() { diff --git a/core/src/test/java/com/google/bitcoin/core/BlockChainTest.java b/core/src/test/java/com/google/bitcoin/core/BlockChainTest.java index 73c48bcb..a6a18d92 100644 --- a/core/src/test/java/com/google/bitcoin/core/BlockChainTest.java +++ b/core/src/test/java/com/google/bitcoin/core/BlockChainTest.java @@ -17,6 +17,7 @@ package com.google.bitcoin.core; import com.google.bitcoin.core.Wallet.BalanceType; +import com.google.bitcoin.params.TestNet2Params; import com.google.bitcoin.store.BlockStore; import com.google.bitcoin.store.MemoryBlockStore; import com.google.bitcoin.utils.BriefLogFormatter; @@ -35,7 +36,6 @@ import static org.junit.Assert.*; // Handling of chain splits/reorgs are in ChainSplitTests. public class BlockChainTest { - private static final NetworkParameters testNet = NetworkParameters.testNet2(); private BlockChain testNetChain; private Wallet wallet; @@ -46,6 +46,13 @@ public class BlockChainTest { private final StoredBlock[] block = new StoredBlock[1]; private Transaction coinbaseTransaction; + private static class TweakableTestNet2Params extends TestNet2Params { + public void setProofOfWorkLimit(BigInteger limit) { + proofOfWorkLimit = limit; + } + } + private static final TweakableTestNet2Params testNet = new TweakableTestNet2Params(); + private void resetBlockStore() { blockStore = new MemoryBlockStore(unitTestParams); } @@ -54,6 +61,7 @@ public class BlockChainTest { public void setUp() throws Exception { BriefLogFormatter.initVerbose(); testNetChain = new BlockChain(testNet, new Wallet(testNet), new MemoryBlockStore(testNet)); + unitTestParams = NetworkParameters.unitTests(); wallet = new Wallet(unitTestParams) { @Override @@ -161,7 +169,7 @@ public class BlockChainTest { // artificially shortened period. Block prev = unitTestParams.getGenesisBlock(); Block.fakeClock = System.currentTimeMillis() / 1000; - for (int i = 0; i < unitTestParams.interval - 1; i++) { + for (int i = 0; i < unitTestParams.getInterval() - 1; i++) { Block newBlock = prev.createNextBlock(coinbaseTo, Block.fakeClock); assertTrue(chain.add(newBlock)); prev = newBlock; @@ -209,9 +217,9 @@ public class BlockChainTest { } // Accept any level of difficulty now. - BigInteger oldVal = testNet.proofOfWorkLimit; - testNet.proofOfWorkLimit = new BigInteger - ("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16); + BigInteger oldVal = testNet.getProofOfWorkLimit(); + testNet.setProofOfWorkLimit(new BigInteger + ("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)); try { testNetChain.add(bad); // We should not get here as the difficulty target should not be changing at this point. @@ -219,7 +227,7 @@ public class BlockChainTest { } catch (VerificationException e) { assertTrue(e.getMessage(), e.getCause().getMessage().contains("Unexpected change in difficulty")); } - testNet.proofOfWorkLimit = oldVal; + testNet.setProofOfWorkLimit(oldVal); // TODO: Test difficulty change is not out of range when a transition period becomes valid. } diff --git a/core/src/test/java/com/google/bitcoin/core/FullPrunedBlockChainTest.java b/core/src/test/java/com/google/bitcoin/core/FullPrunedBlockChainTest.java index eb440082..d32c23ff 100644 --- a/core/src/test/java/com/google/bitcoin/core/FullPrunedBlockChainTest.java +++ b/core/src/test/java/com/google/bitcoin/core/FullPrunedBlockChainTest.java @@ -18,13 +18,13 @@ package com.google.bitcoin.core; import com.google.bitcoin.core.Transaction.SigHash; +import com.google.bitcoin.params.UnitTestParams; import com.google.bitcoin.script.Script; import com.google.bitcoin.store.BlockStoreException; import com.google.bitcoin.store.FullPrunedBlockStore; import com.google.bitcoin.store.MemoryFullPrunedBlockStore; import com.google.bitcoin.utils.BlockFileLoader; import com.google.bitcoin.utils.BriefLogFormatter; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; @@ -45,33 +45,28 @@ import static org.junit.Assert.*; public class FullPrunedBlockChainTest { private static final Logger log = LoggerFactory.getLogger(FullPrunedBlockChainTest.class); - private NetworkParameters unitTestParams; + private NetworkParameters params; private FullPrunedBlockChain chain; private FullPrunedBlockStore store; - private int oldInterval; - @Before public void setUp() throws Exception { BriefLogFormatter.init(); - unitTestParams = NetworkParameters.unitTests(); - oldInterval = unitTestParams.interval; - unitTestParams.interval = 10000; + params = new UnitTestParams() { + @Override public int getInterval() { + return 10000; + } + }; } - @After - public void tearDown() { - unitTestParams.interval = oldInterval; - } - @Test public void testGeneratedChain() throws Exception { - // Tests various test cases from FullBlockTestGenerator - FullBlockTestGenerator generator = new FullBlockTestGenerator(unitTestParams); + // Tests various test cases from FullBlockTestGenerator + FullBlockTestGenerator generator = new FullBlockTestGenerator(params); BlockAndValidityList blockList = generator.getBlocksToTest(false, false, null); - store = new MemoryFullPrunedBlockStore(unitTestParams, blockList.maximumReorgBlockCount); - chain = new FullPrunedBlockChain(unitTestParams, store); + store = new MemoryFullPrunedBlockStore(params, blockList.maximumReorgBlockCount); + chain = new FullPrunedBlockChain(params, store); for (BlockAndValidity block : blockList.list) { boolean threw = false; @@ -109,8 +104,8 @@ public class FullPrunedBlockChainTest { @Test public void testFinalizedBlocks() throws Exception { final int UNDOABLE_BLOCKS_STORED = 10; - store = new MemoryFullPrunedBlockStore(unitTestParams, UNDOABLE_BLOCKS_STORED); - chain = new FullPrunedBlockChain(unitTestParams, store); + store = new MemoryFullPrunedBlockStore(params, UNDOABLE_BLOCKS_STORED); + chain = new FullPrunedBlockChain(params, store); // Check that we aren't accidentally leaving any references // to the full StoredUndoableBlock's lying around (ie memory leaks) @@ -118,11 +113,11 @@ public class FullPrunedBlockChainTest { ECKey outKey = new ECKey(); // Build some blocks on genesis block to create a spendable output - Block rollingBlock = unitTestParams.getGenesisBlock().createNextBlockWithCoinbase(outKey.getPubKey()); + Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(outKey.getPubKey()); chain.add(rollingBlock); - TransactionOutPoint spendableOutput = new TransactionOutPoint(unitTestParams, 0, rollingBlock.getTransactions().get(0).getHash()); + TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, rollingBlock.getTransactions().get(0).getHash()); byte[] spendableOutputScriptPubKey = rollingBlock.getTransactions().get(0).getOutputs().get(0).getScriptBytes(); - for (int i = 1; i < unitTestParams.getSpendableCoinbaseDepth(); i++) { + for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) { rollingBlock = rollingBlock.createNextBlockWithCoinbase(outKey.getPubKey()); chain.add(rollingBlock); } @@ -131,9 +126,9 @@ public class FullPrunedBlockChainTest { (store.getTransactionOutput(spendableOutput.getHash(), spendableOutput.getIndex())); rollingBlock = rollingBlock.createNextBlock(null); - Transaction t = new Transaction(unitTestParams); + Transaction t = new Transaction(params); // Entirely invalid scriptPubKey - t.addOutput(new TransactionOutput(unitTestParams, t, Utils.toNanoCoins(50, 0), new byte[] {})); + t.addOutput(new TransactionOutput(params, t, Utils.toNanoCoins(50, 0), new byte[] {})); addInputToTransaction(t, spendableOutput, spendableOutputScriptPubKey, outKey); rollingBlock.addTransaction(t); rollingBlock.solve(); @@ -161,7 +156,7 @@ public class FullPrunedBlockChainTest { } private void addInputToTransaction(Transaction t, TransactionOutPoint prevOut, byte[] prevOutScriptPubKey, ECKey sigKey) throws ScriptException { - TransactionInput input = new TransactionInput(unitTestParams, t, new byte[]{}, prevOut); + TransactionInput input = new TransactionInput(params, t, new byte[]{}, prevOut); t.addInput(input); Sha256Hash hash = t.hashTransactionForSignature(0, prevOutScriptPubKey, SigHash.ALL, false); diff --git a/tools/src/main/java/com/google/bitcoin/tools/BuildCheckpoints.java b/tools/src/main/java/com/google/bitcoin/tools/BuildCheckpoints.java index 7524af68..1e65d52e 100644 --- a/tools/src/main/java/com/google/bitcoin/tools/BuildCheckpoints.java +++ b/tools/src/main/java/com/google/bitcoin/tools/BuildCheckpoints.java @@ -3,7 +3,6 @@ package com.google.bitcoin.tools; import com.google.bitcoin.core.*; import com.google.bitcoin.store.BlockStore; import com.google.bitcoin.store.MemoryBlockStore; -import com.google.bitcoin.store.SPVBlockStore; import com.google.bitcoin.utils.BriefLogFormatter; import java.io.*; @@ -43,7 +42,7 @@ public class BuildCheckpoints { @Override public void notifyNewBestBlock(StoredBlock block) throws VerificationException { int height = block.getHeight(); - if (height % params.interval == 0 && block.getHeader().getTimeSeconds() <= oneMonthAgo) { + if (height % params.getInterval() == 0 && block.getHeader().getTimeSeconds() <= oneMonthAgo) { System.out.println(String.format("Checkpointing block %s at height %d", block.getHeader().getHash(), block.getHeight())); checkpoints.put(height, block);