Skip to content

oasisprotocol/rofl-header-oracle

Repository files navigation

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) 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:

# 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

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:

# 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:

# 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

# 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:

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

# 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

Packages

 
 
 

Contributors