Add account commands

This commit is contained in:
Ferran Borreguero 2021-10-04 16:19:58 +02:00
parent 8fe5c5a329
commit 74d384b6a4
8 changed files with 316 additions and 2 deletions

32
command/account.go Normal file
View file

@ -0,0 +1,32 @@
package main
import "github.com/mitchellh/cli"
type Account struct {
UI cli.Ui
}
// Help implements the cli.Command interface
func (a *Account) Help() string {
return `Usage: bor account <subcommand>
This command groups actions to interact with accounts.
List the running deployments:
$ bor account new
Display the status of a specific deployment:
$ bor account import`
}
// Synopsis implements the cli.Command interface
func (a *Account) Synopsis() string {
return "Interact with accounts"
}
// Run implements the cli.Command interface
func (a *Account) Run(args []string) int {
return cli.RunResultHelp
}

74
command/account_import.go Normal file
View file

@ -0,0 +1,74 @@
package main
import (
"fmt"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/crypto"
)
type AccountImportCommand struct {
*Meta
}
// Help implements the cli.Command interface
func (a *AccountImportCommand) Help() string {
return `Usage: bor account import
Import a private key into a new account.
Import an account:
$ bor account import key.json
` + a.Flags().Help()
}
func (a *AccountImportCommand) Flags() *flagset.Flagset {
return a.NewFlagSet("account import")
}
// Synopsis implements the cli.Command interface
func (a *AccountImportCommand) Synopsis() string {
return "Import a private key into a new account"
}
// Run implements the cli.Command interface
func (a *AccountImportCommand) Run(args []string) int {
flags := a.Flags()
if err := flags.Parse(args); err != nil {
a.UI.Error(err.Error())
return 1
}
args = flags.Args()
if len(args) != 1 {
a.UI.Error("Expected one argument")
return 1
}
key, err := crypto.LoadECDSA(args[0])
if err != nil {
a.UI.Error(fmt.Sprintf("Failed to load the private key '%s': %v", args[0], err))
return 1
}
keystore, err := a.GetKeystore()
if err != nil {
a.UI.Error(fmt.Sprintf("Failed to get keystore: %v", err))
return 1
}
password, err := a.AskPassword()
if err != nil {
a.UI.Error(err.Error())
return 1
}
acct, err := keystore.ImportECDSA(key, password)
if err != nil {
utils.Fatalf("Could not create the account: %v", err)
}
a.UI.Output(fmt.Sprintf("Account created: %s", acct.Address.String()))
return 0
}

62
command/account_list.go Normal file
View file

@ -0,0 +1,62 @@
package main
import (
"fmt"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/command/flagset"
)
type AccountListCommand struct {
*Meta
}
// Help implements the cli.Command interface
func (a *AccountListCommand) Help() string {
return `Usage: bor account list
List the local accounts.
` + a.Flags().Help()
}
func (a *AccountListCommand) Flags() *flagset.Flagset {
return a.NewFlagSet("account list")
}
// Synopsis implements the cli.Command interface
func (a *AccountListCommand) Synopsis() string {
return "List the local accounts"
}
// Run implements the cli.Command interface
func (a *AccountListCommand) Run(args []string) int {
flags := a.Flags()
if err := flags.Parse(args); err != nil {
a.UI.Error(err.Error())
return 1
}
keystore, err := a.GetKeystore()
if err != nil {
a.UI.Error(fmt.Sprintf("Failed to get keystore: %v", err))
return 1
}
a.UI.Output(formatAccounts(keystore.Accounts()))
return 0
}
func formatAccounts(accts []accounts.Account) string {
if len(accts) == 0 {
return "No accounts found"
}
rows := make([]string, len(accts)+1)
rows[0] = "Index|Address"
for i, d := range accts {
rows[i+1] = fmt.Sprintf("%d|%s",
i,
d.Address.String())
}
return formatList(rows)
}

62
command/account_new.go Normal file
View file

@ -0,0 +1,62 @@
package main
import (
"fmt"
"github.com/ethereum/go-ethereum/command/flagset"
)
type AccountNewCommand struct {
*Meta
}
// Help implements the cli.Command interface
func (a *AccountNewCommand) Help() string {
return `Usage: bor account new
Create a new local account.
` + a.Flags().Help()
}
func (a *AccountNewCommand) Flags() *flagset.Flagset {
return a.NewFlagSet("account new")
}
// Synopsis implements the cli.Command interface
func (a *AccountNewCommand) Synopsis() string {
return "Create a new local account"
}
// Run implements the cli.Command interface
func (a *AccountNewCommand) Run(args []string) int {
flags := a.Flags()
if err := flags.Parse(args); err != nil {
a.UI.Error(err.Error())
return 1
}
keystore, err := a.GetKeystore()
if err != nil {
a.UI.Error(fmt.Sprintf("Failed to get keystore: %v", err))
return 1
}
password, err := a.AskPassword()
if err != nil {
a.UI.Error(err.Error())
return 1
}
account, err := keystore.NewAccount(password)
if err != nil {
a.UI.Error(fmt.Sprintf("Failed to create new account: %v", err))
return 1
}
a.UI.Output("\nYour new key was generated")
a.UI.Output(fmt.Sprintf("Public address of the key: %s", account.Address.Hex()))
a.UI.Output(fmt.Sprintf("Path of the secret key file: %s", account.URL.Path))
return 0
}

View file

@ -4,8 +4,12 @@ import (
"fmt"
"os"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/command/flagset"
"github.com/ethereum/go-ethereum/command/server"
"github.com/ethereum/go-ethereum/node"
"github.com/mitchellh/cli"
"github.com/ryanuber/columnize"
)
func main() {
@ -35,6 +39,10 @@ func commands() map[string]cli.CommandFactory {
Writer: os.Stdout,
ErrorWriter: os.Stderr,
}
meta := &Meta{
UI: ui,
}
return map[string]cli.CommandFactory{
"server": func() (cli.Command, error) {
return &server.Command{
@ -46,5 +54,78 @@ func commands() map[string]cli.CommandFactory {
UI: ui,
}, nil
},
"account": func() (cli.Command, error) {
return &Account{
UI: ui,
}, nil
},
"account new": func() (cli.Command, error) {
return &AccountNewCommand{
Meta: meta,
}, nil
},
"account import": func() (cli.Command, error) {
return &AccountImportCommand{
Meta: meta,
}, nil
},
"account list": func() (cli.Command, error) {
return &AccountListCommand{
Meta: meta,
}, nil
},
}
}
// Meta is a helper utility for the commands
type Meta struct {
UI cli.Ui
dataDir string
keyStoreDir string
}
func (m *Meta) NewFlagSet(n string) *flagset.Flagset {
f := flagset.NewFlagSet(n)
f.StringFlag(&flagset.StringFlag{
Name: "datadir",
Value: &m.dataDir,
Usage: "Path of the data directory to store information",
})
f.StringFlag(&flagset.StringFlag{
Name: "keystore",
Value: &m.keyStoreDir,
Usage: "Path of the data directory to store information",
})
return f
}
func (m *Meta) AskPassword() (string, error) {
return m.UI.AskSecret("Your new account is locked with a password. Please give a password. Do not forget this password")
}
func (m *Meta) GetKeystore() (*keystore.KeyStore, error) {
cfg := node.DefaultConfig
cfg.DataDir = m.dataDir
cfg.KeyStoreDir = m.keyStoreDir
stack, err := node.New(&cfg)
if err != nil {
return nil, err
}
keydir := stack.KeyStoreDir()
scryptN := keystore.StandardScryptN
scryptP := keystore.StandardScryptP
keys := keystore.NewKeyStore(keydir, scryptN, scryptP)
return keys, nil
}
func formatList(in []string) string {
columnConf := columnize.DefaultConfig()
columnConf.Empty = "<none>"
return columnize.Format(in, columnConf)
}

View file

@ -12,9 +12,9 @@ type VersionCommand struct {
// Help implements the cli.Command interface
func (c *VersionCommand) Help() string {
return `Usage: ensemble version
return `Usage: bor version
Display the Ensemble version`
Display the Bor version`
}
// Synopsis implements the cli.Command interface

1
go.mod
View file

@ -58,6 +58,7 @@ require (
github.com/prometheus/tsdb v0.7.1
github.com/rjeczalik/notify v0.9.1
github.com/rs/cors v1.7.0
github.com/ryanuber/columnize v2.1.2+incompatible
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4
github.com/stretchr/testify v1.7.0

2
go.sum
View file

@ -393,6 +393,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk=
github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=