gitlab.com/SkynetLabs/skyd@v1.6.9/cmd/skyc/main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"os"
     7  	"reflect"
     8  	"strings"
     9  
    10  	"github.com/spf13/cobra"
    11  
    12  	"gitlab.com/NebulousLabs/errors"
    13  	"gitlab.com/SkynetLabs/skyd/build"
    14  	"gitlab.com/SkynetLabs/skyd/node/api"
    15  	"gitlab.com/SkynetLabs/skyd/node/api/client"
    16  	"go.sia.tech/siad/modules"
    17  )
    18  
    19  var (
    20  	// General Flags
    21  	alertSuppress bool
    22  	siaDir        string // Path to sia data dir
    23  	verbose       bool   // Display additional information
    24  
    25  	// Module Specific Flags
    26  	//
    27  	// Accounting Flags
    28  	accountingRangeEndTime   int64 // The end time for requesting a range of accounting information
    29  	accountingRangeStartTime int64 // The start time for requesting a range of accounting information
    30  	accountingYear           int   // The year for which the accounting information is requested
    31  	accountingQuarter        int   // The current year's quarter for which the accounting information is requested
    32  	accountingMonth          int   // The current year's month for which the accounting information is requested
    33  
    34  	// Daemon Flags
    35  	daemonStackOutputFile  string // The file that the stack trace will be written to
    36  	daemonCPUProfile       bool   // Indicates that the CPU profile should be started
    37  	daemonMemoryProfile    bool   // Indicates that the Memory profile should be started
    38  	daemonProfileDirectory string // The Directory where the profile logs are saved
    39  	daemonTraceProfile     bool   // Indicates that the Trace profile should be started
    40  
    41  	// Host Flags
    42  	hostContractOutputType string // output type for host contracts
    43  	hostFolderRemoveForce  bool   // force folder remove
    44  
    45  	// Renter Flags
    46  	dataPieces                string // the number of data pieces a file should be uploaded with
    47  	parityPieces              string // the number of parity pieces a file should be uploaded with
    48  	renterAllContracts        bool   // Show all active and expired contracts
    49  	renterBubbleAll           bool   // Bubble the entire directory tree
    50  	renterDeleteRoot          bool   // Delete path start from root instead of the UserFolder.
    51  	renterDownloadAsync       bool   // Downloads files asynchronously
    52  	renterDownloadRecursive   bool   // Downloads folders recursively.
    53  	renterDownloadRoot        bool   // Download path start from root instead of the UserFolder.
    54  	renterFuseMountAllowOther bool   // Mount fuse with 'AllowOther' set to true.
    55  	renterListRecursive       bool   // List files of folder recursively.
    56  	renterListRoot            bool   // List path start from root instead of the UserFolder.
    57  	renterRenameRoot          bool   // Rename files relative to root instead of the UserFolder.
    58  	renterShowHistory         bool   // Show download history in addition to download queue.
    59  
    60  	// Renter Allowance Flags
    61  	allowanceFunds       string // amount of money to be used within a period
    62  	allowanceHosts       string // number of hosts to form contracts with
    63  	allowancePeriod      string // length of period
    64  	allowanceRenewWindow string // renew window of allowance
    65  
    66  	allowancePaymentContractInitialFunding string // initial price to pay to create a payment contract
    67  
    68  	allowanceExpectedDownload   string // expected data downloaded within period
    69  	allowanceExpectedRedundancy string // expected redundancy of most uploaded files
    70  	allowanceExpectedStorage    string // expected storage stored on hosts before redundancy
    71  	allowanceExpectedUpload     string // expected data uploaded within period
    72  
    73  	allowanceMaxContractPrice          string // maximum allowed price to form a contract
    74  	allowanceMaxDownloadBandwidthPrice string // max allowed price to download data from a host
    75  	allowanceMaxRPCPrice               string // maximum allowed base price for RPCs
    76  	allowanceMaxSectorAccessPrice      string // max allowed price to access a sector on a host
    77  	allowanceMaxStoragePrice           string // max allowed price to store data on a host
    78  	allowanceMaxUploadBandwidthPrice   string // max allowed price to upload data to a host
    79  
    80  	// Skykey Flags
    81  	skykeyID              string // ID used to identify a Skykey.
    82  	skykeyName            string // Name used to identify a Skykey.
    83  	skykeyRenameAs        string // Optional parameter to rename a Skykey while adding it.
    84  	skykeyShowPrivateKeys bool   // Set to true to show private key data.
    85  	skykeyType            string // Type used to create a new Skykey.
    86  
    87  	// Skynet Flags
    88  	skynetBlocklistHash            bool   // Indicates if the input for the blocklist is already a hash.
    89  	skynetLsRecursive              bool   // List files of folder recursively.
    90  	skynetLsRoot                   bool   // Use root as the base instead of the Skynet folder.
    91  	skynetUnpinRoot                bool   // Use root as the base instead of the Skynet folder.
    92  	skynetUploadDefaultPath        string // Specify the file to serve when no specific file is specified.
    93  	skynetUploadDisableDefaultPath bool   // This skyfile will not have a default path. The only way to use it is to download it.
    94  	skynetUploadDryRun             bool   // Perform a dry-run of the upload. This returns the skylink without actually uploading the file to the network.
    95  	skynetUploadErrorPages         string // Override error files for some error codes. Contains a JSON object that maps error codes to file names.
    96  	skynetUploadRoot               bool   // Use root as the base instead of the Skynet folder.
    97  	skynetUploadSeparately         bool   // When uploading all files from a directory, upload each file separately, generating individual skylinks.
    98  	skynetUploadSilent             bool   // Don't report progress while uploading
    99  	skynetUploadTryFiles           string // A comma-separated list of fallback files, in case the requested file is not available.
   100  	skynetPortalPublic             bool   // Specify if a portal is public or not
   101  
   102  	// Utils Flags
   103  	dictionaryLanguage string // dictionary for seed utils
   104  
   105  	// Wallet Flags
   106  	initForce            bool   // destroy and re-encrypt the wallet on init if it already exists
   107  	initPassword         bool   // supply a custom password when creating a wallet
   108  	walletRawTxn         bool   // Encode/decode transactions in base64-encoded binary.
   109  	walletStartHeight    uint64 // Start height for transaction search.
   110  	walletEndHeight      uint64 // End height for transaction search.
   111  	walletTxnFeeIncluded bool   // include the fee in the balance being sent
   112  )
   113  
   114  var (
   115  	// Globals.
   116  	rootCmd    *cobra.Command // Root command cobra object, used by bash completion cmd.
   117  	httpClient client.Client
   118  )
   119  
   120  // Exit codes.
   121  // inspired by sysexits.h
   122  const (
   123  	exitCodeGeneral = 1  // Not in sysexits.h, but is standard practice.
   124  	exitCodeUsage   = 64 // EX_USAGE in sysexits.h
   125  )
   126  
   127  // wrap wraps a generic command with a check that the command has been
   128  // passed the correct number of arguments. The command must take only strings
   129  // as arguments.
   130  func wrap(fn interface{}) func(*cobra.Command, []string) {
   131  	fnVal, fnType := reflect.ValueOf(fn), reflect.TypeOf(fn)
   132  	if fnType.Kind() != reflect.Func {
   133  		panic("wrapped function has wrong type signature")
   134  	}
   135  	for i := 0; i < fnType.NumIn(); i++ {
   136  		if fnType.In(i).Kind() != reflect.String {
   137  			panic("wrapped function has wrong type signature")
   138  		}
   139  	}
   140  
   141  	return func(cmd *cobra.Command, args []string) {
   142  		if len(args) != fnType.NumIn() {
   143  			_ = cmd.UsageFunc()(cmd)
   144  			os.Exit(exitCodeUsage)
   145  		}
   146  		argVals := make([]reflect.Value, fnType.NumIn())
   147  		for i := range args {
   148  			argVals[i] = reflect.ValueOf(args[i])
   149  		}
   150  		fnVal.Call(argVals)
   151  	}
   152  }
   153  
   154  // die prints its arguments to stderr, in production exits the program with the
   155  // default error code, during tests it passes panic so that tests can catch the
   156  // panic and check printed errors
   157  func die(args ...interface{}) {
   158  	fmt.Fprintln(os.Stderr, args...)
   159  
   160  	if build.Release == "testing" {
   161  		// In testing pass panic that can be catched and the test can continue
   162  		panic(errors.New("die panic for testing"))
   163  	}
   164  	// In production exit
   165  	os.Exit(exitCodeGeneral)
   166  }
   167  
   168  // statuscmd is the handler for the command `siac`
   169  // prints basic information about Sia.
   170  func statuscmd() {
   171  	// For UX formating
   172  	defer fmt.Println()
   173  
   174  	// Consensus Info
   175  	cg, err := httpClient.ConsensusGet()
   176  	if errors.Contains(err, api.ErrAPICallNotRecognized) {
   177  		// Assume module is not loaded if status command is not recognized.
   178  		fmt.Printf("Consensus:\n  Status: %s\n\n", moduleNotReadyStatus)
   179  	} else if err != nil {
   180  		die("Could not get consensus status:", err)
   181  	} else {
   182  		fmt.Printf(`Consensus:
   183    Synced: %v
   184    Height: %v
   185  
   186  `, yesNo(cg.Synced), cg.Height)
   187  	}
   188  
   189  	// Wallet Info
   190  	walletStatus, err := httpClient.WalletGet()
   191  	if errors.Contains(err, api.ErrAPICallNotRecognized) {
   192  		// Assume module is not loaded if status command is not recognized.
   193  		fmt.Printf("Wallet:\n  Status: %s\n\n", moduleNotReadyStatus)
   194  	} else if err != nil {
   195  		die("Could not get wallet status:", err)
   196  	} else if walletStatus.Unlocked {
   197  		fmt.Printf(`Wallet:
   198    Status:          unlocked
   199    Siacoin Balance: %v
   200  
   201  `, currencyUnits(walletStatus.ConfirmedSiacoinBalance))
   202  	} else {
   203  		fmt.Printf(`Wallet:
   204    Status: Locked
   205  
   206  `)
   207  	}
   208  
   209  	// Renter Info
   210  	fmt.Println(`Renter:`)
   211  	err = renterFilesAndContractSummary(verbose)
   212  	if err != nil {
   213  		die(err)
   214  	}
   215  
   216  	if !verbose {
   217  		return
   218  	}
   219  
   220  	// Global Daemon Rate Limits
   221  	dg, err := httpClient.DaemonSettingsGet()
   222  	if err != nil {
   223  		die("Could not get daemon:", err)
   224  	}
   225  	fmt.Printf(`
   226  Global `)
   227  	rateLimitSummary(dg.MaxDownloadSpeed, dg.MaxUploadSpeed)
   228  
   229  	// Gateway Rate Limits
   230  	gg, err := httpClient.GatewayGet()
   231  	if err != nil {
   232  		die("Could not get gateway:", err)
   233  	}
   234  	fmt.Printf(`
   235  Gateway `)
   236  	rateLimitSummary(gg.MaxDownloadSpeed, gg.MaxUploadSpeed)
   237  
   238  	// Renter Rate Limits
   239  	rg, err := httpClient.RenterGet()
   240  	if err != nil {
   241  		die("Error getting renter:", err)
   242  	}
   243  	fmt.Printf(`
   244  Renter `)
   245  	rateLimitSummary(rg.Settings.MaxDownloadSpeed, rg.Settings.MaxUploadSpeed)
   246  }
   247  
   248  // rateLimitSummary displays the a summary of the provided rate limits
   249  func rateLimitSummary(download, upload int64) {
   250  	fmt.Printf(`Rate limits: `)
   251  	if download == 0 {
   252  		fmt.Printf(`
   253    Download Speed: %v`, "no limit")
   254  	} else {
   255  		fmt.Printf(`
   256    Download Speed: %v`, ratelimitUnits(download))
   257  	}
   258  	if upload == 0 {
   259  		fmt.Printf(`
   260    Upload Speed:   %v
   261  `, "no limit")
   262  	} else {
   263  		fmt.Printf(`
   264    Upload Speed:   %v
   265  `, ratelimitUnits(upload))
   266  	}
   267  }
   268  
   269  func main() {
   270  	// initialize commands
   271  	rootCmd = initCmds()
   272  
   273  	// initialize client
   274  	initClient(rootCmd, &verbose, &httpClient, &siaDir, &alertSuppress)
   275  
   276  	// Perform some basic actions after cobra has initialized.
   277  	cobra.OnInitialize(func() {
   278  		// set API password if it was not set
   279  		setAPIPasswordIfNotSet()
   280  
   281  		// Check if the siaDir is set.
   282  		if siaDir == "" {
   283  			// No siaDir passed in, fetch the siaDir
   284  			siaDir = build.SiaDir()
   285  		}
   286  
   287  		// Trim prefixes for client address
   288  		httpClient.Address = strings.TrimPrefix(httpClient.Address, "http://")
   289  		httpClient.Address = strings.TrimPrefix(httpClient.Address, "https://")
   290  
   291  		// Check for Critical Alerts
   292  		alerts, err := httpClient.DaemonAlertsGet()
   293  		if err == nil && len(alerts.CriticalAlerts) > 0 && !alertSuppress {
   294  			printAlerts(alerts.CriticalAlerts, modules.SeverityCritical)
   295  			fmt.Println("------------------")
   296  			fmt.Printf("\n  The above %v critical alerts should be resolved ASAP\n\n", len(alerts.CriticalAlerts))
   297  		}
   298  	})
   299  
   300  	// run
   301  	if err := rootCmd.Execute(); err != nil {
   302  		// Since no commands return errors (all commands set Command.Run instead of
   303  		// Command.RunE), Command.Execute() should only return an error on an
   304  		// invalid command or flag. Therefore Command.Usage() was called (assuming
   305  		// Command.SilenceUsage is false) and we should exit with exitCodeUsage.
   306  		os.Exit(exitCodeUsage)
   307  	}
   308  }
   309  
   310  // initCmds initializes root command and its subcommands
   311  func initCmds() *cobra.Command {
   312  	root := &cobra.Command{
   313  		Use:   os.Args[0],
   314  		Short: "Skynet Client v" + build.NodeVersion,
   315  		Long:  "Skynet Client v" + build.NodeVersion,
   316  		Run:   wrap(statuscmd),
   317  	}
   318  
   319  	// create command tree (alphabetized by root command)
   320  	root.AddCommand(accountingCmd)
   321  	accountingCmd.Flags().Int64Var(&accountingRangeEndTime, "end", 0, "Unix timestamp for the end of the accounting info range.")
   322  	accountingCmd.Flags().Int64Var(&accountingRangeStartTime, "start", 0, "Unix timestamp for the start of the accounting info range.")
   323  	accountingCmd.AddCommand(accountingTxnsCmd)
   324  	accountingTxnsCmd.Flags().IntVar(&accountingYear, "year", 0, "The year for which the accounting information is requested.")
   325  	accountingTxnsCmd.Flags().IntVar(&accountingQuarter, "quarter", 0, "The current year's quarter for which the accounting information is requested. 1 - JFM, 2 - AMJ, 3 - JAS, 4 - OND")
   326  	accountingTxnsCmd.Flags().IntVar(&accountingMonth, "month", 0, "The current year's month (1-12) for which the accounting information is requested.")
   327  
   328  	root.AddCommand(consensusCmd)
   329  	root.AddCommand(jsonCmd)
   330  
   331  	root.AddCommand(gatewayCmd)
   332  	gatewayCmd.AddCommand(gatewayAddressCmd, gatewayBandwidthCmd, gatewayBlocklistCmd, gatewayConnectCmd, gatewayDisconnectCmd, gatewayListCmd, gatewayRatelimitCmd)
   333  	gatewayBlocklistCmd.AddCommand(gatewayBlocklistAppendCmd, gatewayBlocklistClearCmd, gatewayBlocklistRemoveCmd, gatewayBlocklistSetCmd)
   334  
   335  	root.AddCommand(hostCmd)
   336  	hostCmd.AddCommand(hostAnnounceCmd, hostConfigCmd, hostContractCmd, hostFolderCmd, hostSectorCmd)
   337  	hostFolderCmd.AddCommand(hostFolderAddCmd, hostFolderRemoveCmd, hostFolderResizeCmd)
   338  	hostSectorCmd.AddCommand(hostSectorDeleteCmd)
   339  	hostContractCmd.Flags().StringVarP(&hostContractOutputType, "type", "t", "value", "Select output type")
   340  	hostFolderRemoveCmd.Flags().BoolVarP(&hostFolderRemoveForce, "force", "f", false, "Force the removal of the folder and its data")
   341  
   342  	root.AddCommand(hostdbCmd)
   343  	hostdbCmd.AddCommand(hostdbBlockDomainCmd, hostdbBlockedDomainsCmd, hostdbFiltermodeCmd, hostdbSetFiltermodeCmd, hostdbUnblockDomainCmd, hostdbViewCmd)
   344  	hostdbCmd.Flags().IntVarP(&hostdbNumHosts, "numhosts", "n", 0, "Number of hosts to display from the hostdb")
   345  
   346  	root.AddCommand(minerCmd)
   347  	minerCmd.AddCommand(minerStartCmd, minerStopCmd)
   348  
   349  	renterContractInfoCmd.AddCommand(renterContractInfoScanCmd)
   350  
   351  	root.AddCommand(renterCmd)
   352  	renterCmd.AddCommand(renterAllowanceCmd, renterBubbleCmd, renterBackupCreateCmd, renterBackupListCmd, renterBackupLoadCmd,
   353  		renterCleanCmd, renterContractsCmd, renterContractsRecoveryScanProgressCmd, renterDownloadCancelCmd,
   354  		renterDownloadsCmd, renterExportCmd, renterFilesDeleteCmd, renterFilesDownloadCmd,
   355  		renterFilesListCmd, renterFilesRenameCmd, renterFilesUnstuckCmd, renterFilesUploadCmd,
   356  		renterFuseCmd, renterLostCmd, renterPricesCmd, renterRatelimitCmd, renterSetAllowanceCmd,
   357  		renterSetLocalPathCmd, renterTriggerContractRecoveryScanCmd, renterUploadsCmd, renterWorkersCmd,
   358  		renterHealthSummaryCmd, renterContractInfoCmd)
   359  	renterWorkersCmd.AddCommand(renterWorkersAccountsCmd, renterWorkersDownloadsCmd, renterWorkersRepairsCmd, renterWorkersMaintenanceCmd, renterWorkersPriceTableCmd, renterWorkersReadJobsCmd, renterWorkersHasSectorJobSCmd, renterWorkersUploadsCmd, renterWorkersReadRegistryCmd, renterWorkersUpdateRegistryCmd)
   360  
   361  	renterAllowanceCmd.AddCommand(renterAllowanceCancelCmd)
   362  	renterBubbleCmd.Flags().BoolVarP(&renterBubbleAll, "all", "A", false, "Bubble the entire directory tree")
   363  	renterContractsCmd.AddCommand(renterContractsViewCmd)
   364  	renterFilesUploadCmd.AddCommand(renterFilesUploadPauseCmd, renterFilesUploadResumeCmd)
   365  
   366  	renterContractsCmd.Flags().BoolVarP(&renterAllContracts, "all", "A", false, "Show all expired contracts in addition to active contracts")
   367  	renterDownloadsCmd.Flags().BoolVarP(&renterShowHistory, "history", "H", false, "Show download history in addition to the download queue")
   368  	renterFilesDeleteCmd.Flags().BoolVar(&renterDeleteRoot, "root", false, "Delete files and folders from root instead of from the user home directory")
   369  	renterFilesDownloadCmd.Flags().BoolVarP(&renterDownloadAsync, "async", "A", false, "Download file asynchronously")
   370  	renterFilesDownloadCmd.Flags().BoolVarP(&renterDownloadRecursive, "recursive", "R", false, "Download folder recursively")
   371  	renterFilesDownloadCmd.Flags().BoolVar(&renterDownloadRoot, "root", false, "Download files and folders from root instead of from the user home directory")
   372  	renterFilesListCmd.Flags().BoolVarP(&renterListRecursive, "recursive", "R", false, "Recursively list files and folders")
   373  	renterFilesListCmd.Flags().BoolVar(&renterListRoot, "root", false, "List files and folders from root instead of from the user home directory")
   374  	renterFilesUploadCmd.Flags().StringVar(&dataPieces, "data-pieces", "", "the number of data pieces a files should be uploaded with")
   375  	renterFilesUploadCmd.Flags().StringVar(&parityPieces, "parity-pieces", "", "the number of parity pieces a files should be uploaded with")
   376  	renterExportCmd.AddCommand(renterExportContractTxnsCmd)
   377  	renterFilesRenameCmd.Flags().BoolVar(&renterRenameRoot, "root", false, "Rename files relative to root instead of the user homedir")
   378  
   379  	renterSetAllowanceCmd.Flags().StringVar(&allowanceFunds, "amount", "", "amount of money in allowance, specified in currency units")
   380  	renterSetAllowanceCmd.Flags().StringVar(&allowancePeriod, "period", "", "period of allowance in blocks (b), hours (h), days (d) or weeks (w)")
   381  	renterSetAllowanceCmd.Flags().StringVar(&allowanceHosts, "hosts", "", "number of hosts the renter will spread the uploaded data across")
   382  	renterSetAllowanceCmd.Flags().StringVar(&allowanceRenewWindow, "renew-window", "", "renew window in blocks (b), hours (h), days (d) or weeks (w)")
   383  	renterSetAllowanceCmd.Flags().StringVar(&allowancePaymentContractInitialFunding, "payment-contract-initial-funding", "", "Setting this will cause the renter to form payment contracts, making it a Skynet portal.")
   384  	renterSetAllowanceCmd.Flags().StringVar(&allowanceExpectedStorage, "expected-storage", "", "expected storage in bytes (B), kilobytes (KB), megabytes (MB) etc. up to yottabytes (YB)")
   385  	renterSetAllowanceCmd.Flags().StringVar(&allowanceExpectedUpload, "expected-upload", "", "expected upload in period in bytes (B), kilobytes (KB), megabytes (MB) etc. up to yottabytes (YB)")
   386  	renterSetAllowanceCmd.Flags().StringVar(&allowanceExpectedDownload, "expected-download", "", "expected download in period in bytes (B), kilobytes (KB), megabytes (MB) etc. up to yottabytes (YB)")
   387  	renterSetAllowanceCmd.Flags().StringVar(&allowanceExpectedRedundancy, "expected-redundancy", "", "expected redundancy of most uploaded files")
   388  	renterSetAllowanceCmd.Flags().StringVar(&allowanceMaxRPCPrice, "max-rpc-price", "", "the maximum RPC base price that is allowed for a host")
   389  	renterSetAllowanceCmd.Flags().StringVar(&allowanceMaxContractPrice, "max-contract-price", "", "the maximum price that the renter will pay to form a contract with a host")
   390  	renterSetAllowanceCmd.Flags().StringVar(&allowanceMaxDownloadBandwidthPrice, "max-download-bandwidth-price", "", "the maximum price that the renter will pay to download from a host")
   391  	renterSetAllowanceCmd.Flags().StringVar(&allowanceMaxSectorAccessPrice, "max-sector-access-price", "", "the maximum price that the renter will pay to access a sector on a host")
   392  	renterSetAllowanceCmd.Flags().StringVar(&allowanceMaxStoragePrice, "max-storage-price", "", "the maximum price that the renter will pay to store data on a host")
   393  	renterSetAllowanceCmd.Flags().StringVar(&allowanceMaxUploadBandwidthPrice, "max-upload-bandwidth-price", "", "the maximum price that the renter will pay to upload data to a host")
   394  
   395  	renterFuseCmd.AddCommand(renterFuseMountCmd, renterFuseUnmountCmd)
   396  	renterFuseMountCmd.Flags().BoolVarP(&renterFuseMountAllowOther, "allow-other", "", false, "Allow users other than the user that mounted the fuse directory to access and use the fuse directory")
   397  
   398  	root.AddCommand(skynetCmd)
   399  	skynetCmd.AddCommand(skynetBackupCmd, skynetBlocklistCmd, skynetConvertCmd, skynetDownloadCmd, skynetIsBlockedCmd, skynetLsCmd, skynetPinCmd, skynetPortalsCmd, skynetRestoreCmd, skynetSkylinkCmd, skynetUnpinCmd, skynetUploadCmd)
   400  	skynetConvertCmd.Flags().StringVar(&skykeyName, "skykeyname", "", "Specify the skykey to be used by name.")
   401  	skynetConvertCmd.Flags().StringVar(&skykeyID, "skykeyid", "", "Specify the skykey to be used by id.")
   402  	skynetUploadCmd.Flags().BoolVar(&skynetUploadRoot, "root", false, "Use the root folder as the base instead of the Skynet folder")
   403  	skynetUploadCmd.Flags().BoolVar(&skynetUploadDryRun, "dry-run", false, "Perform a dry-run of the upload, returning the skylink without actually uploading the file")
   404  	skynetUploadCmd.Flags().BoolVarP(&skynetUploadSeparately, "separately", "", false, "Upload each file separately, generating individual skylinks")
   405  	skynetUploadCmd.Flags().StringVar(&skynetUploadDefaultPath, "defaultpath", "", "Specify the file to serve when no specific file is specified.")
   406  	skynetUploadCmd.Flags().BoolVarP(&skynetUploadDisableDefaultPath, "disabledefaultpath", "", false, "This skyfile will not have a default path. The only way to use it is to download it. Mutually exclusive with --defaultpath")
   407  	skynetUploadCmd.Flags().StringVar(&skynetUploadErrorPages, "errorpages", "{}", "Specify a JSON map of error codes and filename pairs which override the content served with the given error code. Example: {\"404\":\"notfound.html\"}")
   408  	skynetUploadCmd.Flags().BoolVarP(&skynetUploadSilent, "silent", "", false, "Don't report progress while uploading")
   409  	skynetUploadCmd.Flags().StringVar(&skynetUploadTryFiles, "tryfiles", "", "Specify an ordered, comma-separated list of files to be served if the requested file is not found.")
   410  	skynetUploadCmd.Flags().StringVar(&skykeyID, "skykeyid", "", "Specify the skykey to be used by its key identifier.")
   411  	skynetUploadCmd.Flags().StringVar(&skykeyName, "skykeyname", "", "Specify the skykey to be used by name.")
   412  	skynetUnpinCmd.Flags().BoolVar(&skynetUnpinRoot, "root", false, "Use the root folder as the base instead of the Skynet folder")
   413  	skynetLsCmd.Flags().BoolVarP(&skynetLsRecursive, "recursive", "R", false, "Recursively list skyfiles and folders")
   414  	skynetLsCmd.Flags().BoolVar(&skynetLsRoot, "root", false, "Use the root folder as the base instead of the Skynet folder")
   415  	skynetBlocklistCmd.AddCommand(skynetBlocklistAddCmd, skynetBlocklistRemoveCmd)
   416  	skynetBlocklistAddCmd.Flags().BoolVar(&skynetBlocklistHash, "hash", false, "Indicates if the input is already a hash of the Skylink's Merkleroot")
   417  	skynetBlocklistRemoveCmd.Flags().BoolVar(&skynetBlocklistHash, "hash", false, "Indicates if the input is already a hash of the Skylink's Merkleroot")
   418  	skynetPortalsCmd.AddCommand(skynetPortalsAddCmd, skynetPortalsRemoveCmd)
   419  	skynetPortalsAddCmd.Flags().BoolVar(&skynetPortalPublic, "public", false, "Add this Skynet portal as public")
   420  	skynetSkylinkCmd.AddCommand(skynetSkylinkCompareCmd, skynetSkylinkHealthCmd, skynetSkylinkLayoutCmd, skynetSkylinkMetadataCmd)
   421  
   422  	root.AddCommand(skykeyCmd)
   423  	skykeyCmd.AddCommand(skykeyAddCmd, skykeyCreateCmd, skykeyDeleteCmd, skykeyGetCmd, skykeyGetIDCmd, skykeyListCmd)
   424  	skykeyAddCmd.Flags().StringVar(&skykeyRenameAs, "rename-as", "", "The new name for the skykey being added")
   425  	skykeyCreateCmd.Flags().StringVar(&skykeyType, "type", "", "The type of the skykey")
   426  	skykeyDeleteCmd.AddCommand(skykeyDeleteNameCmd, skykeyDeleteIDCmd)
   427  	skykeyGetCmd.Flags().StringVar(&skykeyName, "name", "", "The name of the skykey")
   428  	skykeyGetCmd.Flags().StringVar(&skykeyID, "id", "", "The base-64 encoded skykey ID")
   429  	skykeyListCmd.Flags().BoolVar(&skykeyShowPrivateKeys, "show-priv-keys", false, "Show private key data.")
   430  
   431  	// Daemon Commands
   432  	root.AddCommand(alertsCmd, globalRatelimitCmd, profileCmd, stackCmd, stopCmd, versionCmd)
   433  	profileCmd.AddCommand(profileStartCmd, profileStopCmd)
   434  	profileStartCmd.Flags().BoolVarP(&daemonCPUProfile, "cpu", "c", false, "Start the CPU profile")
   435  	profileStartCmd.Flags().BoolVarP(&daemonMemoryProfile, "memory", "m", false, "Start the Memory profile")
   436  	profileStartCmd.Flags().StringVar(&daemonProfileDirectory, "profileDir", "", "Specify the directory where the profile logs are to be saved")
   437  	profileStartCmd.Flags().BoolVarP(&daemonTraceProfile, "trace", "t", false, "Start the Trace profile")
   438  	stackCmd.Flags().StringVarP(&daemonStackOutputFile, "filename", "f", "stack.txt", "Specify the output file for the stack trace")
   439  
   440  	root.AddCommand(utilsCmd)
   441  	utilsCmd.AddCommand(bashcomplCmd, mangenCmd, utilsBruteForceSeedCmd, utilsCheckSigCmd,
   442  		utilsDecodeRawTxnCmd, utilsDisplayAPIPasswordCmd, utilsEncodeRawTxnCmd, utilsHastingsCmd,
   443  		utilsSigHashCmd, utilsUploadedsizeCmd, utilsVerifySeedCmd)
   444  
   445  	utilsVerifySeedCmd.Flags().StringVarP(&dictionaryLanguage, "language", "l", "english", "which dictionary you want to use")
   446  
   447  	root.AddCommand(walletCmd)
   448  	walletCmd.AddCommand(walletAddressCmd, walletAddressesCmd, walletBalanceCmd, walletBroadcastCmd, walletChangepasswordCmd,
   449  		walletInitCmd, walletInitSeedCmd, walletLoadCmd, walletLockCmd, walletSeedsCmd, walletSendCmd,
   450  		walletSignCmd, walletSweepCmd, walletTransactionsCmd, walletUnlockCmd)
   451  	walletInitCmd.Flags().BoolVarP(&initPassword, "password", "p", false, "Prompt for a custom password")
   452  	walletInitCmd.Flags().BoolVarP(&initForce, "force", "", false, "destroy the existing wallet and re-encrypt")
   453  	walletInitSeedCmd.Flags().BoolVarP(&initForce, "force", "", false, "destroy the existing wallet")
   454  	walletLoadCmd.AddCommand(walletLoad033xCmd, walletLoadSeedCmd, walletLoadSiagCmd)
   455  	walletSendCmd.AddCommand(walletSendSiacoinsCmd, walletSendSiafundsCmd)
   456  	walletSendSiacoinsCmd.Flags().BoolVarP(&walletTxnFeeIncluded, "fee-included", "", false, "Take the transaction fee out of the balance being submitted instead of the fee being additional")
   457  	walletUnlockCmd.Flags().BoolVarP(&initPassword, "password", "p", false, "Display interactive password prompt even if SIA_WALLET_PASSWORD is set")
   458  	walletBroadcastCmd.Flags().BoolVarP(&walletRawTxn, "raw", "", false, "Decode transaction as base64 instead of JSON")
   459  	walletSignCmd.Flags().BoolVarP(&walletRawTxn, "raw", "", false, "Encode signed transaction as base64 instead of JSON")
   460  	walletTransactionsCmd.Flags().Uint64Var(&walletStartHeight, "startheight", 0, " Height of the block where transaction history should begin.")
   461  	walletTransactionsCmd.Flags().Uint64Var(&walletEndHeight, "endheight", math.MaxUint64, " Height of the block where transaction history should end.")
   462  
   463  	return root
   464  }
   465  
   466  // initClient initializes client cmd flags and default values
   467  func initClient(root *cobra.Command, verbose *bool, c *client.Client, siaDir *string, alertSuppress *bool) {
   468  	root.PersistentFlags().BoolVarP(verbose, "verbose", "v", false, "Display additional information")
   469  	root.PersistentFlags().StringVarP(&c.Address, "addr", "a", "localhost:9980", "which host/port to communicate with (i.e. the host/port siad is listening on)")
   470  	root.PersistentFlags().StringVarP(&c.Password, "apipassword", "", "", "the password for the API's http authentication")
   471  	root.PersistentFlags().StringVarP(siaDir, "sia-directory", "d", "", "location of the sia directory")
   472  	root.PersistentFlags().StringVarP(&c.UserAgent, "useragent", "", "Sia-Agent", "the useragent used by siac to connect to the daemon's API")
   473  	root.PersistentFlags().BoolVarP(alertSuppress, "alert-suppress", "s", false, "suppress siac alerts")
   474  	root.PersistentFlags().StringVar(&c.Address, "portal", client.DefaultAddress, "Use a Skynet portal to complete requests, i.e. siasky.net.")
   475  }
   476  
   477  // setAPIPasswordIfNotSet sets API password if it was not set
   478  func setAPIPasswordIfNotSet() {
   479  	// Check if the API Password is set
   480  	if httpClient.Password == "" {
   481  		// No password passed in, fetch the API Password
   482  		pw, err := build.APIPassword()
   483  		if err != nil {
   484  			fmt.Println("Exiting: Error getting API Password:", err)
   485  			os.Exit(exitCodeGeneral)
   486  		}
   487  		httpClient.Password = pw
   488  	}
   489  }