Building a Privacy-Preserving AI Oracle on Oasis Sapphire
Source: Dev.to
Source: Dev.to – Building a Privacy‑Preserving AI Oracle on Oasis Sapphire
Introduction
Recently I built a cross‑chain confidential Trust Score Oracle using Sapphire ParaTime, Oasis Privacy Layer, and ROFL on the Oasis Network. That project focused on computing a reputation score across multiple blockchains while keeping the scoring logic private.
In this tutorial we’ll explore a different but related use case: building a privacy‑preserving AI inference oracle.
Instead of calculating a trust score from on‑chain activity, we will:
- Run an AI model privately.
- Process sensitive user data.
- Return verifiable results on‑chain.
This pattern is extremely useful for applications such as:
- Private credit scoring
- Confidential trading signals
- Healthcare ML analytics
- AI‑powered DeFi risk models
Why Confidential Compute?
Most blockchains are fully transparent—every transaction and input is publicly visible. This creates a major problem for AI‑driven applications.
Example: a credit‑scoring system.
Typical inputs include:
- Income
- Transaction history
- Loan‑repayment data
- Identity attributes
None of these data points can safely be published on a public blockchain.
The confidential compute model of the Oasis Network solves this problem. It lets smart contracts execute encrypted computations inside secure enclaves. By separating consensus from computation, Oasis enables specialized runtimes that are tailored to different workloads.
+------------------------------------------------+
| Applications |
+------------------------------------------------+
| ParaTime Layer |
|------------------------------------------------|
| Sapphire (Confidential EVM) |
| Emerald (EVM‑compatible) |
| Custom Runtimes |
+------------------------------------------------+
| Consensus Layer |
| Proof‑of‑Stake Validators |
+------------------------------------------------+Tutorial Overview
In this tutorial we will use Sapphire ParaTime, which provides:
- EVM compatibility
- Encrypted state
- Confidential smart contracts
Our goal: Build a Confidential AI Oracle.
System Architecture
flowchart TD
A[User Wallet<br/>(encrypted data)] --> B[Sapphire Smart Contract<br/>(confidential EVM)]
B --> C[ROFL Oracle Worker]
C --> D[Secure Enclave<br/>AI Model Inference]
D --> E[Signed Result<br/>returned on‑chain]Workflow
- Receive encrypted user data.
- Run an AI model inside a secure environment.
- Produce a prediction.
- Store only the result on‑chain.
Detailed Data Flow
graph LR
U[User Wallet] -->|Encrypted Data| SC[Sapphire Smart Contract]
SC -->|Request| OW[ROFL Worker]
OW -->|AI Model Inference| SR[Signed Result]
SR -->|Store on‑chain| SCThis cleaned‑up markdown preserves the original information while improving readability and visual consistency.
Use‑Case Example: Private Credit Scoring
Imagine a DeFi lending protocol that wants to compute a credit score before issuing a loan, but the borrower’s financial data must remain private.
| Traditional Flow | Confidential Flow |
|---|---|
| User → Smart Contract → Public Data (visible to everyone) | User Data (encrypted) → Confidential Runtime → AI Model → Credit Score (only the result is visible) |
Project Structure
oasis-private-ai-oracle/
├─ contracts/
│ └─ CreditOracle.sol
├─ oracle/
│ ├─ worker.py
│ ├─ model.py
│ └─ enclave_runner.py
├─ scripts/
│ └─ deploy.js
├─ frontend/
│ └─ submit-data.ts
└─ README.mdConfidential Oracle Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract CreditOracle {
address public oracle;
struct ScoreRecord {
uint256 score;
uint256 timestamp;
}
mapping(address => ScoreRecord) public scores;
event ScoreUpdated(address indexed user, uint256 score);
constructor(address _oracle) {
oracle = _oracle;
}
function submitScore(
address user,
uint256 score,
bytes calldata signature
) external {
require(verify(user, score, signature), "invalid signature");
scores[user] = ScoreRecord({
score: score,
timestamp: block.timestamp
});
emit ScoreUpdated(user, score);
}
function verify(
address user,
uint256 score,
bytes memory signature
) internal view returns (bool) {
bytes32 message = keccak256(abi.encodePacked(user, score));
bytes32 ethSigned = keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", message)
);
(bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
address recovered = ecrecover(ethSigned, v, r, s);
return recovered == oracle;
}
function splitSignature(bytes memory sig)
internal
pure
returns (bytes32 r, bytes32 s, uint8 v)
{
require(sig.length == 65, "invalid signature length");
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
}
}Contract Highlights
- Verifies oracle signatures to prevent tampering.
- Stores final scores with timestamps.
- Emits a
ScoreUpdatedevent for off‑chain indexing.
Oracle Worker (Python)
# oracle/worker.py
from web3 import Web3
import json
from model import predict_score
from eth_account import Account
# ----------------------------------------------------------------------
# Configuration
# ----------------------------------------------------------------------
RPC_URL = "https://sapphire.oasis.io"
PRIVATE_KEY = "YOUR_ORACLE_PRIVATE_KEY" # keep secret!
CONTRACT_ADDRESS = "DEPLOYED_CONTRACT_ADDRESS"
ABI_PATH = "CreditOracleABI.json"
# ----------------------------------------------------------------------
# Setup Web3
# ----------------------------------------------------------------------
w3 = Web3(Web3.HTTPProvider(RPC_URL))
account = Account.from_key(PRIVATE_KEY)
with open(ABI_PATH) as f:
contract_abi = json.load(f)
oracle_contract = w3.eth.contract(address=CONTRACT_ADDRESS, abi=contract_abi)
# ----------------------------------------------------------------------
# Core worker logic
# ----------------------------------------------------------------------
def handle_request(user_address: str, encrypted_payload: bytes):
"""
Process an oracle request:
1. Decrypt the payload inside the secure enclave (omitted here).
2. Run the AI model to obtain a credit score.
3. Sign the result with the oracle's private key.
4. Submit the signed score to the on‑chain contract.
"""
# ---- 1. Decrypt (placeholder) ----
# plaintext = enclave_decrypt(encrypted_payload)
# ---- 2. Predict ----
# Replace the placeholder with actual data extraction from `plaintext`.
score = predict_score(/* plaintext data */)
# ---- 3. Sign ----
message_hash = w3.solidityKeccak(
["address", "uint256"],
[user_address, score]
)
eth_message = w3.solidityKeccak(
["string", "bytes32"],
["\x19Ethereum Signed Message:\n32", message_hash]
)
signed = account.signHash(eth_message)
# ---- 4. Submit ----
tx = oracle_contract.functions.submitScore(
user_address,
score,
signed.signature
).buildTransaction({
"from": account.address,
"nonce": w3.eth.get_transaction_count(account.address),
"gas": 200_000,
"gasPrice": w3.toWei("0.001", "ether"),
"chainId": 0x5A # Sapphire chain ID
})
signed_tx = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
print(f"Submitted score for {user_address}, tx hash: {tx_hash.hex()}")The worker runs inside the ROFL enclave, ensuring that the AI model and user data never leave the trusted environment.
Deployment & Interaction
Compile & Deploy
npx hardhat run scripts/deploy.js --network sapphireDeploy
CreditOracle.solto the Sapphire network.Upload the AI model
- Transfer
model.pyand any required assets to the enclave host (e.g., viascpor a shared volume).
- Transfer
Start the Python worker
python oracle/worker.pyThis launches the enclave‑side inference service.
Frontend integration
frontend/submit-data.tsencrypts user data with the enclave’s public key.- The encrypted payload is then sent to the smart contract for verification.
Summary
- Confidential Compute on Oasis lets you keep sensitive inputs private while still leveraging on‑chain verification.
- The Confidential AI Oracle pattern combines encrypted EVM contracts (Sapphire) with a secure off‑chain worker (ROFL) to deliver AI‑driven results that are verifiable and tamper‑proof.
- This architecture can be adapted to many privacy‑sensitive blockchain use‑cases beyond credit scoring, such as:
- confidential trading signals,
- health‑data analytics,
- AI‑enhanced DeFi risk models.
Oracle Implementation (Python)
from web3 import Web3
from eth_account import Account
# ----------------------------------------------------------------------
# Initialise Web3 provider
# ----------------------------------------------------------------------
w3 = Web3(Web3.HTTPProvider(RPC_URL))
# ----------------------------------------------------------------------
# Load the signing account
# ----------------------------------------------------------------------
account = Account.from_key(PRIVATE_KEY)
# ----------------------------------------------------------------------
# Load the contract instance
# ----------------------------------------------------------------------
contract = w3.eth.contract(
address=contract_address,
abi=contract_abi,
)
def run_oracle(user_address: str, user_data) -> str:
"""
1️⃣ Compute the AI score.
2️⃣ Sign the (address, score) tuple.
3️⃣ Submit the signed score to the contract.
Returns the transaction hash as a hex string.
"""
# ------------------------------------------------------------------
# 1️⃣ Compute the score
# ------------------------------------------------------------------
score = predict_score(user_data)
# ------------------------------------------------------------------
# 2️⃣ Create a deterministic message hash
# ------------------------------------------------------------------
message = Web3.solidity_keccak(
["address", "uint256"],
[user_address, score],
)
# ------------------------------------------------------------------
# 3️⃣ Sign the message
# ------------------------------------------------------------------
signed = account.sign_message(
Web3.eth.account._hash_message(message)
)
# ------------------------------------------------------------------
# 4️⃣ Build the transaction
# ------------------------------------------------------------------
tx = contract.functions.submitScore(
user_address,
score,
signed.signature,
).build_transaction(
{
"from": account.address,
"nonce": w3.eth.get_transaction_count(account.address),
}
)
# ------------------------------------------------------------------
# 5️⃣ Sign & send the transaction
# ------------------------------------------------------------------
signed_tx = w3.eth.account.sign_transaction(tx, PRIVATE_KEY)
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
return tx_hash.hex()Where the worker runs
- Secure enclave or confidential compute environment
- Computes the AI score
- Signs the result
- Sends a transaction on‑chain
Simplified Scoring Function (Python)
import numpy as np
def normalize(value: float, max_value: float) -> float:
"""
Scale *value* to the range [0, 1] and clamp the result at 1.
Parameters
----------
value : float
The raw value to be normalised.
max_value : float
The maximum possible value for the feature.
Returns
-------
float
The normalised (and clamped) value.
"""
return min(value / max_value, 1.0)
def predict_score(data: dict) -> int:
"""
Convert a dictionary of user features into a credit‑score‑like metric.
The function normalises the input features, applies a weighted linear
combination, and returns an integer in the range **0‑100**.
Parameters
----------
data : dict
Expected keys:
- ``"income"`` – annual income (USD)
- ``"debt"`` – total debt (USD)
- ``"history"`` – years of credit history
Returns
-------
int
Score scaled to the interval [0, 100].
"""
# Normalise each feature against a reasonable maximum.
income = normalize(data["income"], 100_000) # max $100k
debt = normalize(data["debt"], 50_000) # max $50k
history = normalize(data["history"], 10) # max 10 years
# Weighted linear combination.
score = (
income * 0.5 # income contributes positively
- debt * 0.3 # debt detracts from the score
+ history * 0.2 # longer history adds a small boost
)
# Ensure the score is non‑negative and scale to 0‑100.
score = max(score, 0.0)
return int(score * 100)The function performs basic feature normalisation and yields a realistic‑looking score.
In production you could replace it with a more powerful model such as XGBoost, LightGBM, or a neural network.
Development Setup (Hardhat)
Install Hardhat
npm install -g hardhatConfigure the Sapphire network
// hardhat.config.js
module.exports = {
networks: {
sapphire: {
url: "https://sapphire.oasis.io",
chainId: 23294,
},
},
};Deploy the contract
npx hardhat run scripts/deploy.js --network sapphireEnd‑to‑End Flow
flowchart TD
A[User submits encrypted data] --> B[Oracle decrypts inside secure compute]
B --> C[AI model runs prediction]
C --> D[Oracle signs result]
D --> E[Smart contract stores score]- Step 1: The user submits encrypted data.
- Step 2: The Oracle decrypts the data inside a secure compute environment.
- Step 3: The AI model processes the decrypted data and generates a prediction.
- Step 4: The Oracle signs the prediction result.
- Step 5: The signed result is stored on‑chain by a smart contract as the final score.
Why Confidential Compute + Blockchain?
Without privacy infrastructure, AI + blockchain rarely works because:
- Training data is private
- Models are proprietary
- Inputs contain sensitive information
Applications enabled by confidential runtimes on Oasis Network
- Private DeFi analytics
- Confidential AI marketplaces
- Encrypted healthcare ML
- Secure financial scoring
Possible Extensions
- Zero‑knowledge proofs for inference verification
- Data marketplace for encrypted datasets
- Federated learning across multiple data owners
- Private trading signals
Takeaway
The combination of blockchain verification and confidential computation unlocks entirely new types of applications. With runtimes like Sapphire ParaTime on the Oasis Network, developers can build systems where:
- Data remains private
- Computation is verifiable
- Results are trustless
This opens the door to privacy‑first AI in Web3.