- load-project-env: _lpr_dotenv_source / _lpr_source_relaxed so smom-dbis-138/.env
lines like ${ARBITRUM_MAINNET_RPC} do not abort scripts using set -u
- check-public-report-api: detect /token-aggregation vs apex /api/v1 for networks
- run-completable-tasks: enforce public report API (remove SKIP_EXIT bypass)
- Document verifier behavior in TOKEN_AGGREGATION_REPORT_API_RUNBOOK and verify README
Made-with: Cursor
155 lines
5.5 KiB
Bash
Executable File
155 lines
5.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Verify that the public token-aggregation/report API is reachable and not misrouted to Blockscout.
|
|
# Usage: bash scripts/verify/check-public-report-api.sh [base_url]
|
|
# base_url: Optional API base, defaults to https://explorer.d-bis.org
|
|
#
|
|
# Exit codes:
|
|
# 0 = all expected endpoints returned token-aggregation-style JSON
|
|
# 1 = one or more endpoints returned the wrong shape or were unreachable
|
|
# Set SKIP_EXIT=1 to print diagnostics but exit 0.
|
|
# Set KEEP_GOING=1 to keep checking every endpoint before exiting non-zero.
|
|
# Set SKIP_BRIDGE_ROUTES=0 to assert /api/v1/bridge/routes payload shape.
|
|
# Set SKIP_BRIDGE_PREFLIGHT=0 to assert /api/v1/bridge/preflight payload shape.
|
|
|
|
set -euo pipefail
|
|
|
|
BASE_URL="${1:-https://explorer.d-bis.org}"
|
|
SKIP_EXIT="${SKIP_EXIT:-0}"
|
|
KEEP_GOING="${KEEP_GOING:-0}"
|
|
HAD_FAILURE=0
|
|
|
|
log() { printf '%s\n' "$*"; }
|
|
ok() { printf '[OK] %s\n' "$*"; }
|
|
warn() { printf '[WARN] %s\n' "$*"; }
|
|
|
|
# Use apex /api/v1 first; if misrouted to Blockscout, try /token-aggregation/api/v1 (nginx layouts differ).
|
|
detect_token_aggregation_prefix() {
|
|
local path="/api/v1/networks"
|
|
local prefix url response status body
|
|
for prefix in "" "/token-aggregation"; do
|
|
url="${BASE_URL%/}${prefix}${path}"
|
|
response="$(curl -sSL --max-time 15 -w $'\n%{http_code}' "$url" 2>/dev/null)" || continue
|
|
status="$(printf '%s' "$response" | tail -n 1)"
|
|
body="$(printf '%s' "$response" | sed '$d')"
|
|
if printf '%s' "$body" | jq -e 'type == "object" and has("message") and has("result") and has("status")' >/dev/null 2>&1; then
|
|
continue
|
|
fi
|
|
if printf '%s' "$body" | jq -e 'type == "object" and (.networks | type == "array")' >/dev/null 2>&1; then
|
|
printf '%s\n' "$prefix"
|
|
return 0
|
|
fi
|
|
done
|
|
printf '%s\n' ""
|
|
}
|
|
fail() {
|
|
printf '[FAIL] %s\n' "$*"
|
|
HAD_FAILURE=1
|
|
if [[ "$SKIP_EXIT" != "1" && "$KEEP_GOING" != "1" ]]; then
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_json_shape() {
|
|
local name="$1"
|
|
local url="$2"
|
|
local jq_expr="$3"
|
|
local expected_desc="$4"
|
|
local response
|
|
local body
|
|
local status
|
|
|
|
if ! response="$(curl -sSL --max-time 20 -w $'\n%{http_code}' "$url" 2>/dev/null)"; then
|
|
fail "$name request failed: $url"
|
|
return 0
|
|
fi
|
|
|
|
status="$(printf '%s' "$response" | tail -n 1)"
|
|
body="$(printf '%s' "$response" | sed '$d')"
|
|
|
|
if printf '%s' "$body" | jq -e 'type == "object" and has("message") and has("result") and has("status")' >/dev/null 2>&1; then
|
|
fail "$name is returning Blockscout-style JSON (HTTP $status) instead of token-aggregation JSON. See docs/04-configuration/TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md"
|
|
return 0
|
|
fi
|
|
|
|
if printf '%s' "$body" | jq -e 'type == "object" and has("error")' >/dev/null 2>&1; then
|
|
local api_error
|
|
api_error="$(printf '%s' "$body" | jq -r '.error' 2>/dev/null || echo 'unknown error')"
|
|
fail "$name returned token-aggregation error payload (HTTP $status): $api_error"
|
|
return 0
|
|
fi
|
|
|
|
if printf '%s' "$body" | jq -e "$jq_expr" >/dev/null 2>&1; then
|
|
ok "$name healthy ($expected_desc, HTTP $status)"
|
|
return 0
|
|
fi
|
|
|
|
local summary
|
|
summary="$(printf '%s' "$body" | head -c 240 | tr '\n' ' ')"
|
|
fail "$name returned unexpected payload (HTTP $status). Expected $expected_desc. Sample: $summary"
|
|
}
|
|
|
|
log "=== Public report API check ==="
|
|
log "Base URL: $BASE_URL"
|
|
TA_PREFIX="$(detect_token_aggregation_prefix)"
|
|
if [[ -n "$TA_PREFIX" ]]; then
|
|
log "Using token-aggregation path prefix: ${TA_PREFIX}/api/v1/"
|
|
else
|
|
log "Using token-aggregation path prefix: /api/v1/ (default; may still be Blockscout if both layouts fail shape test)"
|
|
fi
|
|
log ""
|
|
|
|
check_json_shape \
|
|
"token-list" \
|
|
"${BASE_URL%/}${TA_PREFIX}/api/v1/report/token-list?chainId=138" \
|
|
'type == "object" and (.tokens | type == "array")' \
|
|
'object with .tokens[]'
|
|
|
|
check_json_shape \
|
|
"coingecko report" \
|
|
"${BASE_URL%/}${TA_PREFIX}/api/v1/report/coingecko?chainId=138" \
|
|
'type == "object"' \
|
|
'token-aggregation report JSON object'
|
|
|
|
check_json_shape \
|
|
"cmc report" \
|
|
"${BASE_URL%/}${TA_PREFIX}/api/v1/report/cmc?chainId=138" \
|
|
'type == "object"' \
|
|
'token-aggregation report JSON object'
|
|
|
|
check_json_shape \
|
|
"networks" \
|
|
"${BASE_URL%/}${TA_PREFIX}/api/v1/networks" \
|
|
'type == "object" and (.networks | type == "array")' \
|
|
'object with .networks[]'
|
|
|
|
# Bridge routes (requires token-aggregation build with GET /api/v1/bridge/routes). Off by default until edge is deployed.
|
|
if [[ "${SKIP_BRIDGE_ROUTES:-1}" != "1" ]]; then
|
|
check_json_shape \
|
|
"bridge-routes" \
|
|
"${BASE_URL%/}${TA_PREFIX}/api/v1/bridge/routes" \
|
|
'type == "object" and (.chain138Bridges | type == "object") and (.routes | type == "object")' \
|
|
'object with .chain138Bridges and .routes'
|
|
fi
|
|
|
|
# GRU preflight (shape only; does not require all pairs to be runtime-ready). Off by default until edge is deployed.
|
|
if [[ "${SKIP_BRIDGE_PREFLIGHT:-1}" != "1" ]]; then
|
|
check_json_shape \
|
|
"bridge-preflight" \
|
|
"${BASE_URL%/}${TA_PREFIX}/api/v1/bridge/preflight" \
|
|
'type == "object" and (.gruTransport | type == "object") and (.gruTransport.summary.transportPairs | type == "number") and (.gruTransport.blockedPairs | type == "array")' \
|
|
'object with .gruTransport.summary and .gruTransport.blockedPairs[]'
|
|
fi
|
|
|
|
log ""
|
|
if (( HAD_FAILURE > 0 )); then
|
|
if [[ "$SKIP_EXIT" == "1" ]]; then
|
|
warn "SKIP_EXIT=1 set: non-healthy endpoints were reported without failing."
|
|
elif [[ "$KEEP_GOING" == "1" ]]; then
|
|
exit 1
|
|
fi
|
|
elif [[ "$SKIP_EXIT" == "1" ]]; then
|
|
warn "SKIP_EXIT=1 set: non-healthy endpoints were reported without failing."
|
|
else
|
|
ok "Public report API endpoints look healthy."
|
|
fi
|