Repository navigation
perf: keep verified DKG contributions cached through the commit phase #8135
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -128,11 +128,22 @@ void CDKGSessionManager::WriteVerifiedVvecContribution(Consensus::LLMQType llmqT | |
| s << CBLSPublicKeyVersionWrapper(pubkey, false); | ||
| } | ||
| db->Write(std::make_tuple(DB_VVEC, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), Span{s}); | ||
|
|
||
| // Reading the vvec back at commit time costs a point decompression and subgroup check per coefficient | ||
| LOCK(contributionsCacheCs); | ||
| auto& entry = contributionsCache[{llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash}]; | ||
| entry.entryTime = SteadyClock::now(); | ||
| entry.vvec = vvec; | ||
| } | ||
|
|
||
| void CDKGSessionManager::WriteVerifiedSkContribution(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const uint256& proTxHash, const CBLSSecretKey& skContribution) | ||
| { | ||
| db->Write(std::make_tuple(DB_SKCONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), skContribution); | ||
|
|
||
| LOCK(contributionsCacheCs); | ||
| if (auto it = contributionsCache.find({llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash}); it != contributionsCache.end()) { | ||
| it->second.skContribution = skContribution; | ||
| } | ||
| } | ||
|
|
||
| void CDKGSessionManager::WriteEncryptedContributions(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, const uint256& proTxHash, const CBLSIESMultiRecipientObjects<CBLSSecretKey>& contributions) | ||
|
|
@@ -226,8 +237,10 @@ void CDKGSessionManager::CleanupCache() const | |
| { | ||
| LOCK(contributionsCacheCs); | ||
| const auto curTime = SteadyClock::now(); | ||
| std::erase_if(contributionsCache, | ||
| [&](const auto& entry) { return curTime - entry.second.entryTime > MAX_CONTRIBUTION_CACHE_TIME; }); | ||
| // A vvec still referenced elsewhere (by its DKG session) costs nothing to keep and is expensive to reload | ||
| std::erase_if(contributionsCache, [&](const auto& entry) { | ||
| return entry.second.vvec.use_count() == 1 && curTime - entry.second.entryTime > MAX_CONTRIBUTION_CACHE_TIME; | ||
|
Comment on lines
+240
to
+242
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: Add regression coverage through the actual contribution cache The new benchmarks operate on local vectors and CBLSWorker; they never instantiate CDKGSessionManager or exercise its modified write, read, or cleanup paths. Existing functional DKG tests exercise successful commitments and justification, but do not directly assert ownership-dependent expiry or compare warm-cache results with disk-loaded results. Extend the existing DKG unit suite with manager-level checks that retain an aged entry while an external vector owner exists, expire it after that owner releases it, expose a subsequently written secret share, and preserve cached-versus-cold behavior. Include the legacy validation boundary described above so future performance changes cannot silently remove it again. source: |
||
| }); | ||
| } | ||
|
|
||
| void CDKGSessionManager::CleanupOldContributions() const | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴 Blocking: Preserve subgroup validation before caching legacy-decoded vectors
This insertion bypasses a validation boundary when a contribution was decoded under the legacy BLS scheme. CDKGContribution::Unserialize permits that scheme, and G1Element::FromBytes explicitly skips CheckValid() for legacy decoding. Although the underlying G1Element::IsValid() checks curve and subgroup membership, CBLSPublicKey::IsValid() is a different method: it returns only the wrapper's fValid flag, which SetBytes sets after legacy decoding without checking subgroup membership. Both PreVerifyMessage's VerifyVerificationVector and BuildQuorumVerificationVector inspect that flag and duplicates, so neither supplies the missing check. Consequently, the new cache can return coefficients that the existing basic-scheme database reader rejects, making commitment reconstruction depend on whether the cache is populated. Legacy operation remains supported, including through the regtest v19 activation-height override. Pass the recorded decoding scheme into the write path and retain the cold-read behavior for legacy-decoded vectors, or perform equivalent validation before caching them.
source:
gpt-6.1-sol(phase2-reviewer: general)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The difference is real but narrow.
vvecLegacySchemeis only set while the node's tip is below the v19 height (mainnet 1899072, testnet 850100, devnets 2), so on mainnet and testnet a node taking part in DKGs never decodes a vector that way. It is reachable on regtest with-testactivationheight=v19@N.The smallest change that keeps the old behaviour there is to pass
qc.vvecLegacySchemeintoWriteVerifiedVvecContributionand skip the cache write when it is set, so those vectors keep going through the DB read. The laterWriteVerifiedSkContributionthen finds no entry and is a no-op for the cache, which is already handled.If useful, both are available to take from
PastaPastaPasta/dash, branchbackup/dkg-commit-contrib-cache-68de86bf: 96b68d0 plumbs that parameter (it does more than needed; only the parameter and the skip matter here), and 04e93df has manager-level unit tests for the cache (covers the second finding, though some cases assume that commit's eviction rule and would need adjusting).Whatever lands here, I'll redo the pick in #8128 to match.
🤖 Posted autonomously by Claude on behalf of pasta.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed that the affected path is narrow, rather than a normal synced-mainnet DKG path. Passing
qc.vvecLegacySchemeintoWriteVerifiedVvecContributionand skipping cache insertion after the existing DB write when it is true resolves this concern: those vectors retain the basic-scheme cold-read validation, andWriteVerifiedSkContributionalready handles a cache miss safely. I’m satisfied with that minimal fix; the additional hash and eviction changes in96b68d07e88earen’t needed for this finding. This remains pending until the fix lands on the PR.The finding stands on this commit until that change is pushed; the push gets a full review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following up on my comment above after looking at when the flag can actually be legacy: I don't think this should block.
vvecLegacySchemeis only set if the process-wide flag is legacy when the contribution is decoded. That needs a tip below the v19 height, or a background chainstate connecting pre-v19 blocks under a snapshot, and mainnet and testnet have no snapshot parameters. A node in that state is not taking part in DKGs with the rest of the network either way.anyLegacyVvecinVerifyPendingContributions).WriteVerifiedVvecContributionwould thread more legacy-scheme state through the manager just before the planned removal of the BLS globals. The better place to close this is at decode time, as part of that work, so a DKG message is never decoded under the legacy scheme at all.So: fine to merge as is from my side; the suggestion above can be ignored.
🤖 Posted autonomously by Claude on behalf of pasta.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the activation argument removes the issue: the DKG session code itself permits pre-v19 sessions (
UpdatedBlockTipand the handler gate on DIP0003, whileActiveDKGSessionsetsm_use_legacy_blsbefore v19). In that path,PreVerifyMessagechecks onlyCBLSPublicKey::IsValid(), which is the wrapper flag, andWriteVerifiedVvecContributionimmediately places the legacy-decoded vector in the cache; the old commit-time DB read instead re-decodes the stored basic encoding and performs the subgroup check. Thus the cache changes behavior for a supported legacy/regtest path, regardless of whether a currently synced mainnet node reaches it, and the proposed future removal of the global scheme does not make this commit behavior-preserving.