SDK Architecture
SDK Architecture - Technical Deep Dive
This document provides a comprehensive technical overview of the Roru SDK architecture, including detailed component descriptions, data flows, and implementation details.
Architecture Overview
SDK Structure
The Roru SDK is organized into four distinct layers, each with specific responsibilities:
┌─────────────────────────────────────────┐
│ Application Layer │
│ (Your Application Code) │
└──────────────┬────────────────────────────┘
│
┌──────────────▼────────────────────────────┐
│ API Layer │
│ - TransactionBuilder │
│ - Wallet API │
│ - ProofGenerator │
│ - StateSync │
│ - OfflineTransfer │
└──────────────┬────────────────────────────┘
│
┌──────────────▼────────────────────────────┐
│ Core Layer │
│ - Note Management │
│ - Commitment Operations │
│ - Nullifier Generation │
│ - Merkle Tree Operations │
│ - Key Management │
└──────────────┬────────────────────────────┘
│
┌──────────────▼────────────────────────────┐
│ Protocol Layer │
│ - Shielded State Tree │
│ - ZK Circuit Execution │
│ - Proof Generation/Verification │
│ - Transaction Validation │
└──────────────┬────────────────────────────┘
│
┌──────────────▼────────────────────────────┐
│ Network Layer │
│ - Encrypted RPC Client │
│ - WebSocket Connections │
│ - State Synchronization │
│ - Transaction Broadcasting │
└────────────────────────────────────────────┘Layer Responsibilities
API Layer:
Provides high-level, developer-friendly interfaces
Handles input validation and error formatting
Manages async operations and callbacks
Abstracts protocol complexity
Core Layer:
Implements cryptographic primitives
Manages local state and caching
Handles key derivation and storage
Performs note selection and management
Protocol Layer:
Executes zero-knowledge circuits
Generates and verifies proofs
Manages shielded state tree
Validates transactions
Network Layer:
Handles all network communication
Manages connection pooling and retries
Implements encryption and authentication
Synchronizes state with infrastructure
Core Components
RoruClient
The RoruClient is the main entry point for SDK operations. It manages connections, configuration, and provides access to all SDK functionality.
Rust Implementation:
use roru_sdk::prelude::*;
pub struct RoruClient {
config: ClientConfig,
network_client: NetworkClient,
state_manager: StateManager,
key_manager: KeyManager,
proof_generator: ProofGenerator,
}
impl RoruClient {
/// Create a new Roru client with configuration
pub async fn new(config: ClientConfig) -> Result<Self> {
// Initialize network client with encrypted connection
let network_client = NetworkClient::new(
config.infra_endpoint.clone(),
config.api_key.clone(),
).await?;
// Initialize state manager with local cache
let state_manager = StateManager::new(
config.state_cache_path.clone(),
network_client.clone(),
).await?;
// Initialize key manager with secure storage
let key_manager = KeyManager::new(
config.key_storage_path.clone(),
)?;
// Initialize proof generator with circuit keys
let proof_generator = ProofGenerator::new(
config.proving_key_path.clone(),
)?;
Ok(Self {
config,
network_client,
state_manager,
key_manager,
proof_generator,
})
}
/// Create a new wallet instance
pub async fn create_wallet(&self) -> Result<Wallet> {
// Generate master key
let master_key = self.key_manager.generate_master_key()?;
// Derive wallet keys
let spending_key = self.key_manager.derive_spending_key(&master_key)?;
let viewing_key = self.key_manager.derive_viewing_key(&master_key)?;
// Create wallet with keys
Wallet::new(
spending_key,
viewing_key,
self.state_manager.clone(),
self.proof_generator.clone(),
self.network_client.clone(),
)
}
/// Load existing wallet from keys
pub async fn load_wallet(&self, master_key: &MasterKey) -> Result<Wallet> {
let spending_key = self.key_manager.derive_spending_key(master_key)?;
let viewing_key = self.key_manager.derive_viewing_key(master_key)?;
Wallet::new(
spending_key,
viewing_key,
self.state_manager.clone(),
self.proof_generator.clone(),
self.network_client.clone(),
)
}
/// Get current state root
pub async fn get_state_root(&self) -> Result<StateRoot> {
self.state_manager.get_current_root().await
}
/// Sync state with network
pub async fn sync_state(&self) -> Result<SyncResult> {
self.state_manager.sync().await
}
}TypeScript Implementation:
import { RoruClient, ClientConfig, Wallet } from '@roru/sdk';
export class RoruClient {
private config: ClientConfig;
private networkClient: NetworkClient;
private stateManager: StateManager;
private keyManager: KeyManager;
private proofGenerator: ProofGenerator;
constructor(config: ClientConfig) {
this.config = config;
// Initialize components
this.networkClient = new NetworkClient(config.infraEndpoint, config.apiKey);
this.stateManager = new StateManager(config.stateCachePath, this.networkClient);
this.keyManager = new KeyManager(config.keyStoragePath);
this.proofGenerator = new ProofGenerator(config.provingKeyPath);
}
async createWallet(): Promise<Wallet> {
const masterKey = await this.keyManager.generateMasterKey();
const spendingKey = await this.keyManager.deriveSpendingKey(masterKey);
const viewingKey = await this.keyManager.deriveViewingKey(masterKey);
return new Wallet(
spendingKey,
viewingKey,
this.stateManager,
this.proofGenerator,
this.networkClient
);
}
async loadWallet(masterKey: MasterKey): Promise<Wallet> {
const spendingKey = await this.keyManager.deriveSpendingKey(masterKey);
const viewingKey = await this.keyManager.deriveViewingKey(masterKey);
return new Wallet(
spendingKey,
viewingKey,
this.stateManager,
this.proofGenerator,
this.networkClient
);
}
async getStateRoot(): Promise<StateRoot> {
return await this.stateManager.getCurrentRoot();
}
async syncState(): Promise<SyncResult> {
return await this.stateManager.sync();
}
}Wallet
The Wallet component manages user funds, transactions, and state synchronization.
Rust Implementation:
pub struct Wallet {
spending_key: SpendingKey,
viewing_key: ViewingKey,
state_manager: StateManager,
proof_generator: ProofGenerator,
network_client: NetworkClient,
local_state: LocalState,
}
impl Wallet {
pub fn new(
spending_key: SpendingKey,
viewing_key: ViewingKey,
state_manager: StateManager,
proof_generator: ProofGenerator,
network_client: NetworkClient,
) -> Result<Self> {
let local_state = LocalState::new();
Ok(Self {
spending_key,
viewing_key,
state_manager,
proof_generator,
network_client,
local_state,
})
}
/// Generate a new shielded address
pub fn generate_address(&self) -> Result<ShieldedAddress> {
let address_index = self.local_state.next_address_index();
let address = self.viewing_key.derive_address(address_index)?;
self.local_state.add_address(address.clone());
Ok(address)
}
/// Get shielded balance
pub async fn get_balance(&self) -> Result<Balance> {
// Sync state first
self.sync_state().await?;
// Calculate balance from local notes
let notes = self.local_state.get_notes(&self.viewing_key)?;
let balance = notes.iter()
.map(|note| note.value)
.sum();
Ok(Balance {
shielded: balance,
unconfirmed: 0,
})
}
/// Send a private transaction
pub async fn send(
&self,
recipient: ShieldedAddress,
amount: u64,
) -> Result<TransactionResult> {
// Sync state to get latest notes
self.sync_state().await?;
// Select input notes
let input_notes = self.select_notes(amount)?;
// Create output notes
let change = self.calculate_change(&input_notes, amount)?;
let mut outputs = vec![
Note::new(amount, recipient.clone(), &self.viewing_key)?,
];
if change > 0 {
let change_address = self.generate_address()?;
outputs.push(Note::new(change, change_address, &self.viewing_key)?);
}
// Build transaction
let tx = TransactionBuilder::new()
.inputs(input_notes)
.outputs(outputs)
.build()?;
// Generate proof
let proof = self.proof_generator.generate_proof(&tx).await?;
// Sign transaction
let signed_tx = self.sign_transaction(tx, proof)?;
// Broadcast transaction
let tx_hash = self.network_client.broadcast_transaction(&signed_tx).await?;
Ok(TransactionResult {
tx_hash,
status: TransactionStatus::Pending,
})
}
/// Sync state with network
pub async fn sync_state(&self) -> Result<SyncResult> {
// Get current state root from network
let network_root = self.network_client.get_state_root().await?;
let local_root = self.local_state.get_state_root();
if network_root != local_root {
// Download incremental updates
let updates = self.network_client.get_state_updates(
local_root,
network_root,
).await?;
// Apply updates to local state
for update in updates {
self.local_state.apply_update(update, &self.viewing_key)?;
}
}
Ok(SyncResult {
synced: true,
new_notes: self.local_state.get_new_notes_count(),
})
}
fn select_notes(&self, amount: u64) -> Result<Vec<Note>> {
let available_notes = self.local_state.get_notes(&self.viewing_key)?;
// Greedy selection algorithm
let mut selected = Vec::new();
let mut total = 0u64;
for note in available_notes {
if total >= amount {
break;
}
selected.push(note);
total += note.value;
}
if total < amount {
return Err(Error::InsufficientBalance);
}
Ok(selected)
}
fn sign_transaction(
&self,
tx: Transaction,
proof: Proof,
) -> Result<SignedTransaction> {
// Create transaction bundle
let bundle = TransactionBundle {
transaction: tx,
proof,
signature: self.spending_key.sign(&tx)?,
};
Ok(SignedTransaction { bundle })
}
}Module Structure
Transaction Module
Purpose: Build and manage private transactions
Key Types:
pub struct TransactionBuilder {
inputs: Vec<InputNote>,
outputs: Vec<OutputNote>,
fee: Option<u64>,
memo: Option<Vec<u8>>,
}
pub struct Transaction {
pub inputs: Vec<Input>,
pub outputs: Vec<Output>,
pub proof: Proof,
pub nullifiers: Vec<Nullifier>,
pub public_data: PublicData,
}Proof Module
Purpose: Generate and verify zero-knowledge proofs
Key Types:
pub struct ProofGenerator {
proving_key: ProvingKey,
circuit: Circuit,
}
pub struct Proof {
pub a: Point,
pub b: Point,
pub c: Point,
pub public_inputs: Vec<Scalar>,
}State Module
Purpose: Manage and synchronize shielded state
Key Types:
pub struct StateManager {
local_cache: StateCache,
network_client: NetworkClient,
state_tree: StateTree,
}
pub struct StateSync {
current_root: StateRoot,
updates: Vec<StateUpdate>,
}Network Module
Purpose: Handle network communication
Key Types:
pub struct NetworkClient {
endpoint: String,
api_key: String,
http_client: HttpClient,
ws_client: Option<WebSocketClient>,
}
pub struct NetworkResponse<T> {
pub data: T,
pub status: u16,
pub headers: HashMap<String, String>,
}Data Flow
Transaction Creation Flow
User calls wallet.send()
│
├─> Sync state (get latest notes)
│
├─> Select input notes
│
├─> Create output notes
│
├─> Build transaction
│
├─> Generate proof
│
├─> Sign transaction
│
└─> Broadcast to networkState Synchronization Flow
wallet.sync_state()
│
├─> Get network state root
│
├─> Compare with local root
│
├─> Request incremental updates
│
├─> Download Merkle proofs
│
├─> Verify proofs
│
└─> Update local stateConfiguration
ClientConfig
Rust:
pub struct ClientConfig {
pub infra_endpoint: String,
pub api_key: Option<String>,
pub state_cache_path: PathBuf,
pub key_storage_path: PathBuf,
pub proving_key_path: PathBuf,
pub network_id: NetworkId,
pub timeout: Duration,
pub retry_config: RetryConfig,
}TypeScript:
interface ClientConfig {
infraEndpoint: string;
apiKey?: string;
stateCachePath: string;
keyStoragePath: string;
provingKeyPath: string;
networkId: NetworkId;
timeout?: number;
retryConfig?: RetryConfig;
}Error Handling
Error Types
#[derive(Debug, Error)]
pub enum RoruError {
#[error("Network error: {0}")]
Network(#[from] NetworkError),
#[error("Insufficient balance")]
InsufficientBalance,
#[error("Invalid transaction: {0}")]
InvalidTransaction(String),
#[error("Proof generation failed: {0}")]
ProofGeneration(String),
#[error("State sync failed: {0}")]
StateSync(String),
#[error("Key management error: {0}")]
KeyManagement(#[from] KeyError),
}Performance Considerations
Caching
State cache: Reduces network calls
Proof cache: Reuses proofs when possible
Address cache: Fast address lookups
Optimization
Batch operations: Multiple transactions at once
Parallel proof generation: Multiple proofs concurrently
Incremental sync: Only download changes
Conclusion
The SDK architecture provides:
Modularity: Clear separation of concerns
Extensibility: Easy to add new features
Performance: Optimized for production use
Developer Experience: Clean, intuitive APIs
Security: Secure key management and operations
Understanding the SDK architecture is essential for effective development with Roru Labs.
Last updated
