Skip to content

feat: stop encrypting DKG shares to self#1529

Merged
0xjei merged 5 commits into
mainfrom
feat/skip-self-dkg-shares
May 15, 2026
Merged

feat: stop encrypting DKG shares to self#1529
0xjei merged 5 commits into
mainfrom
feat/skip-self-dkg-shares

Conversation

@hmzakhalid

@hmzakhalid hmzakhalid commented May 3, 2026

Copy link
Copy Markdown
Collaborator

During threshold key generation, every node was previously encrypting a share of its own secret to itself, sending it over the wire, then decrypting it back, a wasteful round-trip with no security benefit (a node already knows its own share). This PR removes the self-loop end-to-end.

Summary by CodeRabbit

  • Refactor
    • DKG/share flow reorganized to treat the node’s own plaintext share separately and integrate it into aggregation.
  • New Features
    • Sparse recipient encryption (ability to skip self slot) with aligned witness handling and APIs to support sparse recipient sets.
  • Bug Fixes
    • Improved validation and error handling for share/plaintext shapes, dimensions, and proof set completeness.
  • Documentation
    • Updated DKG encryption, proof-indexing, and recursive aggregation descriptions.
  • Tests
    • Added/updated tests covering sparse skip-index behavior and related folding assertions.

Review Change Stack

@vercel

vercel Bot commented May 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
crisp Ready Ready Preview, Comment May 14, 2026 8:41am
enclave-docs Ready Ready Preview, Comment May 14, 2026 8:41am

Request Review

@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 40edec5e-8a23-43d9-873f-2096023f129d

📥 Commits

Reviewing files that changed from the base of the PR and between 867538b and 76fc042.

📒 Files selected for processing (6)
  • agent/flow-trace/00_INDEX.md
  • agent/flow-trace/04_DKG_AND_COMPUTATION.md
  • circuits/bin/recursive_aggregation/node_fold/src/main.nr
  • crates/keyshare/src/threshold_keyshare.rs
  • crates/keyshare/src/threshold_share_collector.rs
  • crates/zk-prover/tests/node_fold_correlated_e2e_tests.rs
✅ Files skipped from review due to trivial changes (1)
  • agent/flow-trace/00_INDEX.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • agent/flow-trace/04_DKG_AND_COMPUTATION.md
  • crates/keyshare/src/threshold_keyshare.rs

📝 Walkthrough

Walkthrough

The PR implements a protocol shift in the DKG share decryption flow, transitioning from decrypting H parties' shares to decrypting only (H − 1) external parties' shares, with the node's own share handled separately via plaintext caching. The node now skips self-encryption during BFV share distribution, caches its own plaintext shares, excludes itself from share collection, and passes both its plaintext share data and the external ciphertexts (as optional slots) to proof-generation circuits.

Changes

DKG Share Decryption with Own-Party Separation

Layer / File(s) Summary
Request & Circuit Data Types
crates/events/src/enclave_event/compute_request/zk.rs, crates/zk-helpers/src/circuits/dkg/share_decryption/circuit.rs
DkgShareDecryptionProofRequest adds own_plaintext_idx and own_share_raw; ShareDecryptionCircuitData.honest_ciphertexts changes to Vec<Option<Vec<Ciphertext>>> and gains own_plaintext_share.
Share Encryption with Skip Logic
crates/trbfv/src/shares/bfv_encrypted.rs
BfvEncryptedShares.shares becomes Vec<Option<BfvEncryptedShare>>; encrypt_all_extended accepts skip_idx: Option<usize> and new encrypt_all_extended_for_share_indices supports sparse recipient indices with empty witnesses for skipped slots.
Share Collection Exclusion
crates/keyshare/src/threshold_share_collector.rs
ThresholdShareCollector::setup now takes own_party_id and excludes it from the todo set during initialization.
Own Share Caching & Self-Skip Integration
crates/keyshare/src/threshold_keyshare.rs
AggregatingDecryptionKey persists own_sk_share_raw and own_esi_shares_raw; ThresholdKeyshare caches pending own DKG shares; handle_shares_generated stores plaintext rows and calls BFV encryption with skip_idx to avoid self-encryption.
C3 Proof Requests & Expected Counts
crates/keyshare/src/threshold_keyshare.rs
C3a/C3b request construction skips own recipient index; handle_all_threshold_shares_collected derives expected proof/esi counts from cached own shares rather than collected on-wire shares.
Decryption-Key Computation & Splicing
crates/keyshare/src/threshold_keyshare.rs
During decryption-key calculation, cached own rows are deserialized, own_plaintext_idx computed, and own rows spliced into plaintext Shamir matrices at that index; honest set/threshold checks updated to include self.
C4 Proof Request Construction
crates/keyshare/src/threshold_keyshare.rs
SK and ESM C4 decryption proof requests are built including own_plaintext_idx and own_share_raw (per-esi for ESM).
Circuit Input Deserialization & Layout
crates/multithread/src/multithread.rs
handle_dkg_share_decryption_proof validates own_plaintext_idx, expects (h-1) * l ciphertexts, deserializes external ciphertext groups, inserts None at own_plaintext_idx, deserializes own_share_raw, validates dimensions, and builds ShareDecryptionCircuitData.
Circuit Computation with Optional Slots
crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs
Inputs::compute conditionally validates own_plaintext_share and iterates slots with match: decrypt Some(party_cts) ciphertexts or read coefficients directly from own_plaintext_share for None, reversing coefficients before commitment; tests and error handling updated.
Sample Generation with Own Slot
crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs
generate_sample builds honest_ciphertexts as Vec<Option<Vec<Ciphertext>>>, designates an own slot, stores plaintext share rows there, and returns the new circuit input shape for tests.
Proof Publication with Optional Slots
crates/zk-prover/src/actors/proof_request.rs
publish_threshold_share_with_proofs uses match share.extract_for_party(...) to handle skipped own slots with trace! logging and to error-log missing shares for other recipients while publishing valid ThresholdShareCreated messages.
Docs, Node-fold & Tests
agent/flow-trace/*, circuits/bin/recursive_aggregation/node_fold/src/main.nr, crates/zk-prover/tests/*
Documentation updated to describe mapping compact slots to real party_id, omitting sender’s plaintext slot from encryption and indexing proofs by real recipient; node-fold assertions and a correlated e2e test updated for sparse self-slot behavior.

Sequence Diagram

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • gnosisguild/enclave#1482 — Related changes that made honest-party ordering deterministic; affects insertion position calculations used here.
  • gnosisguild/enclave#1362 — Introduced DKG share-decryption ZK request shapes this PR extends (DkgShareDecryptionProofRequest variants).
  • gnosisguild/enclave#1356 — Related sparse/skip recipient support and proof-path adjustments for BFV encrypted shares.

Suggested reviewers

  • 0xjei
  • ctrlc03
  • zahrajavar

Poem

🐰 I hide my row where none encrypt,
H−1 dance while I quietly sit.
Some slots are Some, my slot is None,
Proofs skip me — the splice is done.
A rabbit cheers — protocol knit.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: stop encrypting DKG shares to self' accurately and concisely describes the main objective: eliminating the redundant self-encryption of DKG shares.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/skip-self-dkg-shares

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hmzakhalid hmzakhalid requested review from 0xjei, ctrlc03 and zahrajavar May 3, 2026 02:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/keyshare/src/threshold_keyshare.rs`:
- Around line 1179-1188: The code currently passes party_id as the slot index to
BfvEncryptedShares::encrypt_all_extended and to the C3 skip logic, which is
wrong when collected_encryption_keys is filtered or reordered; instead compute
the self slot by finding the position of the local party_id inside
collected_encryption_keys and use that index as the skip index. Concretely:
locate where collected_encryption_keys is available, find the index via a
position lookup of entries matching party_id (e.g.,
collected_encryption_keys.iter().position(|(id, _)| id == &party_id)), handle
the none case with an error, and pass that computed usize as the skip_idx to
encrypt_all_extended and any C3 skip checks rather than using party_id directly
(references: collected_encryption_keys, party_id,
BfvEncryptedShares::encrypt_all_extended, C3 skip logic).

In `@crates/multithread/src/multithread.rs`:
- Around line 1194-1210: After deserializing own_share_raw into
own_plaintext_share you must also validate each inner row length matches the BFV
coefficient count (the expected number of coefficients used for plaintext
polynomials) to avoid variable-length shares; iterate over own_plaintext_share
(use enumerate to get the row index), and for each row check row.len() ==
expected_coefficient_count (the BFV/poly coefficient count used elsewhere in the
module), returning Err(make_zk_error(&request, format!("own_plaintext_share[{}]
has {} coefficients, expected {}", i, row.len(), expected_coefficient_count)))
on mismatch instead of proceeding.

In `@crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs`:
- Around line 201-204: The loop currently calls
data.secret_key.try_decrypt(&party_cts[mod_idx]).unwrap() which panics on
decryption failure; change it to propagate the error as a CircuitsErrors so the
method returns a proper ComputeRequestError instead of crashing. Replace the
unwrap usage in the loop that produces decrypted_pt (inside the function that
iterates mod_idx and computes share_coeffs) with error handling that maps the
try_decrypt Err into an appropriate CircuitsErrors variant (including context
like which mod_idx/party_cts failed) and returns it (using ? or map_err(...)?),
ensuring the rest of the function keeps its existing error type flow.

In `@crates/zk-prover/src/actors/proof_request.rs`:
- Around line 1244-1280: The match arm for
share.extract_for_party(positional_idx) currently treats every None as an
expected self-slot and just traces; change it to only skip (trace) when the None
corresponds to the sender’s own slot and treat any other None as an error:
inside the None branch compare the missing slot to the sender identity (use the
module’s sender/self positional identifier available in this actor—e.g., the
field that represents our own positional index or party id used elsewhere with
real_party_id/positional_idx), keep the trace and skip only for the own-slot
case, and for any other positional_idx log an error (include real_party_id and
positional_idx) so missing external recipient shares aren’t silently dropped
instead of being surfaced for upstream handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6201a406-25b2-497b-aeba-c561c80f41f3

📥 Commits

Reviewing files that changed from the base of the PR and between c7e9802 and 4afa3cc.

📒 Files selected for processing (9)
  • crates/events/src/enclave_event/compute_request/zk.rs
  • crates/keyshare/src/threshold_keyshare.rs
  • crates/keyshare/src/threshold_share_collector.rs
  • crates/multithread/src/multithread.rs
  • crates/trbfv/src/shares/bfv_encrypted.rs
  • crates/zk-helpers/src/circuits/dkg/share_decryption/circuit.rs
  • crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs
  • crates/zk-helpers/src/circuits/dkg/share_decryption/sample.rs
  • crates/zk-prover/src/actors/proof_request.rs

Comment thread crates/keyshare/src/threshold_keyshare.rs Outdated
Comment thread crates/multithread/src/multithread.rs
Comment thread crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs
Comment thread crates/zk-prover/src/actors/proof_request.rs

@0xjei 0xjei left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK - looks good as far as CI passes. Let's wait for @zahrajavar review!

@0xjei 0xjei merged commit a54b403 into main May 15, 2026
83 of 85 checks passed
@github-actions github-actions Bot deleted the feat/skip-self-dkg-shares branch May 23, 2026 03:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants