commit
bc0735b0e9
|
@ -58,6 +58,7 @@ testScriptsExt=(
|
|||
'smartfees.py'
|
||||
'maxblocksinflight.py'
|
||||
'invalidblockrequest.py'
|
||||
'p2p-versionbits-warning.py'
|
||||
# 'forknotify.py'
|
||||
'p2p-acceptblock.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_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()
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
struct CDiskBlockPos
|
||||
{
|
||||
int nFile;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 :"));
|
||||
|
|
87
src/main.cpp
87
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 <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
|
||||
{
|
||||
|
|
27
src/main.h
27
src/main.h
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue