#!/usr/bin/env bash # Cut over Chain 138 oracle from legacy shell/Node mesh to Python chain138-oracle. # # Stops: # - scripts/reserve/pmm-mesh-6s-automation.sh (nohup/systemd legacy) # - scripts/reserve/keeper-service.js # - external oracle-publisher (systemd) if present # # Starts: # - chain138-oracle-api.service (FastAPI spot prices, port 3500) # - chain138-oracle-mesh.service (6s oracle + keeper + PMM loop) # # Updates .env: # CHAIN138_ORACLE_URL=http://127.0.0.1:3500 # TOKEN_AGGREGATION_URL=http://127.0.0.1:3000 (OMNL mint/transfer unchanged) # # Usage: # ./scripts/deployment/cutover-chain138-oracle-python.sh [--dry-run] # RESTART_SETTLEMENT=1 ./scripts/deployment/cutover-chain138-oracle-python.sh # set -euo pipefail DRY_RUN=0 for arg in "$@"; do case "$arg" in --dry-run) DRY_RUN=1 ;; esac done SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" ORACLE_DIR="$REPO_ROOT/services/chain138-oracle" ENV_FILE="${OMNL_BANK_ENV:-$REPO_ROOT/.env}" SYSTEMD_DIR="/etc/systemd/system" API_UNIT="chain138-oracle-api.service" MESH_UNIT="chain138-oracle-mesh.service" LEGACY_UNITS=(chain138-pmm-mesh-automation.service price-feed-keeper.service oracle-publisher.service) RESTART_SETTLEMENT="${RESTART_SETTLEMENT:-1}" log() { echo "[$(date -Iseconds)] $*"; } run() { if [ "$DRY_RUN" -eq 1 ]; then log "DRY-RUN: $*" else log "RUN: $*" eval "$@" fi } cd "$REPO_ROOT" if [ -f "$ENV_FILE" ]; then set -a # shellcheck source=/dev/null source "$ENV_FILE" set +a fi log "=== Chain 138 oracle Python cutover ===" log "Repo: $REPO_ROOT" log "Oracle dir: $ORACLE_DIR" # --- Stop legacy off-chain oracle processes --- stop_pattern() { local pattern="$1" local pids pids=$(pgrep -f "$pattern" 2>/dev/null || true) if [ -n "$pids" ]; then log "Stopping processes matching: $pattern ($pids)" run "pkill -f '$pattern' || true" fi } for unit in "${LEGACY_UNITS[@]}"; do if systemctl is-active --quiet "$unit" 2>/dev/null; then run "systemctl stop '$unit'" fi if systemctl is-enabled --quiet "$unit" 2>/dev/null; then run "systemctl disable '$unit'" fi done stop_pattern "pmm-mesh-6s-automation.sh" stop_pattern "keeper-service.js" stop_pattern "oracle_publisher" # --- Install Python venv + deps --- if [ ! -d "$ORACLE_DIR/.venv" ]; then run "python3 -m venv '$ORACLE_DIR/.venv'" fi run "'$ORACLE_DIR/.venv/bin/pip' install -q -r '$ORACLE_DIR/requirements.txt'" # --- Validate token coverage --- log "Validating M2 registry token price coverage..." if [ "$DRY_RUN" -eq 0 ]; then set -a # shellcheck source=/dev/null source "$ENV_FILE" 2>/dev/null || true set +a PYTHONPATH="$ORACLE_DIR" "$ORACLE_DIR/.venv/bin/python" -m chain138_oracle validate fi # --- Patch .env --- merge_env_key() { local key="$1" local value="$2" if [ ! -f "$ENV_FILE" ]; then run "touch '$ENV_FILE'" fi if grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then run "sed -i 's|^${key}=.*|${key}=${value}|' '$ENV_FILE'" else run "echo '${key}=${value}' >> '$ENV_FILE'" fi } merge_env_key "CHAIN138_ORACLE_URL" "${CHAIN138_ORACLE_URL:-http://127.0.0.1:3500}" merge_env_key "CHAIN138_ORACLE_PORT" "${CHAIN138_ORACLE_PORT:-3500}" merge_env_key "TOKEN_AGGREGATION_URL" "${TOKEN_AGGREGATION_URL:-http://127.0.0.1:3000}" # --- Install systemd units --- install_unit() { local example="$1" local unit="$2" local dest="$SYSTEMD_DIR/$unit" run "cp '$example' '$dest'" run "sed -i 's|/opt/smom-dbis-138|$REPO_ROOT|g' '$dest'" } install_unit "$ORACLE_DIR/systemd/chain138-oracle-api.service.example" "$API_UNIT" install_unit "$ORACLE_DIR/systemd/chain138-oracle-mesh.service.example" "$MESH_UNIT" run "systemctl daemon-reload" run "systemctl enable '$API_UNIT' '$MESH_UNIT'" run "systemctl restart '$API_UNIT'" run "systemctl restart '$MESH_UNIT'" # --- Smoke tests --- smoke() { local url="$1" local label="$2" if [ "$DRY_RUN" -eq 1 ]; then log "DRY-RUN smoke: $label $url" return 0 fi curl -sf "$url" >/dev/null && log "OK: $label" || { log "FAIL: $label ($url)"; return 1; } } ORACLE_BASE="${CHAIN138_ORACLE_URL:-http://127.0.0.1:3500}" CBTC=0xe94260c555ac1d9d3cc9e1632883452ebdf0082e sleep 2 smoke "$ORACLE_BASE/health" "oracle health" smoke "$ORACLE_BASE/api/v1/prices/metamask/v2/chains/138/spot-prices?tokenAddresses=$CBTC" "cBTC spot price" # --- Restart settlement middleware if requested --- if [ "$RESTART_SETTLEMENT" = "1" ]; then for unit in settlement-middleware omnl-settlement-middleware; do if systemctl is-active --quiet "$unit" 2>/dev/null; then run "systemctl restart '$unit'" log "Restarted $unit" break fi done if docker compose -f "$REPO_ROOT/docker-compose.omnl-production.yml" ps settlement-middleware >/dev/null 2>&1; then run "docker compose -f '$REPO_ROOT/docker-compose.omnl-production.yml' up -d chain138-oracle settlement-middleware" fi fi log "=== Cutover complete ===" log "CHAIN138_ORACLE_URL=$ORACLE_BASE" log "TOKEN_AGGREGATION_URL=${TOKEN_AGGREGATION_URL:-http://127.0.0.1:3000}" log "Check: systemctl status $API_UNIT $MESH_UNIT" log "Check: curl -s $ORACLE_BASE/api/v1/oracle/tokens | head"