Merge pull request #51 from jc23424/bip9

BIP 9
This commit is contained in:
BlueSilver22 2018-02-23 07:18:02 -06:00 committed by GitHub
commit bc0735b0e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 978 additions and 30 deletions

View File

@ -58,6 +58,7 @@ testScriptsExt=(
'smartfees.py'
'maxblocksinflight.py'
'invalidblockrequest.py'
'p2p-versionbits-warning.py'
# 'forknotify.py'
'p2p-acceptblock.py'
);

View File

@ -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_UNKNOWN_BIT)
self.send_blocks_with_version(test_node, VB_THRESHOLD-1, nVersion)
# Fill rest of period with regular version blocks
self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD + 1)
# Check that we're not getting any versionbit-related errors in
# getinfo()
assert(not self.vb_pattern.match(self.nodes[0].getinfo()["errors"]))
# 3. Now build one period of blocks with >= 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()

View File

@ -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)

View File

@ -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 \

View File

@ -14,8 +14,6 @@
#include <vector>
#include <boost/foreach.hpp>
struct CDiskBlockPos
{
int nFile;

View File

@ -52,6 +52,13 @@ public:
consensus.fPowAllowMinDifficultyBlocks = false;
consensus.nPowDifficultyBombHeight = 600000;
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;
@ -210,12 +217,21 @@ 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
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
consensus.nPowDifficultyBombHeight = 600000;
pchMessageStart[0] = 0xf6;
pchMessageStart[1] = 0x1b;
pchMessageStart[2] = 0xf6;
pchMessageStart[3] = 0xd6;
vAlertPubKey = ParseHex("048679fb891b15d0cada9692047fd0ae26ad8bfb83fabddbb50334ee5bc0683294deb410be20513c5af6e7b9cec717ade82b27080ee6ef9a245c36a795ab044bb3");
nDefaultPort = 17933;
nPruneAfterHeight = 1000;
@ -303,6 +319,13 @@ 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)
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 999999999999ULL;
consensus.nPowDifficultyBombHeight = 600000;
pchMessageStart[0] = 0xaa;

View File

@ -9,6 +9,25 @@
#include "uint256.h"
namespace Consensus {
enum DeploymentPos
{
DEPLOYMENT_TESTDUMMY,
MAX_VERSION_BITS_DEPLOYMENTS
};
/**
* 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 +59,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;

View File

@ -490,7 +490,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("-blockprioritysize=<n>", 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=<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");
#ifdef FORK_CB_INPUT
strUsage += HelpMessageGroup(_("Fork :"));

View File

@ -27,6 +27,7 @@
#include "util.h"
#include "utilmoneystr.h"
#include "validationinterface.h"
#include "versionbits.h"
#include "wallet/asyncrpcoperation_sendmany.h"
#include <sstream>
@ -2089,6 +2090,51 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const
}
}
// Protected by cs_main
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 nTimeVerify = 0;
static int64_t nTimeConnect = 0;
static int64_t nTimeIndex = 0;
@ -2441,24 +2487,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;
}
}
}
}
@ -3907,6 +3972,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;
@ -5722,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
{

View File

@ -25,6 +25,7 @@
#include "tinyformat.h"
#include "txmempool.h"
#include "uint256.h"
#include "versionbits.h"
#include <algorithm>
#include <exception>
@ -162,11 +163,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.
@ -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;
@ -295,7 +299,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 +308,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 +324,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 +368,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 +541,13 @@ extern CBlockTreeDB *pblocktree;
*/
int GetSpendHeight(const CCoinsViewCache& inputs);
extern VersionBitsCache versionbitscache;
/**
* 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);
}

View File

@ -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
@ -291,6 +286,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

View File

@ -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;
@ -54,7 +54,7 @@ public:
void SetNull()
{
nVersion = CBlockHeader::CURRENT_VERSION;
nVersion = 0;
hashPrevBlock.SetNull();
hashMerkleRoot.SetNull();
hashReserved.SetNull();

View File

@ -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)
{

View File

@ -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<std::string> 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) {

View File

@ -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);

View File

@ -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 <boost/test/unit_test.hpp>
/* 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<CBlockIndex*> 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<<bit), 0);
// Mine 2011 more blocks at the old time, and check that CBV isn't setting the bit yet.
for (int i=1; i<2012; i++) {
lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens
// to be 4, and the bit we're testing happens to be bit 28.
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
}
// Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
// CBV should still not yet set the bit.
nTime = nStartTime;
for (int i=2012; i<=2016; i++) {
lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
}
// Advance to the next period and transition to STARTED,
lastBlock = firstChain.Mine(6048, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// so ComputeBlockVersion should now set the bit,
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// and should also be using the VERSIONBITS_TOP_BITS.
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
// Check that ComputeBlockVersion will set the bit until nTimeout
nTime += 600;
int blocksToMine = 4032; // test blocks for up to 2 time periods
int nHeight = 6048;
// These blocks are all before nTimeout is reached.
while (nTime < nTimeout && blocksToMine > 0) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
blocksToMine--;
nTime += 600;
nHeight += 1;
};
nTime = nTimeout;
// FAILED is only triggered at the end of a period, so CBV should be setting
// the bit until the period transition.
for (int i=0; i<2015; i++) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
nHeight += 1;
}
// The next block should trigger no longer setting the bit.
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
// On a new chain:
// verify that the bit will be set after lock-in, and then stop being set
// after activation.
nTime = nStartTime;
// Mine one period worth of blocks, and check that the bit will be on for the
// next period.
lastBlock = secondChain.Mine(2016, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// Mine another period worth of blocks, signaling the new bit.
lastBlock = secondChain.Mine(4032, nStartTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
// After one period of setting the bit on each block, it should have locked in.
// We keep setting the bit for one more period though, until activation.
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// Now check that we keep mining the block until the end of this period, and
// then stop at the beginning of the next period.
lastBlock = secondChain.Mine(6047, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
lastBlock = secondChain.Mine(6048, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
// Finally, verify that after a soft fork has activated, CBV no longer uses
// VERSIONBITS_LAST_OLD_BLOCK_VERSION.
//BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
}
BOOST_AUTO_TEST_SUITE_END()

141
src/versionbits.cpp Normal file
View File

@ -0,0 +1,141 @@
// 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"
#include "consensus/params.h"
const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = {
{
/*.name =*/ "testdummy",
}
};
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) {
// 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;
}
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();
}
}

68
src/versionbits.h Normal file
View File

@ -0,0 +1,68 @@
// 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;
struct BIP9DeploymentInfo {
/** Deployment name */
const char *name;
/** Whether GBT clients can safely ignore this rule in simplified usage */
bool gbt_force;
};
extern const struct BIP9DeploymentInfo 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:
// 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