Skip to main content
This guide covers all FHE operations available in the Privora Program SDK.

Loading Encrypted Values

Before performing operations, load encrypted values from their references:
use privora_sdk_program::prelude::*;

// From hash reference stored in account
let price_ref: EncryptedRef<u8> = account.price_ref;

// Load the actual encrypted value
let price: Encrypted<u8> = price_ref.load()?;
The load() method:
  1. Takes the hash from EncryptedRef
  2. Fetches the ciphertext from the content store via syscall
  3. Returns an Encrypted<T> ready for computation

Arithmetic Operations

Addition

// Method syntax (returns Result)
let sum: Encrypted<u8> = a.add(&b)?;

// Operator syntax (panics on error)
let sum: Encrypted<u8> = &a + &b;
Operators use references (&a + &b) to avoid consuming the values.

Subtraction

// Method syntax
let difference: Encrypted<u8> = a.sub(&b)?;

// Operator syntax
let difference: Encrypted<u8> = &a - &b;
FHE subtraction can underflow. If a is less than b, the result wraps around (modular arithmetic). Design your program to avoid or handle this.

Multiplication

// Method syntax
let product: Encrypted<u8> = a.mul(&b)?;

// Operator syntax
let product: Encrypted<u8> = &a * &b;

Chaining Operations

// Calculate: (a + b) * c
let result = a.add(&b)?.mul(&c)?;

// With operators (use parentheses carefully)
let result = &(&a + &b) * &c;

// More readable with intermediate variables
let sum = &a + &b;
let result = &sum * &c;

Comparison Operations

Comparisons return EncryptedBool, which can be used for conditional logic.

Greater Than or Equal

let is_ge: EncryptedBool = buy_price.ge(&sell_price)?;

Greater Than

let is_gt: EncryptedBool = price.gt(&threshold)?;

Less Than or Equal

let is_le: EncryptedBool = amount.le(&limit)?;

Less Than

let is_lt: EncryptedBool = balance.lt(&required)?;

Equality

let is_equal: EncryptedBool = value_a.eq_enc(&value_b)?;
The method is eq_enc (not eq) to avoid conflict with Rust’s Eq trait.

Min/Max Operations

Get the minimum or maximum of two encrypted values:
// Minimum
let min_value: Encrypted<u8> = buy_qty.min(&sell_qty)?;

// Maximum
let max_value: Encrypted<u8> = price_a.max(&price_b)?;
These are implemented using encrypted comparison and selection.

Conditional Selection

The select method implements encrypted if-then-else:
let condition: EncryptedBool = buy_price.ge(&sell_price)?;

// If condition is true, return sell_price; else return buy_price
let fill_price: Encrypted<u8> = condition.select(&sell_price, &buy_price)?;
This is equivalent to:
if (buy_price >= sell_price) {
    fill_price = sell_price;
} else {
    fill_price = buy_price;
}
Key insight: Both branches are always computed. FHE cannot skip computation based on encrypted conditions.

Storing Results

After computation, store results back to the content store:
// Compute result
let result: Encrypted<u8> = a.add(&b)?;

// Store and get hash reference
let result_ref: EncryptedRef<u8> = result.store()?;

// Save reference to account
account.result_ref = result_ref;

Complete Example

use privora_sdk_program::prelude::*;

pub fn match_orders(
    buy_price_ref: EncryptedRef<u8>,
    buy_qty_ref: EncryptedRef<u8>,
    sell_price_ref: EncryptedRef<u8>,
    sell_qty_ref: EncryptedRef<u8>,
) -> Result<(EncryptedRef<u8>, EncryptedRef<u8>), ProgramError> {
    // 1. Load all encrypted values
    let buy_price = buy_price_ref.load()?;
    let buy_qty = buy_qty_ref.load()?;
    let sell_price = sell_price_ref.load()?;
    let sell_qty = sell_qty_ref.load()?;

    // 2. Check if orders can match (buy >= sell)
    let can_match: EncryptedBool = buy_price.ge(&sell_price)?;

    // 3. Calculate fill quantity (minimum of both)
    let fill_qty: Encrypted<u8> = buy_qty.min(&sell_qty)?;

    // 4. Use sell price as fill price (standard matching)
    // The fill price is the sell_price (taker price)

    // 5. Store results
    let fill_price_ref = sell_price.store()?;
    let fill_qty_ref = fill_qty.store()?;

    Ok((fill_price_ref, fill_qty_ref))
}

Operation Costs

FHE operations have different computational costs:
OperationRelative CostNotes
Addition1xFast, use freely
Subtraction1xSame as addition
Multiplication2-3xMore expensive
Comparison5-10xRequires bootstrapping
Min/Max5-10xBuilt on comparison
Select5-10xBuilt on comparison

Optimization Tips

  1. Batch comparisons: Do all arithmetic first, then comparisons
// Good: Arithmetic first, then compare
let total_a = price_a.mul(&qty_a)?;
let total_b = price_b.mul(&qty_b)?;
let a_is_larger = total_a.gt(&total_b)?;

// Avoid: Interleaving comparisons with arithmetic
  1. Minimize comparisons: Each comparison is expensive
// Good: One comparison
let can_match = buy_price.ge(&sell_price)?;

// Avoid: Redundant comparisons
let can_match = buy_price.ge(&sell_price)?;
let cant_match = buy_price.lt(&sell_price)?; // Redundant!
  1. Reuse loaded values
// Good: Load once, use multiple times
let price = price_ref.load()?;
let result1 = price.add(&amount1)?;
let result2 = price.add(&amount2)?;

// Avoid: Loading multiple times
let result1 = price_ref.load()?.add(&amount1)?;
let result2 = price_ref.load()?.add(&amount2)?; // Unnecessary second load

Type Safety

The SDK enforces type safety at compile time:
let a: Encrypted<u8> = a_ref.load()?;
let b: Encrypted<u64> = b_ref.load()?;

// This won't compile!
let sum = a.add(&b)?; // Error: expected Encrypted<u8>, found Encrypted<u64>

Error Handling

All operations return Result:
// Handle errors explicitly
let price = match price_ref.load() {
    Ok(p) => p,
    Err(e) => {
        msg!("Failed to load price: {:?}", e);
        return Err(e);
    }
};

// Or use ? operator
let price = price_ref.load()?;
let sum = price.add(&other)?;
let result_ref = sum.store()?;

Next Steps