Files
zeekayandHanzo Dev 62d5f28c22 A chain address is built in one place
The node moved every chain route from /v1/bc/<alias> to /v1/chain/<alias>
and deleted the old prefix, so every chain call answered 404. The address
was spelled out at 112 sites across 34 files and constants.ChainAliasPrefix
was used at none of them, which is why nothing failed to compile when the
segment moved.

route.Chain(uri, alias) composes it now, and route.Alias reads one back,
replacing three hand-rolled parsers — one of which took the alias by
position in the split path and only worked because the new segment happens
to leave the component count unchanged.

TestChainAddressBuiltOnlyHere walks every .go file in the module and fails
on a chain address in a string literal, so the segment cannot drift back;
prose is free to name a route. TestGuardCatchesEachShape plants each
forbidden shape and requires the guard to find it.

The endpoint box and the eleven-chain table were the same list written
twice; there is one list, rendered once, measured rather than hand-padded.
The generated CLI reference and the faucet manifest compose through the
same function.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-30 15:05:42 -07:00

288 lines
9.7 KiB
Go

// Copyright (C) 2022-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package cmd
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/luxfi/cli/pkg/route"
"github.com/luxfi/constants"
"github.com/spf13/cobra"
)
// evmBlockchainID is the chain this command deploys.
const evmBlockchainID = "2G8mK7VCZX1dV8iPjkkTDMpYGZDCNLLVdTJVLmMsG5ZV7zKVmB"
var (
evmNetworkID int
evmDataDir string
evmPort int
evmChainConfig string
evmSkipBuild bool
evmBackend string // gevm, revm, cevm, auto
evmGPU bool // enable GPU acceleration
)
var evmCmd = &cobra.Command{
Use: "evm",
Short: "Manage EVM chain deployments",
Long: `Deploy and manage EVM chains with existing state.
This command allows you to deploy a new EVM chain using an existing PebbleDB database,
enabling easy migration and state preservation across network deployments.`,
}
var evmDeployCmd = &cobra.Command{
Use: "deploy",
Short: "Deploy EVM chain",
Long: `Deploy a new EVM chain, optionally reusing an existing data directory.
Example:
lux evm deploy # Uses default ~/.lux/evm/
lux evm deploy --network-id 2 # Deploy on testnet
lux evm deploy --data-dir ~/.lux/evm # Specify data directory`,
RunE: deployEVM,
}
func NewEVMCmd() *cobra.Command {
evmCmd.AddCommand(evmDeployCmd)
evmDeployCmd.Flags().IntVar(&evmNetworkID, "network-id", int(constants.MainnetID), "Network ID for the deployment (1=mainnet, 2=testnet, 3=devnet)")
evmDeployCmd.Flags().StringVar(&evmDataDir, "data-dir", "", "Data directory for the node (default: ~/.lux/evm)")
evmDeployCmd.Flags().IntVar(&evmPort, "port", 9630, "Port for the node RPC")
evmDeployCmd.Flags().StringVar(&evmChainConfig, "chain-config", "", "Path to chain configuration JSON")
evmDeployCmd.Flags().BoolVar(&evmSkipBuild, "skip-build", false, "Skip building the EVM plugin")
evmDeployCmd.Flags().StringVar(&evmBackend, "backend", "gevm", "EVM backend: gevm (Go), revm (Rust), cevm (C++), auto")
evmDeployCmd.Flags().BoolVar(&evmGPU, "gpu", false, "Enable GPU acceleration (Metal/CUDA)")
return evmCmd
}
func deployEVM(cmd *cobra.Command, args []string) error {
// Set default data directory if not provided - use ~/.lux/evm
if evmDataDir == "" {
evmDataDir = filepath.Join(os.Getenv("HOME"), ".lux", "evm")
}
// Check if data directory exists and has an existing database
var existingDB string
blockchainID := evmBlockchainID
// Check for existing database in standard locations
possibleDBPaths := []string{
filepath.Join(evmDataDir, "chains", blockchainID, "db"),
filepath.Join(evmDataDir, "db", blockchainID, "db"),
}
for _, dbPath := range possibleDBPaths {
if _, err := os.Stat(dbPath); err == nil {
existingDB = dbPath
fmt.Printf("āœ… Found existing PebbleDB at %s\n", existingDB)
// Check database size
cmd := exec.Command("du", "-sh", existingDB) //nolint:gosec // G204: Known command
output, err := cmd.Output()
if err == nil {
fmt.Printf(" Database size: %s", output)
}
break
}
}
fmt.Printf("\nDeploying EVM chain\n")
fmt.Printf(" Network ID: %d\n", evmNetworkID)
fmt.Printf(" Data Directory: %s\n", evmDataDir)
fmt.Printf(" RPC Port: %d\n", evmPort)
// Create directory structure
dirs := []string{
evmDataDir,
filepath.Join(evmDataDir, "staking"),
filepath.Join(evmDataDir, "plugins"),
filepath.Join(evmDataDir, "logs"),
filepath.Join(evmDataDir, "db"),
filepath.Join(evmDataDir, "configs", "chains"),
}
for _, dir := range dirs {
if err := os.MkdirAll(dir, 0o750); err != nil {
return fmt.Errorf("failed to create directory %s: %w", dir, err)
}
}
// Build EVM plugin if needed
if !evmSkipBuild {
// Compute build tags from --backend and --gpu flags
var tags []string
switch evmBackend {
case "revm":
tags = append(tags, "revm")
case "cevm":
tags = append(tags, "cevm")
case "auto":
tags = append(tags, "revm", "cevm") // link all, select at runtime
}
if evmGPU {
tags = append(tags, "gpu")
}
tagStr := ""
if len(tags) > 0 {
tagStr = "-tags " + strings.Join(tags, ",")
}
fmt.Printf("\n Building EVM plugin (backend=%s, gpu=%v)...\n", evmBackend, evmGPU)
buildScript := fmt.Sprintf("cd %s && CGO_ENABLED=1 go build %s -o %s/plugins/evm ./plugin",
filepath.Join(os.Getenv("HOME"), "work", "lux", "evm"),
tagStr,
evmDataDir)
buildCmd := exec.Command("bash", "-c", buildScript)
buildCmd.Stdout = os.Stdout
buildCmd.Stderr = os.Stderr
if err := buildCmd.Run(); err != nil {
return fmt.Errorf("failed to build EVM: %w", err)
}
}
// Copy EVM plugin from build location
pluginSrc := "/home/z/work/lux/evm/build/mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6"
// If build location doesn't exist, try lux-cli plugins directory
if _, err := os.Stat(pluginSrc); os.IsNotExist(err) {
pluginSrc = filepath.Join(os.Getenv("HOME"), ".lux-cli", "plugins", "mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6")
}
pluginDst := filepath.Join(evmDataDir, "plugins", "mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6")
if _, err := os.Stat(pluginSrc); err == nil {
copyCmd := exec.Command("cp", pluginSrc, pluginDst) //nolint:gosec // G204: Known command
if err := copyCmd.Run(); err != nil {
return fmt.Errorf("failed to copy EVM plugin: %w", err)
}
_ = os.Chmod(pluginDst, 0o755) //nolint:gosec // G302: Executable needs 0755
fmt.Println("āœ… EVM plugin installed")
}
// If existing database wasn't found in the data directory, we may still be able to use existing data
// The database would already be in place if we're reusing an existing data directory
if existingDB != "" {
fmt.Printf("āœ… Will reuse existing PebbleDB database\n")
fmt.Printf(" Database path: %s\n", existingDB)
}
// Generate staking certificates
fmt.Println("\nšŸ” Generating staking certificates...")
stakingDir := filepath.Join(evmDataDir, "staking")
genCertCmd := exec.Command("openssl", "req", "-x509", "-newkey", "rsa:4096", //nolint:gosec // G204: Known openssl command
"-keyout", filepath.Join(stakingDir, "staker.key"),
"-out", filepath.Join(stakingDir, "staker.crt"),
"-sha256", "-days", "365", "-nodes",
"-subj", "/CN=LuxEVM")
if err := genCertCmd.Run(); err != nil {
return fmt.Errorf("failed to generate certificates: %w", err)
}
copyCmd := exec.Command("cp", //nolint:gosec // G204: Known command
filepath.Join(stakingDir, "staker.key"),
filepath.Join(stakingDir, "signer.key"))
_ = copyCmd.Run()
// Create chain configuration if provided
if evmChainConfig != "" {
chainConfigDst := filepath.Join(evmDataDir, "configs", "chains",
evmBlockchainID, "config.json")
_ = os.MkdirAll(filepath.Dir(chainConfigDst), 0o750)
copyCmd := exec.Command("cp", evmChainConfig, chainConfigDst) //nolint:gosec // G204: Known command
if err := copyCmd.Run(); err != nil {
fmt.Printf("Warning: Could not copy chain config: %v\n", err)
}
}
// Create node configuration
nodeConfig := map[string]interface{}{
"network-id": evmNetworkID,
"data-dir": evmDataDir,
"db-dir": filepath.Join(evmDataDir, "db"),
"log-dir": filepath.Join(evmDataDir, "logs"),
"plugin-dir": filepath.Join(evmDataDir, "plugins"),
"chain-config-dir": filepath.Join(evmDataDir, "configs", "chains"),
"log-level": "info",
"http-host": "0.0.0.0",
"http-port": evmPort,
"staking-enabled": false,
"sybil-protection-enabled": false,
"consensus-sample-size": 1,
"consensus-quorum-size": 1,
"api-admin-enabled": true,
"api-metrics-enabled": true,
"api-health-enabled": true,
"api-info-enabled": true,
"index-enabled": true,
"db-type": "badgerdb",
"http-allowed-origins": "*",
"http-allowed-hosts": "*",
"chain-data-dir": filepath.Join(evmDataDir, "chaindata"),
}
configPath := filepath.Join(evmDataDir, "config.json")
configFile, err := os.Create(configPath) //nolint:gosec // G304: Creating config in app's data directory
if err != nil {
return fmt.Errorf("failed to create config file: %w", err)
}
defer func() { _ = configFile.Close() }()
encoder := json.NewEncoder(configFile)
encoder.SetIndent("", " ")
if err := encoder.Encode(nodeConfig); err != nil {
return fmt.Errorf("failed to write config: %w", err)
}
rpc := route.Chain(fmt.Sprintf("http://localhost:%d", evmPort), evmBlockchainID) + "/rpc"
// Create launch script
launchScript := filepath.Join(evmDataDir, "launch.sh")
script := fmt.Sprintf(`#!/bin/bash
echo "Starting EVM chain node..."
echo " Data directory: %s"
echo " RPC endpoint: %s"
echo ""
exec /home/z/work/lux/node/build/luxd --config-file=%s
`, evmDataDir, rpc, configPath)
if err := os.WriteFile(launchScript, []byte(script), 0o755); err != nil { //nolint:gosec // G306: Launch script needs to be executable
return fmt.Errorf("failed to create launch script: %w", err)
}
fmt.Println("\nāœ… EVM deployment prepared successfully!")
fmt.Println("\nšŸ“‹ Deployment Summary:")
fmt.Printf(" Data Directory: %s\n", evmDataDir)
fmt.Printf(" Config File: %s\n", configPath)
fmt.Printf(" Launch Script: %s\n", launchScript)
if existingDB != "" {
fmt.Printf(" Using Existing DB: %s (9.3GB with 1M+ blocks)\n", existingDB)
fmt.Println(" State: All accounts, balances, and contracts preserved")
}
fmt.Println("\nTo start the EVM chain:")
fmt.Printf(" %s\n", launchScript)
fmt.Println("\nšŸ“” Once running, access via:")
fmt.Printf(" RPC: %s\n", rpc)
return nil
}
// Additional helper commands can be added here for:
// - lux evm status - Check EVM status
// - lux evm stop - Stop EVM node
// - lux evm logs - View EVM logs
// - lux evm info - Get blockchain info