Writing an E3 Contract
The E3 contract defines your program on-chain. It verifies parameters during the creation of new E3 instances, validates inputs for your Secure Process, and publishes the computation output.
Smart Contract Components
Each E3 Program consists of two contract components:
- E3 Program contract
- Input Validator contract
E3 Program
The E3 program forms the core of each E3, implementing two functions: one to validate input parameters when a new E3 instance is created and another to verify the output of the computation.
validate
function validate(
uint256 e3Id,
uint256 seed,
bytes calldata e3ProgramParams,
bytes calldata computeProviderParams
)
external
returns (bytes32 encryptionSchemeId, IInputValidator inputValidator);
When a new instance of your E3 Program is requested, the validate
function is called to validate
and initialize the new E3. Some useful validations include:
- Random Seed Initialization: Use the
seed
parameter to instantiate the E3 with a specific random seed - Custom Parameters: Utilize
e3ProgramParams
to pass in any additional arbitrary parameters, most commonly the address of your Input Validator contract. - Compute Provider Setup: Use
computeProviderParams
to validate the configuration of the Compute Provider chosen for your E3 Program.
For an example, see this mockup (opens in a new tab) or check out the demo implementation for the CRISP protocol (opens in a new tab).
verify
function verify(
uint256 e3Id,
bytes32 ciphertextOutput,
bytes memory proof
) external returns (bool success);
The verify function intakes the output of the E3 computation and the accompanying proof generated by your chosen Compute Provider to assess the validity of the proof and ciphertext. You can see an example of this using RISC Zero's Verifier in our CRISP E3 contract (opens in a new tab).
Input Validator
In order to ensure correct computation, we recommend creating or using an existing input verifier contract to check that the encrypted data submitted to your E3 is properly structured. This will most likely be used in tandem with a proof generated by your Data Provider.
Responsibilities:
- Data Decoding: Decode encrypted input data to its intended format.
- ZKP Verification: Verify any associated ZKPs to ensure input correctness.
- Input Acceptance: Return validated input for inclusion in the computation.
Example:
pragma solidity >=0.8.27;
import {IInputValidator} from "@gnosis-guild/enclave/interfaces.sol";
contract MyInputValidator is IInputValidator {
function validate(address sender, bytes memory data) external override returns (bytes memory, bool) {
// Decode data
// Verify ZKP
// Return validated input
}
}