Skip to content

Commit

Permalink
Merge pull request #288 from oasisprotocol/amela/wallet-import-non-in…
Browse files Browse the repository at this point in the history
…teractive

feat(cmd/wallet): Add non-interactive mode to import cmd
  • Loading branch information
amela authored Oct 8, 2024
2 parents 0496cc4 + 4de9328 commit e1ff712
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 68 deletions.
184 changes: 117 additions & 67 deletions cmd/wallet/import.go
Original file line number Diff line number Diff line change
@@ -1,80 +1,130 @@
package wallet

import (
"fmt"
"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)
} else {
afCfg["algorithm"] = algorithm
afCfg["number"] = number
switch algorithm {
case wallet.AlgorithmEd25519Raw, wallet.AlgorithmSecp256k1Raw, wallet.AlgorithmSr25519Raw:
kind = wallet.ImportKindPrivateKey
default:
kind = wallet.ImportKindMnemonic
}
}

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

var src *wallet.ImportSource
if !common.GetAnswerYes() {
// 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()

src = &wallet.ImportSource{
Kind: kind,
Data: answers.Data,
}
} else {
src = &wallet.ImportSource{
Kind: kind,
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", wallet.AlgorithmEd25519Raw, fmt.Sprintf("Cryptographic algorithm to use for this account [%s]", strings.Join(walletFile.SupportedAlgorithmsForImport(nil), ", ")))
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
2 changes: 1 addition & 1 deletion examples/transaction/sign.y.out
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Method: staking.Transfer
Body:
To: oasis1qrydpazemvuwtnp3efm7vmfvg3tde044qg6cxwzx
Amount: 1.0 TEST
Nonce: 48
Nonce: 49
Fee:
Amount: 0.0 TEST
Gas limit: 1265
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 e1ff712

Please sign in to comment.