Putting It All Together

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


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.