123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- package main
- import (
- "encoding/hex"
- "encoding/json"
- "errors"
- "fmt"
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/accounts/pluggable"
- "github.com/ethereum/go-ethereum/cmd/utils"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/node"
- "github.com/ethereum/go-ethereum/plugin"
- "gopkg.in/urfave/cli.v1"
- )
- var (
- quorumAccountPluginCommands = cli.Command{
- Name: "plugin",
- Usage: "Manage 'account' plugin accounts",
- Description: `
- geth account plugin
-
- Quorum supports alternate account management methods through the use of 'account' plugins.
-
- See docs.goquorum.com for more info.
- `,
- Subcommands: []cli.Command{
- {
- Name: "list",
- Usage: "Print summary of existing 'account' plugin accounts",
- Action: utils.MigrateFlags(listPluginAccountsCLIAction),
- Flags: []cli.Flag{
- utils.PluginSettingsFlag, // flag is used implicitly by makeConfigNode()
- utils.PluginLocalVerifyFlag,
- utils.PluginPublicKeyFlag,
- utils.PluginSkipVerifyFlag,
- },
- Description: `
- geth account plugin list
- Print a short summary of all accounts for the given plugin settings`,
- },
- {
- Name: "new",
- Usage: "Create a new account using an 'account' plugin",
- Action: utils.MigrateFlags(createPluginAccountCLIAction),
- Flags: []cli.Flag{
- utils.PluginSettingsFlag,
- utils.PluginLocalVerifyFlag,
- utils.PluginPublicKeyFlag,
- utils.PluginSkipVerifyFlag,
- utils.AccountPluginNewAccountConfigFlag,
- },
- Description: fmt.Sprintf(`
- geth account plugin new
- Creates a new account using an 'account' plugin and prints the address.
- --%v and --%v flags are required.
- Each 'account' plugin will have different requirements for the value of --%v.
- For more info see the documentation for the particular 'account' plugin being used.
- `, utils.PluginSettingsFlag.Name, utils.AccountPluginNewAccountConfigFlag.Name, utils.AccountPluginNewAccountConfigFlag.Name),
- },
- {
- Name: "import",
- Usage: "Import a private key into a new account using an 'account' plugin",
- Action: utils.MigrateFlags(importPluginAccountCLIAction),
- Flags: []cli.Flag{
- utils.PluginSettingsFlag,
- utils.PluginLocalVerifyFlag,
- utils.PluginPublicKeyFlag,
- utils.PluginSkipVerifyFlag,
- utils.AccountPluginNewAccountConfigFlag,
- },
- ArgsUsage: "<keyFile>",
- Description: `
- geth account plugin import <keyfile>
- Imports an unencrypted private key from <keyfile> and creates a new account using an 'account' plugin.
- Prints the address.
- The keyfile must contain an unencrypted private key in hexadecimal format.
- --%v and --%v flags are required.
-
- Note:
- Before using this import mechanism to transfer accounts that are already 'account' plugin-managed between nodes, consult
- the documentation for the particular 'account' plugin being used as it may support alternate methods for transferring.
- `,
- },
- },
- }
- // supportedPlugins is the list of supported plugins for the account subcommand
- supportedPlugins = []plugin.PluginInterfaceName{plugin.AccountPluginInterfaceName}
- invalidPluginFlagsErr = fmt.Errorf("--%v and --%v flags must be set", utils.PluginSettingsFlag.Name, utils.AccountPluginNewAccountConfigFlag.Name)
- // makeConfigNodeDelegate is a wrapper for the makeConfigNode function.
- // It can be replaced with a stub for testing.
- makeConfigNodeDelegate configNodeMaker = standardConfigNodeMaker{}
- )
- func listPluginAccountsCLIAction(ctx *cli.Context) error {
- accts, err := listPluginAccounts(ctx)
- if err != nil {
- utils.Fatalf("%v", err)
- }
- var index int
- for _, acct := range accts {
- fmt.Printf("Account #%d: {%x} %s\n", index, acct.Address, &acct.URL)
- index++
- }
- return nil
- }
- func listPluginAccounts(ctx *cli.Context) ([]accounts.Account, error) {
- if !ctx.IsSet(utils.PluginSettingsFlag.Name) {
- return []accounts.Account{}, fmt.Errorf("--%v required", utils.PluginSettingsFlag.Name)
- }
- p, err := setupAccountPluginForCLI(ctx)
- if err != nil {
- return []accounts.Account{}, err
- }
- defer func() {
- if err := p.teardown(); err != nil {
- log.Error("error tearing down account plugin", "err", err)
- }
- }()
- return p.accounts(), nil
- }
- func createPluginAccountCLIAction(ctx *cli.Context) error {
- account, err := createPluginAccount(ctx)
- if err != nil {
- utils.Fatalf("unable to create plugin-backed account: %v", err)
- }
- writePluginAccountToStdOut(account)
- return nil
- }
- func createPluginAccount(ctx *cli.Context) (accounts.Account, error) {
- if !ctx.IsSet(utils.PluginSettingsFlag.Name) || !ctx.IsSet(utils.AccountPluginNewAccountConfigFlag.Name) {
- return accounts.Account{}, invalidPluginFlagsErr
- }
- newAcctCfg, err := getNewAccountConfigFromCLI(ctx)
- if err != nil {
- return accounts.Account{}, err
- }
- p, err := setupAccountPluginForCLI(ctx)
- if err != nil {
- return accounts.Account{}, err
- }
- defer func() {
- if err := p.teardown(); err != nil {
- log.Error("error tearing down account plugin", "err", err)
- }
- }()
- return p.NewAccount(newAcctCfg)
- }
- func importPluginAccountCLIAction(ctx *cli.Context) error {
- account, err := importPluginAccount(ctx)
- if err != nil {
- utils.Fatalf("unable to import key and create plugin-backed account: %v", err)
- }
- writePluginAccountToStdOut(account)
- return nil
- }
- func importPluginAccount(ctx *cli.Context) (accounts.Account, error) {
- keyfile := ctx.Args().First()
- if len(keyfile) == 0 {
- return accounts.Account{}, errors.New("keyfile must be given as argument")
- }
- key, err := crypto.LoadECDSA(keyfile)
- if err != nil {
- return accounts.Account{}, fmt.Errorf("Failed to load the private key: %v", err)
- }
- keyBytes := crypto.FromECDSA(key)
- keyHex := hex.EncodeToString(keyBytes)
- if !ctx.IsSet(utils.PluginSettingsFlag.Name) || !ctx.IsSet(utils.AccountPluginNewAccountConfigFlag.Name) {
- return accounts.Account{}, invalidPluginFlagsErr
- }
- newAcctCfg, err := getNewAccountConfigFromCLI(ctx)
- if err != nil {
- return accounts.Account{}, err
- }
- p, err := setupAccountPluginForCLI(ctx)
- if err != nil {
- return accounts.Account{}, err
- }
- defer func() {
- if err := p.teardown(); err != nil {
- log.Error("error tearing down account plugin", "err", err)
- }
- }()
- return p.ImportRawKey(keyHex, newAcctCfg)
- }
- func getNewAccountConfigFromCLI(ctx *cli.Context) (map[string]interface{}, error) {
- data := ctx.String(utils.AccountPluginNewAccountConfigFlag.Name)
- conf, err := plugin.ReadMultiFormatConfig(data)
- if err != nil {
- return nil, fmt.Errorf("invalid account creation config provided: %v", err)
- }
- // plugin backend expects config to be json map
- confMap := new(map[string]interface{})
- if err := json.Unmarshal(conf, confMap); err != nil {
- return nil, fmt.Errorf("invalid account creation config provided: %v", err)
- }
- return *confMap, nil
- }
- type accountPlugin struct {
- pluggable.AccountCreator
- am *accounts.Manager
- pm *plugin.PluginManager
- }
- func (c *accountPlugin) teardown() error {
- return c.pm.Stop()
- }
- func (c *accountPlugin) accounts() []accounts.Account {
- b := c.am.Backends(pluggable.BackendType)
- if b == nil {
- return []accounts.Account{}
- }
- var accts []accounts.Account
- for _, wallet := range b[0].Wallets() {
- accts = append(accts, wallet.Accounts()...)
- }
- return accts
- }
- // startPluginManagerForAccountCLI is a helper func for use with the account plugin CLI.
- // It creates and starts a new PluginManager with the provided CLI flags.
- // The caller should call teardown on the returned accountPlugin to stop the plugin after use.
- // The returned accountPlugin provides several methods necessary for the account plugin CLI, abstracting the underlying plugin/account types.
- //
- // This func should not be used for anything other than the account CLI.
- // The account plugin, if present, is registered with the existing pluggable.Backend in the stack's AccountManager.
- // This allows the AccountManager to use the account plugin even though the PluginManager is not registered with the stack.
- // Instead of registering a plugin manager with the stack this is manually creating a plugin manager.
- // This means that the plugin manager can be started without having to start the whole stack (P2P client, IPC interface, ...).
- // The purpose of this is to help prevent issues/conflicts if an existing node is already running on this host.
- //
- func setupAccountPluginForCLI(ctx *cli.Context) (*accountPlugin, error) {
- stack, cfg := makeConfigNodeDelegate.makeConfigNode(ctx)
- if cfg.Node.Plugins == nil {
- return nil, errors.New("no plugin config provided")
- }
- if err := cfg.Node.Plugins.CheckSettingsAreSupported(supportedPlugins); err != nil {
- return nil, err
- }
- if err := cfg.Node.ResolvePluginBaseDir(); err != nil {
- return nil, fmt.Errorf("unable to resolve plugin base dir due to %s", err)
- }
- pm, err := plugin.NewPluginManager(
- cfg.Node.UserIdent,
- cfg.Node.Plugins,
- ctx.Bool(utils.PluginSkipVerifyFlag.Name),
- ctx.Bool(utils.PluginLocalVerifyFlag.Name),
- ctx.String(utils.PluginPublicKeyFlag.Name),
- )
- if err != nil {
- return nil, fmt.Errorf("unable to create plugin manager: %v", err)
- }
- if err := pm.Start(); err != nil {
- return nil, fmt.Errorf("unable to start plugin manager: %v", err)
- }
- b := stack.AccountManager().Backends(pluggable.BackendType)[0].(*pluggable.Backend)
- if err := pm.AddAccountPluginToBackend(b); err != nil {
- return nil, fmt.Errorf("unable to load pluggable account backend: %v", err)
- }
- return &accountPlugin{
- AccountCreator: b,
- am: stack.AccountManager(),
- pm: pm,
- }, nil
- }
- func writePluginAccountToStdOut(account accounts.Account) {
- fmt.Printf("\nYour new plugin-backed account was generated\n\n")
- fmt.Printf("Public address of the account: %s\n", account.Address.Hex())
- fmt.Printf("Account URL: %s\n\n", account.URL.Path)
- fmt.Printf("- You can share your public address with anyone. Others need it to interact with you.\n")
- fmt.Printf("- You must NEVER share the secret key with anyone! The key controls access to your funds!\n")
- fmt.Printf("- Consider BACKING UP your account! The specifics of backing up will depend on the plugin backend being used.\n")
- fmt.Printf("- The plugin backend may require you to REMEMBER part/all of the new account config to retrieve the key in the future!\n See the plugin specific documentation for more info.\n\n")
- fmt.Printf("- See the documentation for the plugin being used for more info.\n\n")
- }
- type configNodeMaker interface {
- makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig)
- }
- // standardConfigNodeMaker is a wrapper around the makeConfigNode function to enable mocking in testing
- type standardConfigNodeMaker struct{}
- func (f standardConfigNodeMaker) makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
- return makeConfigNode(ctx)
- }
|