Enclave is now The Interfold. Documentation is being updated.
Building with Interfold

Building with the Interfold

The Interfold smart contract acts as the central coordinator for all E3 operations. It manages computation requests, input validation, Ciphernode Committees (CiCos), and result publication while maintaining the security and privacy guarantees of the protocol.

The Interfold Smart Contract

Core Responsibilities

  • Manage E3 computation requests
  • Coordinate Ciphernode Committees
  • Handle encrypted input submission
  • Manage Merkle trees for input verification
  • Publish computation results
  • Emit events for off-chain services

Key State Variables

contract Enclave {
  // Address of the Ciphernode registry.
  ICiphernodeRegistry public ciphernodeRegistry;
 
  // Address of the bonding registry.
  IBondingRegistry public bondingRegistry;
 
  // Address of the slashing manager.
  ISlashingManager public slashingManager;
 
  // Address of the refund manager.
  IE3RefundManager public e3RefundManager;
 
  // Fee token used for E3 payments.
  IERC20 public feeToken;
 
  // Mapping of allowed E3 Programs.
  mapping(IE3Program e3Program => bool allowed) public e3Programs;
 
  // Mapping of E3s.
  mapping(uint256 e3Id => E3 e3) public e3s;
 
  // Mapping of enabled encryption schemes.
  mapping(bytes32 encryptionSchemeId => IDecryptionVerifier decryptionVerifier)
    public decryptionVerifiers;
 
  // Mapping of E3 payment amounts.
  mapping(uint256 e3Id => uint256 amount) public e3Payments;
 
  // Maximum allowed duration for an E3's input window.
  uint256 public maxDuration;
 
  // Auto-incrementing E3 identifier.
  uint256 public nexte3Id;
}

E3 Struct

Each computation is tracked by an E3 struct:

struct E3 {
  uint256 seed; // Random seed for committee selection
  uint32[2] threshold; // [M, N] - M required out of N members
  uint256 requestBlock; // Block when E3 was requested
  uint256[2] inputWindow; // [start, end] timestamps for input acceptance
  bytes32 encryptionSchemeId; // Encryption scheme identifier
  IE3Program e3Program; // E3 Program contract address
  bytes e3ProgramParams; // ABI-encoded program params
  bytes customParams; // Arbitrary application-defined params
  IDecryptionVerifier decryptionVerifier; // Verifier for decryption proofs
  bytes32 committeePublicKey; // Hash of committee's shared public key
  bytes32 ciphertextOutput; // Hash of encrypted output
  bytes plaintextOutput; // Decrypted final result
  address requester; // Entity that requested computation
}

Lifecycle Stages

Each E3 progresses through these stages:

StageValueDescription
None0E3 does not exist
Requested1E3 submitted, committee selection initiated
CommitteeFinalized2Sortition complete, DKG started
KeyPublished3Committee public key published, inputs accepted
CiphertextReady4Encrypted output published, awaiting decryption
Complete5Plaintext output published, rewards distributed
Failed6Timeout or fault detected, refund initiated

View Functions

The Enclave contract exposes these read-only functions for querying E3 state:

// Fetch the complete E3 struct (reverts if e3Id doesn't exist)
function getE3(uint256 e3Id) external view returns (E3 memory e3);
 
// Get current lifecycle stage
function getE3Stage(uint256 e3Id) external view returns (E3Stage stage);
 
// Estimate the fee for an E3 request
function getE3Quote(E3RequestParams calldata params) external view returns (uint256 fee);
 
// Get the decryption verifier for an encryption scheme
function getDecryptionVerifier(
  bytes32 encryptionSchemeId
) external view returns (IDecryptionVerifier);
 
// Get failure reason (if E3 has failed)
function getFailureReason(uint256 e3Id) external view returns (FailureReason reason);
 
// Get the address that requested the E3
function getRequester(uint256 e3Id) external view returns (address requester);
 
// Get timeline deadlines (dkgDeadline, computeDeadline, decryptionDeadline)
function getDeadlines(uint256 e3Id) external view returns (E3Deadlines memory deadlines);
 
// Get global timeout configuration
function getTimeoutConfig() external view returns (E3TimeoutConfig memory config);

Requesting Computation

Request Flow

  1. Users submit E3RequestParams containing thresholds, timing windows, program references, and optional custom params.

    function request(
      E3RequestParams calldata requestParams
    ) external returns (uint256 e3Id, E3 memory e3);
  2. Contract validates the struct, estimates the fee, and stores an E3 record seeded with a random value derived from block entropy.

  3. The E3 program's validate hook returns the encryption scheme ID, which is persisted on the E3 struct.

  4. Committee selection is delegated to the ciphernodeRegistry via requestCommittee.

  5. E3Requested(e3Id, requester, e3ProgramAddress) is emitted for off-chain watchers.

Committee and Ciphernode Management

Committee Selection

  • An E3 request triggers a deterministic sortition process to select a Ciphernode Committee (CiCo) from the global pool of available Ciphernodes.
  • Selected nodes coordinate to produce a shared public key with the requested threshold.
  • The public key is published to the Ciphernode Registry smart contract.
  • For bonding, ticket economics, CLI commands, and operational responsibilities, see the Ciphernode Operators guide.

E3 Activation

After the committee is selected and finalized via sortition, the selected Ciphernodes perform a Distributed Key Generation (DKG) to produce a shared public key. When the aggregated public key is published, the CiphernodeRegistry calls onCommitteePublished() on the Interfold contract, which transitions the E3 to the KeyPublished stage.

Once the E3 reaches the KeyPublished stage and the input window opens (block.timestamp >= inputWindow[0]), Data Providers can submit inputs to the computation.

Input Publication

Inputs are published to Program contracts' publishInput() function.

function publishInput(
    uint256 e3Id,
    bytes memory data
) external

Inputs can be accumulated incrementally into a Merkle tree (LazyIMT, or ultimately chosen by the program implementation). This allows you to:

  1. Use the published Merkle root as part of your E3 Program's Secure Process to ensure all published inputs are processed by the Compute Provider.
  2. Prevent input tampering or omission.
  3. Anchor proofs for correct execution.

Merkle Tree Construction

The Interfold uses a Lean Merkle Tree implementation. Each input is hashed using the PoseidonT3 Hash function, and the root is updated with each new input.

Your E3 program should:

  1. Reconstruct the Merkle tree from the given inputs
  2. Verify the resulting root matches on-chain root

Input Validation

As much as possible, you should aim to validate inputs via proofs generated by Data Providers, rather than in your Secure Process. This pushes computation to the edges and allows you to reduce the complexity of your FHE computation.

Your E3 Program must include logic that validates user inputs. For simplicity, this can be included inside the publishInput function of the E3 Program contract.

At a minimum, this logic should validate a proof that the given ciphertext is a valid encryption to the E3's public key. It is also recommended to bundle in proofs to validate:

  • The legitimacy of the Data Provider (e.g., ensuring they are listed in a registry of approved data providers).
  • Range checks, checksums, and/or other well-formedness criteria for the plaintext that was encrypted.

Result Publication and Events

Key Events

Interfold Contract Events:

event E3Requested(uint256 e3Id, E3 e3, IE3Program indexed e3Program);
 
event E3StageChanged(uint256 indexed e3Id, E3Stage previousStage, E3Stage newStage);
 
event CiphertextOutputPublished(uint256 indexed e3Id, bytes ciphertextOutput);
 
event PlaintextOutputPublished(uint256 indexed e3Id, bytes plaintextOutput);
 
event E3Failed(uint256 indexed e3Id, E3Stage failedAtStage, FailureReason reason);
 
event RewardsDistributed(uint256 indexed e3Id, address[] nodes, uint256[] amounts);

Program Contract Events:

event InputPublished(uint256 indexed e3Id, bytes data, uint256 inputHash, uint256 index);

Result Publication Flow

  1. The Compute Provider submits a proof and ciphertext output.
  2. The Interfold uses the E3 Program contract to verify the proof and emits CiphertextOutputPublished.
  3. Ciphernodes decrypt the ciphertext output.
  4. The plaintext result is then published and the Decryption Verifier validates the decryption proof.
  5. Rewards are distributed to active committee members.
  6. Events are emitted for off-chain services.

Example Usage

Submitting a Request

const enclaveContract = new ethers.Contract(enclaveAddress, enclaveAbi, signer)
 
const requestParams = {
  threshold: [3, 5],
  inputWindow: [inputWindowStart, inputWindowEnd],
  e3Program: e3ProgramAddress,
  e3ProgramParams,
  computeProviderParams,
  customParams: '0x',
}
 
const tx = await enclaveContract.request(requestParams)
const receipt = await tx.wait()
const e3Id = receipt.logs
  .map((log) => enclaveContract.interface.parseLog(log))
  .find((parsed) => parsed?.name === 'E3Requested')?.args?.e3Id

Monitoring Results

enclaveContract.on('PlaintextOutputPublished', (e3Id, plaintext) => {
  console.log(`Computation ${e3Id} completed with result:`, plaintext)
})

Submitting Inputs

You can submit inputs by calling the publishInput function on your E3 Program contract:

const tx = await programContract.publishInput(e3Id, encryptedInput)
await tx.wait()

Security Considerations

  1. Committee Selection

The size and threshold of the Ciphernode Committee you select for your computation directly impacts both the cost and economic security of your E3. A higher threshold means more economic security (a higher cost to compromise the E3), but incurs a higher protocol fee.

  1. Input Validation

Your Program contract is critical to ensuring that inputs come from approved parties, are sanitized for your computation, and truthfully correspond to any specific sources of truth. You can use your Program contract to push computation to the edges, reducing the complexity of your FHE computation.

  1. Result Verification

The Interfold protocol will accept and decrypt whatever output is reported by your Compute Provider. In other words, your E3s inherit the trust assumptions of your Compute Provider. An incorrectly implemented Secure Process in your E3 Program or a dishonest or compromised Compute Provider could leak information.

  1. Event Handling

If you are consuming Interfold outputs offchain, allow for sufficient block confirmations to be confident in the finality of the outcome and to appropriately handle re-orgs.

Best Practices

See the dedicated Best Practices guide for updated operational playbooks covering parameter selection, validator design, compute provider trust, and observability.