diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 1c947a4881..fc121b0fc5 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -38,6 +38,7 @@ BITCOIN_TESTS =\ test/compress_tests.cpp \ test/crypto_tests.cpp \ test/DoS_tests.cpp \ + test/genesis_tests.cpp \ test/getarg_tests.cpp \ test/hash_tests.cpp \ test/key_tests.cpp \ diff --git a/src/chainparams.cpp b/src/chainparams.cpp index c71f2da775..2602f06634 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -80,7 +80,7 @@ static const Checkpoints::CCheckpointData dataRegtest = { class CMainParams : public CChainParams { public: - CMainParams() { + CMainParams(CScript scriptDestination) { networkID = CBaseChainParams::MAIN; strNetworkID = "main"; /** @@ -127,7 +127,9 @@ public: genesis.hashMerkleRoot = genesis.BuildMerkleTree(); genesis.nVersion = 1; genesis.nTime = 1231006505; - CScript scriptDestination(CScript() << OP_5 << ParseHex("027d5d62861df77fc9a37dbe901a579d686d1423be5f56d6fc50bb9de3480871d1") << ParseHex("03b41ea6ba73b94c901fdd43e782aaf70016cc124b72a086e77f6e9f4f942ca9bb") << ParseHex("02be643c3350bade7c96f6f28d1750af2ef507bc1f08dd38f82749214ab90d9037") << ParseHex("021df31471281d4478df85bfce08a10aab82601dca949a79950f8ddf7002bd915a") << ParseHex("0320ea4fcf77b63e89094e681a5bd50355900bf961c10c9c82876cb3238979c0ed") << ParseHex("021c4c92c8380659eb567b497b936b274424662909e1ffebc603672ed8433f4aa1") << ParseHex("027841250cfadc06c603da8bc58f6cd91e62f369826c8718eb6bd114601dd0c5ac") << OP_7 << OP_CHECKMULTISIG); + if (scriptDestination.empty()) { + scriptDestination = CScript() << OP_5 << ParseHex("027d5d62861df77fc9a37dbe901a579d686d1423be5f56d6fc50bb9de3480871d1") << ParseHex("03b41ea6ba73b94c901fdd43e782aaf70016cc124b72a086e77f6e9f4f942ca9bb") << ParseHex("02be643c3350bade7c96f6f28d1750af2ef507bc1f08dd38f82749214ab90d9037") << ParseHex("021df31471281d4478df85bfce08a10aab82601dca949a79950f8ddf7002bd915a") << ParseHex("0320ea4fcf77b63e89094e681a5bd50355900bf961c10c9c82876cb3238979c0ed") << ParseHex("021c4c92c8380659eb567b497b936b274424662909e1ffebc603672ed8433f4aa1") << ParseHex("027841250cfadc06c603da8bc58f6cd91e62f369826c8718eb6bd114601dd0c5ac") << OP_7 << OP_CHECKMULTISIG; + } genesis.proof = CProof(scriptDestination, CScript()); // genesis block gets a PoW pass hashGenesisBlock = genesis.GetHash(); @@ -163,14 +165,13 @@ public: return data; } }; -static CMainParams mainParams; /** * Testnet (v3) */ class CTestNetParams : public CMainParams { public: - CTestNetParams() { + CTestNetParams(CScript scriptDestination) : CMainParams(scriptDestination) { networkID = CBaseChainParams::TESTNET; strNetworkID = "test"; pchMessageStart[0] = 0xee; @@ -217,14 +218,13 @@ public: return dataTestnet; } }; -static CTestNetParams testNetParams; /** * Regression test */ class CRegTestParams : public CTestNetParams { public: - CRegTestParams() { + CRegTestParams(CScript scriptDestination) : CTestNetParams(scriptDestination) { networkID = CBaseChainParams::REGTEST; strNetworkID = "regtest"; pchMessageStart[0] = 0xfa; @@ -249,7 +249,9 @@ public: txNew.vout[1].scriptPubKey = CScript() << OP_TRUE; genesis.vtx[0] = CTransaction(txNew); - CScript scriptDestination(CScript() << OP_TRUE); + if (scriptDestination.empty()) { + scriptDestination = CScript() << OP_TRUE; + } genesis.proof = CProof(scriptDestination, CScript()); // genesis block gets a PoW pass genesis.hashMerkleRoot = genesis.BuildMerkleTree(); @@ -272,14 +274,13 @@ public: return dataRegtest; } }; -static CRegTestParams regTestParams; /** * Unit test */ class CUnitTestParams : public CMainParams, public CModifiableParams { public: - CUnitTestParams() { + CUnitTestParams(CScript scriptDestination) : CMainParams(scriptDestination) { networkID = CBaseChainParams::UNITTEST; strNetworkID = "unittest"; nDefaultPort = 18445; @@ -308,50 +309,47 @@ public: virtual void setAllowMinDifficultyBlocks(bool afAllowMinDifficultyBlocks) { fAllowMinDifficultyBlocks=afAllowMinDifficultyBlocks; } virtual void setSkipProofOfWorkCheck(bool afSkipProofOfWorkCheck) { fSkipProofOfWorkCheck = afSkipProofOfWorkCheck; } }; -static CUnitTestParams unitTestParams; - -static CChainParams *pCurrentParams = 0; - -CModifiableParams *ModifiableParams() -{ - assert(pCurrentParams); - assert(pCurrentParams==&unitTestParams); - return (CModifiableParams*)&unitTestParams; -} +static CChainParams *pCurrentParams; const CChainParams &Params() { assert(pCurrentParams); return *pCurrentParams; } -CChainParams &Params(CBaseChainParams::Network network) { + +CChainParams* CChainParams::Factory(CBaseChainParams::Network network, CScript scriptDestination) { switch (network) { case CBaseChainParams::MAIN: - return mainParams; + return new CMainParams(scriptDestination); case CBaseChainParams::TESTNET: - return testNetParams; + return new CTestNetParams(scriptDestination); case CBaseChainParams::REGTEST: - return regTestParams; + return new CRegTestParams(scriptDestination); case CBaseChainParams::UNITTEST: - return unitTestParams; + return new CUnitTestParams(scriptDestination); default: assert(false && "Unimplemented network"); - return mainParams; + return NULL; } } -void SelectParams(CBaseChainParams::Network network) { +void SelectParams(CBaseChainParams::Network network, CScript scriptDestination) { SelectBaseParams(network); - pCurrentParams = &Params(network); + pCurrentParams = CChainParams::Factory(network, scriptDestination); +} + +void SelectParams(CBaseChainParams::Network network) { + SelectParams(network, CScript()); } bool SelectParamsFromCommandLine() { CBaseChainParams::Network network = NetworkIdFromCommandLine(); + CScript scriptDestination = ScriptDestinationFromCommandLine(); if (network == CBaseChainParams::MAX_NETWORK_TYPES) return false; - SelectParams(network); + SelectParams(network, scriptDestination); return true; } diff --git a/src/chainparams.h b/src/chainparams.h index 6a79edaa8d..553fd6ea94 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -80,6 +80,12 @@ public: const std::vector& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } const std::vector& FixedSeeds() const { return vFixedSeeds; } virtual const Checkpoints::CCheckpointData& Checkpoints() const = 0; + /** + * Creates and returns a CChainParams* of the chosen chain. The caller has to delete the object. + * @returns a CChainParams* of the chosen chain. + * @throws a std::runtime_error if the chain is not supported. + */ + static CChainParams* Factory(CBaseChainParams::Network network, CScript scriptDestination); protected: CChainParams() {} @@ -139,12 +145,14 @@ const CChainParams &Params(); /** Return parameters for the given network. */ CChainParams &Params(CBaseChainParams::Network network); -/** Get modifiable network parameters (UNITTEST only) */ -CModifiableParams *ModifiableParams(); - /** Sets the params returned by Params() to those for the given network. */ void SelectParams(CBaseChainParams::Network network); +/** + * Sets the params returned by Params() to those for the given network + * with given blocksigning pubkey */ +void SelectParams(CBaseChainParams::Network network, CScript scriptDestination); + /** * Looks for -regtest or -testnet and then calls SelectParams as appropriate. * Returns false if an invalid combination is given. diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index b3fd738fd7..afb00ebcd5 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -5,9 +5,12 @@ #include "chainparamsbase.h" +#include "script/script.h" #include "util.h" +#include "utilstrencodings.h" #include +#include #include @@ -114,6 +117,20 @@ CBaseChainParams::Network NetworkIdFromCommandLine() return CBaseChainParams::MAIN; } +CScript ScriptDestinationFromCommandLine() +{ + std::string sd = GetArg("-genesisscriptdestination", ""); + if (!sd.empty()) { + if (IsHex(sd)) { + std::vector sd_raw(ParseHex(sd)); + return CScript(sd_raw.begin(), sd_raw.end()); + } else { + fprintf(stderr, "Warning: Genesis script destination was not valid hex, ignoring it.\n"); + } + } + return CScript(); +} + bool SelectBaseParamsFromCommandLine() { CBaseChainParams::Network network = NetworkIdFromCommandLine(); diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index eaf3fea1b6..8db1ce0e49 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -8,6 +8,8 @@ #include #include +#include "script/script.h" + /** * CBaseChainParams defines the base parameters (shared between bitcoin-cli and bitcoind) * of a given instance of the Bitcoin system. @@ -50,6 +52,12 @@ void SelectBaseParams(CBaseChainParams::Network network); */ CBaseChainParams::Network NetworkIdFromCommandLine(); +/** + * Looks for hex-encoded -genesisscriptdistenation and returns a CScript of it. + * Returns an empty script if the flag is missing or badly encoded. + */ +CScript ScriptDestinationFromCommandLine(); + /** * Calls NetworkIdFromCommandLine() and then calls SelectParams as appropriate. * Returns false if an invalid combination is given. diff --git a/src/init.cpp b/src/init.cpp index 546349ef81..0f9d80a6cb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -346,6 +346,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += " -gen " + strprintf(_("Generate coins (default: %u)"), 0) + "\n"; strUsage += " -genproclimit= " + strprintf(_("Set the number of threads for coin generation if enabled (-1 = all cores, default: %d)"), 1) + "\n"; #endif + strUsage += " -genesisscriptdestination " + _("Set the scriptPubKey used when signing blocks. This is intended for testing, " + "as changing it forks the node off of the network") + "\n"; strUsage += " -help-debug " + _("Show all debugging options (usage: --help -help-debug)") + "\n"; strUsage += " -logips " + strprintf(_("Include IP addresses in debug output (default: %u)"), 0) + "\n"; strUsage += " -logtimestamps " + strprintf(_("Prepend debug output with timestamp (default: %u)"), 1) + "\n"; diff --git a/src/main.cpp b/src/main.cpp index b295839d62..c3c0dd8cd0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1288,7 +1288,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) +bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos) { // Open history file to append CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); @@ -2215,7 +2215,7 @@ static int64_t nTimeProofTxn = 0; * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. */ -bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) { +bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, const CBlock *pblock) { assert(pindexNew->pprev == chainActive.Tip()); mempool.check(pcoinsTip); // Read block from disk. @@ -2354,7 +2354,7 @@ static void PruneBlockIndexCandidates() { * Try to make some progress towards making pindexMostWork the active block. * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. */ -static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, CBlock *pblock) { +static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, const CBlock *pblock) { AssertLockHeld(cs_main); bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); @@ -2423,7 +2423,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo * or an activated best chain. pblock is either NULL or a pointer to a block * that is already loaded (to avoid loading it again from disk). */ -bool ActivateBestChain(CValidationState &state, CBlock *pblock) { +bool ActivateBestChain(CValidationState &state, const CBlock *pblock) { CBlockIndex *pindexNewTip = NULL; CBlockIndex *pindexMostWork = NULL; do { @@ -3359,7 +3359,7 @@ bool InitBlockIndex() { // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) if (!fReindex) { try { - CBlock &block = const_cast(Params().GenesisBlock()); + const CBlock &block = Params().GenesisBlock(); // Start new block file unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); CDiskBlockPos blockPos; diff --git a/src/main.h b/src/main.h index 167debffce..b60963e185 100644 --- a/src/main.h +++ b/src/main.h @@ -196,7 +196,7 @@ std::string GetWarnings(std::string strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); /** Find the best known block, and make it the tip of the block chain */ -bool ActivateBestChain(CValidationState &state, CBlock *pblock = NULL); +bool ActivateBestChain(CValidationState &state, const CBlock *pblock = NULL); CAmount GetBlockValue(int nHeight, const CAmount& nFees); /** Create a new block index entry for a given block hash */ @@ -390,7 +390,7 @@ public: /** Functions for disk access for blocks */ -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos); +bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos); bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos); bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); diff --git a/src/test/genesis_tests.cpp b/src/test/genesis_tests.cpp new file mode 100644 index 0000000000..e33f923760 --- /dev/null +++ b/src/test/genesis_tests.cpp @@ -0,0 +1,87 @@ +// Copyright (c) 2011-2014 The Bitcoin Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "chainparams.h" +#include "uint256.h" +#include "utilstrencodings.h" + +#include + +BOOST_AUTO_TEST_SUITE(genesis_tests) + +// Goal: check that the standard mainnet genesis block is computed correctly +BOOST_AUTO_TEST_CASE(genesis_mainnet) +{ + SelectParams(CBaseChainParams::MAIN); + const CChainParams *params = &Params(); + BOOST_CHECK_EQUAL(params->HashGenesisBlock().GetHex(), + params->GenesisBlock().GetHash().GetHex()); + BOOST_CHECK_EQUAL(params->HashGenesisBlock().GetHex(), + "b811a5eeaf27432278c032a0b520f829be2b92fafff9789efe2755fff8ef547b"); + // Check that unittest genesis hash is same as the mainnet one + const CChainParams *params2 = &Params(); + BOOST_CHECK_EQUAL(params2->HashGenesisBlock().GetHex(), + params2->GenesisBlock().GetHash().GetHex()); + BOOST_CHECK_EQUAL(params2->HashGenesisBlock().GetHex(), + "b811a5eeaf27432278c032a0b520f829be2b92fafff9789efe2755fff8ef547b"); +} + +// Goal: check that the standard testnet genesis block is computed correctly +BOOST_AUTO_TEST_CASE(genesis_testnet) +{ + SelectParams(CBaseChainParams::TESTNET); + const CChainParams *params = &Params(); + BOOST_CHECK_EQUAL(params->HashGenesisBlock().GetHex(), + params->GenesisBlock().GetHash().GetHex()); + BOOST_CHECK_EQUAL(params->HashGenesisBlock().GetHex(), + "f7f0ca371b1003dc7346bab766b4a131f9e3a5d68820a364d70921cb15b95eaa"); + // Return params to base case for other tests + SelectParams(CBaseChainParams::MAIN); +} + +// Goal: check that the default regtest genesis block is computed correctly +BOOST_AUTO_TEST_CASE(genesis_regtest) +{ + SelectParams(CBaseChainParams::REGTEST); + const CChainParams *params = &Params(); + BOOST_CHECK_EQUAL(params->HashGenesisBlock().GetHex(), + params->GenesisBlock().GetHash().GetHex()); + BOOST_CHECK_EQUAL(params->HashGenesisBlock().GetHex(), + "b41d03dc310957765223df2bf2f4b3609c79b8b6ac0a0764b20754f972d48b6c"); + // Return params to base case for other tests + SelectParams(CBaseChainParams::MAIN); +} + +// Goal: check that the replacing the signing script pubkey works correctly +BOOST_AUTO_TEST_CASE(genesis_customscript) +{ + CScript unsignable = CScript() << OP_FALSE; + + SelectParams(CBaseChainParams::MAIN, unsignable); + const CChainParams *params1 = &Params(); + BOOST_CHECK_EQUAL(params1->HashGenesisBlock().GetHex(), + params1->GenesisBlock().GetHash().GetHex()); + BOOST_CHECK_EQUAL(params1->HashGenesisBlock().GetHex(), + "2ec431e3858cef95882cd7d627599fd757afa81938f0b886bee691a48b1e9b58"); + + SelectParams(CBaseChainParams::TESTNET, unsignable); + const CChainParams *params2 = &Params(); + BOOST_CHECK_EQUAL(params2->HashGenesisBlock().GetHex(), + params2->GenesisBlock().GetHash().GetHex()); + BOOST_CHECK_EQUAL(params2->HashGenesisBlock().GetHex(), + "e28e10e75c6f55edfdb914831b8d48c79fd858b6020963a038ee97dcaae2b355"); + + SelectParams(CBaseChainParams::REGTEST, unsignable); + const CChainParams *params3 = &Params(); + BOOST_CHECK_EQUAL(params3->HashGenesisBlock().GetHex(), + params3->GenesisBlock().GetHash().GetHex()); + BOOST_CHECK_EQUAL(params3->HashGenesisBlock().GetHex(), + "a99a962c80d71fe1eb0b30217a6f14bc571e3eee97bc16663cf42d9e22eb6cb2"); + + // Return params to base case for other tests + SelectParams(CBaseChainParams::MAIN); +} + +BOOST_AUTO_TEST_SUITE_END() +