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).
E3RequestedRequested
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
| Stage | Call that enters it | Usual caller | Interfold events |
|---|---|---|---|
Requested | Interfold.request | Requester | E3Requested, E3StageChanged |
CommitteeFinalized | CiphernodeRegistry.finalizeCommittee, then onCommitteeFinalized | A committee candidate | CommitteeFinalized, E3StageChanged |
KeyPublished | CiphernodeRegistry.publishCommittee, then onCommitteePublished | Active aggregator | CommitteeFormed, E3StageChanged |
CiphertextReady | Interfold.publishCiphertextOutput | Application server | CiphertextOutputReferencePublished, E3StageChanged |
Complete | Interfold.publishPlaintextOutput | Active aggregator | PlaintextOutputPublished, E3StageChanged |
Failed | markE3Failed, cancelE3, or a call from the registry or slashing manager | Anyone, after a deadline | E3Failed, 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
paramSetis different fromexpectedCryptoConfigId. - The quote from
getE3Quoteis more thanmaxFee. inputWindow[0]is before the current time, orinputWindow[1]is beforeinputWindow[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
verifyfunction 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:
- It credits the protocol share (
protocolShareBps, recorded at request time) to the treasury. - It splits the rest of the service fee among the active committee members.
- Each reward recipient claims with
claimReward(e3Id)orclaimRewards(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.
finalizeCommitteerecordsCommitteeFormationTimeoutorInsufficientCommitteeMembers. - Committee viability. A slash expels a member, and the active members drop below H. The
SlashingManagerrecordsInsufficientCommitteeMembers. - Cancellation. The requester calls
cancelE3(e3Id). This is possible only inRequested, when no usable VRF result exists and the committee deadline has passed. The reason isCommitteeFormationTimeout.
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.
| Deadline | Set when | Value | Reason when it passes |
|---|---|---|---|
| Committee deadline | Request, then VRF result | Request time + randomnessRequestTimeout. After a usable VRF result: fulfillment time + sortitionSubmissionWindow | CommitteeFormationTimeout |
| DKG deadline | CommitteeFinalized | Committee deadline + dkgWindow | DKGTimeout |
| Compute deadline | KeyPublished | The later of key publication time and inputWindow[1], + computeWindow | ComputeTimeout |
| Decryption deadline | CiphertextReady | Ciphertext publication time + decryptionWindow | DecryptionTimeout |
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.
| Parameter | Contract | Mainnet | Local development |
|---|---|---|---|
randomnessRequestTimeout | CiphernodeRegistry | 3,600 s (1 hour) | 3,600 s (1 hour) |
sortitionSubmissionWindow | CiphernodeRegistry | 600 s (10 minutes) | 60 s |
dkgWindow | Interfold | 21,600 s (6 hours) | 7,200 s (2 hours) |
computeWindow | Interfold | 604,800 s (7 days) | 86,400 s (1 day) |
decryptionWindow | Interfold | 21,600 s (6 hours) | 3,600 s (1 hour) |
markFailedGracePeriod | Interfold | 3,600 s (1 hour) | 0 |
maxDuration | Interfold | 2,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.
| Value | Reason | Recorded by | Cost falls on |
|---|---|---|---|
| 0 | None | Not a failure | None |
| 1 | CommitteeFormationTimeout | markE3Failed in Requested, finalizeCommittee, cancelE3 | Ciphernodes |
| 2 | InsufficientCommitteeMembers | finalizeCommittee, SlashingManager | Ciphernodes |
| 3 | DKGTimeout | markE3Failed in CommitteeFinalized | Ciphernodes |
| 4 | DKGInvalidShares | No contract path in this release | Ciphernodes |
| 5 | NoInputsReceived | No contract path in this release | Requester |
| 6 | ComputeTimeout | markE3Failed in KeyPublished | Requester |
| 7 | ComputeProviderExpired | No contract path in this release | Requester |
| 8 | ComputeProviderFailed | No contract path in this release | Requester |
| 9 | RequesterCancelled | Reserved for ABI compatibility. cancelE3 records reason 1. | Requester |
| 10 | DecryptionTimeout | markE3Failed in CiphertextReady | Ciphernodes |
| 11 | DecryptionInvalidShares | No contract path in this release | Ciphernodes |
| 12 | VerificationFailed | No contract path in this release | Ciphernodes |
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:
- Anyone calls
processE3Failure(e3Id). The Interfold contract sends the service fee escrow toE3RefundManagerand emitsE3FailureProcessed. E3RefundManagercalculates the split from the failure reason and the policy recorded at request time.- The requester calls
claimRequesterRefund(e3Id). - 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.