Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: single node quorum #6437

Draft
wants to merge 18 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
33 changes: 13 additions & 20 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -914,14 +914,18 @@ class CRegTestParams : public CChainParams {
AddLLMQ(Consensus::LLMQType::LLMQ_TEST_V17);
AddLLMQ(Consensus::LLMQType::LLMQ_TEST_DIP0024);
AddLLMQ(Consensus::LLMQType::LLMQ_TEST_PLATFORM);
AddLLMQ(Consensus::LLMQType::LLMQ_SINGLE_NODE);
consensus.llmqTypeChainLocks = Consensus::LLMQType::LLMQ_TEST;
consensus.llmqTypeDIP0024InstantSend = Consensus::LLMQType::LLMQ_TEST_DIP0024;
consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_TEST_PLATFORM;
consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_TEST;

UpdateLLMQTestParametersFromArgs(args, Consensus::LLMQType::LLMQ_TEST);
UpdateLLMQTestParametersFromArgs(args, Consensus::LLMQType::LLMQ_TEST_INSTANTSEND);
UpdateLLMQInstantSendDIP0024FromArgs(args);

UpdateLLMQTypeFromArgs(args, "-llmqtestchainlocks", consensus.llmqTypeChainLocks);
UpdateLLMQTypeFromArgs(args, "-llmqtestinstantsenddip0024", consensus.llmqTypeDIP0024InstantSend);
UpdateLLMQTypeFromArgs(args, "-llmqtestplatform", consensus.llmqTypePlatform);
}

/**
Expand Down Expand Up @@ -984,16 +988,8 @@ class CRegTestParams : public CChainParams {
params->dkgBadVotesThreshold = threshold;
}

/**
* Allows modifying the LLMQ type for InstantSend (DIP0024).
*/
void UpdateLLMQDIP0024InstantSend(Consensus::LLMQType llmqType)
{
consensus.llmqTypeDIP0024InstantSend = llmqType;
}

void UpdateLLMQTestParametersFromArgs(const ArgsManager& args, const Consensus::LLMQType llmqType);
void UpdateLLMQInstantSendDIP0024FromArgs(const ArgsManager& args);
void UpdateLLMQTypeFromArgs(const ArgsManager& args, const std::string& arg_name, Consensus::LLMQType& llmqTypeToSet);
};

static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& consensus)
Expand Down Expand Up @@ -1169,26 +1165,23 @@ void CRegTestParams::UpdateLLMQTestParametersFromArgs(const ArgsManager& args, c
UpdateLLMQTestParameters(size, threshold, llmqType);
}

void CRegTestParams::UpdateLLMQInstantSendDIP0024FromArgs(const ArgsManager& args)
void CRegTestParams::UpdateLLMQTypeFromArgs(const ArgsManager& args, const std::string& arg_name, Consensus::LLMQType& llmqTypeToSet)
{
if (!args.IsArgSet("-llmqtestinstantsenddip0024")) return;

const auto& llmq_params_opt = GetLLMQ(consensus.llmqTypeDIP0024InstantSend);
assert(llmq_params_opt.has_value());

std::string strLLMQType = gArgs.GetArg("-llmqtestinstantsenddip0024", std::string(llmq_params_opt->name));
if (!args.IsArgSet(arg_name)) return;

const std::string strLLMQType = gArgs.GetArg(arg_name, "");
Consensus::LLMQType llmqType = Consensus::LLMQType::LLMQ_NONE;
for (const auto& params : consensus.llmqs) {
if (params.name == strLLMQType) {
llmqType = params.type;
}
}
if (llmqType == Consensus::LLMQType::LLMQ_NONE) {
throw std::runtime_error("Invalid LLMQ type specified for -llmqtestinstantsenddip0024.");
throw std::runtime_error(strprintf("Invalid LLMQ type specified for %s", arg_name));
}
LogPrintf("Setting llmqtestinstantsenddip0024 to %ld\n", ToUnderlying(llmqType));
UpdateLLMQDIP0024InstantSend(llmqType);

LogPrintf("Setting %s to %ld\n", arg_name, ToUnderlying(llmqType));
llmqTypeToSet = llmqType;
}

void CDevNetParams::UpdateDevnetSubsidyAndDiffParametersFromArgs(const ArgsManager& args)
Expand Down
2 changes: 2 additions & 0 deletions src/chainparamsbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-llmqmnhf=<quorum name>", "Override the default LLMQ type used for EHF. (default: llmq_devnet, devnet-only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-llmqtestinstantsenddip0024=<quorum name>", "Override the default LLMQ type used for InstantSendDIP0024. Used mainly to test Platform. (default: llmq_test_dip0024, regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-llmqtestinstantsendparams=<size>:<threshold>", "Override the default LLMQ size for the LLMQ_TEST_INSTANTSEND quorums (default: 3:2, regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-llmqtestplatform=<quorum name>", "Override the default LLMQ type used for Platform. (default: llmq_test_platform, regtest-only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-llmqtestchainlocks=<quorum name>", "Override the default LLMQ type used for Chainlocks. (default: llmq_test_test, regtest-only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-llmqtestparams=<size>:<threshold>", "Override the default LLMQ size for the LLMQ_TEST quorum (default: 3:2, regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-powtargetspacing=<n>", "Override the default PowTargetSpacing value in seconds (default: 2.5 minutes, devnet-only)", ArgsManager::ALLOW_INT, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-minimumdifficultyblocks=<n>", "The number of blocks that can be mined with the minimum difficulty at the start of a chain (default: 0, devnet-only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
Expand Down
37 changes: 25 additions & 12 deletions src/llmq/commitment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ bool CFinalCommitment::Verify(CDeterministicMNManager& dmnman, gsl::not_null<con
LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumPublicKey\n", quorumHash.ToString());
return false;
}
if (quorumVvecHash.IsNull()) {
if (llmq_params.size != 1 && quorumVvecHash.IsNull()) {
LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumVvecHash\n", quorumHash.ToString());
return false;
}
Expand Down Expand Up @@ -114,19 +114,32 @@ bool CFinalCommitment::Verify(CDeterministicMNManager& dmnman, gsl::not_null<con
LogPrint(BCLog::LLMQ, "CFinalCommitment::%s members[%s] quorumPublicKey[%s] commitmentHash[%s]\n",
__func__, ss3.str(), quorumPublicKey.ToString(), commitmentHash.ToString());
}
std::vector<CBLSPublicKey> memberPubKeys;
for (const auto i : irange::range(members.size())) {
if (!signers[i]) {
continue;
if (llmq_params.size == 1) {
LogPrintf("pubkey operator: %s\n", members[0]->pdmnState->pubKeyOperator.Get().ToString());
if (!membersSig.VerifyInsecure(members[0]->pdmnState->pubKeyOperator.Get(), commitmentHash)) {
// memberPubKeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get());
LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid member signature\n", quorumHash.ToString());
return false;
}
memberPubKeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get());
}

if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) {
LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid aggregated members signature\n", quorumHash.ToString());
return false;
}
/*
if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) {
}
*/
} else {
std::vector<CBLSPublicKey> memberPubKeys;
for (const auto i : irange::range(members.size())) {
if (!signers[i]) {
continue;
}
memberPubKeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get());
}

if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) {
LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid aggregated members signature\n", quorumHash.ToString());
return false;
}
}
if (!quorumSig.VerifyInsecure(quorumPublicKey, commitmentHash)) {
LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorum signature\n", quorumHash.ToString());
return false;
Expand Down Expand Up @@ -160,7 +173,7 @@ bool CFinalCommitment::VerifySizes(const Consensus::LLMQParams& params) const
return false;
}
if (validMembers.size() != size_t(params.size)) {
LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid signers.size=%d\n", quorumHash.ToString(), signers.size());
LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid validMembers.size=%d\n", quorumHash.ToString(), validMembers.size());
return false;
}
return true;
Expand Down
59 changes: 59 additions & 0 deletions src/llmq/dkgsession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ void CDKGSession::Contribute(CDKGPendingMessages& pendingMessages, PeerManager&
return;
}

assert(params.threshold > 1); // we should not get there with single-node-quorums

cxxtimer::Timer t1(true);
logger.Batch("generating contributions");
if (!blsWorker.GenerateContributions(params.threshold, memberIds, vvecContribution, m_sk_contributions)) {
Expand Down Expand Up @@ -274,6 +276,7 @@ bool CDKGSession::PreVerifyMessage(const CDKGContribution& qc, bool& retBan) con
return true;
}

// TODO: remove duplicated code between all ReceiveMessage: CDKGContribution, CDKGComplaint, CDKGJustification, CDKGPrematureCommitment
std::optional<CInv> CDKGSession::ReceiveMessage(const CDKGContribution& qc)
{
CDKGLogger logger(*this, __func__, __LINE__);
Expand Down Expand Up @@ -1233,6 +1236,7 @@ std::vector<CFinalCommitment> CDKGSession::FinalizeCommitments()
fqc.quorumVvecHash = first.quorumVvecHash;

const bool isQuorumRotationEnabled{IsQuorumRotationEnabled(params, m_quorum_base_block_index)};
// TODO: always put `true` here: so far as v19 is activated, we always write BASIC now
fqc.nVersion = CFinalCommitment::GetVersion(isQuorumRotationEnabled, DeploymentActiveAfter(m_quorum_base_block_index, Params().GetConsensus(), Consensus::DEPLOYMENT_V19));
fqc.quorumIndex = isQuorumRotationEnabled ? quorumIndex : 0;

Expand Down Expand Up @@ -1290,6 +1294,61 @@ std::vector<CFinalCommitment> CDKGSession::FinalizeCommitments()
return finalCommitments;
}

CFinalCommitment CDKGSession::FinalizeSingleCommitment()
{
if (!AreWeMember()) {
return {};
}

CDKGLogger logger(*this, __func__, __LINE__);

std::vector<CBLSId> signerIds;
std::vector<CBLSSignature> thresholdSigs;

CFinalCommitment fqc(params, m_quorum_base_block_index->GetBlockHash());


fqc.signers = {true};
fqc.validMembers = {true};

CBLSSecretKey sk1;
sk1.MakeNewKey();

fqc.quorumPublicKey = sk1.GetPublicKey();
fqc.quorumVvecHash = {};

// use just MN's operator public key as quorum pubkey.
// TODO: use sk1 here instead and use recovery mechanism from shares, but that's not trivial to do
const bool workaround_qpublic_key = true;
if (workaround_qpublic_key) {
fqc.quorumPublicKey = m_mn_activeman->GetPubKey();
}
const bool isQuorumRotationEnabled{false};
fqc.nVersion = CFinalCommitment::GetVersion(isQuorumRotationEnabled, DeploymentActiveAfter(m_quorum_base_block_index, Params().GetConsensus(), Consensus::DEPLOYMENT_V19));
fqc.quorumIndex = 0;

uint256 commitmentHash = BuildCommitmentHash(fqc.llmqType, fqc.quorumHash, fqc.validMembers, fqc.quorumPublicKey, fqc.quorumVvecHash);
fqc.quorumSig = sk1.Sign(commitmentHash);

fqc.membersSig = m_mn_activeman->Sign(commitmentHash);

if (workaround_qpublic_key) {
fqc.quorumSig = fqc.membersSig;
}

if (!fqc.Verify(m_dmnman, m_quorum_base_block_index, true)) {
logger.Batch("failed to verify final commitment");
assert(false);
}

logger.Batch("final commitment: validMembers=%d, signers=%d, quorumPublicKey=%s",
fqc.CountValidMembers(), fqc.CountSigners(), fqc.quorumPublicKey.ToString());

logger.Flush();

return fqc;
}

CDKGMember* CDKGSession::GetMember(const uint256& proTxHash) const
{
auto it = membersMap.find(proTxHash);
Expand Down
3 changes: 3 additions & 0 deletions src/llmq/dkgsession.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ class CDKGSession
// Phase 5: aggregate/finalize
std::vector<CFinalCommitment> FinalizeCommitments();

// All Phases 5-in-1 for single-node-quorum
CFinalCommitment FinalizeSingleCommitment();

[[nodiscard]] bool AreWeMember() const { return !myProTxHash.IsNull(); }
void MarkBadMember(size_t idx);

Expand Down
16 changes: 16 additions & 0 deletions src/llmq/dkgsessionhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,22 @@ void CDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& peerman)

const auto tip_mn_list = m_dmnman.GetListAtChainTip();
utils::EnsureQuorumConnections(params, connman, m_dmnman, m_sporkman, tip_mn_list, pQuorumBaseBlockIndex, curSession->myProTxHash, /* is_masternode = */ m_mn_activeman != nullptr);
if (params.size == 1) // TODO: add here check AreWeMember instead checking is-null for final-commitment
{
auto finalCommitment = curSession->FinalizeSingleCommitment();
if (finalCommitment.IsNull()) {
LogPrintf("final commitment is null here -- is-member=%d\n", curSession->AreWeMember());
WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash);
return;
}

if (auto inv_opt = quorumBlockProcessor.AddMineableCommitment(finalCommitment); inv_opt.has_value()) {
peerman.RelayInv(inv_opt.value());
}
WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash);
return;
}

if (curSession->AreWeMember()) {
utils::AddQuorumProbeConnections(params, connman, m_dmnman, m_mn_metaman, m_sporkman, tip_mn_list, pQuorumBaseBlockIndex, curSession->myProTxHash);
}
Expand Down
5 changes: 3 additions & 2 deletions src/llmq/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,6 @@ bool IsQuorumTypeEnabledInternal(Consensus::LLMQType llmqType, gsl::not_null<con
optIsDIP0024Active.value_or(DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_DIP0024))};
switch (llmqType)
{
case Consensus::LLMQType::LLMQ_DEVNET:
return true;
case Consensus::LLMQType::LLMQ_50_60:
return !fDIP0024IsActive || Params().NetworkIDString() == CBaseChainParams::TESTNET ||
Params().NetworkIDString() == CBaseChainParams::DEVNET;
Expand All @@ -141,6 +139,8 @@ bool IsQuorumTypeEnabledInternal(Consensus::LLMQType llmqType, gsl::not_null<con
case Consensus::LLMQType::LLMQ_400_60:
case Consensus::LLMQType::LLMQ_400_85:
case Consensus::LLMQType::LLMQ_DEVNET_PLATFORM:
case Consensus::LLMQType::LLMQ_DEVNET:
case Consensus::LLMQType::LLMQ_SINGLE_NODE:
return true;

case Consensus::LLMQType::LLMQ_TEST_V17: {
Expand All @@ -154,6 +154,7 @@ bool IsQuorumTypeEnabledInternal(Consensus::LLMQType llmqType, gsl::not_null<con
case Consensus::LLMQType::LLMQ_TEST_DIP0024: {
return fDIP0024IsActive;
}
// TODO: remove it in case of testnet reset
case Consensus::LLMQType::LLMQ_25_67:
return pindexPrev->nHeight >= TESTNET_LLMQ_25_67_ACTIVATION_HEIGHT;

Expand Down
34 changes: 32 additions & 2 deletions src/llmq/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ enum class LLMQType : uint8_t {
LLMQ_TEST_PLATFORM = 106, // 3 members, 2 (66%) threshold, one per hour.

// for devnets only. rotated version (v2) for devnets
LLMQ_DEVNET_DIP0024 = 105 // 8 members, 4 (50%) threshold, one per hour. Params might differ when -llmqdevnetparams is used
LLMQ_DEVNET_DIP0024 = 105, // 8 members, 4 (50%) threshold, one per hour. Params might differ when -llmqdevnetparams is used

LLMQ_SINGLE_NODE = 111, // 1 memeber, 1 threshold, one per hour.
};

// Configures a LLMQ and its DKG
Expand Down Expand Up @@ -129,7 +131,7 @@ static_assert(std::is_trivially_copyable_v<Consensus::LLMQParams>, "LLMQParams i
static_assert(std::is_trivially_assignable_v<Consensus::LLMQParams, Consensus::LLMQParams>, "LLMQParams is not trivially assignable");


static constexpr std::array<LLMQParams, 14> available_llmqs = {
static constexpr std::array<LLMQParams, 15> available_llmqs = {

/**
* llmq_test
Expand Down Expand Up @@ -502,6 +504,34 @@ static constexpr std::array<LLMQParams, 14> available_llmqs = {
.recoveryMembers = 12,
},

/**
* llmq_1_100
* This quorum is used explicitly on Regtest and requires
* just 1 participant
*
* Used for Platform for easy setup testing environment
*/
LLMQParams{
.type = LLMQType::LLMQ_SINGLE_NODE,
.name = "llmq_1_100",
.useRotation = false,
.size = 1,
.minSize = 1,
.threshold = 1,

.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,
.keepOldKeys = 4,
.recoveryMembers = 1,
},

}; // available_llmqs

} // namespace Consensus
Expand Down
6 changes: 6 additions & 0 deletions src/llmq/quorums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,12 @@ CQuorumPtr CQuorumManager::BuildQuorumFromCommitment(const Consensus::LLMQType l

quorum->Init(std::move(qc), pQuorumBaseBlockIndex, minedBlockHash, members);

if (populate_cache && llmq_params_opt->size == 1) {
WITH_LOCK(cs_map_quorums, mapQuorumsCache[llmqType].insert(quorumHash, quorum));

return quorum;
}

bool hasValidVvec = false;
if (WITH_LOCK(cs_db, return quorum->ReadContributions(*db))) {
hasValidVvec = true;
Expand Down
Loading
Loading