Skip to content

Commit

Permalink
feat: new single node quorum and functional test feature_llmq_singlen…
Browse files Browse the repository at this point in the history
…ode.py
  • Loading branch information
knst committed Jan 17, 2025
1 parent 461b204 commit 606ad66
Show file tree
Hide file tree
Showing 10 changed files with 342 additions and 19 deletions.
31 changes: 19 additions & 12 deletions src/llmq/commitment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ bool CFinalCommitment::Verify(CDeterministicMNManager& dmnman, CQuorumSnapshotMa
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 @@ -115,19 +115,26 @@ bool CFinalCommitment::Verify(CDeterministicMNManager& dmnman, CQuorumSnapshotMa
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)) {
LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid member signature\n", quorumHash.ToString());
return false;
}
} 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());
}
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)) {
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 @@ -161,7 +168,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
58 changes: 58 additions & 0 deletions src/llmq/dkgsession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,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 @@ -1236,6 +1238,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 @@ -1293,6 +1296,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, m_use_legacy_bls);

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

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

if (!fqc.Verify(m_dmnman, m_qsnapman, 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 @@ -382,6 +382,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
12 changes: 12 additions & 0 deletions src/llmq/dkgsessionhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,18 @@ void CDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& peerman)
return changed;
});

if (params.size == 1) {
LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] h=%s - finalize-single-commitment \n", __func__, params.name, quorumIndex, quorumHash.ToString());
auto finalCommitment = curSession->FinalizeSingleCommitment();
if (!finalCommitment.IsNull()) { // it can be null only if we are not member
if (auto inv_opt = quorumBlockProcessor.AddMineableCommitment(finalCommitment); inv_opt.has_value()) {
peerman.RelayInv(inv_opt.value());
}
}
WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash);
return;
}

const auto tip_mn_list = m_dmnman.GetListAtChainTip();
utils::EnsureQuorumConnections(params, connman, m_dmnman, m_sporkman, m_qsnapman, tip_mn_list, pQuorumBaseBlockIndex,
curSession->myProTxHash, /* is_masternode = */ m_mn_activeman != nullptr);
Expand Down
2 changes: 1 addition & 1 deletion src/llmq/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ 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
};

// Configures a LLMQ and its DKG
Expand Down
6 changes: 6 additions & 0 deletions src/llmq/quorums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,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
45 changes: 45 additions & 0 deletions src/llmq/signing_shares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,22 @@ void CSigSharesManager::TryRecoverSig(PeerManager& peerman, const CQuorumCPtr& q
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, peerman);
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 @@ -1524,6 +1540,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()), 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
17 changes: 11 additions & 6 deletions src/rpc/quorums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ static RPCHelpMan quorum_list_extended()
};
}

static UniValue BuildQuorumInfo(const llmq::CQuorumBlockProcessor& quorum_block_processor, const llmq::CQuorumCPtr& quorum, bool includeMembers, bool includeSkShare)
static UniValue BuildQuorumInfo(const llmq::CQuorumBlockProcessor& quorum_block_processor, const llmq::CQuorumCPtr& quorum, bool includeMembers, bool includeSkShare, bool single_node_quorum = false)
{
UniValue ret(UniValue::VOBJ);

Expand Down Expand Up @@ -210,9 +210,13 @@ static UniValue BuildQuorumInfo(const llmq::CQuorumBlockProcessor& quorum_block_
mo.pushKV("pubKeyOperator", dmn->pdmnState->pubKeyOperator.ToString());
mo.pushKV("valid", quorum->qc->validMembers[i]);
if (quorum->qc->validMembers[i]) {
CBLSPublicKey pubKey = quorum->GetPubKeyShare(i);
if (pubKey.IsValid()) {
mo.pushKV("pubKeyShare", pubKey.ToString());
if (single_node_quorum) {
mo.pushKV("pubKeyShare", dmn->pdmnState->pubKeyOperator.ToString());
} else {
CBLSPublicKey pubKey = quorum->GetPubKeyShare(i);
if (pubKey.IsValid()) {
mo.pushKV("pubKeyShare", pubKey.ToString());
}
}
}
membersArr.push_back(mo);
Expand Down Expand Up @@ -245,7 +249,8 @@ static RPCHelpMan quorum_info()
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);

const Consensus::LLMQType llmqType{static_cast<Consensus::LLMQType>(ParseInt32V(request.params[0], "llmqType"))};
if (!Params().GetLLMQ(llmqType).has_value()) {
auto llmq_opt = Params().GetLLMQ(llmqType);
if (!llmq_opt.has_value()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
}

Expand All @@ -260,7 +265,7 @@ static RPCHelpMan quorum_info()
throw JSONRPCError(RPC_INVALID_PARAMETER, "quorum not found");
}

return BuildQuorumInfo(*llmq_ctx.quorum_block_processor, quorum, true, includeSkShare);
return BuildQuorumInfo(*llmq_ctx.quorum_block_processor, quorum, true, includeSkShare, llmq_opt->size == 1);
},
};
}
Expand Down
Loading

0 comments on commit 606ad66

Please sign in to comment.