From 2476423362ec1a11e79eef04b064c109488d8a84 Mon Sep 17 00:00:00 2001 From: Satoshi Qazi Muhammed Date: Thu, 15 Oct 2026 10:52:35 +0400 Subject: [PATCH] Add a Zagros Asset Hub history indexer for the explorer host The explorer at explorer.pezkuwichain.io needs transfer history for Zagros Asset Hub. It runs on the explorer host next to its own archive node, so the manifest points at 127.0.0.1:9955 and the compose file binds everything locally with memory limits and no published ports. deploy-vps now ignores these files: a change to the Zagros indexer must not rebuild or restart the mainnet stack and its bots on the indexer host. --- .github/workflows/deploy-vps.yml | 5 ++ .gitignore | 1 + docker-compose.zagros.yml | 82 ++++++++++++++++++++++++++ ops/zagros/README.md | 38 ++++++++++++ zagros-assethub.yaml | 99 ++++++++++++++++++++++++++++++++ 5 files changed, 225 insertions(+) create mode 100644 docker-compose.zagros.yml create mode 100644 ops/zagros/README.md create mode 100644 zagros-assethub.yaml diff --git a/.github/workflows/deploy-vps.yml b/.github/workflows/deploy-vps.yml index 76919cc..f999815 100644 --- a/.github/workflows/deploy-vps.yml +++ b/.github/workflows/deploy-vps.yml @@ -2,6 +2,11 @@ name: Deploy to VPS on: push: branches: [main] + # The Zagros history indexer runs on the explorer host, not here. + paths-ignore: + - zagros-assethub.yaml + - docker-compose.zagros.yml + - ops/zagros/** concurrency: group: deploy-vps diff --git a/.gitignore b/.gitignore index 564b7a1..f95497f 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ secrets/ # SubQuery .data/ project.yaml +.env.zagros diff --git a/docker-compose.zagros.yml b/docker-compose.zagros.yml new file mode 100644 index 0000000..fe5d70d --- /dev/null +++ b/docker-compose.zagros.yml @@ -0,0 +1,82 @@ +# Zagros Asset Hub history for explorer.pezkuwichain.io. +# +# Runs on the explorer host next to its own archive node (systemd unit +# zagros-ah-archive, RPC on 127.0.0.1:9955), never on the mainnet indexer host: +# deploy-vps ignores this file and zagros-assethub.yaml. +# +# Everything uses the host network. Postgres binds 127.0.0.1; subql-node (3101) +# and subql-query (3100) cannot be told to, so the host firewall (default deny, +# only 22/80/443 open) keeps them local. Docker would bypass that firewall for +# published ports, so nothing is published. +# nginx exposes only POST /history/assethub (same origin as the explorer). +# +# printf 'ZAGROS_DB_PASS=%s\n' "$(openssl rand -hex 24)" > .env.zagros && chmod 600 .env.zagros +# docker compose -f docker-compose.zagros.yml --env-file .env.zagros up -d +# +# The mapping in dist/ must be built first (see ops/zagros/README.md). +name: zagros-history + +services: + postgres: + image: postgres:16-alpine + network_mode: host + command: ["postgres", "-c", "listen_addresses=127.0.0.1", "-c", "port=5433"] + volumes: + - zagros-pgdata:/var/lib/postgresql/data + - ./docker/init:/docker-entrypoint-initdb.d + environment: + POSTGRES_PASSWORD: ${ZAGROS_DB_PASS:?set ZAGROS_DB_PASS in .env.zagros} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -h 127.0.0.1 -p 5433"] + interval: 5s + timeout: 5s + retries: 10 + mem_limit: 1g + restart: unless-stopped + + subquery-node-assethub: + build: + context: . + dockerfile: docker/Dockerfile.node + network_mode: host + depends_on: + postgres: { condition: service_healthy } + environment: + TZ: UTC + DB_USER: postgres + DB_PASS: ${ZAGROS_DB_PASS:?set ZAGROS_DB_PASS in .env.zagros} + DB_DATABASE: postgres + DB_HOST: 127.0.0.1 + DB_PORT: 5433 + volumes: + - ./:/app/project:ro + command: + - -f=/app/project/zagros-assethub.yaml + - --db-schema=subquery-zagros-assethub + - --disable-historical=true + - --batch-size=30 + - --port=3101 + mem_limit: 1g + restart: unless-stopped + + graphql-engine-assethub: + image: onfinality/subql-query:v1.5.0 + network_mode: host + depends_on: + - subquery-node-assethub + environment: + DB_USER: postgres + DB_PASS: ${ZAGROS_DB_PASS:?set ZAGROS_DB_PASS in .env.zagros} + DB_DATABASE: postgres + DB_HOST: 127.0.0.1 + DB_PORT: 5433 + command: + - --name=subquery-zagros-assethub + - --indexer=http://127.0.0.1:3101 + - --port=3100 + - --query-limit=100 + mem_limit: 512m + restart: unless-stopped + +volumes: + zagros-pgdata: diff --git a/ops/zagros/README.md b/ops/zagros/README.md new file mode 100644 index 0000000..dfe9f5e --- /dev/null +++ b/ops/zagros/README.md @@ -0,0 +1,38 @@ +# Zagros Asset Hub history (explorer host) + +Serves `HistoryElement` for Zagros Asset Hub to explorer.pezkuwichain.io. It +runs on the explorer host next to its own archive node, never on the mainnet +indexer host (`deploy-vps` ignores `zagros-assethub.yaml`, +`docker-compose.zagros.yml` and this directory). + +## Pieces + +| piece | where | listens | +|---|---|---| +| archive node `zagros-ah-archive` (systemd, `--state-pruning archive --blocks-pruning archive`, relay followed over RPC) | host | RPC 127.0.0.1:9955 | +| postgres 16 | compose | 127.0.0.1:5433 | +| subql-node, `zagros-assethub.yaml` | compose | 3101 (firewalled) | +| subql-query | compose | 3100 (firewalled); nginx exposes `POST /history/assethub` only | + +## Build and start + +```sh +git clone https://github.com/pezkuwichain/pezkuwi-subquery /opt/zagros-history +cd /opt/zagros-history +# Build the mapping (dist/) without installing Node on the host. +docker run --rm -v "$PWD":/w -w /w node:24-alpine sh -c \ + 'corepack enable && yarn install --immutable && yarn subql codegen zagros-assethub.yaml && yarn subql build zagros-assethub.yaml' +printf 'ZAGROS_DB_PASS=%s\n' "$(openssl rand -hex 24)" > .env.zagros && chmod 600 .env.zagros +docker compose -f docker-compose.zagros.yml --env-file .env.zagros up -d --build +``` + +## Check + +```sh +curl -s -X POST 127.0.0.1:3100 -H 'content-type: application/json' \ + -d '{"query":"{_metadata{lastProcessedHeight targetHeight}}"}' +``` + +`targetHeight - lastProcessedHeight` above 100 for ten minutes means the +indexer has stalled; the explorer then shows "on its way" for payments it has +not seen yet. diff --git a/zagros-assethub.yaml b/zagros-assethub.yaml new file mode 100644 index 0000000..24f6a66 --- /dev/null +++ b/zagros-assethub.yaml @@ -0,0 +1,99 @@ +specVersion: 1.0.0 +name: subquery-zagros-assethub +version: 1.0.0 +runner: + node: + name: "@subql/node" + version: ">=4.6.6" + query: + name: "@subql/query" + version: "*" +description: Zagros Asset Hub SubQuery - history for explorer.pezkuwichain.io (same handlers as pezkuwi-assethub.yaml) +repository: https://github.com/pezkuwichain/pezkuwi-subquery +schema: + file: ./schema.graphql +network: + chainId: "0x063f739077881df9f291548de301b2e11d6a45cc545c24e7bf18d8a40f0ce182" + endpoint: + - ws://127.0.0.1:9955 +dataSources: + - name: main + kind: substrate/Runtime + startBlock: 1 + mapping: + file: ./dist/index.js + handlers: + # Block handler: initialize active pool stakers from chain state + - handler: handleBlock + kind: substrate/BlockHandler + # Signed extrinsics for history + - handler: handleHistoryElement + kind: substrate/CallHandler + filter: + isSigned: true + # Nomination Pools rewards + - handler: handlePoolReward + kind: substrate/EventHandler + filter: + module: nominationPools + method: PaidOut + # Pool member bonded (track active stakers) + - handler: handlePoolBonded + kind: substrate/EventHandler + filter: + module: nominationPools + method: Bonded + # Pool member unbonded (cleanup active stakers) + - handler: handlePoolUnbonded + kind: substrate/EventHandler + filter: + module: nominationPools + method: Unbonded + # Pool bonded slash + - handler: handlePoolBondedSlash + kind: substrate/EventHandler + filter: + module: nominationPools + method: PoolSlashed + # Pool unbonding slash + - handler: handlePoolUnbondingSlash + kind: substrate/EventHandler + filter: + module: nominationPools + method: UnbondingPoolSlashed + # Staking rewards on AH (validators + direct nominators) + - handler: handleAHRewarded + kind: substrate/EventHandler + filter: + module: staking + method: Rewarded + # Staking slashes on AH + - handler: handleAHSlashed + kind: substrate/EventHandler + filter: + module: staking + method: Slashed + # Era changes on AH (staking pallet lives here) - old format + - handler: handleAHNewEra + kind: substrate/EventHandler + filter: + module: staking + method: StakingElection + # Era changes on AH - new format (Polkadot 2.0) + - handler: handleAHStakersElected + kind: substrate/EventHandler + filter: + module: staking + method: StakersElected + # Native transfers + - handler: handleTransfer + kind: substrate/EventHandler + filter: + module: balances + method: Transfer + # Asset transfers + - handler: handleAssetTransfer + kind: substrate/EventHandler + filter: + module: assets + method: Transferred