Skip to content

Commit

Permalink
cli: Move addressbook to separate cmd and config section
Browse files Browse the repository at this point in the history
  • Loading branch information
matevz committed Sep 6, 2022
1 parent cc29cab commit 199776b
Show file tree
Hide file tree
Showing 12 changed files with 465 additions and 214 deletions.
118 changes: 118 additions & 0 deletions cli/addressbook/address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package addressbook

import (
"fmt"

"github.com/AlecAivazis/survey/v2"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/mitchellh/mapstructure"
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/helpers"
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/types"
"github.com/spf13/cobra"
)

const (
// Kind is the account kind for the test accounts.
Kind = "address"

// ethAddressCfgKey is an optional key passed in the config files.
ethAddressCfgKey = "eth_address"
)

// ImportSource is a source of imported address account.
type ImportSource struct {
Data string
}

type addressAccountConfig struct {
EthAddress string `mapstructure:"eth_address,omitempty"`
}

type AddressAccount struct {
address types.Address
cfg *addressAccountConfig
}

func newAccount(address types.Address, cfg *addressAccountConfig) (*AddressAccount, error) {
return &AddressAccount{address: address, cfg: cfg}, nil
}

type AddressAccountFactory struct{}

func (a *AddressAccountFactory) unmarshalConfig(raw map[string]interface{}) (*addressAccountConfig, error) {
if raw == nil {
// No eth_address defined.
return &addressAccountConfig{}, nil
}

var cfg addressAccountConfig
if err := mapstructure.Decode(raw, &cfg); err != nil {
return nil, err
}
return &cfg, nil
}

func (a *AddressAccountFactory) Create(name string, cfg map[string]interface{}) (*AddressAccount, error) {
return nil, fmt.Errorf("address: create not supported")
}

func (a *AddressAccountFactory) Load(_ string, rawCfg map[string]interface{}, rawAddr string) (*AddressAccount, error) {
cfg, err := a.unmarshalConfig(rawCfg)
if err != nil {
return nil, err
}

addr, _, err := helpers.ResolveEthOrOasisAddress(rawAddr)
if err != nil {
return nil, err
}

return newAccount(*addr, cfg)
}

func (a *AddressAccountFactory) Import(cfg map[string]interface{}, src *ImportSource) (*AddressAccount, error) {
addr, ethAddr, err := helpers.ResolveEthOrOasisAddress(src.Data)
if err != nil {
return nil, err
}

ethAddrHex := ""
if ethAddr != nil {
ethAddrHex = ethAddr.Hex()
cfg[ethAddressCfgKey] = ethAddrHex
}

return newAccount(*addr, &addressAccountConfig{
EthAddress: ethAddrHex,
})
}

func (a *AddressAccountFactory) DataPrompt() survey.Prompt {
return &survey.Multiline{Message: "Address (bech32 or hex-encoded):"}
}

func (a *AddressAccountFactory) DataValidator() survey.Validator {
return func(ans interface{}) error {
if addr, _, err := helpers.ResolveEthOrOasisAddress(ans.(string)); addr == nil {
if err != nil {
return err
}
return fmt.Errorf("unsupported address format")
}
return nil
}
}

func (a *AddressAccount) Address() types.Address {
return a.address
}

func (a *AddressAccount) EthAddress() *ethCommon.Address {
if a.cfg.EthAddress != "" {
_, ethAddr, err := helpers.ResolveEthOrOasisAddress(a.cfg.EthAddress)
cobra.CheckErr(err)

return ethAddr
}
return nil
}
157 changes: 157 additions & 0 deletions cli/cmd/addressbook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package cmd

import (
"fmt"
"sort"

"github.com/AlecAivazis/survey/v2"
"github.com/oasisprotocol/oasis-sdk/cli/addressbook"
"github.com/oasisprotocol/oasis-sdk/cli/config"
"github.com/oasisprotocol/oasis-sdk/cli/table"
"github.com/spf13/cobra"
)

var (
addressBookCmd = &cobra.Command{
Use: "addressbook",
Short: "Manage addresses in the local addressbook",
}

abListCmd = &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
Short: "List addresses stored in addressbook",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
cfg := config.Global()
table := table.New()
table.SetHeader([]string{"Label", "Address"})

var output [][]string
for name, acc := range cfg.AddressBook.All {
output = append(output, []string{
name,
acc.Address,
})
}

// Sort output by name.
sort.Slice(output, func(i, j int) bool {
return output[i][0] < output[j][0]
})

table.AppendBulk(output)
table.Render()
},
}

abAddCmd = &cobra.Command{
Use: "add <name>",
Short: "Add an address to addressbook",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
cfg := config.Global()
name := args[0]

if _, exists := cfg.AddressBook.All[name]; exists {
cobra.CheckErr(fmt.Errorf("address account '%s' already exists", name))
}
if _, exists := cfg.Wallet.All[name]; exists {
cobra.CheckErr(fmt.Errorf("account '%s' already exists", name))
}

// NOTE: We only support importing into the file-based wallet for now.
af := addressbook.AddressAccountFactory{}

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

accCfg := &config.AddressAccount{
Config: map[string]interface{}{},
}
src := &addressbook.ImportSource{
Data: answers.Data,
}

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

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

abShowCmd = &cobra.Command{
Use: "show <name>",
Short: "Show address information",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
name := args[0]
addressAccount, err := config.Global().AddressBook.Load(name)
cobra.CheckErr(err)

fmt.Printf("Name: %s\n", name)
if addressAccount.EthAddress() != nil {
fmt.Printf("Ethereum address: %s\n", addressAccount.EthAddress().Hex())
}
fmt.Printf("Native address: %s\n", addressAccount.Address())
},
}

abRmCmd = &cobra.Command{
Use: "rm <name>",
Aliases: []string{"remove"},
Short: "Remove an address from addressbook",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
cfg := config.Global()
name := args[0]

// Early check for whether the wallet exists so that we don't ask for confirmation first.
if _, exists := cfg.AddressBook.All[name]; !exists {
cobra.CheckErr(fmt.Errorf("account '%s' does not exist", name))
}

err := cfg.AddressBook.Remove(name)
cobra.CheckErr(err)

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

abRenameCmd = &cobra.Command{
Use: "rename <old> <new>",
Short: "Rename address label",
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
cfg := config.Global()
oldName, newName := args[0], args[1]

err := cfg.AddressBook.Rename(oldName, newName)
cobra.CheckErr(err)

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

func init() {
addressBookCmd.AddCommand(abAddCmd)
addressBookCmd.AddCommand(abListCmd)
addressBookCmd.AddCommand(abRenameCmd)
addressBookCmd.AddCommand(abRmCmd)
addressBookCmd.AddCommand(abShowCmd)
}
6 changes: 6 additions & 0 deletions cli/cmd/common/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,11 @@ func ResolveLocalAccountOrAddress(net *configSdk.Network, address string) (*type
return &addr, nil
}

// Check, if address is the account name in the addressbook.
if acc, ok := config.Global().AddressBook.All[address]; ok {
addr := acc.GetAddress()
return &addr, nil
}

return helpers.ResolveAddress(net, address)
}
1 change: 1 addition & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func init() {
rootCmd.AddCommand(paratimeCmd)
rootCmd.AddCommand(walletCmd)
rootCmd.AddCommand(accountsCmd)
rootCmd.AddCommand(addressBookCmd)
rootCmd.AddCommand(contractsCmd)
rootCmd.AddCommand(inspect.Cmd)
}
38 changes: 18 additions & 20 deletions cli/cmd/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"github.com/oasisprotocol/oasis-sdk/cli/config"
"github.com/oasisprotocol/oasis-sdk/cli/table"
"github.com/oasisprotocol/oasis-sdk/cli/wallet"
walletAddress "github.com/oasisprotocol/oasis-sdk/cli/wallet/address"
walletFile "github.com/oasisprotocol/oasis-sdk/cli/wallet/file"
)

Expand Down Expand Up @@ -88,6 +87,9 @@ var (
err = accCfg.SetConfigFromFlags()
cobra.CheckErr(err)

if _, exists := cfg.AddressBook.All[name]; exists {
cobra.CheckErr(fmt.Errorf("address account '%s' already exists", name))
}
err = cfg.Wallet.Create(name, passphrase, accCfg)
cobra.CheckErr(err)

Expand Down Expand Up @@ -188,22 +190,22 @@ var (
if _, exists := cfg.Wallet.All[name]; exists {
cobra.CheckErr(fmt.Errorf("account '%s' already exists", name))
}
if _, exists := cfg.AddressBook.All[name]; exists {
cobra.CheckErr(fmt.Errorf("address account '%s' already exists", name))
}

// NOTE: Import is currently supported by file and address account factories.
kindFactory := map[wallet.ImportKind]wallet.Factory{}
// 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 _, fk := range []string{walletFile.Kind, walletAddress.Kind} {
f, err := wallet.Load(fk)
cobra.CheckErr(err)
for _, kind := range f.SupportedImportKinds() {
supportedKinds = append(supportedKinds, string(kind))
kindFactory[kind] = f
}
for _, kind := range af.SupportedImportKinds() {
supportedKinds = append(supportedKinds, string(kind))
}

// Ask for import kind.
var kindRaw string
err := survey.AskOne(&survey.Select{
err = survey.AskOne(&survey.Select{
Message: "Import kind:",
Options: supportedKinds,
}, &kindRaw)
Expand All @@ -213,8 +215,6 @@ var (
err = kind.UnmarshalText([]byte(kindRaw))
cobra.CheckErr(err)

af := kindFactory[kind]

// Ask for wallet configuration.
afCfg, err := af.GetConfigFromSurvey(&kind)
cobra.CheckErr(err)
Expand All @@ -223,16 +223,13 @@ var (
var answers struct {
Data string
}
questions := []*survey.Question{}
if prompt := af.DataPrompt(kind, afCfg); prompt != nil {
questions = append(questions, &survey.Question{
questions := []*survey.Question{
{
Name: "data",
Prompt: prompt,
Prompt: af.DataPrompt(kind, afCfg),
Validate: af.DataValidator(kind, afCfg),
},
)
}

err = survey.Ask(questions, &answers)
cobra.CheckErr(err)

Expand All @@ -250,6 +247,7 @@ var (
Kind: kind,
Data: answers.Data,
}

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

Expand Down
Loading

0 comments on commit 199776b

Please sign in to comment.