mirror of
https://github.com/hanzoai/bootnode.git
synced 2026-09-30 20:46:32 +00:00
feat: add Graph Node + IPFS + subgraph infrastructure
- Add graph-node, graph-ipfs, graph-postgres to compose.yml (subgraph profile) - Add k8s manifests for Graph Node stack (lux/graph-node.yaml) - Add deploy-subgraph.sh for AMM V3/V2 subgraph deployment - Update deploy.sh with 'graph' target for Graph Node infrastructure - Matches existing lux-k8s lux-explorer namespace setup
This commit is contained in:
+73
@@ -223,6 +223,77 @@ services:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
# =============================================================================
|
||||
# OPTIONAL: SUBGRAPH (docker compose --profile subgraph up -d)
|
||||
# Graph Node + IPFS + dedicated Postgres for subgraph indexing
|
||||
# =============================================================================
|
||||
|
||||
graph-postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: bootnode-graph-postgres
|
||||
profiles: ["subgraph"]
|
||||
environment:
|
||||
POSTGRES_USER: graph
|
||||
POSTGRES_PASSWORD: graph-bootnode-2026
|
||||
POSTGRES_DB: graph_node
|
||||
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
|
||||
ports:
|
||||
- "15432:5432"
|
||||
volumes:
|
||||
- graph-postgres:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U graph"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
graph-ipfs:
|
||||
image: ipfs/kubo:v0.24.0
|
||||
container_name: bootnode-graph-ipfs
|
||||
profiles: ["subgraph"]
|
||||
ports:
|
||||
- "15001:5001"
|
||||
- "18080:8080"
|
||||
volumes:
|
||||
- graph-ipfs:/data/ipfs
|
||||
environment:
|
||||
IPFS_PROFILE: server
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "ipfs dag stat /ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
graph-node:
|
||||
image: graphprotocol/graph-node:v0.35.1
|
||||
container_name: bootnode-graph-node
|
||||
profiles: ["subgraph"]
|
||||
environment:
|
||||
postgres_host: graph-postgres
|
||||
postgres_port: 5432
|
||||
postgres_user: graph
|
||||
postgres_pass: graph-bootnode-2026
|
||||
postgres_db: graph_node
|
||||
ipfs: graph-ipfs:5001
|
||||
ethereum: "lux:${LUX_RPC_URL:-http://host.docker.internal:9630/ext/bc/C/rpc}"
|
||||
GRAPH_LOG: info
|
||||
ETHEREUM_REORG_THRESHOLD: 1
|
||||
ETHEREUM_ANCESTOR_COUNT: 1
|
||||
node_id: graph_node
|
||||
ports:
|
||||
- "18000:8000" # GraphQL queries
|
||||
- "18001:8001" # GraphQL websocket
|
||||
- "18020:8020" # Admin (deploy/create subgraphs)
|
||||
- "18030:8030" # Status
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
depends_on:
|
||||
graph-postgres:
|
||||
condition: service_healthy
|
||||
graph-ipfs:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
postgres:
|
||||
redis:
|
||||
@@ -230,3 +301,5 @@ volumes:
|
||||
qdrant:
|
||||
meilisearch:
|
||||
grafana:
|
||||
graph-postgres:
|
||||
graph-ipfs:
|
||||
|
||||
Executable
+103
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deploy or redeploy a subgraph to the Lux graph-node.
|
||||
#
|
||||
# Usage:
|
||||
# ./deploy-subgraph.sh # Deploy all subgraphs
|
||||
# ./deploy-subgraph.sh amm-v3 # Deploy AMM V3 only
|
||||
# ./deploy-subgraph.sh amm-v3 --wipe # Wipe and redeploy AMM V3
|
||||
#
|
||||
# Prerequisites:
|
||||
# - graph-node running (k8s or local compose)
|
||||
# - Port-forwards active or local compose ports
|
||||
# - npx graph-cli available
|
||||
#
|
||||
# Environment variables:
|
||||
# GRAPH_NODE_URL - Graph admin endpoint (default: http://localhost:18020)
|
||||
# GRAPH_IPFS_URL - IPFS endpoint (default: http://localhost:15001)
|
||||
# EXCHANGE_DIR - Exchange repo root (default: ~/work/lux/exchange)
|
||||
set -euo pipefail
|
||||
|
||||
GRAPH_NODE_URL="${GRAPH_NODE_URL:-http://localhost:18020}"
|
||||
GRAPH_IPFS_URL="${GRAPH_IPFS_URL:-http://localhost:15001}"
|
||||
EXCHANGE_DIR="${EXCHANGE_DIR:-$HOME/work/lux/exchange}"
|
||||
|
||||
SUBGRAPH="${1:-all}"
|
||||
WIPE="${2:-}"
|
||||
|
||||
deploy_subgraph() {
|
||||
local name="$1"
|
||||
local dir="$2"
|
||||
local version="$3"
|
||||
|
||||
echo "=== Deploying $name (v$version) ==="
|
||||
|
||||
if [ "$WIPE" = "--wipe" ]; then
|
||||
echo "Removing existing deployment..."
|
||||
curl -sf -X POST "$GRAPH_NODE_URL" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"jsonrpc\":\"2.0\",\"method\":\"subgraph_remove\",\"params\":{\"name\":\"luxfi/$name\"},\"id\":1}" \
|
||||
2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Create subgraph name (idempotent)
|
||||
curl -sf -X POST "$GRAPH_NODE_URL" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"jsonrpc\":\"2.0\",\"method\":\"subgraph_create\",\"params\":{\"name\":\"luxfi/$name\"},\"id\":1}" \
|
||||
2>/dev/null || true
|
||||
|
||||
# Build and deploy
|
||||
cd "$dir"
|
||||
npx graph codegen 2>&1 | tail -2
|
||||
npx graph build 2>&1 | tail -2
|
||||
npx graph deploy "luxfi/$name" \
|
||||
--node "$GRAPH_NODE_URL" \
|
||||
--ipfs "$GRAPH_IPFS_URL" \
|
||||
--version-label "v$version" 2>&1 | tail -5
|
||||
|
||||
echo "Deployed luxfi/$name v$version"
|
||||
echo ""
|
||||
}
|
||||
|
||||
check_sync() {
|
||||
local name="$1"
|
||||
local query_url="${GRAPH_NODE_URL/18020/18000}"
|
||||
# Replace admin port with query port
|
||||
query_url="${query_url/8020/8000}"
|
||||
|
||||
echo "Checking sync status for luxfi/$name..."
|
||||
curl -sf "$query_url/subgraphs/name/luxfi/$name" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"query":"{ _meta { block { number } hasIndexingErrors } }"}' 2>/dev/null \
|
||||
| python3 -m json.tool 2>/dev/null || echo "(not yet syncing)"
|
||||
}
|
||||
|
||||
case "$SUBGRAPH" in
|
||||
amm-v3)
|
||||
deploy_subgraph "amm-v3" "$EXCHANGE_DIR/subgraphs/dex-v3" "1.8.0"
|
||||
check_sync "amm-v3"
|
||||
;;
|
||||
amm-v2)
|
||||
if [ -d "$EXCHANGE_DIR/subgraphs/dex-factory" ]; then
|
||||
deploy_subgraph "amm-v2" "$EXCHANGE_DIR/subgraphs/dex-factory" "1.0.0"
|
||||
check_sync "amm-v2"
|
||||
else
|
||||
echo "AMM V2 subgraph not found at $EXCHANGE_DIR/subgraphs/dex-factory"
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
check_sync "amm-v3"
|
||||
check_sync "amm-v2"
|
||||
;;
|
||||
all)
|
||||
deploy_subgraph "amm-v3" "$EXCHANGE_DIR/subgraphs/dex-v3" "1.8.0"
|
||||
if [ -d "$EXCHANGE_DIR/subgraphs/dex-factory" ]; then
|
||||
deploy_subgraph "amm-v2" "$EXCHANGE_DIR/subgraphs/dex-factory" "1.0.0"
|
||||
fi
|
||||
echo "=== Sync Status ==="
|
||||
check_sync "amm-v3"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {amm-v3|amm-v2|all|status} [--wipe]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deploy bootnode-operator to lux-k8s cluster.
|
||||
#
|
||||
# Prerequisites:
|
||||
# - kubectl configured for lux-k8s cluster
|
||||
# - CRD YAMLs and RBAC applied
|
||||
#
|
||||
# Usage:
|
||||
# ./deploy.sh # Apply all manifests
|
||||
# ./deploy.sh crds # Apply CRDs only
|
||||
# ./deploy.sh operator # Apply operator only
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
apply_crds() {
|
||||
echo "=== Applying CRDs ==="
|
||||
kubectl apply -f "$SCRIPT_DIR/crds.yaml"
|
||||
echo "Waiting for CRDs to be established..."
|
||||
kubectl wait --for=condition=Established crd/clusters.bootnode.dev --timeout=30s
|
||||
kubectl wait --for=condition=Established crd/chaindeployments.bootnode.dev --timeout=30s
|
||||
}
|
||||
|
||||
apply_rbac() {
|
||||
echo "=== Applying RBAC ==="
|
||||
kubectl apply -f "$SCRIPT_DIR/rbac.yaml"
|
||||
}
|
||||
|
||||
apply_operator() {
|
||||
echo "=== Deploying operator ==="
|
||||
kubectl apply -f "$SCRIPT_DIR/operator-deployment.yaml"
|
||||
echo "Waiting for operator to be ready..."
|
||||
kubectl rollout status deployment/bootnode-operator -n bootnode-system --timeout=120s
|
||||
}
|
||||
|
||||
apply_graph() {
|
||||
local ns="${GRAPH_NAMESPACE:-lux-explorer}"
|
||||
echo "=== Deploying Graph Node infrastructure to $ns ==="
|
||||
kubectl apply -f "$SCRIPT_DIR/graph-node.yaml" -n "$ns"
|
||||
echo "Waiting for graph-postgres..."
|
||||
kubectl rollout status statefulset/graph-postgres -n "$ns" --timeout=120s
|
||||
echo "Waiting for graph-ipfs..."
|
||||
kubectl rollout status deployment/graph-ipfs -n "$ns" --timeout=120s
|
||||
echo "Waiting for graph-node..."
|
||||
kubectl rollout status statefulset/graph-node -n "$ns" --timeout=120s
|
||||
echo "Graph Node ready. Deploy subgraphs with: ./deploy-subgraph.sh"
|
||||
}
|
||||
|
||||
case "${1:-all}" in
|
||||
crds)
|
||||
apply_crds
|
||||
;;
|
||||
rbac)
|
||||
apply_rbac
|
||||
;;
|
||||
operator)
|
||||
apply_operator
|
||||
;;
|
||||
graph)
|
||||
apply_graph
|
||||
;;
|
||||
all)
|
||||
apply_crds
|
||||
apply_rbac
|
||||
apply_operator
|
||||
echo "=== Done ==="
|
||||
kubectl get pods -n bootnode-system
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {crds|rbac|operator|graph|all}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,278 @@
|
||||
# Graph Node infrastructure for Lux subgraph indexing
|
||||
# Includes: PostgreSQL, IPFS, Graph Node
|
||||
# Deploy: kubectl apply -f graph-node.yaml -n lux-explorer
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: graph-node-config
|
||||
labels:
|
||||
app: graph-node
|
||||
component: exchange-backend
|
||||
data:
|
||||
config.toml: |
|
||||
[store]
|
||||
[store.primary]
|
||||
connection = "postgresql://graph:graph-lux-2026@graph-postgres:5432/graph_node?sslmode=disable"
|
||||
weight = 1
|
||||
pool_size = 10
|
||||
|
||||
[deployment]
|
||||
[[deployment.rule]]
|
||||
shard = "primary"
|
||||
indexers = ["graph_node"]
|
||||
|
||||
[chains]
|
||||
ingestor = "graph_node"
|
||||
|
||||
[chains.lux]
|
||||
shard = "primary"
|
||||
provider = [
|
||||
{ label = "lux-mainnet", url = "http://luxd-0.luxd-headless.lux-mainnet.svc:9630/ext/bc/C/rpc", features = ["archive"] }
|
||||
]
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: graph-postgres
|
||||
labels:
|
||||
app: graph-postgres
|
||||
component: exchange-backend
|
||||
spec:
|
||||
replicas: 1
|
||||
serviceName: graph-postgres
|
||||
selector:
|
||||
matchLabels:
|
||||
app: graph-postgres
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: graph-postgres
|
||||
component: exchange-backend
|
||||
spec:
|
||||
containers:
|
||||
- name: postgres
|
||||
image: postgres:16-alpine
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
value: graph
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: graph-lux-2026
|
||||
- name: POSTGRES_DB
|
||||
value: graph_node
|
||||
- name: POSTGRES_INITDB_ARGS
|
||||
value: "-E UTF8 --locale=C"
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: 2Gi
|
||||
volumeMounts:
|
||||
- name: graph-postgres-data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
livenessProbe:
|
||||
exec:
|
||||
command: ["pg_isready", "-U", "graph"]
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["pg_isready", "-U", "graph"]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: graph-postgres-data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
storageClassName: do-block-storage
|
||||
resources:
|
||||
requests:
|
||||
storage: 50Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: graph-postgres
|
||||
labels:
|
||||
app: graph-postgres
|
||||
spec:
|
||||
selector:
|
||||
app: graph-postgres
|
||||
ports:
|
||||
- port: 5432
|
||||
targetPort: 5432
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: graph-ipfs
|
||||
labels:
|
||||
app: graph-ipfs
|
||||
component: exchange-backend
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: graph-ipfs
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: graph-ipfs
|
||||
component: exchange-backend
|
||||
spec:
|
||||
containers:
|
||||
- name: ipfs
|
||||
image: ipfs/kubo:v0.24.0
|
||||
env:
|
||||
- name: IPFS_PROFILE
|
||||
value: server
|
||||
ports:
|
||||
- containerPort: 5001
|
||||
name: api
|
||||
- containerPort: 8080
|
||||
name: gateway
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
volumeMounts:
|
||||
- name: ipfs-data
|
||||
mountPath: /data/ipfs
|
||||
volumes:
|
||||
- name: ipfs-data
|
||||
persistentVolumeClaim:
|
||||
claimName: graph-ipfs-data
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: graph-ipfs-data
|
||||
labels:
|
||||
app: graph-ipfs
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
storageClassName: do-block-storage
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: graph-ipfs
|
||||
labels:
|
||||
app: graph-ipfs
|
||||
spec:
|
||||
selector:
|
||||
app: graph-ipfs
|
||||
ports:
|
||||
- port: 5001
|
||||
targetPort: 5001
|
||||
name: api
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
name: gateway
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: graph-node
|
||||
labels:
|
||||
app: graph-node
|
||||
component: exchange-backend
|
||||
spec:
|
||||
replicas: 1
|
||||
serviceName: graph-node
|
||||
selector:
|
||||
matchLabels:
|
||||
app: graph-node
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: graph-node
|
||||
component: exchange-backend
|
||||
spec:
|
||||
containers:
|
||||
- name: graph-node
|
||||
image: graphprotocol/graph-node:v0.35.1
|
||||
env:
|
||||
- name: GRAPH_NODE_CONFIG
|
||||
value: /etc/graph-node/config.toml
|
||||
- name: node_id
|
||||
value: graph_node
|
||||
- name: GRAPH_LOG
|
||||
value: info
|
||||
- name: ipfs
|
||||
value: graph-ipfs:5001
|
||||
- name: ETHEREUM_REORG_THRESHOLD
|
||||
value: "1"
|
||||
- name: ETHEREUM_ANCESTOR_COUNT
|
||||
value: "1"
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
name: graphql
|
||||
- containerPort: 8001
|
||||
name: ws
|
||||
- containerPort: 8020
|
||||
name: admin
|
||||
- containerPort: 8030
|
||||
name: status
|
||||
- containerPort: 8040
|
||||
name: metrics
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 2Gi
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: 4Gi
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/graph-node
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8030
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8030
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: graph-node-config
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: graph-node
|
||||
labels:
|
||||
app: graph-node
|
||||
spec:
|
||||
selector:
|
||||
app: graph-node
|
||||
ports:
|
||||
- port: 8000
|
||||
targetPort: 8000
|
||||
name: graphql
|
||||
- port: 8001
|
||||
targetPort: 8001
|
||||
name: ws
|
||||
- port: 8020
|
||||
targetPort: 8020
|
||||
name: admin
|
||||
- port: 8030
|
||||
targetPort: 8030
|
||||
name: status
|
||||
Reference in New Issue
Block a user