-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cli: Move addressbook to separate cmd and config section
- Loading branch information
Showing
12 changed files
with
465 additions
and
214 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.