System Design
onchain vs. Trusted
A precise breakdown of every system invariant, whether it is enforced by onchain logic or depends on a trusted party.
Summary matrix
| Property | Enforced by | Trust level |
|---|---|---|
| User authentication | Soroban require_auth | onchain |
| Token custody | Soroban contract escrow | onchain |
| Balance confidentiality | ElGamal encryption | Cryptography |
| No overdraft (transfer/withdraw) | ZK range proof | Cryptography |
| Encrypted balance updates | Fairyring homomorphic ops | onchain |
| Replay protection | tx_id monotonic counter | onchain |
| ZKP verification (onchain) | Fairyring zkp module | onchain |
| ZKP verification (off-chain) | FairyPort Rust FFI | Relayer |
| Event relay / liveness | FairyPort operation | Relayer |
| Private key safety | User's device / wallet | User |
| Proof generation correctness | SDK code | SDK |
Enforced onchain: Stellar Soroban
These properties are guaranteed by the Soroban smart contract runtime and cannot be bypassed by any party, including FairyPort.
User authentication
owner.require_auth() is enforced by the Soroban runtime before any state mutation. Only the account owner can call deposit, transfer, apply_pending, or withdraw.
Token custody (escrow)
Tokens are transferred into the Soroban contract on deposit and returned on withdrawal callback. The contract holds the escrow; no external party can move funds without a valid relayer callback.
Account existence
AccountCore is persisted in Soroban persistent storage. An account is marked exists=true after creation and finalized=true only after FairyPort relays the Fairyring confirmation.
Pending action state
The Soroban contract tracks a pending_action boolean per account. A second action cannot begin while one is in flight, preventing double-submit races.
Fee collection
If a fee is configured, it is collected by the Soroban contract from the sender's fee token balance atomically within the same transaction as the transfer request.
Encrypted balance storage (Stellar side)
Available(owner, token) and Pending(owner, token) ciphertexts are stored in Soroban persistent storage and updated only by the relayer callback after Fairyring confirmation.
Enforced onchain: Fairyring CosmWasm
These properties are guaranteed by the CosmWasm contract and the Fairyring chain, independent of the relayer.
Whitelist enforcement
Only addresses in the allowed_addresses set can call ExecuteMsg on the CosmWasm contract. Checked on every message before any state mutation.
Replay protection
last_processed_id is stored per account. Any tx_id ≤ stored value triggers an idempotent response without re-running cryptographic operations.
Homomorphic balance arithmetic
All balance updates (add, subtract) are performed as point arithmetic on the Ristretto255 curve. No plaintext amounts are ever computed or stored onchain.
ZK proof verification (onchain mode)
When use_offchain_verify=false, the Fairyring zkp module verifies equality proofs, range proofs, and ciphertext validity proofs before accepting any transfer or withdrawal.
Encrypted multi-denom balance store
available_balance and pending_balance are stored as Vec<DenomCipher> one ElGamal ciphertext per denomination per account. New denoms are initialised to encrypted zero.
Pending transfer ledger
PendingTransfer records are stored indexed by (sender, credit_id) and are used to reconstruct idempotent responses on duplicate tx_id handling.
Overflow protection
pending_credit_counter uses checked_add and returns ContractError::Overflow on wrap-around, preventing integer overflow attacks.
Trusted: FairyPort relayer
These properties depend on the relayer operating correctly. Failures are liveness failures (not safety failures) unless noted.
Event relay (liveness)
FairyPort must observe and forward events from both chains. If offline, user operations hang in pending state on Stellar. This is a liveness not safety assumption.
Off-chain ZKP verification (optional)
safety risk in off-chain modeWhen use_offchain_verify=true, FairyPort's Rust FFI library verifies ZK proofs before forwarding. A compromised relayer could pass invalid proofs. Use onchain verification to remove this trust.
Message ordering
FairyPort processes events in ledger/block order. If two events from the same account are reordered, tx_id replay protection on Fairyring prevents double application.
Checkpoint persistence
FairyPort persists the last processed ledger/block to LevelDB. On restart, it replays missed events from the checkpoint. Corrupt checkpoints can be deleted to force a full replay.
Dead-letter management
Permanently failed transactions are written to failed_txs.json. Operators must review and potentially manually resubmit these. FairyPort cannot auto-recover from all failure modes.
Trusted: user & SDK
These properties are the user's responsibility and cannot be enforced by any onchain mechanism.
ElGamal private key custody
The user's private key is generated client-side and never leaves the device. Loss of the key means balances cannot be decrypted. No recovery mechanism exists onchain.
Client-side proof generation
ZK proofs are generated in the browser/app using the SDK. A buggy or malicious SDK could generate incorrect proofs. Use the official @fairblock/stellar-stabletrust SDK.
Accurate amount inputs
The user specifies the transfer/withdrawal amount. The SDK encodes this as a plaintext scalar embedded in the ZK proof. No onchain mechanism bounds transfer amounts beyond the range proof.
