mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
feat!: calculate quorum members using v20 cbtx clsig (#5366)
## Issue being fixed or feature implemented Implementation of Randomness Beacon Part 2. This PR is the next step of #5262. Starting from v20 activation fork, members for quorums are sorted using (if available) the best CL signature found in Coinbase. If no CL signature is present yet, then the usual way is used (By using Blockhash instead) ## What was done? ## How Has This Been Tested? Test `feature_llmq_rotation.py` was updated to cover both rotated and non-rotated quorums. 2 quorums are mined first to ensure Chainlock are working earlier. Then dip_24 activation is replaced by v20 activation. The only direct way to test this change is to make sure that all expected quorums after v20 activation are properly formed. Note: A `wait_for_chainlocked_block_all_nodes` is called between every rotation cycle to ensure that Coinbase will use a different Chainlock signature. ## Breaking Changes Yes, quorum members will be calculated differently. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
parent
04a31c76e0
commit
9eee9ee680
@ -26,6 +26,11 @@
|
||||
|
||||
static constexpr int TESTNET_LLMQ_25_67_ACTIVATION_HEIGHT = 847000;
|
||||
|
||||
/**
|
||||
* Forward declarations
|
||||
*/
|
||||
std::optional<std::pair<CBLSSignature, uint32_t>> GetNonNullCoinbaseChainlock(const CBlockIndex* pindex);
|
||||
|
||||
namespace llmq
|
||||
{
|
||||
|
||||
@ -43,7 +48,7 @@ static std::vector<std::vector<CDeterministicMNCPtr>> BuildNewQuorumQuarterMembe
|
||||
|
||||
static PreviousQuorumQuarters GetPreviousQuorumQuarterMembers(const Consensus::LLMQParams& llmqParams, const CBlockIndex* pBlockHMinusCIndex, const CBlockIndex* pBlockHMinus2CIndex, const CBlockIndex* pBlockHMinus3CIndex, int nHeight);
|
||||
static std::vector<std::vector<CDeterministicMNCPtr>> GetQuorumQuarterMembersBySnapshot(const Consensus::LLMQParams& llmqParams, const CBlockIndex* pQuorumBaseBlockIndex, const llmq::CQuorumSnapshot& snapshot, int nHeights);
|
||||
static std::pair<CDeterministicMNList, CDeterministicMNList> GetMNUsageBySnapshot(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const llmq::CQuorumSnapshot& snapshot, int nHeight);
|
||||
static std::pair<CDeterministicMNList, CDeterministicMNList> GetMNUsageBySnapshot(const Consensus::LLMQParams& llmqParams, const CBlockIndex* pQuorumBaseBlockIndex, const llmq::CQuorumSnapshot& snapshot, int nHeight);
|
||||
|
||||
static void BuildQuorumSnapshot(const Consensus::LLMQParams& llmqParams, const CDeterministicMNList& allMns, const CDeterministicMNList& mnUsedAtH, std::vector<CDeterministicMNCPtr>& sortedCombinedMns, CQuorumSnapshot& quorumSnapshot, int nHeight, std::vector<int>& skipList, const CBlockIndex* pQuorumBaseBlockIndex);
|
||||
|
||||
@ -58,6 +63,29 @@ void PreComputeQuorumMembers(const CBlockIndex* pQuorumBaseBlockIndex, bool rese
|
||||
}
|
||||
}
|
||||
|
||||
uint256 GetHashModifier(const Consensus::LLMQParams& llmqParams, const CBlockIndex* pQuorumBaseBlockIndex)
|
||||
{
|
||||
const CBlockIndex* pWorkBlockIndex = pQuorumBaseBlockIndex->GetAncestor(pQuorumBaseBlockIndex->nHeight - 8);
|
||||
|
||||
if (IsV20Active(pWorkBlockIndex)) {
|
||||
// v20 is active: calculate modifier using the new way.
|
||||
auto cbcl = GetNonNullCoinbaseChainlock(pWorkBlockIndex);
|
||||
if (cbcl.has_value()) {
|
||||
// We have a non-null CL signature: calculate modifier using this CL signature
|
||||
auto& [bestCLSignature, bestCLHeightDiff] = cbcl.value();
|
||||
return ::SerializeHash(std::make_tuple(llmqParams.type, pWorkBlockIndex->nHeight, bestCLSignature));
|
||||
}
|
||||
// No non-null CL signature found in coinbase: calculate modifier using block hash only
|
||||
return ::SerializeHash(std::make_pair(llmqParams.type, pWorkBlockIndex->GetBlockHash()));
|
||||
}
|
||||
|
||||
// v20 isn't active yet: calculate modifier using the usual way
|
||||
if (llmqParams.useRotation) {
|
||||
return ::SerializeHash(std::make_pair(llmqParams.type, pWorkBlockIndex->GetBlockHash()));
|
||||
}
|
||||
return ::SerializeHash(std::make_pair(llmqParams.type, pQuorumBaseBlockIndex->GetBlockHash()));
|
||||
}
|
||||
|
||||
std::vector<CDeterministicMNCPtr> GetAllQuorumMembers(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, bool reset_cache)
|
||||
{
|
||||
static CCriticalSection cs_members;
|
||||
@ -135,11 +163,19 @@ std::vector<CDeterministicMNCPtr> GetAllQuorumMembers(Consensus::LLMQType llmqTy
|
||||
|
||||
std::vector<CDeterministicMNCPtr> ComputeQuorumMembers(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex)
|
||||
{
|
||||
auto allMns = deterministicMNManager->GetListForBlock(pQuorumBaseBlockIndex);
|
||||
auto modifier = ::SerializeHash(std::make_pair(llmqType, pQuorumBaseBlockIndex->GetBlockHash()));
|
||||
bool HPMNOnly = (Params().GetConsensus().llmqTypePlatform == llmqType) && IsV19Active(pQuorumBaseBlockIndex);
|
||||
const auto& llmq_params_opt = GetLLMQParams(llmqType);
|
||||
assert(llmq_params_opt.has_value());
|
||||
if (llmq_params_opt->useRotation) {
|
||||
ASSERT_IF_DEBUG(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
const CBlockIndex* pWorkBlockIndex = IsV20Active(pQuorumBaseBlockIndex) ?
|
||||
pQuorumBaseBlockIndex->GetAncestor(pQuorumBaseBlockIndex->nHeight - 8) :
|
||||
pQuorumBaseBlockIndex;
|
||||
const auto modifier = GetHashModifier(llmq_params_opt.value(), pQuorumBaseBlockIndex);
|
||||
auto allMns = deterministicMNManager->GetListForBlock(pWorkBlockIndex);
|
||||
return allMns.CalculateQuorum(llmq_params_opt->size, modifier, HPMNOnly);
|
||||
}
|
||||
|
||||
@ -264,7 +300,7 @@ std::vector<std::vector<CDeterministicMNCPtr>> BuildNewQuorumQuarterMembers(cons
|
||||
size_t quorumSize = static_cast<size_t>(llmqParams.size);
|
||||
auto quarterSize{quorumSize / 4};
|
||||
const CBlockIndex* pWorkBlockIndex = pQuorumBaseBlockIndex->GetAncestor(pQuorumBaseBlockIndex->nHeight - 8);
|
||||
auto modifier = ::SerializeHash(std::make_pair(llmqParams.type, pWorkBlockIndex->GetBlockHash()));
|
||||
const auto modifier = GetHashModifier(llmqParams, pQuorumBaseBlockIndex);
|
||||
|
||||
auto allMns = deterministicMNManager->GetListForBlock(pWorkBlockIndex);
|
||||
|
||||
@ -414,8 +450,7 @@ void BuildQuorumSnapshot(const Consensus::LLMQParams& llmqParams, const CDetermi
|
||||
CQuorumSnapshot& quorumSnapshot, int nHeight, std::vector<int>& skipList, const CBlockIndex* pQuorumBaseBlockIndex)
|
||||
{
|
||||
quorumSnapshot.activeQuorumMembers.resize(allMns.GetAllMNsCount());
|
||||
const CBlockIndex* pWorkBlockIndex = pQuorumBaseBlockIndex->GetAncestor(pQuorumBaseBlockIndex->nHeight - 8);
|
||||
auto modifier = ::SerializeHash(std::make_pair(llmqParams.type, pWorkBlockIndex->GetBlockHash()));
|
||||
const auto modifier = GetHashModifier(llmqParams, pQuorumBaseBlockIndex);
|
||||
auto sortedAllMns = allMns.CalculateQuorum(allMns.GetAllMNsCount(), modifier);
|
||||
|
||||
LogPrint(BCLog::LLMQ, "BuildQuorumSnapshot h[%d] numMns[%d]\n", pQuorumBaseBlockIndex->nHeight, allMns.GetAllMNsCount());
|
||||
@ -447,9 +482,8 @@ std::vector<std::vector<CDeterministicMNCPtr>> GetQuorumQuarterMembersBySnapshot
|
||||
{
|
||||
std::vector<CDeterministicMNCPtr> sortedCombinedMns;
|
||||
{
|
||||
const CBlockIndex* pWorkBlockIndex = pQuorumBaseBlockIndex->GetAncestor(pQuorumBaseBlockIndex->nHeight - 8);
|
||||
const auto modifier = ::SerializeHash(std::make_pair(llmqParams.type, pWorkBlockIndex->GetBlockHash()));
|
||||
const auto [MnsUsedAtH, MnsNotUsedAtH] = GetMNUsageBySnapshot(llmqParams.type, pQuorumBaseBlockIndex, snapshot, nHeight);
|
||||
const auto modifier = GetHashModifier(llmqParams, pQuorumBaseBlockIndex);
|
||||
const auto [MnsUsedAtH, MnsNotUsedAtH] = GetMNUsageBySnapshot(llmqParams, pQuorumBaseBlockIndex, snapshot, nHeight);
|
||||
// the list begins with all the unused MNs
|
||||
auto sortedMnsNotUsedAtH = MnsNotUsedAtH.CalculateQuorum(MnsNotUsedAtH.GetAllMNsCount(), modifier);
|
||||
sortedCombinedMns = std::move(sortedMnsNotUsedAtH);
|
||||
@ -531,7 +565,7 @@ std::vector<std::vector<CDeterministicMNCPtr>> GetQuorumQuarterMembersBySnapshot
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<CDeterministicMNList, CDeterministicMNList> GetMNUsageBySnapshot(Consensus::LLMQType llmqType,
|
||||
std::pair<CDeterministicMNList, CDeterministicMNList> GetMNUsageBySnapshot(const Consensus::LLMQParams& llmqParams,
|
||||
const CBlockIndex* pQuorumBaseBlockIndex,
|
||||
const llmq::CQuorumSnapshot& snapshot,
|
||||
int nHeight)
|
||||
@ -540,7 +574,7 @@ std::pair<CDeterministicMNList, CDeterministicMNList> GetMNUsageBySnapshot(Conse
|
||||
CDeterministicMNList nonUsedMNs;
|
||||
|
||||
const CBlockIndex* pWorkBlockIndex = pQuorumBaseBlockIndex->GetAncestor(pQuorumBaseBlockIndex->nHeight - 8);
|
||||
auto modifier = ::SerializeHash(std::make_pair(llmqType, pWorkBlockIndex->GetBlockHash()));
|
||||
const auto modifier = GetHashModifier(llmqParams, pQuorumBaseBlockIndex);
|
||||
|
||||
auto allMns = deterministicMNManager->GetListForBlock(pWorkBlockIndex);
|
||||
auto sortedAllMns = allMns.CalculateQuorum(allMns.GetAllMNsCount(), modifier);
|
||||
|
@ -58,7 +58,7 @@ namespace utils
|
||||
std::vector<CDeterministicMNCPtr> GetAllQuorumMembers(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, bool reset_cache = false);
|
||||
|
||||
void PreComputeQuorumMembers(const CBlockIndex* pQuorumBaseBlockIndex, bool reset_cache = false);
|
||||
|
||||
uint256 GetHashModifier(const Consensus::LLMQParams& llmqParams, const CBlockIndex* pQuorumBaseBlockIndex);
|
||||
uint256 BuildCommitmentHash(Consensus::LLMQType llmqType, const uint256& blockHash, const std::vector<bool>& validMembers, const CBLSPublicKey& pubKey, const uint256& vvecHash);
|
||||
uint256 BuildSignHash(Consensus::LLMQType llmqType, const uint256& quorumHash, const uint256& id, const uint256& msgHash);
|
||||
|
||||
|
@ -73,20 +73,58 @@ class LLMQQuorumRotationTest(DashTestFramework):
|
||||
self.nodes[0].sporkupdate("SPORK_17_QUORUM_DKG_ENABLED", 0)
|
||||
self.wait_for_sporks_same()
|
||||
|
||||
self.activate_dip0024(expected_activation_height=900)
|
||||
self.log.info("Activated DIP0024 at height:" + str(self.nodes[0].getblockcount()))
|
||||
b_h_0 = self.nodes[0].getbestblockhash()
|
||||
|
||||
#Mine 2 quorums so that Chainlocks can be available: Need them to include CL in CbTx as soon as v20 activates
|
||||
self.log.info("Mining 2 quorums")
|
||||
h_0 = self.mine_quorum()
|
||||
h_100_0 = QuorumId(100, int(h_0, 16))
|
||||
h_106_0 = QuorumId(106, int(h_0, 16))
|
||||
h_104_0 = QuorumId(104, int(h_0, 16))
|
||||
h_1 = self.mine_quorum()
|
||||
h_100_1 = QuorumId(100, int(h_1, 16))
|
||||
h_106_1 = QuorumId(106, int(h_1, 16))
|
||||
h_104_1 = QuorumId(104, int(h_1, 16))
|
||||
|
||||
self.log.info("Mine single block, wait for chainlock")
|
||||
self.nodes[0].generate(1)
|
||||
self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash())
|
||||
|
||||
b_h_1 = self.nodes[0].getbestblockhash()
|
||||
|
||||
expectedDeleted = []
|
||||
expectedNew = [h_100_0, h_106_0, h_104_0, h_100_1, h_106_1, h_104_1]
|
||||
quorumList = self.test_getmnlistdiff_quorums(b_h_0, b_h_1, {}, expectedDeleted, expectedNew)
|
||||
|
||||
self.activate_v20(expected_activation_height=1440)
|
||||
self.log.info("Activated v20 at height:" + str(self.nodes[0].getblockcount()))
|
||||
|
||||
# v20 is active for the next block, not for the tip
|
||||
self.nodes[0].generate(1)
|
||||
|
||||
self.log.info("Wait for chainlock")
|
||||
self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash())
|
||||
|
||||
#At this point, we need to move forward 3 cycles (3 x 24 blocks) so the first 3 quarters can be created (without DKG sessions)
|
||||
#self.log.info("Start at H height:" + str(self.nodes[0].getblockcount()))
|
||||
self.move_to_next_cycle()
|
||||
self.log.info("Cycle H height:" + str(self.nodes[0].getblockcount()))
|
||||
self.log.info("Wait for chainlock")
|
||||
self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash())
|
||||
self.move_to_next_cycle()
|
||||
self.log.info("Cycle H+C height:" + str(self.nodes[0].getblockcount()))
|
||||
self.log.info("Wait for chainlock")
|
||||
self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash())
|
||||
self.move_to_next_cycle()
|
||||
self.log.info("Cycle H+2C height:" + str(self.nodes[0].getblockcount()))
|
||||
self.log.info("Wait for chainlock")
|
||||
self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash())
|
||||
|
||||
b_0 = self.nodes[0].getbestblockhash()
|
||||
|
||||
self.log.info("Wait for chainlock")
|
||||
self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash())
|
||||
|
||||
(quorum_info_0_0, quorum_info_0_1) = self.mine_cycle_quorum(llmq_type_name=llmq_type_name, llmq_type=llmq_type)
|
||||
assert(self.test_quorum_listextended(quorum_info_0_0, llmq_type_name))
|
||||
assert(self.test_quorum_listextended(quorum_info_0_1, llmq_type_name))
|
||||
@ -103,9 +141,12 @@ class LLMQQuorumRotationTest(DashTestFramework):
|
||||
q_103_0_1 = QuorumId(103, int(quorum_info_0_1["quorumHash"], 16))
|
||||
|
||||
b_1 = self.nodes[0].getbestblockhash()
|
||||
expectedDeleted = []
|
||||
expectedDeleted = [h_100_0, h_104_0]
|
||||
expectedNew = [q_100_0, q_102_0, q_104_0, q_103_0_0, q_103_0_1]
|
||||
quorumList = self.test_getmnlistdiff_quorums(b_0, b_1, {}, expectedDeleted, expectedNew)
|
||||
quorumList = self.test_getmnlistdiff_quorums(b_0, b_1, quorumList, expectedDeleted, expectedNew)
|
||||
|
||||
self.log.info("Wait for chainlock")
|
||||
self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash())
|
||||
|
||||
(quorum_info_1_0, quorum_info_1_1) = self.mine_cycle_quorum(llmq_type_name=llmq_type_name, llmq_type=llmq_type)
|
||||
assert(self.test_quorum_listextended(quorum_info_1_0, llmq_type_name))
|
||||
@ -122,7 +163,7 @@ class LLMQQuorumRotationTest(DashTestFramework):
|
||||
q_103_1_1 = QuorumId(103, int(quorum_info_1_1["quorumHash"], 16))
|
||||
|
||||
b_2 = self.nodes[0].getbestblockhash()
|
||||
expectedDeleted = [q_103_0_0, q_103_0_1]
|
||||
expectedDeleted = [h_100_1, q_103_0_0, q_103_0_1]
|
||||
expectedNew = [q_100_1, q_102_1, q_103_1_0, q_103_1_1]
|
||||
quorumList = self.test_getmnlistdiff_quorums(b_1, b_2, quorumList, expectedDeleted, expectedNew)
|
||||
|
||||
@ -137,6 +178,9 @@ class LLMQQuorumRotationTest(DashTestFramework):
|
||||
assert_greater_than_or_equal(len(intersection(quorum_members_0_0, quorum_members_1_0)), 3)
|
||||
assert_greater_than_or_equal(len(intersection(quorum_members_0_1, quorum_members_1_1)), 3)
|
||||
|
||||
self.log.info("Wait for chainlock")
|
||||
self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash())
|
||||
|
||||
self.log.info("Mine a quorum to invalidate")
|
||||
(quorum_info_3_0, quorum_info_3_1) = self.mine_cycle_quorum(llmq_type_name=llmq_type_name, llmq_type=llmq_type)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user