Skip to content

feat: integrate share proofs in ciphernode [skip-line-limit]#1334

Merged
hmzakhalid merged 16 commits into
mainfrom
feat/integrate-t1-share-computation
Feb 23, 2026
Merged

feat: integrate share proofs in ciphernode [skip-line-limit]#1334
hmzakhalid merged 16 commits into
mainfrom
feat/integrate-t1-share-computation

Conversation

@ctrlc03

@ctrlc03 ctrlc03 commented Feb 16, 2026

Copy link
Copy Markdown
Collaborator

Integrate sk_share_computation and e_sm_share_computation

fix #1332

Summary by CodeRabbit

  • New Features

    • Added coordinated multi-proof share-computation workflow enabling generation, signing, and publishing of multiple cryptographic proofs as part of key operations.
  • Improvements

    • Improved handling of encrypted secret materials and internal flows to preserve private-witness semantics and streamline proof generation.
  • Tests & Infrastructure

    • Added additional ZK circuit fixtures, increased test timeouts for stability, and migrated CI jobs to self-hosted runners.

@vercel

vercel Bot commented Feb 16, 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 Feb 23, 2026 10:08am
enclave-docs Ready Ready Preview, Comment Feb 23, 2026 10:08am

Request Review

@coderabbitai

coderabbitai Bot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Integrates T2 share-computation circuits into the threshold key generation flow: adds ShareComputation ZK request/response and signed-event types, wires multi-proof orchestration in the zk-prover, extends keyshare state to track signed proofs, and implements the multithread prover path to generate share-computation proofs.

Changes

Cohort / File(s) Summary
ZK Request/Response types
crates/events/src/enclave_event/compute_request/zk.rs, crates/events/src/enclave_event/compute_request/mod.rs
Added ShareComputation variant to ZkRequest/ZkResponse; introduced ShareComputationProofRequest and ShareComputationProofResponse types and ToString output update.
Signed event & integration
crates/events/src/enclave_event/share_computation_proof_signed.rs, crates/events/src/enclave_event/mod.rs
New Actix message ShareComputationProofSigned with fields (e3_id, party_id, proof_type, signed_proof); re-exported and included in EnclaveEventData and macro-driven EventType wiring.
Keyshare state & handlers
crates/keyshare/src/threshold_keyshare.rs, crates/events/src/enclave_event/threshold_share_pending.rs
Extended ThresholdSharePending, AggregatingDecryptionKey, ReadyForDecryption, Decrypting to carry sk/e_sm signed-proof slots; added handle_share_computation_proof_signed and routing for ShareComputationProofSigned.
Multithread ZK prover path
crates/multithread/src/multithread.rs, crates/multithread/Cargo.toml
Implemented handle_share_computation_proof to decrypt inputs, deserialize polynomials/shared-secrets, compute parity matrices, run ShareComputationCircuit, and return ShareComputationProofResponse; added bincode/ndarray/num-bigint deps.
Proof orchestration (zk-prover actor)
crates/zk-prover/src/actors/proof_request.rs
Added three-part threshold workflow: ThresholdProofKind, PendingThresholdProofs, correlation mapping; launch 3 parallel ZK requests (PkGeneration, SkShareComputation, ESmShareComputation), handle responses, sign proofs, and publish ShareComputationProofSigned + ThresholdShareCreated.
Circuit & serialization changes
crates/trbfv/src/gen_esi_sss.rs, crates/trbfv/src/shares/shares.rs, crates/zk-helpers/src/circuits/computation.rs
GenEsiSssRequest.e_sm_raw changed to SensitiveBytes with new into_inner(cipher) decryption helper; added SharedSecret::moduli_data() accessor; derived Hash for DkgInputType.
Tests & fixtures
crates/tests/tests/integration.rs, crates/test-helpers/src/usecase_helpers.rs, examples/CRISP/test/crisp.spec.ts, templates/default/tests/integration.spec.ts
Copied T2a/T2b circuit JSON+VK into test fixtures; adjusted generate_shares_hash_map to clone e_sm_raw; increased some test timeouts/durations.
CI runner change
.github/workflows/ci.yml
Changed CI jobs to run on self-hosted runners instead of ubuntu-latest (runner field updates).

Sequence Diagram

sequenceDiagram
    participant KS as KeyShare (ThresholdKeyshare)
    participant PR as ProofRequest Actor
    participant ZK as ZK Prover (Multithread)
    participant EV as Event Bus (EnclaveEventData)

    KS->>PR: handle_threshold_share_pending (ThresholdSharePending with 3 requests)
    activate PR
    PR->>PR: create correlations for PkGen, SkShareComp, ESmShareComp
    PR->>ZK: ZkRequest::PkGeneration
    PR->>ZK: ZkRequest::ShareComputation (Sk)
    PR->>ZK: ZkRequest::ShareComputation (ESm)
    deactivate PR

    activate ZK
    ZK->>ZK: handle_pk_generation_proof
    ZK-->>PR: ZkResponse::PkGeneration

    ZK->>ZK: handle_share_computation_proof (decrypt → deserialize → parity → prove)
    ZK-->>PR: ZkResponse::ShareComputation
    ZK->>ZK: handle_share_computation_proof (second)
    ZK-->>PR: ZkResponse::ShareComputation
    deactivate ZK

    activate PR
    PR->>PR: handle_threshold_proof_response (resolve by correlation)
    PR->>PR: sign_proof for each proof
    PR->>EV: Publish ShareComputationProofSigned (Sk)
    PR->>EV: Publish ShareComputationProofSigned (ESm)
    PR->>EV: Publish ThresholdShareCreated (with signed proofs)
    deactivate PR

    activate KS
    EV->>KS: ShareComputationProofSigned (Sk)
    KS->>KS: handle_share_computation_proof_signed (store signed_sk_share_computation_proof)
    EV->>KS: ShareComputationProofSigned (ESm)
    KS->>KS: handle_share_computation_proof_signed (store signed_e_sm_share_computation_proof)
    EV->>KS: ThresholdShareCreated
    KS->>KS: transition → ReadyForDecryption / Decrypting (include proofs)
    deactivate KS
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

ciphernode

Suggested reviewers

  • hmzakhalid
  • cedoor

Poem

🐰 I hopped through circuits, bits in tow,
Three proofs I fetched from embered glow,
Signed them neat with tassel and cheer,
Sent them home so keys appear —
A crunchy carrot for every peer!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive Most changes are in-scope, but some modifications appear tangential: CI runner changes to self-hosted, timeouts increased in test fixtures, and workspace dependencies added without clear direct connection to T2 circuits. Clarify whether CI migration, test timeout increases, and workspace dependency additions (bincode, ndarray, num-bigint) are prerequisites or separate efforts from T2 circuit integration.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main change: integrating share proofs (T2 circuits for sk_share_computation and e_sm_share_computation) into ciphernodes.
Linked Issues check ✅ Passed The PR successfully implements the objectives from issue #1332 by adding sk_share_computation and e_sm_share_computation circuit support throughout the codebase.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/integrate-t1-share-computation

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.

@ctrlc03 ctrlc03 changed the title feat: integrate share proofs in ciphernode feat: integrate share proofs in ciphernode [skip-line-limit] Feb 16, 2026

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/trbfv/src/gen_pk_share_and_sk_sss.rs (1)

59-111: ⚠️ Potential issue | 🔴 Critical

Unencrypted sensitive cryptographic material persisted to storage — ProofRequestData requires encryption or secure handling.

The sk_raw, eek_raw, pk0_share_raw, and a_raw fields stored in ProofRequestData are sensitive cryptographic material (secret key polynomial, error polynomial, etc.) serialized as unencrypted ArcBytes. These fields are not only held in memory but are persisted to storage via the Persistable<ThresholdKeyshareState> wrapper (threshold_keyshare.rs:322), where GeneratingThresholdShareData containing ProofRequestData is part of the state hierarchy.

Unlike sk_sss which is encrypted via Cipher, these raw polynomial fields pass through unencrypted both in memory and at rest in the database. This creates a significant security exposure for cryptographic secrets.

Consider encrypting ProofRequestData fields or storing them only transiently without persistence, similar to how sk_sss is handled.

crates/zk-helpers/src/ciphernodes_committee.rs (1)

41-41: ⚠️ Potential issue | 🟠 Major

unreachable!() becomes reachable now that the enum is deserializable.

With Serialize/Deserialize derived, a Medium or Large variant can now arrive via deserialization (e.g., from config or network messages), hitting the unreachable!() and panicking at runtime. Consider returning an error or a default instead.

Proposed fix
-            _ => unreachable!(),
+            other => unimplemented!("Committee size {:?} is not yet supported", other),
🤖 Fix all issues with AI agents
In `@crates/keyshare/src/threshold_keyshare.rs`:
- Around line 430-502: The handlers handle_pk_generation_proof_signed and
handle_share_computation_proof_signed currently assume state::try_into() yields
AggregatingDecryptionKey and will fail (dropping proofs) if the state already
transitioned (e.g., KeyshareState::ReadyForDecryption or
KeyshareState::Decrypting); modify both handlers to accept and update
AggregatingDecryptionKey when it's nested inside those later states (or maintain
a pending_proofs container in the root state) instead of unconditionally calling
try_into(): pattern-match s (the KeyshareState) to find AggregatingDecryptionKey
in KeyshareState::AggregatingDecryptionKey or inside
KeyshareState::ReadyForDecryption / KeyshareState::Decrypting, update the inner
AggregatingDecryptionKey fields (signed_pk_generation_proof or the
signed_*_share_computation_proof set) and write the new state back; ensure you
do not propagate try_into errors for these expected transitions—log unexpected
states but persist the proof so it’s present when KeyshareCreated is emitted.
- Around line 894-901: The T2b proof request currently uses esi_sss_raw[0] which
both can panic if esi_sss_raw is empty and omits proofs for ciphertexts beyond
the first; update the construction of ShareComputationProofRequest
(e_sm_share_computation_request) to handle all ciphertexts: either
change/overload the request to accept all serialized shares (use
secret_sss_raw_list or similar) and pass esi_sss_raw (the full Vec) in place of
secret_sss_raw, or if the protocol only requires a single proof, add an explicit
non-empty check/assert on esi_sss_raw (and validate num_ciphertexts==1) before
accessing [0] and document that constraint; adjust call sites that build/consume
ShareComputationProofRequest accordingly and ensure encrypted_esi_sss /
esi_sss_raw generation and usages (and any loop that encrypts shares) are
consistent with the chosen fix.

In `@crates/trbfv/src/gen_esi_sss.rs`:
- Line 88: The info log "gen_esi_sss:generate_smudging_error..." is misleading;
update the log call near the generate_secret_shares_from_poly invocation to
accurately describe the operation (e.g.,
"gen_esi_sss:generate_secret_shares_from_poly started" or similar) so it matches
the function generate_secret_shares_from_poly and current behavior; locate the
info! invocation in gen_esi_sss.rs and replace the message text accordingly.

In `@crates/zk-prover/src/actors/proof_request.rs`:
- Around line 163-197: The T2a/T2b publish error paths currently only log errors
and leave entries in threshold_correlation and pending_threshold, causing
orphaned state; update the Err branches for the ComputeRequest::zk calls that
publish ZkRequest::ShareComputation (for variables t2a_corr and t2b_corr) to
mirror the T1 (PkGeneration) failure handling: remove the correlation entry from
self.threshold_correlation (using t2a_corr/t2b_corr) and remove the pending
entry keyed by e3_id from self.pending_threshold, log the error, and then return
early (or propagate the error) so the method exits without leaving the pending
state. Ensure you reference the existing symbols threshold_correlation,
pending_threshold, t2a_corr, t2b_corr, e3_id, and the Publish call locations
when applying the fix.
🧹 Nitpick comments (12)
examples/CRISP/test/crisp.spec.ts (1)

113-115: Consider replacing the hard sleep with a polling loop, similar to waitForE3Ready.

A 130-second waitForTimeout is brittle — it either wastes time when things are fast or flakes when they're slow. A polling approach (e.g., periodically checking for the expected "Historic polls" state) would make this more resilient, especially as proof generation times may continue to change.

That said, this is a pre-existing pattern and the bump itself is justified by the added share computation overhead.

crates/tests/tests/integration.rs (1)

103-165: Consider extracting a small helper to reduce repetitive fixture copying.

Each circuit setup follows the same pattern: create_dir_all → copy .json → copy .vk. With five circuits now, a small closure or helper function would reduce boilerplate and make it easier to add future circuits.

♻️ Optional: extract a helper
+        // Helper to copy circuit fixtures
+        let copy_circuit = |circuit_dir: PathBuf, name: &str| {
+            let fixtures = fixtures_dir.clone();
+            async move {
+                tokio::fs::create_dir_all(&circuit_dir).await.unwrap();
+                tokio::fs::copy(
+                    fixtures.join(format!("{name}.json")),
+                    circuit_dir.join(format!("{name}.json")),
+                )
+                .await
+                .unwrap();
+                tokio::fs::copy(
+                    fixtures.join(format!("{name}.vk")),
+                    circuit_dir.join(format!("{name}.vk")),
+                )
+                .await
+                .unwrap();
+            }
+        };
+
+        copy_circuit(circuits_dir.join("dkg").join("pk"), "pk").await;
+        copy_circuit(circuits_dir.join("threshold").join("pk_generation"), "pk_generation").await;
+        copy_circuit(circuits_dir.join("dkg").join("sk_share_computation"), "sk_share_computation").await;
+        copy_circuit(circuits_dir.join("dkg").join("e_sm_share_computation"), "e_sm_share_computation").await;
crates/zk-prover/src/circuits/utils.rs (1)

35-44: Code correctly handles negative integers; minor ordering inconsistency with coefficient parsing.

The concern about negative i64 values is unfounded—FieldElement::try_from_str correctly parses negative decimal strings by reducing them modulo p (via arkworks field arithmetic), so passing "-5" is safe and requires no fallback.

One minor consistency improvement: here u64 is checked before i64 (lines 35, 40), but the coefficient handler checks i64 then u64 (lines 63–64). Aligning the order would improve readability and consistency.

crates/trbfv/src/shares/shares.rs (1)

24-26: Consider returning &[Array2<u64>] instead of &Vec<Array2<u64>>.

Idiomatic Rust prefers returning a slice reference (&[T]) from getters rather than &Vec<T>, as it decouples the public API from the internal storage type.

♻️ Proposed change
-    pub fn moduli_data(&self) -> &Vec<Array2<u64>> {
+    pub fn moduli_data(&self) -> &[Array2<u64>] {
         &self.moduli_data
     }
crates/trbfv/src/gen_esi_sss.rs (1)

32-39: TryFrom is now infallible — could be simplified to From.

Since the conversion from GenEsiSssRequest to InnerRequest no longer performs any fallible operation (it was previously converting error_size from bytes to BigUint), TryFrom can be replaced with From. This is a minor nit and can be deferred.

crates/test-helpers/src/usecase_helpers.rs (1)

74-75: Hardcoded lambda: 40 and num_ciphertexts: 1 — consider making these parameters.

These values are fine for testing, but if different tests need different security parameters or multi-ciphertext scenarios, accepting them as function arguments would improve reusability. Low priority since this is a test helper.

crates/multithread/src/multithread.rs (1)

430-434: Consider using try_into() instead of as u32 for the committee size casts.

committee.n as u32 and committee.threshold as u32 will silently truncate if the source type is usize on a 64-bit platform. While committee sizes are practically small, using u32::try_from(committee.n).map_err(...) would be consistent with the defensive error handling throughout this function.

♻️ Suggested safer cast
     let circuit_data = ShareComputationCircuitData {
         dkg_input_type: req.dkg_input_type,
         secret,
         secret_sss,
         parity_matrix,
-        n_parties: committee.n as u32,
-        threshold: committee.threshold as u32,
+        n_parties: u32::try_from(committee.n)
+            .map_err(|e| make_zk_error(&request, format!("n_parties overflow: {}", e)))?,
+        threshold: u32::try_from(committee.threshold)
+            .map_err(|e| make_zk_error(&request, format!("threshold overflow: {}", e)))?,
     };
crates/zk-prover/src/actors/proof_request.rs (1)

259-360: publish_threshold_share_with_proofs — partial publish on signing/publish failure.

If signing the first proof succeeds but a subsequent sign or bus publish fails, the function returns early without any rollback. For example, if signed_sk_share signing fails (line 277), the function returns without publishing anything — which is fine. But if the ThresholdShareCreated loop (lines 305-324) partially publishes some shares before a later PkGenerationProofSigned publish fails (line 334), some parties will receive shares but the signed proofs won't follow.

This is likely acceptable for now since the threshold protocol requires all proofs to verify, so incomplete data won't be accepted downstream. But it's worth noting for reliability.

crates/events/src/enclave_event/compute_request/zk.rs (1)

87-107: Nit: field initialization order in PkGenerationProofRequest::new differs from declaration order.

The struct declares fields as pk0_share, a, sk, eek, e_sm, params_preset, committee_size but the constructor body assigns e_sm after params_preset (line 103). This is valid Rust but reduces readability.

Suggested reorder
         Self {
             pk0_share: pk0_share.into(),
             a: a.into(),
             sk: sk.into(),
             eek: eek.into(),
-            params_preset,
             e_sm: e_sm.into(),
+            params_preset,
             committee_size,
         }
crates/keyshare/src/threshold_keyshare.rs (3)

882-882: Hardcoded CiphernodesCommitteeSize::Small.

The TODO is noted but this is used in three places (lines 882, 891, 900) for proof request construction. Worth tracking as a follow-up to derive from configuration.

Do you want me to open an issue to track deriving CiphernodesCommitteeSize from configuration?


766-793: Two-phase state mutation in handle_gen_esi_sss_response is correct but subtle.

The code first stores esi_sss in GeneratingThresholdShareData, then checks readiness, calls handle_shares_generated (which reads from the same state), and then transitions to AggregatingDecryptionKey. This ordering is intentional and correct — handle_shares_generated needs the full GeneratingThresholdShareData — but it's worth a brief comment at line 776 explaining why the shares-generated handler is called before the state transition.


1147-1152: Errors from proof-signed handlers are silently discarded.

let _ = discards the Result from both handle_pk_generation_proof_signed and handle_share_computation_proof_signed. While this is consistent with the pattern used for handle_threshold_share_created and handle_encryption_key_created in this match block, given the ordering concern noted above, these errors may indicate a real problem (state mismatch). Consider at minimum logging a warning on failure, similar to how E3Failed is handled with a warn!.

Proposed improvement
             EnclaveEventData::PkGenerationProofSigned(data) => {
-                let _ = self.handle_pk_generation_proof_signed(TypedEvent::new(data, ec));
+                if let Err(err) = self.handle_pk_generation_proof_signed(TypedEvent::new(data, ec)) {
+                    warn!("Failed to handle PkGenerationProofSigned: {err}");
+                }
             }
             EnclaveEventData::ShareComputationProofSigned(data) => {
-                let _ = self.handle_share_computation_proof_signed(TypedEvent::new(data, ec));
+                if let Err(err) = self.handle_share_computation_proof_signed(TypedEvent::new(data, ec)) {
+                    warn!("Failed to handle ShareComputationProofSigned: {err}");
+                }
             }

Comment thread crates/keyshare/src/threshold_keyshare.rs
Comment thread crates/keyshare/src/threshold_keyshare.rs
Comment thread crates/trbfv/src/gen_esi_sss.rs Outdated
Comment thread crates/zk-prover/src/actors/proof_request.rs

@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.

🤖 Fix all issues with AI agents
Verify each finding against the current code and only fix it if needed.


In `@crates/events/src/enclave_event/compute_request/zk.rs`:
- Around line 27-40: Add a constructor for ShareComputationProofRequest to match
the API pattern used by PkBfvProofRequest::new and
PkGenerationProofRequest::new: implement an associated function pub fn
new(secret_raw: SensitiveBytes, secret_sss_raw: SensitiveBytes, dkg_input_type:
DkgInputType, params_preset: BfvPreset, committee_size:
CiphernodesCommitteeSize) -> Self that returns a ShareComputationProofRequest
populated from the parameters (and perform any basic validation or
transformation here if needed), and add docs/tests as appropriate so callers use
ShareComputationProofRequest::new instead of direct struct construction.
- Around line 93-101: The returned Self initializer in the constructor/impl
lists fields in an order that doesn’t match the struct declaration: swap the
positions of params_preset and e_sm so the initializer matches the struct's
declared field order (keep pk0_share, a, sk, eek as-is, then params_preset,
e_sm, committee_size) in the function where Self { pk0_share, a, sk, eek,
params_preset, e_sm, committee_size } is returned.

In `@crates/keyshare/src/threshold_keyshare.rs`:
- Around line 696-708: The code silently skips creating and dispatching
GenEsiSss when GeneratingThresholdShareData.ciphernode_selected is None, which
can stall the state machine; update the branch that currently only handles
Some(ciphernode_selected) around self.state.try_get()?.try_into()? and
handle_gen_esi_sss_requested to either log a warning and return an error (or
transition the state) when ciphernode_selected is None so the unexpected
condition is surfaced; ensure the e_sm_raw unwrap remains validated
(e_sm_raw.expect(...)) or convert to a propagated error if missing, referencing
GeneratingThresholdShareData, ciphernode_selected, handle_gen_esi_sss_requested,
and GenEsiSss to locate the logic to change.
- Line 883: Replace the hardcoded CiphernodesCommitteeSize::Small used in the
three proof request constructions with a value derived from actual committee
configuration (e.g., compute from threshold_n or the E3 configuration object) so
circuit selection matches runtime committee size; locate the three occurrences
of CiphernodesCommitteeSize::Small in threshold_keyshare.rs (used in the proof
request builders) and change them to read from the committee size provider
(e.g., a function or field like threshold_n or e3_config.get_committee_size()),
add a small helper or use existing metadata to map that numeric committee size
to the CiphernodesCommitteeSize enum, and ensure the new value is validated or
defaulted if the config is missing.
- Around line 1148-1153: The two branches handling
EnclaveEventData::PkGenerationProofSigned and
EnclaveEventData::ShareComputationProofSigned currently discard errors with "let
_ =", hiding failures from handle_pk_generation_proof_signed and
handle_share_computation_proof_signed; update these branches to surface errors
like other handlers by chaining the call to .trap(...) with a clear context
message (e.g., "pk generation proof signed handling" and "share computation
proof signed handling") or at minimum match the Result and log the Err using the
module logger before returning, referencing the functions
handle_pk_generation_proof_signed, handle_share_computation_proof_signed and the
enum variants so failures are not silently suppressed.
- Around line 490-498: The match's `other` arm currently logs a warning but
leaves `current` unchanged and still calls
`s.new_state(KeyshareState::AggregatingDecryptionKey(updated))`, causing an
unnecessary persist; update the logic in the `ShareComputationProofSigned`
handling so that when the `other` (unexpected `ProofType`) branch is hit you
short-circuit (e.g., return an Err or otherwise exit the handler) instead of
falling through to `s.new_state`, or alternatively set a flag and skip calling
`s.new_state` if `updated` is identical to `current`; locate the match over
`ProofType`, the `other` arm, the `current` identifier, and the
`s.new_state(KeyshareState::AggregatingDecryptionKey(updated))` call and
implement the early return/skip to avoid redundant persistence.
- Around line 105-132: The fields signed_sk_share_computation_proof and
signed_e_sm_share_computation_proof are carried in AggregatingDecryptionKey,
ReadyForDecryption, and Decrypting but never consumed; either remove these
fields (and their serde derives) from the three structs to avoid serializing
dead data, or explicitly consume/publish them where the other proof is used
(e.g., when constructing/emitting KeyshareCreated) and add a TODO comment
explaining future use. Locate the fields named signed_sk_share_computation_proof
and signed_e_sm_share_computation_proof inside the structs
AggregatingDecryptionKey, ReadyForDecryption, and Decrypting and either delete
those field declarations and related uses, or propagate them into the
KeyshareCreated emission/handling code (where signed_pk_generation_proof is
used) so they are read/published, then mark with a TODO if kept for future
features.

In `@crates/multithread/src/multithread.rs`:
- Around line 391-470: The inline step comments in
handle_share_computation_proof and handle_pk_generation_proof are mis-numbered
(duplicate "4" in handle_share_computation_proof and duplicate "3" in
handle_pk_generation_proof); update the comment tokens so steps are sequential
(for handle_share_computation_proof change the second "4" to "5" and increment
subsequent step numbers accordingly, and for handle_pk_generation_proof change
the second "3" to "4" and renumber following steps) while leaving code logic
unchanged — search for the functions handle_share_computation_proof and
handle_pk_generation_proof to locate the comment lines to edit.
- Around line 425-430: Replace the closure currently used to convert elements
when building secret_sss with a direct function reference; specifically, in the
map where you call shared_secret.moduli_data().iter().map(|arr| arr.mapv(|v|
BigInt::from(v))), change the inner closure |v| BigInt::from(v) to the function
reference BigInt::from so the code uses arr.mapv(BigInt::from) when constructing
secret_sss from moduli_data().

In `@crates/trbfv/src/gen_pk_share_and_sk_sss.rs`:
- Line 155: The call to share_manager.coeffs_to_poly_level0 currently does an
unnecessary allocation: change the argument from
sk_share.coeffs.clone().as_ref() to sk_share.coeffs.as_ref() so you pass a
reference to the Box<[i64]> without cloning; update the invocation in
gen_pk_share_and_sk_sss.rs where sk_poly is assigned (the call to
coeffs_to_poly_level0) to remove the .clone() and keep .as_ref() directly.
- Around line 131-134: Add a short clarifying comment next to the call to
PublicKeyShare::new_extended explaining that the third returned value (the
generated secret key) is intentionally ignored because sk_share from
SecretKey::random is used instead; reference the variables in the line
(pk0_share, a, _, eek and sk_share) so future readers know the underscore is
discarding the redundant secret key on purpose.
- Around line 137-153: There are two identical ShareManager instances
(share_manager_for_esm and share_manager) and an unnecessary params.clone() when
creating them; remove the duplication by creating a single mutable ShareManager
(let mut share_manager = ShareManager::new(num_ciphernodes as usize, threshold
as usize, params)) before calling trbfv.generate_smudging_error and reuse that
same share_manager for bigints_to_poly and later mutable operations; also remove
the extra params.clone() when constructing ShareManager and avoid cloning coeffs
by passing a borrow into bigints_to_poly (use &esi_coeffs rather than cloning).

In `@crates/zk-prover/src/actors/proof_request.rs`:
- Around line 341-360: The loop currently publishes ThresholdShareCreated to
every recipient_party_id in 0..num_parties including the sender itself; change
the loop to skip self-delivery by checking recipient_party_id against this
actor's own party id (e.g. if recipient_party_id == self.party_id { continue }
or use the appropriate field name like self.local_party_id), so that before
calling share.extract_for_party and self.bus.publish you ignore the self target
and avoid duplicate/wasteful publishing of ThresholdShareCreated messages.
- Around line 273-289: The code uses ProofType::T1SkShareComputation and
ProofType::T1ESmShareComputation (where the “T1” prefix denotes protocol phase)
but the surrounding error messages refer to them as "T2a"/"T2b" (proof
numbering); add a brief clarifying comment immediately above the sign_proof
calls (or above the enum usage) stating that the ProofType names use phase-based
"T1" naming while external/doc labels call them "Proof 2a/2b", so the mismatch
between ProofType::T1SkShareComputation / ProofType::T1ESmShareComputation and
the error strings ("Failed to sign T2a proof …", "Failed to sign T2b proof …")
is intentional and not a typo; leave existing function names and error messages
unchanged.
🧹 Nitpick comments (14)
🤖 Fix all nitpicks with AI agents
Verify each finding against the current code and only fix it if needed.


In `@crates/events/src/enclave_event/compute_request/zk.rs`:
- Around line 27-40: Add a constructor for ShareComputationProofRequest to match
the API pattern used by PkBfvProofRequest::new and
PkGenerationProofRequest::new: implement an associated function pub fn
new(secret_raw: SensitiveBytes, secret_sss_raw: SensitiveBytes, dkg_input_type:
DkgInputType, params_preset: BfvPreset, committee_size:
CiphernodesCommitteeSize) -> Self that returns a ShareComputationProofRequest
populated from the parameters (and perform any basic validation or
transformation here if needed), and add docs/tests as appropriate so callers use
ShareComputationProofRequest::new instead of direct struct construction.
- Around line 93-101: The returned Self initializer in the constructor/impl
lists fields in an order that doesn’t match the struct declaration: swap the
positions of params_preset and e_sm so the initializer matches the struct's
declared field order (keep pk0_share, a, sk, eek as-is, then params_preset,
e_sm, committee_size) in the function where Self { pk0_share, a, sk, eek,
params_preset, e_sm, committee_size } is returned.

In `@crates/keyshare/src/threshold_keyshare.rs`:
- Around line 696-708: The code silently skips creating and dispatching
GenEsiSss when GeneratingThresholdShareData.ciphernode_selected is None, which
can stall the state machine; update the branch that currently only handles
Some(ciphernode_selected) around self.state.try_get()?.try_into()? and
handle_gen_esi_sss_requested to either log a warning and return an error (or
transition the state) when ciphernode_selected is None so the unexpected
condition is surfaced; ensure the e_sm_raw unwrap remains validated
(e_sm_raw.expect(...)) or convert to a propagated error if missing, referencing
GeneratingThresholdShareData, ciphernode_selected, handle_gen_esi_sss_requested,
and GenEsiSss to locate the logic to change.
- Line 883: Replace the hardcoded CiphernodesCommitteeSize::Small used in the
three proof request constructions with a value derived from actual committee
configuration (e.g., compute from threshold_n or the E3 configuration object) so
circuit selection matches runtime committee size; locate the three occurrences
of CiphernodesCommitteeSize::Small in threshold_keyshare.rs (used in the proof
request builders) and change them to read from the committee size provider
(e.g., a function or field like threshold_n or e3_config.get_committee_size()),
add a small helper or use existing metadata to map that numeric committee size
to the CiphernodesCommitteeSize enum, and ensure the new value is validated or
defaulted if the config is missing.
- Around line 1148-1153: The two branches handling
EnclaveEventData::PkGenerationProofSigned and
EnclaveEventData::ShareComputationProofSigned currently discard errors with "let
_ =", hiding failures from handle_pk_generation_proof_signed and
handle_share_computation_proof_signed; update these branches to surface errors
like other handlers by chaining the call to .trap(...) with a clear context
message (e.g., "pk generation proof signed handling" and "share computation
proof signed handling") or at minimum match the Result and log the Err using the
module logger before returning, referencing the functions
handle_pk_generation_proof_signed, handle_share_computation_proof_signed and the
enum variants so failures are not silently suppressed.
- Around line 490-498: The match's `other` arm currently logs a warning but
leaves `current` unchanged and still calls
`s.new_state(KeyshareState::AggregatingDecryptionKey(updated))`, causing an
unnecessary persist; update the logic in the `ShareComputationProofSigned`
handling so that when the `other` (unexpected `ProofType`) branch is hit you
short-circuit (e.g., return an Err or otherwise exit the handler) instead of
falling through to `s.new_state`, or alternatively set a flag and skip calling
`s.new_state` if `updated` is identical to `current`; locate the match over
`ProofType`, the `other` arm, the `current` identifier, and the
`s.new_state(KeyshareState::AggregatingDecryptionKey(updated))` call and
implement the early return/skip to avoid redundant persistence.
- Around line 105-132: The fields signed_sk_share_computation_proof and
signed_e_sm_share_computation_proof are carried in AggregatingDecryptionKey,
ReadyForDecryption, and Decrypting but never consumed; either remove these
fields (and their serde derives) from the three structs to avoid serializing
dead data, or explicitly consume/publish them where the other proof is used
(e.g., when constructing/emitting KeyshareCreated) and add a TODO comment
explaining future use. Locate the fields named signed_sk_share_computation_proof
and signed_e_sm_share_computation_proof inside the structs
AggregatingDecryptionKey, ReadyForDecryption, and Decrypting and either delete
those field declarations and related uses, or propagate them into the
KeyshareCreated emission/handling code (where signed_pk_generation_proof is
used) so they are read/published, then mark with a TODO if kept for future
features.

In `@crates/multithread/src/multithread.rs`:
- Around line 391-470: The inline step comments in
handle_share_computation_proof and handle_pk_generation_proof are mis-numbered
(duplicate "4" in handle_share_computation_proof and duplicate "3" in
handle_pk_generation_proof); update the comment tokens so steps are sequential
(for handle_share_computation_proof change the second "4" to "5" and increment
subsequent step numbers accordingly, and for handle_pk_generation_proof change
the second "3" to "4" and renumber following steps) while leaving code logic
unchanged — search for the functions handle_share_computation_proof and
handle_pk_generation_proof to locate the comment lines to edit.
- Around line 425-430: Replace the closure currently used to convert elements
when building secret_sss with a direct function reference; specifically, in the
map where you call shared_secret.moduli_data().iter().map(|arr| arr.mapv(|v|
BigInt::from(v))), change the inner closure |v| BigInt::from(v) to the function
reference BigInt::from so the code uses arr.mapv(BigInt::from) when constructing
secret_sss from moduli_data().

In `@crates/trbfv/src/gen_pk_share_and_sk_sss.rs`:
- Line 155: The call to share_manager.coeffs_to_poly_level0 currently does an
unnecessary allocation: change the argument from
sk_share.coeffs.clone().as_ref() to sk_share.coeffs.as_ref() so you pass a
reference to the Box<[i64]> without cloning; update the invocation in
gen_pk_share_and_sk_sss.rs where sk_poly is assigned (the call to
coeffs_to_poly_level0) to remove the .clone() and keep .as_ref() directly.
- Around line 131-134: Add a short clarifying comment next to the call to
PublicKeyShare::new_extended explaining that the third returned value (the
generated secret key) is intentionally ignored because sk_share from
SecretKey::random is used instead; reference the variables in the line
(pk0_share, a, _, eek and sk_share) so future readers know the underscore is
discarding the redundant secret key on purpose.
- Around line 137-153: There are two identical ShareManager instances
(share_manager_for_esm and share_manager) and an unnecessary params.clone() when
creating them; remove the duplication by creating a single mutable ShareManager
(let mut share_manager = ShareManager::new(num_ciphernodes as usize, threshold
as usize, params)) before calling trbfv.generate_smudging_error and reuse that
same share_manager for bigints_to_poly and later mutable operations; also remove
the extra params.clone() when constructing ShareManager and avoid cloning coeffs
by passing a borrow into bigints_to_poly (use &esi_coeffs rather than cloning).

In `@crates/zk-prover/src/actors/proof_request.rs`:
- Around line 341-360: The loop currently publishes ThresholdShareCreated to
every recipient_party_id in 0..num_parties including the sender itself; change
the loop to skip self-delivery by checking recipient_party_id against this
actor's own party id (e.g. if recipient_party_id == self.party_id { continue }
or use the appropriate field name like self.local_party_id), so that before
calling share.extract_for_party and self.bus.publish you ignore the self target
and avoid duplicate/wasteful publishing of ThresholdShareCreated messages.
- Around line 273-289: The code uses ProofType::T1SkShareComputation and
ProofType::T1ESmShareComputation (where the “T1” prefix denotes protocol phase)
but the surrounding error messages refer to them as "T2a"/"T2b" (proof
numbering); add a brief clarifying comment immediately above the sign_proof
calls (or above the enum usage) stating that the ProofType names use phase-based
"T1" naming while external/doc labels call them "Proof 2a/2b", so the mismatch
between ProofType::T1SkShareComputation / ProofType::T1ESmShareComputation and
the error strings ("Failed to sign T2a proof …", "Failed to sign T2b proof …")
is intentional and not a typo; leave existing function names and error messages
unchanged.
crates/events/src/enclave_event/compute_request/zk.rs (2)

27-40: Consider adding a constructor for ShareComputationProofRequest.

PkBfvProofRequest and PkGenerationProofRequest both have new() constructors, but ShareComputationProofRequest does not. Adding one would keep the API surface consistent and give a single place to add future validation or transformation logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/events/src/enclave_event/compute_request/zk.rs` around lines 27 - 40,
Add a constructor for ShareComputationProofRequest to match the API pattern used
by PkBfvProofRequest::new and PkGenerationProofRequest::new: implement an
associated function pub fn new(secret_raw: SensitiveBytes, secret_sss_raw:
SensitiveBytes, dkg_input_type: DkgInputType, params_preset: BfvPreset,
committee_size: CiphernodesCommitteeSize) -> Self that returns a
ShareComputationProofRequest populated from the parameters (and perform any
basic validation or transformation here if needed), and add docs/tests as
appropriate so callers use ShareComputationProofRequest::new instead of direct
struct construction.

93-101: Nit: field initialization order doesn't match declaration order.

e_sm and params_preset are swapped compared to the struct definition (lines 55–72). In Rust this is functionally correct, but matching declaration order improves readability and makes diffs cleaner.

Suggested reorder
         Self {
             pk0_share: pk0_share.into(),
             a: a.into(),
             sk,
             eek,
-            params_preset,
             e_sm,
+            params_preset,
             committee_size,
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/events/src/enclave_event/compute_request/zk.rs` around lines 93 - 101,
The returned Self initializer in the constructor/impl lists fields in an order
that doesn’t match the struct declaration: swap the positions of params_preset
and e_sm so the initializer matches the struct's declared field order (keep
pk0_share, a, sk, eek as-is, then params_preset, e_sm, committee_size) in the
function where Self { pk0_share, a, sk, eek, params_preset, e_sm, committee_size
} is returned.
crates/multithread/src/multithread.rs (2)

391-470: Duplicate step numbering in comments.

Steps are numbered "4" twice (lines 421 and 432). The second "4" should be "5", and subsequent steps renumbered accordingly.

Similarly in handle_pk_generation_proof, step "3" appears twice (lines 495 and 511).

Proposed fix for handle_share_computation_proof
-    // 4. Deserialize Shamir shares (bincode-encoded SharedSecret)
+    // 4. Deserialize Shamir shares (bincode-encoded SharedSecret)
     ...
-    // 4. Compute parity matrix
+    // 5. Compute parity matrix
     ...
-    // 5. Build circuit data
+    // 6. Build circuit data
     ...
-    // 6. Generate proof
+    // 7. Generate proof
     ...
-    // 7. Return response
+    // 8. Return response
Proposed fix for handle_pk_generation_proof
-    // 3. Convert Poly → CrtPolynomial
+    // 4. Convert Poly → CrtPolynomial
     ...
-    // 4. Build circuit data
+    // 5. Build circuit data
     ...
-    // 5. Generate proof via Provable trait
+    // 6. Generate proof via Provable trait
     ...
-    // 6. Return response
+    // 7. Return response
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/multithread/src/multithread.rs` around lines 391 - 470, The inline
step comments in handle_share_computation_proof and handle_pk_generation_proof
are mis-numbered (duplicate "4" in handle_share_computation_proof and duplicate
"3" in handle_pk_generation_proof); update the comment tokens so steps are
sequential (for handle_share_computation_proof change the second "4" to "5" and
increment subsequent step numbers accordingly, and for
handle_pk_generation_proof change the second "3" to "4" and renumber following
steps) while leaving code logic unchanged — search for the functions
handle_share_computation_proof and handle_pk_generation_proof to locate the
comment lines to edit.

425-430: Nit: closure can be simplified to a function reference.

|v| BigInt::from(v) can be written as BigInt::from directly.

Suggested diff
     let secret_sss: Vec<Array2<BigInt>> = shared_secret
         .moduli_data()
         .iter()
-        .map(|arr| arr.mapv(|v| BigInt::from(v)))
+        .map(|arr| arr.mapv(BigInt::from))
         .collect();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/multithread/src/multithread.rs` around lines 425 - 430, Replace the
closure currently used to convert elements when building secret_sss with a
direct function reference; specifically, in the map where you call
shared_secret.moduli_data().iter().map(|arr| arr.mapv(|v| BigInt::from(v))),
change the inner closure |v| BigInt::from(v) to the function reference
BigInt::from so the code uses arr.mapv(BigInt::from) when constructing
secret_sss from moduli_data().
crates/zk-prover/src/actors/proof_request.rs (2)

341-360: Consider whether publishing shares to all parties (including self) is intended.

The loop iterates 0..num_parties and publishes a ThresholdShareCreated for every recipient, including the sender's own party_id. If self-delivery is wasteful or causes duplicate processing downstream, you may want to skip it.

#!/bin/bash
# Check if ThresholdShareCreated handlers filter out self-delivery
rg -n 'ThresholdShareCreated' --type rust -A 10 | head -80
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/zk-prover/src/actors/proof_request.rs` around lines 341 - 360, The
loop currently publishes ThresholdShareCreated to every recipient_party_id in
0..num_parties including the sender itself; change the loop to skip
self-delivery by checking recipient_party_id against this actor's own party id
(e.g. if recipient_party_id == self.party_id { continue } or use the appropriate
field name like self.local_party_id), so that before calling
share.extract_for_party and self.bus.publish you ignore the self target and
avoid duplicate/wasteful publishing of ThresholdShareCreated messages.

273-289: Naming scheme uses different conventions: T1 variants reference protocol phase, not proof numbering.

The ProofType::T1SkShareComputation and ProofType::T1ESmShareComputation variants are correctly named according to their enum definition. The T1 prefix refers to the protocol phase (not a typo), while the enum documentation labels them as "Proof 2a" and "Proof 2b", creating a naming inconsistency with the surrounding error messages that call them "T2a" and "T2b" proofs. This is confusing but intentional. Adding a clarifying comment explaining the dual naming convention (phase vs. proof numbering) would improve readability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/zk-prover/src/actors/proof_request.rs` around lines 273 - 289, The
code uses ProofType::T1SkShareComputation and ProofType::T1ESmShareComputation
(where the “T1” prefix denotes protocol phase) but the surrounding error
messages refer to them as "T2a"/"T2b" (proof numbering); add a brief clarifying
comment immediately above the sign_proof calls (or above the enum usage) stating
that the ProofType names use phase-based "T1" naming while external/doc labels
call them "Proof 2a/2b", so the mismatch between ProofType::T1SkShareComputation
/ ProofType::T1ESmShareComputation and the error strings ("Failed to sign T2a
proof …", "Failed to sign T2b proof …") is intentional and not a typo; leave
existing function names and error messages unchanged.
crates/trbfv/src/gen_pk_share_and_sk_sss.rs (3)

155-155: Remove unnecessary .clone() before .as_ref().

sk_share.coeffs is a Box<[i64]> which implements AsRef<[i64]>, so calling .as_ref() directly works without the intermediate .clone() allocation. The same pattern is already used correctly elsewhere in the codebase (e.g., crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/sample.rs:86).

♻️ Suggested fix
-    let sk_poly = share_manager.coeffs_to_poly_level0(sk_share.coeffs.clone().as_ref())?;
+    let sk_poly = share_manager.coeffs_to_poly_level0(sk_share.coeffs.as_ref())?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/trbfv/src/gen_pk_share_and_sk_sss.rs` at line 155, The call to
share_manager.coeffs_to_poly_level0 currently does an unnecessary allocation:
change the argument from sk_share.coeffs.clone().as_ref() to
sk_share.coeffs.as_ref() so you pass a reference to the Box<[i64]> without
cloning; update the invocation in gen_pk_share_and_sk_sss.rs where sk_poly is
assigned (the call to coeffs_to_poly_level0) to remove the .clone() and keep
.as_ref() directly.

131-134: Consider adding a clarifying comment explaining why the third return value from new_extended is discarded.

The function PublicKeyShare::new_extended returns four values. In this code, the third value is intentionally discarded because the function already has sk_share from the earlier SecretKey::random call. A brief comment (e.g., // Third value (sk) not needed; we already have sk_share from above) would help future maintainers understand this design choice.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/trbfv/src/gen_pk_share_and_sk_sss.rs` around lines 131 - 134, Add a
short clarifying comment next to the call to PublicKeyShare::new_extended
explaining that the third returned value (the generated secret key) is
intentionally ignored because sk_share from SecretKey::random is used instead;
reference the variables in the line (pk0_share, a, _, eek and sk_share) so
future readers know the underscore is discarding the redundant secret key on
purpose.

137-153: Consolidate ShareManager to eliminate redundant allocation and remove unnecessary clone.

share_manager_for_esm and share_manager are identical instances. Create a single let mut share_manager before calling bigints_to_poly (which requires &self only) and reuse it for subsequent mutable operations. Additionally, remove the unnecessary clone() at line 155 since coeffs can be borrowed directly.

♻️ Suggested consolidation
-    let share_manager_for_esm =
-        ShareManager::new(num_ciphernodes as usize, threshold as usize, params.clone());
     let esi_coeffs = trbfv.generate_smudging_error(
         req.num_ciphertexts,
         req.lambda,
         &mut *rng.lock().unwrap(),
     )?;
-    let e_sm_rns = share_manager_for_esm.bigints_to_poly(&esi_coeffs)?;
+
+    let mut share_manager =
+        ShareManager::new(num_ciphernodes as usize, threshold as usize, params.clone());
+
+    let e_sm_rns = share_manager.bigints_to_poly(&esi_coeffs)?;
     let e_sm_raw = ArcBytes::from_bytes(&e_sm_rns.deref().to_bytes());
 
     let pk0_share_raw = ArcBytes::from_bytes(&pk0_share.to_bytes());
     let a_raw = ArcBytes::from_bytes(&a.to_bytes());
     let eek_raw = ArcBytes::from_bytes(&eek.to_bytes());
 
-    let mut share_manager =
-        ShareManager::new(num_ciphernodes as usize, threshold as usize, params.clone());
-
-    let sk_poly = share_manager.coeffs_to_poly_level0(sk_share.coeffs.clone().as_ref())?;
+    let sk_poly = share_manager.coeffs_to_poly_level0(sk_share.coeffs.as_ref())?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/trbfv/src/gen_pk_share_and_sk_sss.rs` around lines 137 - 153, There
are two identical ShareManager instances (share_manager_for_esm and
share_manager) and an unnecessary params.clone() when creating them; remove the
duplication by creating a single mutable ShareManager (let mut share_manager =
ShareManager::new(num_ciphernodes as usize, threshold as usize, params)) before
calling trbfv.generate_smudging_error and reuse that same share_manager for
bigints_to_poly and later mutable operations; also remove the extra
params.clone() when constructing ShareManager and avoid cloning coeffs by
passing a borrow into bigints_to_poly (use &esi_coeffs rather than cloning).
crates/keyshare/src/threshold_keyshare.rs (5)

696-708: expect on Line 704 is safe but consider defensive handling for ciphernode_selected.

If ciphernode_selected is None (Line 698), the GenEsiSss request is silently skipped and the state machine stalls in GeneratingThresholdShare. While this shouldn't happen in the current flow (it's always Some per Line 594), consider logging a warning or returning an error on the else branch to surface unexpected conditions.

Proposed fix
-        if let Some(ciphernode_selected) = current_state.ciphernode_selected {
+        let Some(ciphernode_selected) = current_state.ciphernode_selected else {
+            bail!("ciphernode_selected missing in GeneratingThresholdShareData - state machine cannot proceed");
+        };
+        {
             self.handle_gen_esi_sss_requested(TypedEvent::new(
                 GenEsiSss {
                     ciphernode_selected,
                     e_sm_raw: current_state
                         .e_sm_raw
                         .expect("e_sm_raw should be set at this point"),
                 },
                 ec.clone(),
             ))?;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/keyshare/src/threshold_keyshare.rs` around lines 696 - 708, The code
silently skips creating and dispatching GenEsiSss when
GeneratingThresholdShareData.ciphernode_selected is None, which can stall the
state machine; update the branch that currently only handles
Some(ciphernode_selected) around self.state.try_get()?.try_into()? and
handle_gen_esi_sss_requested to either log a warning and return an error (or
transition the state) when ciphernode_selected is None so the unexpected
condition is surfaced; ensure the e_sm_raw unwrap remains validated
(e_sm_raw.expect(...)) or convert to a propagated error if missing, referencing
GeneratingThresholdShareData, ciphernode_selected, handle_gen_esi_sss_requested,
and GenEsiSss to locate the logic to change.

883-883: Hardcoded CiphernodesCommitteeSize::Small in three proof requests.

The TODO on Line 883 acknowledges this should be derived from config. Since the committee size directly impacts circuit selection and proof verification, this should be tracked. Consider deriving it from threshold_n or the E3 configuration.

Would you like me to open an issue to track deriving CiphernodesCommitteeSize from the actual committee configuration?

Also applies to: 892-892, 901-901

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/keyshare/src/threshold_keyshare.rs` at line 883, Replace the hardcoded
CiphernodesCommitteeSize::Small used in the three proof request constructions
with a value derived from actual committee configuration (e.g., compute from
threshold_n or the E3 configuration object) so circuit selection matches runtime
committee size; locate the three occurrences of CiphernodesCommitteeSize::Small
in threshold_keyshare.rs (used in the proof request builders) and change them to
read from the committee size provider (e.g., a function or field like
threshold_n or e3_config.get_committee_size()), add a small helper or use
existing metadata to map that numeric committee size to the
CiphernodesCommitteeSize enum, and ensure the new value is validated or
defaulted if the config is missing.

1148-1153: Silent error suppression with let _ = masks proof storage failures.

Errors from handle_pk_generation_proof_signed and handle_share_computation_proof_signed are silently discarded. If a state mismatch or persistence failure occurs, there's no log trace. Other handlers in this file (e.g., Handler<TypedEvent<ComputeResponse>>) use the trap pattern for error handling. Consider using trap or at minimum logging on error:

Proposed fix
             EnclaveEventData::PkGenerationProofSigned(data) => {
-                let _ = self.handle_pk_generation_proof_signed(TypedEvent::new(data, ec));
+                if let Err(e) = self.handle_pk_generation_proof_signed(TypedEvent::new(data, ec)) {
+                    warn!("Failed to handle PkGenerationProofSigned: {}", e);
+                }
             }
             EnclaveEventData::ShareComputationProofSigned(data) => {
-                let _ = self.handle_share_computation_proof_signed(TypedEvent::new(data, ec));
+                if let Err(e) = self.handle_share_computation_proof_signed(TypedEvent::new(data, ec)) {
+                    warn!("Failed to handle ShareComputationProofSigned: {}", e);
+                }
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/keyshare/src/threshold_keyshare.rs` around lines 1148 - 1153, The two
branches handling EnclaveEventData::PkGenerationProofSigned and
EnclaveEventData::ShareComputationProofSigned currently discard errors with "let
_ =", hiding failures from handle_pk_generation_proof_signed and
handle_share_computation_proof_signed; update these branches to surface errors
like other handlers by chaining the call to .trap(...) with a clear context
message (e.g., "pk generation proof signed handling" and "share computation
proof signed handling") or at minimum match the Result and log the Err using the
module logger before returning, referencing the functions
handle_pk_generation_proof_signed, handle_share_computation_proof_signed and the
enum variants so failures are not silently suppressed.

490-498: other arm in proof type match still writes unchanged state.

When an unexpected ProofType is received, the warn! fires but current (unchanged) is still passed to s.new_state(...), triggering a redundant persist. Consider returning an error or short-circuiting:

Proposed fix
                 ProofType::T1ESmShareComputation => AggregatingDecryptionKey {
                     signed_e_sm_share_computation_proof: Some(msg.signed_proof),
                     ..current
                 },
                 other => {
                     warn!(
                         "Unexpected proof type {:?} in ShareComputationProofSigned",
                         other
                     );
-                    current
+                    bail!("Unexpected proof type {:?} in ShareComputationProofSigned", other);
                 }
             };
-            s.new_state(KeyshareState::AggregatingDecryptionKey(updated))
+            s.new_state(KeyshareState::AggregatingDecryptionKey(updated))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/keyshare/src/threshold_keyshare.rs` around lines 490 - 498, The
match's `other` arm currently logs a warning but leaves `current` unchanged and
still calls `s.new_state(KeyshareState::AggregatingDecryptionKey(updated))`,
causing an unnecessary persist; update the logic in the
`ShareComputationProofSigned` handling so that when the `other` (unexpected
`ProofType`) branch is hit you short-circuit (e.g., return an Err or otherwise
exit the handler) instead of falling through to `s.new_state`, or alternatively
set a flag and skip calling `s.new_state` if `updated` is identical to
`current`; locate the match over `ProofType`, the `other` arm, the `current`
identifier, and the
`s.new_state(KeyshareState::AggregatingDecryptionKey(updated))` call and
implement the early return/skip to avoid redundant persistence.

105-132: signed_sk_share_computation_proof and signed_e_sm_share_computation_proof are carried through all states but never consumed.

These two proofs are threaded from AggregatingDecryptionKeyReadyForDecryptionDecrypting but are never read or published. Only signed_pk_generation_proof is used (Line 1038 in KeyshareCreated). If these are intended for future use, consider adding a TODO comment; otherwise they're dead data inflating serialized state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/keyshare/src/threshold_keyshare.rs` around lines 105 - 132, The fields
signed_sk_share_computation_proof and signed_e_sm_share_computation_proof are
carried in AggregatingDecryptionKey, ReadyForDecryption, and Decrypting but
never consumed; either remove these fields (and their serde derives) from the
three structs to avoid serializing dead data, or explicitly consume/publish them
where the other proof is used (e.g., when constructing/emitting KeyshareCreated)
and add a TODO comment explaining future use. Locate the fields named
signed_sk_share_computation_proof and signed_e_sm_share_computation_proof inside
the structs AggregatingDecryptionKey, ReadyForDecryption, and Decrypting and
either delete those field declarations and related uses, or propagate them into
the KeyshareCreated emission/handling code (where signed_pk_generation_proof is
used) so they are read/published, then mark with a TODO if kept for future
features.

@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.

🧹 Nitpick comments (3)
crates/multithread/src/multithread.rs (1)

423-502: Share computation proof handler is well-structured.

The implementation correctly:

  • Builds threshold params via build_pair_for_preset (needed for compute_parity_matrix and polynomial deserialization).
  • Conditionally centers the polynomial only for DkgInputType::SecretKey.
  • Converts Shamir share data from u64 to BigInt for circuit compatibility.
  • Uses consistent error mapping patterns.

Two minor nits: (1) duplicate step numbering in comments — there are two "Step 4" labels at lines 453 and 464; (2) .mapv(|v| BigInt::from(v)) at line 461 could be .mapv(BigInt::from).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/multithread/src/multithread.rs` around lines 423 - 502, In
handle_share_computation_proof: fix two minor nits — rename the duplicate
comment label (the second "Step 4" that precedes the parity matrix/build circuit
block) so the step numbers are sequential/unique, and simplify the conversion
closure by replacing .mapv(|v| BigInt::from(v)) with .mapv(BigInt::from) on the
shared_secret → secret_sss conversion to use the function pointer form.
crates/zk-prover/src/actors/proof_request.rs (1)

266-296: Error messages incorrectly refer to "T2a"/"T2b" proofs when these variants are documented as "T1" tier.

The ProofType enum in crates/events/src/enclave_event/signed_proof.rs documents T1SkShareComputation and T1ESmShareComputation as "T1 — ... (Proof 2a)" and "T1 — ... (Proof 2b)" respectively, where the numbers refer to proof numbering, not tier classification. However, the error messages at lines 279 and 287 claim these are "T2a" and "T2b" proofs, which contradicts the enum documentation. Update the error messages to reference "T1" tier or the specific proof names to avoid confusion during debugging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/zk-prover/src/actors/proof_request.rs` around lines 266 - 296, In
publish_threshold_share_with_proofs, the error messages for failed signature of
T1SkShareComputation and T1ESmShareComputation incorrectly call them
"T2a"/"T2b"; change these messages to reference the correct tier/name (e.g.,
"T1SkShareComputation (T1 — Proof 2a)" and "T1ESmShareComputation (T1 — Proof
2b)" or simply "T1") so they match the ProofType enum documentation; update the
error strings in the sign_proof failure branches for
ProofType::T1SkShareComputation and ProofType::T1ESmShareComputation in
publish_threshold_share_with_proofs accordingly.
crates/keyshare/src/threshold_keyshare.rs (1)

882-918: CiphernodesCommitteeSize::Small is hardcoded for all three proof requests at lines 879, 888, and 900.

The TODO: derive from config comment at line 879 indicates this is a known placeholder. However, Medium and Large committee size variants are not yet fully implemented (the match cases are commented out in ciphernodes_committee.rs). Before deriving from configuration, the implementation for these variants must be completed and the hardcoded values replaced with config-driven logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/keyshare/src/threshold_keyshare.rs` around lines 882 - 918, The three
ShareComputationProofRequest constructions currently hardcode
CiphernodesCommitteeSize::Small; replace those hardcoded values with a
committee_size value derived from configuration (as hinted by the TODO) and pass
that variable into each ShareComputationProofRequest (e.g., for
sk_share_computation_request and e_sm_share_computation_request and the related
T1 request). Before doing so, implement the missing Medium and Large variants in
ciphernodes_committee.rs (uncomment/complete the match arms and any helper
logic) so the config-driven committee_size can return correct values; then read
the configured committee size at the start of the function, assign it to a local
variable (committee_size) and use that variable when constructing
ThresholdSharePending and its embedded ShareComputationProofRequest instances.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/keyshare/src/threshold_keyshare.rs`:
- Around line 462-502: The code attempts to always convert the current state
into an AggregatingDecryptionKey via try_into inside
handle_share_computation_proof_signed, which will fail if the state already
moved to ReadyForDecryption; fix by matching the mutable state variant before
converting/updating: inside self.state.try_mutate(&ec, |s| { ... })
pattern-match s (or s.clone()) for
KeyshareState::AggregatingDecryptionKey(current) and only update that branch by
inserting the appropriate signed proof into current (using the existing match on
msg.proof_type); in the non-matching branches (e.g., ReadyForDecryption) simply
log a debug/warn about late proof and return the unchanged state instead of
attempting try_into or propagating an error. This removes the race-prone
try_into call and prevents silent errors.

In `@crates/trbfv/src/gen_esi_sss.rs`:
- Line 88: The info log in gen_esi_sss.rs currently prints
"gen_esi_sss:generate_smudging_error..." which is misleading because the code is
performing generate_secret_shares_from_poly; update the log message in the
generate_secret_shares_from_poly call site to accurately describe the operation
(e.g., "gen_esi_sss:generate_secret_shares_from_poly..." or
"gen_esi_sss:generating secret shares from poly...") so logs match the function
action and aid debugging; locate the log invocation by searching for
info!("gen_esi_sss:generate_smudging_error") within gen_esi_sss and replace the
string accordingly.

---

Nitpick comments:
In `@crates/keyshare/src/threshold_keyshare.rs`:
- Around line 882-918: The three ShareComputationProofRequest constructions
currently hardcode CiphernodesCommitteeSize::Small; replace those hardcoded
values with a committee_size value derived from configuration (as hinted by the
TODO) and pass that variable into each ShareComputationProofRequest (e.g., for
sk_share_computation_request and e_sm_share_computation_request and the related
T1 request). Before doing so, implement the missing Medium and Large variants in
ciphernodes_committee.rs (uncomment/complete the match arms and any helper
logic) so the config-driven committee_size can return correct values; then read
the configured committee size at the start of the function, assign it to a local
variable (committee_size) and use that variable when constructing
ThresholdSharePending and its embedded ShareComputationProofRequest instances.

In `@crates/multithread/src/multithread.rs`:
- Around line 423-502: In handle_share_computation_proof: fix two minor nits —
rename the duplicate comment label (the second "Step 4" that precedes the parity
matrix/build circuit block) so the step numbers are sequential/unique, and
simplify the conversion closure by replacing .mapv(|v| BigInt::from(v)) with
.mapv(BigInt::from) on the shared_secret → secret_sss conversion to use the
function pointer form.

In `@crates/zk-prover/src/actors/proof_request.rs`:
- Around line 266-296: In publish_threshold_share_with_proofs, the error
messages for failed signature of T1SkShareComputation and T1ESmShareComputation
incorrectly call them "T2a"/"T2b"; change these messages to reference the
correct tier/name (e.g., "T1SkShareComputation (T1 — Proof 2a)" and
"T1ESmShareComputation (T1 — Proof 2b)" or simply "T1") so they match the
ProofType enum documentation; update the error strings in the sign_proof failure
branches for ProofType::T1SkShareComputation and
ProofType::T1ESmShareComputation in publish_threshold_share_with_proofs
accordingly.

@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

🧹 Nitpick comments (3)
crates/trbfv/src/shares/shares.rs (1)

24-26: Consider returning &[Array2<u64>] instead of &Vec<Array2<u64>>.

Idiomatic Rust prefers &[T] over &Vec<T> for read-only accessors since it's more general and avoids coupling callers to Vec.

♻️ Suggested change
-    pub fn moduli_data(&self) -> &Vec<Array2<u64>> {
-        &self.moduli_data
+    pub fn moduli_data(&self) -> &[Array2<u64>] {
+        &self.moduli_data
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/trbfv/src/shares/shares.rs` around lines 24 - 26, The accessor
moduli_data currently returns &Vec<Array2<u64>>; change its signature to return
a slice reference & [Array2<u64>] and return &self.moduli_data[..] to be more
general and idiomatic. Update the function declaration for moduli_data and
adjust any call sites that expect &Vec<Array2<u64>> to accept a slice
(&[Array2<u64>]) or to call .to_vec() only where an owned Vec is needed. Ensure
the type imports and visibility remain unchanged.
crates/multithread/src/multithread.rs (1)

494-498: Prefer ShareComputationProofResponse::new() for consistency.

PkBfvProofResponse::new(proof) and PkGenerationProofResponse::new(proof) are used in sibling functions; constructing ShareComputationProofResponse inline is inconsistent.

♻️ Proposed fix
-    Ok(ComputeResponse::zk(
-        ZkResponse::ShareComputation(ShareComputationProofResponse {
-            proof,
-            dkg_input_type: req.dkg_input_type,
-        }),
+    Ok(ComputeResponse::zk(
+        ZkResponse::ShareComputation(ShareComputationProofResponse::new(
+            proof,
+            req.dkg_input_type,
+        )),
         request.correlation_id,
         request.e3_id,
     ))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/multithread/src/multithread.rs` around lines 494 - 498, Construct the
response using ShareComputationProofResponse::new(...) instead of building the
struct inline to match sibling usage (like PkBfvProofResponse::new and
PkGenerationProofResponse::new); replace the inline
ShareComputationProofResponse { proof, dkg_input_type: req.dkg_input_type } used
inside ComputeResponse::zk(...) with a call to
ShareComputationProofResponse::new(proof, req.dkg_input_type) (or the correct
parameter order expected by ShareComputationProofResponse::new) so the code is
consistent and uses the type's constructor helper.
crates/events/src/enclave_event/compute_request/zk.rs (1)

27-40: ShareComputationProofRequest is missing a new() constructor.

Both PkBfvProofRequest and PkGenerationProofRequest expose new() constructors; ShareComputationProofRequest departs from this pattern.

♻️ Proposed constructor
+impl ShareComputationProofRequest {
+    pub fn new(
+        secret_raw: SensitiveBytes,
+        secret_sss_raw: SensitiveBytes,
+        dkg_input_type: DkgInputType,
+        params_preset: BfvPreset,
+        committee_size: CiphernodesCommitteeSize,
+    ) -> Self {
+        Self {
+            secret_raw,
+            secret_sss_raw,
+            dkg_input_type,
+            params_preset,
+            committee_size,
+        }
+    }
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/events/src/enclave_event/compute_request/zk.rs` around lines 27 - 40,
Add a public constructor pub fn new(...) -> Self for
ShareComputationProofRequest to match the pattern used by PkBfvProofRequest and
PkGenerationProofRequest: accept parameters for secret_raw: SensitiveBytes,
secret_sss_raw: SensitiveBytes, dkg_input_type: DkgInputType, params_preset:
BfvPreset, and committee_size: CiphernodesCommitteeSize and return a
ShareComputationProofRequest with those fields set accordingly; implement it as
an inherent impl on ShareComputationProofRequest so callers can construct
instances via ShareComputationProofRequest::new(...).
🤖 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 875-889: T2a/T2b share computation requests (the
ShareComputationProofRequest instances, e_sm_share_computation_request and the
earlier t1 request) hardcode CiphernodesCommitteeSize::Small but lack the
matching TODO comment; update the two ShareComputationProofRequest constructions
that set committee_size (e.g., the variable e_sm_share_computation_request and
the T1 request) to include the same "// TODO: derive from config" comment next
to the committee_size assignment so the intent and consistency are clear, or
refactor to read committee size from the shared config variable used elsewhere
if preferred.
- Around line 456-496: Update the doc comment above
handle_share_computation_proof_signed and the related log message that mentions
"T2a/T2b" to use the formal ProofType variant names (T1SkShareComputation and
T1ESmShareComputation) or else use the consistent "Proof 2a/Proof 2b" wording;
locate the doc comment on the handle_share_computation_proof_signed method and
the info!/log call that describes the three proofs and replace the informal
"T2a/T2b" wording with the chosen formal variant names
(ProofType::T1SkShareComputation, ProofType::T1ESmShareComputation) so comments
and logs match the enum definition.

In `@crates/multithread/src/multithread.rs`:
- Around line 453-468: Update the duplicate step label: the comment above the
compute_parity_matrix call is mistakenly labeled "// 4. Compute parity matrix"
but the prior block that deserializes SharedSecret (using variable
secret_sss_bytes → SharedSecret → secret_sss) is already "// 4."; change the
comment preceding compute_parity_matrix (the call to compute_parity_matrix(...
threshold_params.moduli(), committee.n, committee.threshold) where committee
comes from req.committee_size.values()) to "// 5. Compute parity matrix" so the
step numbering is sequential.

---

Duplicate comments:
In `@crates/keyshare/src/threshold_keyshare.rs`:
- Around line 879-889: Previously the code indexed esi_sss_raw with [0], which
could panic; fix by using a safe bounds guard when constructing
e_sm_share_computation_request: call .into_iter().next().ok_or_else(||
anyhow!("esi_sss_raw is empty — expected at least one entry"))? to obtain
secret_sss_raw instead of direct indexing. Locate the
ShareComputationProofRequest creation (variable e_sm_share_computation_request)
and ensure it uses esi_sss_raw.into_iter().next().ok_or_else(...) as shown so
the empty-case returns an error rather than panicking.

In `@crates/trbfv/src/gen_esi_sss.rs`:
- Line 88: The log message in the gen_esi_sss::generate_secret_shares_from_poly
path was misleading and should be updated to accurately reflect the operation;
update the info! call inside the generate_secret_shares_from_poly function (and
any other occurrences in the gen_esi_sss module) to use
"gen_esi_sss:generate_secret_shares_from_poly..." instead of
"gen_esi_sss:generate_smudging_error..." and run a quick grep for the old string
to replace any remaining instances to keep logs consistent.

---

Nitpick comments:
In `@crates/events/src/enclave_event/compute_request/zk.rs`:
- Around line 27-40: Add a public constructor pub fn new(...) -> Self for
ShareComputationProofRequest to match the pattern used by PkBfvProofRequest and
PkGenerationProofRequest: accept parameters for secret_raw: SensitiveBytes,
secret_sss_raw: SensitiveBytes, dkg_input_type: DkgInputType, params_preset:
BfvPreset, and committee_size: CiphernodesCommitteeSize and return a
ShareComputationProofRequest with those fields set accordingly; implement it as
an inherent impl on ShareComputationProofRequest so callers can construct
instances via ShareComputationProofRequest::new(...).

In `@crates/multithread/src/multithread.rs`:
- Around line 494-498: Construct the response using
ShareComputationProofResponse::new(...) instead of building the struct inline to
match sibling usage (like PkBfvProofResponse::new and
PkGenerationProofResponse::new); replace the inline
ShareComputationProofResponse { proof, dkg_input_type: req.dkg_input_type } used
inside ComputeResponse::zk(...) with a call to
ShareComputationProofResponse::new(proof, req.dkg_input_type) (or the correct
parameter order expected by ShareComputationProofResponse::new) so the code is
consistent and uses the type's constructor helper.

In `@crates/trbfv/src/shares/shares.rs`:
- Around line 24-26: The accessor moduli_data currently returns
&Vec<Array2<u64>>; change its signature to return a slice reference &
[Array2<u64>] and return &self.moduli_data[..] to be more general and idiomatic.
Update the function declaration for moduli_data and adjust any call sites that
expect &Vec<Array2<u64>> to accept a slice (&[Array2<u64>]) or to call .to_vec()
only where an owned Vec is needed. Ensure the type imports and visibility remain
unchanged.

Comment thread crates/keyshare/src/threshold_keyshare.rs Outdated
Comment thread crates/keyshare/src/threshold_keyshare.rs
Comment thread crates/multithread/src/multithread.rs
Comment thread crates/zk-prover/src/actors/proof_request.rs

@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: 1

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

568-568: Bare self-hosted label may route jobs to incompatible runners.

build_circuits and zk_prover_e2e both explicitly download barretenberg-amd64-linux.tar.gz, so they require a Linux x86-64 runner. The bare self-hosted label will match any registered self-hosted runner (including macOS or arm64 Linux), which would cause a silent failure when the wrong binary is executed. Add OS and architecture labels to restrict routing:

🛠️ Proposed fix
-    runs-on: self-hosted   # build_circuits (line 568)
+    runs-on: [self-hosted, linux, x64]

-    runs-on: self-hosted   # zk_prover_e2e (line 621)
+    runs-on: [self-hosted, linux, x64]

The same labels should be applied to any other jobs that install Linux-specific tooling (e.g., apt-get, ppa:ethereum/ethereum) — rust_tests, build_enclave_cli, crisp_unit, crisp_e2e, build_e3_support_risc0, test_enclave_init.

Also applies to: 621-621

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml at line 568, Change jobs that currently use the
bare runs-on: self-hosted to use a labeled array restricting OS/arch so they
only run on Linux x86-64 runners; specifically update runs-on for build_circuits
and zk_prover_e2e (and also rust_tests, build_enclave_cli, crisp_unit,
crisp_e2e, build_e3_support_risc0, test_enclave_init) from runs-on: self-hosted
to something like runs-on: [self-hosted, linux, x86_64] (or your repo's
equivalent linux/ubuntu and x86_64 labels) so the jobs that download
barretenberg-amd64-linux.tar.gz and use Linux-specific tooling are scheduled
only on compatible runners.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Line 37: The workflow currently sets runs-on: self-hosted for
pull_request-triggered jobs which exposes the self-hosted runner to arbitrary PR
code; update the CI so PR-triggered jobs (e.g., the jobs named rust_tests,
build_circuits, build_sdk, zk_prover_integration and any other jobs currently
using runs-on: self-hosted) either run on GitHub-hosted runners instead or are
gated by an environment protection rule (add environment:
<protected-environment-name> with required reviewers) so fork PRs cannot execute
on the self-hosted machine; alternatively split self-hosted jobs to only run on
push/merge while keeping pull_request jobs on GitHub-hosted runners or adopt
ephemeral self-hosted runners for isolation.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Line 568: Change jobs that currently use the bare runs-on: self-hosted to use
a labeled array restricting OS/arch so they only run on Linux x86-64 runners;
specifically update runs-on for build_circuits and zk_prover_e2e (and also
rust_tests, build_enclave_cli, crisp_unit, crisp_e2e, build_e3_support_risc0,
test_enclave_init) from runs-on: self-hosted to something like runs-on:
[self-hosted, linux, x86_64] (or your repo's equivalent linux/ubuntu and x86_64
labels) so the jobs that download barretenberg-amd64-linux.tar.gz and use
Linux-specific tooling are scheduled only on compatible runners.

Comment thread .github/workflows/ci.yml Outdated
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.

Integrate T2 circuits into ciphernodes

2 participants