Core
Core crate responsibilities, design, interfaces, and workflows
Overview & Design
System Overview
The encifher-core crate provides computation engine, core data types, cryptographic interfaces, and Sled-based storage utilities for Encifher coprocessor. Smart contracts operate on 16-byte symbolic references (handles) instead of raw ciphertexts. Core implements the decrypt → compute → encrypt pipeline and deterministic handle derivation used by other coprocessor services.
Core is a library crate which does not run any services.
Capabilities
-
Computation engine
Core compute operations :
- Arithmetic: Add, Subtract, Multiply, Divide, Reminder
- Relational: Equal, LessEqual, LessThan, NotEqual, GreaterEqual, GreaterThan
- Bitwise: BitAnd, BitOr, BitXor, ShiftLeft, ShiftRight, RotLeft, RotRight
- Control flow: IfThenElse
- Unary: Negate, Not
- Min/Max: Minimum, Maximum
Additional Operation variants :
- Special: TrivialEncrypt, VerifyCiphertext, Cast
- Access control: SetAccess, IsAllowed
- Decryption: Decrypt
-
Cryptographic interface
Ciphertrait with local Threshold-ElGamal implementation- Single and batch decryption helpers with fallback mechanisms
-
Core data types
Handle(u128 type alias)CiphertextWithCts: Container for encrypted data with CTS metadataRequestParams: Request with handles and operation metadataBatchData: Collection of groups for batch processingGroup: Computation group with inputs, expression, and outputsBatchTree: Merkle tree over request-ciphertext pairsRequestAndCiphertext: Combined request and encrypted result
-
Storage utilities
KvDB: Key-value database wrapper on Sled with explicit flush
-
Event ingestion
- EventListener for Helius Laserstream integration
- Kafka publishing capabilities via events module
-
Clients
IndexerClient: For retreiving ciphertexts and updating computed ciphertexts.KafkaClient: Generic producer/consumer for managing onchain computation requests.
Module Organization
- Cryptography:
accumulator,kms,verifier - Compute/operations:
compute,ops - Storage/data:
db,types,codec,constants - Integration:
client,events,tee - Utilities:
utils(handle derivation, encoding helpers, parsing) - Request handling:
request(request type definitions)
Architecture & Workflows
Computation Flow
- Inputs: Callers pass serialized operands and per-operand flags that indicate scalar (plaintext) vs ciphertext
- Decrypt: Non-scalar operands are decrypted via
Ciphertrait (single or batched) - Compute: Plaintext operations execute in process (arithmetic, comparisons, bitwise, control flow)
- Output: Callers may encrypt results (
encrypt_result) and derive typed handles. Core does not persist results to external services.
Decryption helpers use scalar_byte to distinguish plaintext from encrypted inputs :
- Value 1: treat as plaintext, skip decryption
- Value 0: encrypted data, decrypt via Cipher trait
Data Model and Cryptographic Flow
Ciphertext Container
CiphertextWithCts encodes encrypted data with CTS metadata :
- Encoding format:
[4-byte LE cts_len] || cts || ciphertext_bytes - Hashing uses Keccak256 over
cts || ciphertext_bytes - Strict deserialization with length validation (panics on invalid input)
Batch and Expression Model
BatchData contains one or more computation groups with validation:
- Required non-zero
batch_hashidentifier - Validation enforces :
- At least one group present
- Expression syntax via regex
^[a-z0-9AB_+\-*/><!&|^=?:() ]+$ - Balanced parentheses and correct operator/operand arity
- Merkle proof verification against root hash
- ECDSA signature verification over root_hash
Group represents single computation unit :
- List of inputs with handles and optional ciphertexts
- Expression string defining computation
- Signer pubkey for authorization
- Output handle and intermediate handles
- Associated request parameters
- Tokenizer for expression parsing with special handling for operations like
decrypt,set_access
GroupInput provides per-input metadata :
- Handle identifier
- Optional ciphertext data
- Merkle proof components (leaf hash, inclusion proof, root hash)
- DER-encoded ECDSA signature for batch validation
- Timestamp
BatchTree aggregates results :
- Merkle tree (using Keccak256) over request-ciphertext pairs
- Uses
SimpleMerkleTreefrom chain_client crate
RequestParams encapsulates operation details :
- Request ID and signer pubkey
- LHS and RHS handles
- Operation type and scalar flags
- Optional fields: middle, anon_transfer_handles, decrypt_handles, access_handles, balance_conversion_handles
Signature Verification
Batch Validation :
- Signatures are DER-encoded ECDSA over
root_hash(32 bytes) - Verification uses secp256k1 public key from
TEE_PUBLIC_KEYenvironment variable - Public key format: 65-byte uncompressed (0x04 prefix)
Signature::from_der(&signature)withmessage = input.root_hash.to_vec()
IndexerClient Fetches :
- When merkle root signature is present: compact 64-byte ECDSA signature
- Verified against
TEE_PUBLIC_KEYbefore accepting response - Signature is over returned
merkle_root(32 bytes) Signature::from_compact(&signature)
Public Interfaces
Cipher Trait
The Cipher trait defines encryption/decryption interface :
pub trait Cipher {
fn encrypt(&self, data: &[u8]) -> Result<CiphertextWithCts, Box<dyn std::error::Error>>;
async fn decrypt(&mut self, cipher: CiphertextWithCts) -> Result<Vec<u8>, Box<dyn std::error::Error>>;
async fn decrypt_batch(&mut self, cts: Vec<CiphertextWithCts>) -> Result<Vec<Vec<u8>>, Box<dyn std::error::Error>>;
}IndexerClient
Client for retrieving ciphertexts from indexer service :
- Configured with indexer base URL
- Methods:
indexer_fetch_ciphertext(single) andindexer_fetch_multiple_ciphertexts(batch)
Behavior :
- Scalar mode (
scalar_byte == 1): Returns 16-byte big-endian handle as plaintext - Encrypted mode (
scalar_byte == 0):- Calls
POST /v1/get-ciphertextendpoint - Verifies compact 64-byte ECDSA signature over merkle root (if present)
- Returns serialized
CiphertextWithCtsorvec![0; 32]sentinel if not found
- Calls
- Read-only client; writes handled by processor/indexer services
KafkaClient
Generic Kafka client for producing/consuming JSON messages :
- Type parameter
Tmust be JSON serializable/deserializable - Can be configured as consumer OR producer (not both, mutually exclusive)
- Consumer:
with_consumer(broker, group_id, topic)streams messages - Producer:
with_producer(broker)sends JSON messages to topics - Auto-serializes/deserializes messages
BN254 Accumulator
Cryptographic accumulator for membership proofs :
- Manages G1/G2 group elements and accumulator state
- Methods:
new,add_member,membership_witness,verify_membership hash_to_scalar: Converts arbitrary data to BN254 field elements via Keccak256verify_membership_solana: Uses Solana'salt_bn128_pairingsyscall for on-chain verification- Accumulator value is product of all added member scalars
TEE Helper
Provides access to TEE public key :
get_public_key(): Returns static reference to TEE public key bytes- Reads from
TEE_PUBLIC_KEYenvironment variable (hex-encoded) - Expected format: 65-byte uncompressed secp256k1 (0x04 prefix)
- Cached in
OnceLockfor efficiency
pub fn get_public_key() -> &'static Vec<u8> {
TEE_PUBLIC_KEY.get_or_init(|| {
let public_key = std::env::var("TEE_PUBLIC_KEY").expect("Failed to get public key");
hex::decode(public_key).expect("Failed to decode public key")
})
}Submitter Integration
BatchTreeSubmission structure for chain submission :
- Accumulator root (0x-prefixed hex, 32 bytes)
- ECDSA signature over root with secp256k1 recovery ID
- List of batch trees
- Optional DA submission ID (set after publishing to data availability layer)
- Note: Current on-chain transactions only include root, signature, and recovery_id
Utility Functions
Key helper functions in utils module :
append_type: Adds type bits to handleuint128_to_bytes16/bytes16_to_uint128: Handle conversion utilitiesascii_to_decimal: Number decoding (hex-aware)decimal_to_ascii: Number encodingnormalize_to_32_bytes: Pads or truncates to 32-byte arrays
Storage Strategy
KvDB (Sled-based Key-Value Store)
Operations :
insert: Insert or update key-value pair (auto-flushes)get: Retrieve value by keyremove: Delete key-value pair (auto-flushes)contains_key: Check key existenceiter: Iterate over all entriesflush: Explicit flush to diskshutdown: Graceful shutdown with flush
Storage Patterns:
- ACL permissions use
hashv(signer || handle)as key with boolean value
QueueDB (Sled-based FIFO Queue)
Operations :
push: Add item to tail (auto-flushes)pop: Remove and return item from head (auto-flushes)peek: View head item without removalflush: Explicit flush to diskshutdown: Graceful shutdown with flush
Implementation :
- FIFO queue with
__head__and__tail__metadata keys (static byte slices) - Entry keys are big-endian u64 indices
- Every write operation explicitly flushes for durability
Error Handling & Resilience
Error Handling Patterns
- Standard
Result<T, E>returns throughout - Logging via
logcrate (info, warn, error levels) - No crate-level typed error enums; uses
Box<dyn std::error::Error>for flexibility
Resilience Mechanisms
- CiphertextWithCts parsing: Strict length validation; panics on invalid input to prevent unsafe partial decoding
- Decryption fallbacks:
- Single decrypt: Returns 32-byte vector
[0,0,...,0,2]on failure - Batch decrypt: Treats
vec![0; 32]as "missing ciphertext" sentinel; preserves sentinel in output
- Single decrypt: Returns 32-byte vector
- IndexerClient: Returns 32-byte zero vector sentinel when ciphertext not found
- Threshold decryption: Returns 32-byte fallback vector on aggregation failure
- Division by zero: Returns error instead of panic
Configuration
Environment Variables
TEE_PUBLIC_KEY(required): Hex-encoded 65-byte uncompressed secp256k1 public key for signature verification
Feature Flags
- No feature flags defined in this crate
Runtime Configuration
- Consumers pass configuration via function parameters
- Database paths configured at open-time for
KvDBandQueueDB - Shared configuration management available via
config_loadercrate (used by consumer services)
Tests
Unit Tests
Accumulator :
- Membership proof verification for multiple members
- Non-member rejection (no witness for non-members)
- Solana pairing syscall verification
- Byte serialization validation
- Edge cases: zero points and large scalars
Compute :
- Arithmetic: add, subtract, multiply, divide
- Comparison: less_than, less_equal, equal
- Bitwise: bit_and
- Control flow: if_then_else
- Encrypt/decrypt round-trips
KMS :
- Local threshold encrypt/decrypt flows
- Batch decryption
- Partial decryption and aggregation using indices 0 and 2
Ops/Utils :
- Handle derivation helpers
- ACL permission management
- Operand parsing and encoding
Integration Tests
Integration tests and service-level behaviors (indexer round-trips, production TEE/attestation, end-to-end batch processing) are implemented in consumer crates and services (processor, indexer, submitter).