mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
222 lines
6.9 KiB
Go
222 lines
6.9 KiB
Go
// Copyright (C) 2024, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
package subnetcmd
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/luxfi/cli/v2/pkg/ux"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
targetBase string
|
|
hotSwap bool
|
|
)
|
|
|
|
func newMigrateBaseCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "migrate-base [subnetName]",
|
|
Short: "Migrate subnet to a different base chain",
|
|
Long: `Migrate a subnet (L2) to use a different base chain for sequencing.
|
|
|
|
This allows moving between Lux, Ethereum, or Lux as the sequencing layer
|
|
while preserving all state and history. The migration requires governance
|
|
approval and a brief pause for state checkpoint.
|
|
|
|
Example:
|
|
lux subnet migrate-base mySubnet --target ethereum`,
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: migrateBase,
|
|
}
|
|
|
|
cmd.Flags().StringVar(&targetBase, "target", "", "Target base chain (lux, ethereum, lux)")
|
|
cmd.Flags().BoolVar(&hotSwap, "hot-swap", false, "Attempt hot-swap migration (experimental)")
|
|
|
|
return cmd
|
|
}
|
|
|
|
func migrateBase(cmd *cobra.Command, args []string) error {
|
|
subnetName := args[0]
|
|
|
|
ux.Logger.PrintToUser("š Base Chain Migration")
|
|
ux.Logger.PrintToUser("======================")
|
|
ux.Logger.PrintToUser("")
|
|
|
|
// Load subnet configuration
|
|
sc, err := app.LoadSidecar(subnetName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load subnet %s: %w", subnetName, err)
|
|
}
|
|
|
|
if sc.Sovereign {
|
|
return fmt.Errorf("%s is a sovereign L1 and cannot use base chain sequencing", subnetName)
|
|
}
|
|
|
|
ux.Logger.PrintToUser("š Current Configuration:")
|
|
ux.Logger.PrintToUser(" Subnet Name: %s", subnetName)
|
|
ux.Logger.PrintToUser(" Current Base: %s", sc.BaseChain)
|
|
ux.Logger.PrintToUser(" Sequencer Type: %s", sc.SequencerType)
|
|
ux.Logger.PrintToUser(" Block Time: %dms", sc.L1BlockTime)
|
|
ux.Logger.PrintToUser("")
|
|
|
|
// Target base selection
|
|
if targetBase == "" {
|
|
baseOptions := []string{
|
|
"Lux (100ms blocks, lowest cost)",
|
|
"Ethereum (12s blocks, highest security)",
|
|
"Lux (2s blocks, fast finality)",
|
|
}
|
|
|
|
// Remove current base from options
|
|
filteredOptions := []string{}
|
|
for _, opt := range baseOptions {
|
|
if !isCurrentBase(opt, sc.BaseChain) {
|
|
filteredOptions = append(filteredOptions, opt)
|
|
}
|
|
}
|
|
|
|
choice, err := app.Prompt.CaptureList(
|
|
"Select new base chain",
|
|
filteredOptions,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
targetBase = parseBaseChoice(choice)
|
|
}
|
|
|
|
// Show migration impact
|
|
ux.Logger.PrintToUser("ā” Migration Impact Analysis:")
|
|
showMigrationImpact(sc.BaseChain, targetBase)
|
|
ux.Logger.PrintToUser("")
|
|
|
|
// Governance requirement
|
|
ux.Logger.PrintToUser("šļø Governance Requirements:")
|
|
ux.Logger.PrintToUser(" - Proposal submission: 100 LUX")
|
|
ux.Logger.PrintToUser(" - Voting period: 7 days")
|
|
ux.Logger.PrintToUser(" - Quorum: 10%% of staked tokens")
|
|
ux.Logger.PrintToUser(" - Approval threshold: 66%%")
|
|
ux.Logger.PrintToUser("")
|
|
|
|
// Migration mode
|
|
if !hotSwap {
|
|
ux.Logger.PrintToUser("š Cold Migration Process:")
|
|
ux.Logger.PrintToUser(" 1. Freeze subnet at specific block")
|
|
ux.Logger.PrintToUser(" 2. Create state checkpoint")
|
|
ux.Logger.PrintToUser(" 3. Deploy new inbox on target chain")
|
|
ux.Logger.PrintToUser(" 4. Update rollup configuration")
|
|
ux.Logger.PrintToUser(" 5. Resume from checkpoint")
|
|
ux.Logger.PrintToUser(" Expected downtime: ~10 minutes")
|
|
} else {
|
|
ux.Logger.PrintToUser("š„ Hot-Swap Migration (Experimental):")
|
|
ux.Logger.PrintToUser(" 1. Deploy inbox on target chain")
|
|
ux.Logger.PrintToUser(" 2. Dual-post blocks for transition period")
|
|
ux.Logger.PrintToUser(" 3. Validators switch to new base")
|
|
ux.Logger.PrintToUser(" 4. Deprecate old inbox")
|
|
ux.Logger.PrintToUser(" Expected downtime: None")
|
|
ux.Logger.PrintToUser(" ā ļø Higher complexity and gas costs")
|
|
}
|
|
ux.Logger.PrintToUser("")
|
|
|
|
// Confirm migration
|
|
confirm, err := app.Prompt.CaptureYesNo("Proceed with migration proposal?")
|
|
if err != nil || !confirm {
|
|
return fmt.Errorf("migration cancelled")
|
|
}
|
|
|
|
// Create governance proposal
|
|
ux.Logger.PrintToUser("\nš Creating Governance Proposal...")
|
|
|
|
// In a real implementation, this would create and submit a governance proposal
|
|
// proposal := &models.GovernanceProposal{
|
|
// Type: "base-migration",
|
|
// Title: fmt.Sprintf("Migrate %s from %s to %s", subnetName, sc.BaseChain, targetBase),
|
|
// Description: fmt.Sprintf("Migrate subnet to improve performance and reduce costs"),
|
|
// L2Name: subnetName,
|
|
// CurrentBase: sc.BaseChain,
|
|
// TargetBase: targetBase,
|
|
// HotSwap: hotSwap,
|
|
// CreatedAt: time.Now().Unix(),
|
|
// }
|
|
|
|
// Simulate proposal submission
|
|
ux.Logger.PrintToUser(" Submitting proposal...")
|
|
time.Sleep(2 * time.Second)
|
|
|
|
proposalID := fmt.Sprintf("PROP-%d", time.Now().Unix())
|
|
ux.Logger.PrintToUser(" ā
Proposal submitted: %s", proposalID)
|
|
ux.Logger.PrintToUser("")
|
|
|
|
// Next steps
|
|
ux.Logger.PrintToUser("š Proposal Status:")
|
|
ux.Logger.PrintToUser(" ID: %s", proposalID)
|
|
ux.Logger.PrintToUser(" Status: Voting Active")
|
|
ux.Logger.PrintToUser(" Ends: %s", time.Now().Add(7*24*time.Hour).Format("2006-01-02 15:04"))
|
|
ux.Logger.PrintToUser("")
|
|
|
|
ux.Logger.PrintToUser("š” Next steps:")
|
|
ux.Logger.PrintToUser(" 1. Share proposal: lux governance share %s", proposalID)
|
|
ux.Logger.PrintToUser(" 2. Monitor votes: lux governance status %s", proposalID)
|
|
ux.Logger.PrintToUser(" 3. Execute if passed: lux governance execute %s", proposalID)
|
|
|
|
return nil
|
|
}
|
|
|
|
func isCurrentBase(option, current string) bool {
|
|
switch current {
|
|
case "lux":
|
|
return option == "Lux (100ms blocks, lowest cost)"
|
|
case "ethereum":
|
|
return option == "Ethereum (12s blocks, highest security)"
|
|
case "lux":
|
|
return option == "Lux (2s blocks, fast finality)"
|
|
}
|
|
return false
|
|
}
|
|
|
|
func parseBaseChoice(choice string) string {
|
|
switch choice {
|
|
case "Lux (100ms blocks, lowest cost)":
|
|
return "lux"
|
|
case "Ethereum (12s blocks, highest security)":
|
|
return "ethereum"
|
|
case "Lux (2s blocks, fast finality)":
|
|
return "lux"
|
|
}
|
|
return choice
|
|
}
|
|
|
|
func showMigrationImpact(from, to string) {
|
|
// Block time changes
|
|
fromTime := getBlockTime(from)
|
|
toTime := getBlockTime(to)
|
|
|
|
if toTime < fromTime {
|
|
ux.Logger.PrintToUser(" ā¬ļø Faster block times: %dms ā %dms", fromTime, toTime)
|
|
ux.Logger.PrintToUser(" ā
Lower latency for users")
|
|
} else if toTime > fromTime {
|
|
ux.Logger.PrintToUser(" ā¬ļø Slower block times: %dms ā %dms", fromTime, toTime)
|
|
ux.Logger.PrintToUser(" ā ļø Higher latency but more security")
|
|
}
|
|
|
|
// Cost implications
|
|
switch to {
|
|
case "lux":
|
|
ux.Logger.PrintToUser(" šø Lowest data costs")
|
|
ux.Logger.PrintToUser(" š Ultra-low latency (100ms)")
|
|
case "ethereum":
|
|
ux.Logger.PrintToUser(" š° Higher data costs (ETH gas)")
|
|
ux.Logger.PrintToUser(" š”ļø Maximum security inheritance")
|
|
case "lux":
|
|
ux.Logger.PrintToUser(" šµ Moderate data costs")
|
|
ux.Logger.PrintToUser(" ā” Fast finality (~1s)")
|
|
}
|
|
|
|
// MEV implications
|
|
ux.Logger.PrintToUser(" š MEV flows to %s builders", to)
|
|
}
|
|
|