Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 54 additions & 47 deletions cmd/blockchaincmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import (
"path/filepath"

"github.com/ava-labs/avalanche-cli/pkg/dependencies"
"github.com/spf13/pflag"

"github.com/ava-labs/avalanche-cli/cmd/flags"
"github.com/ava-labs/avalanche-cli/cmd/interchaincmd/messengercmd"
"github.com/ava-labs/avalanche-cli/cmd/interchaincmd/relayercmd"
"github.com/ava-labs/avalanche-cli/cmd/networkcmd"
"github.com/ava-labs/avalanche-cli/pkg/blockchain"
"github.com/ava-labs/avalanche-cli/pkg/cobrautils"
"github.com/ava-labs/avalanche-cli/pkg/constants"
"github.com/ava-labs/avalanche-cli/pkg/contract"
"github.com/ava-labs/avalanche-cli/pkg/interchain/relayer"
Expand Down Expand Up @@ -126,9 +126,16 @@ redeploy the chain with fresh state. You can deploy the same Blockchain to multi
so you can take your locally tested Blockchain and deploy it on Fuji or Mainnet.`,
RunE: deployBlockchain,
PersistentPostRun: handlePostRun,
Args: cobrautils.ExactArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
requiredArgCount := 1
if len(args) != requiredArgCount {
_ = cmd.Help() // show full help with flag grouping
return utils.ErrWrongArgCount(requiredArgCount, len(args))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also probably this error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean move utils.ErrWrongArgCount into cobrautils.ErrWrongArgCount

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is done in #2803

}
return nil
},
}
networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, networkoptions.DefaultSupportedNetworkOptions)
networkGroup := networkoptions.GetNetworkFlagsGroup(cmd, &globalNetworkFlags, true, networkoptions.DefaultSupportedNetworkOptions)
flags.AddSignatureAggregatorFlagsToCmd(cmd, &deployFlags.SigAggFlags)
cmd.Flags().StringVar(
&userProvidedAvagoVersion,
Expand All @@ -142,49 +149,13 @@ so you can take your locally tested Blockchain and deploy it on Fuji or Mainnet.
cmd.Flags().StringSliceVar(&controlKeys, "control-keys", nil, "addresses that may make blockchain changes")
cmd.Flags().StringSliceVar(&subnetAuthKeys, "auth-keys", nil, "control keys that will be used to authenticate chain creation")
cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "file path of the blockchain creation tx")
cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet deploy only]")
cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [local/devnet deploy only]")
cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)")
cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses")
cmd.Flags().StringVarP(&subnetIDStr, "subnet-id", "u", "", "do not create a subnet, deploy the blockchain into the given subnet id")
cmd.Flags().Uint32Var(&mainnetChainID, "mainnet-chain-id", 0, "use different ChainID for mainnet deployment")
cmd.Flags().StringVar(&avagoBinaryPath, "avalanchego-path", "", "use this avalanchego binary path")
cmd.Flags().BoolVar(&subnetOnly, "subnet-only", false, "only create a subnet")
cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-local-teleporter", false, "skip automatic ICM deploy on local networks [to be deprecated]")
cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-teleporter-deploy", false, "skip automatic ICM deploy")
cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-icm-deploy", false, "skip automatic ICM deploy")
cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "noicm", false, "skip automatic ICM deploy")
cmd.Flags().BoolVar(&icmSpec.SkipRelayerDeploy, skipRelayerFlagName, false, "skip relayer deploy")
cmd.Flags().StringVar(
&icmSpec.ICMVersion,
"teleporter-version",
constants.LatestReleaseVersionTag,
"ICM version to deploy",
)
cmd.Flags().StringVar(
&icmSpec.ICMVersion,
"icm-version",
constants.LatestReleaseVersionTag,
"ICM version to deploy",
)
cmd.Flags().StringVar(
&icmSpec.RelayerVersion,
"relayer-version",
constants.DefaultRelayerVersion,
"relayer version to deploy",
)
cmd.Flags().StringVar(&icmSpec.RelayerBinPath, "relayer-path", "", "relayer binary to use")
cmd.Flags().StringVar(&icmSpec.RelayerLogLevel, "relayer-log-level", "info", "log level to be used for relayer logs")
cmd.Flags().Float64Var(&relayerAmount, "relayer-amount", 0, "automatically fund relayer fee payments with the given amount")
cmd.Flags().StringVar(&relayerKeyName, "relayer-key", "", "key to be used by default both for rewards and to pay fees")
cmd.Flags().StringVar(&icmKeyName, "icm-key", constants.ICMKeyName, "key to be used to pay for ICM deploys")
cmd.Flags().StringVar(&cchainIcmKeyName, "cchain-icm-key", "", "key to be used to pay for ICM deploys on C-Chain")
cmd.Flags().BoolVar(&relayCChain, "relay-cchain", true, "relay C-Chain as source and destination")
cmd.Flags().StringVar(&cChainFundingKey, "cchain-funding-key", "", "key to be used to fund relayer account on cchain")
cmd.Flags().BoolVar(&relayerAllowPrivateIPs, "relayer-allow-private-ips", true, "allow relayer to connec to private ips")
cmd.Flags().StringVar(&icmSpec.MessengerContractAddressPath, "teleporter-messenger-contract-address-path", "", "path to an ICM Messenger contract address file")
cmd.Flags().StringVar(&icmSpec.MessengerDeployerAddressPath, "teleporter-messenger-deployer-address-path", "", "path to an ICM Messenger deployer address file")
cmd.Flags().StringVar(&icmSpec.MessengerDeployerTxPath, "teleporter-messenger-deployer-tx-path", "", "path to an ICM Messenger deployer tx file")
cmd.Flags().StringVar(&icmSpec.RegistryBydecodePath, "teleporter-registry-bytecode-path", "", "path to an ICM Registry bytecode file")
cmd.Flags().StringVar(&bootstrapValidatorsJSONFilePath, "bootstrap-filepath", "", "JSON file path that provides details about bootstrap validators, leave Node-ID and BLS values empty if using --generate-node-id=true")
cmd.Flags().BoolVar(&generateNodeID, "generate-node-id", false, "whether to create new node id for bootstrap validators (Node-ID and BLS values in bootstrap JSON file will be overridden if --bootstrap-filepath flag is used)")
cmd.Flags().StringSliceVar(&bootstrapEndpoints, "bootstrap-endpoints", nil, "take validator node info from the given endpoints")
Expand All @@ -202,15 +173,51 @@ so you can take your locally tested Blockchain and deploy it on Fuji or Mainnet.
cmd.Flags().UintSliceVar(&stakingPorts, "staking-port", []uint{}, "staking port for node(s)")
cmd.Flags().StringVar(&changeOwnerAddress, "change-owner-address", "", "address that will receive change if node is no longer L1 validator")

cmd.Flags().Uint64Var(&poSMinimumStakeAmount, "pos-minimum-stake-amount", 1, "minimum stake amount")
cmd.Flags().Uint64Var(&poSMaximumStakeAmount, "pos-maximum-stake-amount", 1000, "maximum stake amount")
cmd.Flags().Uint64Var(&poSMinimumStakeDuration, "pos-minimum-stake-duration", constants.PoSL1MinimumStakeDurationSeconds, "minimum stake duration (in seconds)")
cmd.Flags().Uint16Var(&poSMinimumDelegationFee, "pos-minimum-delegation-fee", 1, "minimum delegation fee")
cmd.Flags().Uint8Var(&poSMaximumStakeMultiplier, "pos-maximum-stake-multiplier", 1, "maximum stake multiplier")
cmd.Flags().Uint64Var(&poSWeightToValueFactor, "pos-weight-to-value-factor", 1, "weight to value factor")

cmd.Flags().BoolVar(&partialSync, "partial-sync", true, "set primary network partial sync for new validators")
cmd.Flags().Uint32Var(&numNodes, "num-nodes", constants.LocalNetworkNumNodes, "number of nodes to be created on local network deploy")

icmGroup := flags.RegisterFlagGroup(cmd, "ICM Flags", "show-icm-flags", false, func(set *pflag.FlagSet) {
set.BoolVar(&icmSpec.SkipICMDeploy, "skip-icm-deploy", false, "Skip automatic ICM deploy")
set.BoolVar(&icmSpec.SkipICMDeploy, "skip-local-teleporter", false, "skip automatic ICM deploy on local networks [to be deprecated]")
set.BoolVar(&icmSpec.SkipICMDeploy, "skip-teleporter-deploy", false, "skip automatic ICM deploy")
set.BoolVar(&icmSpec.SkipICMDeploy, "noicm", false, "skip automatic ICM deploy")

set.BoolVar(&icmSpec.SkipRelayerDeploy, skipRelayerFlagName, false, "skip relayer deploy")

set.StringVar(&icmSpec.ICMVersion, "teleporter-version", constants.LatestReleaseVersionTag, "ICM version to deploy")
set.StringVar(&icmSpec.ICMVersion, "icm-version", constants.LatestReleaseVersionTag, "ICM version to deploy")

set.StringVar(&icmSpec.RelayerVersion, "relayer-version", constants.DefaultRelayerVersion, "relayer version to deploy")
set.StringVar(&icmSpec.RelayerBinPath, "relayer-path", "", "relayer binary to use")
set.StringVar(&icmSpec.RelayerLogLevel, "relayer-log-level", "info", "log level to be used for relayer logs")

set.Float64Var(&relayerAmount, "relayer-amount", 0, "automatically fund relayer fee payments with the given amount")
set.StringVar(&relayerKeyName, "relayer-key", "", "key to be used by default both for rewards and to pay fees")

set.StringVar(&icmKeyName, "icm-key", constants.ICMKeyName, "key to be used to pay for ICM deploys")
set.StringVar(&cchainIcmKeyName, "cchain-icm-key", "", "key to be used to pay for ICM deploys on C-Chain")

set.BoolVar(&relayCChain, "relay-cchain", true, "relay C-Chain as source and destination")
set.StringVar(&cChainFundingKey, "cchain-funding-key", "", "key to be used to fund relayer account on cchain")

set.BoolVar(&relayerAllowPrivateIPs, "relayer-allow-private-ips", true, "allow relayer to connec to private ips")

set.StringVar(&icmSpec.MessengerContractAddressPath, "teleporter-messenger-contract-address-path", "", "path to an ICM Messenger contract address file")
set.StringVar(&icmSpec.MessengerDeployerAddressPath, "teleporter-messenger-deployer-address-path", "", "path to an ICM Messenger deployer address file")
set.StringVar(&icmSpec.MessengerDeployerTxPath, "teleporter-messenger-deployer-tx-path", "", "path to an ICM Messenger deployer tx file")
set.StringVar(&icmSpec.RegistryBydecodePath, "teleporter-registry-bytecode-path", "", "path to an ICM Registry bytecode file")
})

posGroup := flags.RegisterFlagGroup(cmd, "Proof Of Stake Flags", "show-pos-flags", false, func(set *pflag.FlagSet) {
set.Uint64Var(&poSMinimumStakeAmount, "pos-minimum-stake-amount", 1, "minimum stake amount")
set.Uint64Var(&poSMaximumStakeAmount, "pos-maximum-stake-amount", 1000, "maximum stake amount")
set.Uint64Var(&poSMinimumStakeDuration, "pos-minimum-stake-duration", constants.PoSL1MinimumStakeDurationSeconds, "minimum stake duration (in seconds)")
set.Uint16Var(&poSMinimumDelegationFee, "pos-minimum-delegation-fee", 1, "minimum delegation fee")
set.Uint8Var(&poSMaximumStakeMultiplier, "pos-maximum-stake-multiplier", 1, "maximum stake multiplier")
set.Uint64Var(&poSWeightToValueFactor, "pos-weight-to-value-factor", 1, "weight to value factor")
})

cmd.SetHelpFunc(flags.WithGroupedHelp([]flags.GroupedFlags{networkGroup, icmGroup, posGroup}))
return cmd
}

Expand Down
133 changes: 133 additions & 0 deletions cmd/flags/groupedFlags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (C) 2022, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package flags

import (
"fmt"
"os"
"strings"

"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

type GroupedFlags struct {
Name string
ShowFlag string
FlagSet *pflag.FlagSet
IsAlwaysVisible bool
}

// WithGroupedHelp returns a cobra Run function that shows help organized by groups.
// It first shows normal command usage, then prints grouped flags, hiding some unless explicitly requested.
func WithGroupedHelp(groups []GroupedFlags) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, _ []string) {
shownGroups := determineShownGroups(groups)

printUsage(cmd)

for _, group := range groups {
printGroup(cmd, group, shownGroups[group.Name])
}
}
}

// determineShownGroups decides which flag groups should be visible based on user input and group settings.
func determineShownGroups(groups []GroupedFlags) map[string]bool {
shown := make(map[string]bool)
for _, group := range groups {
if group.IsAlwaysVisible || flagExists(group.ShowFlag, os.Args) {
shown[group.Name] = true
}
}
return shown
}

// printUsage prints the general command usage/help text.
func printUsage(cmd *cobra.Command) {
if err := cmd.Root().UsageFunc()(cmd); err != nil {
fmt.Fprintf(cmd.ErrOrStderr(), "error showing command usage: %v\n", err)
}
}

// printGroup prints a specific group of flags, properly formatted and optionally hidden if not shown.
func printGroup(cmd *cobra.Command, group GroupedFlags, isShown bool) {
fmt.Fprintf(cmd.OutOrStdout(), "\n%s:\n", group.Name)

if !isShown {
fmt.Fprintf(cmd.OutOrStdout(), " (hidden) Use %s to show these options\n", group.ShowFlag)
return
}

flags, maxLen := collectFlags(group.FlagSet)

for _, flag := range flags {
padding := strings.Repeat(" ", maxLen-len(flag.nameAndType)+2)
fmt.Fprintf(cmd.OutOrStdout(), " %s%s%s\n", flag.nameAndType, padding, flag.usage)
}
}

// flagInfo holds the formatted flag name/type and its usage string.
type flagInfo struct {
nameAndType string
usage string
}

// collectFlags gathers all flags in a flag set, calculating the maximum width needed for alignment.
func collectFlags(flagSet *pflag.FlagSet) ([]flagInfo, int) {
var flags []flagInfo
maxLen := 0

flagSet.VisitAll(func(flag *pflag.Flag) {
nameAndType := "--" + flag.Name
if flag.Value.Type() != "bool" {
nameAndType += " " + flag.Value.Type()
}
if len(nameAndType) > maxLen {
maxLen = len(nameAndType)
}
flags = append(flags, flagInfo{nameAndType, flag.Usage})
})

return flags, maxLen
}

func flagExists(target string, args []string) bool {
for _, arg := range args {
if arg == target {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do we handle option casing (upper case\lower case) in CLI in general?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good question, we always put lowercase for flags in CLI. Not sure if cobra (the CLI SDK that we use) automatically does this for us.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found this https://github.com/spf13/pflag/blob/master/flag.go#L224 so i assume if we don't do anything special, the pflag pkg will honor the casing we passed in

return true
}
}
return false
}

func RegisterFlagGroup(cmd *cobra.Command, groupName string, showFlag string, isAlwaysVisible bool, defineFlags func(set *pflag.FlagSet)) GroupedFlags {
// Define the showFlag for controlling visibility
show := false
cmd.Flags().BoolVar(&show, showFlag, false, fmt.Sprintf("Show %s", groupName))

// Always hide the showFlag itself
cmd.Flags().Lookup(showFlag).Hidden = true

// Create a new FlagSet for this group
flagSet := pflag.NewFlagSet(groupName, pflag.ContinueOnError)

// Caller defines the flags
defineFlags(flagSet)

// Add the flagSet to the cmd's global flag list
cmd.Flags().AddFlagSet(flagSet)

// Always hide the grouped flags globally
flagSet.VisitAll(func(f *pflag.Flag) {
cmd.Flags().Lookup(f.Name).Hidden = true
})

// Return GroupedFlags
return GroupedFlags{
Name: groupName,
ShowFlag: "--" + showFlag,
FlagSet: flagSet,
IsAlwaysVisible: isAlwaysVisible,
}
}
31 changes: 31 additions & 0 deletions pkg/networkoptions/network_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"regexp"
"strings"

"github.com/spf13/pflag"

"github.com/ava-labs/avalanche-cli/cmd/flags"
"github.com/ava-labs/avalanche-cli/pkg/application"
"github.com/ava-labs/avalanche-cli/pkg/constants"
Expand Down Expand Up @@ -127,6 +129,35 @@ func AddNetworkFlagsToCmd(cmd *cobra.Command, networkFlags *NetworkFlags, addEnd
}
}

func GetNetworkFlagsGroup(cmd *cobra.Command, networkFlags *NetworkFlags, addEndpoint bool, supportedNetworkOptions []NetworkOption) flags.GroupedFlags {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should replace the existing AddNetworkFlagsToCmd command that is used by other commands right now

return flags.RegisterFlagGroup(cmd, "Network Flags (Select One)", "show-network-flags", true, func(set *pflag.FlagSet) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network group is always visible

addCluster := false
for _, networkOption := range supportedNetworkOptions {
switch networkOption {
case Local:
set.BoolVarP(&networkFlags.UseLocal, "local", "l", false, "operate on a local network")
case Devnet:
set.BoolVar(&networkFlags.UseDevnet, "devnet", false, "operate on a devnet network")
addEndpoint = true
addCluster = true
case Fuji:
set.BoolVarP(&networkFlags.UseFuji, "testnet", "t", false, "operate on testnet (alias to `fuji`)")
set.BoolVarP(&networkFlags.UseFuji, "fuji", "f", false, "operate on fuji (alias to `testnet`)")
case Mainnet:
set.BoolVarP(&networkFlags.UseMainnet, "mainnet", "m", false, "operate on mainnet")
case Cluster:
addCluster = true
}
}
if addCluster {
set.StringVar(&networkFlags.ClusterName, "cluster", "", "operate on the given cluster")
}
if addEndpoint {
set.StringVar(&networkFlags.Endpoint, "endpoint", "", "use the given endpoint for network operations")
}
})
}

func GetSupportedNetworkOptionsForSubnet(
app *application.Avalanche,
subnetName string,
Expand Down
4 changes: 4 additions & 0 deletions pkg/utils/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ import (
"golang.org/x/mod/semver"
)

func ErrWrongArgCount(expected, got int) error {
return fmt.Errorf("requires %d arg(s), got %d", expected, got)
}

func SetupRealtimeCLIOutput(
cmd *exec.Cmd,
redirectStdout bool,
Expand Down
Loading