diff --git a/docs/04-configuration/TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md b/docs/04-configuration/TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md index a443f34..1c9c3cf 100644 --- a/docs/04-configuration/TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md +++ b/docs/04-configuration/TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md @@ -13,7 +13,7 @@ bash metamask-integration/chain138-snap/scripts/verify-snap-api-and-icons.sh htt **Expected when working:** Token list and networks return valid JSON with `.tokens` and `.networks`. -**If you see "no .tokens" or "no .networks":** The `/api/v1/` path is likely proxied to Blockscout (or another backend) instead of token-aggregation. Proceed to §2. +**If you see "no .tokens" or "no .networks":** The `/api/v1/` path is likely proxied to Blockscout (or another backend) instead of token-aggregation. Proceed to §2. **Repo check:** `scripts/verify/check-public-report-api.sh` tries apex `/api/v1/` first, then `/token-aggregation/api/v1/`, and uses whichever returns a `.networks` array. --- diff --git a/scripts/lib/load-project-env.sh b/scripts/lib/load-project-env.sh index 069a45e..bff18dc 100644 --- a/scripts/lib/load-project-env.sh +++ b/scripts/lib/load-project-env.sh @@ -13,24 +13,57 @@ export PROJECT_ROOT # err_exit: print message and exit (use when load-project-env is sourced) err_exit() { echo "ERROR: $1" >&2; exit 1; } +# Dotenv / shell env snippets may use ${OTHER_VAR} without :- defaults; callers may use set -u. +_lpr_source_relaxed() { + local f="$1" + [[ -f "$f" ]] || return 0 + local _had_u=0 + [[ -o nounset ]] && _had_u=1 + set +u + # shellcheck disable=SC1090 + source "$f" 2>/dev/null || true + if [[ "$_had_u" -eq 1 ]]; then + set -u + else + set +u + fi +} + +_lpr_dotenv_source() { + local f="$1" + [[ -f "$f" ]] || return 0 + local _had_u=0 + [[ -o nounset ]] && _had_u=1 + set +u + set -a + # shellcheck disable=SC1090 + source "$f" 2>/dev/null || true + set +a + if [[ "$_had_u" -eq 1 ]]; then + set -u + else + set +u + fi +} + # Path validation [[ -d "$PROJECT_ROOT" ]] || err_exit "PROJECT_ROOT not a directory: $PROJECT_ROOT" [[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] || echo "WARN: config/ip-addresses.conf not found; using defaults" >&2 # 1. Root .env (Cloudflare, Proxmox, etc.) -[[ -f "${PROJECT_ROOT}/.env" ]] && set -a && source "${PROJECT_ROOT}/.env" 2>/dev/null && set +a +_lpr_dotenv_source "${PROJECT_ROOT}/.env" # 2. IP/config from centralized config -[[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] && source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true +[[ -f "${PROJECT_ROOT}/config/ip-addresses.conf" ]] && _lpr_source_relaxed "${PROJECT_ROOT}/config/ip-addresses.conf" || true # 3. smom-dbis-138 .env (PRIVATE_KEY, bridge addrs, RPC) — PRIVATE_KEY is read from this dotenv when not set -[[ -f "${PROJECT_ROOT}/smom-dbis-138/.env" ]] && set -a && source "${PROJECT_ROOT}/smom-dbis-138/.env" 2>/dev/null && set +a +_lpr_dotenv_source "${PROJECT_ROOT}/smom-dbis-138/.env" # 3b. Secure secrets (PRIVATE_KEY) — when not set, try ~/.secure-secrets/private-keys.env -[[ -z "${PRIVATE_KEY:-}" ]] && [[ -f "${HOME}/.secure-secrets/private-keys.env" ]] && set -a && source "${HOME}/.secure-secrets/private-keys.env" 2>/dev/null && set +a +[[ -z "${PRIVATE_KEY:-}" ]] && [[ -f "${HOME}/.secure-secrets/private-keys.env" ]] && _lpr_dotenv_source "${HOME}/.secure-secrets/private-keys.env" # 4. dbis_core config if present -[[ -f "${PROJECT_ROOT}/dbis_core/config/dbis-core-proxmox.conf" ]] && source "${PROJECT_ROOT}/dbis_core/config/dbis-core-proxmox.conf" 2>/dev/null || true +[[ -f "${PROJECT_ROOT}/dbis_core/config/dbis-core-proxmox.conf" ]] && _lpr_source_relaxed "${PROJECT_ROOT}/dbis_core/config/dbis-core-proxmox.conf" || true # 4b. Strip trailing CR/LF from RPC URL vars (editor mistakes; breaks cast/curl) for _lpr_k in RPC_URL_138 RPC_URL CHAIN138_RPC CHAIN138_RPC_URL ETHEREUM_MAINNET_RPC \ diff --git a/scripts/run-completable-tasks-from-anywhere.sh b/scripts/run-completable-tasks-from-anywhere.sh index e5acb32..fcc0a42 100755 --- a/scripts/run-completable-tasks-from-anywhere.sh +++ b/scripts/run-completable-tasks-from-anywhere.sh @@ -23,7 +23,7 @@ if $DRY_RUN; then echo " (optional: python3 -m pip install check-jsonschema — step 1 then validates config/dbis-institutional JSON Schemas too)" echo "2. On-chain check (138): SKIP_EXIT=1 bash scripts/verify/check-contracts-on-chain-138.sh || true" echo "3. All validation: bash scripts/verify/run-all-validation.sh --skip-genesis" - echo "4. Public report API: SKIP_EXIT=1 bash scripts/verify/check-public-report-api.sh || true" + echo "4. Public report API: bash scripts/verify/check-public-report-api.sh" echo "5. Reconcile .env: bash scripts/verify/reconcile-env-canonical.sh --print" echo "" echo "Run without --dry-run to execute. Exit 0 = success." @@ -50,7 +50,7 @@ echo "" # 4. Emit canonical .env lines for reconciliation echo "[Step 4/5] Public report API / token-aggregation health..." -SKIP_EXIT=1 bash scripts/verify/check-public-report-api.sh || true +bash scripts/verify/check-public-report-api.sh echo "" # 5. Emit canonical .env lines for reconciliation diff --git a/scripts/verify/README.md b/scripts/verify/README.md index efa6eeb..170c66f 100644 --- a/scripts/verify/README.md +++ b/scripts/verify/README.md @@ -29,7 +29,7 @@ One-line install (Debian/Ubuntu): `sudo apt install -y sshpass rsync dnsutils ip - `backup-npmplus.sh` - Full NPMplus backup (database, API exports, certificates) - `check-contracts-on-chain-138.sh` - Check that Chain 138 deployed contracts have bytecode on-chain (`cast code` for 31 addresses; requires `cast` and RPC access). Use `[RPC_URL]` or env `RPC_URL_138`; `--dry-run` lists addresses only (no RPC calls); `SKIP_EXIT=1` to exit 0 when RPC unreachable. -- `check-public-report-api.sh` - Verify that `explorer.d-bis.org/api/v1/report/*` and `/api/v1/networks` return token-aggregation JSON rather than Blockscout-style `/api/v1` responses. Use `SKIP_EXIT=1` for diagnostic-only mode. Set `SKIP_BRIDGE_ROUTES=0` to assert `/api/v1/bridge/routes`, and `SKIP_BRIDGE_PREFLIGHT=0` to assert `/api/v1/bridge/preflight` payload shape. +- `check-public-report-api.sh` - Verify token-aggregation report + networks JSON (not Blockscout). Probes `/api/v1/networks` first, then `/token-aggregation/api/v1/networks`, and uses the working prefix for all checks. Use `SKIP_EXIT=1` for diagnostic-only mode. Set `SKIP_BRIDGE_ROUTES=0` / `SKIP_BRIDGE_PREFLIGHT=0` for bridge assertions. - `check-token-aggregation-chain138-api.sh` - Hits tokens, pools, quote, `bridge/routes`, `bridge/status`, `bridge/preflight`, and networks on both `/api/v1/*` and `/token-aggregation/api/v1/*`. `BASE_URL=https://explorer.d-bis.org` (default) or `http://192.168.11.140`. - `check-gru-transport-preflight.sh` - Operator-focused GRU runtime preflight. Calls `/api/v1/bridge/preflight`, prints blocked pairs with `eligibilityBlockers` / `runtimeMissingRequirements`, and fails unless all active pairs are runtime-ready or `ALLOW_BLOCKED=1` is set. - `check-cstar-v2-transport-stack.sh` - Predeploy Forge verifier for the `c* V2` bridge stack. Runs the base V2 token suite, legacy reserve-verifier compatibility suite, V2 reserve/verifier full L1/L2 round-trip suite, and the core `CWMultiTokenBridge` round-trip suite. diff --git a/scripts/verify/check-public-report-api.sh b/scripts/verify/check-public-report-api.sh index 2f20c3f..f5b0d57 100755 --- a/scripts/verify/check-public-report-api.sh +++ b/scripts/verify/check-public-report-api.sh @@ -21,6 +21,26 @@ 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 @@ -70,29 +90,35 @@ check_json_shape() { 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/api/v1/report/token-list?chainId=138" \ + "${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/api/v1/report/coingecko?chainId=138" \ + "${BASE_URL%/}${TA_PREFIX}/api/v1/report/coingecko?chainId=138" \ 'type == "object"' \ 'token-aggregation report JSON object' check_json_shape \ "cmc report" \ - "$BASE_URL/api/v1/report/cmc?chainId=138" \ + "${BASE_URL%/}${TA_PREFIX}/api/v1/report/cmc?chainId=138" \ 'type == "object"' \ 'token-aggregation report JSON object' check_json_shape \ "networks" \ - "$BASE_URL/api/v1/networks" \ + "${BASE_URL%/}${TA_PREFIX}/api/v1/networks" \ 'type == "object" and (.networks | type == "array")' \ 'object with .networks[]' @@ -100,7 +126,7 @@ check_json_shape \ if [[ "${SKIP_BRIDGE_ROUTES:-1}" != "1" ]]; then check_json_shape \ "bridge-routes" \ - "$BASE_URL/api/v1/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 @@ -109,7 +135,7 @@ fi if [[ "${SKIP_BRIDGE_PREFLIGHT:-1}" != "1" ]]; then check_json_shape \ "bridge-preflight" \ - "$BASE_URL/api/v1/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