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/sdk2. 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(¬e)?;
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 --releaseSubmitting 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);