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 12 commits into
base: develop
Choose a base branch
from
Draft
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
36 changes: 27 additions & 9 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,7 @@ 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;
Expand All @@ -927,6 +928,7 @@ class CRegTestParams : public CChainParams {
UpdateLLMQTestParametersFromArgs(args, Consensus::LLMQType::LLMQ_TEST);
UpdateLLMQTestParametersFromArgs(args, Consensus::LLMQType::LLMQ_TEST_INSTANTSEND);
UpdateLLMQInstantSendDIP0024FromArgs(args);
UpdateLLMQTestPlatformFromArgs(args);
}

/**
Expand Down Expand Up @@ -1004,16 +1006,9 @@ 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 UpdateLLMQTestPlatformFromArgs(const ArgsManager& args);
};

static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& consensus)
Expand Down Expand Up @@ -1235,7 +1230,30 @@ void CRegTestParams::UpdateLLMQInstantSendDIP0024FromArgs(const ArgsManager& arg
throw std::runtime_error("Invalid LLMQ type specified for -llmqtestinstantsenddip0024.");
}
LogPrintf("Setting llmqtestinstantsenddip0024 to %ld\n", ToUnderlying(llmqType));
UpdateLLMQDIP0024InstantSend(llmqType);

consensus.llmqTypeDIP0024InstantSend = llmqType;
}

void CRegTestParams::UpdateLLMQTestPlatformFromArgs(const ArgsManager& args)
{
if (!args.IsArgSet("-llmqtestplatform")) return;

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

std::string strLLMQType = gArgs.GetArg("-llmqtestplatform", std::string(llmq_params_opt->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 -llmqtestplatform.");
}
LogPrintf("Setting llmqtestplatform to size=%ld\n", static_cast<uint8_t>(llmqType));
consensus.llmqTypePlatform = llmqType;
}

void CDevNetParams::UpdateDevnetSubsidyAndDiffParametersFromArgs(const ArgsManager& args)
Expand Down
1 change: 1 addition & 0 deletions src/chainparamsbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ 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("-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 @@ -164,6 +164,8 @@ void CDKGSession::Contribute(CDKGPendingMessages& pendingMessages)
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 @@ -276,6 +278,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 @@ -1234,6 +1237,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 @@ -1291,6 +1295,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
14 changes: 14 additions & 0 deletions src/llmq/dkgsessionhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,20 @@ void CDKGSessionHandler::HandleDKGRound()

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());
return;
}

if (auto inv_opt = quorumBlockProcessor.AddMineableCommitment(finalCommitment); inv_opt.has_value()) {
Assert(m_peerman.get())->RelayInv(inv_opt.value());
}
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
const bool fHaveDIP0024Quorums{optHaveDIP0024Quorums.value_or(pindexPrev->nHeight >= consensusParams.DIP0024QuorumsHeight)};
switch (llmqType)
{
case Consensus::LLMQType::LLMQ_DEVNET:
return true;
case Consensus::LLMQType::LLMQ_50_60:
return !fDIP0024IsActive || !fHaveDIP0024Quorums || 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
45 changes: 45 additions & 0 deletions src/llmq/signing_shares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,22 @@ void CSigSharesManager::TryRecoverSig(const CQuorumCPtr& quorum, const uint256&
return;
}

if (quorum->params.size == 1) {
if (sigSharesForSignHash->empty()) {
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- impossible to recover single-node signature - no shares yet. id=%s, msgHash=%s\n", __func__,
id.ToString(), msgHash.ToString());
return;
}
const auto& sigShare = sigSharesForSignHash->begin()->second;
CBLSSignature recoveredSig = sigShare.sigShare.Get();
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- recover single-node signature. id=%s, msgHash=%s\n", __func__,
id.ToString(), msgHash.ToString());

auto rs = std::make_shared<CRecoveredSig>(quorum->params.type, quorum->qc->quorumHash, id, msgHash, recoveredSig);
sigman.ProcessRecoveredSig(rs);
return; // end of single-quorum processing
}

sigSharesForRecovery.reserve((size_t) quorum->params.threshold);
idsForRecovery.reserve((size_t) quorum->params.threshold);
for (auto it = sigSharesForSignHash->begin(); it != sigSharesForSignHash->end() && sigSharesForRecovery.size() < size_t(quorum->params.threshold); ++it) {
Expand Down Expand Up @@ -1521,6 +1537,35 @@ std::optional<CSigShare> CSigSharesManager::CreateSigShare(const CQuorumCPtr& qu
return std::nullopt;
}

if (quorum->params.size == 1) {
int memberIdx = quorum->GetMemberIndex(activeMasterNodeProTxHash);
if (memberIdx == -1) {
// this should really not happen (IsValidMember gave true)
return std::nullopt;
}

CSigShare sigShare(quorum->params.type, quorum->qc->quorumHash, id, msgHash, uint16_t(memberIdx), {});
uint256 signHash = sigShare.buildSignHash();

// TODO: This one should be SIGN by QUORUM key, not by OPERATOR key
// see TODO in CDKGSession::FinalizeSingleCommitment for details
sigShare.sigShare.Set(m_mn_activeman->Sign(signHash), bls::bls_legacy_scheme.load());

if (!sigShare.sigShare.Get().IsValid()) {
LogPrintf("CSigSharesManager::%s -- failed to sign sigShare. signHash=%s, id=%s, msgHash=%s, time=%s\n", __func__,
signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(), t.count());
return std::nullopt;
}

sigShare.UpdateKey();

LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- created sigShare. signHash=%s, id=%s, msgHash=%s, llmqType=%d, quorum=%s, time=%s\n", __func__,
signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(), ToUnderlying(quorum->params.type), quorum->qc->quorumHash.ToString(), t.count());

return sigShare;


}
const CBLSSecretKey& skShare = quorum->GetSkShare();
if (!skShare.IsValid()) {
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have our skShare for quorum %s\n", __func__, quorum->qc->quorumHash.ToString());
Expand Down
Loading
Loading