diff --git a/docker/testnet/Makefile b/docker/testnet/Makefile index 5e20abc1..5c7de6c4 100644 --- a/docker/testnet/Makefile +++ b/docker/testnet/Makefile @@ -1,25 +1,28 @@ # Orbinum Testnet — node operations # -# Auto-detects the node role from which compose file exists on this server: -# - docker-compose.rpc.yml present → RPC / bootnode -# - docker-compose.yml present → validator +# Picks the node role. Override with ROLE=rpc or ROLE=validator on any command: +# make down # auto-detect (rpc if its compose exists, else validator) +# make down ROLE=validator # force validator (needed when BOTH compose files exist) +# make up ROLE=rpc # Same Makefile on every server; each one operates on its own node. # # Usage: make (run from this directory) # make help lists all targets -# ── Role auto-detection ─────────────────────────────────────────────────────── -# Prefer the RPC compose if it exists, else fall back to the validator compose. -COMPOSE := $(firstword $(wildcard docker-compose.rpc.yml docker-compose.yml)) -# The volume name is "_"; project defaults to this dir's name. +# ── Role selection ──────────────────────────────────────────────────────────── +# ROLE may be set on the command line. If unset, auto-detect: prefer the RPC +# compose when present, else the validator compose. When BOTH files exist on the +# same checkout (e.g. the dev repo), auto-detect always picks rpc — pass +# ROLE=validator explicitly to operate on the validator stack. +ROLE ?= $(if $(wildcard docker-compose.rpc.yml),rpc,validator) PROJECT := $(notdir $(CURDIR)) -ifeq ($(COMPOSE),docker-compose.rpc.yml) +ifeq ($(ROLE),rpc) + COMPOSE := docker-compose.rpc.yml CONTAINER := orbinum-rpc-node - ROLE := RPC VOLUME := $(PROJECT)_rpc-node-data else + COMPOSE := docker-compose.yml CONTAINER := orbinum-validator - ROLE := validator VOLUME := $(PROJECT)_validator-data endif @@ -94,6 +97,19 @@ reset-chain: ## Wipe chain DB + network cache for a re-genesis. KEEPS keystore. rm -rf /data/chains/orbinum_testnet/db /data/chains/orbinum_testnet/network @echo "✅ Chain data wiped. Run 'make up' to start from the new genesis." +# ── Full cleanup (DESTRUCTIVE — wipes EVERYTHING) ───────────────────────────── +.PHONY: clean +clean: ## Wipe ALL: containers, volumes (chain DB + keystore), networks, images. Asks first. + @echo "⚠️ This permanently DESTROYS everything for $(ROLE) ($(CONTAINER)):" + @echo " - containers (stopped + removed)" + @echo " - volumes → chain database AND keystore (session keys LOST)" + @echo " - networks (project networks removed)" + @echo " - images (compose images removed)" + @echo " This is NOT recoverable. Back up the keystore first if you need it." + @printf " Type 'yes' to continue: " && read ans && [ "$$ans" = "yes" ] || (echo "aborted" && exit 1) + $(DC) down --volumes --remove-orphans --rmi all + @echo "✅ Full cleanup done. Run 'make up' to start fresh." + # ── Help ────────────────────────────────────────────────────────────────────── .PHONY: help help: ## Show this help diff --git a/docker/testnet/README.md b/docker/testnet/README.md index 5e8871ba..ab0662d5 100644 --- a/docker/testnet/README.md +++ b/docker/testnet/README.md @@ -173,7 +173,7 @@ With your `.env` file in place (see A.3), start the node from `node/docker/testn ```bash docker compose pull # download pre-built image (~1-2 min) docker compose up -d # start validator + watchtower -docker compose logs -f validator +docker compose logs -f orbinum-validator ``` Wait until you see `Idle` in the logs before proceeding: @@ -185,7 +185,7 @@ Wait until you see `Idle` in the logs before proceeding: Get your **Peer ID** — you will need it when requesting approval: ```bash -docker compose logs validator | grep "Local node identity" +docker compose logs orbinum-validator | grep "Local node identity" # -> Local node identity is: 12D3KooWxxxxx... ``` @@ -264,8 +264,8 @@ This writes the keys into the **node's local keystore** via RPC. These are local The validator's RPC port (`9944`) is **not** published to the host or the network — it only listens on `127.0.0.1` *inside* the container. Run the calls -with `docker compose exec`, which executes them from inside the container where -`localhost:9944` is reachable (the image ships `curl` for this purpose). +with `docker exec orbinum-validator`, which executes them from inside the container +where `localhost:9944` is reachable (the image ships `curl` for this purpose). The node must be running before you proceed. @@ -273,18 +273,45 @@ The node must be running before you proceed. # Replace , , with your values # Aura (Sr25519) -docker compose exec validator curl -s -H "Content-Type: application/json" \ +docker exec orbinum-validator curl -s -H "Content-Type: application/json" \ -d '{"id":1,"jsonrpc":"2.0","method":"author_insertKey","params":["aura","",""]}' \ http://localhost:9944 # GRANDPA (Ed25519) -docker compose exec validator curl -s -H "Content-Type: application/json" \ +docker exec orbinum-validator curl -s -H "Content-Type: application/json" \ -d '{"id":1,"jsonrpc":"2.0","method":"author_insertKey","params":["gran","",""]}' \ http://localhost:9944 ``` Each call returns `{"result":null}` on success. +**Verify the keys were inserted.** Two files must exist in the keystore — one for +aura, one for grandpa: + +```bash +docker exec orbinum-validator ls /data/chains/orbinum_testnet/keystore/ +# expect 2 files (the 8-hex prefix is the key type: 61757261=aura, 6772616e=gran) +``` + +You can also ask the node directly whether it holds each public key: + +```bash +# Aura — replace +docker exec orbinum-validator curl -s -H "Content-Type: application/json" \ + -d '{"id":1,"jsonrpc":"2.0","method":"author_hasKey","params":["","aura"]}' \ + http://localhost:9944 + +# GRANDPA — replace +docker exec orbinum-validator curl -s -H "Content-Type: application/json" \ + -d '{"id":1,"jsonrpc":"2.0","method":"author_hasKey","params":["","gran"]}' \ + http://localhost:9944 +``` + +Both must return `{"result":true}`. If either is `false`, the seed and the public +key don't match — re-check them before continuing (GRANDPA finalization needs ≥2 of +3 validators holding their `gran` key, or the chain produces blocks but never +finalizes). + --- ### 5. Get Your EVM Relay Address @@ -292,7 +319,7 @@ Each call returns `{"result":null}` on success. After inserting the Aura key, restart the node: ```bash -docker compose restart validator +docker compose restart orbinum-validator ``` The node reads the Aura key from the keystore, derives the EVM relay address, and prints it in the logs: @@ -322,7 +349,7 @@ First, get the combined session key from the node (same `docker compose exec` pattern as Step 4 — the RPC is only reachable inside the container): ```bash -docker compose exec validator curl -s -H "Content-Type: application/json" \ +docker exec orbinum-validator curl -s -H "Content-Type: application/json" \ -d '{"id":1,"jsonrpc":"2.0","method":"author_rotateKeys","params":[]}' \ http://localhost:9944 # Returns: {"result":"0x"} @@ -440,7 +467,7 @@ If you are still in the **pending** queue (not yet approved), you can also call ```bash # View validator logs -docker compose logs -f validator +docker compose logs -f orbinum-validator # View Watchtower logs (check when updates were applied) docker compose logs -f watchtower @@ -449,7 +476,7 @@ docker compose logs -f watchtower docker compose down # Restart validator only -docker compose restart validator +docker compose restart orbinum-validator # Force update now (without waiting for Watchtower) docker compose pull && docker compose up -d @@ -472,8 +499,8 @@ docker stats orbinum-validator **`author_insertKey` returns an error** - Make sure the node is running before calling the RPC - Port `9944` is not exposed to the host or network — it only listens on - `127.0.0.1` inside the container. Always call it via `docker compose exec - validator curl ... http://localhost:9944` (see Step 4), not from the host directly. + `127.0.0.1` inside the container. Always call it via `docker exec + orbinum-validator curl ... http://localhost:9944` (see Step 4), not from the host directly. **Node is not producing blocks after approval** - Verify all keys are inserted: restart and check logs for `Loaded session key` diff --git a/docker/testnet/docker-compose.rpc.yml b/docker/testnet/docker-compose.rpc.yml index a5878276..8c6ca206 100644 --- a/docker/testnet/docker-compose.rpc.yml +++ b/docker/testnet/docker-compose.rpc.yml @@ -17,6 +17,9 @@ services: --rpc-cors all --rpc-external --rpc-methods Safe + --rpc-max-connections 1000 + --state-pruning archive + --blocks-pruning archive --no-mdns --no-private-ipv4 --prometheus-external diff --git a/docker/testnet/rpc-node.md b/docker/testnet/rpc-node.md index 1657a3c4..28b7c130 100644 --- a/docker/testnet/rpc-node.md +++ b/docker/testnet/rpc-node.md @@ -50,7 +50,7 @@ a Let's Encrypt certificate for `RPC_DOMAIN` automatically — no Nginx or Certb ```bash docker compose -f docker-compose.rpc.yml up -d -docker compose -f docker-compose.rpc.yml logs -f rpc-node +docker compose -f docker-compose.rpc.yml logs -f orbinum-rpc-node ``` Wait until you see `Idle` or `Syncing` in the logs. On first start, check that Caddy @@ -62,16 +62,28 @@ docker compose -f docker-compose.rpc.yml logs caddy | grep -i "certificate obtai ## Verify -Use the domain you set in `RPC_DOMAIN` (replace below): +The public endpoint is locked down by Caddy: requests must send an `Origin` +header that matches `CADDY_ALLOWED_ORIGINS`, else 403. To test from anywhere, +pass an allowed origin: ```bash -curl -H "Content-Type: application/json" \ +curl -H "Origin: https://explorer.testnet.orbinum.network" \ + -H "Content-Type: application/json" \ -d '{"id":1,"jsonrpc":"2.0","method":"system_health","params":[]}' \ https://rpc-1.testnet.orbinum.io # Expected: {"jsonrpc":"2.0","result":{"isSyncing":false,"peers":3,...},"id":1} ``` +To bypass Caddy and hit the node's RPC directly (no Origin needed), run the +curl **inside the container** against `localhost:9944`: + +```bash +docker exec orbinum-rpc-node curl -s -H "Content-Type: application/json" \ + -d '{"id":1,"jsonrpc":"2.0","method":"system_health","params":[]}' \ + http://localhost:9944 +``` + The endpoint is ready to use as: - `https://rpc-1.testnet.orbinum.io` — HTTP RPC - `wss://rpc-1.testnet.orbinum.io` — WebSocket (Polkadot.js, Talisman) @@ -85,8 +97,8 @@ docker compose -f docker-compose.rpc.yml down # Full reset docker compose -f docker-compose.rpc.yml down -v -# Check sync status -curl -s -H "Content-Type: application/json" \ +# Check sync status (inside the container — no Origin needed) +docker exec orbinum-rpc-node curl -s -H "Content-Type: application/json" \ -d '{"id":1,"jsonrpc":"2.0","method":"system_syncState","params":[]}' \ - https://rpc-1.testnet.orbinum.io | jq + http://localhost:9944 ```