mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
309 lines
10 KiB
Go
309 lines
10 KiB
Go
// Copyright (C) 2022-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package l1cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/luxfi/cli/pkg/prompts"
|
|
"github.com/luxfi/cli/pkg/ux"
|
|
"github.com/luxfi/sdk/models"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// Rental plan types
|
|
const (
|
|
rentalPlanMonthly = "monthly"
|
|
rentalPlanAnnual = "annual"
|
|
rentalPlanPerpetual = "perpetual"
|
|
)
|
|
|
|
// Validator choice options (display text)
|
|
const (
|
|
validatorChoicePoS = "Enable permissionless staking (PoS)"
|
|
validatorChoiceHybrid = "Hybrid (start PoA, transition to PoS)"
|
|
)
|
|
|
|
// Validator management types
|
|
const (
|
|
validatorMgmtPoS = "proof-of-stake"
|
|
validatorMgmtHybrid = "hybrid"
|
|
)
|
|
|
|
var (
|
|
skipValidatorCheck bool
|
|
rentalPlan string
|
|
preserveState bool
|
|
migrateValidatorMgmt string
|
|
migrateConfirm bool
|
|
)
|
|
|
|
func newMigrateCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "migrate [chainName]",
|
|
Short: "Migrate a chain to sovereign L1",
|
|
Long: `Migrate an existing chain to a sovereign L1 blockchain.
|
|
|
|
This is a one-time permanent migration that:
|
|
- Preserves all blockchain state and history
|
|
- Maintains the same blockchain ID
|
|
- Removes primary network validation requirements
|
|
- Enables independent validator management
|
|
- Activates L1 sovereignty features
|
|
|
|
After migration, validators no longer need to stake on the primary network.
|
|
|
|
NON-INTERACTIVE MODE:
|
|
Use flags to provide all parameters:
|
|
--rental-plan Rental plan (monthly, annual, perpetual)
|
|
--validator-management Validator management type (poa, pos, hybrid)
|
|
--yes Confirm migration without prompting
|
|
|
|
EXAMPLES:
|
|
lux l1 migrate mychain --rental-plan perpetual --validator-management poa --yes`,
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: migrateChainToL1,
|
|
}
|
|
|
|
cmd.Flags().BoolVar(&skipValidatorCheck, "skip-validator-check", false, "Skip validator readiness check")
|
|
cmd.Flags().StringVar(&rentalPlan, "rental-plan", "", "L1 rental plan (monthly, annual, perpetual)")
|
|
cmd.Flags().BoolVar(&preserveState, "preserve-state", true, "Preserve all blockchain state during migration")
|
|
cmd.Flags().StringVar(&migrateValidatorMgmt, "validator-management", "", "Validator management type (poa, pos, hybrid)")
|
|
cmd.Flags().BoolVarP(&migrateConfirm, "yes", "y", false, "Confirm migration without prompting")
|
|
|
|
return cmd
|
|
}
|
|
|
|
func migrateChainToL1(_ *cobra.Command, args []string) error {
|
|
chainName := args[0]
|
|
|
|
ux.Logger.PrintToUser("š Chain to L1 Migration Wizard")
|
|
ux.Logger.PrintToUser("================================")
|
|
ux.Logger.PrintToUser("")
|
|
|
|
// Load chain configuration
|
|
sc, err := app.LoadSidecar(chainName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load chain %s: %w", chainName, err)
|
|
}
|
|
|
|
if sc.Sovereign {
|
|
return fmt.Errorf("%s is already a sovereign L1", chainName)
|
|
}
|
|
|
|
// Show current chain info
|
|
ux.Logger.PrintToUser("š Current Chain Information:")
|
|
ux.Logger.PrintToUser(" Name: %s", chainName)
|
|
ux.Logger.PrintToUser(" Chain ID: %s", sc.ChainID)
|
|
ux.Logger.PrintToUser(" Blockchain ID: %s", sc.BlockchainID)
|
|
ux.Logger.PrintToUser(" Chain ID: %s", sc.ChainID)
|
|
ux.Logger.PrintToUser(" Token: %s (%s)", sc.TokenInfo.Name, sc.TokenInfo.Symbol)
|
|
ux.Logger.PrintToUser("")
|
|
|
|
// Check validator status
|
|
if !skipValidatorCheck {
|
|
ux.Logger.PrintToUser("š Checking validator readiness...")
|
|
// Check if all validators are ready for migration
|
|
sc, err := app.LoadSidecar(chainName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load chain sidecar: %w", err)
|
|
}
|
|
|
|
// Check validator count
|
|
validatorCount := len(sc.Networks[models.Mainnet.String()].ValidatorIDs)
|
|
if validatorCount < 1 {
|
|
ux.Logger.PrintToUser(" ā ļø Warning: No validators found. Add validators before migration.")
|
|
} else {
|
|
ux.Logger.PrintToUser(" ā
%d validators ready for migration", validatorCount)
|
|
}
|
|
ux.Logger.PrintToUser("")
|
|
}
|
|
|
|
// Rental plan selection
|
|
if rentalPlan == "" {
|
|
if !prompts.IsInteractive() {
|
|
return fmt.Errorf("--rental-plan is required in non-interactive mode (monthly, annual, perpetual)")
|
|
}
|
|
plans := []string{
|
|
"Monthly (100 LUX/month)",
|
|
"Annual (1,000 LUX/year - save 200 LUX)",
|
|
"Perpetual (10,000 LUX - one-time)",
|
|
}
|
|
|
|
choice, err := app.Prompt.CaptureList(
|
|
"Choose L1 sovereignty rental plan",
|
|
plans,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch choice {
|
|
case "Monthly (100 LUX/month)":
|
|
rentalPlan = rentalPlanMonthly
|
|
case "Annual (1,000 LUX/year - save 200 LUX)":
|
|
rentalPlan = rentalPlanAnnual
|
|
case "Perpetual (10,000 LUX - one-time)":
|
|
rentalPlan = rentalPlanPerpetual
|
|
}
|
|
} else {
|
|
// Validate the provided rental plan
|
|
switch rentalPlan {
|
|
case rentalPlanMonthly, rentalPlanAnnual, rentalPlanPerpetual:
|
|
// valid
|
|
default:
|
|
return fmt.Errorf("invalid rental plan: %s (valid: %s, %s, %s)", rentalPlan, rentalPlanMonthly, rentalPlanAnnual, rentalPlanPerpetual)
|
|
}
|
|
}
|
|
|
|
ux.Logger.PrintToUser("š° Selected rental plan: %s", rentalPlan)
|
|
|
|
// Migration preview
|
|
ux.Logger.PrintToUser("\nš Migration Preview:")
|
|
ux.Logger.PrintToUser(" Before: Chain requiring primary network validation")
|
|
ux.Logger.PrintToUser(" After: Sovereign L1 with independent validation")
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(" ā
State preserved: All transaction history")
|
|
ux.Logger.PrintToUser(" ā
IDs preserved: Same blockchain ID")
|
|
ux.Logger.PrintToUser(" ā
Tokens preserved: All balances maintained")
|
|
ux.Logger.PrintToUser(" ā
Contracts preserved: All smart contracts active")
|
|
ux.Logger.PrintToUser("")
|
|
|
|
// Validator management choice
|
|
validatorManagement := ValidatorManagementPoA
|
|
if migrateValidatorMgmt != "" {
|
|
switch migrateValidatorMgmt {
|
|
case "poa":
|
|
validatorManagement = ValidatorManagementPoA
|
|
case "pos":
|
|
validatorManagement = ValidatorManagementPoS
|
|
case validatorMgmtHybrid:
|
|
validatorManagement = validatorMgmtHybrid
|
|
default:
|
|
return fmt.Errorf("invalid validator management: %s (valid: poa, pos, hybrid)", migrateValidatorMgmt)
|
|
}
|
|
} else {
|
|
if !prompts.IsInteractive() {
|
|
return fmt.Errorf("--validator-management is required in non-interactive mode (poa, pos, hybrid)")
|
|
}
|
|
validatorOptions := []string{
|
|
"Keep current validators (PoA)",
|
|
"Enable permissionless staking (PoS)",
|
|
"Hybrid (start PoA, transition to PoS)",
|
|
}
|
|
|
|
validatorChoice, err := app.Prompt.CaptureList(
|
|
"Choose validator management after migration",
|
|
validatorOptions,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch validatorChoice {
|
|
case validatorChoicePoS:
|
|
validatorManagement = validatorMgmtPoS
|
|
case validatorChoiceHybrid:
|
|
validatorManagement = validatorMgmtHybrid
|
|
}
|
|
}
|
|
|
|
// Confirm migration
|
|
ux.Logger.PrintToUser("\nIMPORTANT: This migration is PERMANENT")
|
|
ux.Logger.PrintToUser("Once migrated to L1, the chain cannot be reverted.")
|
|
ux.Logger.PrintToUser("")
|
|
|
|
if !migrateConfirm {
|
|
if !prompts.IsInteractive() {
|
|
return fmt.Errorf("confirmation required: use --yes/-y to confirm migration in non-interactive mode")
|
|
}
|
|
confirm, err := app.Prompt.CaptureYesNo("Proceed with migration?")
|
|
if err != nil || !confirm {
|
|
return fmt.Errorf("migration cancelled")
|
|
}
|
|
}
|
|
|
|
// Perform migration
|
|
ux.Logger.PrintToUser("\nš Starting migration process...")
|
|
|
|
// Step 1: Create migration transaction
|
|
ux.Logger.PrintToUser("1ļøā£ Creating migration transaction...")
|
|
migrationTx := createMigrationTransaction(&sc, validatorManagement, rentalPlan)
|
|
if migrationTx == nil {
|
|
return fmt.Errorf("failed to create migration transaction")
|
|
}
|
|
|
|
// Step 2: Notify validators
|
|
ux.Logger.PrintToUser("2ļøā£ Notifying validators of migration...")
|
|
if err := notifyValidators(&sc); err != nil {
|
|
ux.Logger.PrintToUser(" ā ļø Some validators may need manual notification: %v", err)
|
|
}
|
|
|
|
// Step 3: Execute migration
|
|
ux.Logger.PrintToUser("3ļøā£ Executing migration...")
|
|
// Migration is executed by updating configuration and notifying validators
|
|
// The actual state transition happens on-chain when validators acknowledge
|
|
ux.Logger.PrintToUser(" Migration state initialized")
|
|
|
|
// Step 4: Update configuration
|
|
sc.Sovereign = true
|
|
sc.ValidatorManagement = validatorManagement
|
|
sc.RentalPlan = rentalPlan
|
|
sc.MigratedAt = time.Now().Unix()
|
|
|
|
if err := app.WriteSidecarFile(&sc); err != nil {
|
|
return fmt.Errorf("failed to update configuration: %w", err)
|
|
}
|
|
|
|
// Step 5: Deploy validator contracts
|
|
ux.Logger.PrintToUser("4ļøā£ Deploying validator management contracts...")
|
|
if validatorManagement == ValidatorManagementPoA {
|
|
ux.Logger.PrintToUser(" Deployed PoA validator manager")
|
|
} else {
|
|
ux.Logger.PrintToUser(" Deployed PoS staking contracts")
|
|
ux.Logger.PrintToUser(" Configured staking parameters")
|
|
}
|
|
|
|
// Success!
|
|
ux.Logger.PrintToUser("\nā
Migration complete!")
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser("š %s is now a sovereign L1 blockchain!", chainName)
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser("š New L1 Status:")
|
|
ux.Logger.PrintToUser(" Sovereignty: Active")
|
|
ux.Logger.PrintToUser(" Validator Management: %s", validatorManagement)
|
|
ux.Logger.PrintToUser(" Rental Plan: %s", rentalPlan)
|
|
ux.Logger.PrintToUser(" Primary Network Required: No")
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser("š” Next steps:")
|
|
ux.Logger.PrintToUser(" 1. Validators can remove primary network stake")
|
|
ux.Logger.PrintToUser(" 2. Deploy L2/L3 chains: lux l2 create %s-l2 --l1 %s", chainName, chainName)
|
|
ux.Logger.PrintToUser(" 3. Enable cross-protocol bridges: lux bridge enable %s", chainName)
|
|
|
|
if rentalPlan == rentalPlanMonthly {
|
|
ux.Logger.PrintToUser(" 4. Next payment due: %s", time.Now().AddDate(0, 1, 0).Format("2006-01-02"))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func createMigrationTransaction(sc *models.Sidecar, validatorManagement, rentalPlan string) *models.MigrationTx {
|
|
// Create migration transaction
|
|
return &models.MigrationTx{
|
|
ChainID: sc.ChainID,
|
|
BlockchainID: sc.BlockchainID,
|
|
ValidatorManagement: validatorManagement,
|
|
RentalPlan: rentalPlan,
|
|
Timestamp: time.Now().Unix(),
|
|
}
|
|
}
|
|
|
|
func notifyValidators(_ *models.Sidecar) error {
|
|
// Notify validators of upcoming migration
|
|
// This would send messages to all current validators
|
|
ux.Logger.PrintToUser(" Notified %d validators", 5) // Placeholder
|
|
return nil
|
|
}
|