CoProcessor Crates
Crates and their description
The Co-Processor follows a modular architecture with the following main components:
- Core: Core library which exposes majority of function used by other crates like processor, batcher, indexer for computation
- Processor: Main service which runs within TEE, contains method to decrypt and process on user requests.
- Batcher: Consumes events from relayer kafka queue, Does some preprocessing over the requests and construct batches of it which is later relayer to processor for processing.
- Indexer: Indexes the updated ciphertexts and works as a caching mechanism for fetching ciphertext against pointer.
- Chain Client: Contains method which is used by submitter to submit batch root to blockchain.
- Submitter: Contains methods to submit batch root onchain and relevant batch tree related data on Data storage layer like Avail
End-to-End Flow
Use Case: Alice wants to confidentially transfer 100 USDC to Bob. The amounts stay encrypted throughout the entire process.
How It Works
Here's how the Co-Processor handles confidential computations on encrypted data:
Encryption & Storage
Data gets encrypted client-side before it ever touches the network. The system uses a combination of Modified ElGamal for the secret key and ChaCha20Poly1305 for the actual plaintext.
Each encrypted value gets a unique 16-byte handle generated using a cryptographically secure random number generator. This handle is what gets passed around everywhere - on-chain, between services, etc. The actual ciphertext is cached in the Indexer, and the Processor signs it with a TEE attestation.
On-Chain Operations
Encrypted tokens get wrapped on-chain. The smart contracts (eWrapper, eToken, Computation Executor) only see and store handles - never the actual values. These contracts leverage privacy-enhancing technologies (PET) to enable confidential computations over encrypted state.
Every operation (transfer, swap, balance check) triggers the contract to emit an event/transaction with a RequestParams struct. This captures everything needed: the operand handles (lhs, rhs), what operation to perform (Add, Subtract, Multiply, Equal, LessEqual, IfThenElse - we support a bunch), some flags, and who requested it. The blockchain state never touches plaintext.
Event Batching
The Relayer watches the blockchain and grabs these events, then queues them up in Kafka. The Batcher pulls events from the queue, fetches the actual ciphertexts from Indexer, and validates ACL permissions and on-chain commitments.
Then it builds a BatchData structure with groups of requests. Each group has the inputs with Merkle inclusion proofs, an expression string (like "A + B"), and the request details. Batches max out at 100MB, which is plenty for most use cases.
TEE Computation
The Processor receives the batch and performs threshold decryption. Instead of decrypting each value individually, it performs a single batch threshold decryption using distributed KMS nodes, regardless of batch size.
The decryption will happen over across multiple Thetacrypt nodes in a k-of-n setup. A randomly selected leader node will collect the partial decrypts, validates their attestations, and combines them. Once plaintext is recovered, the actual computation runs inside the TEE. The plaintext gets destroyed immediately after computation - it only exists for milliseconds.
Results get re-encrypted with the network key, new handles are generated (deterministically, using the operation metadata and input handles plus chain ID), and everything gets packaged into a BatchTree - a Merkle tree using Keccak256 hashing. The new handle-to-ciphertext mappings and BatchTree with proofs are cached in the Indexer.
On-Chain Commitment
The results will be committed back to the blockchain. The system grabs up to 300 BatchTree roots from the queue and feeds them into a Bn254 elliptic curve accumulator. This creates a single cryptographic commitment representing all those batches.
The accumulator value gets hashed with SHA256 and hex-encoded with "0x" prefix to create a 32-byte root that's compatible with secp256k1_recover (needed for on-chain signature verification). The TEE signs this root, the batch data gets compressed and archived to a DA layer (TurboDA/Avail or Arweave for permanent storage), and finally the accumulator root plus signature gets submitted on-chain where anyone can verify it.
Decryption
To view results, users generate an ephemeral secp256k1 keypair for the request. A timestamped payload gets signed with the Ed25519 wallet key to prove ownership.
The Processor validates the signature (must be within 3600 seconds from timestamp), fetches the ciphertexts, does another batch threshold decryption via KMS, then re-encrypts each plaintext individually to the ephemeral public key using ECIES. Results come back hex-encoded, and the SDK decrypts them client-side with the ephemeral private key. The ephemeral key gets discarded after, so even if someone intercepts the encrypted response, it's useless.