mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
350 lines
8.6 KiB
Go
350 lines
8.6 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
package blockchaincmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
|
|
"github.com/luxfi/cli/v2/pkg/lpmintegration"
|
|
"github.com/luxfi/cli/v2/pkg/cobrautils"
|
|
"github.com/luxfi/cli/v2/pkg/constants"
|
|
"github.com/luxfi/cli/v2/pkg/models"
|
|
"github.com/luxfi/cli/v2/pkg/ux"
|
|
"github.com/luxfi/cli/v2/pkg/vm"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
overwriteImport bool
|
|
repoOrURL string
|
|
subnetAlias string
|
|
branch string
|
|
)
|
|
|
|
// lux blockchain import file
|
|
func newImportFileCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "file [blockchainPath]",
|
|
Short: "Import an existing blockchain config",
|
|
RunE: importFile,
|
|
Args: cobrautils.MaximumNArgs(1),
|
|
Long: `The blockchain import command will import a blockchain configuration from a file or a git repository.
|
|
|
|
To import from a file, you can optionally provide the path as a command-line argument.
|
|
Alternatively, running the command without any arguments triggers an interactive wizard.
|
|
To import from a repository, go through the wizard. By default, an imported Blockchain doesn't
|
|
overwrite an existing Blockchain with the same name. To allow overwrites, provide the --force
|
|
flag.`,
|
|
}
|
|
cmd.Flags().BoolVarP(
|
|
&overwriteImport,
|
|
"force",
|
|
"f",
|
|
false,
|
|
"overwrite the existing configuration if one exists",
|
|
)
|
|
cmd.Flags().StringVar(
|
|
&repoOrURL,
|
|
"repo",
|
|
"",
|
|
"the repo to import (ex: luxfi/plugins-core) or url to download the repo from",
|
|
)
|
|
cmd.Flags().StringVar(
|
|
&branch,
|
|
"branch",
|
|
"",
|
|
"the repo branch to use if downloading a new repo",
|
|
)
|
|
cmd.Flags().StringVar(
|
|
&subnetAlias,
|
|
"blockchain",
|
|
"",
|
|
"the blockchain configuration to import from the provided repo",
|
|
)
|
|
return cmd
|
|
}
|
|
|
|
func importFile(_ *cobra.Command, args []string) error {
|
|
if len(args) == 1 {
|
|
importPath := args[0]
|
|
return importFromFile(importPath)
|
|
}
|
|
|
|
if repoOrURL == "" && branch == "" && subnetAlias == "" {
|
|
fileOption := "File"
|
|
lpmOption := "Repository"
|
|
typeOptions := []string{fileOption, lpmOption}
|
|
promptStr := "Would you like to import your blockchain from a file or a repository?"
|
|
result, err := app.Prompt.CaptureList(promptStr, typeOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if result == fileOption {
|
|
return importFromFile("")
|
|
}
|
|
}
|
|
|
|
// Option must be LPM
|
|
return importFromLPM()
|
|
}
|
|
|
|
func importFromFile(importPath string) error {
|
|
var err error
|
|
if importPath == "" {
|
|
promptStr := "Select the file to import your blockchain from"
|
|
importPath, err = app.Prompt.CaptureExistingFilepath(promptStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
importFileBytes, err := os.ReadFile(importPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
importable := models.Exportable{}
|
|
err = json.Unmarshal(importFileBytes, &importable)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
blockchainName := importable.Sidecar.Name
|
|
if blockchainName == "" {
|
|
return errors.New("export data is malformed: missing blockchain name")
|
|
}
|
|
|
|
if app.GenesisExists(blockchainName) && !overwriteImport {
|
|
return errors.New("blockchain already exists. Use --" + forceFlag + " parameter to overwrite")
|
|
}
|
|
|
|
if importable.Sidecar.VM == models.CustomVM {
|
|
if importable.Sidecar.CustomVMRepoURL == "" {
|
|
return fmt.Errorf("repository url must be defined for custom vm import")
|
|
}
|
|
if importable.Sidecar.CustomVMBranch == "" {
|
|
return fmt.Errorf("repository branch must be defined for custom vm import")
|
|
}
|
|
if importable.Sidecar.CustomVMBuildScript == "" {
|
|
return fmt.Errorf("build script must be defined for custom vm import")
|
|
}
|
|
|
|
if err := vm.BuildCustomVM(app, &importable.Sidecar); err != nil {
|
|
return err
|
|
}
|
|
|
|
vmPath := app.GetCustomVMPath(blockchainName)
|
|
rpcVersion, err := vm.GetVMBinaryProtocolVersion(vmPath)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to get custom binary RPC version: %w", err)
|
|
}
|
|
if rpcVersion != importable.Sidecar.RPCVersion {
|
|
return fmt.Errorf("RPC version mismatch between sidecar and vm binary (%d vs %d)", importable.Sidecar.RPCVersion, rpcVersion)
|
|
}
|
|
}
|
|
|
|
if err := app.WriteGenesisFile(blockchainName, importable.Genesis); err != nil {
|
|
return err
|
|
}
|
|
|
|
if importable.NodeConfig != nil {
|
|
if err := app.WriteLuxdNodeConfigFile(blockchainName, importable.NodeConfig); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
_ = os.RemoveAll(app.GetLuxdNodeConfigPath(blockchainName))
|
|
}
|
|
|
|
if importable.ChainConfig != nil {
|
|
if err := app.WriteChainConfigFile(blockchainName, importable.ChainConfig); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
_ = os.RemoveAll(app.GetChainConfigPath(blockchainName))
|
|
}
|
|
|
|
if importable.SubnetConfig != nil {
|
|
if err := app.WriteLuxdSubnetConfigFile(blockchainName, importable.SubnetConfig); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
_ = os.RemoveAll(app.GetLuxdSubnetConfigPath(blockchainName))
|
|
}
|
|
|
|
if importable.NetworkUpgrades != nil {
|
|
if err := app.WriteNetworkUpgradesFile(blockchainName, importable.NetworkUpgrades); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
_ = os.RemoveAll(app.GetUpgradeBytesFilepath(blockchainName))
|
|
}
|
|
|
|
if err := app.CreateSidecar(&importable.Sidecar); err != nil {
|
|
return err
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Blockchain imported successfully")
|
|
|
|
return nil
|
|
}
|
|
|
|
func importFromLPM() error {
|
|
// setup lpm
|
|
usr, err := user.Current()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lpmBaseDir := filepath.Join(usr.HomeDir, constants.LPMDir)
|
|
if err = lpmintegration.SetupApm(app, lpmBaseDir); err != nil {
|
|
return err
|
|
}
|
|
installedRepos, err := lpmintegration.GetRepos(app)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var repoAlias string
|
|
var repoURL *url.URL
|
|
var promptStr string
|
|
customRepo := "Download new repo"
|
|
|
|
if repoOrURL != "" {
|
|
for _, installedRepo := range installedRepos {
|
|
if repoOrURL == installedRepo {
|
|
repoAlias = installedRepo
|
|
break
|
|
}
|
|
}
|
|
if repoAlias == "" {
|
|
repoAlias = customRepo
|
|
repoURL, err = url.ParseRequestURI(repoOrURL)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid url in flag: %w", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if repoAlias == "" {
|
|
installedRepos = append(installedRepos, customRepo)
|
|
|
|
promptStr := "What repo would you like to import from"
|
|
repoAlias, err = app.Prompt.CaptureList(promptStr, installedRepos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if repoAlias == customRepo {
|
|
if repoURL == nil {
|
|
promptStr = "Enter your repo URL"
|
|
repoURL, err = app.Prompt.CaptureGitURL(promptStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if branch == "" {
|
|
mainBranch := "main"
|
|
masterBranch := "master"
|
|
customBranch := "custom"
|
|
branchList := []string{mainBranch, masterBranch, customBranch}
|
|
promptStr = "What branch would you like to import from"
|
|
branch, err = app.Prompt.CaptureList(promptStr, branchList)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
repoAlias, err = lpmintegration.AddRepo(app, repoURL, branch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = lpmintegration.UpdateRepos(app)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
subnets, err := lpmintegration.GetSubnets(app, repoAlias)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var subnet string
|
|
if subnetAlias != "" {
|
|
for _, availableSubnet := range subnets {
|
|
if subnetAlias == availableSubnet {
|
|
subnet = subnetAlias
|
|
break
|
|
}
|
|
}
|
|
if subnet == "" {
|
|
return fmt.Errorf("unable to find blockchain %s", subnetAlias)
|
|
}
|
|
} else {
|
|
promptStr = "Select a blockchain to import"
|
|
subnet, err = app.Prompt.CaptureList(promptStr, subnets)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
subnetKey := lpmintegration.MakeKey(repoAlias, subnet)
|
|
|
|
// Populate the sidecar and create a genesis
|
|
subnetDescr, err := lpmintegration.LoadSubnetFile(app, subnetKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var vmType models.VMType = models.CustomVM
|
|
|
|
if len(subnetDescr.VMs) == 0 {
|
|
return errors.New("no vms found in the given blockchain")
|
|
} else if len(subnetDescr.VMs) == 0 {
|
|
return errors.New("multiple vm blockchains are not supported")
|
|
}
|
|
|
|
vmDescr, err := lpmintegration.LoadVMFile(app, repoAlias, subnetDescr.VMs[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// this is automatically tagged as a custom VM, so we don't check the RPC
|
|
rpcVersion := 0
|
|
|
|
sidecar := models.Sidecar{
|
|
Name: subnetDescr.Alias,
|
|
VM: vmType,
|
|
RPCVersion: rpcVersion,
|
|
Subnet: subnetDescr.Alias,
|
|
TokenName: constants.DefaultTokenName,
|
|
TokenSymbol: constants.DefaultTokenSymbol,
|
|
Version: constants.SidecarVersion,
|
|
ImportedFromLPM: true,
|
|
ImportedVMID: vmDescr.ID,
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Selected blockchain, installing " + subnetKey)
|
|
|
|
if err = lpmintegration.InstallVM(app, subnetKey); err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.CreateSidecar(&sidecar)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create an empty genesis
|
|
return app.WriteGenesisFile(subnetDescr.Alias, []byte{})
|
|
}
|