diff --git a/TODO b/TODO new file mode 100644 index 00000000..b5793983 --- /dev/null +++ b/TODO @@ -0,0 +1,14 @@ +* upgrade to new version at fixed height: + - P2SH as enforced soft fork (BIP16) + - strict BIP30 as enforced soft fork + - allow larger blocks (DB locks) + - no need to disallow auxpow parent blocks with auxpow-flag in the version + any more; instead, the auxpow is simply never loaded for them + - disallow legacy blocks also on testnet + - restrict auxpow size / coinbase tx size? + +* reenable some of the disabled tests, new alert keys? + +* make dust spam unspendable? + +* simplify regtests with reduced number of nodes? diff --git a/contrib/auxpow-sizes/auxpow-sizes.py b/contrib/auxpow-sizes/auxpow-sizes.py new file mode 100755 index 00000000..1b54e8fd --- /dev/null +++ b/contrib/auxpow-sizes/auxpow-sizes.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python + +# Auxpow Sizes - find sizes of auxpow's in the blockchain +# Copyright (C) 2015 Daniel Kraft +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import jsonrpc +import sys +import urllib + +username = urllib.quote_plus ("devcoin") +password = urllib.quote_plus ("password") +port = 8336 +url = "http://%s:%s@localhost:%d/" % (username, password, port) + +class AuxpowStats: + """ + Keep track of the interesting statistics of the auxpows + found in the blockchain. + """ + + def __init__ (self): + self.merkleLength = dict () + self.txSize = dict () + self.maxMerkle = 0 + self.maxTxSize = 0 + + def add (self, obj): + """ + Add the auxpow described by the block JSON obj (if any) + to the statistics. + """ + + if 'auxpow' not in obj: + return + + txSize = len (obj['auxpow']['tx']['hex']) / 2 + merkleLen = len (obj['auxpow']['merklebranch']) + + if txSize not in self.txSize: + self.txSize[txSize] = 1 + else: + self.txSize[txSize] += 1 + + if merkleLen not in self.merkleLength: + self.merkleLength[merkleLen] = 1 + else: + self.merkleLength[merkleLen] += 1 + + if txSize > self.maxTxSize: + self.maxTxSize = txSize + self.maxTxSizeHash = obj['hash'] + if merkleLen > self.maxMerkle: + self.maxMerkle = merkleLen + self.maxMerkleHash = obj['hash'] + + def output (self): + """ + Output statistics in the end. + """ + + print "Merkle lengths:" + for (key, val) in self.merkleLength.items (): + print "%4d: %6d" % (key, val) + print "Maximum: %d, block %s\n" % (self.maxMerkle, self.maxMerkleHash) + + print "\nCoinbase tx sizes:" + buckets = [0, 1000, 2000, 5000, 10000, 20000, 50000] + bucketCnts = (len (buckets) + 1) * [0] + for (key, val) in self.txSize.items (): + for i in range (len (buckets) - 1, -1, -1): + if (key >= buckets[i]): + bucketCnts[i] += val + for i in range (len (buckets) - 1): + label = "%d - %d" % (buckets[i], buckets[i + 1] - 1) + print " %15s: %6d" % (label, bucketCnts[i]) + label = ">= %d" % buckets[-1] + print " %15s: %6d" % (label, bucketCnts[-1]) + print "Maximum: %d, block %s\n" % (self.maxTxSize, self.maxTxSizeHash) + +rpc = jsonrpc.proxy.ServiceProxy (url) +tips = rpc.getchaintips () +tip = None +for t in tips: + if t['status'] == 'active': + tip = t + break +assert tip is not None + +stats = AuxpowStats () +curHash = tip['hash'] +while True: + obj = rpc.getblock (curHash) + stats.add (obj) + if obj['height'] % 1000 == 0: + sys.stderr.write ("At height %d...\n" % obj['height']) + if 'previousblockhash' not in obj: + break + curHash = obj['previousblockhash'] +stats.output () diff --git a/contrib/auxpow/getwork-wrapper.py b/contrib/auxpow/getwork-wrapper.py new file mode 100755 index 00000000..50a01503 --- /dev/null +++ b/contrib/auxpow/getwork-wrapper.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# Copyright (c) 2018 Daniel Kraft +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# This is a simple wrapper around the merge-mining interface of the core +# daemon (createauxblock and submitauxblock). It handles the creation of +# a "fake" auxpow, providing an external miner with a getwork-like interface. +# +# Since using this loses the ability to *actually* merge mine with a parent +# chain, this is likely not very useful in production. But it can be used +# for testing and debugging. +# +# This needs jsonrpclib, which can be found in the 'python-jsonrpclib' Debian +# package or at https://github.com/joshmarshall/jsonrpclib. It also imports +# auxpow from test/functional/test_framework. + +import codecs +import optparse +import struct +from xmlrpclib import ProtocolError + +import jsonrpclib +from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer + +import auxpow + + +class GetworkWrapper: + """ + The main server class. It sets up a JSON-RPC server and handles requests + coming in. + """ + + def __init__ (self, backend, host, port): + self.backend = backend + self.server = SimpleJSONRPCServer ((host, port)) + + def getwork (data=None): + if data is None: + return self.createWork () + return self.submitWork (data) + self.server.register_function (getwork) + + # We use our own extra nonce to not return the same work twice if + # asked again for new work. + self.extraNonce = 0 + + # Dictionary that holds all created works so they can be retrieved when + # necessary for matching. The key is the (byte-order swapped) block merkle + # hash, which is not changed by the miner when processing the work. + # This is cleared only once a submitted block was accepted. We do not try + # to detect if the chain tip changes externally. + self.works = {} + + def keyForWork (self, data): + """ + Returns the key used in self.works for the given, hex-encoded and + byte-swapped, getwork 'data'. + """ + + return data[2*36 : 2*68] + + def createWork (self): + auxblock = self.backend.getauxblock () + (tx, hdr) = auxpow.constructAuxpow (auxblock['hash']) + + en = self.extraNonce + self.extraNonce = (self.extraNonce + 1) % (1 << 32) + + hdrBytes = bytearray (codecs.decode (hdr, 'hex_codec')) + hdrBytes[0:4] = struct.pack (' 0) { - if(nChainIDParent == params.nAuxpowChainId) - return error("Aux POW parent has our chain ID"); - } else { - const int32_t &nOldChainIDParent = parentBlock.GetOldChainId(); - if(nOldChainIDParent == params.nAuxpowOldChainId) - return error("Aux POW parent has our old chain ID"); - } + if (parentBlock.GetChainId () == nChainId) + return error("Aux POW parent has our chain ID"); + else if(parentBlock.GetOldChainId() == params.nAuxpowOldChainId) + return error("Aux POW parent has our old chain ID"); } if (vChainMerkleBranch.size() > 30) return error("Aux POW chain merkle branch too long"); // Check that the chain merkle root is in the coinbase - const uint256 &nRootHash + const uint256 nRootHash = CheckMerkleBranch (hashAuxBlock, vChainMerkleBranch, nChainIndex); valtype vchRootHash(nRootHash.begin (), nRootHash.end ()); std::reverse (vchRootHash.begin (), vchRootHash.end ()); // correct endian @@ -71,7 +66,7 @@ CAuxPow::check (const uint256& hashAuxBlock, int nChainId, if (coinbaseTx->vin.empty()) return error("Aux POW coinbase has no inputs"); - const CScript &script = coinbaseTx->vin[0].scriptSig; + const CScript script = coinbaseTx->vin[0].scriptSig; // Check that the same work is not submitted twice to our chain. // @@ -114,12 +109,12 @@ CAuxPow::check (const uint256& hashAuxBlock, int nChainId, if (script.end() - pc < 8) return error("Aux POW missing chain merkle tree size and nonce in parent coinbase"); - const uint32_t &nSize = DecodeLE32 (&pc[0]); - const unsigned &merkleHeight = vChainMerkleBranch.size (); + const uint32_t nSize = DecodeLE32 (&pc[0]); + const unsigned merkleHeight = vChainMerkleBranch.size (); if (nSize != (1u << merkleHeight)) return error("Aux POW merkle branch size does not match parent coinbase"); - const uint32_t &nNonce = DecodeLE32 (&pc[4]); + const uint32_t nNonce = DecodeLE32 (&pc[4]); if (nChainIndex != getExpectedIndex (nNonce, nChainId, merkleHeight)) return error("Aux POW wrong index"); @@ -127,8 +122,8 @@ CAuxPow::check (const uint256& hashAuxBlock, int nChainId, } int -CAuxPow::getExpectedIndex (const uint32_t &nNonce, const int &nChainId, - const unsigned &h) +CAuxPow::getExpectedIndex (const uint32_t nNonce, const int nChainId, + const unsigned h) { // Choose a pseudo-random slot in the chain merkle tree // but have it be fixed for a size/nonce/chain combination. diff --git a/src/auxpow.h b/src/auxpow.h index 7090219e..df842886 100644 --- a/src/auxpow.h +++ b/src/auxpow.h @@ -20,8 +20,10 @@ class CBlock; class CBlockHeader; class CBlockIndex; +class CChainState; +class CValidationState; class UniValue; -class ChainstateManager; + namespace auxpow_tests { class CAuxPowForTest; @@ -30,8 +32,6 @@ class CAuxPowForTest; /** Header for merge-mining data in the coinbase. */ static const unsigned char pchMergedMiningHeader[] = { 0xfa, 0xbe, 'm', 'm' }; -typedef std::vector valtype; - /** * Data for the merge-mining auxpow. This uses a merkle tx (the parent block's * coinbase tx) and a second merkle branch to link the actual Devcoin block @@ -65,7 +65,8 @@ class CAuxPow const std::vector& vMerkleBranch, int nIndex); - friend UniValue AuxpowToJSON(const CAuxPow& auxpow, ChainstateManager& chainstate); + friend UniValue AuxpowToJSON(const CAuxPow& auxpow, bool verbose, + CChainState& active_chainstate); friend class auxpow_tests::CAuxPowForTest; public: @@ -83,8 +84,7 @@ class CAuxPow CAuxPow (const CAuxPow&) = delete; void operator= (const CAuxPow&) = delete; - - SERIALIZE_METHODS(CAuxPow, obj) + SERIALIZE_METHODS (CAuxPow, obj) { /* The coinbase Merkle tx' hashBlock field is never actually verified or used in the code for an auxpow (and never was). The parent block @@ -96,8 +96,11 @@ class CAuxPow /* The index of the parent coinbase tx is always zero. */ int nIndex = 0; - /* Data from the coinbase transaction READWRITE(obj.key);READWRITE(obj.key);as Merkle tx. */ - READWRITE (obj.coinbaseTx, hashBlock, obj.vMerkleBranch, nIndex, obj.vChainMerkleBranch, obj.nChainIndex, obj.parentBlock); + /* Data from the coinbase transaction as Merkle tx. */ + READWRITE (obj.coinbaseTx, hashBlock, obj.vMerkleBranch, nIndex); + + /* Additional data for the auxpow itself. */ + READWRITE (obj.vChainMerkleBranch, obj.nChainIndex, obj.parentBlock); } /** @@ -121,6 +124,18 @@ class CAuxPow return parentBlock.GetHash (); } + /** + * Return parent block. This is only used for the temporary parentblock + * auxpow version check. + * @return The parent block. + */ + /* FIXME: Remove after the hardfork. */ + inline const CPureBlockHeader& + getParentBlock () const + { + return parentBlock; + } + /** * Calculate the expected index in the merkle tree. This is also used * for the test-suite. @@ -129,7 +144,7 @@ class CAuxPow * @param h The merkle block height. * @return The expected index for the aux hash. */ - static int getExpectedIndex (const uint32_t &nNonce, const int &nChainId, const unsigned &h); + static int getExpectedIndex (uint32_t nNonce, int nChainId, unsigned h); /** * Constructs a minimal CAuxPow object for the given block header and diff --git a/src/chain.cpp b/src/chain.cpp index a420af72..c3f80dc2 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -4,7 +4,9 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include + #include + /* Moved here from the header, because we need auxpow and the logic becomes more involved. */ CBlockHeader CBlockIndex::GetBlockHeader(const Consensus::Params& consensusParams) const diff --git a/src/chainparams.cpp b/src/chainparams.cpp index f8aa9acc..e6292efd 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -65,7 +65,7 @@ class CMainParams : public CChainParams { consensus.signet_blocks = false; consensus.signet_challenge.clear(); consensus.nSubsidyHalvingInterval = 210000; - consensus.BIP16Exception = uint256{}; + consensus.BIP16Height = 1; consensus.BIP34Height = 23523; consensus.BIP34Hash = uint256S("0x0000000002fde67fbf51d8b3c1fe80b162b0ba5ac58b0afe87c4c94cbe958c58"); consensus.BIP65Height = 23523; // 0000000002fde67fbf51d8b3c1fe80b162b0ba5ac58b0afe87c4c94cbe958c58 @@ -94,10 +94,11 @@ class CMainParams : public CChainParams { consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000025018c6a1a353ba518e9753"); consensus.defaultAssumeValid = uint256S("0xe5f6dbecef5f5b8c49b8a6b7cefc80183dace19c474264b87f07edf55d347860"); // 496000 - // AuxPow - consensus.fStrictChainId = true; consensus.nAuxpowChainId = 4; consensus.nAuxpowOldChainId = 4096; + consensus.nAuxpowStartHeight = 0; + consensus.fStrictChainId = true; + consensus.nLegacyBlocksBefore = 19200; /** * The message start string is designed to be unlikely to occur in normal data. @@ -178,7 +179,7 @@ class CTestNetParams : public CChainParams { consensus.signet_blocks = false; consensus.signet_challenge.clear(); consensus.nSubsidyHalvingInterval = 210000; - consensus.BIP16Exception = uint256{}; + consensus.BIP16Height = 1; consensus.BIP34Height = 1; consensus.BIP34Hash = uint256{}; consensus.BIP65Height = 1; @@ -189,6 +190,7 @@ class CTestNetParams : public CChainParams { consensus.nPowTargetTimespan = 24 * 60 * 60; // one day consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; + consensus.nMinDifficultySince = 0; consensus.fPowNoRetargeting = false; consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains consensus.nMinerConfirmationWindow = 144; // nPowTargetTimespan / nPowTargetSpacing @@ -207,10 +209,11 @@ class CTestNetParams : public CChainParams { consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000141f25d13472"); // 838467 consensus.defaultAssumeValid = uint256S("0x0000003243223caf052c7e5e6710fae794dbdc10949a594550f073dbf5755bd4"); // 838467 - // AuxPow - consensus.fStrictChainId = false; + consensus.nAuxpowStartHeight = 0; consensus.nAuxpowChainId = 16; consensus.nAuxpowOldChainId = 4096; + consensus.fStrictChainId = false; + consensus.nLegacyBlocksBefore = -1; pchMessageStart[0] = 0x64; // d pchMessageStart[1] = 0x65; // e @@ -316,15 +319,9 @@ class SigNetParams : public CChainParams { strNetworkID = CBaseChainParams::SIGNET; consensus.signet_blocks = true; - - // AuxPow - consensus.fStrictChainId = false; - consensus.nAuxpowChainId = 8; - consensus.nAuxpowOldChainId = 4096; - consensus.signet_challenge.assign(bin.begin(), bin.end()); consensus.nSubsidyHalvingInterval = 210000; - consensus.BIP16Exception = uint256{}; + consensus.BIP16Height = 1; consensus.BIP34Height = 1; consensus.BIP34Hash = uint256{}; consensus.BIP65Height = 1; @@ -350,6 +347,12 @@ class SigNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + consensus.nAuxpowStartHeight = 0; + consensus.nAuxpowChainId = 8; + consensus.nAuxpowOldChainId = 4096; + consensus.fStrictChainId = true; + consensus.nLegacyBlocksBefore = 0; + // message start is defined as the first 4 bytes of the sha256d of the block script CHashWriter h(SER_DISK, 0); h << consensus.signet_challenge; @@ -392,21 +395,21 @@ class CRegTestParams : public CChainParams { consensus.signet_blocks = false; consensus.signet_challenge.clear(); consensus.nSubsidyHalvingInterval = 150; - consensus.BIP16Exception = uint256(); + consensus.BIP16Height = 1; consensus.BIP34Height = 500; // BIP34 activated on regtest (Used in functional tests) - consensus.BIP34Hash = uint256(); consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in functional tests) consensus.BIP66Height = 1251; // BIP66 activated on regtest (Used in functional tests) consensus.CSVHeight = 432; // CSV activated on regtest (Used in rpc activation tests) consensus.SegwitHeight = 0; // SEGWIT is always activated on regtest unless overridden + consensus.MinBIP9WarningHeight = 0; consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.nPowTargetTimespan = 24 * 60 * 60; // one day consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; + consensus.nMinDifficultySince = 0; consensus.fPowNoRetargeting = true; consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) - consensus.MinBIP9WarningHeight = 0; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; @@ -421,10 +424,11 @@ class CRegTestParams : public CChainParams { consensus.nMinimumChainWork = uint256{}; consensus.defaultAssumeValid = uint256{}; - // AuxPow - consensus.fStrictChainId = false; + consensus.nAuxpowStartHeight = 0; consensus.nAuxpowChainId = 8; consensus.nAuxpowOldChainId = 4096; + consensus.fStrictChainId = true; + consensus.nLegacyBlocksBefore = 0; pchMessageStart[0] = 0x64; // d pchMessageStart[1] = 0x65; // e @@ -496,6 +500,16 @@ class CRegTestParams : public CChainParams { void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args) { + if (args.IsArgSet("-bip16height")) { + int64_t height = args.GetArg("-bip16height", consensus.BIP16Height); + if (height < -1 || height >= std::numeric_limits::max()) { + throw std::runtime_error(strprintf("Activation height %ld for BIP16 is out of valid range. Use -1 to disable BIP16.", height)); + } else if (height == -1) { + LogPrintf("BIP16 disabled for testing\n"); + height = std::numeric_limits::max(); + } + consensus.BIP16Height = static_cast(height); + } if (args.IsArgSet("-segwitheight")) { int64_t height = args.GetArg("-segwitheight", consensus.SegwitHeight); if (height < -1 || height >= std::numeric_limits::max()) { diff --git a/src/consensus/params.h b/src/consensus/params.h index 786c742a..31ca109d 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -9,6 +9,8 @@ #include #include +#include + namespace Consensus { /** @@ -18,6 +20,7 @@ namespace Consensus { enum BuriedDeployment : int16_t { // buried deployments get negative values to avoid overlap with DeploymentPos DEPLOYMENT_HEIGHTINCB = std::numeric_limits::min(), + DEPLOYMENT_P2SH, DEPLOYMENT_CLTV, DEPLOYMENT_DERSIG, DEPLOYMENT_CSV, @@ -70,8 +73,8 @@ struct BIP9Deployment { struct Params { uint256 hashGenesisBlock; int nSubsidyHalvingInterval; - /* Block hash that is excepted from BIP16 enforcement */ - uint256 BIP16Exception; + /** Block height at with BIP16 becomes active */ + int BIP16Height; /** Block height and hash at which BIP34 becomes active */ int BIP34Height; uint256 BIP34Hash; @@ -99,6 +102,7 @@ struct Params { /** Proof of work parameters */ uint256 powLimit; bool fPowAllowMinDifficultyBlocks; + int64_t nMinDifficultySince; bool fPowNoRetargeting; int64_t nPowTargetSpacing; int64_t nPowTargetTimespan; @@ -107,12 +111,6 @@ struct Params { uint256 nMinimumChainWork; /** By default assume that the signatures in ancestors of this block are valid */ uint256 defaultAssumeValid; - /** Auxpow parameters */ - int32_t nAuxpowChainId; - int32_t nAuxpowOldChainId; - int nAuxpowStartHeight; - bool fStrictChainId; - int nLegacyBlocksBefore; // -1 for "always allow" /** * If true, witness commitments contain a payload equal to a Devcoin Script solution @@ -124,6 +122,8 @@ struct Params { int DeploymentHeight(BuriedDeployment dep) const { switch (dep) { + case DEPLOYMENT_P2SH: + return BIP16Height; case DEPLOYMENT_HEIGHTINCB: return BIP34Height; case DEPLOYMENT_CLTV: @@ -137,6 +137,39 @@ struct Params { } // no default case, so the compiler can warn about missing cases return std::numeric_limits::max(); } + + /** Auxpow parameters */ + int32_t nAuxpowChainId; + int32_t nAuxpowOldChainId; + int nAuxpowStartHeight; + bool fStrictChainId; + int nLegacyBlocksBefore; // -1 for "always allow" + + /** + * Check whether or not minimum difficulty blocks are allowed + * with the given time stamp. + * @param nBlockTime Time of the block with minimum difficulty. + * @return True if it is allowed to have minimum difficulty. + */ + bool AllowMinDifficultyBlocks(int64_t nBlockTime) const + { + if (!fPowAllowMinDifficultyBlocks) + return false; + return nBlockTime > nMinDifficultySince; + } + + /** + * Check whether or not to allow legacy blocks at the given height. + * @param nHeight Height of the block to check. + * @return True if it is allowed to have a legacy version. + */ + bool AllowLegacyBlocks(unsigned nHeight) const + { + if (nLegacyBlocksBefore < 0) + return true; + return static_cast (nHeight) < nLegacyBlocksBefore; + } + }; } // namespace Consensus diff --git a/src/deploymentinfo.cpp b/src/deploymentinfo.cpp index 1d6e1220..abff5a58 100644 --- a/src/deploymentinfo.cpp +++ b/src/deploymentinfo.cpp @@ -21,6 +21,8 @@ std::string DeploymentName(Consensus::BuriedDeployment dep) { assert(ValidDeployment(dep)); switch (dep) { + case Consensus::DEPLOYMENT_P2SH: + return "bip16"; case Consensus::DEPLOYMENT_HEIGHTINCB: return "bip34"; case Consensus::DEPLOYMENT_CLTV: diff --git a/src/miner.cpp b/src/miner.cpp index 544c72dc..499a511e 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -36,7 +36,7 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam pblock->nTime = nNewTime; // Updating time can change work required on testnet: - if (consensusParams.fPowAllowMinDifficultyBlocks) + if (consensusParams.AllowMinDifficultyBlocks(pblock->GetBlockTime())) pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams); return nNewTime - nOldTime; @@ -131,7 +131,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc // -regtest only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios if (chainparams.MineBlocksOnDemand()) - pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion); + pblock->SetBaseVersion(gArgs.GetArg("-blockversion", pblock->GetBaseVersion()), nChainId); pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); diff --git a/src/net.h b/src/net.h index b1fd22fb..fa2becc6 100644 --- a/src/net.h +++ b/src/net.h @@ -54,8 +54,14 @@ static const int TIMEOUT_INTERVAL = 20 * 60; static constexpr auto FEELER_INTERVAL = 2min; /** Run the extra block-relay-only connection loop once every 5 minutes. **/ static constexpr auto EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL = 5min; -/** Maximum length of incoming protocol messages (no message over 12 MB is currently acceptable). */ -static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 12 * 1000 * 1000; +/** + * Maximum length of incoming protocol messages (no message over 32 MiB is + * currently acceptable). Bitcoin has 4 MiB here, but we need more space + * to allow for 2,000 block headers with auxpow. + */ +/* FIXME: Once the headers size limit is deployed sufficiently in the network, + we may want to lower this again if it seems useful. */ +static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 32 * 1024 * 1024; /** Maximum length of the user agent string in `version` message */ static const unsigned int MAX_SUBVERSION_LENGTH = 256; /** Maximum number of automatic outgoing nodes over which we'll relay everything (blocks, tx, addrs, etc) */ diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 856659a5..a9eba6b3 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -98,8 +98,25 @@ static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16; /** Time during which a peer must stall block download progress before being disconnected. */ static constexpr auto BLOCK_STALLING_TIMEOUT = 2s; /** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends - * less than this number, we reached its tip. Changing this value is a protocol upgrade. */ + * less than this number, we reached its tip. Changing this value is a protocol upgrade. + * + * With a protocol upgrade, we now enforce an additional restriction on the + * total size of a "headers" message (see below). The absolute limit + * on the number of headers still applies as well, so that we do not get + * overloaded both with small and large headers. + */ static const unsigned int MAX_HEADERS_RESULTS = 2000; +// DEVCOIN +/** Maximum size of a "headers" message. This is enforced starting with + * SIZE_HEADERS_LIMIT_VERSION peers and prevents overloading if we have + * very large headers (due to auxpow). + */ +static const unsigned int MAX_HEADERS_SIZE = (6 << 20); // 6 MiB +/** Size of a headers message that is the threshold for assuming that the + * peer has more headers (even if we have less than MAX_HEADERS_RESULTS). + * This is used starting with SIZE_HEADERS_LIMIT_VERSION peers. + */ +static const unsigned int THRESHOLD_HEADERS_SIZE = (4 << 20); // 4 MiB /** Maximum depth of blocks we're willing to serve as compact blocks to peers * when requested. For older blocks, a regular BLOCK response will be sent. */ static const int MAX_CMPCTBLOCK_DEPTH = 5; @@ -162,13 +179,7 @@ static constexpr double MAX_ADDR_RATE_PER_SECOND{0.1}; * based increments won't go above this, but the MAX_ADDR_TO_SEND increment following GETADDR * is exempt from this limit. */ static constexpr size_t MAX_ADDR_PROCESSING_TOKEN_BUCKET{MAX_ADDR_TO_SEND}; -// DEVCOIN -static const unsigned int MAX_HEADERS_SIZE = (6 << 20); // 6 MiB -/** Size of a headers message that is the threshold for assuming that the - * peer has more headers (even if we have less than MAX_HEADERS_RESULTS). - * This is used starting with SIZE_HEADERS_LIMIT_VERSION peers. - */ -static const unsigned int THRESHOLD_HEADERS_SIZE = (4 << 20); // 4 MiB + // Internal stuff namespace { /** Blocks that are in flight, and that are in the queue to be downloaded. */ @@ -2013,6 +2024,17 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, return; } + size_t nSize = 0; + for (const auto& header : headers) { + nSize += GetSerializeSize(header, PROTOCOL_VERSION); + if (pfrom.nVersion >= SIZE_HEADERS_LIMIT_VERSION + && nSize > MAX_HEADERS_SIZE) { + LOCK(cs_main); + Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nSize)); + return; + } + } + bool received_new_header = false; const CBlockIndex *pindexLast = nullptr; { @@ -2089,7 +2111,14 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, nodestate->m_last_block_announcement = GetTime(); } - if (nCount == MAX_HEADERS_RESULTS) { + bool maxSize = (nCount == MAX_HEADERS_RESULTS); + if (pfrom.nVersion >= SIZE_HEADERS_LIMIT_VERSION + && nSize >= THRESHOLD_HEADERS_SIZE) + maxSize = true; + // FIXME: This change (with hasNewHeaders) is rolled back in Bitcoin, + // but I think it should stay here for merge-mined coins. Try to get + // it fixed again upstream and then update the fix. + if (maxSize && received_new_header) { // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or pindexBestHeader, continue // from there instead. @@ -3143,12 +3172,13 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, nSize += GetSerializeSize(header, PROTOCOL_VERSION); vHeaders.push_back(header); if (nCount >= MAX_HEADERS_RESULTS - || pindex->GetBlockHash() == hashStop) + || pindex->GetBlockHash() == hashStop) break; if (pfrom.nVersion >= SIZE_HEADERS_LIMIT_VERSION - && nSize >= THRESHOLD_HEADERS_SIZE) + && nSize >= THRESHOLD_HEADERS_SIZE) break; } + /* Check maximum headers size before pushing the message if the peer enforces it. This should not fail since we break above in the loop at the threshold and the threshold @@ -3160,8 +3190,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, else { LogPrint(BCLog::NET, "pushing %u headers, %u bytes\n", nCount, nSize); - // pindex can be nullptr either if we sent m_chainman.ActiveChain().Tip() OR - // if our peer has m_chainman.ActiveChain().Tip() (and thus we are sending an empty + // pindex can be nullptr either if we sent ::ChainActive().Tip() OR + // if our peer has ::ChainActive().Tip() (and thus we are sending an empty // headers message). In both cases it's safe to update // pindexBestHeaderSent to be our tip. // diff --git a/src/pow.cpp b/src/pow.cpp index f3f84ad5..b69800f2 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -10,7 +10,7 @@ #include #include -unsigned int GetNextWorkRequired_Original(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) +unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) { assert(pindexLast != nullptr); unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact(); @@ -18,8 +18,16 @@ unsigned int GetNextWorkRequired_Original(const CBlockIndex* pindexLast, const C // Only change once per difficulty adjustment interval if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0) { - if (params.fPowAllowMinDifficultyBlocks) + if (params.AllowMinDifficultyBlocks(pblock->GetBlockTime())) { + /* khal's port of this code from Bitcoin to the old namecoind + has a bug: Comparison of block times is done by an unsigned + difference. Consequently, the minimum difficulty is also + applied if the block's timestamp is earlier than the preceding + block's. Reproduce this. */ + if (pblock->GetBlockTime() < pindexLast->GetBlockTime()) + return nProofOfWorkLimit; + // Special difficulty rule for testnet: // If the new block's timestamp is more than 2* 10 minutes // then allow mining of a min-difficulty block. @@ -37,8 +45,15 @@ unsigned int GetNextWorkRequired_Original(const CBlockIndex* pindexLast, const C return pindexLast->nBits; } + /* Adapt the retargeting interval after merge-mining start + according to the changed Devcoin rules. */ + int nBlocksBack = params.DifficultyAdjustmentInterval() - 1; + if (pindexLast->nHeight >= params.nAuxpowStartHeight + && (pindexLast->nHeight + 1 > params.DifficultyAdjustmentInterval())) + nBlocksBack = params.DifficultyAdjustmentInterval(); + // Go back by what we want to be 14 days worth of blocks - int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1); + int nHeightFirst = pindexLast->nHeight - nBlocksBack; assert(nHeightFirst >= 0); const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst); assert(pindexFirst); @@ -48,17 +63,15 @@ unsigned int GetNextWorkRequired_Original(const CBlockIndex* pindexLast, const C unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params) { - int64_t nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks - if (params.fPowNoRetargeting) return pindexLast->nBits; // Limit adjustment step int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime; - if (nActualTimespan < nPowTargetTimespan/4) - nActualTimespan = nPowTargetTimespan/4; - if (nActualTimespan > nPowTargetTimespan*4) - nActualTimespan = nPowTargetTimespan*4; + if (nActualTimespan < params.nPowTargetTimespan/4) + nActualTimespan = params.nPowTargetTimespan/4; + if (nActualTimespan > params.nPowTargetTimespan*4) + nActualTimespan = params.nPowTargetTimespan*4; // Retarget const arith_uint256 bnPowLimit = UintToArith256(params.powLimit); @@ -183,7 +196,7 @@ unsigned int GetNextWorkRequired_Old(const CBlockIndex* pindexLast, const CBlock return bnNew.GetCompact(); } -unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) +unsigned int GetNextWorkRequired_New(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) { return GetNextWorkRequired_Old(pindexLast, pblock, params); } diff --git a/src/primitives/block.h b/src/primitives/block.h index bd6a892d..799600e2 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -23,6 +23,7 @@ class CBlockHeader : public CPureBlockHeader { public: + // auxpow (if this is a merge-minded block) std::shared_ptr auxpow; @@ -31,27 +32,18 @@ class CBlockHeader : public CPureBlockHeader SetNull(); } - template - void Serialize(Stream& s) const + SERIALIZE_METHODS(CBlockHeader, obj) { - s << *(CPureBlockHeader*)this; - if (this->IsAuxpow()) + READWRITEAS(CPureBlockHeader, obj); + + if (obj.IsAuxpow()) { - assert(auxpow != nullptr); - s << *auxpow; - } - } - template - void Unserialize(Stream& s) - { - s >> *(CPureBlockHeader*)this; - if (this->IsAuxpow()) + SER_READ(obj, obj.auxpow = std::make_shared()); + assert(obj.auxpow != nullptr); + READWRITE(*obj.auxpow); + } else { - auxpow = std::make_shared(); - assert(auxpow != nullptr); - s >> *auxpow; - } else { - auxpow.reset(); + SER_READ(obj, obj.auxpow.reset()); } } diff --git a/src/primitives/pureheader.cpp b/src/primitives/pureheader.cpp index ef0e37e4..563720a3 100644 --- a/src/primitives/pureheader.cpp +++ b/src/primitives/pureheader.cpp @@ -1,52 +1,26 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Syscoin and Devcoin Core developers +// Copyright (c) 2009-2014 The Bitcoin Core and Devcoin Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include + #include +#include + uint256 CPureBlockHeader::GetHash() const { return SerializeHash(*this); } -void CPureBlockHeader::SetBaseVersion(int32_t nBaseVersion, const int32_t &nChainId) +void CPureBlockHeader::SetBaseVersion(int32_t nBaseVersion, int32_t nChainId) { - const int32_t withoutTopMask = nBaseVersion & ~VERSIONAUXPOW_TOP_MASK; - assert(withoutTopMask >= 0 && withoutTopMask < VERSION_CHAIN_START); + assert(nBaseVersion >= 1 && nBaseVersion < VERSION_AUXPOW); assert(!IsAuxpow()); - nVersion = nBaseVersion | (nChainId << VERSION_START_BIT); -} - -void CPureBlockHeader::SetOldBaseVersion(int32_t nBaseVersion, const int32_t &nChainId) -{ - assert(nBaseVersion >= 1 && nBaseVersion < VERSION_CHAIN_START); - assert(!IsAuxpow()); - nVersion = nBaseVersion | (nChainId << VERSION_START_BIT); -} - -int32_t CPureBlockHeader::GetBaseVersion(const int32_t &ver) -{ - // remove AuxPow flag and Chain ID - return (ver & ~VERSION_AUXPOW) & ~VERSION_AUXPOW_CHAINID_SHIFTED; -} - -int32_t CPureBlockHeader::GetOldBaseVersion(const int32_t &ver) -{ - - return (ver & ~VERSION_AUXPOW) & ~VERSION_OLD_AUXPOW_CHAINID_SHIFTED; -} - -int32_t CPureBlockHeader::GetChainId(const int32_t &ver) -{ - // if auxpow is set then mask with Chain ID and shift back VERSION_START_BIT to get the real value - if((ver & VERSION_AUXPOW) > 0) - return (ver & MASK_AUXPOW_CHAINID_SHIFTED) >> VERSION_START_BIT; - else - return 0; + nVersion = nBaseVersion | (nChainId * VERSION_CHAIN_START); } -int32_t CPureBlockHeader::GetOldChainId(const int32_t &ver) +int32_t CPureBlockHeader::GetOldChainId(const int32_t &ver) { if((ver & VERSION_AUXPOW) > 0) return (ver & MASK_OLD_AUXPOW_CHAINID_SHIFTED) >> VERSION_START_BIT; diff --git a/src/primitives/pureheader.h b/src/primitives/pureheader.h index 3ed76152..4a221d65 100644 --- a/src/primitives/pureheader.h +++ b/src/primitives/pureheader.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2013 The Syscoin and Devcoin Core developers +// Copyright (c) 2009-2013 The Bitcoin Core and Devcoin Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -8,6 +8,7 @@ #include #include + /** * A block header without auxpow information. This "intermediate step" * in constructing the full header is useful, because it breaks the cyclic @@ -17,25 +18,16 @@ */ class CPureBlockHeader { -public: - static const uint32_t CHAINID = 4; // should match params.nAuxpowChainId - static const uint32_t OLD_CHAINID = 4096; // should match params.nAuxpowOldChainId private: - /* Mask for dummy bit and versionbits top mask. */ - static const int32_t VERSIONAUXPOW_TOP_MASK = (1 << 28) + (1 << 29) + (1 << 30); + /* Modifiers to the version. */ static const int32_t VERSION_AUXPOW = (1 << 8); static const uint8_t VERSION_START_BIT = 16; + /** Bits above are reserved for the auxpow chain ID. */ - static const int32_t VERSION_CHAIN_START = (1 << VERSION_START_BIT); - // mask to get Chain ID from version field, chainid is 16 in SYS so 0x001f mask should be OK - static const int32_t MASK_AUXPOW_CHAINID_SHIFTED = (0x001f << VERSION_START_BIT); // mask to get old Chain ID from version field, legacy one is 4096 (2^12) in SYS so 0x1fff mask is needed static const int32_t MASK_OLD_AUXPOW_CHAINID_SHIFTED = (0x1fff << VERSION_START_BIT); - // shifted Chain ID in version field - static const int32_t VERSION_AUXPOW_CHAINID_SHIFTED = (CHAINID << VERSION_START_BIT); - // shifted legaacy Chain ID, 4096 shifted by VERSION_START_BIT - static const int32_t VERSION_OLD_AUXPOW_CHAINID_SHIFTED = (OLD_CHAINID << VERSION_START_BIT); + static const int32_t VERSION_CHAIN_START = (1 << 16); public: // header @@ -89,16 +81,11 @@ class CPureBlockHeader { return GetBaseVersion(nVersion); } - inline int32_t GetOldBaseVersion() const + static inline int32_t GetBaseVersion(int32_t ver) { - return GetOldBaseVersion(nVersion); + return ver % VERSION_AUXPOW; } - static int32_t GetBaseVersion(const int32_t &ver); - static int32_t GetOldBaseVersion(const int32_t &ver); - static inline bool IsValidBaseVersion(const int32_t &nBaseVersion) { - return (nBaseVersion & ~VERSIONAUXPOW_TOP_MASK) < VERSION_CHAIN_START; - } /** * Set the base version (apart from chain ID and auxpow flag) to * the one given. This should only be called when auxpow is not yet @@ -106,24 +93,17 @@ class CPureBlockHeader * @param nBaseVersion The base version. * @param nChainId The auxpow chain ID. */ - void SetBaseVersion(int32_t nBaseVersion, const int32_t &nChainId); - /** - * Set the legacy base version (apart from chain ID and auxpow flag) to - * the one given. This should only be called when auxpow is not yet - * set, to initialise a block! - * @param nBaseVersion The base version. - * @param nChainId The auxpow chain ID. - */ - void SetOldBaseVersion(int32_t nBaseVersion, const int32_t &nChainId); + void SetBaseVersion(int32_t nBaseVersion, int32_t nChainId); + /** * Extract the chain ID. * @return The chain ID encoded in the version. */ inline int32_t GetChainId() const { - return GetChainId(nVersion); + return nVersion / VERSION_CHAIN_START; } - static int32_t GetChainId(const int32_t &ver); + /** * Extract the legacy chain ID. * @return The chain ID encoded in the version before versionbits were fixed to work with auxpow @@ -133,11 +113,11 @@ class CPureBlockHeader return GetOldChainId(nVersion); } static int32_t GetOldChainId(const int& ver); + /** * Set the chain ID. This is used for the test suite. - * @param chainId The chain ID to set. */ - inline void SetChainId(const int32_t &chainId) + inline void SetChainId(int32_t chainId) { nVersion %= VERSION_CHAIN_START; nVersion |= chainId * VERSION_CHAIN_START; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index f036b3aa..096302e4 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include