From 3eef83218d70aada938e20e12be5a246f76fca80 Mon Sep 17 00:00:00 2001 From: jc Date: Sat, 27 Jan 2018 07:45:49 -0500 Subject: [PATCH 01/12] begin integrating bip9 --- src/Makefile.am | 2 + src/chain.h | 2 - src/chainparams.cpp | 10 ++ src/consensus/params.h | 28 +++++ src/init.cpp | 2 +- src/main.cpp | 18 ++++ src/main.h | 21 ++-- src/miner.cpp | 6 ++ src/primitives/block.h | 2 +- src/versionbits.cpp | 229 +++++++++++++++++++++++++++++++++++++++++ src/versionbits.h | 80 ++++++++++++++ 11 files changed, 388 insertions(+), 12 deletions(-) create mode 100644 src/versionbits.cpp create mode 100644 src/versionbits.h diff --git a/src/Makefile.am b/src/Makefile.am index 82ea11da..b6913641 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -184,6 +184,7 @@ BITCOIN_CORE_H = \ utiltime.h \ validationinterface.h \ version.h \ + versionbits.h \ wallet/asyncrpcoperation_sendmany.h \ wallet/crypter.h \ wallet/db.h \ @@ -240,6 +241,7 @@ libbitcoin_server_a_SOURCES = \ txdb.cpp \ txmempool.cpp \ validationinterface.cpp \ + versionbits.cpp \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) diff --git a/src/chain.h b/src/chain.h index b7e8a917..66d95c3e 100644 --- a/src/chain.h +++ b/src/chain.h @@ -14,8 +14,6 @@ #include -#include - struct CDiskBlockPos { int nFile; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index fa0f24cf..6a8d5bbe 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -51,6 +51,9 @@ public: consensus.nPowTargetSpacing = 2.5 * 60; consensus.fPowAllowMinDifficultyBlocks = false; + consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016 + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + pchMessageStart[0] = 0xa8; pchMessageStart[1] = 0xea; pchMessageStart[2] = 0xb2; @@ -209,10 +212,14 @@ public: consensus.powLimit = uint256S("07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); consensus.fPowAllowMinDifficultyBlocks = true; + consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + pchMessageStart[0] = 0xf6; pchMessageStart[1] = 0x1b; pchMessageStart[2] = 0xf6; pchMessageStart[3] = 0xd6; + vAlertPubKey = ParseHex("048679fb891b15d0cada9692047fd0ae26ad8bfb83fabddbb50334ee5bc0683294deb410be20513c5af6e7b9cec717ade82b27080ee6ef9a245c36a795ab044bb3"); nDefaultPort = 17933; nPruneAfterHeight = 1000; @@ -300,6 +307,9 @@ public: assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down consensus.nPowMaxAdjustUp = 0; // Turn off adjustment up + consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains + consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) + pchMessageStart[0] = 0xaa; pchMessageStart[1] = 0xe8; pchMessageStart[2] = 0x3f; diff --git a/src/consensus/params.h b/src/consensus/params.h index 9987f2e7..067c9f5f 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -9,6 +9,24 @@ #include "uint256.h" namespace Consensus { + +enum DeploymentPos +{ + MAX_VERSION_BITS_DEPLOYMENTS = 0, +}; + +/** + * Struct for each individual consensus rule change using BIP9. + */ +struct BIP9Deployment { + /** Bit position to select the particular bit in nVersion. */ + int bit; + /** Start MedianTime for version bits miner confirmation. Can be a date in the past */ + int64_t nStartTime; + /** Timeout/expiry MedianTime for the deployment attempt. */ + int64_t nTimeout; +}; + /** * Parameters that influence chain consensus. */ @@ -40,6 +58,16 @@ struct Params { int nMajorityEnforceBlockUpgrade; int nMajorityRejectBlockOutdated; int nMajorityWindow; + + /** + * Minimum blocks including miner confirmation of the total of 2016 blocks in a retargetting period, + * (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments. + * Examples: 1916 for 95%, 1512 for testchains. + */ + uint32_t nRuleChangeActivationThreshold; + uint32_t nMinerConfirmationWindow; + BIP9Deployment vDeployments[MAX_VERSION_BITS_DEPLOYMENTS]; + /** Proof of work parameters */ uint256 powLimit; bool fPowAllowMinDifficultyBlocks; diff --git a/src/init.cpp b/src/init.cpp index 4ccc87c2..8a3fba79 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -490,7 +490,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-blockmaxsize=", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); strUsage += HelpMessageOpt("-blockprioritysize=", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); if (GetBoolArg("-help-debug", false)) - strUsage += HelpMessageOpt("-blockversion=", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION)); + strUsage += HelpMessageOpt("-blockversion=", "Override block version to test forking scenarios"); #ifdef FORK_CB_INPUT strUsage += HelpMessageGroup(_("Fork :")); diff --git a/src/main.cpp b/src/main.cpp index fbb9fcac..2fc25b8b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2089,6 +2089,24 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const } } +// Protected by cs_main +static VersionBitsCache versionbitscache; + +int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(cs_main); + int32_t nVersion = VERSIONBITS_TOP_BITS; + + for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { + ThresholdState state = VersionBitsState(pindexPrev, params, (Consensus::DeploymentPos)i, versionbitscache); + if (state == THRESHOLD_LOCKED_IN || state == THRESHOLD_STARTED) { + nVersion |= VersionBitsMask(params, (Consensus::DeploymentPos)i); + } + } + + return nVersion; +} + static int64_t nTimeVerify = 0; static int64_t nTimeConnect = 0; static int64_t nTimeIndex = 0; diff --git a/src/main.h b/src/main.h index ab77e76f..4634b8ca 100644 --- a/src/main.h +++ b/src/main.h @@ -162,11 +162,11 @@ void RegisterNodeSignals(CNodeSignals& nodeSignals); /** Unregister a network node */ void UnregisterNodeSignals(CNodeSignals& nodeSignals); -/** +/** * Process an incoming block. This only returns after the best known valid * block is made active. Note that it does not, however, guarantee that the * specific block passed to it has been checked for validity! - * + * * @param[out] state This may be set to an Error state if any error occurred processing it, including during validation/connection/etc of otherwise unrelated blocks during reorganisation; or it may be set to an Invalid state if pblock is itself invalid (but this is not guaranteed even when the block is checked). If you want to *possibly* get feedback on whether pblock is valid, you must also install a CValidationInterface (see validationinterface.h) - this will have its BlockChecked method called whenever *any* block completes validation. * @param[in] pfrom The node which we are receiving the block from; it is added to mapBlockSource and may be penalised if the block is invalid. * @param[in] pblock The block we want to process. @@ -295,7 +295,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF /** * Check transaction inputs, and make sure any * pay-to-script-hash transactions are evaluating IsStandard scripts - * + * * Why bother? To avoid denial-of-service attacks; an attacker * can submit a standard HASH... OP_EQUAL transaction, * which will get accepted into blocks. The redemption @@ -304,14 +304,14 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF * DUP CHECKSIG DROP ... repeated 100 times... OP_1 */ -/** +/** * Check for standard transaction types * @param[in] mapInputs Map of previous transactions that have outputs we're spending * @return True if all inputs (scriptSigs) use only standard transaction forms */ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); -/** +/** * Count ECDSA signature operations the old-fashioned (pre-0.6) way * @return number of sigops this transaction's outputs will produce when spent * @see CTransaction::FetchInputs @@ -320,7 +320,7 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx); /** * Count ECDSA signature operations in pay-to-script-hash inputs. - * + * * @param[in] mapInputs Map of previous transactions that have outputs we're spending * @return maximum number of sigops required to validate this transaction's inputs * @see CTransaction::FetchInputs @@ -364,9 +364,9 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime); */ bool CheckFinalTx(const CTransaction &tx, int flags = -1); -/** +/** * Closure representing one script verification - * Note that this stores references to the spending transaction + * Note that this stores references to the spending transaction */ class CScriptCheck { @@ -537,6 +537,11 @@ extern CBlockTreeDB *pblocktree; */ int GetSpendHeight(const CCoinsViewCache& inputs); +/** + * Determine what nVersion a new block should use. + */ +int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params); + namespace Consensus { bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams); } diff --git a/src/miner.cpp b/src/miner.cpp index 65f9b6f9..1d8b40ac 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -291,6 +291,12 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) const int nHeight = pindexPrev->nHeight + 1; pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); + pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + // -regtest only: allow overriding block.nVersion with + // -blockversion=N to test forking scenarios + if (chainparams.MineBlocksOnDemand()) + pblock->nVersion = GetArg("-blockversion", pblock->nVersion); + CCoinsViewCache view(pcoinsTip); // Priority order to process transactions diff --git a/src/primitives/block.h b/src/primitives/block.h index 6b3f13a8..f2738c45 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -54,7 +54,7 @@ public: void SetNull() { - nVersion = CBlockHeader::CURRENT_VERSION; + nVersion = 0; hashPrevBlock.SetNull(); hashMerkleRoot.SetNull(); hashReserved.SetNull(); diff --git a/src/versionbits.cpp b/src/versionbits.cpp new file mode 100644 index 00000000..d2ee49db --- /dev/null +++ b/src/versionbits.cpp @@ -0,0 +1,229 @@ +// Copyright (c) 2016-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { + { + /*.name =*/ "testdummy", + /*.gbt_force =*/ true, + }, + { + /*.name =*/ "csv", + /*.gbt_force =*/ true, + }, + { + /*.name =*/ "segwit", + /*.gbt_force =*/ true, + } +}; + +ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const +{ + int nPeriod = Period(params); + int nThreshold = Threshold(params); + int64_t nTimeStart = BeginTime(params); + int64_t nTimeTimeout = EndTime(params); + + // Check if this deployment is always active. + if (nTimeStart == Consensus::BIP9Deployment::ALWAYS_ACTIVE) { + return THRESHOLD_ACTIVE; + } + + // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. + if (pindexPrev != nullptr) { + pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); + } + + // Walk backwards in steps of nPeriod to find a pindexPrev whose information is known + std::vector vToCompute; + while (cache.count(pindexPrev) == 0) { + if (pindexPrev == nullptr) { + // The genesis block is by definition defined. + cache[pindexPrev] = THRESHOLD_DEFINED; + break; + } + if (pindexPrev->GetMedianTimePast() < nTimeStart) { + // Optimization: don't recompute down further, as we know every earlier block will be before the start time + cache[pindexPrev] = THRESHOLD_DEFINED; + break; + } + vToCompute.push_back(pindexPrev); + pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); + } + + // At this point, cache[pindexPrev] is known + assert(cache.count(pindexPrev)); + ThresholdState state = cache[pindexPrev]; + + // Now walk forward and compute the state of descendants of pindexPrev + while (!vToCompute.empty()) { + ThresholdState stateNext = state; + pindexPrev = vToCompute.back(); + vToCompute.pop_back(); + + switch (state) { + case THRESHOLD_DEFINED: { + if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { + stateNext = THRESHOLD_FAILED; + } else if (pindexPrev->GetMedianTimePast() >= nTimeStart) { + stateNext = THRESHOLD_STARTED; + } + break; + } + case THRESHOLD_STARTED: { + if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { + stateNext = THRESHOLD_FAILED; + break; + } + // We need to count + const CBlockIndex* pindexCount = pindexPrev; + int count = 0; + for (int i = 0; i < nPeriod; i++) { + if (Condition(pindexCount, params)) { + count++; + } + pindexCount = pindexCount->pprev; + } + if (count >= nThreshold) { + stateNext = THRESHOLD_LOCKED_IN; + } + break; + } + case THRESHOLD_LOCKED_IN: { + // Always progresses into ACTIVE. + stateNext = THRESHOLD_ACTIVE; + break; + } + case THRESHOLD_FAILED: + case THRESHOLD_ACTIVE: { + // Nothing happens, these are terminal states. + break; + } + } + cache[pindexPrev] = state = stateNext; + } + + return state; +} + +// return the numerical statistics of blocks signalling the specified BIP9 condition in this current period +BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const +{ + BIP9Stats stats = {}; + + stats.period = Period(params); + stats.threshold = Threshold(params); + + if (pindex == nullptr) + return stats; + + // Find beginning of period + const CBlockIndex* pindexEndOfPrevPeriod = pindex->GetAncestor(pindex->nHeight - ((pindex->nHeight + 1) % stats.period)); + stats.elapsed = pindex->nHeight - pindexEndOfPrevPeriod->nHeight; + + // Count from current block to beginning of period + int count = 0; + const CBlockIndex* currentIndex = pindex; + while (pindexEndOfPrevPeriod->nHeight != currentIndex->nHeight){ + if (Condition(currentIndex, params)) + count++; + currentIndex = currentIndex->pprev; + } + + stats.count = count; + stats.possible = (stats.period - stats.threshold ) >= (stats.elapsed - count); + + return stats; +} + +int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const +{ + int64_t start_time = BeginTime(params); + if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE) { + return 0; + } + + const ThresholdState initialState = GetStateFor(pindexPrev, params, cache); + + // BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment." + if (initialState == THRESHOLD_DEFINED) { + return 0; + } + + const int nPeriod = Period(params); + + // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. + // To ease understanding of the following height calculation, it helps to remember that + // right now pindexPrev points to the block prior to the block that we are computing for, thus: + // if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and + // if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period. + // The parent of the genesis block is represented by nullptr. + pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); + + const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); + + while (previousPeriodParent != nullptr && GetStateFor(previousPeriodParent, params, cache) == initialState) { + pindexPrev = previousPeriodParent; + previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); + } + + // Adjust the result because right now we point to the parent block. + return pindexPrev->nHeight + 1; +} + +namespace +{ +/** + * Class to implement versionbits logic. + */ +class VersionBitsConditionChecker : public AbstractThresholdConditionChecker { +private: + const Consensus::DeploymentPos id; + +protected: + int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; } + int64_t EndTime(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeout; } + int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; } + int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; } + + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override + { + return (((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (pindex->nVersion & Mask(params)) != 0); + } + +public: + explicit VersionBitsConditionChecker(Consensus::DeploymentPos id_) : id(id_) {} + uint32_t Mask(const Consensus::Params& params) const { return ((uint32_t)1) << params.vDeployments[id].bit; } +}; + +} // namespace + +ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache) +{ + return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]); +} + +BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindexPrev, params); +} + +int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache) +{ + return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, cache.caches[pos]); +} + +uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + return VersionBitsConditionChecker(pos).Mask(params); +} + +void VersionBitsCache::Clear() +{ + for (unsigned int d = 0; d < Consensus::MAX_VERSION_BITS_DEPLOYMENTS; d++) { + caches[d].clear(); + } +} diff --git a/src/versionbits.h b/src/versionbits.h new file mode 100644 index 00000000..1600dc8c --- /dev/null +++ b/src/versionbits.h @@ -0,0 +1,80 @@ +// Copyright (c) 2016-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CONSENSUS_VERSIONBITS +#define BITCOIN_CONSENSUS_VERSIONBITS + +#include +#include + +/** What block version to use for new blocks (pre versionbits) */ +static const int32_t VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4; +/** What bits to set in version for versionbits blocks */ +static const int32_t VERSIONBITS_TOP_BITS = 0x20000000UL; +/** What bitmask determines whether versionbits is in use */ +static const int32_t VERSIONBITS_TOP_MASK = 0xE0000000UL; +/** Total bits available for versionbits */ +static const int32_t VERSIONBITS_NUM_BITS = 29; + +enum ThresholdState { + THRESHOLD_DEFINED, + THRESHOLD_STARTED, + THRESHOLD_LOCKED_IN, + THRESHOLD_ACTIVE, + THRESHOLD_FAILED, +}; + +// A map that gives the state for blocks whose height is a multiple of Period(). +// The map is indexed by the block's parent, however, so all keys in the map +// will either be nullptr or a block with (height + 1) % Period() == 0. +typedef std::map ThresholdConditionCache; + +struct VBDeploymentInfo { + /** Deployment name */ + const char *name; + /** Whether GBT clients can safely ignore this rule in simplified usage */ + bool gbt_force; +}; + +struct BIP9Stats { + int period; + int threshold; + int elapsed; + int count; + bool possible; +}; + +extern const struct VBDeploymentInfo VersionBitsDeploymentInfo[]; + +/** + * Abstract class that implements BIP9-style threshold logic, and caches results. + */ +class AbstractThresholdConditionChecker { +protected: + virtual bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const =0; + virtual int64_t BeginTime(const Consensus::Params& params) const =0; + virtual int64_t EndTime(const Consensus::Params& params) const =0; + virtual int Period(const Consensus::Params& params) const =0; + virtual int Threshold(const Consensus::Params& params) const =0; + +public: + BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const; + // Note that the functions below take a pindexPrev as input: they compute information for block B based on its parent. + ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const; + int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const; +}; + +struct VersionBitsCache +{ + ThresholdConditionCache caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS]; + + void Clear(); +}; + +ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache); +BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos); +int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache); +uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos); + +#endif From 3a642589876bc31b2f5470be5b037ca127c2425f Mon Sep 17 00:00:00 2001 From: jc Date: Thu, 1 Feb 2018 15:40:38 -0500 Subject: [PATCH 02/12] bip9 wip continued --- src/main.cpp | 62 ++++++++++++++++++++++++++++++++++++---- src/miner.cpp | 5 ---- src/primitives/block.h | 2 +- src/test/miner_tests.cpp | 33 +++++++++++++++++++-- 4 files changed, 87 insertions(+), 15 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2fc25b8b..7917d6f5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2107,6 +2107,33 @@ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Para return nVersion; } +/** + * Threshold condition checker that triggers when unknown versionbits are seen on the network. + */ +class WarningBitsConditionChecker : public AbstractThresholdConditionChecker +{ +private: + int bit; + +public: + WarningBitsConditionChecker(int bitIn) : bit(bitIn) {} + + int64_t BeginTime(const Consensus::Params& params) const { return 0; } + int64_t EndTime(const Consensus::Params& params) const { return std::numeric_limits::max(); } + int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; } + int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; } + + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const + { + return ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && + ((pindex->nVersion >> bit) & 1) != 0 && + ((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0; + } +}; + +// Protected by cs_main +static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; + static int64_t nTimeVerify = 0; static int64_t nTimeConnect = 0; static int64_t nTimeIndex = 0; @@ -2459,24 +2486,43 @@ void static UpdateTip(CBlockIndex *pindexNew) { // Check the version of the last 100 blocks to see if we need to upgrade: static bool fWarned = false; - if (!IsInitialBlockDownload() && !fWarned) + if (!IsInitialBlockDownload()) { int nUpgraded = 0; const CBlockIndex* pindex = chainActive.Tip(); + for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) { + WarningBitsConditionChecker checker(bit); + ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]); + if (state == THRESHOLD_ACTIVE || state == THRESHOLD_LOCKED_IN) { + if (state == THRESHOLD_ACTIVE) { + strMiscWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit); + if (!fWarned) { + CAlert::Notify(strMiscWarning, true); + fWarned = true; + } + } else { + LogPrintf("%s: unknown new rules are about to activate (versionbit %i)\n", __func__, bit); + } + } + } + for (int i = 0; i < 100 && pindex != NULL; i++) { - if (pindex->nVersion > CBlock::CURRENT_VERSION) + int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus()); + if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0) ++nUpgraded; pindex = pindex->pprev; } if (nUpgraded > 0) - LogPrintf("%s: %d of last 100 blocks above version %d\n", __func__, nUpgraded, (int)CBlock::CURRENT_VERSION); + LogPrintf("%s: %d of last 100 blocks have unexpected version\n", __func__, nUpgraded; if (nUpgraded > 100/2) { // strMiscWarning is read by GetWarnings(), called by the JSON-RPC code to warn the user: - strMiscWarning = _("Warning: This version is obsolete; upgrade required!"); - CAlert::Notify(strMiscWarning, true); - fWarned = true; + strMiscWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect"); + if (!fWarned) { + CAlert::Notify(strMiscWarning, true); + fWarned = true; + } } } } @@ -3925,6 +3971,10 @@ void UnloadBlockIndex() setDirtyFileInfo.clear(); mapNodeState.clear(); recentRejects.reset(NULL); + versionbitscache.Clear(); + for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) { + warningcache[b].clear(); + } BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { delete entry.second; diff --git a/src/miner.cpp b/src/miner.cpp index 1d8b40ac..399d8a73 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -257,11 +257,6 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) return NULL; CBlock *pblock = &pblocktemplate->block; // pointer for convenience - // -regtest only: allow overriding block.nVersion with - // -blockversion=N to test forking scenarios - if (Params().MineBlocksOnDemand()) - pblock->nVersion = GetArg("-blockversion", pblock->nVersion); - // Add dummy coinbase tx as first transaction pblock->vtx.push_back(CTransaction()); pblocktemplate->vTxFees.push_back(-1); // updated at end diff --git a/src/primitives/block.h b/src/primitives/block.h index f2738c45..2fb2de39 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -22,7 +22,7 @@ class CBlockHeader public: // header static const size_t HEADER_SIZE=4+32+32+32+4+4+32; // excluding Equihash solution - static const int32_t CURRENT_VERSION=4; + int32_t nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index f72b3770..bc66db84 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -378,13 +378,40 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // subsidy changing int nHeight = chainActive.Height(); - chainActive.Tip()->nHeight = 209999; + // Create an actual 209999-long block chain (without valid blocks). + while (chainActive.Tip()->nHeight < 209999) { + CBlockIndex* prev = chainActive.Tip(); + CBlockIndex* next = new CBlockIndex(); + next->phashBlock = new uint256(GetRandHash()); + pcoinsTip->SetBestBlock(next->GetBlockHash()); + next->pprev = prev; + next->nHeight = prev->nHeight + 1; + next->BuildSkip(); + chainActive.SetTip(next); + } BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; - chainActive.Tip()->nHeight = 210000; + // Extend to a 210000-long block chain. + while (chainActive.Tip()->nHeight < 210000) { + CBlockIndex* prev = chainActive.Tip(); + CBlockIndex* next = new CBlockIndex(); + next->phashBlock = new uint256(GetRandHash()); + pcoinsTip->SetBestBlock(next->GetBlockHash()); + next->pprev = prev; + next->nHeight = prev->nHeight + 1; + next->BuildSkip(); + chainActive.SetTip(next); + } BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; - chainActive.Tip()->nHeight = nHeight; + // Delete the dummy blocks again. + while (chainActive.Tip()->nHeight > nHeight) { + CBlockIndex* del = chainActive.Tip(); + chainActive.SetTip(del->pprev); + pcoinsTip->SetBestBlock(del->pprev->GetBlockHash()); + delete del->phashBlock; + delete del; + } // non-final txs in mempool SetMockTime(chainActive.Tip()->GetMedianTimePast()+1); From c4691b238a35c868064a6cbebaa47500ad66f37f Mon Sep 17 00:00:00 2001 From: jc Date: Sat, 3 Feb 2018 11:06:30 -0500 Subject: [PATCH 03/12] bip9 wip: building Fix typo: Optimizaton -> Optimization Implement BIP 9 GBT changes - BIP9DeploymentInfo struct for static deployment info - VersionBitsDeploymentInfo: Avoid C++11ism by commenting parameter names - getblocktemplate: Make sure to set deployments in the version if it is LOCKED_IN - In this commit, all rules are considered required for clients to support remove cvs deployment info getblocktemplate: Explicitly handle the distinction between GBT-affecting softforks vs not fix nonce --- qa/pull-tester/rpc-tests.sh | 1 + qa/rpc-tests/p2p-versionbits-warning.py | 160 ++++++++++++ src/Makefile.test.include | 1 + src/chainparams.cpp | 11 + src/consensus/params.h | 3 +- src/main.cpp | 11 +- src/main.h | 6 + src/rpcblockchain.cpp | 22 ++ src/rpcmining.cpp | 79 +++++- src/test/versionbits_tests.cpp | 316 ++++++++++++++++++++++++ src/versionbits.cpp | 200 +++++---------- src/versionbits.h | 24 +- 12 files changed, 666 insertions(+), 168 deletions(-) create mode 100755 qa/rpc-tests/p2p-versionbits-warning.py create mode 100644 src/test/versionbits_tests.cpp diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index bd335764..7663df18 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -58,6 +58,7 @@ testScriptsExt=( 'smartfees.py' 'maxblocksinflight.py' 'invalidblockrequest.py' + 'p2p-versionbits-warning.py' # 'forknotify.py' 'p2p-acceptblock.py' ); diff --git a/qa/rpc-tests/p2p-versionbits-warning.py b/qa/rpc-tests/p2p-versionbits-warning.py new file mode 100755 index 00000000..061dcbf0 --- /dev/null +++ b/qa/rpc-tests/p2p-versionbits-warning.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python2 +# Copyright (c) 2016 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. +# + +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import time +from test_framework.blocktools import create_block, create_coinbase + +''' +Test version bits' warning system. + +Generate chains with block versions that appear to be signalling unknown +soft-forks, and test that warning alerts are generated. +''' + +VB_PERIOD = 144 # versionbits period length for regtest +VB_THRESHOLD = 108 # versionbits activation threshold for regtest +VB_TOP_BITS = 0x20000000 +VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment + +# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending +# p2p messages to a node, generating the messages in the main testing logic. +class TestNode(NodeConnCB): + def __init__(self): + NodeConnCB.__init__(self) + self.connection = None + self.ping_counter = 1 + self.last_pong = msg_pong() + + def add_connection(self, conn): + self.connection = conn + + def on_inv(self, conn, message): + pass + + # Wrapper for the NodeConn's send_message function + def send_message(self, message): + self.connection.send_message(message) + + def on_pong(self, conn, message): + self.last_pong = message + + # Sync up with the node after delivery of a block + def sync_with_ping(self, timeout=30): + self.connection.send_message(msg_ping(nonce=self.ping_counter)) + received_pong = False + sleep_time = 0.05 + while not received_pong and timeout > 0: + time.sleep(sleep_time) + timeout -= sleep_time + with mininode_lock: + if self.last_pong.nonce == self.ping_counter: + received_pong = True + self.ping_counter += 1 + return received_pong + + +class VersionBitsWarningTest(BitcoinTestFramework): + def setup_chain(self): + initialize_chain_clean(self.options.tmpdir, 1) + + def setup_network(self): + self.nodes = [] + self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") + # Open and close to create zero-length file + with open(self.alert_filename, 'w') as f: + pass + self.node_options = ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""] + self.nodes.append(start_node(0, self.options.tmpdir, self.node_options)) + + import re + self.vb_pattern = re.compile("^Warning.*versionbit") + + # Send numblocks blocks via peer with nVersionToUse set. + def send_blocks_with_version(self, peer, numblocks, nVersionToUse): + tip = self.nodes[0].getbestblockhash() + height = self.nodes[0].getblockcount() + block_time = self.nodes[0].getblockheader(tip)["time"]+1 + tip = int(tip, 16) + + for i in xrange(numblocks): + block = create_block(tip, create_coinbase(height+1), block_time) + block.nVersion = nVersionToUse + block.solve() + peer.send_message(msg_block(block)) + block_time += 1 + height += 1 + tip = block.sha256 + peer.sync_with_ping() + + def test_versionbits_in_alert_file(self): + with open(self.alert_filename, 'r') as f: + alert_text = f.read() + assert(self.vb_pattern.match(alert_text)) + + def run_test(self): + # Setup the p2p connection and start up the network thread. + test_node = TestNode() + + connections = [] + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)) + test_node.add_connection(connections[0]) + + NetworkThread().start() # Start up network handling in another thread + + # Test logic begins here + test_node.wait_for_verack() + + # 1. Have the node mine one period worth of blocks + self.nodes[0].generate(VB_PERIOD) + + # 2. Now build one period of blocks on the tip, with < VB_THRESHOLD + # blocks signaling some unknown bit. + nVersion = VB_TOP_BITS | (1<= VB_THRESHOLD blocks signaling + # some unknown bit + self.send_blocks_with_version(test_node, VB_THRESHOLD, nVersion) + self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD) + # Might not get a versionbits-related alert yet, as we should + # have gotten a different alert due to more than 51/100 blocks + # being of unexpected version. + # Check that getinfo() shows some kind of error. + assert(len(self.nodes[0].getinfo()["errors"]) != 0) + + # Mine a period worth of expected blocks so the generic block-version warning + # is cleared, and restart the node. This should move the versionbit state + # to ACTIVE. + self.nodes[0].generate(VB_PERIOD) + stop_node(self.nodes[0], 0) + wait_bitcoinds() + # Empty out the alert file + with open(self.alert_filename, 'w') as f: + pass + self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]) + + # Connecting one block should be enough to generate an error. + self.nodes[0].generate(1) + assert(len(self.nodes[0].getinfo()["errors"]) != 0) + stop_node(self.nodes[0], 0) + wait_bitcoinds() + self.test_versionbits_in_alert_file() + + # Test framework expects the node to still be running... + self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]) + + +if __name__ == '__main__': + VersionBitsWarningTest().main() diff --git a/src/Makefile.test.include b/src/Makefile.test.include index d18f2964..da46acdd 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -86,6 +86,7 @@ BITCOIN_TESTS =\ test/timedata_tests.cpp \ test/torcontrol_tests.cpp \ test/transaction_tests.cpp \ + test/versionbits_tests.cpp \ test/uint256_tests.cpp \ test/univalue_tests.cpp \ test/util_tests.cpp \ diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 6a8d5bbe..ba415fb9 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -54,6 +54,10 @@ public: consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016 consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008 + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008 + pchMessageStart[0] = 0xa8; pchMessageStart[1] = 0xea; pchMessageStart[2] = 0xb2; @@ -215,6 +219,10 @@ public: consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008 + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008 + pchMessageStart[0] = 0xf6; pchMessageStart[1] = 0x1b; pchMessageStart[2] = 0xf6; @@ -309,6 +317,9 @@ public: consensus.nPowMaxAdjustUp = 0; // Turn off adjustment up consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 999999999999ULL; pchMessageStart[0] = 0xaa; pchMessageStart[1] = 0xe8; diff --git a/src/consensus/params.h b/src/consensus/params.h index 067c9f5f..70e3c0ba 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -12,7 +12,8 @@ namespace Consensus { enum DeploymentPos { - MAX_VERSION_BITS_DEPLOYMENTS = 0, + DEPLOYMENT_TESTDUMMY, + MAX_VERSION_BITS_DEPLOYMENTS }; /** diff --git a/src/main.cpp b/src/main.cpp index 7917d6f5..9f1a1d80 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,6 +27,7 @@ #include "util.h" #include "utilmoneystr.h" #include "validationinterface.h" +#include "versionbits.h" #include "wallet/asyncrpcoperation_sendmany.h" #include @@ -2090,7 +2091,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const } // Protected by cs_main -static VersionBitsCache versionbitscache; +VersionBitsCache versionbitscache; int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) { @@ -2514,7 +2515,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { pindex = pindex->pprev; } if (nUpgraded > 0) - LogPrintf("%s: %d of last 100 blocks have unexpected version\n", __func__, nUpgraded; + LogPrintf("%s: %d of last 100 blocks have unexpected version\n", __func__, nUpgraded); if (nUpgraded > 100/2) { // strMiscWarning is read by GetWarnings(), called by the JSON-RPC code to warn the user: @@ -5790,7 +5791,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle) return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); } - +ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + LOCK(cs_main); + return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache); +} class CMainCleanup { diff --git a/src/main.h b/src/main.h index 4634b8ca..35beeba6 100644 --- a/src/main.h +++ b/src/main.h @@ -25,6 +25,7 @@ #include "tinyformat.h" #include "txmempool.h" #include "uint256.h" +#include "versionbits.h" #include #include @@ -257,6 +258,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa bool* pfMissingInputs, bool fRejectAbsurdFee=false); +/** Get the BIP9 state for a given deployment at the current tip. */ +ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos); + struct CNodeStateStats { int nMisbehavior; int nSyncHeight; @@ -537,6 +541,8 @@ extern CBlockTreeDB *pblocktree; */ int GetSpendHeight(const CCoinsViewCache& inputs); +extern VersionBitsCache versionbitscache; + /** * Determine what nVersion a new block should use. */ diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 939430be..d0e35bd4 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -634,6 +634,20 @@ static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* return rv; } +static UniValue BIP9SoftForkDesc(const std::string& name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +{ + UniValue rv(UniValue::VOBJ); + rv.push_back(Pair("id", name)); + switch (VersionBitsTipState(consensusParams, id)) { + case THRESHOLD_DEFINED: rv.push_back(Pair("status", "defined")); break; + case THRESHOLD_STARTED: rv.push_back(Pair("status", "started")); break; + case THRESHOLD_LOCKED_IN: rv.push_back(Pair("status", "locked_in")); break; + case THRESHOLD_ACTIVE: rv.push_back(Pair("status", "active")); break; + case THRESHOLD_FAILED: rv.push_back(Pair("status", "failed")); break; + } + return rv; +} + UniValue getblockchaininfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -662,6 +676,12 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) " },\n" " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n" " }, ...\n" + " ],\n" + " \"bip9_softforks\": [ (array) status of BIP9 softforks in progress\n" + " {\n" + " \"id\": \"xxxx\", (string) name of the softfork\n" + " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"lockedin\", \"active\", \"failed\"\n" + " }\n" " ]\n" "}\n" "\nExamples:\n" @@ -688,10 +708,12 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) const Consensus::Params& consensusParams = Params().GetConsensus(); CBlockIndex* tip = chainActive.Tip(); UniValue softforks(UniValue::VARR); + UniValue bip9_softforks(UniValue::VARR); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); obj.push_back(Pair("softforks", softforks)); + obj.push_back(Pair("bip9_softforks", bip9_softforks)); if (fPruneMode) { diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 36a4cfa6..0f68922b 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -6,6 +6,7 @@ #include "amount.h" #include "chainparams.h" #include "consensus/consensus.h" +#include "consensus/params.h" #include "consensus/validation.h" #include "core_io.h" #ifdef ENABLE_MINING @@ -425,6 +426,15 @@ static UniValue BIP22ValidationResult(const CValidationState& state) return "valid?"; } +std::string gbt_vb_name(const Consensus::DeploymentPos pos) { + const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; + std::string s = vbinfo.name; + if (!vbinfo.gbt_force) { + s.insert(s.begin(), '!'); + } + return s; +} + UniValue getblocktemplate(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 1) @@ -432,7 +442,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) "getblocktemplate ( \"jsonrequestobject\" )\n" "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" "It returns data needed to construct a block to work on.\n" - "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" + "For full specification, see BIPs 22 and 9:\n" + " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n" + " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n" "\nArguments:\n" "1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n" @@ -448,6 +460,12 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"version\" : n, (numeric) The block version\n" + " \"rules\" : [ \"rulename\", ... ], (array of strings) specific block rules that are to be enforced\n" + " \"vbavailable\" : { (json object) set of pending, supported versionbit (BIP 9) softfork deployments\n" + " \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n" + " ,...\n" + " },\n" + " \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\n" " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" " {\n" @@ -502,8 +520,10 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) std::string strMode = "template"; UniValue lpval = NullUniValue; + // TODO: Re-enable coinbasevalue once a specification has been written bool coinbasetxn = true; + std::set setClientRules; if (params.size() > 0) { const UniValue& oparam = params[0].get_obj(); @@ -547,6 +567,14 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) TestBlockValidity(state, block, pindexPrev, false, true); return BIP22ValidationResult(state); } + + const UniValue& aClientRules = find_value(oparam, "rules"); + if (aClientRules.isArray()) { + for (unsigned int i = 0; i < aClientRules.size(); ++i) { + const UniValue& v = aClientRules[i]; + setClientRules.insert(v.get_str()); + } + } } if (strMode != "template") @@ -640,9 +668,10 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) pindexPrev = pindexPrevNew; } CBlock* pblock = &pblocktemplate->block; // pointer for convenience + const Consensus::Params& consensusParams = Params().GetConsensus(); // Update nTime - UpdateTime(pblock, Params().GetConsensus(), pindexPrev); + UpdateTime(pblock, consensusParams, pindexPrev); pblock->nNonce = uint256(); UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal"); @@ -704,7 +733,53 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); result.push_back(Pair("capabilities", aCaps)); + + UniValue aRules(UniValue::VARR); + UniValue vbavailable(UniValue::VOBJ); + for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) { + Consensus::DeploymentPos pos = Consensus::DeploymentPos(i); + ThresholdState state = VersionBitsState(pindexPrev, consensusParams, pos, versionbitscache); + switch (state) { + case THRESHOLD_DEFINED: + case THRESHOLD_FAILED: + // Not exposed to GBT at all + break; + case THRESHOLD_LOCKED_IN: + // Ensure bit is set in block version + pblock->nVersion |= VersionBitsMask(consensusParams, pos); + // FALL THROUGH to get vbavailable set... + case THRESHOLD_STARTED: + { + const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; + vbavailable.push_back(Pair(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit)); + if (setClientRules.find(vbinfo.name) == setClientRules.end()) { + if (!vbinfo.gbt_force) { + // If the client doesn't support this, don't indicate it in the [default] version + pblock->nVersion &= ~VersionBitsMask(consensusParams, pos); + } + } + break; + } + case THRESHOLD_ACTIVE: + { + // Add to rules only + const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; + aRules.push_back(gbt_vb_name(pos)); + if (setClientRules.find(vbinfo.name) == setClientRules.end()) { + // Not supported by the client; make sure it's safe to proceed + if (!vbinfo.gbt_force) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Support for '%s' rule requires explicit client support", vbinfo.name)); + } + } + break; + } + } + } result.push_back(Pair("version", pblock->nVersion)); + result.push_back(Pair("rules", aRules)); + result.push_back(Pair("vbavailable", vbavailable)); + result.push_back(Pair("vbrequired", int(0))); + result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); result.push_back(Pair("transactions", transactions)); if (coinbasetxn) { diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp new file mode 100644 index 00000000..1f86a06a --- /dev/null +++ b/src/test/versionbits_tests.cpp @@ -0,0 +1,316 @@ +// Copyright (c) 2014-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "chain.h" +#include "random.h" +#include "versionbits.h" +#include "test/test_bitcoin.h" +#include "chainparams.h" +#include "main.h" +#include "consensus/params.h" + +#include + +/* Define a virtual block time, one block per 10 minutes after Nov 14 2014, 0:55:36am */ +int32_t TestTime(int nHeight) { return 1415926536 + 600 * nHeight; } + +static const Consensus::Params paramsDummy = Consensus::Params(); + +class TestConditionChecker : public AbstractThresholdConditionChecker +{ +private: + mutable ThresholdConditionCache cache; + +public: + int64_t BeginTime(const Consensus::Params& params) const { return TestTime(10000); } + int64_t EndTime(const Consensus::Params& params) const { return TestTime(20000); } + int Period(const Consensus::Params& params) const { return 1000; } + int Threshold(const Consensus::Params& params) const { return 900; } + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const { return (pindex->nVersion & 0x100); } + + ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, paramsDummy, cache); } +}; + +#define CHECKERS 6 + +class VersionBitsTester +{ + // A fake blockchain + std::vector vpblock; + + // 6 independent checkers for the same bit. + // The first one performs all checks, the second only 50%, the third only 25%, etc... + // This is to test whether lack of cached information leads to the same results. + TestConditionChecker checker[CHECKERS]; + + // Test counter (to identify failures) + int num; + +public: + VersionBitsTester() : num(0) {} + + VersionBitsTester& Reset() { + for (unsigned int i = 0; i < vpblock.size(); i++) { + delete vpblock[i]; + } + for (unsigned int i = 0; i < CHECKERS; i++) { + checker[i] = TestConditionChecker(); + } + vpblock.clear(); + return *this; + } + + ~VersionBitsTester() { + Reset(); + } + + VersionBitsTester& Mine(unsigned int height, int32_t nTime, int32_t nVersion) { + while (vpblock.size() < height) { + CBlockIndex* pindex = new CBlockIndex(); + pindex->nHeight = vpblock.size(); + pindex->pprev = vpblock.size() > 0 ? vpblock.back() : NULL; + pindex->nTime = nTime; + pindex->nVersion = nVersion; + pindex->BuildSkip(); + vpblock.push_back(pindex); + } + return *this; + } + + VersionBitsTester& TestDefined() { + for (int i = 0; i < CHECKERS; i++) { + if ((insecure_rand() & ((1 << i) - 1)) == 0) { + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED", num)); + } + } + num++; + return *this; + } + + VersionBitsTester& TestStarted() { + for (int i = 0; i < CHECKERS; i++) { + if ((insecure_rand() & ((1 << i) - 1)) == 0) { + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_STARTED, strprintf("Test %i for STARTED", num)); + } + } + num++; + return *this; + } + + VersionBitsTester& TestLockedIn() { + for (int i = 0; i < CHECKERS; i++) { + if ((insecure_rand() & ((1 << i) - 1)) == 0) { + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_LOCKED_IN, strprintf("Test %i for LOCKED_IN", num)); + } + } + num++; + return *this; + } + + VersionBitsTester& TestActive() { + for (int i = 0; i < CHECKERS; i++) { + if ((insecure_rand() & ((1 << i) - 1)) == 0) { + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE", num)); + } + } + num++; + return *this; + } + + VersionBitsTester& TestFailed() { + for (int i = 0; i < CHECKERS; i++) { + if ((insecure_rand() & ((1 << i) - 1)) == 0) { + BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_FAILED, strprintf("Test %i for FAILED", num)); + } + } + num++; + return *this; + } + + CBlockIndex * Tip() { return vpblock.size() ? vpblock.back() : NULL; } +}; + +BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup) + +BOOST_AUTO_TEST_CASE(versionbits_test) +{ + for (int i = 0; i < 64; i++) { + // DEFINED -> FAILED + VersionBitsTester().TestDefined() + .Mine(1, TestTime(1), 0x100).TestDefined() + .Mine(11, TestTime(11), 0x100).TestDefined() + .Mine(989, TestTime(989), 0x100).TestDefined() + .Mine(999, TestTime(20000), 0x100).TestDefined() + .Mine(1000, TestTime(20000), 0x100).TestFailed() + .Mine(1999, TestTime(30001), 0x100).TestFailed() + .Mine(2000, TestTime(30002), 0x100).TestFailed() + .Mine(2001, TestTime(30003), 0x100).TestFailed() + .Mine(2999, TestTime(30004), 0x100).TestFailed() + .Mine(3000, TestTime(30005), 0x100).TestFailed() + + // DEFINED -> STARTED -> FAILED + .Reset().TestDefined() + .Mine(1, TestTime(1), 0).TestDefined() + .Mine(1000, TestTime(10000) - 1, 0x100).TestDefined() // One second more and it would be defined + .Mine(2000, TestTime(10000), 0x100).TestStarted() // So that's what happens the next period + .Mine(2051, TestTime(10010), 0).TestStarted() // 51 old blocks + .Mine(2950, TestTime(10020), 0x100).TestStarted() // 899 new blocks + .Mine(3000, TestTime(20000), 0).TestFailed() // 50 old blocks (so 899 out of the past 1000) + .Mine(4000, TestTime(20010), 0x100).TestFailed() + + // DEFINED -> STARTED -> FAILED while threshold reached + .Reset().TestDefined() + .Mine(1, TestTime(1), 0).TestDefined() + .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined() // One second more and it would be defined + .Mine(2000, TestTime(10000), 0x101).TestStarted() // So that's what happens the next period + .Mine(2999, TestTime(30000), 0x100).TestStarted() // 999 new blocks + .Mine(3000, TestTime(30000), 0x100).TestFailed() // 1 new block (so 1000 out of the past 1000 are new) + .Mine(3999, TestTime(30001), 0).TestFailed() + .Mine(4000, TestTime(30002), 0).TestFailed() + .Mine(14333, TestTime(30003), 0).TestFailed() + .Mine(24000, TestTime(40000), 0).TestFailed() + + // DEFINED -> STARTED -> LOCKEDIN at the last minute -> ACTIVE + .Reset().TestDefined() + .Mine(1, TestTime(1), 0).TestDefined() + .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined() // One second more and it would be defined + .Mine(2000, TestTime(10000), 0x101).TestStarted() // So that's what happens the next period + .Mine(2050, TestTime(10010), 0x200).TestStarted() // 50 old blocks + .Mine(2950, TestTime(10020), 0x100).TestStarted() // 900 new blocks + .Mine(2999, TestTime(19999), 0x200).TestStarted() // 49 old blocks + .Mine(3000, TestTime(29999), 0x200).TestLockedIn() // 1 old block (so 900 out of the past 1000) + .Mine(3999, TestTime(30001), 0).TestLockedIn() + .Mine(4000, TestTime(30002), 0).TestActive() + .Mine(14333, TestTime(30003), 0).TestActive() + .Mine(24000, TestTime(40000), 0).TestActive(); + } + + // Sanity checks of version bit deployments + const Consensus::Params &mainnetParams = Params(CBaseChainParams::MAIN).GetConsensus(); + for (int i=0; i<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { + uint32_t bitmask = VersionBitsMask(mainnetParams, (Consensus::DeploymentPos)i); + // Make sure that no deployment tries to set an invalid bit. + BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask); + + // Verify that the deployment windows of different deployment using the + // same bit are disjoint. + // This test may need modification at such time as a new deployment + // is proposed that reuses the bit of an activated soft fork, before the + // end time of that soft fork. (Alternatively, the end time of that + // activated soft fork could be later changed to be earlier to avoid + // overlap.) + for (int j=i+1; j<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; j++) { + if (VersionBitsMask(mainnetParams, (Consensus::DeploymentPos)j) == bitmask) { + BOOST_CHECK(mainnetParams.vDeployments[j].nStartTime > mainnetParams.vDeployments[i].nTimeout || + mainnetParams.vDeployments[i].nStartTime > mainnetParams.vDeployments[j].nTimeout); + } + } + } +} + +BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) +{ + // Check that ComputeBlockVersion will set the appropriate bit correctly + // on mainnet. + const Consensus::Params &mainnetParams = Params(CBaseChainParams::MAIN).GetConsensus(); + + // Use the TESTDUMMY deployment for testing purposes. + int64_t bit = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit; + int64_t nStartTime = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime; + int64_t nTimeout = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout; + + assert(nStartTime < nTimeout); + + // In the first chain, test that the bit is set by CBV until it has failed. + // In the second chain, test the bit is set by CBV while STARTED and + // LOCKED-IN, and then no longer set while ACTIVE. + VersionBitsTester firstChain, secondChain; + + // Start generating blocks before nStartTime + int64_t nTime = nStartTime - 1; + + // Before MedianTimePast of the chain has crossed nStartTime, the bit + // should not be set. + CBlockIndex *lastBlock = NULL; + lastBlock = firstChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1< 0) { + lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1< -#include +#include "versionbits.h" -const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { +#include "consensus/params.h" + +const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { { /*.name =*/ "testdummy", - /*.gbt_force =*/ true, - }, - { - /*.name =*/ "csv", - /*.gbt_force =*/ true, - }, - { - /*.name =*/ "segwit", - /*.gbt_force =*/ true, } }; @@ -27,20 +19,15 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* int64_t nTimeStart = BeginTime(params); int64_t nTimeTimeout = EndTime(params); - // Check if this deployment is always active. - if (nTimeStart == Consensus::BIP9Deployment::ALWAYS_ACTIVE) { - return THRESHOLD_ACTIVE; - } - // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. - if (pindexPrev != nullptr) { + if (pindexPrev != NULL) { pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); } // Walk backwards in steps of nPeriod to find a pindexPrev whose information is known std::vector vToCompute; while (cache.count(pindexPrev) == 0) { - if (pindexPrev == nullptr) { + if (pindexPrev == NULL) { // The genesis block is by definition defined. cache[pindexPrev] = THRESHOLD_DEFINED; break; @@ -65,43 +52,43 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* vToCompute.pop_back(); switch (state) { - case THRESHOLD_DEFINED: { - if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { - stateNext = THRESHOLD_FAILED; - } else if (pindexPrev->GetMedianTimePast() >= nTimeStart) { - stateNext = THRESHOLD_STARTED; - } + case THRESHOLD_DEFINED: { + if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { + stateNext = THRESHOLD_FAILED; + } else if (pindexPrev->GetMedianTimePast() >= nTimeStart) { + stateNext = THRESHOLD_STARTED; + } + break; + } + case THRESHOLD_STARTED: { + if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { + stateNext = THRESHOLD_FAILED; break; } - case THRESHOLD_STARTED: { - if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { - stateNext = THRESHOLD_FAILED; - break; + // We need to count + const CBlockIndex* pindexCount = pindexPrev; + int count = 0; + for (int i = 0; i < nPeriod; i++) { + if (Condition(pindexCount, params)) { + count++; } - // We need to count - const CBlockIndex* pindexCount = pindexPrev; - int count = 0; - for (int i = 0; i < nPeriod; i++) { - if (Condition(pindexCount, params)) { - count++; - } - pindexCount = pindexCount->pprev; - } - if (count >= nThreshold) { - stateNext = THRESHOLD_LOCKED_IN; - } - break; + pindexCount = pindexCount->pprev; } - case THRESHOLD_LOCKED_IN: { - // Always progresses into ACTIVE. - stateNext = THRESHOLD_ACTIVE; - break; - } - case THRESHOLD_FAILED: - case THRESHOLD_ACTIVE: { - // Nothing happens, these are terminal states. - break; + if (count >= nThreshold) { + stateNext = THRESHOLD_LOCKED_IN; } + break; + } + case THRESHOLD_LOCKED_IN: { + // Always progresses into ACTIVE. + stateNext = THRESHOLD_ACTIVE; + break; + } + case THRESHOLD_FAILED: + case THRESHOLD_ACTIVE: { + // Nothing happens, these are terminal states. + break; + } } cache[pindexPrev] = state = stateNext; } @@ -109,113 +96,38 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* return state; } -// return the numerical statistics of blocks signalling the specified BIP9 condition in this current period -BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const -{ - BIP9Stats stats = {}; - - stats.period = Period(params); - stats.threshold = Threshold(params); - - if (pindex == nullptr) - return stats; - - // Find beginning of period - const CBlockIndex* pindexEndOfPrevPeriod = pindex->GetAncestor(pindex->nHeight - ((pindex->nHeight + 1) % stats.period)); - stats.elapsed = pindex->nHeight - pindexEndOfPrevPeriod->nHeight; - - // Count from current block to beginning of period - int count = 0; - const CBlockIndex* currentIndex = pindex; - while (pindexEndOfPrevPeriod->nHeight != currentIndex->nHeight){ - if (Condition(currentIndex, params)) - count++; - currentIndex = currentIndex->pprev; - } - - stats.count = count; - stats.possible = (stats.period - stats.threshold ) >= (stats.elapsed - count); - - return stats; -} - -int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const -{ - int64_t start_time = BeginTime(params); - if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE) { - return 0; - } - - const ThresholdState initialState = GetStateFor(pindexPrev, params, cache); - - // BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment." - if (initialState == THRESHOLD_DEFINED) { - return 0; - } - - const int nPeriod = Period(params); - - // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. - // To ease understanding of the following height calculation, it helps to remember that - // right now pindexPrev points to the block prior to the block that we are computing for, thus: - // if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and - // if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period. - // The parent of the genesis block is represented by nullptr. - pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); - - const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); - - while (previousPeriodParent != nullptr && GetStateFor(previousPeriodParent, params, cache) == initialState) { - pindexPrev = previousPeriodParent; - previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); - } - - // Adjust the result because right now we point to the parent block. - return pindexPrev->nHeight + 1; -} - namespace { /** * Class to implement versionbits logic. */ -class VersionBitsConditionChecker : public AbstractThresholdConditionChecker { -private: - const Consensus::DeploymentPos id; + class VersionBitsConditionChecker : public AbstractThresholdConditionChecker { + private: + const Consensus::DeploymentPos id; -protected: - int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; } - int64_t EndTime(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeout; } - int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; } - int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; } + protected: + int64_t BeginTime(const Consensus::Params& params) const { return params.vDeployments[id].nStartTime; } + int64_t EndTime(const Consensus::Params& params) const { return params.vDeployments[id].nTimeout; } + int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; } + int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; } - bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override - { - return (((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (pindex->nVersion & Mask(params)) != 0); - } + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const + { + return (((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (pindex->nVersion & Mask(params)) != 0); + } -public: - explicit VersionBitsConditionChecker(Consensus::DeploymentPos id_) : id(id_) {} - uint32_t Mask(const Consensus::Params& params) const { return ((uint32_t)1) << params.vDeployments[id].bit; } -}; + public: + VersionBitsConditionChecker(Consensus::DeploymentPos id_) : id(id_) {} + uint32_t Mask(const Consensus::Params& params) const { return ((uint32_t)1) << params.vDeployments[id].bit; } + }; -} // namespace +} ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache) { return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]); } -BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) -{ - return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindexPrev, params); -} - -int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache) -{ - return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, cache.caches[pos]); -} - uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos) { return VersionBitsConditionChecker(pos).Mask(params); diff --git a/src/versionbits.h b/src/versionbits.h index 1600dc8c..ede2dcdd 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -1,11 +1,11 @@ -// Copyright (c) 2016-2017 The Bitcoin Core developers +// Copyright (c) 2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_CONSENSUS_VERSIONBITS #define BITCOIN_CONSENSUS_VERSIONBITS -#include +#include "chain.h" #include /** What block version to use for new blocks (pre versionbits) */ @@ -27,25 +27,17 @@ enum ThresholdState { // A map that gives the state for blocks whose height is a multiple of Period(). // The map is indexed by the block's parent, however, so all keys in the map -// will either be nullptr or a block with (height + 1) % Period() == 0. +// will either be NULL or a block with (height + 1) % Period() == 0. typedef std::map ThresholdConditionCache; -struct VBDeploymentInfo { +struct BIP9DeploymentInfo { /** Deployment name */ const char *name; /** Whether GBT clients can safely ignore this rule in simplified usage */ bool gbt_force; }; -struct BIP9Stats { - int period; - int threshold; - int elapsed; - int count; - bool possible; -}; - -extern const struct VBDeploymentInfo VersionBitsDeploymentInfo[]; +extern const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[]; /** * Abstract class that implements BIP9-style threshold logic, and caches results. @@ -59,10 +51,8 @@ protected: virtual int Threshold(const Consensus::Params& params) const =0; public: - BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const; - // Note that the functions below take a pindexPrev as input: they compute information for block B based on its parent. + // Note that the function below takes a pindexPrev as input: they compute information for block B based on its parent. ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const; - int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const; }; struct VersionBitsCache @@ -73,8 +63,6 @@ struct VersionBitsCache }; ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache); -BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos); -int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache); uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos); #endif From 916697eaa945304ab28328105812570051f64fe2 Mon Sep 17 00:00:00 2001 From: jc Date: Sat, 3 Feb 2018 09:55:16 -0500 Subject: [PATCH 04/12] add a difficulty bomb at height 600000 --- src/chainparams.cpp | 5 +++++ src/consensus/params.h | 2 ++ src/pow.cpp | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index fa0f24cf..7733effd 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -50,6 +50,7 @@ public: consensus.nPowMaxAdjustUp = 16; // 16% adjustment up consensus.nPowTargetSpacing = 2.5 * 60; consensus.fPowAllowMinDifficultyBlocks = false; + consensus.nPowDifficultyBombHeight = 600000; pchMessageStart[0] = 0xa8; pchMessageStart[1] = 0xea; @@ -209,6 +210,8 @@ public: consensus.powLimit = uint256S("07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); consensus.fPowAllowMinDifficultyBlocks = true; + consensus.nPowDifficultyBombHeight = 600000; + pchMessageStart[0] = 0xf6; pchMessageStart[1] = 0x1b; pchMessageStart[2] = 0xf6; @@ -300,6 +303,8 @@ public: assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down consensus.nPowMaxAdjustUp = 0; // Turn off adjustment up + consensus.nPowDifficultyBombHeight = 600000; + pchMessageStart[0] = 0xaa; pchMessageStart[1] = 0xe8; pchMessageStart[2] = 0x3f; diff --git a/src/consensus/params.h b/src/consensus/params.h index 9987f2e7..7d7c04ec 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -50,6 +50,8 @@ struct Params { int64_t AveragingWindowTimespan() const { return nPowAveragingWindow * nPowTargetSpacing; } int64_t MinActualTimespan() const { return (AveragingWindowTimespan() * (100 - nPowMaxAdjustUp )) / 100; } int64_t MaxActualTimespan() const { return (AveragingWindowTimespan() * (100 + nPowMaxAdjustDown)) / 100; } + + int nPowDifficultyBombHeight; }; } // namespace Consensus diff --git a/src/pow.cpp b/src/pow.cpp index 83e83e9e..837c1522 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -9,6 +9,7 @@ #include "chain.h" #include "chainparams.h" #include "crypto/equihash.h" +#include "main.h" #include "primitives/block.h" #include "streams.h" #include "uint256.h" @@ -23,11 +24,16 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) { unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact(); + unsigned int nProofOfWorkBomb = UintToArith256(uint256S("000000000000000000000000000000000000000000000000000000000000ffff")).GetCompact(); // Genesis block if (pindexLast == NULL) return nProofOfWorkLimit; + // difficulty bomb + else if(pindexLast->nHeight > params.nPowDifficultyBombHeight) + return nProofOfWorkBomb; + // Find the first block in the averaging interval const CBlockIndex* pindexFirst = pindexLast; arith_uint256 bnTot {0}; From 18dde3827a1030832f0511750610d884214001c4 Mon Sep 17 00:00:00 2001 From: jc Date: Sat, 20 Jan 2018 11:24:09 -0500 Subject: [PATCH 05/12] move fork heights to chainparams and fix coinbase scriptSig match style & more bugfixes change max sigops and further cleanup lock cs_main use genesis nBits use a sentinel value in fork block header refactor pow & soln validation in fork range exempt fork blocks from fewer checks more tweaks to make fork blocks more standard --- src/chainparams.cpp | 6 +- src/chainparams.h | 6 + src/consensus/consensus.h | 2 +- src/consensus/params.h | 6 +- src/init.cpp | 4 +- src/main.cpp | 80 +++++------ src/main.h | 33 +++-- src/miner.cpp | 274 +++++++++++++++++++++----------------- src/miner.h | 3 +- src/pow.cpp | 22 +-- src/pow.h | 2 +- src/txdb.cpp | 11 +- src/txdb.h | 5 - 13 files changed, 239 insertions(+), 215 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index fa0f24cf..a1957fb1 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -188,8 +188,12 @@ public: //// "t3fmYHU2DnVaQgPhDs6TMFVmyC3qbWEWgXN", /* main-index: 52*/ //// "t3T4WmAp6nrLkJ24iPpGeCe1fSWTPv47ASG", /* main-index: 53*/ //// "t3fP6GrDM4QVwdjFhmCxGNbe7jXXXSDQ5dv", /* main-index: 54*/ -}; + }; assert(vFoundersRewardAddress.size() <= consensus.GetLastFoundersRewardBlockHeight()); + + //current ZCL height is 200K-300K, this value here is placeholder, it will have to be changed to correct fork block height + nForkStartHeight = 1000000; + nForkHeightRange = 65000; } }; static CMainParams mainParams; diff --git a/src/chainparams.h b/src/chainparams.h index a1de7b49..c95a8557 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -81,6 +81,9 @@ public: std::string GetFoundersRewardAddressAtIndex(int i) const; /** Enforce coinbase consensus rule in regtest mode */ void SetRegTestCoinbaseMustBeProtected() { consensus.fCoinbaseMustBeProtected = true; } + + uint64_t ForkStartHeight() const { return nForkStartHeight; }; + uint64_t ForkHeightRange() const { return nForkHeightRange; }; protected: CChainParams() {} @@ -106,6 +109,9 @@ protected: bool fTestnetToBeDeprecatedFieldRPC = false; Checkpoints::CCheckpointData checkpointData; std::vector vFoundersRewardAddress; + + uint64_t nForkStartHeight; + uint64_t nForkHeightRange; }; /** diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index d700666b..3a762c79 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -13,7 +13,7 @@ static const int32_t MIN_TX_VERSION = 1; /** The maximum allowed size for a serialized block, in bytes (network rule) */ static const unsigned int MAX_BLOCK_SIZE = 2000000; /** The maximum allowed number of signature check operations in a block (network rule) */ -static const unsigned int MAX_BLOCK_SIGOPS = 20000; +static const unsigned int MAX_BLOCK_SIGOPS = 50000; /** The maximum size of a transaction (network rule) */ static const unsigned int MAX_TX_SIZE = 100000; /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ diff --git a/src/consensus/params.h b/src/consensus/params.h index 9987f2e7..56ddd242 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -47,9 +47,9 @@ struct Params { int64_t nPowMaxAdjustDown; int64_t nPowMaxAdjustUp; int64_t nPowTargetSpacing; - int64_t AveragingWindowTimespan() const { return nPowAveragingWindow * nPowTargetSpacing; } - int64_t MinActualTimespan() const { return (AveragingWindowTimespan() * (100 - nPowMaxAdjustUp )) / 100; } - int64_t MaxActualTimespan() const { return (AveragingWindowTimespan() * (100 + nPowMaxAdjustDown)) / 100; } + int64_t AveragingWindowTimespan(bool isFork = false) const { return nPowAveragingWindow * nPowTargetSpacing / (isFork ? 20 : 1); } + int64_t MinActualTimespan(bool isFork = false) const { return (AveragingWindowTimespan(isFork) * (100 - nPowMaxAdjustUp )) / 100; } + int64_t MaxActualTimespan(bool isFork = false) const { return (AveragingWindowTimespan(isFork) * (100 + nPowMaxAdjustDown)) / 100; } }; } // namespace Consensus diff --git a/src/init.cpp b/src/init.cpp index 4ccc87c2..97369f16 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -898,8 +898,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #ifdef FORK_CB_INPUT forkUtxoPath = GetArg("-utxo-path", ""); - forkStartHeight = GetArg("-fork-startheight", FORK_BLOCK_HEIGHT_START); - forkHeightRange = GetArg("-fork-heightrange", FORK_BLOCK_HEIGHT_RANGE); + forkStartHeight = GetArg("-fork-startheight", chainparams.ForkStartHeight()); + forkHeightRange = GetArg("-fork-heightrange", chainparams.ForkHeightRange()); forkCBPerBlock = GetArg("-fork-cbperblock", FORK_COINBASE_PER_BLOCK); #endif diff --git a/src/main.cpp b/src/main.cpp index fbb9fcac..1327c7cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -76,17 +76,19 @@ bool fAlerts = DEFAULT_ALERTS; #include std::string forkUtxoPath; -int64_t forkStartHeight = FORK_BLOCK_HEIGHT_START; -int64_t forkHeightRange = FORK_BLOCK_HEIGHT_RANGE; -int64_t forkCBPerBlock = FORK_COINBASE_PER_BLOCK; +int64_t forkStartHeight; +int64_t forkHeightRange; +int64_t forkCBPerBlock; +uint256 forkExtraHashSentinel = uint256S("f0f0f0f0fafafafaffffffffffffffffffffffffffffffffafafafaf0f0f0f0f"); +uint256 hashPid = GetRandHash(); std::string GetUTXOFileName(int nHeight) { - boost::filesystem::path utxo_path(forkUtxoPath); + boost::filesystem::path utxo_path(forkUtxoPath); if (utxo_path.empty() || !utxo_path.has_filename()) { LogPrintf("GetUTXOFileName(): UTXO path is not specified, add utxo-path= to your btcprivate.conf and restart"); - return ""; + return ""; } std::stringstream ss; @@ -1403,19 +1405,11 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString()); } -#ifdef FORK_CB_INPUT - if (!isForkBlock(nHeight)) { //when block is in fork region - don't check Solution and PoW -#endif - // Check the header if (!(CheckEquihashSolution(&block, Params()) && CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus()))) return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); -#ifdef FORK_CB_INPUT - } -#endif - return true; } @@ -1937,7 +1931,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex // Check that all outputs are available and match the outputs in the block itself // exactly. - { + { CCoinsModifier outs = view.ModifyCoins(hash); outs->ClearUnspendable(); @@ -2571,6 +2565,10 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * mempool.check(pcoinsTip); // Update chainActive & related variables. UpdateTip(pindexNew); + LogPrintf("NEWTIP %s %d %d %s %s\n", + hashPid.ToString(), pindexNew->nHeight, GetTime(), + pblock->vtx[0].vin[0].scriptSig.ToString(), pblock->GetHash().ToString()); + // Tell wallet about transactions that went from mempool // to conflicted: BOOST_FOREACH(const CTransaction &tx, txConflicted) { @@ -3030,10 +3028,6 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f return state.DoS(100, error("CheckBlockHeader(): block version too low"), REJECT_INVALID, "version-too-low"); -#ifdef FORK_CB_INPUT - if (!isTipInForkRange()) { //when in FORK mode (tip is in forking region) - don't check Solution and PoW -#endif - // Check Equihash solution is valid if (fCheckPOW && !CheckEquihashSolution(&block, Params())) return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"), @@ -3044,10 +3038,6 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f return state.DoS(50, error("CheckBlockHeader(): proof of work failed"), REJECT_INVALID, "high-hash"); -#ifdef FORK_CB_INPUT - } -#endif - // Check timestamp if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"), @@ -3097,20 +3087,22 @@ bool CheckBlock(const CBlock& block, CValidationState& state, return state.DoS(100, error("CheckBlock(): first tx is not coinbase"), REJECT_INVALID, "bad-cb-missing"); -#ifdef FORK_CB_INPUT - if (isTipInForkRange()) { //when in FORK mode (tip is in forking region) blocks might have up to fork pre-defined value coinbases + //fork blocks might have up to fork pre-defined value coinbases and nothing else + if (looksLikeForkBlockHeader(block)) { if (block.vtx.size() > forkCBPerBlock) - return state.DoS(100, error("CheckBlock(): it is forking block but there are more than %d coinbase txns", forkCBPerBlock), - REJECT_INVALID, "bad-cb-multiple"); + return state.DoS(100, error("CheckBlock(): fork block: too many txns %d > %d coinbase txns", block.vtx.size(), forkCBPerBlock), + REJECT_INVALID, "bad-fork-too-many-tx"); + + for (unsigned int i = 1; i < block.vtx.size(); i++) + if (!block.vtx[i].IsCoinBase()) + return state.DoS(100, error("CheckBlock(): fork block: non-coinbase found"), + REJECT_INVALID, "bad-fork-non-cb"); } else { -#endif for (unsigned int i = 1; i < block.vtx.size(); i++) if (block.vtx[i].IsCoinBase()) return state.DoS(100, error("CheckBlock(): more than one coinbase"), REJECT_INVALID, "bad-cb-multiple"); -#ifdef FORK_CB_INPUT } -#endif // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -3141,16 +3133,21 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta int nHeight = pindexPrev->nHeight+1; + // because we bypass checks using the indicia in the header + // we reject any blocks that look like fork blocks but really + // are non-fork blocks + if(looksLikeForkBlockHeader(block) && !isForkBlock(nHeight)) + return state.DoS(100, error("%s: non-fork block looks like fork block", __func__), + REJECT_INVALID, "bad-fork-hashreserved"); + + if(!looksLikeForkBlockHeader(block) && isForkBlock(nHeight)) + return state.DoS(100, error("%s: fork block does not look like fork block", __func__), + REJECT_INVALID, "bad-fork-hashreserved"); + // Check proof of work -#ifdef FORK_CB_INPUT - if (!isForkBlock(nHeight)) { //If current block is FORK, don't check work required -#endif if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) return state.DoS(100, error("%s: incorrect proof of work", __func__), REJECT_INVALID, "bad-diffbits"); -#ifdef FORK_CB_INPUT - } -#endif // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) @@ -3305,7 +3302,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!if_utxo.is_open()) { LogPrintf("AcceptBlock(): FORK Block - Cannot open UTXO file - %s\n", utxo_file_path); } else { - LogPrintf("AcceptBlock(): FORK Block - Validating block - %u with UTXO file - %s\n", nHeight, utxo_file_path); + LogPrintf("AcceptBlock(): FORK Block - Validating block - %u / %s with UTXO file - %s\n", + nHeight, block.GetHash().ToString(), utxo_file_path); vector > txFromFile; txFromFile.reserve(forkCBPerBlock); @@ -3337,7 +3335,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, } unsigned char* pks = (unsigned char*)pubKeyScript.get(); CScript script = CScript(pks, pks+pbsize); - + txFromFile.push_back(make_pair(amount, script)); if (!if_utxo.read(&term, 1)) { @@ -3359,7 +3357,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, REJECT_INVALID, "bad-fork-block"); pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); - return false; + return false; } int txid = 0; @@ -3477,7 +3475,7 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block, state, pindexPrev)) return false; - + if (!CheckBlock(block, state, verifier, fCheckPOW, fCheckMerkleRoot)) return false; if (!ContextualCheckBlock(block, state, pindexPrev)) @@ -3665,11 +3663,7 @@ bool static LoadBlockIndexDB() { const CChainParams& chainparams = Params(); -#ifdef FORK_CB_INPUT - if (!pblocktree->LoadBlockIndexGuts(forkStartHeight, forkStartHeight+forkHeightRange)) -#else if (!pblocktree->LoadBlockIndexGuts()) -#endif return false; boost::this_thread::interruption_point(); diff --git a/src/main.h b/src/main.h index ab77e76f..b9fd7197 100644 --- a/src/main.h +++ b/src/main.h @@ -162,11 +162,11 @@ void RegisterNodeSignals(CNodeSignals& nodeSignals); /** Unregister a network node */ void UnregisterNodeSignals(CNodeSignals& nodeSignals); -/** +/** * Process an incoming block. This only returns after the best known valid * block is made active. Note that it does not, however, guarantee that the * specific block passed to it has been checked for validity! - * + * * @param[out] state This may be set to an Error state if any error occurred processing it, including during validation/connection/etc of otherwise unrelated blocks during reorganisation; or it may be set to an Invalid state if pblock is itself invalid (but this is not guaranteed even when the block is checked). If you want to *possibly* get feedback on whether pblock is valid, you must also install a CValidationInterface (see validationinterface.h) - this will have its BlockChecked method called whenever *any* block completes validation. * @param[in] pfrom The node which we are receiving the block from; it is added to mapBlockSource and may be penalised if the block is invalid. * @param[in] pblock The block we want to process. @@ -295,7 +295,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF /** * Check transaction inputs, and make sure any * pay-to-script-hash transactions are evaluating IsStandard scripts - * + * * Why bother? To avoid denial-of-service attacks; an attacker * can submit a standard HASH... OP_EQUAL transaction, * which will get accepted into blocks. The redemption @@ -304,14 +304,14 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF * DUP CHECKSIG DROP ... repeated 100 times... OP_1 */ -/** +/** * Check for standard transaction types * @param[in] mapInputs Map of previous transactions that have outputs we're spending * @return True if all inputs (scriptSigs) use only standard transaction forms */ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); -/** +/** * Count ECDSA signature operations the old-fashioned (pre-0.6) way * @return number of sigops this transaction's outputs will produce when spent * @see CTransaction::FetchInputs @@ -320,7 +320,7 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx); /** * Count ECDSA signature operations in pay-to-script-hash inputs. - * + * * @param[in] mapInputs Map of previous transactions that have outputs we're spending * @return maximum number of sigops required to validate this transaction's inputs * @see CTransaction::FetchInputs @@ -364,9 +364,9 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime); */ bool CheckFinalTx(const CTransaction &tx, int flags = -1); -/** +/** * Closure representing one script verification - * Note that this stores references to the spending transaction + * Note that this stores references to the spending transaction */ class CScriptCheck { @@ -542,14 +542,13 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins } #ifdef FORK_CB_INPUT -#define FORK_BLOCK_HEIGHT_START 1000000 //current ZCL height is 200K-300K, this value here is placeholder, it will have to be changed to correct fork block height -#define FORK_BLOCK_HEIGHT_RANGE 65000 #define FORK_COINBASE_PER_BLOCK 10000 extern std::string forkUtxoPath; extern int64_t forkStartHeight; extern int64_t forkHeightRange; extern int64_t forkCBPerBlock; +extern uint256 forkExtraHashSentinel; std::string GetUTXOFileName(int nHeight); @@ -586,17 +585,15 @@ inline bool isForkBlock(int nHeight) { return (nHeight > forkStartHeight && nHeight <= forkStartHeight + forkHeightRange); } -inline bool isTipInForkRange() + +inline bool looksLikeForkBlockHeader(const CBlockHeader& header) { - return chainActive.Tip()? isForkBlock(chainActive.Tip()->nHeight): false; -} -inline bool isNextTipInForkRange() -{ - return chainActive.Tip()? isForkBlock(chainActive.Tip()->nHeight + 1): false; + return header.hashReserved == forkExtraHashSentinel; } + inline uint64_t bytes2uint64(char *array) { - uint64_t x = + uint64_t x = static_cast(array[0]) & 0x00000000000000ff | static_cast(array[1]) << 8 & 0x000000000000ff00 | static_cast(array[2]) << 16 & 0x0000000000ff0000 | @@ -609,4 +606,6 @@ inline uint64_t bytes2uint64(char *array) } #endif +extern uint256 hashPid; + #endif // BITCOIN_MAIN_H diff --git a/src/miner.cpp b/src/miner.cpp index 65f9b6f9..396b78a1 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -114,72 +114,120 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, #ifdef FORK_CB_INPUT CBlockTemplate* CreateNewForkBlock(bool& bFileNotFound) { - CBlockIndex* pindexPrev = chainActive.Tip(); - const int nHeight = pindexPrev->nHeight + 1; + /* + Because CreateNewForkBlock does file io when + reading utxos, we grab the main lock to peek + at the tip, release it to read the file and + fill in the template and then reacquire it + at the end to check if the active tip changed + while we were doing the above + */ + + CBlockTemplate * ret; + int tipHeight; + int snappedHeight; + const CChainParams& chainparams = Params(); + + { + LOCK(cs_main); + tipHeight = chainActive.Tip()->nHeight; + } + + do + { + snappedHeight = tipHeight; + ret = CreateNewForkBlock(bFileNotFound, snappedHeight + 1); + + { + LOCK(cs_main); + CBlockIndex* pindexPrev = chainActive.Tip(); + CBlock *pblock = &ret->block; // pointer for convenience + + tipHeight = pindexPrev->nHeight; + if(tipHeight != snappedHeight) + { + LogPrintf("WARN: tip changed from %u to %u while generating block template\n", + snappedHeight, tipHeight); + continue; + } + + // if we are good then fill in the final details + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); + + CValidationState state; + if (!TestBlockValidity(state, *pblock, pindexPrev, false, false)) + throw std::runtime_error("CreateNewForkBlock(): TestBlockValidity failed"); + } + } while(snappedHeight != tipHeight); + + return ret; +} + +CBlockTemplate* CreateNewForkBlock(bool& bFileNotFound, const int nHeight) +{ + const CChainParams& chainparams = Params(); + + const int nForkHeight = nHeight - forkStartHeight; string utxo_file_path = GetUTXOFileName(nHeight); - std::ifstream if_utxo(utxo_file_path, std::ios::binary | std::ios::in); if (!if_utxo.is_open()) { bFileNotFound = true; - LogPrintf("CreateNewForkBlock(): Cannot open UTXO file - %s\n", utxo_file_path); + LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: Cannot open UTXO file - %s\n", + nHeight, nForkHeight, forkHeightRange, utxo_file_path); return NULL; } - const CChainParams& chainparams = Params(); // Create new block std::unique_ptr pblocktemplate(new CBlockTemplate()); if(!pblocktemplate.get()) return NULL; CBlock *pblock = &pblocktemplate->block; // pointer for convenience - // -regtest only: allow overriding block.nVersion with - // -blockversion=N to test forking scenarios - if (Params().MineBlocksOnDemand()) - pblock->nVersion = GetArg("-blockversion", pblock->nVersion); - // Largest block you're willing to create: unsigned int nBlockMaxSize = (unsigned int)(MAX_BLOCK_SIZE-1000); - - // Minimum block size you want to create; block will be filled with free transactions - // until there are no more or the block reaches this size: - unsigned int nBlockMinSize = DEFAULT_BLOCK_MIN_SIZE; + uint64_t nBlockTotalAmount = 0; uint64_t nBlockSize = 1000; uint64_t nBlockTx = 0; + uint64_t nBlockSigOps = 100; while (if_utxo && nBlockTx < forkCBPerBlock) { char term = 0; char coin[8] = {}; if (!if_utxo.read(coin, 8)) { - LogPrintf("CreateNewForkBlock(): UTXO file corrupted? - No more data (Amount)\n"); + // the last file may be shorter than forkCBPerBlock + if(!if_utxo.eof() || nForkHeight != forkHeightRange) + LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: UTXO file corrupted? - No more data (Amount)\n", + nHeight, nForkHeight, forkHeightRange); break; } char pubkeysize[8] = {}; if (!if_utxo.read(pubkeysize, 8)) { - LogPrintf("CreateNewForkBlock(): UTXO file corrupted? - Not more data (PubKeyScript size)\n"); + LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: UTXO file corrupted? - Not more data (PubKeyScript size)\n", + nHeight, nForkHeight, forkHeightRange); break; } - + int pbsize = bytes2uint64(pubkeysize); - - //LogPrintf("CreateNewForkBlock():PubKeyScript size = %d\n", pbsize); - if (pbsize == 0) { - LogPrintf("CreateNewForkBlock(): Warning! UTXO file corrupted? PubKeyScript size = 0\n"); + LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: UTXO file corrupted? - PubKeyScript size = 0\n", + nHeight, nForkHeight, forkHeightRange); //but proceed } std::unique_ptr pubKeyScript(new char[pbsize]); if (!if_utxo.read(&pubKeyScript[0], pbsize)) { - LogPrintf("CreateNewForkBlock(): UTXO file corrupted? Not more data (PubKeyScript)\n"); + LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: UTXO file corrupted? Not more data (PubKeyScript)\n", + nHeight, nForkHeight, forkHeightRange); break; } uint64_t amount = bytes2uint64(coin); - //LogPrintf("CreateNewForkBlock(): txn %u has ammount %u\n", nBlockTx, amount); // Add coinbase tx's CMutableTransaction txNew; @@ -190,34 +238,45 @@ CBlockTemplate* CreateNewForkBlock(bool& bFileNotFound) unsigned char* pks = (unsigned char*)pubKeyScript.get(); txNew.vout[0].scriptPubKey = CScript(pks, pks+pbsize); - //LogPrintf("CreateNewForkBlock(): ScriptPubKey: %s\n", txNew.vout[0].scriptPubKey.ToString()); - - txNew.vout[0].nValue = amount; - txNew.vin[0].scriptSig = CScript() << nHeight+nBlockTx << OP_0; + if(nBlockTx == 0) + txNew.vin[0].scriptSig = CScript() << nHeight << CScriptNum(nBlockTx) << ToByteVector(hashPid) << OP_0; + else + txNew.vin[0].scriptSig = CScript() << nHeight << CScriptNum(nBlockTx) << OP_0; unsigned int nTxSize = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); if (nBlockSize + nTxSize >= nBlockMaxSize) - break;//We cannot skip transaction here as in regular case - or we will loose that skipped transaction - - pblock->vtx.push_back(txNew); - pblocktemplate->vTxFees.push_back(-1); // updated at end - pblocktemplate->vTxSigOps.push_back(-1); // updated at end - nBlockSize += nTxSize; - ++nBlockTx; - - - if (!if_utxo.read(&term, 1)) { - LogPrintf("CreateNewForkBlock(): No more data (record separator)\n"); + { + LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: %u: block would exceed max size\n", + nHeight, nForkHeight, forkHeightRange, nBlockTx); break; } - if (term != '\n') { - //This maybe not an error, but warning none the less - LogPrintf("CreateNewForkBlock(): Warning! No record separator ('0xA') was found\n"); - if_utxo.seekg(-1, ios_base::cur); //move one char back - if it is not a separator, maybe there is not separators at all + + // Legacy limits on sigOps: + unsigned int nTxSigOps = GetLegacySigOpCount(txNew); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + { + LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: %u: block would exceed max sigops\n", + nHeight, nForkHeight, forkHeightRange, nBlockTx); + break; + } + + pblock->vtx.push_back(txNew); + pblocktemplate->vTxFees.push_back(0); + pblocktemplate->vTxSigOps.push_back(nTxSigOps); + nBlockSize += nTxSize; + nBlockSigOps += nTxSigOps; + nBlockTotalAmount += amount; + ++nBlockTx; + + if (!if_utxo.read(&term, 1) || term != '\n') { + LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: invalid record separator\n", + nHeight, nForkHeight, forkHeightRange); + break; } } - LogPrintf("CreateNewForkBlock(): %u tnxs and total size %u\n", nBlockTx, nBlockSize); + LogPrintf("CreateNewForkBlock(): [%u, %u of %u]: txns=%u size=%u amount=%u sigops=%u\n", + nHeight, nForkHeight, forkHeightRange, nBlockTx, nBlockSize, nBlockTotalAmount, nBlockSigOps); // Randomise nonce arith_uint256 nonce = UintToArith256(GetRandHash()); @@ -227,22 +286,8 @@ CBlockTemplate* CreateNewForkBlock(bool& bFileNotFound) pblock->nNonce = ArithToUint256(nonce); // Fill in header - pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - pblock->hashReserved = uint256(); - UpdateTime(pblock, Params().GetConsensus(), pindexPrev); - pblock->nBits = 0x207fffff; // Difficulty = 1 - //0x207fffff -> 0x 7fffff00 00000000 00000000 00000000 00000000 00000000 00000000 00000000 - //alternativly - //0x1d00ffff -> 0x 00000000 ffff0000 00000000 00000000 00000000 00000000 00000000 00000000 - // or - //0x1f07ffff -> 0x 007fffff 00000000 00000000 00000000 00000000 00000000 00000000 00000000 - + pblock->hashReserved = forkExtraHashSentinel; pblock->nSolution.clear(); - pblocktemplate->vTxSigOps[0] = 0; //NO SigOps - - CValidationState state; - if (!TestBlockValidity(state, *pblock, pindexPrev, false, false)) - throw std::runtime_error("CreateNewForkBlock(): TestBlockValidity failed"); return pblocktemplate.release(); } @@ -593,13 +638,10 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese static bool ProcessBlockFound(CBlock* pblock) #endif // ENABLE_WALLET { -#ifdef FORK_CB_INPUT - if (!isNextTipInForkRange()) { -#endif - LogPrintf("%s\n", pblock->ToString()); -#ifdef FORK_CB_INPUT + if (!looksLikeForkBlockHeader(*pblock)) { + LogPrintf("%s\n", pblock->ToString()); } -#endif + LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue)); // Found a solution @@ -642,7 +684,7 @@ void static BitcoinMiner(CWallet *pwallet) void static BitcoinMiner() #endif { - LogPrintf("BTCPrivate Miner started\n"); + LogPrintf("BTCPrivate Miner started %s\n"); SetThreadPriority(THREAD_PRIORITY_LOWEST); RenameThread("btcp-miner"); const CChainParams& chainparams = Params(); @@ -672,10 +714,7 @@ void static BitcoinMiner() ); miningTimer.start(); -#ifdef FORK_CB_INPUT bool bForkModeStarted = false; -#endif - try { while (true) { if (chainparams.MiningRequiresPeers()) { @@ -704,8 +743,19 @@ void static BitcoinMiner() // unique_ptr pblocktemplate; -#ifdef FORK_CB_INPUT bool isNextBlockFork = isForkBlock(pindexPrev->nHeight+1); + + bool mineOnlyFork = GetBoolArg("-forkmineonly", false); + if(mineOnlyFork && !isNextBlockFork) { + miningTimer.stop(); + do { + MilliSleep(100); + pindexPrev = chainActive.Tip(); + isNextBlockFork = isForkBlock(pindexPrev->nHeight + 1); + } while(!isNextBlockFork); + miningTimer.start(); + } + if (isNextBlockFork) { if (!bForkModeStarted) { LogPrintf("BTCPrivate Miner: switching into fork mode\n"); @@ -717,28 +767,26 @@ void static BitcoinMiner() if (!pblocktemplate.get()) { if (bFileNotFound) { MilliSleep(1000); - continue; + continue; } else { LogPrintf("Error in BTCPrivate Miner: Cannot create Fork Block\n"); return; } } pblock = &pblocktemplate->block; + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - n = 48; k = 5; - - LogPrintf("Running BTCPrivate Miner with %u forking transactions in block (%u bytes) and N = %d, K = %d\n", pblock->vtx.size(), - ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION), - n, k); + LogPrintf("Running BTCPrivate Miner with %u forking transactions in block (%u bytes) and N = %d, K = %d\n", + pblock->vtx.size(), + ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION), + n, k); } else { //if not in forking mode and/or provided file is read to the end - exit if (bForkModeStarted) { LogPrintf("BTCPrivate Miner: Fork is done - switching back to regular miner\n"); - n = chainparams.EquihashN(); - k = chainparams.EquihashK(); bForkModeStarted = false; } -#endif + nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); #ifdef ENABLE_WALLET @@ -759,13 +807,12 @@ void static BitcoinMiner() pblock = &pblocktemplate->block; LogPrintf("Running BTCPrivate Miner with %u transactions in block (%u bytes)\n", pblock->vtx.size(), - ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); + ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); -#ifdef FORK_CB_INPUT + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); } //else -#endif - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + // // Search @@ -798,38 +845,31 @@ void static BitcoinMiner() solver, pblock->nNonce.ToString()); std::function)> validBlock = - - [&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams + [&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams #ifdef ENABLE_WALLET - , &pwallet, &reservekey + , &pwallet, &reservekey #endif -#ifdef FORK_CB_INPUT - , &isNextBlockFork -#endif - ] (std::vector soln) { + , &isNextBlockFork] (std::vector soln) { // Write the solution to the hash and compute the result. LogPrint("pow", "- Checking solution against target\n"); pblock->nSolution = soln; solutionTargetChecks.increment(); -#ifdef FORK_CB_INPUT - if (!isNextBlockFork) { -#endif if (UintToArith256(pblock->GetHash()) > hashTarget) { return false; } -#ifdef FORK_CB_INPUT - } -#endif + // Found a solution SetThreadPriority(THREAD_PRIORITY_NORMAL); LogPrintf("BTCPrivate Miner:\n"); LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex()); + + if (ProcessBlockFound(pblock #ifdef ENABLE_WALLET - if (ProcessBlockFound(pblock, *pwallet, reservekey)) { -#else - if (ProcessBlockFound(pblock)) { + , *pwallet, reservekey #endif + )) { + // Ignore chain updates caused by us std::lock_guard lock{m_cs}; cancelSolver = false; @@ -837,7 +877,7 @@ void static BitcoinMiner() SetThreadPriority(THREAD_PRIORITY_LOWEST); // In regression test mode, stop mining after a block is found. - if (chainparams.MineBlocksOnDemand()) { + if (chainparams.MineBlocksOnDemand() && !isNextBlockFork) { // Increment here because throwing skips the call below ehSolverRuns.increment(); throw boost::thread_interrupted(); @@ -845,6 +885,7 @@ void static BitcoinMiner() return true; }; + std::function cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) { std::lock_guard lock{m_cs}; return cancelSolver; @@ -901,20 +942,13 @@ void static BitcoinMiner() // Check for stop or if block needs to be rebuilt boost::this_thread::interruption_point(); -#ifdef FORK_CB_INPUT - if (!isNextBlockFork) { -#endif - // Regtest mode doesn't require peers - if (vNodes.empty() && chainparams.MiningRequiresPeers()) - break; - if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff) - break; - if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) - break; -#ifdef FORK_CB_INPUT - } -#endif - + // Regtest mode doesn't require peers + if (vNodes.empty() && chainparams.MiningRequiresPeers()) + break; + if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff) + break; + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) + break; if (pindexPrev != chainActive.Tip()) break; @@ -922,17 +956,11 @@ void static BitcoinMiner() pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); -#ifdef FORK_CB_INPUT - if (!isNextBlockFork) { -#endif - if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks) - { - // Changing pblock->nTime can change work required on testnet: - hashTarget.SetCompact(pblock->nBits); - } -#ifdef FORK_CB_INPUT + if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks) + { + // Changing pblock->nTime can change work required on testnet: + hashTarget.SetCompact(pblock->nBits); } -#endif } } } diff --git a/src/miner.h b/src/miner.h index 1ae86dca..9b4f7811 100644 --- a/src/miner.h +++ b/src/miner.h @@ -50,7 +50,8 @@ void GenerateBitcoins(bool fGenerate, int nThreads); void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); #ifdef FORK_CB_INPUT -CBlockTemplate* CreateNewForkBlock(); +CBlockTemplate* CreateNewForkBlock(bool&); +CBlockTemplate* CreateNewForkBlock(bool&,const int); #endif #endif // BITCOIN_MINER_H diff --git a/src/pow.cpp b/src/pow.cpp index 83e83e9e..69fff400 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -9,6 +9,7 @@ #include "chain.h" #include "chainparams.h" #include "crypto/equihash.h" +#include "main.h" #include "primitives/block.h" #include "streams.h" #include "uint256.h" @@ -28,6 +29,10 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead if (pindexLast == NULL) return nProofOfWorkLimit; + // right post fork + else if(!isForkBlock(pindexLast->nHeight + 1) && isForkBlock(pindexLast->nHeight + 1 - params.nPowAveragingWindow)) + return nProofOfWorkLimit; + // Find the first block in the averaging interval const CBlockIndex* pindexFirst = pindexLast; arith_uint256 bnTot {0}; @@ -44,29 +49,30 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead arith_uint256 bnAvg {bnTot / params.nPowAveragingWindow}; - return CalculateNextWorkRequired(bnAvg, pindexLast->GetMedianTimePast(), pindexFirst->GetMedianTimePast(), params); + bool isFork = isForkBlock(pindexLast->nHeight + 1); + return CalculateNextWorkRequired(bnAvg, pindexLast->GetMedianTimePast(), pindexFirst->GetMedianTimePast(), params, isFork); } unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg, int64_t nLastBlockTime, int64_t nFirstBlockTime, - const Consensus::Params& params) + const Consensus::Params& params, bool isFork) { // Limit adjustment step // Use medians to prevent time-warp attacks int64_t nActualTimespan = nLastBlockTime - nFirstBlockTime; LogPrint("pow", " nActualTimespan = %d before dampening\n", nActualTimespan); - nActualTimespan = params.AveragingWindowTimespan() + (nActualTimespan - params.AveragingWindowTimespan())/4; + nActualTimespan = params.AveragingWindowTimespan(isFork) + (nActualTimespan - params.AveragingWindowTimespan(isFork))/4; LogPrint("pow", " nActualTimespan = %d before bounds\n", nActualTimespan); - if (nActualTimespan < params.MinActualTimespan()) - nActualTimespan = params.MinActualTimespan(); - if (nActualTimespan > params.MaxActualTimespan()) - nActualTimespan = params.MaxActualTimespan(); + if (nActualTimespan < params.MinActualTimespan(isFork)) + nActualTimespan = params.MinActualTimespan(isFork); + if (nActualTimespan > params.MaxActualTimespan(isFork)) + nActualTimespan = params.MaxActualTimespan(isFork); // Retarget const arith_uint256 bnPowLimit = UintToArith256(params.powLimit); arith_uint256 bnNew {bnAvg}; - bnNew /= params.AveragingWindowTimespan(); + bnNew /= params.AveragingWindowTimespan(isFork); bnNew *= nActualTimespan; if (bnNew > bnPowLimit) diff --git a/src/pow.h b/src/pow.h index 30d0f5e6..bd88c13a 100644 --- a/src/pow.h +++ b/src/pow.h @@ -19,7 +19,7 @@ class arith_uint256; unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params&); unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg, int64_t nLastBlockTime, int64_t nFirstBlockTime, - const Consensus::Params&); + const Consensus::Params&, bool isFork = false); /** Check whether the Equihash solution in a block header is valid */ bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams&); diff --git a/src/txdb.cpp b/src/txdb.cpp index 3143cab1..8ad9ffc9 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -272,11 +272,7 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { return true; } -#ifdef FORK_CB_INPUT -bool CBlockTreeDB::LoadBlockIndexGuts(int64_t forkStart, int64_t forkStop) -#else bool CBlockTreeDB::LoadBlockIndexGuts() -#endif { boost::scoped_ptr pcursor(NewIterator()); @@ -308,6 +304,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->hashAnchor = diskindex.hashAnchor; pindexNew->nVersion = diskindex.nVersion; pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; + pindexNew->hashReserved = diskindex.hashReserved; pindexNew->nTime = diskindex.nTime; pindexNew->nBits = diskindex.nBits; pindexNew->nNonce = diskindex.nNonce; @@ -315,14 +312,8 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; -#ifdef FORK_CB_INPUT - if (pindexNew->nHeight < forkStart || pindexNew->nHeight >= forkStop) { -#endif if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus())) return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); -#ifdef FORK_CB_INPUT - } -#endif pcursor->Next(); } else { diff --git a/src/txdb.h b/src/txdb.h index a1f86d09..d02f3c9e 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -68,12 +68,7 @@ public: bool WriteFlag(const std::string &name, bool fValue); bool ReadFlag(const std::string &name, bool &fValue); -#ifdef FORK_CB_INPUT - bool LoadBlockIndexGuts(int64_t forkStart, int64_t forkStop); -#else bool LoadBlockIndexGuts(); -#endif - }; #endif // BITCOIN_TXDB_H From 6a9c62705aa98bbd8ec081871877c65438f2aca3 Mon Sep 17 00:00:00 2001 From: sken Date: Thu, 15 Feb 2018 01:31:23 -0600 Subject: [PATCH 06/12] add windows cross compiling instructions --- README.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f29221fa..3cb0be9f 100644 --- a/README.md +++ b/README.md @@ -35,20 +35,63 @@ Install: ``` ### Windows -Get dependencies: +There are two proven ways to build BTCP for Windows: + +* On Linux using [Mingw-w64](https://mingw-w64.org/doku.php) cross compiler tool chain. Ubuntu 16.04 Xenial is proven to work and the instructions is for such release. +* On Windows 10 (64-bit version) using [Windows Subsystem for Linux (WSL)](https://msdn.microsoft.com/commandline/wsl/about) and Mingw-w64 cross compiler tool chain. + +With Windows 10, Microsoft released a feature called WSL. It basically allows you to run a bash shell directly on Windows in an ubuntu environment. WSL can be installed with other Linux variants, but as mentioned before, the distro proven to work is Ubuntu. +Follow this [link](https://msdn.microsoft.com/en-us/commandline/wsl/install_guide) for installing WSL first + +### Building for Windows 64-Bit +1. Get the usual dependencies: ```{r, engine='bash'} sudo apt-get install \ build-essential pkg-config libc6-dev m4 g++-multilib \ autoconf libtool ncurses-dev unzip git python \ - zlib1g-dev wget bsdmainutils automake mingw-w64 + zlib1g-dev wget bsdmainutils automake ``` -Install (Cross-Compiled, building on Windows is not supported yet): +2. Set the default ming32 gcc/g++ compiler option to posix + ```{r, engine='bash'} -# Build +sudo apt install mingw-w64 +sudo update-alternatives --config x86_64-w64-mingw32-gcc +sudo update-alternatives --config x86_64-w64-mingw32-g++ +``` + +3. Install Rust +```{r, engine='bash'} +curl https://sh.rustup.rs -sSf | sh +source ~/.cargo/env +rustup install stable-x86_64-unknown-linux-gnu +rustup install stable-x86_64-pc-windows-gnu +rustup target add x86_64-pc-windows-gnu +vi ~/.cargo/config +``` +and add: +``` +[target.x86_64-pc-windows-gnu] +linker = "/usr/bin/x86_64-w64-mingw32-gcc" +``` + +Note that in WSL, the BTCPrivate source code must be somewhere in the default mount file system. i.e /usr/src/BTCPrivate, and not on /mnt/d/. What this means is that you cannot build directly on the windows system + +4. Build for Windows + +```{r, engine='bash'} +PATH=$(echo "$PATH" | sed -e 's/:\/mnt.*//g') # strip out problematic Windows %PATH% imported var ./btcputil/build-win.sh -j$(nproc) ``` -The exe will save to `src` which you can then move to a windows machine + +5. Installation + +After building in WSL, you can make a copy of the compiled executables to a directory on your Windows file system. This is done the following way + +```{r, engine='bash'} +make install DESTDIR=/mnt/c/btcp/BTCPrivate +``` +This will install the executables to `c:\btcp\BTCPrivate ### Mac Get dependencies: From 0e8f79e8ab8e575f17b126578896da47fa4e04f2 Mon Sep 17 00:00:00 2001 From: sken Date: Thu, 15 Feb 2018 03:18:08 -0600 Subject: [PATCH 07/12] add sha256 checksum line for windows rust --- depends/packages/librustzcash.mk | 10 +++++++--- depends/packages/rust.mk | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/depends/packages/librustzcash.mk b/depends/packages/librustzcash.mk index e27adec2..e86c5505 100644 --- a/depends/packages/librustzcash.mk +++ b/depends/packages/librustzcash.mk @@ -7,13 +7,17 @@ $(package)_sha256_hash=a5760a90d4a1045c8944204f29fa2a3cf2f800afee400f88bf89bbfe2 $(package)_git_commit=91348647a86201a9482ad4ad68398152dc3d635e $(package)_dependencies=rust +# cargo build --release define $(package)_build_cmds - cargo build --release + cargo build --release --lib --target="x86_64-pc-windows-gnu" endef +# cp target/release/librustzcash.a $($(package)_staging_dir)$(host_prefix)/lib/ && \ +# + define $(package)_stage_cmds mkdir $($(package)_staging_dir)$(host_prefix)/lib/ && \ mkdir $($(package)_staging_dir)$(host_prefix)/include/ && \ - cp target/release/librustzcash.a $($(package)_staging_dir)$(host_prefix)/lib/ && \ + cp target/x86_64-pc-windows-gnu/release/rustzcash.lib $($(package)_staging_dir)$(host_prefix)/lib/librustzcash.a && \ cp include/librustzcash.h $($(package)_staging_dir)$(host_prefix)/include/ -endef +endef \ No newline at end of file diff --git a/depends/packages/rust.mk b/depends/packages/rust.mk index f61dd223..2c7a3156 100644 --- a/depends/packages/rust.mk +++ b/depends/packages/rust.mk @@ -2,7 +2,9 @@ package=rust $(package)_version=1.16.0 $(package)_download_path=https://static.rust-lang.org/dist $(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz +$(package)_file_name=rust-$($(package)_version)-x86_64-pc-windows-gnu.tar.gz $(package)_sha256_hash_linux=48621912c242753ba37cad5145df375eeba41c81079df46f93ffb4896542e8fd +$(package)_sha256_hash=523cd248363afdc4e2c0e1f219607897b6925294a33154d7e67224addfd15eb0 $(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz $(package)_sha256_hash_darwin=2d08259ee038d3a2c77a93f1a31fc59e7a1d6d1bbfcba3dba3c8213b2e5d1926 From a24aa1740884e607c55e978f9c65211f083a8e6c Mon Sep 17 00:00:00 2001 From: sken Date: Thu, 15 Feb 2018 17:26:29 -0600 Subject: [PATCH 08/12] add mingw-w64 dependency to read me --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cb0be9f..bdb030fb 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Follow this [link](https://msdn.microsoft.com/en-us/commandline/wsl/install_guid sudo apt-get install \ build-essential pkg-config libc6-dev m4 g++-multilib \ autoconf libtool ncurses-dev unzip git python \ - zlib1g-dev wget bsdmainutils automake + zlib1g-dev wget bsdmainutils automake mingw-w64 ``` 2. Set the default ming32 gcc/g++ compiler option to posix From ecf8c99241030d9a32837daa31f14e1666661837 Mon Sep 17 00:00:00 2001 From: jc Date: Sat, 17 Feb 2018 17:12:26 -0500 Subject: [PATCH 09/12] only exempt fork blocks from coinbase shielding requirement but not maturity limit --- src/chainparams.cpp | 4 ++-- src/main.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index a1957fb1..e8c76274 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -37,7 +37,7 @@ public: CMainParams() { strNetworkID = "main"; strCurrencyUnits = "BTCP"; - consensus.fCoinbaseMustBeProtected = false; + consensus.fCoinbaseMustBeProtected = true; consensus.nSubsidySlowStartInterval = 2; consensus.nSubsidyHalvingInterval = 840000; consensus.nMajorityEnforceBlockUpgrade = 750; @@ -206,7 +206,7 @@ public: CTestNetParams() { strNetworkID = "test"; strCurrencyUnits = "BTCPT"; - consensus.fCoinbaseMustBeProtected = false; + consensus.fCoinbaseMustBeProtected = true; consensus.nMajorityEnforceBlockUpgrade = 51; consensus.nMajorityRejectBlockOutdated = 75; consensus.nMajorityWindow = 400; diff --git a/src/main.cpp b/src/main.cpp index 1327c7cc..f64e7c91 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1696,6 +1696,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins // Ensure that coinbases cannot be spent to transparent outputs // Disabled on regtest if (fCoinbaseEnforcedProtectionEnabled && + !isForkBlock(coins->nHeight) && consensusParams.fCoinbaseMustBeProtected && !tx.vout.empty()) { return state.Invalid( From 3c07498cf04c37fd6a1fb2d1eae2b9f2cc9a7b1e Mon Sep 17 00:00:00 2001 From: floreslorca Date: Tue, 20 Feb 2018 22:44:39 -0600 Subject: [PATCH 10/12] fix rust make files to allow multiple builds --- README.md | 7 +++++-- depends/packages/librustzcash.mk | 15 +++++++++------ depends/packages/rust.mk | 6 +++--- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index bdb030fb..80fa2d2c 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,13 @@ sudo apt-get install \ zlib1g-dev wget bsdmainutils automake mingw-w64 ``` -2. Set the default ming32 gcc/g++ compiler option to posix +2. Set the default ming32 gcc/g++ compiler option to posix, fix problem with packages in Xenial ```{r, engine='bash'} -sudo apt install mingw-w64 +sudo apt install software-properties-common +sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu zesty universe" +sudo apt update +sudo apt upgrade sudo update-alternatives --config x86_64-w64-mingw32-gcc sudo update-alternatives --config x86_64-w64-mingw32-g++ ``` diff --git a/depends/packages/librustzcash.mk b/depends/packages/librustzcash.mk index e86c5505..a1213bd8 100644 --- a/depends/packages/librustzcash.mk +++ b/depends/packages/librustzcash.mk @@ -6,18 +6,21 @@ $(package)_download_file=$($(package)_git_commit).tar.gz $(package)_sha256_hash=a5760a90d4a1045c8944204f29fa2a3cf2f800afee400f88bf89bbfe2cce1279 $(package)_git_commit=91348647a86201a9482ad4ad68398152dc3d635e $(package)_dependencies=rust +ifeq ($(host_os),mingw32) + rust_build=cargo build --release --lib --target="x86_64-pc-windows-gnu" + rust_target=target/x86_64-pc-windows-gnu/release/rustzcash.lib $($(package)_staging_dir)$(host_prefix)/lib/librustzcash.a +else + rust_build=cargo build --release + rust_target=target/release/librustzcash.a $($(package)_staging_dir)$(host_prefix)/lib/ +endif -# cargo build --release define $(package)_build_cmds - cargo build --release --lib --target="x86_64-pc-windows-gnu" + $(rust_build) endef -# cp target/release/librustzcash.a $($(package)_staging_dir)$(host_prefix)/lib/ && \ -# - define $(package)_stage_cmds mkdir $($(package)_staging_dir)$(host_prefix)/lib/ && \ mkdir $($(package)_staging_dir)$(host_prefix)/include/ && \ - cp target/x86_64-pc-windows-gnu/release/rustzcash.lib $($(package)_staging_dir)$(host_prefix)/lib/librustzcash.a && \ + cp $(rust_target) && \ cp include/librustzcash.h $($(package)_staging_dir)$(host_prefix)/include/ endef \ No newline at end of file diff --git a/depends/packages/rust.mk b/depends/packages/rust.mk index 2c7a3156..89b29505 100644 --- a/depends/packages/rust.mk +++ b/depends/packages/rust.mk @@ -2,12 +2,12 @@ package=rust $(package)_version=1.16.0 $(package)_download_path=https://static.rust-lang.org/dist $(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz -$(package)_file_name=rust-$($(package)_version)-x86_64-pc-windows-gnu.tar.gz $(package)_sha256_hash_linux=48621912c242753ba37cad5145df375eeba41c81079df46f93ffb4896542e8fd -$(package)_sha256_hash=523cd248363afdc4e2c0e1f219607897b6925294a33154d7e67224addfd15eb0 $(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz $(package)_sha256_hash_darwin=2d08259ee038d3a2c77a93f1a31fc59e7a1d6d1bbfcba3dba3c8213b2e5d1926 +$(package)_file_name_mingw32=rust-$($(package)_version)-x86_64-pc-windows-gnu.tar.gz +$(package)_sha256_hash_mingw32=523cd248363afdc4e2c0e1f219607897b6925294a33154d7e67224addfd15eb0 define $(package)_stage_cmds ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(host_prefix)/native --disable-ldconfig -endef +endef \ No newline at end of file From 68af4746ce1a05de427249d3cc90300d6dfbe8a9 Mon Sep 17 00:00:00 2001 From: floreslorca Date: Wed, 21 Feb 2018 00:03:11 -0600 Subject: [PATCH 11/12] remove uneccessary steps --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 80fa2d2c..219ecffc 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,6 @@ sudo apt-get install \ 2. Set the default ming32 gcc/g++ compiler option to posix, fix problem with packages in Xenial ```{r, engine='bash'} -sudo apt install software-properties-common -sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu zesty universe" -sudo apt update -sudo apt upgrade sudo update-alternatives --config x86_64-w64-mingw32-gcc sudo update-alternatives --config x86_64-w64-mingw32-g++ ``` From cffc74a8a0bbf144d5cc92c42d9015497a1f6ccb Mon Sep 17 00:00:00 2001 From: floreslorca Date: Wed, 21 Feb 2018 13:58:18 -0600 Subject: [PATCH 12/12] add notes for multiple build --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 219ecffc..35ca7126 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,10 @@ Install: ./src/btcpd ``` +### Additional notes + +If you plan to build for windows and linux at the same time, be sure to delete all the built files for whatever you build first. An easy way to do this is by taking the binaries out of the repo, delete all files except the .git folder and then do a git hard reset. + About --------------