Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions docker/testnet/Makefile
Original file line number Diff line number Diff line change
@@ -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 <target> (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>_<volume>"; 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

Expand Down Expand Up @@ -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
Expand Down
51 changes: 39 additions & 12 deletions docker/testnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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...
```

Expand Down Expand Up @@ -264,35 +264,62 @@ 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.

```bash
# Replace <MNEMONIC>, <AURA_PUBKEY>, <GRANDPA_PUBKEY> 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","<MNEMONIC>","<AURA_PUBKEY>"]}' \
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","<MNEMONIC>","<GRANDPA_PUBKEY>"]}' \
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 <AURA_PUBKEY>
docker exec orbinum-validator curl -s -H "Content-Type: application/json" \
-d '{"id":1,"jsonrpc":"2.0","method":"author_hasKey","params":["<AURA_PUBKEY>","aura"]}' \
http://localhost:9944

# GRANDPA — replace <GRANDPA_PUBKEY>
docker exec orbinum-validator curl -s -H "Content-Type: application/json" \
-d '{"id":1,"jsonrpc":"2.0","method":"author_hasKey","params":["<GRANDPA_PUBKEY>","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

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:
Expand Down Expand Up @@ -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<combined_hex_session_key>"}
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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`
Expand Down
3 changes: 3 additions & 0 deletions docker/testnet/docker-compose.rpc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 18 additions & 6 deletions docker/testnet/rpc-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
```
Loading