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
48 changes: 48 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,26 @@ static Consensus::LLMQParams llmq_test = {
.recoveryMembers = 3,
};

// this one is for testing only
static Consensus::LLMQParams llmq_test_v17 = {
.type = Consensus::LLMQ_TEST_V17,
.name = "llmq_test_v17",
.size = 3,
.minSize = 2,
.threshold = 2,

.dkgInterval = 24, // one DKG per hour
.dkgPhaseBlocks = 2,
.dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
.dkgMiningWindowEnd = 18,
.dkgBadVotesThreshold = 2,

.signingActiveQuorumCount = 2, // just a few ones to allow easier testing

.keepOldConnections = 3,
.recoveryMembers = 3,
};

// this one is for devnets only
static Consensus::LLMQParams llmq_devnet = {
.type = Consensus::LLMQ_DEVNET,
Expand Down Expand Up @@ -262,6 +282,26 @@ static Consensus::LLMQParams llmq400_85 = {
.recoveryMembers = 100,
};

// Used for Platform
static Consensus::LLMQParams llmq100_67 = {
.type = Consensus::LLMQ_100_67,
.name = "llmq_100_67",
.size = 100,
.minSize = 80,
.threshold = 67,

.dkgInterval = 24, // one DKG per hour
.dkgPhaseBlocks = 2,
.dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
.dkgMiningWindowEnd = 18,
.dkgBadVotesThreshold = 80,

.signingActiveQuorumCount = 24, // a full day worth of LLMQs

.keepOldConnections = 25,
.recoveryMembers = 50,
Comment on lines +301 to +302
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a bit more thought about these values and figuring out what a good value is

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

};


/**
* Main network
Expand Down Expand Up @@ -417,8 +457,10 @@ class CMainParams : public CChainParams {
consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60;
consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;
consensus.llmqs[Consensus::LLMQ_100_67] = llmq100_67;
consensus.llmqTypeChainLocks = Consensus::LLMQ_400_60;
consensus.llmqTypeInstantSend = Consensus::LLMQ_50_60;
consensus.llmqTypePlatform = Consensus::LLMQ_100_67;

fDefaultConsistencyChecks = false;
fRequireStandard = true;
Expand Down Expand Up @@ -615,8 +657,10 @@ class CTestNetParams : public CChainParams {
consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60;
consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;
consensus.llmqs[Consensus::LLMQ_100_67] = llmq100_67;
consensus.llmqTypeChainLocks = Consensus::LLMQ_50_60;
consensus.llmqTypeInstantSend = Consensus::LLMQ_50_60;
consensus.llmqTypePlatform = Consensus::LLMQ_100_67;

fDefaultConsistencyChecks = false;
fRequireStandard = false;
Expand Down Expand Up @@ -794,8 +838,10 @@ class CDevNetParams : public CChainParams {
consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60;
consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;
consensus.llmqs[Consensus::LLMQ_100_67] = llmq100_67;
consensus.llmqTypeChainLocks = Consensus::LLMQ_50_60;
consensus.llmqTypeInstantSend = Consensus::LLMQ_50_60;
consensus.llmqTypePlatform = Consensus::LLMQ_100_67;

fDefaultConsistencyChecks = false;
fRequireStandard = false;
Expand Down Expand Up @@ -972,8 +1018,10 @@ class CRegTestParams : public CChainParams {

// long living quorum params
consensus.llmqs[Consensus::LLMQ_TEST] = llmq_test;
consensus.llmqs[Consensus::LLMQ_TEST_V17] = llmq_test_v17;
consensus.llmqTypeChainLocks = Consensus::LLMQ_TEST;
consensus.llmqTypeInstantSend = Consensus::LLMQ_TEST;
consensus.llmqTypePlatform = Consensus::LLMQ_TEST;
}
};

Expand Down
5 changes: 5 additions & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,16 @@ enum LLMQType : uint8_t
LLMQ_50_60 = 1, // 50 members, 30 (60%) threshold, one per hour
LLMQ_400_60 = 2, // 400 members, 240 (60%) threshold, one every 12 hours
LLMQ_400_85 = 3, // 400 members, 340 (85%) threshold, one every 24 hours
LLMQ_100_67 = 4, // 100 members, 67 (67%) threshold, one per hour

// for testing only
LLMQ_TEST = 100, // 3 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestparams is used

// for devnets only
LLMQ_DEVNET = 101, // 10 members, 6 (60%) threshold, one per hour. Params might differ when -llmqdevnetparams is used

// for testing activation of new quorums only
LLMQ_TEST_V17 = 102, // 3 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestparams is used
};

// Configures a LLMQ and its DKG
Expand Down Expand Up @@ -190,6 +194,7 @@ struct Params {
std::map<LLMQType, LLMQParams> llmqs;
LLMQType llmqTypeChainLocks;
LLMQType llmqTypeInstantSend{LLMQ_NONE};
LLMQType llmqTypePlatform{LLMQ_NONE};
};
} // namespace Consensus

Expand Down
5 changes: 2 additions & 3 deletions src/llmq/quorums_blockprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,13 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex*
// The following checks make sure that there is always a (possibly null) commitment while in the mining phase
// until the first non-null commitment has been mined. After the non-null commitment, no other commitments are
// allowed, including null commitments.
for (const auto& p : Params().GetConsensus().llmqs) {
// Note: must only check quorums that were enabled at the _previous_ block height to match mining logic
for (const auto& type : CLLMQUtils::GetEnabledQuorumTypes(pindex->pprev)) {
// skip these checks when replaying blocks after the crash
if (!chainActive.Tip()) {
break;
}

auto type = p.first;

// does the currently processed block contain a (possibly null) commitment for the current session?
bool hasCommitmentInNewBlock = qcs.count(type) != 0;
bool isCommitmentRequired = IsCommitmentRequired(type, pindex->nHeight);
Expand Down
47 changes: 46 additions & 1 deletion src/llmq/quorums_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ namespace llmq

std::vector<CDeterministicMNCPtr> CLLMQUtils::GetAllQuorumMembers(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum)
{
if (!IsQuorumTypeEnabled(llmqType, pindexQuorum->pprev)) {
return {};
}
auto& params = Params().GetConsensus().llmqs.at(llmqType);
auto allMns = deterministicMNManager->GetListForBlock(pindexQuorum);
auto modifier = ::SerializeHash(std::make_pair(llmqType, pindexQuorum->GetBlockHash()));
Expand Down Expand Up @@ -50,7 +53,7 @@ bool CLLMQUtils::IsAllMembersConnectedEnabled(Consensus::LLMQType llmqType)
if (spork21 == 0) {
return true;
}
if (spork21 == 1 && llmqType != Consensus::LLMQ_400_60 && llmqType != Consensus::LLMQ_400_85) {
if (spork21 == 1 && llmqType != Consensus::LLMQ_100_67 && llmqType != Consensus::LLMQ_400_60 && llmqType != Consensus::LLMQ_400_85) {
return true;
}
return false;
Expand Down Expand Up @@ -254,5 +257,47 @@ bool CLLMQUtils::IsQuorumActive(Consensus::LLMQType llmqType, const uint256& quo
return false;
}

bool CLLMQUtils::IsQuorumTypeEnabled(Consensus::LLMQType llmqType, const CBlockIndex* pindex)
{
const Consensus::Params& consensusParams = Params().GetConsensus();
bool f_v17_Active = VersionBitsState(pindex, consensusParams, Consensus::DEPLOYMENT_V17, versionbitscache) == ThresholdState::ACTIVE;
Comment thread
UdjinM6 marked this conversation as resolved.

switch (llmqType)
{
case Consensus::LLMQ_50_60:
case Consensus::LLMQ_400_60:
case Consensus::LLMQ_400_85:
break;
case Consensus::LLMQ_100_67:
case Consensus::LLMQ_TEST_V17:
if (!f_v17_Active) {
return false;
}
break;
case Consensus::LLMQ_TEST:
case Consensus::LLMQ_DEVNET:
break;
default:
throw std::runtime_error(strprintf("%s: Unknown LLMQ type %d", __func__, llmqType));
}

return true;
}

std::vector<Consensus::LLMQType> CLLMQUtils::GetEnabledQuorumTypes(const CBlockIndex* pindex)
{
std::vector<Consensus::LLMQType> ret;
for (const auto& p : Params().GetConsensus().llmqs) {
if (IsQuorumTypeEnabled(p.first, pindex)) {
ret.push_back(p.first);
}
}
return ret;
}

Consensus::LLMQParams CLLMQUtils::GetLLMQParams(Consensus::LLMQType llmqType)
{
return Params().GetConsensus().llmqs.at(llmqType);
}

} // namespace llmq
3 changes: 3 additions & 0 deletions src/llmq/quorums_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class CLLMQUtils
static void AddQuorumProbeConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexQuorum, const uint256& myProTxHash);

static bool IsQuorumActive(Consensus::LLMQType llmqType, const uint256& quorumHash);
static bool IsQuorumTypeEnabled(Consensus::LLMQType llmqType, const CBlockIndex* pindex);
static std::vector<Consensus::LLMQType> GetEnabledQuorumTypes(const CBlockIndex* pindex);
static Consensus::LLMQParams GetLLMQParams(const Consensus::LLMQType llmqType);

template<typename NodesContainer, typename Continue, typename Callback>
static void IterateNodesRandom(NodesContainer& nodeStates, Continue&& cont, Callback&& callback, FastRandomContext& rnd)
Expand Down
4 changes: 2 additions & 2 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,9 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
: pblock->GetBlockTime();

if (fDIP0003Active_context) {
for (auto& p : chainparams.GetConsensus().llmqs) {
for (const Consensus::LLMQType& type : llmq::CLLMQUtils::GetEnabledQuorumTypes(pindexPrev)) {
CTransactionRef qcTx;
if (llmq::quorumBlockProcessor->GetMinableCommitmentTx(p.first, nHeight, qcTx)) {
if (llmq::quorumBlockProcessor->GetMinableCommitmentTx(type, nHeight, qcTx)) {
pblock->vtx.emplace_back(qcTx);
pblocktemplate->vTxFees.emplace_back(0);
pblocktemplate->vTxSigOps.emplace_back(0);
Expand Down
15 changes: 8 additions & 7 deletions src/rpc/rpcquorums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,16 @@ UniValue quorum_list(const JSONRPCRequest& request)

UniValue ret(UniValue::VOBJ);

for (auto& p : Params().GetConsensus().llmqs) {
for (auto& type : llmq::CLLMQUtils::GetEnabledQuorumTypes(chainActive.Tip())) {
const auto& params = llmq::CLLMQUtils::GetLLMQParams(type);
UniValue v(UniValue::VARR);

auto quorums = llmq::quorumManager->ScanQuorums(p.first, chainActive.Tip(), count > -1 ? count : p.second.signingActiveQuorumCount);
auto quorums = llmq::quorumManager->ScanQuorums(type, chainActive.Tip(), count > -1 ? count : params.signingActiveQuorumCount);
for (auto& q : quorums) {
v.push_back(q->qc.quorumHash.ToString());
}

ret.pushKV(p.second.name, v);
ret.pushKV(params.name, v);
}


Expand Down Expand Up @@ -181,8 +182,8 @@ UniValue quorum_dkgstatus(const JSONRPCRequest& request)

UniValue minableCommitments(UniValue::VOBJ);
UniValue quorumConnections(UniValue::VOBJ);
for (const auto& p : Params().GetConsensus().llmqs) {
auto& params = p.second;
for (const auto& type : llmq::CLLMQUtils::GetEnabledQuorumTypes(chainActive.Tip())) {
const auto& params = llmq::CLLMQUtils::GetLLMQParams(type);

if (fMasternodeMode) {
const CBlockIndex* pindexQuorum = chainActive[tipHeight - (tipHeight % params.dkgInterval)];
Expand Down Expand Up @@ -265,8 +266,8 @@ UniValue quorum_memberof(const JSONRPCRequest& request)

UniValue result(UniValue::VARR);

for (const auto& p : Params().GetConsensus().llmqs) {
auto& params = p.second;
for (const auto& type : llmq::CLLMQUtils::GetEnabledQuorumTypes(pindexTip)) {
const auto& params = llmq::CLLMQUtils::GetLLMQParams(type);
size_t count = params.signingActiveQuorumCount;
if (scanQuorumsCount != -1) {
count = (size_t)scanQuorumsCount;
Expand Down
34 changes: 34 additions & 0 deletions test/functional/feature_new_quorum_type_activation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env python3
# Copyright (c) 2020 The Dash Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, get_bip9_status

'''
feature_new_quorum_type_activation.py

Tests the activation of a new quorum type in v17 via a bip9-like hardfork

'''


class NewQuorumTypeActivationTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1

def run_test(self):
assert_equal(get_bip9_status(self.nodes[0], 'v17')['status'], 'locked_in')
ql = self.nodes[0].quorum("list")
assert_equal(len(ql), 1)
assert("llmq_test_v17" not in ql)
self.nodes[0].generate(99)
assert_equal(get_bip9_status(self.nodes[0], 'v17')['status'], 'active')
self.nodes[0].generate(1)
ql = self.nodes[0].quorum("list")
assert_equal(len(ql), 2)
assert("llmq_test_v17" in ql)


if __name__ == '__main__':
NewQuorumTypeActivationTest().main()
23 changes: 16 additions & 7 deletions test/functional/test_framework/test_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,16 @@ def check_dkg_comitments():
return all_ok
wait_until(check_dkg_comitments, timeout=timeout, sleep=0.1)

def wait_for_quorum_list(self, quorum_hash, nodes, timeout=15, sleep=2):
def wait_func():
if quorum_hash in self.nodes[0].quorum("list")["llmq_test"]:
return True
self.bump_mocktime(sleep, nodes=nodes)
self.nodes[0].generate(1)
sync_blocks(nodes)
return False
wait_until(wait_func, timeout=timeout, sleep=sleep)

def mine_quorum(self, expected_connections=None, expected_members=None, expected_contributions=None, expected_complaints=0, expected_justifications=0, expected_commitments=None, mninfos_online=None, mninfos_valid=None):
spork21_active = self.nodes[0].spork('show')['SPORK_21_QUORUM_ALL_CONNECTED'] <= 1

Expand All @@ -921,8 +931,6 @@ def mine_quorum(self, expected_connections=None, expected_members=None, expected

nodes = [self.nodes[0]] + [mn.node for mn in mninfos_online]

quorums = self.nodes[0].quorum("list")

# move forward to next DKG
skip_count = 24 - (self.nodes[0].getblockcount() % 24)
if skip_count != 0:
Expand Down Expand Up @@ -974,12 +982,13 @@ def mine_quorum(self, expected_connections=None, expected_members=None, expected
self.log.info("Mining final commitment")
self.bump_mocktime(1, nodes=nodes)
self.nodes[0].generate(1)
while quorums == self.nodes[0].quorum("list"):
time.sleep(2)
self.bump_mocktime(1, nodes=nodes)
self.nodes[0].generate(1)
sync_blocks(nodes)
sync_blocks(nodes)

self.log.info("Waiting for quorum to appear in the list")
self.wait_for_quorum_list(q, nodes)

new_quorum = self.nodes[0].quorum("list", 1)["llmq_test"][0]
assert_equal(q, new_quorum)
quorum_info = self.nodes[0].quorum("info", 100, new_quorum)

# Mine 8 (SIGN_HEIGHT_OFFSET) more blocks to make sure that the new quorum gets eligable for signing sessions
Expand Down
1 change: 1 addition & 0 deletions test/functional/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
'wallet_encryption.py',
'feature_dersig.py',
'feature_cltv.py',
'feature_new_quorum_type_activation.py',
'feature_governance_objects.py',
'rpc_uptime.py',
'wallet_resendwallettransactions.py',
Expand Down