Skip to content

Commit

Permalink
feat(cmd/wallet): Add non-interactive mode to import cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
amela committed Sep 26, 2024
1 parent 0496cc4 commit 4bdca5e
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 67 deletions.
186 changes: 119 additions & 67 deletions cmd/wallet/import.go
Original file line number Diff line number Diff line change
@@ -1,80 +1,132 @@
package wallet

import (
"strings"

"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"

"github.com/oasisprotocol/cli/cmd/common"
"github.com/oasisprotocol/cli/config"
"github.com/oasisprotocol/cli/wallet"
walletFile "github.com/oasisprotocol/cli/wallet/file"
)

var importCmd = &cobra.Command{
Use: "import <name>",
Short: "Import an existing account",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
cfg := config.Global()
name := args[0]

checkAccountExists(cfg, name)

// NOTE: We only support importing into the file-based wallet for now.
af, err := wallet.Load(walletFile.Kind)
cobra.CheckErr(err)

// Ask for import kind.
var supportedKinds []string
for _, kind := range af.SupportedImportKinds() {
supportedKinds = append(supportedKinds, string(kind))
}

var kindRaw string
err = survey.AskOne(&survey.Select{
Message: "Kind:",
Options: supportedKinds,
}, &kindRaw)
cobra.CheckErr(err)

var kind wallet.ImportKind
err = kind.UnmarshalText([]byte(kindRaw))
cobra.CheckErr(err)

// Ask for wallet configuration.
afCfg, err := af.GetConfigFromSurvey(&kind)
cobra.CheckErr(err)

// Ask for import data.
var answers struct {
Data string
}
questions := []*survey.Question{
{
Name: "data",
Prompt: af.DataPrompt(kind, afCfg),
Validate: af.DataValidator(kind, afCfg),
},
}
err = survey.Ask(questions, &answers)
cobra.CheckErr(err)

// Ask for passphrase.
passphrase := common.AskNewPassphrase()

accCfg := &config.Account{
Kind: af.Kind(),
Config: afCfg,
}
src := &wallet.ImportSource{
Kind: kind,
Data: answers.Data,
}

err = cfg.Wallet.Import(name, passphrase, accCfg, src)
cobra.CheckErr(err)

err = cfg.Save()
cobra.CheckErr(err)
},
var (
algorithm string
number uint32
secret string

passphrase string
accCfg *config.Account
kind wallet.ImportKind

importCmd = &cobra.Command{
Use: "import <name> [flags]",
Short: "Import an existing account",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
cfg := config.Global()
name := args[0]

checkAccountExists(cfg, name)

// NOTE: We only support importing into the file-based wallet for now.
af, err := wallet.Load(walletFile.Kind)
cobra.CheckErr(err)

afCfg := make(map[string]interface{})

if !common.GetAnswerYes() {
// Ask for import kind.
var supportedKinds []string
for _, kind := range af.SupportedImportKinds() {
supportedKinds = append(supportedKinds, string(kind))
}

var kindRaw string
err = survey.AskOne(&survey.Select{
Message: "Kind:",
Options: supportedKinds,
}, &kindRaw)
cobra.CheckErr(err)

err = kind.UnmarshalText([]byte(kindRaw))
cobra.CheckErr(err)

// Ask for wallet configuration.
afCfg, err = af.GetConfigFromSurvey(&kind)
cobra.CheckErr(err)

// Ask for import data.
var answers struct {
Data string
}
questions := []*survey.Question{
{
Name: "data",
Prompt: af.DataPrompt(kind, afCfg),
Validate: af.DataValidator(kind, afCfg),
},
}
err = survey.Ask(questions, &answers)
cobra.CheckErr(err)

// Ask for passphrase.
passphrase = common.AskNewPassphrase()
}

if algorithm != "" {
afCfg["algorithm"] = algorithm
}

if number != 0 {
afCfg["number"] = 0
}

accCfg = &config.Account{
Kind: af.Kind(),
Config: afCfg,
}

whitespace := strings.Contains(secret, " ")
if whitespace {
kind = wallet.ImportKindMnemonic
} else {
kind = wallet.ImportKindPrivateKey
}

src := &wallet.ImportSource{
Kind: kind,
Data: secret,
}

if secret != "" {
src.Data = secret
}

err = cfg.Wallet.Import(name, passphrase, accCfg, src)
cobra.CheckErr(err)

err = cfg.Save()
cobra.CheckErr(err)
},
}
)

func init() {
importCmd.Flags().AddFlagSet(common.AnswerYesFlag)

algorithmFlag := flag.NewFlagSet("", flag.ContinueOnError)
algorithmFlag.StringVar(&algorithm, "algorithm", "", "Cryptographic algorithm to use for this account [ed25519-adr8, secp256k1-bip44, sr25519-adr8] (default \"ed25519-adr8\")")
importCmd.Flags().AddFlagSet(algorithmFlag)

numberFlag := flag.NewFlagSet("", flag.ContinueOnError)
numberFlag.Uint32Var(&number, "number", 0, "Key number to use in the key derivation scheme")
importCmd.Flags().AddFlagSet(numberFlag)

secretFlag := flag.NewFlagSet("", flag.ContinueOnError)
secretFlag.StringVar(&secret, "secret", "", "A secret key or mnemonic to use for this account")
importCmd.Flags().AddFlagSet(secretFlag)
}
18 changes: 18 additions & 0 deletions docs/wallet.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,24 @@ Let's make another Secp256k1 account and entering a hex-encoded raw private key:

![code](../examples/wallet/import-secp256k1-raw.out.static)

To override the defaults, you can pass `--algorithm`, `--number` and `--secret`
parameters. This is especially useful, if you are running the command in a
non-interactive mode:

![code](../examples/wallet/import-secp256k1-bip44-y.in.static)

:::danger Be cautious when importing accounts in non-interactive mode

Since the account's secret is provided as a command line parameter in the
non-interactive mode, make sure you **read the account's secret from a file or
an environment variable**. Otherwise, the secret may be stored and exposed in
your shell history.

Also, protecting your account with a password is currently not supported in the
non-interactive mode.

:::

## List Accounts Stored in Your Wallet {#list}

You can list all available accounts in your wallet with `wallet list`:
Expand Down
1 change: 1 addition & 0 deletions examples/wallet/import-secp256k1-bip44-y.in.static
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
oasis wallet import eugene --algorithm secp256k1-bip44 --number 0 --secret 'man ankle mystery favorite tone number ice west spare marriage control lucky life together neither' -y
1 change: 1 addition & 0 deletions examples/wallet/import-secp256k1-bip44-y.out.static
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

0 comments on commit 4bdca5e

Please sign in to comment.