Skip to content

fix: add real encoded tally#1122

Merged
ctrlc03 merged 14 commits into
mainfrom
refactor/new-decoding
Dec 18, 2025
Merged

fix: add real encoded tally#1122
ctrlc03 merged 14 commits into
mainfrom
refactor/new-decoding

Conversation

@cedoor

@cedoor cedoor commented Dec 17, 2025

Copy link
Copy Markdown
Contributor

Fixes #1121

Summary by CodeRabbit

  • New Features

    • Added a utilities component to decode byte/hex-encoded tallies into yes/no counts.
    • Support for much larger vote counts.
  • Chores

    • APIs and storage switched to string-backed tallies and accept byte-encoded inputs.
    • Contracts and server tally processing updated with input-length validation.
    • Example config, CI cache config and default timeouts adjusted.
  • Tests

    • Updated tests to reflect new decoding behavior and larger expected counts.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel

vercel Bot commented Dec 17, 2025

Copy link
Copy Markdown

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

2 Skipped Deployments
Project Deployment Review Updated (UTC)
crisp Skipped Skipped Dec 18, 2025 10:18am
enclave-docs Skipped Skipped Dec 18, 2025 10:18am

@coderabbitai

coderabbitai Bot commented Dec 17, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a new Rust crate crisp-utils to decode byte-encoded tallies; updates TS SDK to accept hex/byte tallies and changes constant typing; refactors Solidity to decode bytes and mock to accept bytes; server models/repo/indexer switch to string-stored counts and BigUint inputs; tests and CI adjusted.

Changes

Cohort / File(s) Summary
Workspace & Cargo
examples/CRISP/Cargo.toml, examples/CRISP/server/Cargo.toml
Registered new crisp-utils crate in the CRISP workspace and enabled it for the server workspace.
New Rust Crate
examples/CRISP/crates/crisp-utils/Cargo.toml, examples/CRISP/crates/crisp-utils/src/lib.rs
New crisp-utils crate exposing VoteCounts and decode_tally(tally_bytes: &[u8]) that decodes little-endian 8-byte chunks into u64 values and computes yes/no tallies; includes unit tests.
TypeScript SDK — constants
examples/CRISP/packages/crisp-sdk/src/constants.ts
MAXIMUM_VOTE_VALUE changed from a BigInt(...) expression to a plain number expression.
TypeScript SDK — vote logic & tests
examples/CRISP/packages/crisp-sdk/src/vote.ts, examples/CRISP/packages/crisp-sdk/tests/vote.test.ts
decodeTally signature changed to accept a hex string; added hexToBytes and decodeBytesToNumbers (little-endian 8-byte chunks); decoding/splitting logic updated; tests adjusted to expect larger tallies and use number-based helpers.
Solidity contract & mocks
examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol, examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockEnclave.sol
Removed HALF_LARGEST_MINIMUM_DEGREE, added InvalidTallyLength() and _decodeBytesToUint64Array(bytes) helper; refactored decodeTally to use byte-decoding and updated weight loops. Mock setPlaintextOutput now accepts raw bytes.
Solidity tests
examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts
Tests updated to set a single hex-encoded tally (bytes) and assert new decoded values (10000000000n / 30000000000n).
Server models
examples/CRISP/server/src/server/models.rs
Tally/vote fields changed from u64 to String; From<E3> for WebResultRequest now uses vote_count for total_votes.
Server repository
examples/CRISP/server/src/server/repo.rs
set_votes now accepts BigUint and persists votes as strings; get_web_result_request uses get_vote_count() for total votes.
Server indexer
examples/CRISP/server/src/server/indexer.rs
Replaced manual decode_bytes_to_vec_u64 usage with crisp_utils::decode_tally; now logs and stores vote_counts.yes / vote_counts.no.
CI workflow
.github/workflows/ci.yml
Tuned Rust cache step for the CRISP e2e job to use explicit cargo-lock-path and rust-target-path pointing at examples/CRISP.
Env & Tests
examples/CRISP/server/.env.example, examples/CRISP/test/crisp.spec.ts
Adjusted E3 timing environment values and reduced test timeouts/waits to match new durations.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as TS Tests / Frontend
    participant SDK as Crisp SDK (TS)
    participant Contract as CRISPProgram (Solidity)
    participant Server as CRISP Server (Rust)
    participant Utils as crisp-utils (Rust crate)
    participant Repo as Server Repo (DB)

    Client->>SDK: submit/verify hex tally
    SDK->>SDK: hexToBytes → decodeBytesToNumbers → split halves → compute yes/no
    Contract->>Server: expose plaintextOutput (bytes)
    Server->>Utils: decode_tally(bytes)
    Utils-->>Server: VoteCounts { yes, no }
    Server->>Repo: set_votes(BigUint yes, BigUint no)
    Repo-->>Server: persist counts as strings
    Server-->>Client: return WebResultRequest (tallies as strings)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Pay extra attention to:
    • Endianness and length validation in Rust decode_tally and Solidity _decodeBytesToUint64Array.
    • Correctness of weight/exponent calculations in Solidity vs Rust/TS implementations.
    • Cross-language numeric conversions: TS number/BigInt ↔ Rust BigUint ↔ Solidity uint64/bytes ↔ persisted strings.
    • Signature changes: MockEnclave.setPlaintextOutput(bytes) and CrispE3Repository::set_votes(BigUint, BigUint) and all call sites.

Possibly related PRs

Suggested labels

crisp

Suggested reviewers

  • ctrlc03
  • 0xjei
  • hmzakhalid

Poem

🐰 I nibble bytes and hop with glee,

Little-endian carrots yield yes and no,
Halves unfold and numbers grow,
Crates and contracts sing in harmony,
Hooray — tallies hop home! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive All changes are directly related to implementing the new tally decoding logic [#1121], though some changes like workflow caching updates and environment variable adjustments appear peripheral to the core objective. Clarify whether CI/CD workflow caching optimizations and environment configuration parameter updates (.env.example, .github/workflows/ci.yml) are intentionally part of this tally decoding refactor or should be separated into independent PRs.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: add real encoded tally' directly describes the main change: implementing proper encoded tally handling with realistic encoding logic instead of placeholder values.
Linked Issues check ✅ Passed The PR implements decoding of tally data on the CRISP server using new encoding logic [#1121], including a new crisp-utils crate with decode_tally function, updates to server indexer, and changes to contracts and SDK to handle the new encoding.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/new-decoding

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8dc9255 and 217cc6e.

📒 Files selected for processing (1)
  • examples/CRISP/test/crisp.spec.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/CRISP/test/crisp.spec.ts

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.

@cedoor cedoor force-pushed the refactor/new-decoding branch from b835472 to fe99f60 Compare December 17, 2025 16:11
@vercel vercel Bot temporarily deployed to Preview – enclave-docs December 17, 2025 16:11 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp December 17, 2025 16:11 Inactive

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

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fef2979 and fe99f60.

⛔ Files ignored due to path filters (1)
  • examples/CRISP/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • examples/CRISP/Cargo.toml (2 hunks)
  • examples/CRISP/crates/crisp-utils/Cargo.toml (1 hunks)
  • examples/CRISP/crates/crisp-utils/src/lib.rs (1 hunks)
  • examples/CRISP/packages/crisp-sdk/src/constants.ts (1 hunks)
  • examples/CRISP/packages/crisp-sdk/src/types.ts (1 hunks)
  • examples/CRISP/packages/crisp-sdk/src/vote.ts (6 hunks)
  • examples/CRISP/packages/crisp-sdk/tests/vote.test.ts (4 hunks)
  • examples/CRISP/server/Cargo.toml (1 hunks)
  • examples/CRISP/server/src/server/indexer.rs (2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-08-25T10:28:56.174Z
Learnt from: ctrlc03
Repo: gnosisguild/enclave PR: 657
File: Cargo.toml:32-34
Timestamp: 2025-08-25T10:28:56.174Z
Learning: The examples/CRISP directory has its own Cargo.toml workspace configuration with members like "server", "wasm-crypto", "program/core", "program/client", etc. The root workspace intentionally excludes "examples/CRISP/server", "examples/CRISP/program", and "examples/CRISP/wasm-crypto" to prevent double workspace membership, which is the correct approach for self-contained example workspaces.

Applied to files:

  • examples/CRISP/server/Cargo.toml
  • examples/CRISP/Cargo.toml
  • examples/CRISP/crates/crisp-utils/Cargo.toml
📚 Learning: 2024-10-22T02:36:01.448Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 145
File: packages/ciphernode/data/Cargo.toml:0-0
Timestamp: 2024-10-22T02:36:01.448Z
Learning: In `packages/ciphernode/data/Cargo.toml`, `actix-rt` is only used in tests and should remain in `[dev-dependencies]`.

Applied to files:

  • examples/CRISP/server/Cargo.toml
  • examples/CRISP/Cargo.toml
  • examples/CRISP/crates/crisp-utils/Cargo.toml
📚 Learning: 2025-11-05T14:12:57.814Z
Learnt from: ctrlc03
Repo: gnosisguild/enclave PR: 963
File: examples/CRISP/client/package.json:25-25
Timestamp: 2025-11-05T14:12:57.814Z
Learning: In the Enclave/CRISP codebase, `enclave-e3/sdk` and `crisp-e3/sdk` are different packages: `enclave-e3/sdk` is the general Enclave SDK, while `crisp-e3/sdk` is the CRISP-specific SDK. The CRISP client (`examples/CRISP/client`) intentionally depends on `enclave-e3/sdk`, not `crisp-e3/sdk`.

Applied to files:

  • examples/CRISP/server/Cargo.toml
  • examples/CRISP/Cargo.toml
  • examples/CRISP/crates/crisp-utils/Cargo.toml
📚 Learning: 2024-09-26T03:11:29.311Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 107
File: packages/ciphernode/sortition/src/distance.rs:1-1
Timestamp: 2024-09-26T03:11:29.311Z
Learning: In `packages/ciphernode/core/src/events.rs`, the import statements use the correct and updated `alloy::primitives` module.

Applied to files:

  • examples/CRISP/Cargo.toml
🧬 Code graph analysis (2)
examples/CRISP/crates/crisp-utils/src/lib.rs (1)
crates/bfv-helpers/src/lib.rs (1)
  • decode_bytes_to_vec_u64 (453-462)
examples/CRISP/packages/crisp-sdk/tests/vote.test.ts (1)
examples/CRISP/packages/crisp-sdk/src/vote.ts (2)
  • decodeTally (83-117)
  • encodeVote (26-47)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: crisp_unit
  • GitHub Check: rust_unit
  • GitHub Check: rust_integration
  • GitHub Check: integration_prebuild
  • GitHub Check: build_enclave_cli
  • GitHub Check: test_net
  • GitHub Check: Build & Push Image
🔇 Additional comments (15)
examples/CRISP/server/Cargo.toml (1)

49-49: LGTM!

The workspace dependency addition follows the established pattern used by crisp-constants and correctly integrates the new crisp-utils crate. Based on learnings, this aligns with the self-contained CRISP workspace configuration.

examples/CRISP/crates/crisp-utils/Cargo.toml (1)

1-13: LGTM!

The new crate configuration is well-structured, follows the workspace inheritance pattern, and declares minimal dependencies appropriate for FHE tally decoding utilities.

examples/CRISP/packages/crisp-sdk/src/constants.ts (1)

24-24: LGTM! The conversion from BigInt to number is safe here.

2^50 - 1 is well within JavaScript's Number.MAX_SAFE_INTEGER (2^53 - 1), so no precision loss occurs. Note that if HALF_LARGEST_MINIMUM_DEGREE were ever increased beyond 52, this would need to revert to BigInt.

examples/CRISP/packages/crisp-sdk/src/vote.ts (5)

31-32: LGTM!

Converting number to BigInt before binary encoding maintains compatibility with the toBinary helper while allowing the Vote type to use number fields.


83-117: Verify decoded values are binary (0 or 1).

The conversion logic at lines 102-111 assumes each element in yesBinary/noBinary is either 0 or 1. If decodeBytesToNumbers can return values other than 0/1 (which seems likely given it reads full 8-byte integers), the weighted sum will produce incorrect results.

Please clarify: Are the decoded byte chunks guaranteed to represent single-bit values (0 or 1), or can they be arbitrary integers? If the latter, the binary-to-decimal conversion logic would need adjustment.


136-136: LGTM!

Consistent with the Vote type change to use number fields.


187-189: LGTM!

The negative value check correctly uses number comparison, consistent with the Vote type change.


211-211: LGTM!

Consistent with the Vote type change to use number fields.

examples/CRISP/Cargo.toml (2)

9-10: LGTM!

The new crisp-utils crate is correctly added to the workspace members. Based on learnings, this follows the established pattern for the self-contained CRISP workspace.


24-24: LGTM!

The workspace dependency declaration follows the same pattern as crisp-constants and correctly references the local crate path.

examples/CRISP/server/src/server/indexer.rs (2)

18-18: LGTM: Import statement is correct.

The import of decode_tally from the new crisp_utils crate is properly declared.


293-301: LGTM: Clean integration of tally decoding utility.

The replacement of manual decoding logic with the dedicated decode_tally function improves code clarity and maintainability. Error handling via the ? operator correctly propagates decoding failures, and the decoded values are properly extracted and logged before being persisted.

examples/CRISP/crates/crisp-utils/src/lib.rs (1)

60-71: LGTM: Test validates real FHE output decoding.

The test uses actual FHE computation output and correctly validates the expected decoded values (10 billion yes, 30 billion no). This provides good coverage for the decoding logic with realistic data.

examples/CRISP/packages/crisp-sdk/tests/vote.test.ts (1)

37-37: LGTM: Test updates align with type changes.

The migration from bigint to number literals is consistent throughout the test suite. The decodeHalf helper function correctly returns number and uses parseInt for base-2 conversion, aligning with the updated Vote type definition.

Also applies to: 55-56, 61-79

examples/CRISP/packages/crisp-sdk/src/types.ts (1)

74-78: No action needed. The number type is appropriate for the Vote fields.

The system architecture explicitly constrains vote values to MAXIMUM_VOTE_VALUE = 2^50 - 1 (approximately 1.1 quadrillion), which is well below Number.MAX_SAFE_INTEGER of 2^53 - 1 (approximately 9 quadrillion). Vote values are validated against this maximum before use, preventing any precision loss. The number type is suitable for this domain.

Likely an incorrect or invalid review comment.

Comment thread examples/CRISP/crates/crisp-utils/src/lib.rs
Comment thread examples/CRISP/packages/crisp-sdk/src/vote.ts
Comment thread examples/CRISP/packages/crisp-sdk/tests/vote.test.ts Outdated
@vercel vercel Bot temporarily deployed to Preview – enclave-docs December 17, 2025 16:39 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp December 17, 2025 16:39 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs December 17, 2025 16:40 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp December 17, 2025 16:40 Inactive

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

♻️ Duplicate comments (1)
examples/CRISP/packages/crisp-sdk/src/vote.ts (1)

54-76: Critical: Bitwise operations truncate values beyond 32 bits.

JavaScript bitwise operators (<<, |=) operate on 32-bit signed integers. When j >= 4, the shift amount exceeds 31 bits, causing the upper 4 bytes to be lost. This means 64-bit little-endian values cannot be correctly reconstructed.

Apply this diff to fix the truncation using BigInt for reconstruction:

 const decodeBytesToNumbers = (data: Uint8Array): number[] => {
   if (data.length % 8 !== 0) {
     throw new Error('Data length must be multiple of 8')
   }

   const arrayLength = data.length / 8
   const result: number[] = []

   for (let i = 0; i < arrayLength; i++) {
     const offset = i * 8
-    let value = 0
+    let value = 0n

     // Read 8 bytes in little-endian order
     for (let j = 0; j < 8; j++) {
       const byteValue = data[offset + j]
-      value |= byteValue << (j * 8)
+      value |= BigInt(byteValue) << BigInt(j * 8)
     }

-    result.push(value)
+    result.push(Number(value))
   }

   return result
 }

Note: If values can exceed Number.MAX_SAFE_INTEGER (2^53 - 1), the function should return bigint[] instead to avoid precision loss.

🧹 Nitpick comments (2)
examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol (1)

242-244: Consider using uint256 for the loop counter.

While functionally correct, using uint64 for the loop counter j is unconventional in Solidity. The standard practice is to use uint256 for loop counters, which is also the default type for integer literals.

Apply this diff:

       // Read 8 bytes in little-endian order
-      for (uint64 j = 0; j < 8; j++) {
+      for (uint256 j = 0; j < 8; j++) {
         value |= uint64(uint8(data[offset + j])) << (j * 8);
       }
examples/CRISP/crates/crisp-utils/src/lib.rs (1)

31-54: Consider adding basic validation for empty input.

While the BigUint approach correctly prevents overflow, the function doesn't handle the edge case of empty input. If tally_bytes is empty, decode_bytes_to_vec_u64 would return an empty vector, leading to half_d = 0 and empty slices, which would produce yes = 0 and no = 0. This might be acceptable behavior, but consider documenting it or adding explicit validation.

Consider adding a comment or validation:

 pub fn decode_tally(tally_bytes: &[u8]) -> Result<VoteCounts> {
     // Decode bytes to numbers array (little-endian, 8 bytes per value)
     let decoded = decode_bytes_to_vec_u64(tally_bytes)?;
+    
+    // Empty input results in zero votes for both options
+    if decoded.is_empty() {
+        return Ok(VoteCounts { yes: BigUint::from(0u64), no: BigUint::from(0u64) });
+    }
 
     // Votes are right-aligned with leading zeros, so we can use the entire halves
     let half_d = decoded.len() / 2;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe99f60 and 722d710.

⛔ Files ignored due to path filters (1)
  • examples/CRISP/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • examples/CRISP/crates/crisp-utils/Cargo.toml (1 hunks)
  • examples/CRISP/crates/crisp-utils/src/lib.rs (1 hunks)
  • examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol (2 hunks)
  • examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockEnclave.sol (1 hunks)
  • examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts (3 hunks)
  • examples/CRISP/packages/crisp-sdk/src/vote.ts (3 hunks)
  • examples/CRISP/packages/crisp-sdk/tests/vote.test.ts (1 hunks)
  • examples/CRISP/server/src/server/indexer.rs (2 hunks)
  • examples/CRISP/server/src/server/models.rs (3 hunks)
  • examples/CRISP/server/src/server/repo.rs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/CRISP/crates/crisp-utils/Cargo.toml
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: cedoor
Repo: gnosisguild/enclave PR: 1122
File: examples/CRISP/crates/crisp-utils/src/lib.rs:30-54
Timestamp: 2025-12-17T16:25:05.292Z
Learning: In the CRISP project's `decode_tally` function (examples/CRISP/crates/crisp-utils/src/lib.rs), the decoded u64 values from `decode_bytes_to_vec_u64` are not necessarily binary (0 or 1). They can be any u64 value and are used as coefficients in a weighted sum with powers of 2.
📚 Learning: 2025-12-17T16:25:05.292Z
Learnt from: cedoor
Repo: gnosisguild/enclave PR: 1122
File: examples/CRISP/crates/crisp-utils/src/lib.rs:30-54
Timestamp: 2025-12-17T16:25:05.292Z
Learning: In the CRISP project's `decode_tally` function (examples/CRISP/crates/crisp-utils/src/lib.rs), the decoded u64 values from `decode_bytes_to_vec_u64` are not necessarily binary (0 or 1). They can be any u64 value and are used as coefficients in a weighted sum with powers of 2.

Applied to files:

  • examples/CRISP/server/src/server/indexer.rs
  • examples/CRISP/server/src/server/repo.rs
  • examples/CRISP/packages/crisp-sdk/src/vote.ts
  • examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol
  • examples/CRISP/packages/crisp-sdk/tests/vote.test.ts
📚 Learning: 2025-08-28T08:58:17.434Z
Learnt from: ctrlc03
Repo: gnosisguild/enclave PR: 665
File: packages/enclave-sdk/src/contract-client.ts:110-118
Timestamp: 2025-08-28T08:58:17.434Z
Learning: viem's TypeScript ABI inference maps uint32[2] to [number, number], not [bigint, bigint]. The library uses number type for uint32 values to align with traditional database patterns and avoid unnecessary BigInt conversion.

Applied to files:

  • examples/CRISP/packages/crisp-sdk/src/vote.ts
📚 Learning: 2025-12-17T16:25:05.292Z
Learnt from: cedoor
Repo: gnosisguild/enclave PR: 1122
File: examples/CRISP/crates/crisp-utils/src/lib.rs:30-54
Timestamp: 2025-12-17T16:25:05.292Z
Learning: In examples/CRISP/crates/crisp-utils/src/lib.rs, when reviewing decode_tally and decode_bytes_to_vec_u64, do not assume decoded values are binary; treat each value as a coefficient (any u64) in a sum using powers of 2. Verify that arithmetic uses a weighted sum with 2^i, and that non-boolean values are handled correctly. Update tests to cover arbitrary coefficients and edge cases.

Applied to files:

  • examples/CRISP/crates/crisp-utils/src/lib.rs
📚 Learning: 2025-11-12T10:08:30.720Z
Learnt from: ctrlc03
Repo: gnosisguild/enclave PR: 996
File: examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol:144-169
Timestamp: 2025-11-12T10:08:30.720Z
Learning: In the CRISP program (examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol), there are plans to move the merkle tree from the Enclave contract to the program contract itself in the future, which would allow direct calls to validateInput to be stored in the merkle tree.

Applied to files:

  • examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol
🧬 Code graph analysis (3)
examples/CRISP/crates/crisp-utils/src/lib.rs (1)
crates/bfv-helpers/src/lib.rs (1)
  • decode_bytes_to_vec_u64 (453-462)
examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts (2)
examples/CRISP/packages/crisp-contracts/tests/utils.ts (2)
  • deployMockEnclave (43-47)
  • deployCRISPProgram (69-87)
examples/CRISP/client/libs/crispWorker.js (1)
  • vote (19-19)
examples/CRISP/packages/crisp-sdk/tests/vote.test.ts (1)
examples/CRISP/packages/crisp-sdk/src/vote.ts (2)
  • decodeTally (83-117)
  • encodeVote (26-47)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build_e3_support_dev
  • GitHub Check: build_crisp_sdk
  • GitHub Check: build_sdk
  • GitHub Check: build_enclave_cli
  • GitHub Check: rust_integration
  • GitHub Check: rust_unit
  • GitHub Check: integration_prebuild
  • GitHub Check: test_contracts
🔇 Additional comments (16)
examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol (2)

166-169: LGTM!

The yes votes calculation correctly applies descending powers of 2 as weights (2^(halfD-1) down to 2^0), which aligns with the weighted coefficient scheme. Based on learnings, the decoded u64 values can be any value and are properly used as coefficients here.


172-174: LGTM!

The no votes calculation correctly mirrors the yes votes logic with descending powers of 2 (2^(halfD-1) down to 2^0), ensuring consistent decoding of both vote tallies.

examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockEnclave.sol (1)

15-17: LGTM!

The simplified signature directly stores the bytes representation, aligning with the new tally decoding approach introduced across the PR.

examples/CRISP/server/src/server/indexer.rs (2)

18-18: LGTM!

The import correctly integrates the new tally decoding utility from the crisp-utils crate.


293-305: LGTM!

The decoding logic is cleanly refactored to use the centralized decode_tally utility. Error handling is preserved, and the vote counts are correctly passed to the repository layer.

examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts (2)

25-38: LGTM!

The test correctly validates the new single-tally decoding path with appropriate large integer expectations (10 billion and 30 billion).


49-49: Fix vote inputs to use bigint notation to match the Vote type definition.

The test uses plain numbers { yes: 10, no: 0 } at lines 49 and 78, but the Vote type explicitly requires bigint values. Update to { yes: 10n, no: 0n } to align with the type signature and existing documentation.

examples/CRISP/server/src/server/repo.rs (1)

169-183: LGTM!

The type change from u64 to BigUint with string storage correctly supports arbitrary-precision vote counts that can exceed 64-bit integer limits. The conversion to string via to_string() is appropriate for persistence.

examples/CRISP/packages/crisp-sdk/tests/vote.test.ts (2)

48-56: LGTM!

The test correctly validates the new tally decoding with expected values of 10 billion and 30 billion, matching the crisp-utils implementation.


60-79: LGTM!

The test helper decodeHalf correctly uses parseInt with base 2 for internal test validation. The return type change to number is appropriate for the test assertions.

examples/CRISP/crates/crisp-utils/src/lib.rs (1)

62-71: LGTM!

The test correctly validates the decoding logic with realistic FHE output data and expected large integer counts.

examples/CRISP/packages/crisp-sdk/src/vote.ts (2)

83-117: LGTM!

The decodeTally function correctly converts hex strings to bytes and processes them in two halves using BigInt arithmetic for the weighted sum. The logic aligns with the crisp-utils Rust implementation.


183-186: LGTM!

The vote validation correctly converts MAXIMUM_VOTE_VALUE to BigInt for comparison, ensuring type consistency.

examples/CRISP/server/src/server/models.rs (3)

129-136: LGTM!

The type change from u64 to String for tally fields correctly supports arbitrary-precision vote counts. The removal of total_votes aligns with the refactored data model.


160-192: LGTM!

The model fields consistently use String for vote tallies across E3 and E3Crisp structs, maintaining type consistency throughout the codebase.


208-219: LGTM!

The conversion implementation correctly maps the updated String-based tally fields without the removed total_votes field.

Comment thread examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol
Comment thread examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol Outdated
Comment thread examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol Outdated
@vercel vercel Bot temporarily deployed to Preview – crisp December 17, 2025 17:02 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs December 17, 2025 17:02 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp December 17, 2025 17:17 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs December 17, 2025 17:17 Inactive
ctrlc03
ctrlc03 previously approved these changes Dec 17, 2025

@ctrlc03 ctrlc03 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

tACK

@ctrlc03 ctrlc03 enabled auto-merge (squash) December 17, 2025 17:19
@vercel vercel Bot temporarily deployed to Preview – enclave-docs December 17, 2025 17:45 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp December 17, 2025 17:45 Inactive

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

Caution

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

⚠️ Outside diff range comments (1)
examples/CRISP/server/src/server/repo.rs (1)

169-183: Fix the misleading error message.

The error message on line 181 incorrectly references "ciphertext_input" when this method is setting votes.

             })
         })
         .await
-        .map_err(|_| eyre::eyre!("Could not append ciphertext_input for '{key}'"))?;
+        .map_err(|_| eyre::eyre!("Could not set votes for '{key}'"))?;
     Ok(())
 }
🧹 Nitpick comments (1)
examples/CRISP/server/src/server/repo.rs (1)

195-207: Minor inefficiency: redundant get_crisp() call.

get_crisp() is called on line 197, and then get_vote_count() on line 205 internally calls get_crisp() again. Consider reusing the already-fetched e3_crisp to avoid the duplicate store access.

     pub async fn get_web_result_request(&self) -> Result<WebResultRequest> {
         let e3 = self.get_e3().await?;
         let e3_crisp = self.get_crisp().await?;
         Ok(WebResultRequest {
             round_id: e3.id,
             option_1_tally: e3_crisp.votes_option_1,
             option_2_tally: e3_crisp.votes_option_2,
             option_1_emoji: e3_crisp.emojis[0].clone(),
             option_2_emoji: e3_crisp.emojis[1].clone(),
             end_time: e3.expiration,
-            total_votes: self.get_vote_count().await?,
+            total_votes: u64::try_from(e3_crisp.has_voted.len())?,
         })
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b30e9e6 and fdb9430.

📒 Files selected for processing (2)
  • examples/CRISP/server/src/server/models.rs (4 hunks)
  • examples/CRISP/server/src/server/repo.rs (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cedoor
Repo: gnosisguild/enclave PR: 1122
File: examples/CRISP/crates/crisp-utils/src/lib.rs:30-54
Timestamp: 2025-12-17T16:25:05.292Z
Learning: In the CRISP project's `decode_tally` function (examples/CRISP/crates/crisp-utils/src/lib.rs), the decoded u64 values from `decode_bytes_to_vec_u64` are not necessarily binary (0 or 1). They can be any u64 value and are used as coefficients in a weighted sum with powers of 2.
📚 Learning: 2025-12-17T16:25:05.292Z
Learnt from: cedoor
Repo: gnosisguild/enclave PR: 1122
File: examples/CRISP/crates/crisp-utils/src/lib.rs:30-54
Timestamp: 2025-12-17T16:25:05.292Z
Learning: In the CRISP project's `decode_tally` function (examples/CRISP/crates/crisp-utils/src/lib.rs), the decoded u64 values from `decode_bytes_to_vec_u64` are not necessarily binary (0 or 1). They can be any u64 value and are used as coefficients in a weighted sum with powers of 2.

Applied to files:

  • examples/CRISP/server/src/server/repo.rs
🧬 Code graph analysis (1)
examples/CRISP/server/src/server/models.rs (1)
examples/CRISP/server/src/server/indexer.rs (1)
  • e3 (332-332)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build_crisp_sdk
  • GitHub Check: rust_integration
  • GitHub Check: integration_prebuild
  • GitHub Check: build_sdk
  • GitHub Check: rust_unit
  • GitHub Check: build_enclave_cli
  • GitHub Check: test_net
  • GitHub Check: Build & Push Image
🔇 Additional comments (6)
examples/CRISP/server/src/server/repo.rs (2)

14-14: LGTM!

The BigUint import is correctly added to support large integer vote counts that may exceed standard integer limits.


129-130: LGTM!

The initialization now uses string literals to align with the updated E3Crisp model where votes_option_1 and votes_option_2 are String types.

examples/CRISP/server/src/server/models.rs (4)

128-137: LGTM!

The WebResultRequest struct correctly uses String for tally fields to accommodate large integers serialized from BigUint, while keeping total_votes as u64 for the actual voter count.


171-172: LGTM!

Type change to String for vote tallies is consistent with the broader refactoring to support BigUint-derived values.


201-202: LGTM!

E3Crisp model correctly updated to use String for vote fields, matching the storage format in repo.rs.


209-220: LGTM!

The From<E3> implementation correctly maps the String tally fields and sources total_votes from vote_count instead of summing the tally values (which are now strings representing potentially large integers).

@ctrlc03 ctrlc03 self-requested a review December 17, 2025 18:00
ctrlc03
ctrlc03 previously approved these changes Dec 17, 2025
@cedoor cedoor changed the title test: add real encoded tally fix: add real encoded tally Dec 18, 2025
@vercel vercel Bot temporarily deployed to Preview – crisp December 18, 2025 09:58 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs December 18, 2025 09:58 Inactive

@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)
examples/CRISP/server/.env.example (1)

22-26: Reorder configuration keys alphabetically.

The configuration changes look good semantically (E3_WINDOW_SIZE for activation window, E3_DURATION reduced from 160 to 60 for input submission interval). However, static analysis flags that E3_DURATION should be ordered before E3_WINDOW_SIZE to maintain alphabetical consistency with other environment variables.

🔎 Apply this diff to reorder the keys:
-# Defines the time window during which an e3 can be activated
-E3_WINDOW_SIZE=40
 # Defines the time interval during which users can submit their inputs
 # After this interval, the computation phase starts automatically
 E3_DURATION=60
+# Defines the time window during which an e3 can be activated
+E3_WINDOW_SIZE=40
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6073bf6 and 8dc9255.

📒 Files selected for processing (2)
  • examples/CRISP/server/.env.example (1 hunks)
  • examples/CRISP/test/crisp.spec.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: cedoor
Repo: gnosisguild/enclave PR: 1122
File: examples/CRISP/crates/crisp-utils/src/lib.rs:30-54
Timestamp: 2025-12-17T16:25:05.292Z
Learning: In the CRISP project's `decode_tally` function (examples/CRISP/crates/crisp-utils/src/lib.rs), the decoded u64 values from `decode_bytes_to_vec_u64` are not necessarily binary (0 or 1). They can be any u64 value and are used as coefficients in a weighted sum with powers of 2.
🪛 dotenv-linter (4.0.0)
examples/CRISP/server/.env.example

[warning] 26-26: [UnorderedKey] The E3_DURATION key should go before the E3_WINDOW_SIZE key

(UnorderedKey)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build_crisp_sdk
  • GitHub Check: build_sdk
  • GitHub Check: rust_unit
  • GitHub Check: integration_prebuild
  • GitHub Check: test_net
  • GitHub Check: build_enclave_cli
  • GitHub Check: rust_integration
  • GitHub Check: Build & Push Image
🔇 Additional comments (1)
examples/CRISP/test/crisp.spec.ts (1)

47-47: Document the rationale for timeout reductions and verify CI stability.

The E3 activation timeout was intentionally reduced from 300000ms to 60000ms (5x reduction), and the post-signature wait from 310000ms to 100000ms (~3x reduction). While these changes indicate performance improvements, they should be verified to ensure test reliability across CI environments, which may have different performance characteristics than local development.

Consider:

  • Running this test multiple times in CI to confirm consistency
  • Adding a comment explaining why these specific timeout values are sufficient
  • Documenting any performance improvements that justified these reductions

Comment thread examples/CRISP/test/crisp.spec.ts Outdated
@vercel vercel Bot temporarily deployed to Preview – enclave-docs December 18, 2025 10:18 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp December 18, 2025 10:18 Inactive
@ctrlc03 ctrlc03 self-requested a review December 18, 2025 10:42
@ctrlc03 ctrlc03 merged commit 122fead into main Dec 18, 2025
27 checks passed
@ctrlc03 ctrlc03 deleted the refactor/new-decoding branch December 18, 2025 10:42
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.

Decode tally on CRISP server with new encoding logic

2 participants