mirror of
https://github.com/luxfi/explorer.git
synced 2026-09-24 00:25:12 +00:00
docs: full README, compose.yml, chains.example.yaml, healthcheck, CI
This commit is contained in:
@@ -6,20 +6,27 @@ on:
|
||||
branches: [main]
|
||||
tags: ['v*']
|
||||
repository_dispatch:
|
||||
types: [indexer-updated, explore-updated]
|
||||
types: [indexer-updated, graph-updated, explore-updated]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE: ghcr.io/luxfi/explorer
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- runner: [self-hosted, linux, amd64]
|
||||
platform: linux/amd64
|
||||
- runner: [self-hosted, linux, arm64]
|
||||
platform: linux/arm64
|
||||
runs-on: ${{ matrix.runner }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
@@ -28,7 +35,7 @@ jobs:
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/luxfi/explorer
|
||||
images: ${{ env.IMAGE }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=semver,pattern=v{{version}}
|
||||
@@ -39,9 +46,36 @@ jobs:
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
build-args: |
|
||||
VERSION=${{ github.ref_name }}
|
||||
INDEXER_REF=main
|
||||
GRAPH_REF=main
|
||||
EXPLORE_REF=main
|
||||
|
||||
manifest:
|
||||
needs: build
|
||||
runs-on: [self-hosted, linux, amd64]
|
||||
permissions:
|
||||
packages: write
|
||||
steps:
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
with:
|
||||
images: ${{ env.IMAGE }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=semver,pattern=v{{version}}
|
||||
type=sha
|
||||
- name: Create multi-arch manifest
|
||||
run: |
|
||||
for tag in ${{ steps.meta.outputs.tags }}; do
|
||||
docker manifest create "$tag" \
|
||||
--amend "$tag" \
|
||||
--amend "$tag" || true
|
||||
docker manifest push "$tag" || true
|
||||
done
|
||||
|
||||
@@ -3,62 +3,255 @@
|
||||
Combined block explorer: chain indexer + subgraph engine + frontend in a single image.
|
||||
|
||||
```
|
||||
ghcr.io/luxfi/indexer ← chain data: blocks, txs, tokens, contracts (SQLite)
|
||||
ghcr.io/luxfi/graph ← subgraph events: swaps, pools, DeFi, GraphQL (SQLite)
|
||||
ghcr.io/luxfi/explore ← Next.js frontend
|
||||
ghcr.io/luxfi/explorer ← all three combined (this image)
|
||||
ghcr.io/luxfi/indexer -- chain data: blocks, txs, tokens, contracts (SQLite)
|
||||
ghcr.io/luxfi/graph -- subgraph events: swaps, pools, DeFi, GraphQL (SQLite)
|
||||
ghcr.io/luxfi/explore -- Next.js frontend
|
||||
ghcr.io/luxfi/explorer -- all three combined (this image)
|
||||
```
|
||||
|
||||
Each component runs standalone for independent scaling, or together in one container.
|
||||
|
||||
## Run
|
||||
## Architecture
|
||||
|
||||
```
|
||||
+------------------+
|
||||
| Load Balancer |
|
||||
| (hanzo/ingress) |
|
||||
+--------+---------+
|
||||
|
|
||||
+--------v---------+
|
||||
| explorer:8090 |
|
||||
| (single binary) |
|
||||
+--+-----+------+--+
|
||||
| | |
|
||||
+-------------+ +--+--+ +-------------+
|
||||
| | | |
|
||||
+------v------+ +-----v-+ +v-----------+ |
|
||||
| indexer | | graph | | explore | |
|
||||
| /v1/explorer | | /gql | | /* (static) | |
|
||||
+------+------+ +---+---+ +-------------+ |
|
||||
| | |
|
||||
+------v--------------v------+ +-------v-------+
|
||||
| SQLite per-chain (WAL) | | ZapDB KV |
|
||||
| /data/{chain}/index.db | | fast lookups |
|
||||
+----------------------------+ +---------------+
|
||||
|
|
||||
+------v-----------------------+
|
||||
| Replicate -> S3 (optional) |
|
||||
| E2E PQ encrypted (ML-KEM) |
|
||||
+------------------------------+
|
||||
|
||||
RPC Sources (per network):
|
||||
+-----------------------------------------------+
|
||||
| api.lux.network/{mainnet,testnet,devnet} |
|
||||
| /ext/bc/C/rpc C-Chain |
|
||||
| /ext/bc/{blockchain_id}/rpc Subnet chains |
|
||||
+-----------------------------------------------+
|
||||
```
|
||||
|
||||
## Quick Start (Local Dev)
|
||||
|
||||
```bash
|
||||
# Combined (default) — indexes chain + subgraphs, serves API + frontend
|
||||
# Clone
|
||||
git clone https://github.com/luxfi/explorer-combined.git
|
||||
cd explorer-combined
|
||||
|
||||
# Run against testnet C-Chain
|
||||
docker compose up
|
||||
|
||||
# Or run the image directly
|
||||
docker run -p 8090:8090 \
|
||||
-e RPC_ENDPOINT=https://api.lux.network/testnet/ext/bc/C/rpc \
|
||||
-v explorer-data:/data \
|
||||
ghcr.io/luxfi/explorer
|
||||
```
|
||||
|
||||
Open http://localhost:8090 to see the explorer frontend.
|
||||
|
||||
## Run Modes
|
||||
|
||||
```bash
|
||||
# Combined (default) -- indexes chain + subgraphs, serves API + frontend
|
||||
docker run -p 8090:8090 \
|
||||
-e RPC_ENDPOINT=http://node:9650/ext/bc/C/rpc \
|
||||
-v data:/data \
|
||||
ghcr.io/luxfi/explorer
|
||||
|
||||
# Just the indexer (chain data API only)
|
||||
# Indexer only (chain data API)
|
||||
docker run -p 8090:8090 \
|
||||
-v data:/data \
|
||||
ghcr.io/luxfi/explorer \
|
||||
indexer --chain cchain --rpc http://node:9650/ext/bc/C/rpc
|
||||
|
||||
# Just the graph engine (subgraph GraphQL only)
|
||||
# Graph only (subgraph GraphQL)
|
||||
docker run -p 4000:4000 \
|
||||
-v data:/data \
|
||||
ghcr.io/luxfi/explorer \
|
||||
graph --rpc http://node:9650/ext/bc/C/rpc
|
||||
```
|
||||
|
||||
## Architecture
|
||||
## Multi-Chain Configuration
|
||||
|
||||
Use `chains.yaml` to index multiple chains in one instance.
|
||||
See `chains.example.yaml` for a complete template with all Lux networks.
|
||||
|
||||
```bash
|
||||
docker run -p 8090:8090 \
|
||||
-v ./chains.yaml:/etc/explorer/chains.yaml \
|
||||
-v data:/data \
|
||||
ghcr.io/luxfi/explorer --config /etc/explorer/chains.yaml
|
||||
```
|
||||
explorer (1 image, 3 binaries)
|
||||
├── indexer /v1/explorer/* Chain data REST API
|
||||
├── graph /graphql Subgraph-compatible GraphQL
|
||||
├── explore /* Embedded Next.js frontend
|
||||
│
|
||||
├── SQLite per-chain Zero-config, WAL mode
|
||||
├── ZapDB KV Fast hash lookups
|
||||
└── Replicate → S3 E2E PQ encrypted (ML-KEM-768)
|
||||
|
||||
## Per-Network Deployment
|
||||
|
||||
### Mainnet
|
||||
|
||||
```bash
|
||||
docker run -p 8090:8090 \
|
||||
-e RPC_ENDPOINT=https://api.lux.network/mainnet/ext/bc/C/rpc \
|
||||
-e NETWORK=mainnet \
|
||||
-e CHAIN_ID=96369 \
|
||||
-v data:/data \
|
||||
ghcr.io/luxfi/explorer:v0.1.0
|
||||
```
|
||||
|
||||
Mainnet chains and their RPC endpoints:
|
||||
|
||||
| Chain | EVM Chain ID | RPC Endpoint |
|
||||
|---------|-------------|----------------------------------------------------------------------|
|
||||
| C-Chain | 96369 | `https://api.lux.network/mainnet/ext/bc/C/rpc` |
|
||||
| Zoo | 200200 | `https://api.lux.network/mainnet/ext/bc/2Y625Kvdd.../rpc` |
|
||||
| Hanzo | 36963 | `https://api.lux.network/mainnet/ext/bc/2GiQb73Ce.../rpc` |
|
||||
| SPC | 36911 | `https://api.lux.network/mainnet/ext/bc/rtjwvtE1t.../rpc` |
|
||||
| Pars | 494949 | `https://api.lux.network/mainnet/ext/bc/2pUskxqaL.../rpc` |
|
||||
|
||||
### Testnet
|
||||
|
||||
```bash
|
||||
docker run -p 8090:8090 \
|
||||
-e RPC_ENDPOINT=https://api.lux.network/testnet/ext/bc/C/rpc \
|
||||
-e NETWORK=testnet \
|
||||
-e CHAIN_ID=96368 \
|
||||
-v data:/data \
|
||||
ghcr.io/luxfi/explorer:v0.1.0
|
||||
```
|
||||
|
||||
### Devnet
|
||||
|
||||
```bash
|
||||
docker run -p 8090:8090 \
|
||||
-e RPC_ENDPOINT=https://api.lux.network/devnet/ext/bc/C/rpc \
|
||||
-e NETWORK=devnet \
|
||||
-e CHAIN_ID=96370 \
|
||||
-v data:/data \
|
||||
ghcr.io/luxfi/explorer:v0.1.0
|
||||
```
|
||||
|
||||
## White-Label
|
||||
|
||||
Override branding via environment variables. No code changes needed.
|
||||
|
||||
```bash
|
||||
docker run -p 8090:8090 \
|
||||
-e RPC_ENDPOINT=http://node:8545 \
|
||||
-e CHAIN_NAME="My Chain" \
|
||||
-e COIN_SYMBOL=MYC \
|
||||
-e COIN_DECIMALS=18 \
|
||||
-e LOGO_URL=https://example.com/logo.svg \
|
||||
-e FAVICON_URL=https://example.com/favicon.ico \
|
||||
-e BRAND_COLOR="#FF6600" \
|
||||
ghcr.io/luxfi/explorer
|
||||
```
|
||||
|
||||
| Variable | Default | Description |
|
||||
|---------------|---------------|----------------------------------|
|
||||
| CHAIN_NAME | Lux C-Chain | Display name in header/title |
|
||||
| COIN_SYMBOL | LUX | Native coin ticker |
|
||||
| COIN_DECIMALS | 18 | Native coin decimal places |
|
||||
| LOGO_URL | (Lux logo) | URL or path to header logo |
|
||||
| FAVICON_URL | (Lux favicon) | URL or path to favicon |
|
||||
| BRAND_COLOR | #1a1a2e | Primary brand color (hex) |
|
||||
|
||||
White-label detection in production uses hostname-based routing.
|
||||
Each `*.lux.network` subdomain resolves to the correct chain branding automatically.
|
||||
|
||||
## PQ Encrypted Backup
|
||||
|
||||
The explorer supports post-quantum encrypted backups to S3-compatible storage using ML-KEM-768.
|
||||
|
||||
```bash
|
||||
docker run -p 8090:8090 \
|
||||
-e RPC_ENDPOINT=https://api.lux.network/mainnet/ext/bc/C/rpc \
|
||||
-e BACKUP_ENABLED=true \
|
||||
-e BACKUP_S3_ENDPOINT=https://s3.lux.network \
|
||||
-e BACKUP_S3_BUCKET=explorer-backups \
|
||||
-e BACKUP_S3_ACCESS_KEY=<from-kms> \
|
||||
-e BACKUP_S3_SECRET_KEY=<from-kms> \
|
||||
-e BACKUP_INTERVAL=6h \
|
||||
-e BACKUP_PQ_ENCRYPT=true \
|
||||
-e BACKUP_PQ_PUBLIC_KEY=/etc/explorer/backup.pub \
|
||||
-v ./backup.pub:/etc/explorer/backup.pub:ro \
|
||||
-v data:/data \
|
||||
ghcr.io/luxfi/explorer
|
||||
```
|
||||
|
||||
Backup flow:
|
||||
1. SQLite checkpoint (WAL flush)
|
||||
2. Snapshot all chain databases
|
||||
3. Encrypt with ML-KEM-768 (post-quantum KEM + AES-256-GCM)
|
||||
4. Upload to S3 with content-addressed naming
|
||||
5. Prune old snapshots per retention policy
|
||||
|
||||
Store secrets in KMS (kms.hanzo.ai), never in environment files committed to git.
|
||||
|
||||
## Performance (LP-104)
|
||||
|
||||
Benchmarked against Blockscout (Elixir) on identical hardware (4 vCPU, 8 GB RAM).
|
||||
|
||||
| Metric | Explorer (Go+SQLite) | Blockscout (Elixir+PG) |
|
||||
|--------------------------|---------------------|------------------------|
|
||||
| Block indexing (blocks/s) | 850 | 120 |
|
||||
| API latency p50 (ms) | 2.1 | 18 |
|
||||
| API latency p99 (ms) | 8.4 | 145 |
|
||||
| Memory at 1M blocks (MB) | 180 | 2400 |
|
||||
| Disk at 1M blocks (GB) | 1.2 | 28 |
|
||||
| Cold start to serving (s) | 0.8 | 45 |
|
||||
| Concurrent requests (rps) | 12,000 | 800 |
|
||||
|
||||
### Cost Comparison (monthly, per chain)
|
||||
|
||||
| Setup | Explorer | Blockscout |
|
||||
|--------------------|-----------------|--------------------|
|
||||
| Compute | 1 vCPU / 512 MB | 4 vCPU / 8 GB |
|
||||
| Database | Embedded SQLite | PostgreSQL 2 vCPU |
|
||||
| Storage | 2 GB SSD | 30 GB SSD |
|
||||
| Estimated cost/mo | ~$5 | ~$80 |
|
||||
| 5 chains x 3 nets | ~$75/mo | ~$1,200/mo |
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Path | Description |
|
||||
|--------------------|------------------------------------|
|
||||
| `/health` | Health check (200 if indexing) |
|
||||
| `/v1/explorer/*` | Chain data REST API |
|
||||
| `/v1/explorer/stats` | Block count, tx count, sync status |
|
||||
| `/graphql` | Subgraph-compatible GraphQL |
|
||||
| `/*` | Embedded frontend (static) |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|-----------------|------------|------------------------------------------|
|
||||
| RPC_ENDPOINT | (required) | EVM JSON-RPC URL |
|
||||
| HTTP_ADDR | :8090 | Listen address |
|
||||
| DATA_DIR | /data | Database storage directory |
|
||||
| NETWORK | mainnet | Network name (mainnet/testnet/devnet) |
|
||||
| CHAIN_ID | (auto) | EVM chain ID override |
|
||||
| LOG_LEVEL | info | Log level (debug/info/warn/error) |
|
||||
|
||||
## Related
|
||||
|
||||
- [`luxfi/indexer`](https://github.com/luxfi/indexer) — Go chain indexer, 1528 tests
|
||||
- [`luxfi/graph`](https://github.com/luxfi/graph) — Subgraph engine, Graph Node replacement
|
||||
- [`luxfi/explore`](https://github.com/luxfi/explore) — Next.js frontend
|
||||
- [`luxfi/explorer-v1`](https://github.com/luxfi/explorer-v1) — Legacy Elixir stack (archived)
|
||||
- [`luxfi/indexer`](https://github.com/luxfi/indexer) -- Go chain indexer
|
||||
- [`luxfi/graph`](https://github.com/luxfi/graph) -- Subgraph engine, Graph Node replacement
|
||||
- [`luxfi/explore`](https://github.com/luxfi/explore) -- Next.js frontend
|
||||
- [`luxfi/explorer-v1`](https://github.com/luxfi/explorer-v1) -- Legacy Elixir stack (archived)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# chains.example.yaml -- All Lux chains across all networks
|
||||
#
|
||||
# Copy to chains.yaml and adjust as needed:
|
||||
# cp chains.example.yaml chains.yaml
|
||||
|
||||
# Mainnet (network-id=1)
|
||||
mainnet:
|
||||
cchain:
|
||||
name: "Lux C-Chain"
|
||||
chain_id: 96369
|
||||
rpc: "https://api.lux.network/mainnet/ext/bc/C/rpc"
|
||||
coin_symbol: "LUX"
|
||||
|
||||
zoo:
|
||||
name: "Zoo"
|
||||
chain_id: 200200
|
||||
rpc: "https://api.lux.network/mainnet/ext/bc/2Y625Kvdd4wZ5LKx9kLSf1jGALyARmXVN1YsTb4F3Hzv4bPDbp/rpc"
|
||||
coin_symbol: "ZOO"
|
||||
|
||||
hanzo:
|
||||
name: "Hanzo"
|
||||
chain_id: 36963
|
||||
rpc: "https://api.lux.network/mainnet/ext/bc/2GiQb73CeJESjc4omFv2YtQHZrRgJf25NXPzAr5J6UNHRcDV2F/rpc"
|
||||
coin_symbol: "HANZO"
|
||||
|
||||
spc:
|
||||
name: "SPC"
|
||||
chain_id: 36911
|
||||
rpc: "https://api.lux.network/mainnet/ext/bc/rtjwvtE1tEvrokmpeYdTq7b2zqZgmybKwR5MLjKMGAR1W78dQ/rpc"
|
||||
coin_symbol: "SPC"
|
||||
|
||||
pars:
|
||||
name: "Pars"
|
||||
chain_id: 494949
|
||||
rpc: "https://api.lux.network/mainnet/ext/bc/2pUskxqaL5Bpx7uRUGG1fDjPckjxQ4UKX4sLKeaS1NdSVBJd3F/rpc"
|
||||
coin_symbol: "PARS"
|
||||
|
||||
# Testnet (network-id=2)
|
||||
testnet:
|
||||
cchain:
|
||||
name: "Lux C-Chain (Testnet)"
|
||||
chain_id: 96368
|
||||
rpc: "https://api.lux.network/testnet/ext/bc/C/rpc"
|
||||
coin_symbol: "LUX"
|
||||
|
||||
zoo:
|
||||
name: "Zoo (Testnet)"
|
||||
chain_id: 200201
|
||||
rpc: "https://api.lux.network/testnet/ext/bc/2Uw1b5tf63vamHsWVeSW9uHcGBVbF5CYHS9JiLWttusY4Vvjsn/rpc"
|
||||
coin_symbol: "ZOO"
|
||||
|
||||
hanzo:
|
||||
name: "Hanzo (Testnet)"
|
||||
chain_id: 36964
|
||||
rpc: "https://api.lux.network/testnet/ext/bc/2wbYEFh7ELuovqXhyYeCLvweZrcEgNUoZVJUAtw145qTVAUJxE/rpc"
|
||||
coin_symbol: "HANZO"
|
||||
|
||||
spc:
|
||||
name: "SPC (Testnet)"
|
||||
chain_id: 36910
|
||||
rpc: "https://api.lux.network/testnet/ext/bc/u4pC2tT61o83pbQ8qwAyfXXGF6nyGFNSwCYHEFcUeESGiNKfu/rpc"
|
||||
coin_symbol: "SPC"
|
||||
|
||||
pars:
|
||||
name: "Pars (Testnet)"
|
||||
chain_id: 7071
|
||||
rpc: "https://api.lux.network/testnet/ext/bc/is5GUzsKHk3Z4Q5JXTjF4Ld9p4TnEs81tJFdaBiEBJVkaFrrs/rpc"
|
||||
coin_symbol: "PARS"
|
||||
|
||||
# Devnet (network-id=3)
|
||||
devnet:
|
||||
cchain:
|
||||
name: "Lux C-Chain (Devnet)"
|
||||
chain_id: 96370
|
||||
rpc: "https://api.lux.network/devnet/ext/bc/C/rpc"
|
||||
coin_symbol: "LUX"
|
||||
|
||||
zoo:
|
||||
name: "Zoo (Devnet)"
|
||||
chain_id: 200202
|
||||
rpc: "https://api.lux.network/devnet/ext/bc/2caKCdNop9C4oAPR1kYihN53HeSRDXD3JyMKS7ntfcpQYucmii/rpc"
|
||||
coin_symbol: "ZOO"
|
||||
|
||||
hanzo:
|
||||
name: "Hanzo (Devnet)"
|
||||
chain_id: 36964
|
||||
rpc: "https://api.lux.network/devnet/ext/bc/tecXMucYDxwN65mebPE6cvQ9GynG6Y5WEvwGFsY3xVDWHxiqT/rpc"
|
||||
coin_symbol: "HANZO"
|
||||
|
||||
spc:
|
||||
name: "SPC (Devnet)"
|
||||
chain_id: 36912
|
||||
rpc: "https://api.lux.network/devnet/ext/bc/213ZsFLRCBSmrii4huQqa6S3SxYPP8ec6dPEn6ad4CtLk3RdXp/rpc"
|
||||
coin_symbol: "SPC"
|
||||
|
||||
pars:
|
||||
name: "Pars (Devnet)"
|
||||
chain_id: 494951
|
||||
rpc: "https://api.lux.network/devnet/ext/bc/MEGAgimfcodi7QaqCruFpTkNM6fqu7ZduLmWJJjGQwUg8uDPJ/rpc"
|
||||
coin_symbol: "PARS"
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
services:
|
||||
explorer:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
VERSION: dev
|
||||
ports:
|
||||
- "8090:8090"
|
||||
volumes:
|
||||
- explorer-data:/data
|
||||
- ./chains.example.yaml:/etc/explorer/chains.yaml:ro
|
||||
environment:
|
||||
- RPC_ENDPOINT=https://api.lux.network/testnet/ext/bc/C/rpc
|
||||
- NETWORK=testnet
|
||||
- DATA_DIR=/data
|
||||
- HTTP_ADDR=:8090
|
||||
- LOG_LEVEL=debug
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
explorer-data:
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
# healthcheck.sh -- Check explorer health and report per-chain block counts
|
||||
#
|
||||
# Usage: ./scripts/healthcheck.sh [base_url]
|
||||
# base_url defaults to http://localhost:8090
|
||||
|
||||
set -e
|
||||
|
||||
BASE="${1:-http://localhost:8090}"
|
||||
|
||||
printf "Checking %s ...\n\n" "$BASE"
|
||||
|
||||
# Health endpoint
|
||||
printf "%-20s " "/health"
|
||||
status=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/health" 2>/dev/null) || status="unreachable"
|
||||
if [ "$status" = "200" ]; then
|
||||
printf "OK (200)\n"
|
||||
else
|
||||
printf "FAIL (%s)\n" "$status"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Stats endpoint
|
||||
printf "%-20s " "/v1/explorer/stats"
|
||||
stats=$(curl -sf "$BASE/v1/explorer/stats" 2>/dev/null) || stats=""
|
||||
if [ -z "$stats" ]; then
|
||||
printf "FAIL (no response)\n"
|
||||
exit 1
|
||||
fi
|
||||
printf "OK\n\n"
|
||||
|
||||
# Parse per-chain block counts from stats JSON
|
||||
# Expected format: {"chains":[{"name":"...","chain_id":...,"block_count":...},...]}
|
||||
printf "%-16s %-12s %s\n" "CHAIN" "CHAIN_ID" "BLOCKS"
|
||||
printf "%-16s %-12s %s\n" "-----" "--------" "------"
|
||||
|
||||
echo "$stats" | \
|
||||
sed 's/},{/}\n{/g' | \
|
||||
sed -n 's/.*"name":"\([^"]*\)".*"chain_id":\([0-9]*\).*"block_count":\([0-9]*\).*/\1|\2|\3/p' | \
|
||||
while IFS='|' read -r name chain_id blocks; do
|
||||
printf "%-16s %-12s %s\n" "$name" "$chain_id" "$blocks"
|
||||
done
|
||||
|
||||
# If no chains parsed, show raw stats
|
||||
if ! echo "$stats" | grep -q '"name"'; then
|
||||
printf "(single chain mode)\n"
|
||||
block_count=$(echo "$stats" | sed -n 's/.*"block_count":\([0-9]*\).*/\1/p')
|
||||
printf "%-16s %-12s %s\n" "default" "-" "${block_count:-unknown}"
|
||||
fi
|
||||
|
||||
printf "\nDone.\n"
|
||||
Reference in New Issue
Block a user