Zero-Knowledge Range Proof Oracle on Stellar Soroban
Blind Oracle: any smart contract can now ask 'is this number in range?' without ever seeing the number. Prove that private data is within a range, without revealing the data itself. Built for Stellar Hacks: Real-World ZK.
![]() |
![]() |
Smart contracts need to make decisions based on private data โ but publishing raw data on-chain destroys privacy.
"Is BTC above $95,000 right now?" "Is this user's credit score between 600 and 850?" "Does this wallet hold enough collateral?"
The naive answer (publish the number) is unacceptable. Blind Oracle solves this with zero-knowledge proofs.
Private (stays with prover) Public (on-chain)
โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโ
price = $97,500 โโZKโโโถ range: [$95,000 โ $200,000]
nonce = secret in_range: true โ
commitment: 0x8484...
nullifier: 0x2060...
One Groth16 proof. One Stellar transaction. Market resolves. Price never revealed.
Powered by: Groth16 ยท BN254 ยท Circom 2.0 ยท Soroban ยท Poseidon
export ALICE_SECRET="your-stellar-secret-key"
# Interactive range proof demo
bash demo.sh
# Prediction market demo
node --experimental-vm-modules prediction-market-demo.mjsAll resolved on Stellar testnet โ price never revealed:
| Market | Question | Price | Range | Result | TX |
|---|---|---|---|---|---|
| BTC-001 | BTC above $95k? | HIDDEN | $95kโ$200k | YES โ | view |
| BTC-002 | BTC above $95k? | HIDDEN | $95kโ$200k | NO โ | view |
| ETH-001 | ETH between $3.5kโ$5k? | HIDDEN | $3.5kโ$5k | YES โ | view |
| Metric | Value |
|---|---|
| CPU Instructions | 33,176,348 / 100,000,000 (33.2%) |
| Min Resource Fee | 37,472 stroops = 0.0037 XLM |
| Proof Size | 256 bytes (constant) |
| Curve | BN254 โ 2ร cheaper than BLS12-381 |
BN254 is natively supported by Soroban and runs at roughly half the instruction cost of BLS12-381, making Blind Oracle one of the most gas-efficient ZK verifiers on Stellar.
| Network | Contract ID |
|---|---|
| Stellar Testnet | CBXMLDKAE45OIUEOODGFMKZMFE5SA3CSR7NXW7TILBUQBVHEGNCLDQVH |
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PROVER (Off-Chain) โ
โ โ
โ Private: data_value, nonce โ
โ Public: range_min, range_max โ set by market maker โ
โ โ
โ [Circom Circuit] โ [SnarkJS Groth16] โ proof.bin (256B) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ submit
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ STELLAR SOROBAN (On-Chain) โ
โ โ
โ verify(proof_bytes, pub_signals_bytes) โ
โ โ BN254 pairing check (33.2M instructions) โ
โ โ return in_range: true / false โ
โ โ
โ Market resolves. Price never stored anywhere. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
| Guarantee | Mechanism |
|---|---|
range_min โค data_value โค range_max |
GreaterEqThan + LessEqThan (252-bit) |
commitment = Poseidon(data_value, nonce) |
Binding โ prover cannot change value after commit |
nullifier = Poseidon(data_value, commitment) |
Replay protection โ same data = same nullifier |
// One-time: store the Groth16 verification key
set_vk(vk_bytes: Bytes) โ Result<(), VerifierError>
// Per proof: verify range membership
verify(proof_bytes: Bytes, pub_signals_bytes: Bytes) โ Result<bool, VerifierError>
// Returns true โ proof valid AND data is within range
// Returns false โ proof valid but data is outside range
// Returns Error โ malformed inputPrice oracles prove a feed value is within a market's target range without ever publishing the exact price. Markets resolve trustlessly with no data leakage.
Prove age โฅ 18, credit score in range, or accreditation status โ without storing personal data on-chain.
Prove collateral exceeds a threshold for undercollateralized lending without exposing portfolio composition.
Prove transaction values stay within regulatory limits without revealing individual amounts to the protocol.
File: circuits/blind_oracle_v2.circom
Private inputs : data_value, nonce
Public inputs : range_min, range_max โ verifier sets these
Public outputs : commitment, in_range, nullifier
Constraints : 1,806
Curve : BN254
Proof system : Groth16
The range bounds are public inputs โ not hardcoded. One deployed circuit and one deployed contract serve any range, any asset, any use case.
| Scenario | Data | Range | Result | TX |
|---|---|---|---|---|
| Credit Score | 750 | 600โ850 | โ in_range | view |
| KYC Age | 25 | 18โ65 | โ in_range | view |
| DeFi Collateral | 15,000 | 10kโ50k | โ in_range | view |
| Credit Score FAIL | 500 | 600โ850 | โ out of range | view |
| BTC Market YES | HIDDEN | $95kโ$200k | โ YES | view |
| BTC Market NO | HIDDEN | $95kโ$200k | โ NO | view |
| ETH Market YES | HIDDEN | $3.5kโ$5k | โ YES | view |
# Install dependencies
npm install
# 1. Create input
cat > circuits/my_input.json << EOF
{
"data_value": "97500",
"nonce": "314159",
"range_min": "95000",
"range_max": "200000"
}
EOF
# 2. Generate witness
node circuits/blind_oracle_v2_js/generate_witness.js \
circuits/blind_oracle_v2_js/blind_oracle_v2.wasm \
circuits/my_input.json \
circuits/my_witness.wtns
# 3. Generate proof
npx snarkjs groth16 prove \
circuits/circuit_v2_final.zkey \
circuits/my_witness.wtns \
circuits/my_proof.json \
circuits/my_public.json
# 4. Encode + verify on-chain
node encode-proof-v2.mjs
export ALICE_SECRET="S..."
node --experimental-vm-modules test-all-scenarios.mjs| Layer | Technology |
|---|---|
| ZK Proof System | Groth16 (SnarkJS 0.7) |
| ZK Circuit | Circom 2.0 + circomlib |
| Elliptic Curve | BN254 (Barreto-Naehrig) |
| Hash Function | Poseidon |
| Smart Contract | Rust โ WASM (Soroban SDK v22) |
| Blockchain | Stellar Soroban Testnet |
| SDK | @stellar/stellar-sdk v16 |
| Trusted Setup | Powers of Tau (2^16) |
| File | Description |
|---|---|
| ARCHITECTURE.md | System design, data flow, binary formats |
| WHITE_PAPER.md | Cryptographic construction, security analysis |
| GUIDE.md | Developer guide, troubleshooting |
| DEPLOYMENTS.md | All deployments and TX hashes |
- Trusted Setup: Single-contributor ceremony. Production requires multi-party MPC.
- Testnet Only: Not audited for mainnet.
- Nullifier Registry: On-chain nullifier tracking not yet implemented (replay protection is off-chain).
- Reserve Inputs: ZK proves computation correctness, not input truthfulness.
MIT


