https://github.com/oasisprotocol/rofl-header-oracle
Oracle running in Oasis ROFL that reports block headers from other chains to Oasis Sapphire.
https://github.com/oasisprotocol/rofl-header-oracle
block-header oasis oracle python rofl sapphire solidity
Last synced: 16 days ago
JSON representation
Oracle running in Oasis ROFL that reports block headers from other chains to Oasis Sapphire.
- Host: GitHub
- URL: https://github.com/oasisprotocol/rofl-header-oracle
- Owner: oasisprotocol
- License: apache-2.0
- Created: 2025-09-11T21:14:41.000Z (10 months ago)
- Default Branch: master
- Last Pushed: 2026-02-23T12:23:29.000Z (4 months ago)
- Last Synced: 2026-02-23T20:50:09.441Z (4 months ago)
- Topics: block-header, oasis, oracle, python, rofl, sapphire, solidity
- Language: Python
- Homepage: https://explorer.oasis.io/mainnet/sapphire/rofl/app/rofl1qz5h592w87uyftlrht388g3rkf58s5z7n53w5xh0
- Size: 282 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ROFL Header Oracle
A Python-based Oasis ROFL (Runtime OFf-chain Logic) oracle that fetches block
headers from source chains and submits them to the ROFLAdapter contract on
Oasis Sapphire for cross-chain bridge verification.
## Overview
This oracle listens for `BlockHeaderRequested` events from a source chain
contract and responds by fetching the requested block headers and submitting
them to the Oasis Sapphire network through the ROFL runtime. It's designed to
work as part of a Hashi-based cross-chain bridge system.
## Architecture
- **Source Chain**: Listens for events on any EVM-compatible blockchain
- **Target Chain**: Submits block headers to Oasis Sapphire via ROFL
- **ROFL Runtime**: Uses Oasis confidential compute for secure oracle operations
- **Event-Driven**: Processes `BlockHeaderRequested` events in real-time
## Oracle Modes
The oracle supports four operating modes, configured via `ORACLE_MODE`:
| Mode | Use When | How It Works |
|------|----------|-------------|
| `event_listener` | A `BlockHeaderRequester` contract exists on the source chain and emits requests for specific block headers | Polls for `BlockHeaderRequested` events and submits only the requested headers |
| `push` | You need continuous, unconditional block header availability on Sapphire (e.g., for a bridge that may need any recent header) | Pushes the latest block headers at a fixed interval, regardless of demand |
| `watcher` | You want headers only for blocks where specific addresses have on-chain activity (transactions to/from) | Scans blocks for interactions with watched addresses and submits only those block headers. Supports optional internal transaction detection via `debug_traceTransaction` |
| `token_watcher` | You want headers only for blocks containing ERC-20 transfers to specific recipients (e.g., bridge deposit addresses) | Monitors `Transfer` events on configured token contracts filtered by recipient addresses, and submits headers for blocks with matching transfers |
The `watcher` and `token_watcher` modes include a **heartbeat
mechanism** that periodically stores a checkpoint block header even
when no activity is detected, bounding sync time on oracle restart.
## Requirements
- Docker and Docker Compose
- Access to a ROFL-enabled Oasis Network environment
- Source chain RPC endpoint
- Deployed contracts:
- `BlockHeaderRequester` on source chain
- `ROFLAdapter` on Oasis Sapphire
## Configuration
The oracle is configured through environment variables defined in `compose.yaml`:
### Environment Variables
The oracle supports four modes: **event_listener**, **push**, **watcher**,
and **token_watcher**. Some environment variables are required for all
modes, while others are specific to a mode.
#### **Common Variables (All Modes)**
| Variable | Description | Default | Required |
|------------------------|----------------------------------------------------------|--------------------------------------|----------|
| `PYTHONUNBUFFERED` | Disable Python output buffering for immediate logs | `1` | No |
| `SOURCE_RPC_URL` | RPC endpoint for the source chain | `https://ethereum.publicnode.com` | No |
| `TARGET_RPC_URL` | RPC endpoint for the target chain | `https://testnet.sapphire.oasis.io` | No |
| `ROFL_ADAPTER_ADDRESS` | Address of the ROFLAdapter contract on Oasis Sapphire | - | **Yes** |
| `REQUEST_TIMEOUT` | HTTP request timeout (seconds) | `30` | No |
| `RETRY_COUNT` | Number of retry attempts for operations | `3` | No |
| `ORACLE_MODE` | Operating mode (see [Oracle Modes](#oracle-modes)) | `event_listener` | No |
| `LOG_LEVEL` | Logging level: DEBUG, INFO, WARNING, ERROR, CRITICAL | `INFO` | No |
| `JSON_LOGS` | Enable JSON-formatted structured logging | `false` | No |
| `MIN_REPORTER_BALANCE` | Minimum reporter balance in native tokens before startup | `0.001` | No |
---
#### **Event Listener Mode (`ORACLE_MODE=event_listener`)**
| Variable | Description | Default | Required |
|----------------------------|--------------------------------------------------------------------|---------|----------|
| `SOURCE_CONTRACT_ADDRESS` | Address of the BlockHeaderRequester contract on source chain | - | **Yes** |
| `POLLING_INTERVAL` | Seconds between event checks | `12` | No |
| `LOOKBACK_BLOCKS` | Number of blocks to look back on startup | `100` | No |
---
#### **Push Mode (`ORACLE_MODE=push`)**
| Variable | Description | Default | Required |
|--------------------|-----------------------------------------------|---------|----------|
| `PUSH_INTERVAL` | Seconds between block pushes | `60` | No |
| `PUSH_BATCH_SIZE` | Max blocks to push per iteration | `20` | No |
---
#### **Watcher Mode (`ORACLE_MODE=watcher`)**
| Variable | Description | Default | Required |
|---------------------------------|-----------------------------------------------------|---------|----------|
| `WATCH_ADDRESSES` | Comma-separated list of addresses to watch | - | **Yes** |
| `SCAN_INTERVAL` | Seconds between scanning for interactions | `60` | No |
| `WATCHER_BATCH_SIZE` | Max blocks to scan per iteration | `50` | No |
| `LOOKBACK_BLOCKS` | Number of blocks to look back on startup | `100` | No |
| `ENABLE_INTERNAL_TX_DETECTION` | Enable internal transaction detection | `false` | No |
| `HEARTBEAT_INTERVAL_SECONDS` | Seconds between heartbeat checkpoint submissions | `3600` | No |
**Internal Transaction Detection:**
When `ENABLE_INTERNAL_TX_DETECTION=true`, the watcher will also detect interactions
that occur via internal transactions (contract-to-contract calls). This feature:
- **Requires:** Archive node with `debug_traceTransaction` API support
- **Performance:** Significantly slower due to tracing overhead (trace each
transaction)
- **Use case:** Critical when watched addresses primarily interact via smart
contracts
- **Recommended:** Only enable if you have access to archive nodes and need
comprehensive tracking
Without this feature, only direct (external) transactions to/from watched
addresses are detected.
---
#### **Token Watcher Mode (`ORACLE_MODE=token_watcher`)**
| Variable | Description | Default | Required |
|------------------------------|----------------------------------------------------------|---------|----------|
| `TOKEN_ADDRESSES` | Comma-separated list of ERC-20 token contract addresses | - | **Yes** |
| `RECIPIENT_ADDRESSES` | Comma-separated list of recipient addresses to watch | - | **Yes** |
| `SCAN_INTERVAL` | Seconds between scanning for token transfers | `5` | No |
| `MAX_BLOCKS_PER_SCAN` | Max blocks to scan per iteration | `10` | No |
| `HEARTBEAT_INTERVAL_SECONDS` | Seconds between heartbeat checkpoint submissions | `3600` | No |
---
#### **Local Mode (Testing Only)**
| Variable | Description | Default | Required |
|---------------------|--------------------------------------------|---------|--------------------|
| `LOCAL_PRIVATE_KEY` | Private key for local testing mode | - | **Yes (Local Mode)** |
---
**Note:**
- All addresses must be valid EVM addresses (checksummed).
- `LOCAL_PRIVATE_KEY` is only required for local mode/testing.
- If a required variable is missing for the selected mode, the oracle will
fail to start with a clear error.
---
#### Important Notes
- **`PYTHONUNBUFFERED=1`**: Essential for real-time log visibility in
containerized environments. Without this, log output may be buffered and not
appear immediately, making the oracle appear "stuck" when it's actually running normally.
- **`LOCAL_PRIVATE_KEY`**: Required only when running in local mode for testing
without ROFL utilities. Should be a hex-encoded private key.
- **`ENABLE_INTERNAL_TX_DETECTION`**: When enabled in watcher mode, uses
`debug_traceTransaction` to detect internal contract calls. This requires:
- Archive node access (full nodes won't work)
- Debug API enabled on the RPC endpoint
- May require premium RPC provider plans
- Significantly increases processing time (10-100x slower)
- Only use if absolutely necessary for your use case
## Usage
### 1. Set Environment Variables
Create a `.env` file or set environment variables:
```bash
# Required for all modes
export ROFL_ADAPTER_ADDRESS=0xYourROFLAdapterAddress
export SOURCE_RPC_URL=https://your-source-chain-rpc.com
export ORACLE_MODE=event_listener
# Required for event_listener mode
export SOURCE_CONTRACT_ADDRESS=0xYourBlockHeaderRequesterAddress
# Optional: configure other settings
export POLLING_INTERVAL=12
export LOOKBACK_BLOCKS=100
```
### 2. Run with Docker Compose
```bash
docker-compose up --build
```
### 3. Monitor Logs
The oracle will display:
- Initialization progress
- ROFL connection status
- Block range being monitored
- Event processing status
- Periodic heartbeat messages
## Local Mode (Testing)
For testing and development without ROFL infrastructure, the oracle supports a
local mode that simulates transaction submissions and skips ROFL utilities.
### Local Mode Features
- **No ROFL Dependencies**: Skips ROFL utility initialization and socket connections
- **Transaction Simulation**: Logs transaction details instead of actual submission
- **Event Listening**: Full WebSocket and polling event listening functionality
- **Local Private Key**: Uses a local private key for contract interaction testing
### Running in Local Mode
Use the dedicated local testing Docker Compose configuration:
```bash
# Run with local testing configuration
docker compose -f compose.local.yaml up --build
```
### Local Mode Environment Variables
In addition to the standard variables, local mode requires:
```bash
# Required for local mode - add this to your .env file
LOCAL_PRIVATE_KEY=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
```
## Operation
1. **Initialization**: Connects to ROFL runtime and source chain
2. **Event Monitoring**: Polls for `BlockHeaderRequested` events
3. **Block Fetching**: Retrieves requested block headers from source chain
4. **Header Submission**: Submits headers to Sapphire via ROFL
5. **Continuous Operation**: Runs in an infinite loop with configurable intervals
## Monitoring & Health Checks
The oracle includes built-in health check endpoints for container orchestration
and monitoring:
### Health Endpoints
- **`/health`** - Overall system health with component status
- **`/health/live`** - Liveness probe (is the service running)
- **`/health/ready`** - Readiness probe (is the service ready to handle work)
Health endpoints are exposed on port 8080 by default.
### Example Health Check
```bash
# Check overall health
curl http://localhost:8080/health
# Liveness probe (for Kubernetes/Docker)
curl http://localhost:8080/health/live
# Readiness probe
curl http://localhost:8080/health/ready
```
### Error Handling & Resilience
The oracle includes production-grade error handling:
- **Exponential Backoff**: Automatic retry with exponential backoff for
transient failures
- **Circuit Breakers**: Prevents cascading failures when RPCs are down
- **Retry Logic**: Configurable retry attempts (via `RETRY_COUNT`) for all
critical operations
- **Graceful Degradation**: Continues operating even with degraded connectivity
### Structured Logging
Enable JSON-formatted structured logging for production environments:
```bash
export JSON_LOGS=true
```
This outputs logs in JSON format for easier parsing by log aggregation systems
(ELK, Splunk, DataDog, etc.).
## Development
### Dependencies
- Python 3.10+
- oasis-sapphire-py
- web3.py
- httpx
- cbor2
- aiohttp
### Local Development
```bash
# Install dependencies
make sync
# Run tests
make test
# Run tests with coverage
make test-cov
# Format code
make format
# Lint code
make lint
# Fix linting issues
make lint-fix
# Run all checks (format, lint, test)
make pre-commit
# Run the application (via Docker Compose)
docker compose -f compose.local.yaml up --build
```
## Architecture Integration
This oracle is designed to work with:
- Hashi cross-chain message verification system
- Oasis ROFL confidential compute runtime
- EVM-compatible source chains