Secure Process
The Secure Process is the core FHE logic for your E3 Program. It runs within your selected Compute Provider's environment, ultimately producing and publishing a ciphertext output that is decrypted by your Ciphernode Committee. To facilitate this, Enclave provides a Compute Provider package (opens in a new tab) to simplify writing the Secure Process with any Compute Provider.
Using the Compute Provider Package
To simplify integration with Enclave, use the provided Compute Provider package.
Benefits:
- Handles Merkle Tree Construction: Recreates the input Merkle tree inside the compute environment.
- Simplifies Proof Generation: Manages proof creation for computation verification.
- Abstracts Complexity: Allows you to focus on your computation logic.
Implementation:
- Import the Compute Provider package into your project.
- Use its functions to handle tasks such as input processing and proof generation.
Example:
use compute_provider::{ComputeInput, ComputeManager, ComputeProvider, ComputeResult, FHEInputs};
// Implement ComputeProvider trait for your chosen provider
pub struct Risc0Provider;
impl ComputeProvider for Risc0Provider {
type Output = Risc0Output;
fn prove(&self, input: &ComputeInput) -> Self::Output {
// Implement proof generation using RISC Zero / SP1 or any other provider
// ...
}
}
Writing the Secure Process
Your Secure Process defines the core computation logic and runs inside the Compute Provider's environment. Below are the key steps to implement it effectively:
Steps:
- Define the Computation: Specify the exact computation your E3 program needs to perform.
- Implement the Logic: Write the Secure Process using the Compute Provider's supported language (e.g., Rust for RISC Zero).
- Handle Encrypted Inputs: Ensure the program can process encrypted data correctly.
- Focus on Computation: Use the Compute Provider package to handle additional tasks like Merkle tree verification and proof verification, so you can focus on your computation logic.
Example (Rust with RISC Zero):
use fhe::bfv::{BfvParameters, Ciphertext};
use fhe_traits::{Deserialize, Serialize};
use std::sync::Arc;
/// Your secure computation function
pub fn fhe_processor(fhe_inputs: &FHEInputs) -> Vec<u8> {
// Deserialize parameters
let params = Arc::new(BfvParameters::try_deserialize(&fhe_inputs.params).unwrap());
// Initialize sum
let mut sum = Ciphertext::zero(¶ms);
// Sum all ciphertexts
for ciphertext_bytes in &fhe_inputs.ciphertexts {
let ciphertext = Ciphertext::from_bytes(&ciphertext_bytes.0, ¶ms).unwrap();
sum += &ciphertext;
}
// Serialize the result
sum.to_bytes()
}
Running the Secure Process
To run the Secure Process, use the Compute Provider ComputeManager
to execute the program. It
expects:
- The
ComputeProvider
implementation - The
FHEInputs
struct that consists of the FHE parameters and the ciphertexts to use. - The Secure Process function
fhe_processor
- A boolean flag
use_parallel
to indicate whether to use parallel processing. - An optional
batch_size
that will be used for parallel processing. Must be a power of 2.
// Run the secure process inside the Compute Provider
pub fn run_compute(params: FHEInputs) -> Result<(Risc0Output, Vec<u8>)> {
// Use the previously implemented Risc0Provider
let risc0_provider = Risc0Provider;
// Create the ComputeManager with the provider, params, and the secure process function
let mut provider = ComputeManager::new(risc0_provider, params, fhe_processor, false, None);
// Execute the program and get the output
let output: (Risc0Output, Vec<u8>) = provider.start();
Ok(output)
}