Building with Enclave
The Enclave 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 Enclave 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.
CiphernodeRegistry public ciphernodeRegistry;
// Mapping of allowed E3 Programs.
mapping(IE3Program e3Program => bool allowed) public e3Programs;
// Mapping of E3s.
mapping(uint256 e3Id => E3 e3) public e3s;
// Mapping of input merkle trees.
mapping(uint256 e3Id => LeanIMTData imt) public inputs;
// Mapping of enabled encryption schemes.
mapping(bytes32 encryptionSchemeId => IDecryptionVerifier decryptionVerifier)
public decryptionVerifiers;
}
Requesting Computation
Request Flow
- Users submits onchain request
function request(
address filter,
uint32[2] calldata threshold,
uint256[2] calldata startWindow,
uint256 duration,
IE3Program e3Program,
bytes memory e3ProgramParams,
bytes memory computeProviderParams
) external payable
- Contract validates request parameters
- E3 Program contract is set and used to get the InputValidator contract & Encryption scheme.
- Request is submitted to the ciphernodeRegistry for committee selection.
E3Requested
event is emitted
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.
E3 Activation
After the public key is published, anyone can activate the E3 by calling the activate()
function
in the Enclave contract:
function activate(
uint256 e3Id,
bytes memory publicKey
) external
Activating an E3 will allow valid Data Providers to submit inputs to the computation.
Input Publication
Inputs are published directly to the Enclave contract's publishInput()
function.
function publishInput(
uint256 e3Id,
bytes memory data
) external
Inputs are accumulated incrementally into a Merkle tree. This allows you to:
- 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.
- Prevent input tampering or omission.
- Anchor proofs for correct execution.
Merkle Tree Construction
Enclave uses a Lean Merkle Tree implementation. Each input is hashed using the PosidenT3 Hash function, and the root is updated with each new input.
Your E3 program should:
- Reconstruct the Merkle tree from the given inputs
- 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 Prodivers, rather than in your Secure Process. This pushed computation to the edges and allows you to reduce the complexity of your FHE computation.
Your E3 Program must include a contract that implements IInputValidator. When publishing an input,
the Enclave contracts will call the validate()
function on your Input Validator contract.
function validate(
address sender,
bytes memory params
) external returns (bytes memory input, bool success);
At a minimum, this function 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
event E3Requested(
uint256 indexed e3Id,
address indexed requester,
address e3ProgramAddress
);
event InputSubmitted(
uint256 indexed e3Id,
address indexed sender,
bytes32 inputHash
);
event CiphertextOutputPublished(
uint256 indexed e3Id,
bytes32 ciphertextHash
);
event PlaintextOutputPublished(
uint256 indexed e3Id,
bytes plaintext
);
Result Publication Flow
- The Compute Provider submits a proof and ciphertext output.
- Enclave uses the E3 Program contract to verify the proof and emits
CiphertextOutputPublished
. - Ciphernodes decrypt the ciphertext output.
- The plaintext result is published
- Events are emitted for off-chain services
Example Usage
Submitting a Request
const enclaveContract = new ethers.Contract(enclaveAddress, enclaveAbi, signer)
const tx = await enclaveContract.request(
filterAddress,
[thresholdMin, thresholdMax],
[startWindowStart, startWindowEnd],
duration,
e3ProgramAddress,
e3ProgramParams,
computeProviderParams,
{ value: computationFee },
)
const receipt = await tx.wait()
const e3Id = receipt.events.find((e) => e.event === 'E3Requested').args.e3Id
Monitoring Results
enclaveContract.on('PlaintextOutputPublished', (e3Id, plaintext) => {
console.log(`Computation ${e3Id} completed with result:`, plaintext)
})
Submitting Inputs
const tx = await enclaveContract.publishInput(e3Id, encryptedInput)
await tx.wait()
Security Considerations
- 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.
- Input Validation
Your InputValidator 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 InputValidator contract to push computation to the edges, reducing the complexity of your FHE computation.
- Result Verification
The Enclave 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.
- Event Handling
If you are consuming Enclave outputs offchain, allow for sufficient confirmations to be confident in the finality of the outcome and to appropriately handle re-orgs.
Best Practices
-
Request Management
- Set appropriate thresholds
- Choose realistic time windows
-
Input Handling
- Encrypt inputs properly
- Include necessary ZKPs
- Submit within time windows
-
Result Processing
- Listen for all relevant events
- Implement timeout handling
- Verify result integrity
-
Error Handling
- Handle contract reverts
- Implement retry logic
- Log errors appropriately