aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xqa/rpc-tests/bip9-softforks.py9
-rw-r--r--src/main.cpp6
-rw-r--r--src/main.h3
-rw-r--r--src/rpc/blockchain.cpp4
-rw-r--r--src/test/versionbits_tests.cpp104
-rw-r--r--src/versionbits.cpp35
-rw-r--r--src/versionbits.h4
7 files changed, 121 insertions, 44 deletions
diff --git a/qa/rpc-tests/bip9-softforks.py b/qa/rpc-tests/bip9-softforks.py
index be6ddde112..c42ed44c25 100755
--- a/qa/rpc-tests/bip9-softforks.py
+++ b/qa/rpc-tests/bip9-softforks.py
@@ -81,6 +81,9 @@ class BIP9SoftForksTest(ComparisonTestFramework):
return info['bip9_softforks'][key]
def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignature, bitno):
+ assert_equal(self.get_bip9_status(bipName)['status'], 'defined')
+ assert_equal(self.get_bip9_status(bipName)['since'], 0)
+
# generate some coins for later
self.coinbase_blocks = self.nodes[0].generate(2)
self.height = 3 # height of the next block to build
@@ -89,6 +92,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
self.last_block_time = int(time.time())
assert_equal(self.get_bip9_status(bipName)['status'], 'defined')
+ assert_equal(self.get_bip9_status(bipName)['since'], 0)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName not in tmpl['rules'])
assert(bipName not in tmpl['vbavailable'])
@@ -101,6 +105,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
yield TestInstance(test_blocks, sync_every_block=False)
assert_equal(self.get_bip9_status(bipName)['status'], 'started')
+ assert_equal(self.get_bip9_status(bipName)['since'], 144)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName not in tmpl['rules'])
assert_equal(tmpl['vbavailable'][bipName], bitno)
@@ -117,6 +122,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
yield TestInstance(test_blocks, sync_every_block=False)
assert_equal(self.get_bip9_status(bipName)['status'], 'started')
+ assert_equal(self.get_bip9_status(bipName)['since'], 144)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName not in tmpl['rules'])
assert_equal(tmpl['vbavailable'][bipName], bitno)
@@ -133,6 +139,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
yield TestInstance(test_blocks, sync_every_block=False)
assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
+ assert_equal(self.get_bip9_status(bipName)['since'], 432)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName not in tmpl['rules'])
@@ -142,6 +149,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
yield TestInstance(test_blocks, sync_every_block=False)
assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
+ assert_equal(self.get_bip9_status(bipName)['since'], 432)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName not in tmpl['rules'])
@@ -167,6 +175,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
yield TestInstance([[block, True]])
assert_equal(self.get_bip9_status(bipName)['status'], 'active')
+ assert_equal(self.get_bip9_status(bipName)['since'], 576)
tmpl = self.nodes[0].getblocktemplate({})
assert(bipName in tmpl['rules'])
assert(bipName not in tmpl['vbavailable'])
diff --git a/src/main.cpp b/src/main.cpp
index 1777717cd9..50158b4687 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -6918,6 +6918,12 @@ ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::D
return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache);
}
+int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos)
+{
+ LOCK(cs_main);
+ return VersionBitsStateSinceHeight(chainActive.Tip(), params, pos, versionbitscache);
+}
+
class CMainCleanup
{
public:
diff --git a/src/main.h b/src/main.h
index e91f6e46fe..3eab9b89da 100644
--- a/src/main.h
+++ b/src/main.h
@@ -297,7 +297,8 @@ std::string FormatStateMessage(const CValidationState &state);
/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
-
+/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
+int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos);
/**
* Count ECDSA signature operations the old-fashioned (pre-0.6) way
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 5414ac9ffd..141ca87b69 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1009,6 +1009,7 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
}
rv.push_back(Pair("startTime", consensusParams.vDeployments[id].nStartTime));
rv.push_back(Pair("timeout", consensusParams.vDeployments[id].nTimeout));
+ rv.push_back(Pair("since", VersionBitsTipStateSinceHeight(consensusParams, id)));
return rv;
}
@@ -1053,7 +1054,8 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
" \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
" \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
" \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
- " \"timeout\": xx (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
+ " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
+ " \"since\": xx (numeric) height of the first block to which the status applies\n"
" }\n"
" }\n"
"}\n"
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index ffc0ff6f8e..784e796998 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2015 The Bitcoin Core developers
+// Copyright (c) 2014-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.
@@ -30,6 +30,7 @@ public:
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); }
+ int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); }
};
#define CHECKERS 6
@@ -78,6 +79,16 @@ public:
return *this;
}
+ VersionBitsTester& TestStateSinceHeight(int height) {
+ for (int i = 0; i < CHECKERS; i++) {
+ if ((insecure_rand() & ((1 << i) - 1)) == 0) {
+ BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? NULL : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num));
+ }
+ }
+ num++;
+ return *this;
+ }
+
VersionBitsTester& TestDefined() {
for (int i = 0; i < CHECKERS; i++) {
if ((insecure_rand() & ((1 << i) - 1)) == 0) {
@@ -137,53 +148,64 @@ 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()
+ VersionBitsTester().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)
+ .Mine(999, TestTime(20000), 0x100).TestDefined().TestStateSinceHeight(0)
+ .Mine(1000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(1999, TestTime(30001), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(2000, TestTime(30002), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(2001, TestTime(30003), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(2999, TestTime(30004), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(3000, TestTime(30005), 0x100).TestFailed().TestStateSinceHeight(1000)
// 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()
+ .Reset().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(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()
- .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()
+ .Reset().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(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)
+ .Mine(3999, TestTime(30001), 0).TestFailed().TestStateSinceHeight(3000)
+ .Mine(4000, TestTime(30002), 0).TestFailed().TestStateSinceHeight(3000)
+ .Mine(14333, TestTime(30003), 0).TestFailed().TestStateSinceHeight(3000)
+ .Mine(24000, TestTime(40000), 0).TestFailed().TestStateSinceHeight(3000)
// 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();
+ .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(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
+ .Mine(2999, TestTime(19999), 0x200).TestStarted().TestStateSinceHeight(2000) // 49 old blocks
+ .Mine(3000, TestTime(29999), 0x200).TestLockedIn().TestStateSinceHeight(3000) // 1 old block (so 900 out of the past 1000)
+ .Mine(3999, TestTime(30001), 0).TestLockedIn().TestStateSinceHeight(3000)
+ .Mine(4000, TestTime(30002), 0).TestActive().TestStateSinceHeight(4000)
+ .Mine(14333, TestTime(30003), 0).TestActive().TestStateSinceHeight(4000)
+ .Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000)
+
+ // DEFINED multiple periods -> STARTED multiple periods -> FAILED
+ .Reset().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)
+ .Mine(3000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
+ .Mine(4000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
+ .Mine(5000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
+ .Mine(6000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(6000)
+ .Mine(7000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000);
}
// Sanity checks of version bit deployments
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index bf32ae6627..d73f340510 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -105,6 +105,36 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
return state;
}
+int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
+{
+ const ThresholdState initialState = GetStateFor(pindexPrev, params, cache);
+
+ // BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment."
+ if (initialState == THRESHOLD_DEFINED) {
+ return 0;
+ }
+
+ const int nPeriod = Period(params);
+
+ // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
+ // To ease understanding of the following height calculation, it helps to remember that
+ // right now pindexPrev points to the block prior to the block that we are computing for, thus:
+ // if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and
+ // if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period.
+ // The parent of the genesis block is represented by NULL.
+ pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
+
+ const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
+
+ while (previousPeriodParent != NULL && GetStateFor(previousPeriodParent, params, cache) == initialState) {
+ pindexPrev = previousPeriodParent;
+ previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
+ }
+
+ // Adjust the result because right now we point to the parent block.
+ return pindexPrev->nHeight + 1;
+}
+
namespace
{
/**
@@ -137,6 +167,11 @@ ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::
return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]);
}
+int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache)
+{
+ return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, cache.caches[pos]);
+}
+
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
return VersionBitsConditionChecker(pos).Mask(params);
diff --git a/src/versionbits.h b/src/versionbits.h
index ede2dcdda8..7a929266aa 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -51,8 +51,9 @@ protected:
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.
+ // Note that the functions below take a pindexPrev as input: they compute information for block B based on its parent.
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
+ int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
};
struct VersionBitsCache
@@ -63,6 +64,7 @@ struct VersionBitsCache
};
ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
+int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);
#endif