System Design
Threat Model & Trust
A precise accounting of what each component can and cannot do, what properties are enforced by cryptography, and what assumptions the system relies on.
Security properties
Balance confidentiality
GuaranteedAll balances are stored as ElGamal ciphertexts. No plaintext amount is ever written to either chain. A passive observer of either ledger learns nothing about any account balance.
Enforced by: Cryptography
Transfer amount confidentiality
GuaranteedThe transferred amount in a confidential transfer is encrypted under both the sender's and recipient's public keys. Even the relayer sees only proof data not the plaintext amount.
Enforced by: Cryptography
No theft of funds
GuaranteedTokens are held in the Stellar Soroban contract, which only releases them on a valid withdraw callback from the relayer. The relayer cannot forge an approval it can only relay what Fairyring approved.
Enforced by: Contract logic + ZK proofs
No invalid balance creation
GuaranteedA range proof accompanies every withdrawal and transfer, proving the sender's resulting balance is ≥ 0. The Fairyring contract rejects any proof that fails verification.
Enforced by: ZK range proof (Bulletproofs-style)
Sender authorization
GuaranteedThe Soroban contract enforces owner.require_auth() before accepting any action. Only the account owner can initiate deposit, transfer, apply, or withdraw.
Enforced by: Soroban auth framework
Replay protection
GuaranteedEach account maintains a monotonically increasing tx_id on Fairyring. Duplicate messages are detected and re-synthesize the prior response without mutating state.
Enforced by: Contract state (last_processed_id)
Relayer liveness
AssumptionIf FairyPort is offline, user operations will not finalize. The relayer is a liveness assumption, not a safety assumption it can censor but not steal.
Enforced by: Operational (not cryptographic)
Relayer off-chain proof correctness
Mode-dependentWhen use_offchain_verify=true, the relayer verifies ZK proofs via its Rust FFI library. A malicious or buggy relayer could accept invalid proofs. onchain verification removes this assumption.
Enforced by: Depends on mode
What the relayer (FairyPort) can and cannot do
FairyPort is a trusted but not trusted-with-funds component. It is trusted for liveness and (in off-chain mode) proof verification. It has no ability to steal user tokens.
What it CAN do
- Observe all events on both chains (read-only)
- Censor operations by refusing to relay events
- In off-chain mode: accept or reject ZK proofs
- View the structure of proof data (not plaintext amounts)
- Retry failed transactions automatically
- Reorder messages within the bounded event queue
What it CANNOT do
- Steal or redirect user tokens (contract holds escrow)
- Forge a valid ZK proof (computationally infeasible)
- Decrypt any user balance (no access to private keys)
- Execute on behalf of a user (Soroban require_auth enforced)
- Process a duplicate tx_id (Fairyring replay guard)
- Create a withdrawal without a valid range proof
- Alter Fairyring contract state without a whitelisted key
Trust assumptions (ranked by impact)
Relayer liveness
FairyPort must be online and functioning to process user operations. A crashed or censoring relayer blocks all activity. Mitigation: run multiple independent relayer instances; anyone with the relayer key can run a backup.
Relayer off-chain proof verification (optional)
When use_offchain_verify=true, the relayer's Rust FFI library verifies ZK proofs before forwarding to Fairyring. A compromised relayer could skip verification and forward invalid proofs. Use onchain verification (use_offchain_verify=false) to remove this assumption entirely.
Fairyring relayer allowlist
The Fairyring contract enforces a whitelist of allowed addresses that can submit transactions. Only addresses added by the admin can interact with the contract. The admin key represents a trust assumption for the initial allowlist configuration.
Cryptographic soundness
The security of all guarantees depends on the hardness of the discrete logarithm problem on Ristretto255, the soundness of the Bulletproofs range proof system, and the binding property of the Pedersen commitment scheme.
Client-side key custody
The user's ElGamal private key is generated client-side and never transmitted. Loss of the private key means permanent loss of balance decryption capability (though the onchain ciphertext remains). Secure key storage is the user's responsibility.
Zero-knowledge proofs used
| Proof type | Operation | What it proves |
|---|---|---|
| Equality proof | Transfer, Withdraw | The same plaintext is encrypted under both the sender's and recipient's keys (consistency proof) |
| Range proof | Transfer, Withdraw | The resulting sender balance after deduction is ≥ 0 (no overdraft), without revealing the balance |
| Ciphertext validity proof | Transfer | The transfer amount ciphertext is well-formed and encrypted under valid public keys |
| Withdraw nonce binding | Withdraw | The proof is bound to a specific tx_id, preventing proof reuse across different withdrawal operations |
Audit status
The Stellar Soroban contract, Fairyring CosmWasm contract, and FairyPort relayer have not yet been formally audited. The codebase should be treated as a pre-production system and not used with material value until an audit is complete.
