Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests and limit handling for fixed height BIP9 deployment #106

Open
wants to merge 6 commits into
base: 201709_itsalwayssegwitinregtestia
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits

void CChainParams::UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
{
nStartTime = std::max(nStartTime, -Consensus::BIP9Deployment::MAX_FIXED_HEIGHT);
nTimeout = std::min(nTimeout, Consensus::BIP9Deployment::NO_TIMEOUT);

consensus.vDeployments[d].nStartTime = nStartTime;
consensus.vDeployments[d].nTimeout = nTimeout;
}
Expand All @@ -75,6 +78,7 @@ class CMainParams : public CChainParams {
CMainParams() {
strNetworkID = "main";
consensus.nSubsidyHalvingInterval = 210000;
consensus.BIP16Height = 173805; // 00000000000000ce80a7e057163a4db1d5ad7b20fb6f598c9597b9665c8fb0d4 - April 1, 2012
consensus.BIP34Height = 227931;
consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8");
consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
Expand Down Expand Up @@ -181,6 +185,7 @@ class CTestNetParams : public CChainParams {
CTestNetParams() {
strNetworkID = "test";
consensus.nSubsidyHalvingInterval = 210000;
consensus.BIP16Height = 514; // 00000000040b4e986385315e14bee30ad876d8b47f748025b26683116d21aa65
consensus.BIP34Height = 21111;
consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8");
consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
Expand Down Expand Up @@ -271,6 +276,7 @@ class CRegTestParams : public CChainParams {
CRegTestParams() {
strNetworkID = "regtest";
consensus.nSubsidyHalvingInterval = 150;
consensus.BIP16Height = 0; // always enforce P2SH BIP16 on regtest
consensus.BIP34Height = 100000000; // BIP34 has not activated on regtest (far in the future so block v1 are not rejected in tests)
consensus.BIP34Hash = uint256();
consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in rpc activation tests)
Expand All @@ -284,13 +290,13 @@ class CRegTestParams : public CChainParams {
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.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL;
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 999999999999ULL;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = -1; // Always on
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");
Expand Down
8 changes: 7 additions & 1 deletion src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,24 @@ enum DeploymentPos
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 */
/** Start MedianTime for version bits miner confirmation. Can be a date in the past. Negative numbers mean hardcoded height. */
int64_t nStartTime;
/** Timeout/expiry MedianTime for the deployment attempt. */
int64_t nTimeout;

static const int64_t NO_TIMEOUT = 999999999999ULL;
static const int64_t MAX_FIXED_HEIGHT = 4294967296LL;
};


/**
* Parameters that influence chain consensus.
*/
struct Params {
uint256 hashGenesisBlock;
int nSubsidyHalvingInterval;
/** Block height at which BIP16 becomes active */
int BIP16Height;
/** Block height and hash at which BIP34 becomes active */
int BIP34Height;
uint256 BIP34Hash;
Expand Down
35 changes: 18 additions & 17 deletions src/test/miner_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,23 +335,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
mempool.clear();

// invalid (pre-p2sh) txn in mempool, template creation fails
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE;
script = CScript() << OP_0;
tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
tx.vout[0].nValue -= LOWFEE;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
mempool.clear();

// double spend txn pair in mempool, template creation fails
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].scriptSig = CScript() << OP_1;
Expand Down Expand Up @@ -391,6 +374,24 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
chainActive.SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));

// invalid p2sh txn in mempool, template creation fails
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE;
script = CScript() << OP_0;
tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
tx.vout[0].nValue -= LOWFEE;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
mempool.clear();

// Delete the dummy blocks again.
while (chainActive.Tip()->nHeight > nHeight) {
CBlockIndex* del = chainActive.Tip();
Expand Down
1 change: 1 addition & 0 deletions src/test/test_bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ TestingSetup::~TestingSetup()

TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
UpdateVersionBitsParameters(Consensus::DEPLOYMENT_SEGWIT, 0, 999999999999ULL);
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
Expand Down
48 changes: 36 additions & 12 deletions src/test/versionbits_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ class TestConditionChecker : public AbstractThresholdConditionChecker
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); }
};

class TestFixedHeightConditionChecker : public TestConditionChecker
{
public:
const static int Never = 9999999;
int activation_height;
int64_t BeginTime(const Consensus::Params& params) const override { return -activation_height; }
TestFixedHeightConditionChecker(int height) : activation_height(height) { }
TestFixedHeightConditionChecker() : activation_height(Never) { }
};

#define CHECKERS 6

class VersionBitsTester
Expand All @@ -43,26 +53,33 @@ class VersionBitsTester
// 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];
// Another 6 that assume fixed height activation
TestFixedHeightConditionChecker checker_fixed[CHECKERS];

// Test counter (to identify failures)
int num;

public:
VersionBitsTester() : num(0) {}
VersionBitsTester(int height) : num(0) {
for (unsigned int i = 0; i < CHECKERS; i++) {
checker_fixed[i] = TestFixedHeightConditionChecker(height);
}
}

VersionBitsTester& Reset() {
VersionBitsTester& Reset(int height) {
for (unsigned int i = 0; i < vpblock.size(); i++) {
delete vpblock[i];
}
for (unsigned int i = 0; i < CHECKERS; i++) {
checker[i] = TestConditionChecker();
checker_fixed[i] = TestFixedHeightConditionChecker(height);
}
vpblock.clear();
return *this;
}

~VersionBitsTester() {
Reset();
Reset(TestFixedHeightConditionChecker::Never);
}

VersionBitsTester& Mine(unsigned int height, int32_t nTime, int32_t nVersion) {
Expand All @@ -81,7 +98,9 @@ class VersionBitsTester
VersionBitsTester& TestStateSinceHeight(int height) {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
const int height_fixed = (checker_fixed[i].activation_height <= height ? height : 0);
BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num));
BOOST_CHECK_MESSAGE(checker_fixed[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == height_fixed, strprintf("Test %i for StateSinceHeight (fixed height)", num));
}
}
num++;
Expand All @@ -92,6 +111,7 @@ class VersionBitsTester
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED", num));
BOOST_CHECK_MESSAGE(checker_fixed[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED (fixed height)", num));
}
}
num++;
Expand All @@ -102,6 +122,7 @@ class VersionBitsTester
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_STARTED, strprintf("Test %i for STARTED", num));
BOOST_CHECK_MESSAGE(checker_fixed[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for STARTED (fixed height)", num));
}
}
num++;
Expand All @@ -112,6 +133,7 @@ class VersionBitsTester
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_LOCKED_IN, strprintf("Test %i for LOCKED_IN", num));
BOOST_CHECK_MESSAGE(checker_fixed[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for LOCKED_IN (fixed height)", num));
}
}
num++;
Expand All @@ -122,6 +144,7 @@ class VersionBitsTester
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE", num));
BOOST_CHECK_MESSAGE(checker_fixed[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (fixed height)", num));
}
}
num++;
Expand All @@ -132,6 +155,7 @@ class VersionBitsTester
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_FAILED, strprintf("Test %i for FAILED", num));
BOOST_CHECK_MESSAGE(checker_fixed[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for FAILED (fixed height)", num));
}
}
num++;
Expand All @@ -147,7 +171,7 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
{
for (int i = 0; i < 64; i++) {
// DEFINED -> FAILED
VersionBitsTester().TestDefined().TestStateSinceHeight(0)
VersionBitsTester(TestFixedHeightConditionChecker::Never).TestDefined().TestStateSinceHeight(0)
.Mine(1, TestTime(1), 0x100).TestDefined().TestStateSinceHeight(0)
.Mine(11, TestTime(11), 0x100).TestDefined().TestStateSinceHeight(0)
.Mine(989, TestTime(989), 0x100).TestDefined().TestStateSinceHeight(0)
Expand All @@ -160,19 +184,19 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
.Mine(3000, TestTime(30005), 0x100).TestFailed().TestStateSinceHeight(1000)

// DEFINED -> STARTED -> FAILED
.Reset().TestDefined().TestStateSinceHeight(0)
.Reset(TestFixedHeightConditionChecker::Never).TestDefined().TestStateSinceHeight(0)
.Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
.Mine(1000, TestTime(10000) - 1, 0x100).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
.Mine(1000, TestTime(10000) - 1, 0x100).TestDefined().TestStateSinceHeight(0) // One second more and it would be started
.Mine(2000, TestTime(10000), 0x100).TestStarted().TestStateSinceHeight(2000) // So that's what happens the next period
.Mine(2051, TestTime(10010), 0).TestStarted().TestStateSinceHeight(2000) // 51 old blocks
.Mine(2950, TestTime(10020), 0x100).TestStarted().TestStateSinceHeight(2000) // 899 new blocks
.Mine(3000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(3000) // 50 old blocks (so 899 out of the past 1000)
.Mine(4000, TestTime(20010), 0x100).TestFailed().TestStateSinceHeight(3000)

// DEFINED -> STARTED -> FAILED while threshold reached
.Reset().TestDefined().TestStateSinceHeight(0)
.Reset(TestFixedHeightConditionChecker::Never).TestDefined().TestStateSinceHeight(0)
.Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
.Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
.Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be started
.Mine(2000, TestTime(10000), 0x101).TestStarted().TestStateSinceHeight(2000) // So that's what happens the next period
.Mine(2999, TestTime(30000), 0x100).TestStarted().TestStateSinceHeight(2000) // 999 new blocks
.Mine(3000, TestTime(30000), 0x100).TestFailed().TestStateSinceHeight(3000) // 1 new block (so 1000 out of the past 1000 are new)
Expand All @@ -182,9 +206,9 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
.Mine(24000, TestTime(40000), 0).TestFailed().TestStateSinceHeight(3000)

// DEFINED -> STARTED -> LOCKEDIN at the last minute -> ACTIVE
.Reset().TestDefined()
.Reset(4000).TestDefined()
.Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
.Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
.Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be started
.Mine(2000, TestTime(10000), 0x101).TestStarted().TestStateSinceHeight(2000) // So that's what happens the next period
.Mine(2050, TestTime(10010), 0x200).TestStarted().TestStateSinceHeight(2000) // 50 old blocks
.Mine(2950, TestTime(10020), 0x100).TestStarted().TestStateSinceHeight(2000) // 900 new blocks
Expand All @@ -196,7 +220,7 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
.Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000)

// DEFINED multiple periods -> STARTED multiple periods -> FAILED
.Reset().TestDefined().TestStateSinceHeight(0)
.Reset(TestFixedHeightConditionChecker::Never).TestDefined().TestStateSinceHeight(0)
.Mine(999, TestTime(999), 0).TestDefined().TestStateSinceHeight(0)
.Mine(1000, TestTime(1000), 0).TestDefined().TestStateSinceHeight(0)
.Mine(2000, TestTime(2000), 0).TestDefined().TestStateSinceHeight(0)
Expand Down Expand Up @@ -248,7 +272,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// 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;
VersionBitsTester firstChain(6048), secondChain(6048);

// Start generating blocks before nStartTime
int64_t nTime = nStartTime - 1;
Expand Down
9 changes: 5 additions & 4 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1587,11 +1587,12 @@ static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS];
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) {
AssertLockHeld(cs_main);

// BIP16 didn't become active until Apr 1 2012
int64_t nBIP16SwitchTime = 1333238400;
bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime);
unsigned int flags = SCRIPT_VERIFY_NONE;

unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE;
// Start enforcing P2SH (BIP16)
if (pindex->nHeight >= consensusparams.BIP16Height) {
flags |= SCRIPT_VERIFY_P2SH;
}

// Start enforcing the DERSIG (BIP66) rule
if (pindex->nHeight >= consensusparams.BIP66Height) {
Expand Down
12 changes: 12 additions & 0 deletions src/versionbits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
int64_t nTimeStart = BeginTime(params);
int64_t nTimeTimeout = EndTime(params);

// Check if this deployment has a fixed activation height.
if (nTimeStart < 0) {
if (pindexPrev && pindexPrev->nHeight + 1 >= -nTimeStart) return THRESHOLD_ACTIVE;
return THRESHOLD_DEFINED;
}

// A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
if (pindexPrev != nullptr) {
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
Expand Down Expand Up @@ -136,6 +142,12 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI

int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
{
int64_t start_time = BeginTime(params);
if (start_time < 0) {
if (pindexPrev && pindexPrev->nHeight + 1 >= -start_time) return -start_time;
return 0;
}

const ThresholdState initialState = GetStateFor(pindexPrev, params, cache);

// BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment."
Expand Down
Loading