Putting It All Together: A Complete Example
To solidify your understanding, let's walk through an example of building and deploying an E3 Program using RISC Zero.
Step 1: Set Up Development Environment
- Install Required Tools:
- Rust and Cargo
- Node.js and Yarn
- Foundry and Anvil for Ethereum development
- RISC Zero toolchain
Step 2: Write the Secure Process
- Implement the computation logic in Rust.
- Use the Compute Provider package for handling inputs and Merkle tree verification.
Secure Process (Rust):
// src/main.rs
#![no_main]
#![no_std]
use compute_provider::FHEInputs;
use fhe::bfv::{BfvParameters, Ciphertext};
use fhe_traits::{Deserialize, Serialize};
use std::sync::Arc;
risc0_zkvm_guest::entry!(main);
pub fn main() {
// Receive encrypted inputs
let fhe_inputs: FHEInputs = env::read();
// Call your computation function
let result = fhe_processor(&fhe_inputs);
// Commit the result
env::commit(&result);
}
Step 3: Implement Compute Provider
- Implement the
ComputeProvider
trait for RISC Zero. - Handle proof generation and interaction with the zkVM.
Compute Provider Implementation:
pub struct Risc0Provider;
impl ComputeProvider for Risc0Provider {
type Output = Risc0Output;
fn prove(&self, input: &ComputeInput) -> Self::Output {
// Set up the execution environment
let env = ExecutorEnv::builder()
.write(input)
.unwrap()
.build()
.unwrap();
// Generate the proof
let receipt = default_prover()
.prove_with_ctx(
env,
&VerifierContext::default(),
VOTING_ELF,
&ProverOpts::groth16(),
)
.unwrap()
.receipt;
// Extract and return the output
Risc0Output {
result: receipt.journal.decode().unwrap(),
bytes: receipt.journal.bytes.clone(),
seal: groth16::encode(receipt.inner.groth16().unwrap().seal.clone()).unwrap(),
}
}
}
Step 4: Write the E3 Program Contract
- Define your E3 Program contract, integrating with the RISC Zero verifier.
E3 Program Contract (Solidity):
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.27;
import {CRISPBase, IEnclave, IE3Program, IInputValidator} from "evm_base/contracts/CRISPBase.sol";
import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol";
contract MyE3Program is CRISPBase {
bytes32 public constant IMAGE_ID = /* Your Image ID */;
bytes32 public constant ENCRYPTION_SCHEME_ID = /* Your Encryption Scheme ID */;
IRiscZeroVerifier public verifier;
IInputValidator public inputValidator;
constructor(
IEnclave _enclave,
IInputValidator _inputValidator,
IRiscZeroVerifier _verifier
) {
initialize(_enclave, _inputValidator, _verifier);
}
function initialize(
IEnclave _enclave,
IInputValidator _inputValidator,
IRiscZeroVerifier _verifier
) public {
CRISPBase.initialize(_enclave);
inputValidator = _inputValidator;
verifier = _verifier;
}
// Implement validate and verify functions as shown earlier
}
Step 5: Deploy Contracts
- Deploy the Enclave contract to your desired network.
- Deploy your E3 Program contract and register it with the Enclave contract.
Step 6: Run Ciphernodes
- Start Ciphernodes and ensure they are registered with the Enclave contract.
Step 7: Submit a Computation Request
- Use your application or a script to submit a computation request to the Enclave contract.
Example (JavaScript):
await EnclaveContract.request(
filterAddress,
[thresholdMin, thresholdMax],
[startWindowStart, startWindowEnd],
duration,
e3ProgramAddress,
e3ProgramParams,
computeProviderParams,
{ value: computationFee }
);
Step 8: Submit Encrypted Inputs
- Data Providers encrypt their inputs and submit them along with ZKPs.
Step 9: Execute Computation and Verify Proof
- The Compute Provider executes the Secure Process.
- The E3 Program contract verifies the computation proof on-chain.
Step 10: Ciphernodes Decrypt Result
- Ciphernodes submit decryption shares.
- The Decryption Verifier contract verifies the correctness of the decryption.
Step 11: Retrieve Final Result
- Listen for the
PlaintextOutputPublished
event. - Retrieve the plaintext result from the Enclave contract.
Conclusion
By following this guide, you should now have a solid understanding of Enclave and how to build your own E3 Programs. Enclave provides a powerful framework for secure, privacy-preserving computations over encrypted data, enabling a new class of decentralized applications.
Key Takeaways:
- Enclave distributes trust and ensures data remains encrypted throughout computation.
- The modular architecture allows for flexibility in choosing Compute Providers and designing computations.
- The provided Compute Provider package simplifies integration and handles complex tasks like Merkle tree verification.
Additional Resources
- Enclave GitHub Repository: https://github.com/gnosisguild/enclave (opens in a new tab)
- CRISP GitHub Repository: https://github.com/gnosisguild/crisp (opens in a new tab)
- Enclave Starter Template: https://github.com/gnosisguild/enclave-starter-template (opens in a new tab)
- RISC Zero Documentation: https://www.risczero.com (opens in a new tab)
- Compute Provider Package: Check the Enclave repository for the Compute Provider package and examples.
- FHE Libraries: Explore libraries like
fhe.rs
for Rust to handle homomorphic encryption. - Zero-Knowledge Proof Libraries: Use libraries like
risc0-zkvm
for proof generation and verification.
Happy Building!
If you have any questions or need further assistance, feel free to reach out to Gnosis Guild (opens in a new tab), the initial development team building Enclave.