Processor
Processor service responsibilities, endpoints, data formats, and integrations
Processor
Overview
The processor is the main service orchestrating the secure decrypt → compute → encrypt pipeline using the Core crate. It exposes HTTP endpoints to process batches of operations, re-encrypt results for clients, accept new client-encrypted ciphertexts for indexing, and provides operational routes for health and database maintenance.
Key responsibilities
- Execute compute operations by coordinating with Core primitives and Threshold KMS for decryption.
- Build merkle-committed
BatchTrees from processed results and push them to the Indexer. - Accumulate multiple
BatchTreemerkle roots into a BN254 accumulator root and periodically submit the signed commitment to the Submitter (DA + on-chain). - Provide re-encryption service: after verifying client session and signature, batch-decrypt input ciphertexts and re-encrypt the plaintexts using a client-provided ephemeral public key (ECIES).
- Maintain durable Sled databases for queueing and key-value data, with explicit periodic flushing and snapshot/restore utilities.
Services
- API service:
process-batch,re-encrypt,timestamp,health. - Encryption service:
store-ciphertext,health.
Attestation & signing
- Uses
tee::sign::tee_signto produce ECDSA signatures for batch hashes, merkle roots, and accumulator roots. See official docs for deployment via Marlin TEE and init-params usage.
ACL Workflow
The Processor enforces offchain access control for encrypted handles using the KvDB (ACL database). The following diagram shows the three main ACL operations:
ACL Operation Details:
-
SetAccess (Grant Permission)
- Input: accessor pubkey, handle value, allow flag
- If allow=true: stores permission mapping in KvDB
- If allow=false: no-op (permission not granted)
-
Compute (Verify & Auto-grant)
- Verifies caller has permission via
get_handle_permission(signer, value) - Executes computation if permission exists
- Automatically grants ownership of result_handle to signer
- Verifies caller has permission via
-
IsAllowed (Permission Check)
- Query-only operation to check if accessor has permission for handle
- Returns boolean without modifying state
Endpoints
POST /v1/process-batch
- Request body:
BatchData(see Data Formats). - Processing steps:
- Sign
batch_hashwithtee_signby converting the U256 batch hash to string representation; the response includes the signature asack. - For each
Groupin order:- Tokenize the group expression and map to an
Operation/OperationType. - Construct
OperationParamsfor the specific operation (arithmetic/relational/control/bitwise/storage, or specialized operations like anon-transfer, confidential↔anon, decrypt). - Acquire a cipher client:
- Local mode (
local-cipherfeature): in-process Threshold ElGamal client viaget_kms_client_local(). - Production mode: pooled KMS client (
get_pooled_kms_client) usingthreshold_kms.production_peersfrom config for multi-node threshold decryption.
- Local mode (
- Call Core ops to decrypt, compute, and re-encrypt the result. Grant ACL for the resultant handle to the group signer.
- Propagate newly produced ciphertext to subsequent groups in the same batch when they reference the resultant handle.
- Tokenize the group expression and map to an
- Build a
BatchTreefrom the processed results (leaves areRequestAndCiphertext), compute its merkle root using a fixed 4-level merkle tree, and push the tree tobatch_tree_db(QueueDB). - Immediately call the internal Indexer update to push merkle data and leaves. In debug builds, this writes to a local Sled
dev_db; in production, it calls Indexer HTTP.
- Sign
- Response (200):
{ "status": "success", "message": "Batch processed successfully", "ack": "<hex tee signature over batch_hash>", "batch_hash": "<string>", "batch_tree_root": "<hex>", "groups_processed": <number> }
Notes
- Body size limit for this endpoint is 100MB predefined.
ackis the TEE signature over the stringifiedbatch_hash(U256 converted to string).- ACL enforcement: when an operand corresponds to a ciphertext with non-zero Big-R, the processor verifies
get_handle_permission(kv_db, handle, signer)before using it; missing permission short-circuits the group. - Cache: Intermediate plaintext results are cached in memory (HashMap) during batch processing to avoid repeated decryption within the same batch. The cache is backed by a Sled database named
cache_dbfor persistence.
Batch Processing Workflow
The complete flow from receiving BatchData to submitting results shows the orchestration across multiple services:
Processing Steps:
- Receive BatchData with groups from Batcher
- Sign batch_hash with TEE attestation key
- For each group: decrypt operands → compute → re-encrypt result
- Build fixed 4-level Merkle tree from processed results
- Queue BatchTree for accumulator submission
- Push tree and leaves to Indexer for ciphertext storage
- Periodic accumulator job submits batches to Submitter when queue ≥ 300 trees
- Return acknowledgement with TEE signature
Input: BatchData (from Batcher) Output: Signed batch acknowledgement + BatchTree (queued for submission)
POST /v1/re-encrypt
- Request body:
BatchDecryptRequest. - Validation:
- Ed25519 signature over a deterministic JSON payload hash (
ReEncryptPayload), verified withuser_pk. - Session window checks:
start_time_stamp < now < end_time_stampand max duration withinSESSION_VALIDITY_THRESHOLD(3600 seconds = 1 hour). - Payload hash construction mirrors SDK JSON.stringify semantics and uses camelCase key names (e.g.,
ephemeralPubKey).
- Ed25519 signature over a deterministic JSON payload hash (
- Processing steps:
- Fetch the ciphertexts for all
handlesin batch:- Local test mode (checked via
config_loader::is_local_test_mode()): read from Sleddev_db. - Production: call Indexer's
get-ciphertext.
- Local test mode (checked via
- Perform a single batch threshold decryption for all ciphertexts (one threshold session via
decrypt_ciphertext_batch). - For each plaintext, perform individual ECIES re-encryption using the client's
ephemeral_pub_key(non-batch by design viaencrypt_with_pubkey).
- Fetch the ciphertexts for all
- Response (200):
{ "results": [ {"handle": "<string>", "result": "<hex>", "status": "success"} | {"status": "error", ...} ], "user_pk": "<hex>", "ephemeral_pub_key": "<hex>", "session_info": { "start_time": <u64>, "end_time": <u64>, "decryption_type": <0|1> } }
POST /v1/store-ciphertext (encryption service)
- Request body:
CiphertextStorageRequest. - Processing steps:
- Parse
big_r(33-byte point) andcts(hex) and constructCiphertextWithCts. - Compute a deterministic handle:
handle = append_type(first_16_bytes(SHA256(cts || big_r)), data_type).- SHA256 hash is computed by updating hasher with cts string then big_r string.
- First 16 bytes converted to u128 via little-endian interpretation.
- Data type appended via
append_typeto produce final handle.
- Sign the
CiphertextWithCtshash viatee_signand forward to Indexerset-ciphertext. - Return the stringified handle.
- Parse
- Response (200): handle as JSON string.
Notes
big_rmust be exactly 33 bytes; invalid sizes are rejected.
GET /v1/timestamp
- Returns the current processor time in milliseconds and an ISO string.
GET /v1/health
- Health document with uptime, features (
local-ciphervs KMS), capabilities, and build system info.
Batch Accumulation & Submission
- Staging: each
BatchTreeis pushed intobatch_tree_dbafter processing. - Periodic job:
accumulate_and_sendruns continuously in a loop withprocessor.submitter_submission_intervalsecond intervals:- Attempts to pop up to
BATCH_TREE_LENGTH(constant value 300) trees frombatch_tree_db. The loop stops early if the queue is empty before reaching 300 trees. - Adds each merkle root into a BN254 accumulator via
accumulator.add_member. - Produces a 32-byte accumulator root by SHA-256 hashing the textual accumulator field (converted to string via
to_string()). - Signs the root with
tee_sign(signature + recovery id). - Builds
BatchTreeSubmission { acc, signature, recovery_id, batch_trees, da_submission_id: None }and POSTs to Submittersubmit_batch. - On success, updates in-memory
AccumulatorStateby writing the new accumulator.
- Attempts to pop up to
Notes
- The
da_submission_idfield is set toNonein the processor; the submitter populates this field after DA submission.
Indexer Update
- Immediate update: after constructing a
BatchTree, the processor invokes an Indexer update. - Payload:
SetTreeAndLeavesRequestcontainingleaf_hashes,merkle_root,signature(TEE signature over root), andbatch_requestswith eachPreComputedCiphertextRequest(ciphertext, hash, cts, resultant handle, leaf hash, index, original request). - Mode:
- Debug/dev: write each resultant handle → ciphertext to local Sled
dev_db. - Production: POST to Indexer’s
set-batch-treeroute.
- Debug/dev: write each resultant handle → ciphertext to local Sled
Server
- Framework:
warp. - Two services on separate ports from config:
- API service:
process-batch,re-encrypt,timestamp,health, DB utilities. - Encryption service:
store-ciphertext,health.
- API service:
- Binding: listens on
127.0.0.1:<processor.processor_service_port>and127.0.0.1:<processor.encryption_service_port>. - Durability: periodic DB flush task runs every 30 seconds across
batch_tree_db,indexer_db, andkv_db. - Graceful shutdown: flushes all databases before exit.
Public Interfaces
HTTP
- API service routes:
POST /v1/process-batch— body: JSON →BatchData.POST /v1/re-encrypt— body: JSON →BatchDecryptRequest.GET /v1/timestamp,GET /v1/health.
- Encryption service routes:
POST /v1/store-ciphertext— body: JSON →CiphertextStorageRequest.GET /v1/health.
KMS/cipher
- Trait:
Cipher(encrypt, decrypt, decrypt_batch) implemented by local Threshold ElGamal (test) and distributed KMS client. - Pooling:
PooledKMSClientsmart pointer with RAII automatic return to pool to reduce per-request overhead (default pool size 5).- Pool initialized once via
lazy_static!andOnceLockpattern. PooledKMSClientimplementsCiphertrait via delegation to innerKMSClient.- Automatic cleanup on drop returns client to pool or frees it if pool is full.
- Pool initialized once via
- Mode detection:
- Local test mode: uses
get_kms_client_local()which returns a staticThresholdElgamalCipherstored inOnceLock. - Production mode: uses
get_pooled_kms_clientwith configuredproduction_peers.
- Local test mode: uses
- Legacy fallback: if production peers are not found in config, the processor falls back to loading a legacy client JSON from
CONFIG_FILE(defaultconfig.json).
Indexer integration
- Local utilities:
local_fetch_ciphertext,local_fetch_multiple_ciphertextsare internal helper functions for debug mode that read fromdev_db(not exposed API methods). - Push:
set-ciphertextandset-batch-treeviasend_to_indexermodule.
Submitter integration
- Accumulator job posts
BatchTreeSubmissionto Submitter/v1/submit_batchwith retry handled at the Submitter.
TEE signing
tee::sign::tee_sign(&str) -> (Vec<u8>, u8)returns signature bytes and recovery id. Used for batch hashes, merkle roots, and accumulator roots.
Storage
QueueDB(Sled) underbatch_tree_dbandindexer_db.KvDB(Sled) underkv_dbfor ACL and app state.cache_db(Sled): opened by theCachestruct but primary caching uses in-memory HashMap for handle → plaintext mappings to avoid repeated decrypts within processing. Sled provides persistence backing.- DB directories are created under the working directory:
batch_tree_db/,indexer_db/,kv_db/, andcache_db/. - Debug mode: additional
dev_db(Sled) stores ciphertexts for local testing without running a separate indexer service.
Configuration
Processor (config.toml)
[processor]
processor_service_port = 8081
encryption_service_port = 8083
host = "127.0.0.1"
submitter_submission_interval = 300
indexer_submission_interval = 300
indexer_url = "http://127.0.0.1:8080"
submitter_url = "http://127.0.0.1:8082"
[processor.backup]
enabled = false
interval_seconds = 0
prefix = ""
restore_on_boot = falseThreshold KMS
[threshold_kms]
mode = "local-test" # or "production"
# Only for production
[[threshold_kms.production_peers]]
id = 1
ip = "10.0.0.2"
rpc_port = 7000
[[threshold_kms.production_peers]]
id = 2
ip = "10.0.0.3"
rpc_port = 7000Local test short‑circuits to an in‑process cipher. Production creates pooled KMS clients using the configured peers.
Indexer
[indexer]
url = "http://127.0.0.1:8080"
port = 8080Submitter
[submitter]
port = 8082The processor uses processor.submitter_url for HTTP calls to submitter.
Chain context
[chain]
chain_id = 84532
acl_address = "0x..."TEE & Marlin
[tee]
mock_signature = true
# app_id, image_variant, domain_string, build_hash, image_id, kms_url are optionalOfficial deployment mounts init‑params at /init-params; see officialDocs/notionDocs.
Environment
- Feature
local-cipherswitches to local encryption/decryption for development and tests.- When enabled: uses in-process
ThresholdElgamalCipherinitialized once viaOnceLock. - When disabled: uses distributed KMS clients from pool with configured production peers.
- When enabled: uses in-process
- Configuration loading: CLI arguments for
chainidandacl_addressare parsed but overridden by values fromconfig.toml.
Error Handling
- HTTP returns
warp::reject::custom(..)with a concise message. Custom error types:ApiCustomError,HexDecodeError, plus structured validation errors for the re-encryption route. - Re-encryption returns 400 with JSON error details for signature/session failures.
- DB snapshot/restore validates payloads, uses tar.gz with safe path handling and atomic rename.
- Periodic DB flush logs errors per DB and continues without halting the service.
- Invalid inputs are rejected early (e.g., big_r byte length validation, signature verification failures).
Tests
- Unit tests (processor/tests)
- Arithmetic, comparison, bitwise, complex ops
- Anon transfer, confidential↔anon flows, decrypt ops
- Verify ciphertext and set access
- E2E test (
final_e2e_test.rs,local-cipherfeature)- Verifies services up, TEE signing, Indexer store/fetch,
process-batch,timestamp, and mocked DA + Solana submission via chain client.
- Verifies services up, TEE signing, Indexer store/fetch,
- Re‑encryption validation tests
- Payload structure, Ed25519 signature, hash determinism, end‑to‑end validation pipeline.
Notes
- No WebSocket interface; all routes are HTTP under
/v1. - Public keys and request encodings
scalar_byteusage follows Core's conventions; batch decryption uses scalar bytes of zero by default.
- Attestation
- The processor does not perform attestation verification itself; signing and enclave-bound secrets are managed by the TEE runtime.
- Merkle tree construction
- All batch trees use a fixed depth of 4 levels (hardcoded in
get_batch_tree_from_batch).
- All batch trees use a fixed depth of 4 levels (hardcoded in
- Cache architecture
- In-memory HashMap stores intermediate plaintext results during batch processing.
- Sled
cache_dbprovides persistence backing for the cache.
- Debug mode differences
- Uses local
dev_dbfor ciphertext storage instead of calling indexer HTTP endpoints. - Enables immediate indexer updates after batch tree construction.
- Uses local