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); 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) void CChainParams::AddLLMQ(Consensus::LLMQType llmqType)
{ {
assert(!GetLLMQ(llmqType).has_value()); assert(!GetLLMQ(llmqType).has_value());
@ -909,7 +933,7 @@ public:
/** /**
* Allows modifying the Version Bits regtest parameters. * 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].nStartTime = nStartTime;
consensus.vDeployments[d].nTimeout = nTimeout; consensus.vDeployments[d].nTimeout = nTimeout;
@ -925,6 +949,9 @@ public:
if (nFalloffCoeff != -1) { if (nFalloffCoeff != -1) {
consensus.vDeployments[d].nFalloffCoeff = nFalloffCoeff; consensus.vDeployments[d].nFalloffCoeff = nFalloffCoeff;
} }
if (nMNActivationHeight != -1) {
consensus.vDeployments[d].nMNActivationHeight = nMNActivationHeight;
}
} }
void UpdateActivationParametersFromArgs(const ArgsManager& args); void UpdateActivationParametersFromArgs(const ArgsManager& args);
@ -998,13 +1025,13 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
for (const std::string& strDeployment : args.GetArgs("-vbparams")) { for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
std::vector<std::string> vDeploymentParams = SplitString(strDeployment, ':'); 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 " throw std::runtime_error("Version bits parameters malformed, expecting "
"<deployment>:<start>:<end> or " "<deployment>:<start>:<end> or "
"<deployment>:<start>:<end>:<window>:<threshold> 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)) { if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1])); 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])); throw std::runtime_error(strprintf("Invalid nThresholdStart (%s)", vDeploymentParams[4]));
} }
} }
if (vDeploymentParams.size() == 7) { if (vDeploymentParams.size() == 8) {
if (!ParseInt64(vDeploymentParams[5], &nThresholdMin)) { if (!ParseInt64(vDeploymentParams[5], &nThresholdMin)) {
throw std::runtime_error(strprintf("Invalid nThresholdMin (%s)", vDeploymentParams[5])); throw std::runtime_error(strprintf("Invalid nThresholdMin (%s)", vDeploymentParams[5]));
} }
if (!ParseInt64(vDeploymentParams[6], &nFalloffCoeff)) { if (!ParseInt64(vDeploymentParams[6], &nFalloffCoeff)) {
throw std::runtime_error(strprintf("Invalid nFalloffCoeff (%s)", vDeploymentParams[6])); 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; bool found = false;
for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) { 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; found = true;
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, window=%ld, thresholdstart=%ld, thresholdmin=%ld, falloffcoeff=%ld\n", 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); vDeploymentParams[0], nStartTime, nTimeout, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff, nMNActivationHeight);
break; break;
} }
} }

View File

@ -134,6 +134,7 @@ public:
void UpdateDIP8Parameters(int nActivationHeight); void UpdateDIP8Parameters(int nActivationHeight);
void UpdateBudgetParameters(int nMasternodePaymentsStartBlock, int nBudgetPaymentsStartBlock, int nSuperblockStartBlock); void UpdateBudgetParameters(int nMasternodePaymentsStartBlock, int nBudgetPaymentsStartBlock, int nSuperblockStartBlock);
void UpdateLLMQInstantSend(Consensus::LLMQType llmqType); void UpdateLLMQInstantSend(Consensus::LLMQType llmqType);
bool UpdateMNActivationParam(int nBit, int height, int64_t timePast, bool fJustCheck) const;
int PoolMinParticipants() const { return nPoolMinParticipants; } int PoolMinParticipants() const { return nPoolMinParticipants; }
int PoolMaxParticipants() const { return nPoolMaxParticipants; } int PoolMaxParticipants() const { return nPoolMaxParticipants; }
int FulfilledRequestExpireTime() const { return nFulfilledRequestExpireTime; } 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. " 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); "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("-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). " "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 * process (which takes at least 3 BIP9 intervals). Only tests that specifically test the
* behaviour during activation cannot use this. */ * behaviour during activation cannot use this. */
static constexpr int64_t ALWAYS_ACTIVE = -1; 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 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) void signal(int num_blocks, bool expected_lockin)
{ {

View File

@ -24,6 +24,7 @@ private:
public: public:
int64_t BeginTime(const Consensus::Params& params) const override { return TestTime(10000); } int64_t BeginTime(const Consensus::Params& params) const override { return TestTime(10000); }
int64_t EndTime(const Consensus::Params& params) const override { return TestTime(20000); } 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 Period(const Consensus::Params& params) const override { return 1000; }
int Threshold(const Consensus::Params& params, int nAttempt) const override { return 900; } 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); } 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 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(); } 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 Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
int Threshold(const Consensus::Params& params, int nAttempt) const override { return params.nRuleChangeActivationThreshold; } int Threshold(const Consensus::Params& params, int nAttempt) const override { return params.nRuleChangeActivationThreshold; }

View File

@ -5,10 +5,33 @@
#include <versionbits.h> #include <versionbits.h>
#include <consensus/params.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 ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
{ {
int nPeriod = Period(params); int nPeriod = Period(params);
int64_t nTimeStart = BeginTime(params); int64_t nTimeStart = BeginTime(params);
int masternodeStartHeight = MasternodeBeginHeight(params);
int64_t nTimeTimeout = EndTime(params); int64_t nTimeTimeout = EndTime(params);
// Check if this deployment is always active. // Check if this deployment is always active.
@ -29,7 +52,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
cache[pindexPrev] = ThresholdState::DEFINED; cache[pindexPrev] = ThresholdState::DEFINED;
break; 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 // Optimization: don't recompute down further, as we know every earlier block will be before the start time
cache[pindexPrev] = ThresholdState::DEFINED; cache[pindexPrev] = ThresholdState::DEFINED;
break; break;
@ -42,35 +65,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
assert(cache.count(pindexPrev)); assert(cache.count(pindexPrev));
ThresholdState state = cache[pindexPrev]; ThresholdState state = cache[pindexPrev];
auto pindex_search = pindexPrev; int nStartHeight = calculateStartHeight(pindexPrev, state, nPeriod, cache);
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;
}
}
}
// Now walk forward and compute the state of descendants of pindexPrev // Now walk forward and compute the state of descendants of pindexPrev
while (!vToCompute.empty()) { while (!vToCompute.empty()) {
@ -82,7 +77,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
case ThresholdState::DEFINED: { case ThresholdState::DEFINED: {
if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
stateNext = ThresholdState::FAILED; stateNext = ThresholdState::FAILED;
} else if (pindexPrev->GetMedianTimePast() >= nTimeStart) { } else if (pindexPrev->GetMedianTimePast() >= nTimeStart && pindexPrev->nHeight >= masternodeStartHeight) {
stateNext = ThresholdState::STARTED; stateNext = ThresholdState::STARTED;
nStartHeight = pindexPrev->nHeight + 1; nStartHeight = pindexPrev->nHeight + 1;
} }
@ -210,6 +205,16 @@ private:
protected: protected:
int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; } 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; } 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 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 int Threshold(const Consensus::Params& params, int nAttempt) const override

View File

@ -56,6 +56,7 @@ class AbstractThresholdConditionChecker {
protected: protected:
virtual bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const =0; 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 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 int64_t EndTime(const Consensus::Params& params) const =0;
virtual int Period(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; 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): class QuorumDataRecoveryTest(DashTestFramework):
def set_test_params(self): 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_test_params(9, 7, fast_dip3_enforcement=True, extra_args=extra_args)
self.set_dash_llmq_test_params(4, 3) self.set_dash_llmq_test_params(4, 3)

View File

@ -17,7 +17,7 @@ class NewQuorumTypeActivationTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
self.setup_clean_chain = True self.setup_clean_chain = True
self.num_nodes = 1 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): def run_test(self):
self.log.info(get_bip9_details(self.nodes[0], 'testdummy')) self.log.info(get_bip9_details(self.nodes[0], 'testdummy'))