Files
universe/Makefile
T

751 lines
30 KiB
Makefile

# Lux Universe Makefile
# 5-node networks (mainnet + testnet + devnet) + services
#
# Architecture:
# - Nodes: Run locally via `lux cli` (not Docker)
# - Services: Run via Docker (explorers, exchange, bridge, graph-node)
#
# Quick start:
# make start network=main # Start mainnet
# make start network=test # Start testnet
# make start network=dev # Start devnet
# make stop network=main # Stop mainnet
# make status # Show all status
SHELL := /bin/bash
.DEFAULT_GOAL := help
# Colors
RESET := \033[0m
BOLD := \033[1m
GREEN := \033[32m
YELLOW := \033[33m
BLUE := \033[34m
RED := \033[31m
# Paths
STATE_DIR := $(HOME)/work/lux/state
GENESIS_DIR := $(HOME)/work/lux/genesis
# Network parameter (REQUIRED for network operations)
# Usage: make start network=main
network ?=
# Normalize short forms
ifeq ($(network),main)
override network := mainnet
endif
ifeq ($(network),test)
override network := testnet
endif
ifeq ($(network),dev)
override network := devnet
endif
# Port configuration per network
# Mainnet: 9630-9638, Testnet: 9640-9648
ifeq ($(network),mainnet)
HTTP_PORT := 9630
STAKING_PORT := 9631
EXPLORER_PORT := 4000
CHAIN_ID := 96369
ZOO_CHAIN_ID := 200200
else ifeq ($(network),testnet)
HTTP_PORT := 9640
STAKING_PORT := 9641
EXPLORER_PORT := 4001
CHAIN_ID := 96368
ZOO_CHAIN_ID := 200201
else
HTTP_PORT := 9650
STAKING_PORT := 9651
EXPLORER_PORT := 4002
CHAIN_ID := 1337
ZOO_CHAIN_ID := 1338
endif
# Sibling-clone manifest (REPOS.yaml) replaces .gitmodules.
# Each Lux ecosystem repo lives at $(HOME)/work/lux/<name> as a free-standing
# sibling clone pinned to a semver tag — never a nested submodule.
# Helper: scripts/repos.sh {init,update,list} (reads REPOS.yaml via yq).
REPOS_HELPER := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))/scripts/repos.sh
##@ Quick Start
# Binaries
LUXD := $(HOME)/work/lux/node/build/luxd
ZOOD := $(HOME)/work/zoo/node/build/zood
HANZOD := $(HOME)/work/hanzo/node/target/release/hanzod
PLUGIN_DIR := $(HOME)/.lux/plugins/current
LOCAL_DIR := /tmp/lux-localnet
LIGHT_MNEMONIC := light light light light light light light light light light light energy
# VM IDs
ZOO_VMID := 2n2njofjYvece8gZWCNnc1mqkcqfW6kbrhPRZPVzwxrSQrQ4gE
HANZO_VMID := 2CwRhy6P6fQyn3JtZW68BFvn2nRwZ4Z8iar8EEeeLpGKmn5XRT
.PHONY: chain
chain: plugins ## Start just the blockchain (luxd + VM plugins)
.PHONY: up
up: chain ## Start full local universe (chain + services)
@echo "$(BOLD)Starting Lux localnet (luxd --dev)...$(RESET)"
@rm -rf $(LOCAL_DIR) && mkdir -p $(LOCAL_DIR)
@LIGHT_MNEMONIC="$(LIGHT_MNEMONIC)" $(LUXD) --dev \
--http-host=127.0.0.1 --http-port=9660 \
--data-dir=$(LOCAL_DIR) \
--plugin-dir=$(PLUGIN_DIR) \
--log-level=error 2>/dev/null &
@sleep 6
@echo "$(GREEN)luxd running on http://127.0.0.1:9660$(RESET)"
@curl -s http://127.0.0.1:9660/v1/info -X POST -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"info.getNodeID"}' 2>/dev/null | \
python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(' NodeID: ' + r['nodeID'])" 2>/dev/null || true
@echo "$(GREEN)VM plugins: zoo-evm, hanzo-evm, lux-evm$(RESET)"
@echo "$(GREEN)Chains: P, X, C, Z$(RESET)"
@echo ""
@echo "$(BOLD)Endpoints:$(RESET)"
@echo " P-Chain: http://127.0.0.1:9660/v1/chain/P"
@echo " X-Chain: http://127.0.0.1:9660/v1/chain/X"
@echo " C-Chain: http://127.0.0.1:9660/v1/chain/C/rpc"
@echo " Z-Chain: http://127.0.0.1:9660/v1/chain/z-chain/rpc"
@echo ""
@echo " Funded: 0x9011E888 (from LIGHT_MNEMONIC)"
@echo " Stop: make down"
.PHONY: down
down: ## Stop local dev network
@echo "$(BOLD)Stopping localnet...$(RESET)"
@pkill -f "luxd.*9660" 2>/dev/null || true
@echo "$(GREEN)Stopped$(RESET)"
.PHONY: plugins
plugins: ## Install VM plugins (zood, hanzod)
@mkdir -p $(PLUGIN_DIR)
@if [ ! -f $(ZOOD) ]; then echo "Building zood..." && cd $(HOME)/work/zoo/node && \
GONOSUMCHECK="*" GONOSUMDB="*" GOPRIVATE="*" go build -o build/zood . 2>/dev/null; fi
@if [ ! -f $(HANZOD) ]; then echo "Building hanzod..." && cd $(HOME)/work/hanzo/evm && \
cargo build --release -p hanzo-node 2>/dev/null; fi
@ln -sf $(ZOOD) $(PLUGIN_DIR)/$(ZOO_VMID) 2>/dev/null || true
@ln -sf $(HANZOD) $(PLUGIN_DIR)/$(HANZO_VMID) 2>/dev/null || true
.PHONY: all
all: ## Start all networks + services
@$(MAKE) -s start network=mainnet &
@$(MAKE) -s start network=testnet &
@$(MAKE) -s start network=devnet &
@wait
@$(MAKE) -s services
@echo "$(GREEN)$(BOLD)Lux Universe running (mainnet/testnet/devnet)!$(RESET)"
@$(MAKE) -s status-all
.PHONY: stop-all
stop-all: ## Stop everything
@$(MAKE) -s stop network=mainnet
@$(MAKE) -s stop network=testnet
@$(MAKE) -s stop network=devnet
@$(MAKE) -s stop-services
##@ Sibling Repos (REPOS.yaml — no submodules)
.PHONY: init
init: ## Clone every sibling repo at its pinned semver (~/work/lux/<name>)
@echo "$(BOLD)Cloning sibling repos at pinned semver tags...$(RESET)"
@$(REPOS_HELPER) init
.PHONY: update
update: ## Fetch + checkout pinned tag (all repos, or one via name=<repo>)
@echo "$(BOLD)Updating siblings to pinned tags...$(RESET)"
@$(REPOS_HELPER) update $(name)
.PHONY: sync
sync: init ## Alias for init — ensure all siblings cloned at pinned tag
.PHONY: list
list: ## List sibling repos + pinned tags from REPOS.yaml
@$(REPOS_HELPER) list
##@ Network Management
.PHONY: start
start: ## Start network (network=main|test|dev required)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ] && [ "$(network)" != "devnet" ]; then \
echo "$(RED)Error: network=main|test|dev required$(RESET)"; exit 1; fi
@echo "$(BOLD)Starting $(network) (port $(HTTP_PORT))...$(RESET)"
@RUN_E2E=true lux network start --$(network) 2>&1 | tail -5
@sleep 3
@$(MAKE) -s health network=$(network)
@$(MAKE) -s track network=$(network)
.PHONY: stop
stop: ## Stop network (network=main|test|dev required)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ] && [ "$(network)" != "devnet" ]; then \
echo "$(RED)Error: network=main|test|dev required$(RESET)"; exit 1; fi
@echo "$(BOLD)Stopping $(network)...$(RESET)"
@RUN_E2E=true lux network stop --$(network) --force 2>/dev/null || true
@echo "$(GREEN)$(network) stopped$(RESET)"
.PHONY: restart
restart: ## Restart network (network=main|test|dev required)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ] && [ "$(network)" != "devnet" ]; then \
echo "$(RED)Error: network=main|test|dev required$(RESET)"; exit 1; fi
@$(MAKE) -s stop network=$(network)
@$(MAKE) -s start network=$(network)
.PHONY: clean
clean: ## Clean network state (network=main|test|dev required)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ] && [ "$(network)" != "devnet" ]; then \
echo "$(RED)Error: network=main|test|dev required$(RESET)"; exit 1; fi
@echo "$(YELLOW)Cleaning $(network) state...$(RESET)"
@RUN_E2E=true lux network clean --$(network) 2>/dev/null || true
@echo "$(GREEN)$(network) state cleaned$(RESET)"
.PHONY: clean-all
clean-all: ## Clean ALL network state (all networks)
@echo "$(YELLOW)Cleaning all network state...$(RESET)"
@RUN_E2E=true lux network clean --mainnet 2>/dev/null || true
@RUN_E2E=true lux network clean --testnet 2>/dev/null || true
@RUN_E2E=true lux network clean --devnet 2>/dev/null || true
@rm -rf $(HOME)/.lux/runs/* 2>/dev/null || true
@echo "$(GREEN)All network state cleaned$(RESET)"
##@ Health Checks
.PHONY: health
health: ## Check network health (network=main|test|dev required)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ] && [ "$(network)" != "devnet" ]; then \
echo "$(RED)Error: network=main|test|dev required$(RESET)"; exit 1; fi
@echo "$(BOLD)$(network) Health ($(HTTP_PORT)-$$(($(HTTP_PORT)+8))):$(RESET)"
@for i in 0 2 4 6 8; do \
port=$$(($(HTTP_PORT)+$$i)); \
healthy=$$(curl -s -m 1 http://127.0.0.1:$$port/v1/health 2>/dev/null | jq -r '.healthy // false' 2>/dev/null || echo "false"); \
if [ "$$healthy" = "true" ]; then \
echo " Node $$port: $(GREEN)healthy$(RESET)"; \
else \
echo " Node $$port: $(YELLOW)offline$(RESET)"; \
fi; \
done
.PHONY: track
track: ## Ensure tracked chains are deployed (network=main|test|dev required)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ] && [ "$(network)" != "devnet" ]; then \
echo "$(RED)Error: network=main|test|dev required$(RESET)"; exit 1; fi
@bash scripts/ensure-tracked-chains.sh $(network) $(STATE_DIR)
##@ Chain Import
# RLP file paths per network
ifeq ($(network),mainnet)
RLP_DIR := lux-mainnet
RLP_CCHAIN := lux-mainnet-96369.rlp
RLP_ZOO := zoo-mainnet-200200.rlp
ZOO_BC := zoo
else ifeq ($(network),testnet)
RLP_DIR := lux-testnet
RLP_CCHAIN := lux-testnet-96368.rlp
RLP_ZOO := zoo-testnet-200201.rlp
ZOO_BC := zootest
endif
.PHONY: import
import: ## Import chains (network=main|test required)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ]; then \
echo "$(RED)Error: network=main|test required$(RESET)"; exit 1; fi
@echo "$(BOLD)Importing $(network) chains...$(RESET)"
@echo " C-Chain ($(CHAIN_ID))..."
@curl -s -X POST "http://127.0.0.1:$(HTTP_PORT)/v1/chain/C/rpc" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"admin_importChain","params":["$(STATE_DIR)/rlp/$(RLP_DIR)/$(RLP_CCHAIN)"],"id":1}' 2>/dev/null | jq -r '.result // .error // "sent"' &
@echo " Zoo ($(ZOO_CHAIN_ID))..."
@if [ -f "$(STATE_DIR)/rlp/zoo-$(network)/$(RLP_ZOO)" ]; then \
curl -s -X POST "http://127.0.0.1:$(HTTP_PORT)/v1/chain/$(ZOO_BC)/rpc" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"admin_importChain","params":["$(STATE_DIR)/rlp/zoo-$(network)/$(RLP_ZOO)"],"id":1}' 2>/dev/null | jq -r '.result // .error // "sent"' & \
fi
@wait
@echo "$(GREEN)$(network) import started$(RESET)"
##@ Snapshot & Resume
.PHONY: snapshot
snapshot: ## Snapshot network (network=main|test required, name=<name>)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ]; then \
echo "$(RED)Error: network=main|test required$(RESET)"; exit 1; fi
@snap_name="$(name)"; \
if [ -z "$$snap_name" ]; then snap_name="$(network)-$$(date +%Y%m%d-%H%M%S)"; fi; \
echo "$(BOLD)Snapshotting $(network) as '$$snap_name'...$(RESET)"; \
RUN_E2E=true lux network stop --$(network) --force --snapshot-name "$$snap_name" 2>/dev/null || true; \
echo "$(GREEN)$(network) snapshot '$$snap_name' saved$(RESET)"
.PHONY: resume
resume: ## Resume network from snapshot (network=main|test required, name=<name>)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ]; then \
echo "$(RED)Error: network=main|test required$(RESET)"; exit 1; fi
@if [ -z "$(name)" ]; then \
echo "$(BOLD)Available $(network) snapshots:$(RESET)"; \
ls -1 $(HOME)/.lux/snapshots 2>/dev/null | grep $(network) || echo " None"; \
exit 1; \
fi
@echo "$(BOLD)Resuming $(network) from '$(name)'...$(RESET)"
@RUN_E2E=true lux network start --$(network) --snapshot-name "$(name)"
@$(MAKE) -s health network=$(network)
.PHONY: snapshots
snapshots: ## List all snapshots
@echo "$(BOLD)Available snapshots:$(RESET)"
@ls -1 $(HOME)/.lux/snapshots 2>/dev/null || echo " None"
##@ Bootstrap & Snapshot Management
# Bootstrap source directory
BOOTSTRAP_DIR := $(STATE_DIR)/snapshots
.PHONY: bootstrap
bootstrap: ## Bootstrap network from snapshot (network=main|test|dev required)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ] && [ "$(network)" != "devnet" ]; then \
echo "$(RED)Error: network=main|test|dev required$(RESET)"; exit 1; fi
@echo "$(BOLD)Bootstrapping $(network)...$(RESET)"
@mkdir -p $(HOME)/.lux/snapshots
@if [ "$(network)" = "mainnet" ]; then \
BOOTSTRAP_NAME="mainnet-bootstrap"; \
elif [ "$(network)" = "testnet" ]; then \
BOOTSTRAP_NAME="testnet-bootstrap"; \
else \
BOOTSTRAP_NAME="devnet-bootstrap"; \
fi;
@if [ -d "$(BOOTSTRAP_DIR)/$$BOOTSTRAP_NAME" ]; then \
echo "$(BLUE)Extracting bootstrap snapshot...$(RESET)"; \
cd $(HOME)/.lux/snapshots && \
cat "$(BOOTSTRAP_DIR)/$$BOOTSTRAP_NAME/$(network).tar.gz.*" > $$BOOTSTRAP_NAME.tar.gz 2>/dev/null || true; \
if [ -f "$$BOOTSTRAP_NAME.tar.gz" ]; then \
tar -xzf $$BOOTSTRAP_NAME.tar.gz && \
rm -f $$BOOTSTRAP_NAME.tar.gz; \
elif [ -d "$$BOOTSTRAP_NAME" ]; then \
echo "$(YELLOW)Already extracted$(RESET)"; \
else \
echo "$(RED)Error: Bootstrap extraction failed$(RESET)"; exit 1; \
fi;
# Move extracted data to network directory
if [ -d "$$BOOTSTRAP_NAME/run_*" ]; then \
LATEST_RUN=$(ls -td $$BOOTSTRAP_NAME/run_* | head -1); \
echo "$(BLUE)Setting up network from $$LATEST_RUN...$(RESET)"; \
mkdir -p $(HOME)/.lux/$(network); \
for node_num in 1 2 3 4 5; do \
if [ -d "$$LATEST_RUN/node$$node_num" ]; then \
cp -r "$$LATEST_RUN/node$$node_num" "$(HOME)/.lux/$(network)/"; \
fi; \
done; \
cp -f "$$LATEST_RUN/prometheus.yaml" "$(HOME)/.lux/$(network)/" 2>/dev/null || true; \
ln -sf "$(basename $$LATEST_RUN)" "$$BOOTSTRAP_NAME/current"; \
echo "$(GREEN)$(network) bootstrap setup complete$(RESET)"; \
else \
echo "$(RED)Error: No run data found in bootstrap$(RESET)"; exit 1; \
fi; \
else \
echo "$(RED)Error: Bootstrap not found at $(BOOTSTRAP_DIR)/$$BOOTSTRAP_NAME$(RESET)"; \
echo "$(YELLOW)Try: git lfs pull --include='state/snapshots/$(network)-bootstrap/*'$(RESET)"; \
exit 1; \
fi
.PHONY: bootstrap-start
bootstrap-start: ## Bootstrap and start network (network=main|test|dev required)
@$(MAKE) -s bootstrap network=$(network)
@echo "$(BOLD)Starting $(network)...$(RESET)"
@RUN_E2E=true lux network start --$(network)
@$(MAKE) -s health network=$(network)
@$(MAKE) -s track network=$(network)
.PHONY: bootstrap-clean
bootstrap-clean: ## Clean bootstrap data
@echo "$(BOLD)Cleaning bootstrap data...$(RESET)"
@rm -rf $(HOME)/.lux/snapshots/*-bootstrap*
@echo "$(GREEN)Bootstrap data cleaned$(RESET)"
.PHONY: snapshot-save
snapshot-save: ## Save network snapshot (network=main|test|dev required, name=<name>)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ] && [ "$(network)" != "devnet" ]; then \
echo "$(RED)Error: network=main|test|dev required$(RESET)"; exit 1; fi
@snap_name="$(name)"; \
if [ -z "$$snap_name" ]; then snap_name="$(network)-$$(date +%Y%m%d-%H%M%S)"; fi; \
mkdir -p $(HOME)/.lux/snapshots; \
echo "$(BOLD)Saving $(network) snapshot '$$snap_name'...$(RESET)"; \
RUN_E2E=true lux network stop --$(network) --force 2>/dev/null || true; \
cp -r $(HOME)/.lux/$(network) $(HOME)/.lux/snapshots/$$snap_name && \
tar -czf $(HOME)/.lux/snapshots/$$snap_name.tar.gz -C $(HOME)/.lux/snapshots $$snap_name && \
rm -rf $(HOME)/.lux/snapshots/$$snap_name && \
echo "$(GREEN)$(network) snapshot '$$snap_name' saved$(RESET)"
.PHONY: snapshot-load
snapshot-load: ## Load network snapshot (network=main|test|dev required, name=<name>)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ] && [ "$(network)" != "devnet" ]; then \
echo "$(RED)Error: network=main|test|dev required$(RESET)"; exit 1; fi
@if [ -z "$(name)" ]; then \
echo "$(BOLD)Available $(network) snapshots:$(RESET)"; \
ls -1 $(HOME)/.lux/snapshots 2>/dev/null | grep $(network) || echo " None"; \
exit 1; \
fi
@echo "$(BOLD)Loading $(network) from '$(name)'...$(RESET)"
@RUN_E2E=true lux network stop --$(network) --force 2>/dev/null || true; \
mkdir -p $(HOME)/.lux/$(network).backup.$$(date +%Y%m%d-%H%M%S) && \
mv $(HOME)/.lux/$(network) $(HOME)/.lux/$(network).backup.$$(date +%Y%m%d-%H%M%S) 2>/dev/null || true; \
tar -xzf $(HOME)/.lux/snapshots/$(name).tar.gz -C $(HOME)/.lux && \
mv $(HOME)/.lux/snapshots/$(name) $(HOME)/.lux/$(network) && \
echo "$(GREEN)$(network) snapshot '$(name)' loaded$(RESET)"
.PHONY: snapshot-list
snapshot-list: ## List all snapshots
@echo "$(BOLD)Available snapshots:$(RESET)"
@ls -1 $(HOME)/.lux/snapshots 2>/dev/null | sed 's/\.tar\.gz$$//' || echo " None"
.PHONY: snapshot-clean
snapshot-clean: ## Clean all snapshots
@echo "$(BOLD)Cleaning all snapshots...$(RESET)"
@rm -rf $(HOME)/.lux/snapshots/*
@echo "$(GREEN)All snapshots cleaned$(RESET)"
##@ AMM Testing
# AMM network name mapping
ifeq ($(network),mainnet)
AMM_NETWORK := lux
AMM_ZOO := zoo
else ifeq ($(network),testnet)
AMM_NETWORK := lux-testnet
AMM_ZOO := zoo
endif
.PHONY: amm
amm: ## Test AMM on C-chain (network=main|test required)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ]; then \
echo "$(RED)Error: network=main|test required$(RESET)"; exit 1; fi
@echo "$(BOLD)Testing AMM on $(network) C-chain ($(CHAIN_ID))...$(RESET)"
@lux amm status --network $(AMM_NETWORK) --rpc http://127.0.0.1:$(HTTP_PORT)/v1/chain/C/rpc 2>&1 | head -20
.PHONY: amm-zoo
amm-zoo: ## Test AMM on Zoo (network=main|test required)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ]; then \
echo "$(RED)Error: network=main|test required$(RESET)"; exit 1; fi
@echo "$(BOLD)Testing AMM on $(network) Zoo ($(ZOO_CHAIN_ID))...$(RESET)"
@lux amm status --network $(AMM_ZOO) --rpc http://127.0.0.1:$(HTTP_PORT)/v1/chain/$(ZOO_BC)/rpc 2>&1 | head -20
##@ Docker Services
.PHONY: services
services: ## Start all Docker services
@echo "$(BOLD)Starting Docker services...$(RESET)"
docker compose up -d
@echo "$(GREEN)Services started!$(RESET)"
@$(MAKE) -s service-status
.PHONY: stop-services
stop-services: ## Stop all Docker services
@echo "$(BOLD)Stopping Docker services...$(RESET)"
docker compose down
@echo "$(GREEN)Services stopped$(RESET)"
.PHONY: service-status
service-status: ## Show Docker service status
@echo "$(BOLD)Docker Services:$(RESET)"
@docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || echo " No services running"
.PHONY: service-logs
service-logs: ## Tail Docker service logs
docker compose logs -f --tail=100
.PHONY: explorers
explorers: ## Start block explorers
docker compose up -d explorer-lux explorer-zoo
.PHONY: exchange
exchange: ## Start exchange (DEX)
docker compose up -d exchange
.PHONY: bridge
bridge: ## Start bridge
docker compose up -d bridge
.PHONY: graph
graph: ## Start Graph Protocol
docker compose up -d graph-node-lux
##@ Status
.PHONY: status
status: ## Show network status (network=main|test|dev required, or status-all for all)
@if [ "$(network)" != "mainnet" ] && [ "$(network)" != "testnet" ] && [ "$(network)" != "devnet" ]; then \
echo "$(RED)Error: network=main|test|dev required (or use status-all)$(RESET)"; exit 1; fi
@echo "$(BOLD)═══ $(network) ($(HTTP_PORT)) ═══$(RESET)"
@$(MAKE) -s health network=$(network)
@echo ""
@cblock=$$(curl -s -m 1 -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
http://127.0.0.1:$(HTTP_PORT)/v1/chain/C/rpc 2>/dev/null | jq -r '.result // "N/A"' 2>/dev/null); \
if [ -z "$$cblock" ]; then cblock="N/A"; fi; \
echo " C-Chain Block: $$cblock"
@bash scripts/check-tracked-chains.sh $(network) $(HTTP_PORT)
@echo ""
@echo "$(BOLD)Endpoints:$(RESET)"
@echo " C-Chain RPC: http://127.0.0.1:$(HTTP_PORT)/v1/chain/C/rpc"
@echo " Explorer: http://127.0.0.1:$(EXPLORER_PORT)"
.PHONY: status-all
status-all: ## Show all networks + services status
@echo "$(BOLD)═══════════════════════════════════════$(RESET)"
@echo "$(BOLD) LUX UNIVERSE STATUS $(RESET)"
@echo "$(BOLD)═══════════════════════════════════════$(RESET)"
@echo ""
@echo "$(BOLD)═══ MAINNET (9630) ═══$(RESET)"
@$(MAKE) -s health network=mainnet 2>/dev/null || echo " Mainnet: offline"
@cblock=$$(curl -s -m 1 -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
http://127.0.0.1:9630/v1/chain/C/rpc 2>/dev/null | jq -r '.result // "N/A"' 2>/dev/null); \
if [ -z "$$cblock" ]; then cblock="N/A"; fi; \
echo " C-Chain Block: $$cblock"
@bash scripts/check-tracked-chains.sh mainnet 9630
@echo ""
@echo "$(BOLD)═══ TESTNET (9640) ═══$(RESET)"
@$(MAKE) -s health network=testnet 2>/dev/null || echo " Testnet: offline"
@cblock=$$(curl -s -m 1 -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
http://127.0.0.1:9640/v1/chain/C/rpc 2>/dev/null | jq -r '.result // "N/A"' 2>/dev/null); \
if [ -z "$$cblock" ]; then cblock="N/A"; fi; \
echo " C-Chain Block: $$cblock"
@bash scripts/check-tracked-chains.sh testnet 9640
@echo ""
@echo "$(BOLD)═══ DEVNET (9650) ═══$(RESET)"
@$(MAKE) -s health network=devnet 2>/dev/null || echo " Devnet: offline"
@cblock=$$(curl -s -m 1 -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
http://127.0.0.1:9650/v1/chain/C/rpc 2>/dev/null | jq -r '.result // "N/A"' 2>/dev/null); \
if [ -z "$$cblock" ]; then cblock="N/A"; fi; \
echo " C-Chain Block: $$cblock"
@bash scripts/check-tracked-chains.sh devnet 9650
@echo ""
@$(MAKE) -s service-status
@echo ""
@echo "$(BOLD)Endpoints:$(RESET)"
@echo " Mainnet C-Chain: http://127.0.0.1:9630/v1/chain/C/rpc"
@echo " Testnet C-Chain: http://127.0.0.1:9640/v1/chain/C/rpc"
@echo " Devnet C-Chain: http://127.0.0.1:9650/v1/chain/C/rpc"
@echo " Explorer: http://127.0.0.1:4000"
##@ Build
.PHONY: build
build: build-node build-cli ## Build core components
.PHONY: build-node
build-node: ## Build lux node
@echo "$(BOLD)Building node...$(RESET)"
@if [ -f node/Makefile ]; then $(MAKE) -C node build; \
else (cd node && go build -o build/luxd ./main); fi
.PHONY: build-cli
build-cli: ## Build lux CLI
@echo "$(BOLD)Building CLI...$(RESET)"
@(cd cli && go build -o build/lux ./main)
.PHONY: build-evm
build-evm: ## Build EVM plugin
@(cd evm && go build -o build/evm ./plugin)
.PHONY: build-docker
build-docker: ## Build Docker images
docker compose build
##@ Testing
.PHONY: test
test: ## Run tests for core components
@for mod in node geth evm cli; do \
if [ -f "$$mod/go.mod" ]; then \
echo "$(BLUE)Testing $$mod...$(RESET)"; \
(cd $$mod && go test -race -timeout 5m ./...) || exit 1; \
fi; \
done
##@ E2E Testing (Playwright)
# ONE axis: LUX_ENV=local|devnet|testnet|mainnet (default mainnet). Every url,
# port and chainId is resolved by e2e/env.ts from NETWORKS.yaml — never here.
# ONE viewport set: projects mobile(390x844) tablet(834x1194) laptop(1440x900),
# so "mobile and desktop" is the same three frames every run. Narrow a run with
# `vp=<name>` and a spec with `spec=<path>`.
# Screenshots (pass AND fail) land in ~/Desktop/lux-e2e/$(LUX_ENV)/.
LUX_ENV ?= mainnet
PW := LUX_ENV=$(LUX_ENV) npx playwright test $(if $(vp),--project=$(vp),)
.PHONY: e2e
e2e: ## Run all E2E tests (LUX_ENV=local|devnet|testnet|mainnet, vp=mobile|tablet|laptop)
@echo "$(BOLD)E2E — LUX_ENV=$(LUX_ENV)$(if $(vp), project=$(vp),) → ~/Desktop/lux-e2e/$(LUX_ENV)$(RESET)"
@$(PW) $(spec)
.PHONY: e2e-mainnet e2e-testnet e2e-devnet e2e-local
e2e-mainnet: ## Run all E2E against mainnet
@$(MAKE) -s e2e LUX_ENV=mainnet
e2e-testnet: ## Run all E2E against testnet
@$(MAKE) -s e2e LUX_ENV=testnet
e2e-devnet: ## Run all E2E against devnet
@$(MAKE) -s e2e LUX_ENV=devnet
e2e-local: ## Run all E2E against the local stack (make start network=main + ./local/up.sh)
@$(MAKE) -s e2e LUX_ENV=local
.PHONY: e2e-login
e2e-login: ## Prove sign-in for real (needs LUX_E2E_PASSWORD)
@if [ -z "$$LUX_E2E_PASSWORD" ]; then \
echo "$(RED)LUX_E2E_PASSWORD is not set — export it first (never commit it)$(RESET)"; exit 1; fi
@$(PW) e2e/50-login.spec.ts
# ── Is the chain alive ──────────────────────────────────────────────────────
# The one question nothing in the suite asked until 00-liveness. Gates on the
# TIP, calibrated against the chain's own recent cadence — never on readiness,
# which read 5/5 Ready on all fifteen pods while mainnet had been frozen for 59
# hours. Runs in ~25s; run it first, and believe it over any dashboard.
.PHONY: e2e-live
e2e-live: ## Is the chain actually producing blocks? (tip freshness, all 3 networks)
@$(PW) e2e/00-liveness.spec.ts
# ── Every page of every product ─────────────────────────────────────────────
# Routes come from e2e/routes.ts, so these two targets grow with the products
# and never with the Makefile.
.PHONY: e2e-surfaces
e2e-surfaces: ## Sweep every declared route of every product, anonymously
@$(PW) e2e/60-surfaces.spec.ts
.PHONY: e2e-console
e2e-console: ## Sweep all 43 lux.cloud dashboard routes SIGNED IN (needs LUX_E2E_PASSWORD)
@if [ -z "$$LUX_E2E_PASSWORD" ]; then \
echo "$(RED)LUX_E2E_PASSWORD is not set — the authenticated sweep FAILS rather than"; \
echo "skipping, because a skipped sweep is the same colour as a passing one$(RESET)"; exit 1; fi
@$(PW) e2e/61-console.spec.ts
.PHONY: e2e-institutional
e2e-institutional: ## Score the institutional baseline, 27 screens (needs LUX_E2E_PASSWORD)
@LUX_SHOT_SET=institutional-baseline $(PW) e2e/51-institutional-baseline.spec.ts
# The pre-go-live gate: the load-bearing surfaces at all three frames, in
# minutes rather than hours. `make e2e` runs everything including the very large
# legacy broker/exchange specs, which are serial within their own file and so
# dominate a full run.
.PHONY: e2e-gate
e2e-gate: ## Run the go-live gate (liveness + surfaces + validators + defi + bridge + dex + login)
@$(PW) e2e/00-liveness.spec.ts e2e/00-health.spec.ts e2e/01-explorer.spec.ts \
e2e/08-validator-lifecycle.spec.ts \
e2e/09-defi-flow.spec.ts e2e/13-bridge-mpc.spec.ts e2e/14-dex-contracts.spec.ts \
e2e/14-network-independence.spec.ts e2e/16-app-chains.spec.ts \
e2e/60-surfaces.spec.ts \
$(if $(LUX_E2E_PASSWORD),e2e/50-login.spec.ts e2e/61-console.spec.ts,)
.PHONY: e2e-health
e2e-health: ## Run health/RPC E2E tests only
@$(PW) e2e/00-health.spec.ts
.PHONY: e2e-explorer
e2e-explorer: ## Run explorer E2E tests
@$(PW) e2e/01-explorer.spec.ts
.PHONY: e2e-exchange
e2e-exchange: ## Run exchange E2E tests
@$(PW) e2e/02-exchange.spec.ts
.PHONY: e2e-bridge
e2e-bridge: ## Run bridge E2E tests
@$(PW) e2e/03-bridge.spec.ts
.PHONY: e2e-staking
e2e-staking: ## Run staking/delegation E2E tests
@$(PW) e2e/04-staking.spec.ts
.PHONY: e2e-cloud
e2e-cloud: ## Run cloud bootnode E2E tests
@$(PW) e2e/05-cloud.spec.ts
.PHONY: e2e-wallet
e2e-wallet: ## Run wallet E2E tests
@$(PW) e2e/06-wallet.spec.ts
.PHONY: e2e-validator
e2e-validator: ## Run local validator E2E tests (local stack only)
@$(MAKE) -s e2e LUX_ENV=local spec=e2e/07-validator.spec.ts
.PHONY: e2e-install
e2e-install: ## Install Playwright + browsers
@npm ci
@npx playwright install --with-deps chromium
.PHONY: e2e-report
e2e-report: ## Open Playwright HTML report
@npx playwright show-report
.PHONY: e2e-shots
e2e-shots: ## Open the screenshot directory for this env
@open ~/Desktop/lux-e2e/$(LUX_ENV) 2>/dev/null || echo "no shots yet for LUX_ENV=$(LUX_ENV)"
##@ Development
.PHONY: dev
dev: init all ## Quick dev setup (starts both networks)
.PHONY: reset
reset: ## Reset submodules
git submodule foreach --recursive git reset --hard
git submodule update --init --recursive
.PHONY: clean-build
clean-build: ## Clean build artifacts
@for mod in node geth evm cli; do \
[ -d "$$mod" ] && (cd $$mod && go clean -cache 2>/dev/null) || true; \
done
.PHONY: fmt
fmt: ## Format Go code
@for mod in node geth evm cli consensus; do \
[ -f "$$mod/go.mod" ] && (cd $$mod && go fmt ./...) || true; \
done
.PHONY: lint
lint: ## Lint Go code
@for mod in node geth evm cli; do \
[ -f "$$mod/go.mod" ] && (cd $$mod && go vet ./...) || true; \
done
##@ Help
.PHONY: help
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "$(BOLD)Lux Universe$(RESET) - Triple 5-node networks + services\n\nUsage:\n make $(BLUE)<target>$(RESET) [network=main|test|dev]\n"} \
/^[a-zA-Z_-]+:.*?##/ { printf " $(BLUE)%-18s$(RESET) %s\n", $$1, $$2 } \
/^##@/ { printf "\n$(BOLD)%s$(RESET)\n", substr($$0, 5) }' $(MAKEFILE_LIST)
local: ## Boot the full stack locally on the hanzo cloud binary (seedable, no external IAM)
@./local/up.sh
##@ IAM — Lux's identity app graph (deterministic, from this repo only)
# infra/k8s/iam/provision.yaml is the ONE source of truth for Lux's OAuth
# clients. These targets are the ONLY supported way to change them — never
# hand-edit an application in a console, or the next converge silently reverts
# it and the drift is invisible until a login breaks.
IAM_DOC := infra/k8s/iam/provision.yaml
IAM_CTX := do-sfo3-hanzo-k8s
IAM_NS := hanzo
.PHONY: iam-provision iam-provision-plan iam-provision-logs
iam-provision-plan: ## Derive Lux's OAuth clients and print them; writes nothing
@kubectl --context $(IAM_CTX) -n $(IAM_NS) run lux-iam-plan-$$$$ --rm -i --restart=Never \
--image=$$(awk -F'image: ' '/image: ghcr.io/{print $$2; exit}' infra/k8s/iam/provision-job.yaml) \
--overrides='{"spec":{"imagePullSecrets":[{"name":"ghcr-secret"}]}}' \
--command -- /iam2 provision --config=/dev/stdin --url=http://iam.hanzo.svc --dry-run < $(IAM_DOC)
iam-provision: ## Converge lux.id onto provision.yaml (idempotent; never rotates a secret)
@kubectl --context $(IAM_CTX) -n $(IAM_NS) create configmap lux-iam-provision \
--from-file=provision.yaml=$(IAM_DOC) --dry-run=client -o yaml | \
kubectl --context $(IAM_CTX) apply -f -
@kubectl --context $(IAM_CTX) -n $(IAM_NS) delete job lux-iam-provision --ignore-not-found
@kubectl --context $(IAM_CTX) apply -f infra/k8s/iam/provision-job.yaml
@kubectl --context $(IAM_CTX) -n $(IAM_NS) wait --for=condition=complete job/lux-iam-provision --timeout=180s \
|| { echo "provision did not complete — logs:"; $(MAKE) iam-provision-logs; exit 1; }
@$(MAKE) iam-provision-logs
iam-provision-logs: ## Show what the last converge actually did
@kubectl --context $(IAM_CTX) -n $(IAM_NS) logs job/lux-iam-provision --tail=100 2>/dev/null || true