BIP9 Implementation
Inspired by former implementations by Eric Lombrozo and Rusty Russell, and based on code by Jorge Timon.
This commit is contained in:
parent
a6a860796a
commit
6851107b3a
12 changed files with 345 additions and 19 deletions
|
@ -152,6 +152,7 @@ BITCOIN_CORE_H = \
|
||||||
utilmoneystr.h \
|
utilmoneystr.h \
|
||||||
utiltime.h \
|
utiltime.h \
|
||||||
validationinterface.h \
|
validationinterface.h \
|
||||||
|
versionbits.h \
|
||||||
wallet/crypter.h \
|
wallet/crypter.h \
|
||||||
wallet/db.h \
|
wallet/db.h \
|
||||||
wallet/rpcwallet.h \
|
wallet/rpcwallet.h \
|
||||||
|
@ -204,6 +205,7 @@ libbitcoin_server_a_SOURCES = \
|
||||||
txdb.cpp \
|
txdb.cpp \
|
||||||
txmempool.cpp \
|
txmempool.cpp \
|
||||||
validationinterface.cpp \
|
validationinterface.cpp \
|
||||||
|
versionbits.cpp \
|
||||||
$(BITCOIN_CORE_H)
|
$(BITCOIN_CORE_H)
|
||||||
|
|
||||||
if ENABLE_ZMQ
|
if ENABLE_ZMQ
|
||||||
|
|
|
@ -14,8 +14,6 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
|
|
||||||
struct CDiskBlockPos
|
struct CDiskBlockPos
|
||||||
{
|
{
|
||||||
int nFile;
|
int nFile;
|
||||||
|
|
|
@ -81,6 +81,8 @@ public:
|
||||||
consensus.nPowTargetSpacing = 10 * 60;
|
consensus.nPowTargetSpacing = 10 * 60;
|
||||||
consensus.fPowAllowMinDifficultyBlocks = false;
|
consensus.fPowAllowMinDifficultyBlocks = false;
|
||||||
consensus.fPowNoRetargeting = false;
|
consensus.fPowNoRetargeting = false;
|
||||||
|
consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
|
||||||
|
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
||||||
/**
|
/**
|
||||||
* The message start string is designed to be unlikely to occur in normal data.
|
* The message start string is designed to be unlikely to occur in normal data.
|
||||||
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
|
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
|
||||||
|
@ -162,6 +164,8 @@ public:
|
||||||
consensus.nPowTargetSpacing = 10 * 60;
|
consensus.nPowTargetSpacing = 10 * 60;
|
||||||
consensus.fPowAllowMinDifficultyBlocks = true;
|
consensus.fPowAllowMinDifficultyBlocks = true;
|
||||||
consensus.fPowNoRetargeting = false;
|
consensus.fPowNoRetargeting = false;
|
||||||
|
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
|
||||||
|
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
||||||
pchMessageStart[0] = 0x0b;
|
pchMessageStart[0] = 0x0b;
|
||||||
pchMessageStart[1] = 0x11;
|
pchMessageStart[1] = 0x11;
|
||||||
pchMessageStart[2] = 0x09;
|
pchMessageStart[2] = 0x09;
|
||||||
|
@ -225,6 +229,8 @@ public:
|
||||||
consensus.nPowTargetSpacing = 10 * 60;
|
consensus.nPowTargetSpacing = 10 * 60;
|
||||||
consensus.fPowAllowMinDifficultyBlocks = true;
|
consensus.fPowAllowMinDifficultyBlocks = true;
|
||||||
consensus.fPowNoRetargeting = true;
|
consensus.fPowNoRetargeting = true;
|
||||||
|
consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains
|
||||||
|
consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016)
|
||||||
|
|
||||||
pchMessageStart[0] = 0xfa;
|
pchMessageStart[0] = 0xfa;
|
||||||
pchMessageStart[1] = 0xbf;
|
pchMessageStart[1] = 0xbf;
|
||||||
|
|
|
@ -7,8 +7,28 @@
|
||||||
#define BITCOIN_CONSENSUS_PARAMS_H
|
#define BITCOIN_CONSENSUS_PARAMS_H
|
||||||
|
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace Consensus {
|
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.
|
* Parameters that influence chain consensus.
|
||||||
*/
|
*/
|
||||||
|
@ -22,6 +42,14 @@ struct Params {
|
||||||
/** Block height and hash at which BIP34 becomes active */
|
/** Block height and hash at which BIP34 becomes active */
|
||||||
int BIP34Height;
|
int BIP34Height;
|
||||||
uint256 BIP34Hash;
|
uint256 BIP34Hash;
|
||||||
|
/**
|
||||||
|
* 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 */
|
/** Proof of work parameters */
|
||||||
uint256 powLimit;
|
uint256 powLimit;
|
||||||
bool fPowAllowMinDifficultyBlocks;
|
bool fPowAllowMinDifficultyBlocks;
|
||||||
|
|
|
@ -466,7 +466,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||||
strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE));
|
strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE));
|
||||||
strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE));
|
strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE));
|
||||||
if (showDebug)
|
if (showDebug)
|
||||||
strUsage += HelpMessageOpt("-blockversion=<n>", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION));
|
strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios");
|
||||||
|
|
||||||
strUsage += HelpMessageGroup(_("RPC server options:"));
|
strUsage += HelpMessageGroup(_("RPC server options:"));
|
||||||
strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands"));
|
strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands"));
|
||||||
|
|
76
src/main.cpp
76
src/main.cpp
|
@ -34,6 +34,7 @@
|
||||||
#include "utilmoneystr.h"
|
#include "utilmoneystr.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
#include "validationinterface.h"
|
#include "validationinterface.h"
|
||||||
|
#include "versionbits.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
@ -2083,6 +2084,51 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<int64_t>::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 nTimeCheck = 0;
|
static int64_t nTimeCheck = 0;
|
||||||
static int64_t nTimeForks = 0;
|
static int64_t nTimeForks = 0;
|
||||||
static int64_t nTimeVerify = 0;
|
static int64_t nTimeVerify = 0;
|
||||||
|
@ -2452,27 +2498,45 @@ void static UpdateTip(CBlockIndex *pindexNew) {
|
||||||
|
|
||||||
// Check the version of the last 100 blocks to see if we need to upgrade:
|
// Check the version of the last 100 blocks to see if we need to upgrade:
|
||||||
static bool fWarned = false;
|
static bool fWarned = false;
|
||||||
if (!IsInitialBlockDownload() && !fWarned)
|
if (!IsInitialBlockDownload())
|
||||||
{
|
{
|
||||||
int nUpgraded = 0;
|
int nUpgraded = 0;
|
||||||
const CBlockIndex* pindex = chainActive.Tip();
|
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++)
|
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;
|
++nUpgraded;
|
||||||
pindex = pindex->pprev;
|
pindex = pindex->pprev;
|
||||||
}
|
}
|
||||||
if (nUpgraded > 0)
|
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)
|
if (nUpgraded > 100/2)
|
||||||
{
|
{
|
||||||
// strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
|
// strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
|
||||||
strMiscWarning = _("Warning: This version is obsolete; upgrade required!");
|
strMiscWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect");
|
||||||
|
if (!fWarned) {
|
||||||
CAlert::Notify(strMiscWarning, true);
|
CAlert::Notify(strMiscWarning, true);
|
||||||
fWarned = true;
|
fWarned = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */
|
/** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */
|
||||||
bool static DisconnectTip(CValidationState& state, const Consensus::Params& consensusParams)
|
bool static DisconnectTip(CValidationState& state, const Consensus::Params& consensusParams)
|
||||||
|
@ -3763,6 +3827,10 @@ void UnloadBlockIndex()
|
||||||
setDirtyFileInfo.clear();
|
setDirtyFileInfo.clear();
|
||||||
mapNodeState.clear();
|
mapNodeState.clear();
|
||||||
recentRejects.reset(NULL);
|
recentRejects.reset(NULL);
|
||||||
|
versionbitscache.Clear();
|
||||||
|
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
|
||||||
|
warningcache[b].clear();
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) {
|
BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) {
|
||||||
delete entry.second;
|
delete entry.second;
|
||||||
|
|
|
@ -537,6 +537,11 @@ extern CBlockTreeDB *pblocktree;
|
||||||
*/
|
*/
|
||||||
int GetSpendHeight(const CCoinsViewCache& inputs);
|
int GetSpendHeight(const CCoinsViewCache& inputs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine what nVersion a new block should use.
|
||||||
|
*/
|
||||||
|
int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params);
|
||||||
|
|
||||||
/** Reject codes greater or equal to this can be returned by AcceptToMemPool
|
/** Reject codes greater or equal to this can be returned by AcceptToMemPool
|
||||||
* for transactions, to signal internal conditions. They cannot and should not
|
* for transactions, to signal internal conditions. They cannot and should not
|
||||||
* be sent over the P2P network.
|
* be sent over the P2P network.
|
||||||
|
|
|
@ -79,11 +79,6 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
|
||||||
return NULL;
|
return NULL;
|
||||||
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
|
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
|
||||||
|
|
||||||
// -regtest only: allow overriding block.nVersion with
|
|
||||||
// -blockversion=N to test forking scenarios
|
|
||||||
if (chainparams.MineBlocksOnDemand())
|
|
||||||
pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
|
|
||||||
|
|
||||||
// Create coinbase tx
|
// Create coinbase tx
|
||||||
CMutableTransaction txNew;
|
CMutableTransaction txNew;
|
||||||
txNew.vin.resize(1);
|
txNew.vin.resize(1);
|
||||||
|
@ -137,6 +132,12 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
|
||||||
pblock->nTime = GetAdjustedTime();
|
pblock->nTime = GetAdjustedTime();
|
||||||
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
|
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);
|
||||||
|
|
||||||
int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
|
int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
|
||||||
? nMedianTimePast
|
? nMedianTimePast
|
||||||
: pblock->GetBlockTime();
|
: pblock->GetBlockTime();
|
||||||
|
|
|
@ -21,7 +21,6 @@ class CBlockHeader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// header
|
// header
|
||||||
static const int32_t CURRENT_VERSION=4;
|
|
||||||
int32_t nVersion;
|
int32_t nVersion;
|
||||||
uint256 hashPrevBlock;
|
uint256 hashPrevBlock;
|
||||||
uint256 hashMerkleRoot;
|
uint256 hashMerkleRoot;
|
||||||
|
@ -49,7 +48,7 @@ public:
|
||||||
|
|
||||||
void SetNull()
|
void SetNull()
|
||||||
{
|
{
|
||||||
nVersion = CBlockHeader::CURRENT_VERSION;
|
nVersion = 0;
|
||||||
hashPrevBlock.SetNull();
|
hashPrevBlock.SetNull();
|
||||||
hashMerkleRoot.SetNull();
|
hashMerkleRoot.SetNull();
|
||||||
nTime = 0;
|
nTime = 0;
|
||||||
|
|
|
@ -247,13 +247,40 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
|
|
||||||
// subsidy changing
|
// subsidy changing
|
||||||
int nHeight = chainActive.Height();
|
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(chainparams, scriptPubKey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||||
delete pblocktemplate;
|
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(chainparams, scriptPubKey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||||
delete pblocktemplate;
|
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
|
// non-final txs in mempool
|
||||||
SetMockTime(chainActive.Tip()->GetMedianTimePast()+1);
|
SetMockTime(chainActive.Tip()->GetMedianTimePast()+1);
|
||||||
|
|
133
src/versionbits.cpp
Normal file
133
src/versionbits.cpp
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "versionbits.h"
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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 != 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<const CBlockIndex*> vToCompute;
|
||||||
|
while (cache.count(pindexPrev) == 0) {
|
||||||
|
if (pindexPrev == NULL) {
|
||||||
|
// The genesis block is by definition defined.
|
||||||
|
cache[pindexPrev] = THRESHOLD_DEFINED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (pindexPrev->GetMedianTimePast() < nTimeStart) {
|
||||||
|
// Optimizaton: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Class to implement versionbits logic.
|
||||||
|
*/
|
||||||
|
class VersionBitsConditionChecker : public AbstractThresholdConditionChecker {
|
||||||
|
private:
|
||||||
|
const Consensus::DeploymentPos id;
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
return (((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (pindex->nVersion & Mask(params)) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
VersionBitsConditionChecker(Consensus::DeploymentPos id_) : id(id_) {}
|
||||||
|
uint32_t Mask(const Consensus::Params& params) const { return ((uint32_t)1) << params.vDeployments[id].bit; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache)
|
||||||
|
{
|
||||||
|
return VersionBitsConditionChecker(pos).GetStateFor(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();
|
||||||
|
}
|
||||||
|
}
|
59
src/versionbits.h
Normal file
59
src/versionbits.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
// 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 "chain.h"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
/** 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 NULL or a block with (height + 1) % Period() == 0.
|
||||||
|
typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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:
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue