Files

234 lines
5.6 KiB
Go

// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package blockchaincmd
import (
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"github.com/luxfi/cli/v2/pkg/application"
"github.com/luxfi/cli/v2/pkg/constants"
"github.com/luxfi/cli/v2/pkg/models"
"github.com/luxfi/cli/v2/pkg/subnet"
"github.com/luxfi/cli/v2/pkg/utils"
"github.com/luxfi/ids"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
)
var deployed bool
// lux blockchain list
func newListCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List all created Blockchain configurations",
Long: `The blockchain list command prints the names of all created Blockchain configurations. Without any flags,
it prints some general, static information about the Blockchain. With the --deployed flag, the command
shows additional information including the VMID, BlockchainID and SubnetID.`,
RunE: listBlockchains,
}
cmd.Flags().BoolVar(&deployed, "deployed", false, "show additional deploy information")
return cmd
}
type subnetMatrix [][]string
func (c subnetMatrix) Len() int {
return len(c)
}
func (c subnetMatrix) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
// Compare strings by first key of the sub-slice
func (c subnetMatrix) Less(i, j int) bool {
return strings.Compare(c[i][0], c[j][0]) == -1
}
func listBlockchains(cmd *cobra.Command, args []string) error {
if deployed {
return listDeployInfo(cmd, args)
}
header := []string{"subnet", "chain", "chainID", "vmID", "type", "vm version", "from repo"}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(header)
table.SetAutoMergeCellsByColumnIndex([]int{0})
table.SetAutoMergeCells(true)
table.SetRowLine(true)
rows := subnetMatrix{}
cars, err := getSidecars(app)
if err != nil {
return err
}
for _, sc := range cars {
chainID := sc.ChainID
// for older sidecars, check in genesis if sidecar has
// no chainID set
if chainID == "" {
sc, err := app.LoadEvmGenesis(sc.Name)
// ignore the error in this case: just leave it to ""
if err == nil {
chainID = sc.Config.ChainID.String()
}
}
vmID := sc.ImportedVMID
if vmID == "" {
id, err := utils.VMID(sc.Name)
if err != nil {
vmID = constants.NotAvailableLabel
} else {
vmID = id.String()
}
}
rows = append(rows, []string{
sc.Subnet,
sc.Name,
chainID,
vmID,
string(sc.VM),
sc.VMVersion,
strconv.FormatBool(sc.ImportedFromLPM),
})
}
sort.Sort(rows)
for _, row := range rows {
table.Append(row)
}
table.Render()
return nil
}
func getSidecars(app *application.Lux) ([]*models.Sidecar, error) {
subnets, err := os.ReadDir(filepath.Join(app.GetBaseDir(), constants.SubnetDir))
if err != nil {
return nil, err
}
var cars []*models.Sidecar
for _, s := range subnets {
// this shouldn't happen but let's be safe
if !s.IsDir() {
continue
}
subnetDir := filepath.Join(app.GetSubnetDir(), s.Name())
files, err := os.ReadDir(subnetDir)
if err != nil {
return nil, err
}
for _, f := range files {
if f.Name() == constants.SidecarFileName {
carName := s.Name()
// read in sidecar file
sc, err := app.LoadSidecar(carName)
if err != nil {
return nil, err
}
cars = append(cars, &sc)
}
}
}
return cars, nil
}
func listDeployInfo(*cobra.Command, []string) error {
header := []string{"subnet", "chain", "vm ID", "Local Network", "Testnet (testnet)", "Mainnet"}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(header)
table.SetAutoMergeCellsByColumnIndex([]int{0, 1, 2, 3, 4})
table.SetAutoMergeCells(true)
table.SetRowLine(true)
rows := subnetMatrix{}
deployedNames, err := subnet.GetLocallyDeployedSubnets()
if err != nil {
// if the server can not be contacted, or there is a problem with the query,
// DO NOT FAIL, just print No for deployed status
app.Log.Warn("problem contacting server to get deployed subnets")
}
cars, err := getSidecars(app)
if err != nil {
return err
}
testnetKey := models.Testnet.String()
mainKey := models.Mainnet.String()
singleLine := true
for _, sc := range cars {
netToID := map[string][]string{}
deployedLocal := constants.NoLabel
if _, ok := deployedNames[sc.Subnet]; ok {
deployedLocal = constants.YesLabel
}
if _, ok := sc.Networks[testnetKey]; ok {
if sc.Networks[testnetKey].SubnetID != ids.Empty {
netToID[testnetKey] = []string{
constants.SubnetIDLabel + sc.Networks[testnetKey].SubnetID.String(),
constants.BlockchainIDLabel + sc.Networks[testnetKey].BlockchainID.String(),
}
singleLine = false
}
} else {
netToID[testnetKey] = []string{constants.NoLabel, constants.NoLabel}
}
if _, ok := sc.Networks[mainKey]; ok {
if sc.Networks[mainKey].SubnetID != ids.Empty {
netToID[mainKey] = []string{
constants.SubnetIDLabel + sc.Networks[mainKey].SubnetID.String(),
constants.BlockchainIDLabel + sc.Networks[mainKey].BlockchainID.String(),
}
singleLine = false
}
} else {
netToID[mainKey] = []string{constants.NoLabel, constants.NoLabel}
}
vmID := sc.ImportedVMID
if vmID == "" {
id, err := utils.VMID(sc.Name)
if err != nil {
vmID = constants.NotAvailableLabel
} else {
vmID = id.String()
}
}
rows = append(rows, []string{
sc.Subnet,
sc.Name,
vmID,
deployedLocal,
netToID[testnetKey][0],
netToID[mainKey][0],
})
if !singleLine {
rows = append(rows, []string{
sc.Subnet,
sc.Name,
vmID,
deployedLocal,
netToID[testnetKey][1],
netToID[mainKey][1],
})
}
}
sort.Sort(rows)
for _, row := range rows {
table.Append(row)
}
table.Render()
return nil
}