feat: imlemented new hard-fork mechanism that uses MN Activation Height

Altough, it's still disabled because no calls of related methods after processing MnEHF tx
This commit is contained in:
Konstantin Akimov 2023-08-04 03:35:17 +07:00 committed by PastaPastaPasta
parent 5e5b571be6
commit 612faa8868
11 changed files with 90 additions and 44 deletions

View File

@ -105,6 +105,30 @@ static CBlock FindDevNetGenesisBlock(const CBlock &prevBlock, const CAmount& rew
assert(false);
}
bool CChainParams::UpdateMNActivationParam(int nBit, int height, int64_t timePast, bool fJustCheck) const
{
for (int index = 0; index < Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++index) {
if (consensus.vDeployments[index].bit == nBit) {
auto& deployment = consensus.vDeployments[index];
if (timePast > deployment.nTimeout) {
LogPrintf("%s: activation by bit=%d time-outed at height=%d\n", __func__, nBit, height);
continue;
}
if (deployment.nMNActivationHeight < 0) {
LogPrintf("%s: trying to set MnEHF height=%d for non-masternode activation fork bit=%d\n", __func__, height, nBit);
return false;
}
if (!fJustCheck) {
LogPrintf("%s: set MnEHF height=%d for bit=%d successfuly while fJustCheck=%d\n", __func__, height, nBit, fJustCheck);
deployment.nMNActivationHeight = height;
}
return true;
}
}
LogPrintf("%s: not found MnEHF fork bit=%d\n", __func__, nBit);
return false;
}
void CChainParams::AddLLMQ(Consensus::LLMQType llmqType)
{
assert(!GetLLMQ(llmqType).has_value());
@ -909,7 +933,7 @@ public:
/**
* Allows modifying the Version Bits regtest parameters.
*/
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThresholdStart, int64_t nThresholdMin, int64_t nFalloffCoeff)
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThresholdStart, int64_t nThresholdMin, int64_t nFalloffCoeff, int64_t nMNActivationHeight)
{
consensus.vDeployments[d].nStartTime = nStartTime;
consensus.vDeployments[d].nTimeout = nTimeout;
@ -925,6 +949,9 @@ public:
if (nFalloffCoeff != -1) {
consensus.vDeployments[d].nFalloffCoeff = nFalloffCoeff;
}
if (nMNActivationHeight != -1) {
consensus.vDeployments[d].nMNActivationHeight = nMNActivationHeight;
}
}
void UpdateActivationParametersFromArgs(const ArgsManager& args);
@ -998,13 +1025,13 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
std::vector<std::string> vDeploymentParams = SplitString(strDeployment, ':');
if (vDeploymentParams.size() != 3 && vDeploymentParams.size() != 5 && vDeploymentParams.size() != 7) {
if (vDeploymentParams.size() != 3 && vDeploymentParams.size() != 5 && vDeploymentParams.size() != 8) {
throw std::runtime_error("Version bits parameters malformed, expecting "
"<deployment>:<start>:<end> or "
"<deployment>:<start>:<end>:<window>:<threshold> or "
"<deployment>:<start>:<end>:<window>:<thresholdstart>:<thresholdmin>:<falloffcoeff>");
"<deployment>:<start>:<end>:<window>:<thresholdstart>:<thresholdmin>:<falloffcoeff>:<mnactivation>");
}
int64_t nStartTime, nTimeout, nWindowSize = -1, nThresholdStart = -1, nThresholdMin = -1, nFalloffCoeff = -1;
int64_t nStartTime, nTimeout, nWindowSize = -1, nThresholdStart = -1, nThresholdMin = -1, nFalloffCoeff = -1, nMNActivationHeight = -1;
if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
}
@ -1019,21 +1046,24 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
throw std::runtime_error(strprintf("Invalid nThresholdStart (%s)", vDeploymentParams[4]));
}
}
if (vDeploymentParams.size() == 7) {
if (vDeploymentParams.size() == 8) {
if (!ParseInt64(vDeploymentParams[5], &nThresholdMin)) {
throw std::runtime_error(strprintf("Invalid nThresholdMin (%s)", vDeploymentParams[5]));
}
if (!ParseInt64(vDeploymentParams[6], &nFalloffCoeff)) {
throw std::runtime_error(strprintf("Invalid nFalloffCoeff (%s)", vDeploymentParams[6]));
}
if (!ParseInt64(vDeploymentParams[7], &nMNActivationHeight)) {
throw std::runtime_error(strprintf("Invalid nMNActivationHeight (%s)", vDeploymentParams[7]));
}
}
bool found = false;
for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) {
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff);
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff, nMNActivationHeight);
found = true;
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, window=%ld, thresholdstart=%ld, thresholdmin=%ld, falloffcoeff=%ld\n",
vDeploymentParams[0], nStartTime, nTimeout, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff);
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, window=%ld, thresholdstart=%ld, thresholdmin=%ld, falloffcoeff=%ld, mnactivationHeight=%ld\n",
vDeploymentParams[0], nStartTime, nTimeout, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff, nMNActivationHeight);
break;
}
}

View File

@ -134,6 +134,7 @@ public:
void UpdateDIP8Parameters(int nActivationHeight);
void UpdateBudgetParameters(int nMasternodePaymentsStartBlock, int nBudgetPaymentsStartBlock, int nSuperblockStartBlock);
void UpdateLLMQInstantSend(Consensus::LLMQType llmqType);
bool UpdateMNActivationParam(int nBit, int height, int64_t timePast, bool fJustCheck) const;
int PoolMinParticipants() const { return nPoolMinParticipants; }
int PoolMaxParticipants() const { return nPoolMaxParticipants; }
int FulfilledRequestExpireTime() const { return nFulfilledRequestExpireTime; }

View File

@ -36,9 +36,9 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=<deployment>:<start>:<end>(:<window>:<threshold/thresholdstart>(:<thresholdmin>:<falloffcoeff>))",
argsman.AddArg("-vbparams=<deployment>:<start>:<end>(:<window>:<threshold/thresholdstart>(:<thresholdmin>:<falloffcoeff>:<mnactivation>))",
"Use given start/end times for specified version bits deployment (regtest-only). "
"Specifying window, threshold/thresholdstart, thresholdmin and falloffcoeff is optional.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
"Specifying window, threshold/thresholdstart, thresholdmin, falloffcoeff and mnactivation is optional.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
}

View File

@ -48,6 +48,13 @@ struct BIP9Deployment {
* process (which takes at least 3 BIP9 intervals). Only tests that specifically test the
* behaviour during activation cannot use this. */
static constexpr int64_t ALWAYS_ACTIVE = -1;
/** this value is used for forks activated by master nodes.
* negative values means it is regular fork, no masternodes confirmation is needed.
* 0 means that there's no approval from masternodes yet.
* Otherwise it shows minimum height when miner's signals for this block can be assumed
*/
mutable int64_t nMNActivationHeight{-1};
};
/**

View File

@ -34,7 +34,7 @@ static constexpr int threshold(int attempt)
struct TestChainDATSetup : public TestChainSetup
{
TestChainDATSetup() : TestChainSetup(window - 2, {"-vbparams=testdummy:0:999999999999:100:80:60:5"}) {}
TestChainDATSetup() : TestChainSetup(window - 2, {"-vbparams=testdummy:0:999999999999:100:80:60:5:-1"}) {}
void signal(int num_blocks, bool expected_lockin)
{

View File

@ -24,6 +24,7 @@ private:
public:
int64_t BeginTime(const Consensus::Params& params) const override { return TestTime(10000); }
int64_t EndTime(const Consensus::Params& params) const override { return TestTime(20000); }
int MasternodeBeginHeight(const Consensus::Params& params) const override { return 0; }
int Period(const Consensus::Params& params) const override { return 1000; }
int Threshold(const Consensus::Params& params, int nAttempt) const override { return 900; }
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return (pindex->nVersion & 0x100); }

View File

@ -1927,6 +1927,7 @@ public:
int64_t BeginTime(const Consensus::Params& params) const override { return 0; }
int64_t EndTime(const Consensus::Params& params) const override { return std::numeric_limits<int64_t>::max(); }
int MasternodeBeginHeight(const Consensus::Params& params) const override { return 0; }
int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
int Threshold(const Consensus::Params& params, int nAttempt) const override { return params.nRuleChangeActivationThreshold; }

View File

@ -5,10 +5,33 @@
#include <versionbits.h>
#include <consensus/params.h>
static int calculateStartHeight(const CBlockIndex* pindexPrev, ThresholdState state, const int nPeriod, const ThresholdConditionCache& cache) {
int nStartHeight{std::numeric_limits<int>::max()};
// we are interested only in state STARTED
// For state DEFINED: it is not started yet, nothing to do
// For states LOCKED_IN, FAILED, ACTIVE: it is too late, nothing to do
while (state == ThresholdState::STARTED) {
nStartHeight = std::min(pindexPrev->nHeight + 1, nStartHeight);
// we can walk back here because the only way for STARTED state to exist
// in cache already is to be calculated in previous runs via "walk forward"
// loop below starting from DEFINED state.
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
auto cache_it = cache.find(pindexPrev);
assert(cache_it != cache.end());
state = cache_it->second;
}
return nStartHeight;
}
ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
{
int nPeriod = Period(params);
int64_t nTimeStart = BeginTime(params);
int masternodeStartHeight = MasternodeBeginHeight(params);
int64_t nTimeTimeout = EndTime(params);
// Check if this deployment is always active.
@ -29,7 +52,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
cache[pindexPrev] = ThresholdState::DEFINED;
break;
}
if (pindexPrev->GetMedianTimePast() < nTimeStart) {
if (pindexPrev->GetMedianTimePast() < nTimeStart || pindexPrev->nHeight < masternodeStartHeight) {
// Optimization: don't recompute down further, as we know every earlier block will be before the start time
cache[pindexPrev] = ThresholdState::DEFINED;
break;
@ -42,35 +65,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
assert(cache.count(pindexPrev));
ThresholdState state = cache[pindexPrev];
auto pindex_search = pindexPrev;
auto state_search = state;
bool do_search{true};
int nStartHeight{std::numeric_limits<int>::max()};
while (do_search) {
switch (state_search) {
case ThresholdState::DEFINED: {
// not started yet, nothinig to do
do_search = false;
break;
}
case ThresholdState::STARTED: {
nStartHeight = std::min(nStartHeight, pindex_search->nHeight + 1);
// we can walk back here because the only way for STARTED state to exist
// in cache already is to be calculated in previous runs via "walk forward"
// loop below starting from DEFINED state.
pindex_search = pindex_search->GetAncestor(pindex_search->nHeight - nPeriod);
state_search = cache[pindex_search];
break;
}
case ThresholdState::LOCKED_IN:
case ThresholdState::FAILED:
case ThresholdState::ACTIVE: {
// too late, nothing to do
do_search = false;
break;
}
}
}
int nStartHeight = calculateStartHeight(pindexPrev, state, nPeriod, cache);
// Now walk forward and compute the state of descendants of pindexPrev
while (!vToCompute.empty()) {
@ -82,7 +77,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
case ThresholdState::DEFINED: {
if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
stateNext = ThresholdState::FAILED;
} else if (pindexPrev->GetMedianTimePast() >= nTimeStart) {
} else if (pindexPrev->GetMedianTimePast() >= nTimeStart && pindexPrev->nHeight >= masternodeStartHeight) {
stateNext = ThresholdState::STARTED;
nStartHeight = pindexPrev->nHeight + 1;
}
@ -210,6 +205,16 @@ private:
protected:
int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; }
int MasternodeBeginHeight(const Consensus::Params& params) const override {
const auto& deployment = params.vDeployments[id];
if (deployment.nMNActivationHeight == 0) {
return std::numeric_limits<int>::max();
}
if (deployment.nMNActivationHeight < 0) {
return 0;
}
return deployment.nMNActivationHeight;
}
int64_t EndTime(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeout; }
int Period(const Consensus::Params& params) const override { return params.vDeployments[id].nWindowSize ? params.vDeployments[id].nWindowSize : params.nMinerConfirmationWindow; }
int Threshold(const Consensus::Params& params, int nAttempt) const override

View File

@ -56,6 +56,7 @@ 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 int MasternodeBeginHeight(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, int nAttempt) const =0;

View File

@ -24,7 +24,7 @@ llmq_type_strings = {llmq_test: 'llmq_test', llmq_test_v17: 'llmq_test_v17'}
class QuorumDataRecoveryTest(DashTestFramework):
def set_test_params(self):
extra_args = [["-vbparams=testdummy:0:999999999999:10:8:6:5"] for _ in range(9)]
extra_args = [["-vbparams=testdummy:0:999999999999:10:8:6:5:-1"] for _ in range(9)]
self.set_dash_test_params(9, 7, fast_dip3_enforcement=True, extra_args=extra_args)
self.set_dash_llmq_test_params(4, 3)

View File

@ -17,7 +17,7 @@ class NewQuorumTypeActivationTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
self.extra_args = [["-vbparams=testdummy:0:999999999999:10:8:6:5"]]
self.extra_args = [["-vbparams=testdummy:0:999999999999:10:8:6:5:-1"]]
def run_test(self):
self.log.info(get_bip9_details(self.nodes[0], 'testdummy'))