Files
2026-01-14 19:55:40 -08:00

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
}