Skip to content

feat: e3 refund timeout mechanism [skip-line-limit]#1161

Merged
hmzakhalid merged 39 commits into
mainfrom
feature/e3-refund-timeout-mechanism
Feb 10, 2026
Merged

feat: e3 refund timeout mechanism [skip-line-limit]#1161
hmzakhalid merged 39 commits into
mainfrom
feature/e3-refund-timeout-mechanism

Conversation

@hmzakhalid

@hmzakhalid hmzakhalid commented Jan 13, 2026

Copy link
Copy Markdown
Collaborator

When an E3 computation fails - whether the committee never forms, DKG times out, or decryption
fails - there was no way to detect it, refund the requester, or compensate honest nodes for work
they already did. This PR adds that whole infrastructure.

The core idea comes from BlockScience's research, when deciding refunds. If a requester paid for an E3 but the committee failed to decrypt, they shouldn't lose everything - they should get most of it back, minus what honest nodes earned for the
work they completed.

New State Flow

A state machine that tracks every E3 through its stages:

None → Requested → CommitteeFinalized → KeyPublished → CiphertextReady → Complete
                        ↓                    ↓               ↓
                     Failed               Failed          Failed

Each stage has a deadline. When a deadline passes without progressing, anyone can call
markE3Failed() to transition the E3 to the Failed state.

New E3RefundManager.sol Contract

Handles the money side. When an E3 fails, this contract:

  1. Calculates how much work was completed (based on stage)
  2. Splits the original payment between requester, honest nodes, and protocol
  3. Lets each party claim their share

The work value percentages come from the WorkValueAllocation configuration:

Stage Failed At Work Done Requester Gets Honest Nodes Protocol
Requested 0% 95% 0% 5%
CommitteeFinalized 10% 85% 10% 5%
KeyPublished 40% 55% 40% 5%
CiphertextReady 40% 55% 40% 5%

The allocation breakdown:

  • Committee Formation: 10% (sortition work)
  • DKG: 30% (key generation work)
  • Protocol Fee: 5% (always allocated to treasury)
  • Remaining: Goes to requester as refund

Note: the percentages are tentative and subject to change with further discussion

Sequence Diagram

sequenceDiagram
    autonumber
    participant R as Requester
    participant Enc as Enclave
    participant Reg as CiphernodeRegistry

    Note over R, Reg: Happy Path: E3 Completes Successfully

    R->>Enc: request(params, payment)
    Enc->>Enc: e3Payments[e3Id] = payment
    Enc->>Enc: _e3Stages[e3Id] = Requested
    Enc->>Enc: _e3Requesters[e3Id] = msg.sender
    Enc->>Enc: computeDeadline = inputWindow[1] + computeWindow
    Enc->>Reg: requestCommittee(e3Id, seed, threshold)
    Enc-->>R: E3Requested, E3StageChanged(None → Requested)

    Note over Reg: Committee forms via sortition...
    
    Reg->>Enc: onCommitteeFinalized(e3Id)
    Enc->>Enc: stage = CommitteeFinalized
    Enc->>Enc: dkgDeadline = now + dkgWindow
    Enc-->>Reg: CommitteeFinalized, E3StageChanged(Requested → CommitteeFinalized)

    Note over Reg: DKG completes, key published...

    Reg->>Enc: onCommitteePublished(e3Id)
    Enc->>Enc: stage = KeyPublished
    Enc-->>Reg: CommitteeFormed, E3StageChanged(CommitteeFinalized → KeyPublished)

    Note over Enc: Inputs submitted during inputWindow, computation runs...

    Enc->>Enc: publishCiphertextOutput(e3Id, output, proof)
    Enc->>Enc: stage = CiphertextReady
    Enc->>Enc: decryptionDeadline = now + decryptionWindow
    Enc-->>R: CiphertextOutputPublished, E3StageChanged(KeyPublished → CiphertextReady)

    Enc->>Enc: publishPlaintextOutput(e3Id, plaintext, proof)
    Enc->>Enc: stage = Complete ✓
    Enc->>Enc: _distributeRewards(e3Id)
    Enc-->>R: PlaintextOutputPublished, E3StageChanged(CiphertextReady → Complete)
Loading
sequenceDiagram
    autonumber
    participant Anyone
    participant R as Requester
    participant Enc as Enclave
    participant Ref as E3RefundManager

    Note over Anyone, Ref: Failure Path: Timeout Detected

    rect rgb(255, 230, 230)
        Note over Enc: Deadline has passed without progress

        Anyone->>Enc: markE3Failed(e3Id)
        Enc->>Enc: Check: block.timestamp > deadline?
        Enc->>Enc: _e3Stages[e3Id] = Failed
        Enc->>Enc: _e3FailureReasons[e3Id] = reason
        Enc-->>Anyone: E3Failed(e3Id, stage, reason)
    end

    rect rgb(255, 245, 200)
        Note over Enc: Process the failure

        Anyone->>Enc: processE3Failure(e3Id)
        Enc->>Enc: require(_e3Stages[e3Id] == Failed)
        Enc->>Enc: payment = e3Payments[e3Id]
        Enc->>Enc: e3Payments[e3Id] = 0
        Enc->>Enc: honestNodes = _getHonestNodes(e3Id)
        Enc->>Ref: transfer(feeToken, payment)
        Enc->>Ref: calculateRefund(e3Id, payment, honestNodes)
        Ref->>Ref: workDone = calculateWorkValue(failedStage)
        Ref->>Ref: requesterAmt = payment × (100% - workDone - 5%)
        Ref->>Ref: nodeAmt = payment × workDone
        Ref->>Ref: protocolAmt = payment × 5%
        Ref->>Ref: transfer(treasury, protocolAmt)
        Ref-->>Enc: RefundDistributionCalculated
    end

    rect rgb(230, 255, 230)
        Note over R: Claim refunds

        R->>Ref: claimRequesterRefund(e3Id)
        Ref->>Ref: Verify caller == requester
        Ref->>R: transfer(requesterAmt)
        Ref-->>R: RefundClaimed ✓
    end
Loading
flowchart TD
    subgraph Stages
        A[None] --> B[Requested]
        B --> C[CommitteeFinalized]
        C --> D[KeyPublished]
        D --> E[CiphertextReady]
        E --> F[Complete ✓]
    end

    subgraph Failures
        B -.->|committeeDeadline passed| X[Failed]
        C -.->|dkgDeadline passed| X
        D -.->|computeDeadline passed| X
        E -.->|decryptionDeadline passed| X
    end

    style F fill:#90EE90
    style X fill:#FFB6C1
Loading

Summary by CodeRabbit

  • New Features

    • End-to-end failure & refund flows with a new on-chain refund manager and user-facing refund claimability.
    • New lifecycle events and statuses (stage change notifications, failure reasons) and explicit on-chain stage/deadline getters.
    • Program-level publishInput API with enforced input deadlines.
  • Improvements

    • Consolidated timing: requests now use a single inputWindow (start/end) instead of start+duration/expiration.
    • CLI/SDK/task names and checks updated to “ready”/publishInput semantics; committee deadline naming aligned across tooling.

@vercel

vercel Bot commented Jan 13, 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 10, 2026 6:18pm
enclave-docs Ready Ready Preview, Comment Feb 10, 2026 6:18pm

Request Review

@coderabbitai

coderabbitai Bot commented Jan 13, 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

Renames submission_deadline → committee_deadline across events and readers, replaces startWindow/duration/expiration with inputWindow in E3 APIs, adds E3 lifecycle/enums/events and E3RefundManager with refund/claim logic, and updates many clients, indexer, contracts, tests, and templates to use the new lifecycle, deadlines, and input publication flow.

Changes

Cohort / File(s) Summary
Aggregator / Deadline logic
crates/aggregator/src/committee_finalizer.rs
Switched deadline field use from submission_deadline → committee_deadline and adjusted scheduling/logging to use committee_deadline and buffer fallback.
Events crate (new E3 events & renames)
crates/events/src/enclave_event/... (committee_requested.rs, e3_failed.rs, e3_stage_changed.rs, mod.rs)
Renamed CommitteeRequested field to committee_deadline; added E3Failed and E3StageChanged message types and wired them into event enum/mappings.
EVM helpers / contract ABI & RPC surface
crates/evm-helpers/src/contracts.rs, .../events.rs, crates/evm/src/* (ciphernode_registry_sol.rs, enclave_sol_reader.rs)
Replaced startWindow/duration/expiration with inputWindow; added E3Stage/FailureReason/TimeoutConfig/Deadlines; removed activate/publish_input write RPCs; added getters for stage, failure, requester, deadlines, timeout config; updated event parsing and CommitteeRequested mapping to use committeeDeadline.
Indexer / storage flow
crates/indexer/src/indexer.rs, crates/indexer/src/models.rs, crates/indexer/tests/fixtures/fake_enclave.sol, crates/indexer/tests/integration.rs
Removed E3Activated handling; on CommitteePublished now fetches E3 and creates/stores E3 immediately using committee public key; switched model to input_window-based fields and removed activation step.
CRISP examples & client changes
examples/CRISP/** (client, server, crates/evm_helpers, packages/crisp-contracts/**, enclave.config.yaml)
Reworked CRISP program API: replace validateInput → publishInput, change contract and client calls to use inputWindow, update tests, config addresses, and add CRISPProgram.publishInput helper.
Enclave contracts & new refund manager
packages/enclave-contracts/** (contracts, interfaces, artifacts, tasks, scripts, ignition, tests)
Large ABI and contract changes: added E3Stage/FailureReason/TimeoutConfig/Deadlines types and events, added IE3RefundManager interface and E3RefundManager contract, extended Enclave with stage tracking, deadlines, failure processing and refund wiring; updated registry/registry tests and many deployment/task scripts.
Ciphernode registry / committee lifecycle
packages/enclave-contracts/contracts/registry/*, packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol
Renamed submissionDeadline → committeeDeadline across structs/events, added committee failure handling/event CommitteeFormationFailed, typed enclave/bonding registry params, finalizeCommittee now returns bool, exposed getCommitteeDeadline/isOpen.
Sortition / Keyshare / Active job cleanup
crates/sortition/src/sortition.rs, crates/keyshare/src/threshold_keyshare.rs
Subscribe to new E3 events; added decrement_jobs_for_e3 helper and handlers for E3Failed/E3StageChanged to decrement active jobs and signal shutdown on terminal stages.
SDK/client & utils
packages/enclave-sdk/**, packages/enclave-sdk/src/utils.ts, packages/enclave-sdk/src/types.ts, packages/enclave-sdk/src/index.ts, packages/enclave-sdk/src/...
Renamed requestE3 params to use inputWindow, removed activateE3/publishInput APIs, added calculateInputWindow and getCurrentTimestamp(publicClient) async variant, updated types to reflect inputWindow and renamed event fields.
Templates, server and tests
templates/default/**, tests/integration/**, tests/integration/lib/utils.sh, examples/CRISP/test/**
Updated templates and test harnesses to use CommitteePublished/CommitteePublishedData and inputWindow fields, added wallet-based publishInput helpers, added get_evm_timestamp utility and adjusted integration scripts and CI test flows.
Misc UI/client small changes
examples/CRISP/client/src/** (VoteManagement.context.tsx, vote.model.ts, DailyPoll.tsx, RoundPoll.tsx, methods.ts)
Replaced duration/expiration usage with single end_time/end_time field and updated components to derive end time from that field.

Sequence Diagram(s)

sequenceDiagram
    participant Registry as CiphernodeRegistry
    participant Enclave as Enclave Contract
    participant Indexer as Indexer
    participant DB as Repository/DB
    participant Refund as E3RefundManager
    participant Client as CRISP/SDK

    Registry-->>Enclave: emit CommitteePublished(e3Id, nodes, publicKey) (committeeDeadline)
    Indexer->>Registry: listen CommitteePublished
    Indexer->>Enclave: getE3(e3Id) (read inputWindow, params)
    Enclave-->>Indexer: return E3{ inputWindow, requester, params, ... }
    Indexer->>DB: create E3 record (use inputWindow[1] as end_time)
    Indexer-->>Client: notify/emit session scheduling (schedule processing)
    Note over Enclave,Refund: (on failure)
    Enclave->>Refund: processE3Failure(e3Id) / markE3Failed(e3Id, reason)
    Refund-->>DB: store RefundDistribution
    DB-->>Client: allow claims (claimRequesterRefund / claimHonestNodeReward)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

ciphernode, crisp, sdk

Suggested reviewers

  • hmzakhalid

Poem

"A rabbit hops in code so deep,
Deadlines renamed, new types to keep.
Input windows now snug and tight,
Refunds sorted by moonlit night.
Hooray — I bounced the tests to green! 🐇"

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: introducing an E3 refund and timeout mechanism for E3 computations.

✏️ 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 feature/e3-refund-timeout-mechanism

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

Nice work, left some suggestions

Comment thread packages/enclave-contracts/contracts/E3RefundManager.sol Outdated
Comment thread packages/enclave-contracts/contracts/E3RefundManager.sol Outdated
Comment thread packages/enclave-contracts/contracts/E3RefundManager.sol Outdated
Comment thread packages/enclave-contracts/contracts/E3RefundManager.sol Outdated
Comment thread packages/enclave-contracts/contracts/E3RefundManager.sol Outdated
Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
Comment thread packages/enclave-contracts/contracts/E3RefundManager.sol Outdated
@vercel vercel Bot temporarily deployed to Preview – crisp January 20, 2026 15:10 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs January 20, 2026 15:10 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs January 20, 2026 15:24 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp January 20, 2026 15:24 Inactive
Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciphernode Related to the ciphernode package contracts Related to the enclave-contracts package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

E3 Refund and Timeout Detection Mechanism Remove activate step Consider using block.timestamp instead of local time in calculateStartWindow

3 participants