Enclave is now The Interfold.
Learn
E3 Lifecycle

E3 Lifecycle

The Interfold contract (Interfold.sol) moves each E3 through a fixed set of stages. This page gives the call that enters each stage, who makes the call, the events, and what happens next. It also covers the deadlines, the failure reasons, and refunds. It is for developers who request or watch E3s and for operators who serve on committees.

The stages are the values of E3Stage in interfaces/IInterfold.sol: None, Requested, CommitteeFinalized, KeyPublished, CiphertextReady, Complete, and Failed. None means that the E3 does not exist. Every stage change emits E3StageChanged(e3Id, previousStage, newStage).

E3 lifecycle
RequesterE3Requested

Requested

The requester calls Interfold.request(params) and pays the fee. The contract stores the E3 and asks the CiphernodeRegistry for a committee. Next, ciphernodes submit tickets after the VRF result arrives.

Stage reference

StageCall that enters itUsual callerInterfold events
RequestedInterfold.requestRequesterE3Requested, E3StageChanged
CommitteeFinalizedCiphernodeRegistry.finalizeCommittee, then onCommitteeFinalizedA committee candidateCommitteeFinalized, E3StageChanged
KeyPublishedCiphernodeRegistry.publishCommittee, then onCommitteePublishedActive aggregatorCommitteeFormed, E3StageChanged
CiphertextReadyInterfold.publishCiphertextOutputApplication serverCiphertextOutputReferencePublished, E3StageChanged
CompleteInterfold.publishPlaintextOutputActive aggregatorPlaintextOutputPublished, E3StageChanged
FailedmarkE3Failed, cancelE3, or a call from the registry or slashing managerAnyone, after a deadlineE3Failed, E3StageChanged

finalizeCommittee, publishCommittee, publishCiphertextOutput, and publishPlaintextOutput have no caller restriction. Stage checks, deadline checks, and proof verification protect them. The "Usual caller" column shows who sends the transaction in the current software.

Requested

The requester calls request(E3RequestParams). The parameters are committeeSize, inputWindow, e3Program, paramSet, computeProviderParams, customParams, expectedFeeToken, expectedCryptoConfigId, and maxFee. The contract rejects the request in these conditions:

  • Requests are paused, or the E3 program is not registered.
  • The fee token is not allowed, or it is different from expectedFeeToken.
  • The circuit configuration for paramSet is different from expectedCryptoConfigId.
  • The quote from getE3Quote is more than maxFee.
  • inputWindow[0] is before the current time, or inputWindow[1] is before inputWindow[0].
  • The worst-case lifecycle duration is more than maxDuration.

When the checks pass, the contract takes the fee. It keeps the service fee in escrow and credits the flat randomness fee to the treasury. It calls the program validate function, which returns the encryption scheme. It also records the timeout configuration and the dependent contracts for this E3.

The contract then calls CiphernodeRegistry.requestCommittee. The registry checks that at least N operators were active just before the request. It records the ticket price and the IMT root, and it requests one random word from the randomness provider. The registry emits CommitteeRandomnessRequested and DkgFoldAttestationContextEstablished.

After the Chainlink VRF v2.5 result arrives, ciphernodes call submitTicket(e3Id, ticketNumber). Each accepted ticket emits TicketSubmitted. The ticket window is sortitionSubmissionWindow seconds from the VRF fulfillment time. See Sortition.

CommitteeFinalized

After the committee deadline, anyone can call finalizeCommittee(e3Id). In the current software, committee candidates schedule this call 30 seconds apart by party index, so one member acts first.

The registry then checks the result of sortition:

  • If no usable VRF result exists, the E3 fails with CommitteeFormationTimeout.
  • If fewer than N tickets were submitted, the E3 fails with InsufficientCommitteeMembers.
  • Otherwise, the registry sorts the committee by address and calls onCommitteeFinalized.

In both failure cases, the registry also emits CommitteeFormationFailed.

onCommitteeFinalized sets the DKG deadline and records the reward recipient for each member. The Interfold contract emits CommitteeFinalized. The registry emits SortitionCommitteeFinalized(e3Id, committee, scores) and CommitteeActivationChanged(e3Id, true).

Next, the committee runs DKG off-chain. The members exchange encrypted key shares and proofs (C0 to C4). The active aggregator selects a roster of H ready members and proves the key aggregation (C5). See Cryptography.

KeyPublished

The active aggregator calls publishCommittee(e3Id, pkCommitment, proof, dkgAttestationBundle). The registry verifies the DKG proof with BfvPkVerifier and the fold attestations with DkgFoldAttestationVerifier. It stores the DKG anchors that the decryption proof uses later.

The registry then calls onCommitteePublished. The Interfold contract accepts the key only before the DKG deadline and before inputWindow[1]. It sets the compute deadline and emits CommitteeFormed. The registry emits CommitteeProofPublished.

The committee members also publish the serialized public key in chunks with publishCommitteePublicKey. Each chunk emits CommitteePublicKeyChunkPublished. A client rebuilds the key and checks it against pkCommitment before it encrypts.

Next, data providers encrypt their inputs and call publishInput on the E3 program. The E3 program decides which inputs to accept during the input window.

CiphertextReady

The compute provider runs the secure process after inputWindow[1]. An application server then calls publishCiphertextOutput(e3Id, encodedOutputReference). The reference contains the contentHash of the ciphertext bytes, the ciphertextCommitment, the computeProof, and the availabilityProof.

The contract accepts the output only in these conditions:

  • The E3 is in KeyPublished, and the compute deadline has not passed.
  • The input window has closed.
  • The committee still has enough active members.
  • The E3 program accepts the availability receipt for contentHash.
  • The ciphertext verifier and the E3 program verify function accept the compute proof.

The contract stores the hash and the commitment, and it sets the decryption deadline. It emits CiphertextOutputReferencePublished. Next, each committee member computes a decryption share with a C6 proof.

Complete

The active aggregator combines T + 1 decryption shares and proves the result (C7). It calls publishPlaintextOutput(e3Id, plaintextOutput, proof). The contract accepts the output only before the decryption deadline, with a viable committee and a valid decryption proof.

The contract stores the plaintext and pays for the work:

  1. It credits the protocol share (protocolShareBps, recorded at request time) to the treasury.
  2. It splits the rest of the service fee among the active committee members.
  3. Each reward recipient claims with claimReward(e3Id) or claimRewards(e3Ids).

The contract emits PlaintextOutputPublished, RewardsDistributed, and RewardCredited for each member. Applications read the result from getE3(e3Id).plaintextOutput or from the PlaintextOutputPublished event.

Failed

An E3 enters Failed in one of these ways:

  • Timeout. After a deadline, anyone calls markE3Failed(e3Id). The contract selects the reason from the current stage.
  • Committee formation. finalizeCommittee records CommitteeFormationTimeout or InsufficientCommitteeMembers.
  • Committee viability. A slash expels a member, and the active members drop below H. The SlashingManager records InsufficientCommitteeMembers.
  • Cancellation. The requester calls cancelE3(e3Id). This is possible only in Requested, when no usable VRF result exists and the committee deadline has passed. The reason is CommitteeFormationTimeout.

The contract emits E3Failed(e3Id, failedAtStage, reason). If a later expulsion shows that the committee caused a requester-side failure, the SlashingManager changes the reason to InsufficientCommitteeMembers. The contract then emits E3FailureReclassified. This change is possible only before refund settlement.

Deadlines and timeouts

Each E3 records the timeout configuration at request time. A later governance change does not affect an existing E3.

DeadlineSet whenValueReason when it passes
Committee deadlineRequest, then VRF resultRequest time + randomnessRequestTimeout. After a usable VRF result: fulfillment time + sortitionSubmissionWindowCommitteeFormationTimeout
DKG deadlineCommitteeFinalizedCommittee deadline + dkgWindowDKGTimeout
Compute deadlineKeyPublishedThe later of key publication time and inputWindow[1], + computeWindowComputeTimeout
Decryption deadlineCiphertextReadyCiphertext publication time + decryptionWindowDecryptionTimeout

A late finalization does not move the DKG deadline. It leaves less time for DKG. In Requested, a committee with enough tickets can still finalize until the DKG deadline. For that case, markE3Failed accepts the failure only after the committee deadline + dkgWindow.

The committee key must arrive before inputWindow[1]. The contract rejects a later key, and the E3 then fails with DKGTimeout. A safe inputWindow[1] is later than the request time plus the randomness timeout, the ticket window, and the DKG window.

The contract also limits the total duration. At request time, it computes the later of inputWindow[1] and the committee setup time (randomnessRequestTimeout + sortitionSubmissionWindow + dkgWindow). It adds computeWindow and decryptionWindow. The result must not be more than maxDuration.

markFailedGracePeriod protects against short chain or RPC delays. During this period after a deadline, only the requester, the contract owner, or an active committee member can call markE3Failed. After the period, anyone can call it. A value of 0 removes the restriction.

Configured values

The mainnet values come from packages/interfold-contracts/deploy/protocol/mainnet-protocol.config.json. The local values are the defaults for local development deployments (scripts/deployInterfold.ts and CiphernodeRegistryOwnable.sol). Read the live values from the contracts: getTimeoutConfig, markFailedGracePeriod, maxDuration, sortitionSubmissionWindow, and randomnessRequestTimeout.

ParameterContractMainnetLocal development
randomnessRequestTimeoutCiphernodeRegistry3,600 s (1 hour)3,600 s (1 hour)
sortitionSubmissionWindowCiphernodeRegistry600 s (10 minutes)60 s
dkgWindowInterfold21,600 s (6 hours)7,200 s (2 hours)
computeWindowInterfold604,800 s (7 days)86,400 s (1 day)
decryptionWindowInterfold21,600 s (6 hours)3,600 s (1 hour)
markFailedGracePeriodInterfold3,600 s (1 hour)0
maxDurationInterfold2,592,000 s (30 days)2,592,000 s (30 days)

Failure reasons

FailureReason in interfaces/IInterfold.sol lists the reasons. FailurePayerLib.sol assigns the cost of each reason to the requester side or to the ciphernode side.

ValueReasonRecorded byCost falls on
0NoneNot a failureNone
1CommitteeFormationTimeoutmarkE3Failed in Requested, finalizeCommittee, cancelE3Ciphernodes
2InsufficientCommitteeMembersfinalizeCommittee, SlashingManagerCiphernodes
3DKGTimeoutmarkE3Failed in CommitteeFinalizedCiphernodes
4DKGInvalidSharesNo contract path in this releaseCiphernodes
5NoInputsReceivedNo contract path in this releaseRequester
6ComputeTimeoutmarkE3Failed in KeyPublishedRequester
7ComputeProviderExpiredNo contract path in this releaseRequester
8ComputeProviderFailedNo contract path in this releaseRequester
9RequesterCancelledReserved for ABI compatibility. cancelE3 records reason 1.Requester
10DecryptionTimeoutmarkE3Failed in CiphertextReadyCiphernodes
11DecryptionInvalidSharesNo contract path in this releaseCiphernodes
12VerificationFailedNo contract path in this releaseCiphernodes

onE3Failed rejects None, RequesterCancelled, and any value from _MAX_FAILURE_REASON up. Requester-side reasons cover failures of the requester, the data providers, and the compute provider.

Refunds

Interfold keeps only the service fee in escrow. The flat randomness fee goes to the treasury at request time. No success, failure, or cancellation path returns the randomness fee.

After an E3 fails, the funds move in this sequence:

  1. Anyone calls processE3Failure(e3Id). The Interfold contract sends the service fee escrow to E3RefundManager and emits E3FailureProcessed.
  2. E3RefundManager calculates the split from the failure reason and the policy recorded at request time.
  3. The requester calls claimRequesterRefund(e3Id).
  4. The reward recipient of each honest committee member calls claimHonestNodeReward(e3Id, operator).

The split depends on the side that caused the failure:

  • Ciphernode side. The requester gets 100% of the service fee escrow. The treasury gets nothing, and the fee does not pay honest nodes. Honest nodes get compensation only from collateral slashed from faulty nodes.
  • Requester side. Honest committee members get a share for the work that they completed. The treasury gets the protocol share. The requester gets the rest. If no honest node exists, the node share also goes to the requester.

The Requesting an E3 page explains fees, quotes, and refund claims in detail.

Next steps