ParaloomPARALOOM

Developer Guide

Build applications on Paraloom

Developer Guide

Learn how to build privacy-preserving applications on Paraloom.

Overview

Paraloom enables developers to:

  • Execute private computations
  • Process shielded transactions
  • Build confidential applications

Getting Started

1. Install SDK

# Add to Cargo.toml
[dependencies]
paraloom-sdk = "0.1"

Or for JavaScript/TypeScript:

npm install @paraloom/sdk

2. Connect to Network

use paraloom_sdk::Client;

#[tokio::main]
async fn main() {
    let client = Client::devnet().await?;
    println!("Connected to Paraloom devnet");
}
import { Client } from '@paraloom/sdk';

const client = await Client.devnet();
console.log('Connected to Paraloom devnet');

Private Transactions

Deposit SOL

Shield your SOL by depositing into the privacy pool:

use paraloom_sdk::{Client, Commitment};

async fn deposit_sol(client: &Client, amount: u64) -> Result<ShieldedNote> {
    // Generate random commitment
    let randomness = generate_randomness();
    let commitment = Commitment::new(amount, randomness);

    // Deposit to privacy pool
    let note = client.deposit(amount, commitment).await?;

    // Save note securely - needed for withdrawal!
    save_note(&note)?;

    Ok(note)
}

Transfer (Shielded)

Send private transfers:

async fn private_transfer(
    client: &Client,
    note: &ShieldedNote,
    recipient: &ShieldedAddress,
    amount: u64,
) -> Result<ShieldedNote> {
    // Generate proof
    let proof = client.generate_transfer_proof(note, recipient, amount)?;

    // Submit to network
    let new_note = client.submit_transfer(proof).await?;

    Ok(new_note)
}

Withdraw SOL

Unshield your SOL:

async fn withdraw_sol(
    client: &Client,
    note: &ShieldedNote,
    recipient: Pubkey,
) -> Result<Signature> {
    // Generate withdrawal proof
    let proof = client.generate_withdrawal_proof(note, recipient)?;

    // Submit to consensus (7/10 validators must approve)
    let result = client.submit_withdrawal(proof).await?;

    Ok(result.signature)
}

Private Compute

Writing WASM Jobs

Create a compute job in Rust:

// src/lib.rs
#[no_mangle]
pub extern "C" fn compute(input_ptr: *const u8, input_len: usize) -> *mut u8 {
    // Read input
    let input = unsafe {
        std::slice::from_raw_parts(input_ptr, input_len)
    };

    // Your computation here
    let result = process_data(input);

    // Return result
    let boxed = Box::new(result);
    Box::into_raw(boxed)
}

fn process_data(input: &[u8]) -> Vec<u8> {
    // Example: double each byte
    input.iter().map(|b| b.wrapping_mul(2)).collect()
}

Build:

cargo build --target wasm32-unknown-unknown --release

Submitting Compute Jobs

use paraloom_sdk::{Client, Job, JobConfig};

async fn run_compute_job(client: &Client, wasm: &[u8], input: &[u8]) -> Result<Vec<u8>> {
    let job = Job {
        wasm_code: wasm.to_vec(),
        input: input.to_vec(),
        config: JobConfig {
            memory_limit: 64 * 1024 * 1024, // 64MB
            timeout_secs: 30, // default (configurable up to 300s)
            private: false,
        },
    };

    let job_id = client.submit_job(job).await?;

    // Wait for result
    let result = client.wait_for_result(job_id).await?;

    Ok(result.output)
}

Private Compute Jobs

For confidential computation:

async fn run_private_compute(
    client: &Client,
    wasm: &[u8],
    input: &[u8],
) -> Result<Vec<u8>> {
    // Encrypt input
    let (encrypted_input, encryption_key) = encrypt_input(input)?;

    // Create commitment to input
    let input_commitment = create_commitment(&encrypted_input);

    let job = PrivateJob {
        wasm_code: wasm.to_vec(),
        encrypted_input,
        input_commitment,
        config: JobConfig {
            memory_limit: 64 * 1024 * 1024,
            timeout_secs: 30, // default (configurable up to 300s)
            private: true,
        },
    };

    let job_id = client.submit_private_job(job).await?;

    // Wait for encrypted result
    let result = client.wait_for_result(job_id).await?;

    // Decrypt output (only you can decrypt)
    let output = decrypt_output(&result.encrypted_output, &encryption_key)?;

    Ok(output)
}

Working with Proofs

Generate Custom Proofs

use paraloom_sdk::circuits::CustomCircuit;
use ark_groth16::Groth16;
use ark_bls12_381::Bls12_381;

// Define your circuit
struct MyCircuit {
    public_input: u64,
    private_witness: u64,
}

impl ConstraintSynthesizer<Fr> for MyCircuit {
    fn generate_constraints(
        self,
        cs: ConstraintSystemRef<Fr>,
    ) -> Result<(), SynthesisError> {
        // Your constraints here
        let a = FpVar::new_witness(cs.clone(), || Ok(Fr::from(self.private_witness)))?;
        let b = FpVar::new_input(cs.clone(), || Ok(Fr::from(self.public_input)))?;

        // Constraint: a * 2 == b
        let two = FpVar::constant(Fr::from(2u64));
        a.mul(&two).enforce_equal(&b)?;

        Ok(())
    }
}

// Generate proof
let circuit = MyCircuit {
    public_input: 84,
    private_witness: 42,
};
let proof = Groth16::<Bls12_381>::prove(&pk, circuit, &mut rng)?;

Verify Proofs

// Verify locally
let valid = Groth16::<Bls12_381>::verify(&vk, &public_inputs, &proof)?;
assert!(valid);

// Or submit to network for consensus verification
let consensus_result = client.verify_proof(proof, public_inputs).await?;

Error Handling

use paraloom_sdk::Error;

match client.submit_job(job).await {
    Ok(job_id) => println!("Job submitted: {}", job_id),
    Err(Error::InsufficientFunds) => println!("Need more SOL"),
    Err(Error::NetworkError(e)) => println!("Network issue: {}", e),
    Err(Error::ProofInvalid) => println!("Invalid proof"),
    Err(e) => println!("Unknown error: {}", e),
}

Best Practices

Security

Never share your private keys or shielded notes!

  • Store shielded notes securely (encrypted)
  • Use hardware wallets for high-value transactions
  • Verify proofs locally before submitting
  • Use unique randomness for each commitment

Performance

  • Batch multiple operations when possible
  • Cache proving/verifying keys
  • Use appropriate memory limits for compute jobs
  • Monitor job execution times

Privacy

  • Use different addresses for deposits/withdrawals
  • Avoid unique amounts that can be traced
  • Wait between operations to reduce timing analysis
  • Use Tor/VPN for additional network privacy

Example Applications

Private Voting

// Each voter submits encrypted vote
let vote = encrypt_vote(choice, voter_key);
let commitment = create_commitment(&vote);

let job = PrivateJob {
    wasm_code: voting_wasm,
    encrypted_input: vote,
    input_commitment: commitment,
    config: JobConfig::default(),
};

// Submit vote privately
let job_id = client.submit_private_job(job).await?;

Confidential Analytics

// Process sensitive data without revealing it
let encrypted_data = encrypt(&sensitive_data);

let job = PrivateJob {
    wasm_code: analytics_wasm,
    encrypted_input: encrypted_data,
    input_commitment: create_commitment(&encrypted_data),
    config: JobConfig::default(),
};

// Only aggregate results are revealed
let result = client.submit_private_job(job).await?;
let aggregates = decrypt(&result.output);

Resources

On this page