code.vegaprotocol.io/vega@v0.79.0/cmd/vegawallet/commands/wallet_create.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package cmd
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  
    25  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/cli"
    26  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/flags"
    27  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/printer"
    28  	"code.vegaprotocol.io/vega/wallet/api"
    29  	"code.vegaprotocol.io/vega/wallet/wallet"
    30  	"code.vegaprotocol.io/vega/wallet/wallets"
    31  
    32  	"github.com/spf13/cobra"
    33  )
    34  
    35  var (
    36  	createWalletLong = cli.LongDesc(`
    37  		Create a wallet and generate the first Ed25519 key pair.
    38  
    39  		You will be asked to create a passphrase. The passphrase is used to protect
    40  		the file in which the keys are stored. This doesn't affect the key generation
    41  		process in any way.
    42  	`)
    43  
    44  	createWalletExample = cli.Examples(`
    45  		# Creating a wallet
    46  		{{.Software}} create --wallet WALLET
    47  	`)
    48  )
    49  
    50  type createdWallet struct {
    51  	Name                 string `json:"name"`
    52  	KeyDerivationVersion uint32 `json:"keyDerivationVersion"`
    53  	RecoveryPhrase       string `json:"recoveryPhrase"`
    54  	FilePath             string `json:"filePath"`
    55  }
    56  
    57  type firstPublicKey struct {
    58  	PublicKey string            `json:"publicKey"`
    59  	Algorithm wallet.Algorithm  `json:"algorithm"`
    60  	Meta      []wallet.Metadata `json:"metadata"`
    61  }
    62  
    63  type createWalletResult struct {
    64  	Wallet createdWallet  `json:"wallet"`
    65  	Key    firstPublicKey `json:"key"`
    66  }
    67  
    68  type CreateWalletHandler func(api.AdminCreateWalletParams) (createWalletResult, error)
    69  
    70  func NewCmdCreateWallet(w io.Writer, rf *RootFlags) *cobra.Command {
    71  	h := func(params api.AdminCreateWalletParams) (createWalletResult, error) {
    72  		walletStore, err := wallets.InitialiseStore(rf.Home, false)
    73  		if err != nil {
    74  			return createWalletResult{}, fmt.Errorf("couldn't initialise wallets store: %w", err)
    75  		}
    76  		defer walletStore.Close()
    77  
    78  		createWallet := api.NewAdminCreateWallet(walletStore)
    79  
    80  		rawResult, errDetails := createWallet.Handle(context.Background(), params)
    81  		if errDetails != nil {
    82  			return createWalletResult{}, errors.New(errDetails.Data)
    83  		}
    84  
    85  		result := rawResult.(api.AdminCreateWalletResult)
    86  
    87  		return createWalletResult{
    88  			Wallet: createdWallet{
    89  				Name:                 result.Wallet.Name,
    90  				KeyDerivationVersion: result.Wallet.KeyDerivationVersion,
    91  				RecoveryPhrase:       result.Wallet.RecoveryPhrase,
    92  				FilePath:             walletStore.GetWalletPath(result.Wallet.Name),
    93  			},
    94  			Key: firstPublicKey{
    95  				PublicKey: result.Key.PublicKey,
    96  				Algorithm: result.Key.Algorithm,
    97  				Meta:      result.Key.Metadata,
    98  			},
    99  		}, nil
   100  	}
   101  
   102  	return BuildCmdCreateWallet(w, h, rf)
   103  }
   104  
   105  func BuildCmdCreateWallet(w io.Writer, handler CreateWalletHandler, rf *RootFlags) *cobra.Command {
   106  	f := &CreateWalletFlags{}
   107  
   108  	cmd := &cobra.Command{
   109  		Use:     "create",
   110  		Short:   "Create a wallet",
   111  		Long:    createWalletLong,
   112  		Example: createWalletExample,
   113  		RunE: func(_ *cobra.Command, _ []string) error {
   114  			req, err := f.Validate()
   115  			if err != nil {
   116  				return err
   117  			}
   118  
   119  			resp, err := handler(req)
   120  			if err != nil {
   121  				return err
   122  			}
   123  
   124  			switch rf.Output {
   125  			case flags.InteractiveOutput:
   126  				PrintCreateWalletResponse(w, resp)
   127  			case flags.JSONOutput:
   128  				return printer.FprintJSON(w, resp)
   129  			}
   130  
   131  			return nil
   132  		},
   133  	}
   134  
   135  	cmd.Flags().StringVarP(&f.Wallet,
   136  		"wallet", "w",
   137  		"",
   138  		"The wallet where the key is generated in",
   139  	)
   140  	cmd.Flags().StringVarP(&f.PassphraseFile,
   141  		"passphrase-file", "p",
   142  		"",
   143  		"Path to the file containing the wallet's passphrase",
   144  	)
   145  
   146  	return cmd
   147  }
   148  
   149  type CreateWalletFlags struct {
   150  	Wallet         string
   151  	PassphraseFile string
   152  }
   153  
   154  func (f *CreateWalletFlags) Validate() (api.AdminCreateWalletParams, error) {
   155  	req := api.AdminCreateWalletParams{}
   156  
   157  	if len(f.Wallet) == 0 {
   158  		return api.AdminCreateWalletParams{}, flags.MustBeSpecifiedError("wallet")
   159  	}
   160  	req.Wallet = f.Wallet
   161  
   162  	passphrase, err := flags.GetConfirmedPassphrase(f.PassphraseFile)
   163  	if err != nil {
   164  		return api.AdminCreateWalletParams{}, err
   165  	}
   166  	req.Passphrase = passphrase
   167  
   168  	return req, nil
   169  }
   170  
   171  func PrintCreateWalletResponse(w io.Writer, resp createWalletResult) {
   172  	p := printer.NewInteractivePrinter(w)
   173  
   174  	str := p.String()
   175  	defer p.Print(str)
   176  
   177  	str.CheckMark().Text("Wallet ").Bold(resp.Wallet.Name).Text(" has been created at: ").SuccessText(resp.Wallet.FilePath).NextLine()
   178  	str.CheckMark().Text("First key pair has been generated for the wallet ").Bold(resp.Wallet.Name).Text(" at: ").SuccessText(resp.Wallet.FilePath).NextLine()
   179  	str.CheckMark().SuccessText("Creating wallet succeeded").NextSection()
   180  
   181  	str.Text("Wallet recovery phrase:").NextLine()
   182  	str.WarningText(resp.Wallet.RecoveryPhrase).NextLine()
   183  	str.Text("Wallet version:").NextLine()
   184  	str.WarningText(fmt.Sprintf("%d", resp.Wallet.KeyDerivationVersion)).NextLine()
   185  	str.Text("First public key:").NextLine()
   186  	str.WarningText(resp.Key.PublicKey).NextLine()
   187  	str.NextSection()
   188  
   189  	str.RedArrow().DangerText("Important").NextLine()
   190  	str.Text("Write down the ").Bold("recovery phrase").Text(" and the ").Bold("wallet's version").Text(", and store it somewhere safe and secure, now.").NextLine()
   191  	str.DangerText("The recovery phrase will not be displayed ever again, nor will you be able to retrieve it!").NextSection()
   192  
   193  	str.BlueArrow().InfoText("Run the service").NextLine()
   194  	str.Text("Now, you can run the service. See the following command:").NextSection()
   195  	str.Code(fmt.Sprintf("%s service run --help", os.Args[0])).NextLine()
   196  }