Skip to content
Merged
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
31 changes: 31 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ class CMainParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 2420; // 60% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods

consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].bit = 10;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nStartTime = 19999999999; // TODO: To be determined later
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = 999999999999ULL;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 4032;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 3226; // 80% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 2420; // 60% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nFalloffCoeff = 5; // this corresponds to 10 periods

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000008677827656704520eb39"); // 1889000
Expand Down Expand Up @@ -377,6 +384,14 @@ class CTestNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 60; // 60% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods

consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].bit = 10;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nStartTime = 19999999999; // TODO: To be determined later
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = 999999999999ULL;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 80; // 80% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 60; // 60% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nFalloffCoeff = 5; // this corresponds to 10 periods

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000002d68c8cc1b8e54b"); // 851000

Expand Down Expand Up @@ -539,6 +554,14 @@ class CDevNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 60; // 60% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods

consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].bit = 10;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nStartTime = 1661990400; // Sep 1st, 2022
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = 999999999999ULL;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 120;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 80; // 80% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 60; // 60% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nFalloffCoeff = 5; // this corresponds to 10 periods

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000000000000");

Expand Down Expand Up @@ -767,6 +790,14 @@ class CRegTestParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 288; // 60% of 480
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods

consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].bit = 10;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = 999999999999ULL;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 1030;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 800; // 80% of 1000
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 600; // 60% of 1000
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nFalloffCoeff = 5; // this corresponds to 10 periods

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");

Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace Consensus {
enum DeploymentPos {
DEPLOYMENT_TESTDUMMY,
DEPLOYMENT_V20, // Deployment of EHF, LLMQ Randomness Beacon
DEPLOYMENT_MN_RR, // Deployment of Masternode Reward Location Reallocation
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
Expand Down
25 changes: 16 additions & 9 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,10 @@ CDeterministicMNCPtr CDeterministicMNList::GetMNPayee(const CBlockIndex* pIndex)
}

bool isv19Active = llmq::utils::IsV19Active(pIndex);
// Starting from v19 and until v20 (Platform release), HPMN will be rewarded 4 blocks in a row
// TODO: Skip this code once v20 is active
bool isMNRewardReallocation = llmq::utils::IsMNRewardReallocationActive(pIndex);
// Starting from v19 and until MNRewardReallocation (Platform release), HPMN will be rewarded 4 blocks in a row
CDeterministicMNCPtr best = nullptr;
if (isv19Active) {
if (isv19Active && !isMNRewardReallocation) {
ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) {
if (dmn->pdmnState->nLastPaidHeight == nHeight) {
// We found the last MN Payee.
Expand All @@ -211,7 +211,12 @@ CDeterministicMNCPtr CDeterministicMNList::GetMNPayee(const CBlockIndex* pIndex)
return best;
}

std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayees(int nCount) const
std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayeesAtChainTip(int nCount) const
{
return GetProjectedMNPayees(::ChainActive()[nHeight], nCount);
}

std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayees(const CBlockIndex* const pindex, int nCount) const
{
if (nCount < 0 ) {
return {};
Expand All @@ -223,11 +228,12 @@ std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayees(int

auto remaining_hpmn_payments = 0;
CDeterministicMNCPtr hpmn_to_be_skipped = nullptr;
bool isMNRewardReallocation = llmq::utils::IsMNRewardReallocationActive(pindex);
ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) {
if (dmn->pdmnState->nLastPaidHeight == nHeight) {
// We found the last MN Payee.
// If the last payee is a HPMN, we need to check its consecutive payments and pay him again if needed
if (dmn->nType == MnType::HighPerformance && dmn->pdmnState->nConsecutivePayments < dmn_types::HighPerformance.voting_weight) {
if (!isMNRewardReallocation && dmn->nType == MnType::HighPerformance && dmn->pdmnState->nConsecutivePayments < dmn_types::HighPerformance.voting_weight) {
remaining_hpmn_payments = dmn_types::HighPerformance.voting_weight - dmn->pdmnState->nConsecutivePayments;
for ([[maybe_unused]] auto _ : irange::range(remaining_hpmn_payments)) {
result.emplace_back(dmn);
Expand Down Expand Up @@ -720,6 +726,8 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C

DecreasePoSePenalties(newList);

bool isMNRewardReallocation = llmq::utils::IsMNRewardReallocationActive(pindexPrev);

// we skip the coinbase
for (int i = 1; i < (int)block.vtx.size(); i++) {
const CTransaction& tx = *block.vtx[i];
Expand Down Expand Up @@ -937,11 +945,10 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
auto dmn = newList.GetMN(payee->proTxHash);
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
newState->nLastPaidHeight = nHeight;
// Starting from v19 and until v20, HPMN will be paid 4 blocks in a row
// Starting from v19 and until MNRewardReallocation, HPMN will be paid 4 blocks in a row
// No need to check if v19 is active, since HPMN ProRegTx are allowed only after v19 activation
// TODO: Skip this code once v20 is active
// Note: If the payee wasn't found in the current block that's fine
if (dmn->nType == MnType::HighPerformance) {
if (dmn->nType == MnType::HighPerformance && !isMNRewardReallocation) {
++newState->nConsecutivePayments;
if (debugLogs) {
LogPrint(BCLog::MNPAYMENTS, "CDeterministicMNManager::%s -- MN %s is a HPMN, bumping nConsecutivePayments to %d\n",
Expand All @@ -959,8 +966,8 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
// reset nConsecutivePayments on non-paid HPMNs
auto newList2 = newList;
newList2.ForEachMN(false, [&](auto& dmn) {
if (payee != nullptr && dmn.proTxHash == payee->proTxHash) return;
if (dmn.nType != MnType::HighPerformance) return;
if (payee != nullptr && dmn.proTxHash == payee->proTxHash && !isMNRewardReallocation) return;
if (dmn.pdmnState->nConsecutivePayments == 0) return;
if (debugLogs) {
LogPrint(BCLog::MNPAYMENTS, "CDeterministicMNManager::%s -- MN %s, reset nConsecutivePayments %d->0\n",
Expand Down
3 changes: 2 additions & 1 deletion src/evo/deterministicmns.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ class CDeterministicMNList
* @param nCount the number of payees to return. "nCount = max()"" means "all", use it to avoid calling GetValidWeightedMNsCount twice.
* @return
*/
[[nodiscard]] std::vector<CDeterministicMNCPtr> GetProjectedMNPayees(int nCount = std::numeric_limits<int>::max()) const;
[[nodiscard]] std::vector<CDeterministicMNCPtr> GetProjectedMNPayees(const CBlockIndex* const pindex, int nCount = std::numeric_limits<int>::max()) const;
[[nodiscard]] std::vector<CDeterministicMNCPtr> GetProjectedMNPayeesAtChainTip(int nCount = std::numeric_limits<int>::max()) const;

/**
* Calculate a quorum based on the modifier. The resulting list is deterministically sorted by score
Expand Down
9 changes: 9 additions & 0 deletions src/llmq/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,15 @@ bool IsV20Active(const CBlockIndex* pindex)
return VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V20, llmq_versionbitscache) == ThresholdState::ACTIVE;
}

bool IsMNRewardReallocationActive(const CBlockIndex* pindex)
{
assert(pindex);
if (!IsV20Active(pindex)) return false;

LOCK(cs_llmq_vbc);
return VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_MN_RR, llmq_versionbitscache) == ThresholdState::ACTIVE;
}

bool IsInstantSendLLMQTypeShared()
{
if (Params().GetConsensus().llmqTypeInstantSend == Params().GetConsensus().llmqTypeChainLocks ||
Expand Down
1 change: 1 addition & 0 deletions src/llmq/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ bool IsDIP0024Active(const CBlockIndex* pindex);
bool IsV19Active(const CBlockIndex* pindex);
const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex);
bool IsV20Active(const CBlockIndex* pindex);
bool IsMNRewardReallocationActive(const CBlockIndex* pindex);

/// Returns the state of `-llmq-data-recovery`
bool QuorumDataRecoveryEnabled();
Expand Down
2 changes: 1 addition & 1 deletion src/qt/masternodelist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ void MasternodeList::updateDIP3List()

nTimeUpdatedDIP3 = GetTime();

auto projectedPayees = mnList.GetProjectedMNPayees();
auto projectedPayees = mnList.GetProjectedMNPayeesAtChainTip();
std::map<uint256, int> nextPayments;
for (size_t i = 0; i < projectedPayees.size(); i++) {
const auto& dmn = projectedPayees[i];
Expand Down
1 change: 1 addition & 0 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1642,6 +1642,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
BuriedForkDescPushBack(softforks, "realloc", consensusParams.BRRHeight, height);
BuriedForkDescPushBack(softforks, "v19", consensusParams.V19Height, height);
BIP9SoftForkDescPushBack(tip, softforks, "v20", consensusParams, Consensus::DEPLOYMENT_V20);
BIP9SoftForkDescPushBack(tip, softforks, "mn_rr", consensusParams, Consensus::DEPLOYMENT_MN_RR);
BIP9SoftForkDescPushBack(tip, softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);

obj.pushKV("softforks", softforks);
Expand Down
4 changes: 2 additions & 2 deletions src/rpc/masternode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ static UniValue masternode_count(const JSONRPCRequest& request)
static UniValue GetNextMasternodeForPayment(int heightShift)
{
auto mnList = deterministicMNManager->GetListAtChainTip();
auto payees = mnList.GetProjectedMNPayees(heightShift);
auto payees = mnList.GetProjectedMNPayeesAtChainTip(heightShift);
if (payees.empty())
return "unknown";
auto payee = payees.back();
Expand Down Expand Up @@ -362,7 +362,7 @@ static UniValue masternode_winners(const JSONRPCRequest& request, const Chainsta
obj.pushKV(strprintf("%d", h), strPayments);
}

auto projection = deterministicMNManager->GetListForBlock(pindexTip).GetProjectedMNPayees(20);
auto projection = deterministicMNManager->GetListForBlock(pindexTip).GetProjectedMNPayees(pindexTip, 20);
for (size_t i = 0; i < projection.size(); i++) {
int h = nChainTipHeight + 1 + i;
std::string strPayments = GetRequiredPaymentsString(h, projection[i]);
Expand Down
4 changes: 4 additions & 0 deletions src/versionbitsinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
/*.name =*/"v20",
/*.gbt_force =*/true,
},
{
/*.name =*/"mn_rr",
/*.gbt_force =*/true,
},
};
28 changes: 21 additions & 7 deletions test/functional/feature_llmq_hpmn.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,41 +114,55 @@ def run_test(self):
self.log.info("Test that HPMNs are paid 4x blocks in a row")
self.test_hpmn_payments(window_analysis=256)

self.activate_mn_rr()
self.log.info("Activated MN RewardReallocation at height:" + str(self.nodes[0].getblockcount()))

# Generate a few blocks to make HPMN/MN analysis on a pure MN RewardReallocation window
self.bump_mocktime(1)
self.nodes[0].generate(4)
self.sync_blocks()

self.log.info("Test that HPMNs are paid 1 block in a row after MN RewardReallocation activation")
self.test_hpmn_payments(window_analysis=256, v20active=True)

self.log.info(self.nodes[0].masternodelist())

return

def test_hpmn_payments(self, window_analysis):
def test_hpmn_payments(self, window_analysis, v20active=False):
current_hpmn = None
consecutive_payments = 0
n_payments = 0 if v20active else 4
for i in range(0, window_analysis):
payee = self.get_mn_payee_for_block(self.nodes[0].getbestblockhash())
if payee is not None and payee.hpmn:
if current_hpmn is not None and payee.proTxHash == current_hpmn.proTxHash:
# same HPMN
assert consecutive_payments > 0
consecutive_payments += 1
if not v20active:
consecutive_payments += 1
consecutive_payments_rpc = self.nodes[0].protx('info', current_hpmn.proTxHash)['state']['consecutivePayments']
assert_equal(consecutive_payments, consecutive_payments_rpc)
else:
# new HPMN
if current_hpmn is not None:
# make sure the old one was paid 4 times in a row
assert_equal(consecutive_payments, 4)
# make sure the old one was paid N times in a row
assert_equal(consecutive_payments, n_payments)
consecutive_payments_rpc = self.nodes[0].protx('info', current_hpmn.proTxHash)['state']['consecutivePayments']
# old HPMN should have its nConsecutivePayments reset to 0
assert_equal(consecutive_payments_rpc, 0)
consecutive_payments_rpc = self.nodes[0].protx('info', payee.proTxHash)['state']['consecutivePayments']
# if hpmn is the one we start "for" loop with,
# we have no idea how many times it was paid before - rely on rpc results here
consecutive_payments = consecutive_payments_rpc if i == 0 and current_hpmn is None else 1
new_payment_value = 0 if v20active else 1
consecutive_payments = consecutive_payments_rpc if i == 0 and current_hpmn is None else new_payment_value
current_hpmn = payee
assert_equal(consecutive_payments, consecutive_payments_rpc)
else:
# not a HPMN
if current_hpmn is not None:
# make sure the old one was paid 4 times in a row
assert_equal(consecutive_payments, 4)
# make sure the old one was paid N times in a row
assert_equal(consecutive_payments, n_payments)
consecutive_payments_rpc = self.nodes[0].protx('info', current_hpmn.proTxHash)['state']['consecutivePayments']
# old HPMN should have its nConsecutivePayments reset to 0
assert_equal(consecutive_payments_rpc, 0)
Expand Down
9 changes: 9 additions & 0 deletions test/functional/rpc_blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,15 @@ def _test_getblockchaininfo(self):
'timeout': 999999999999,
'since': 0
}, 'active': False},
'mn_rr': {
'type': 'bip9',
'bip9': {
'status': 'defined',
'start_time': 0,
'timeout': 999999999999,
'since': 0
},
'active': False},
'testdummy': {
'type': 'bip9',
'bip9': {
Expand Down
2 changes: 2 additions & 0 deletions test/functional/test_framework/test_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,8 @@ def activate_v19(self, expected_activation_height=None):

def activate_v20(self, expected_activation_height=None):
self.activate_by_name('v20', expected_activation_height)
def activate_mn_rr(self, expected_activation_height=None):
self.activate_by_name('mn_rr', expected_activation_height)

def set_dash_llmq_test_params(self, llmq_size, llmq_threshold):
self.llmq_size = llmq_size
Expand Down