code.vegaprotocol.io/vega@v0.79.0/cmd/vegawallet/commands/wallet_import.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 "strconv" 25 "strings" 26 27 "code.vegaprotocol.io/vega/cmd/vegawallet/commands/cli" 28 "code.vegaprotocol.io/vega/cmd/vegawallet/commands/flags" 29 "code.vegaprotocol.io/vega/cmd/vegawallet/commands/printer" 30 vgfs "code.vegaprotocol.io/vega/libs/fs" 31 vgzap "code.vegaprotocol.io/vega/libs/zap" 32 "code.vegaprotocol.io/vega/wallet/api" 33 "code.vegaprotocol.io/vega/wallet/wallet" 34 "code.vegaprotocol.io/vega/wallet/wallets" 35 36 "github.com/spf13/cobra" 37 ) 38 39 var ( 40 importWalletLong = cli.LongDesc(` 41 Import a wallet using the recovery phrase and generate the first Ed25519 key pair. 42 43 You will be asked to create a passphrase. The passphrase is used to protect 44 the file in which the keys are stored. Hence, it can be different from the 45 original passphrase, used during the wallet creation. This doesn't affect the 46 key generation process in any way. 47 `) 48 49 importWalletExample = cli.Examples(` 50 # Import a wallet using the recovery phrase 51 {{.Software}} import --wallet WALLET --recovery-phrase-file PATH_TO_RECOVERY_PHRASE 52 53 # Import an older version of the wallet using the recovery phrase 54 {{.Software}} import --wallet WALLET --recovery-phrase-file PATH_TO_RECOVERY_PHRASE --version VERSION 55 `) 56 ) 57 58 type ImportWalletHandler func(api.AdminImportWalletParams) (importWalletResult, error) 59 60 type importedWallet struct { 61 Name string `json:"name"` 62 KeyDerivationVersion uint32 `json:"keyDerivationVersion"` 63 FilePath string `json:"filePath"` 64 } 65 66 type importWalletResult struct { 67 Wallet importedWallet `json:"wallet"` 68 Key firstPublicKey `json:"key"` 69 } 70 71 func NewCmdImportWallet(w io.Writer, rf *RootFlags) *cobra.Command { 72 h := func(params api.AdminImportWalletParams) (importWalletResult, error) { 73 walletStore, err := wallets.InitialiseStore(rf.Home, false) 74 if err != nil { 75 return importWalletResult{}, fmt.Errorf("couldn't initialise wallets store: %w", err) 76 } 77 defer walletStore.Close() 78 79 importWallet := api.NewAdminImportWallet(walletStore) 80 81 rawResult, errDetails := importWallet.Handle(context.Background(), params) 82 if errDetails != nil { 83 return importWalletResult{}, errors.New(errDetails.Data) 84 } 85 86 result := rawResult.(api.AdminImportWalletResult) 87 return importWalletResult{ 88 Wallet: importedWallet{ 89 Name: result.Wallet.Name, 90 KeyDerivationVersion: result.Wallet.KeyDerivationVersion, 91 FilePath: walletStore.GetWalletPath(result.Wallet.Name), 92 }, 93 Key: firstPublicKey{ 94 PublicKey: result.Key.PublicKey, 95 Algorithm: result.Key.Algorithm, 96 Meta: result.Key.Metadata, 97 }, 98 }, nil 99 } 100 101 return BuildCmdImportWallet(w, h, rf) 102 } 103 104 func BuildCmdImportWallet(w io.Writer, handler ImportWalletHandler, rf *RootFlags) *cobra.Command { 105 f := &ImportWalletFlags{} 106 107 cmd := &cobra.Command{ 108 Use: "import", 109 Short: "Import a wallet using the recovery phrase", 110 Long: importWalletLong, 111 Example: importWalletExample, 112 RunE: func(_ *cobra.Command, _ []string) error { 113 req, err := f.Validate() 114 if err != nil { 115 return err 116 } 117 118 resp, err := handler(req) 119 if err != nil { 120 return err 121 } 122 123 switch rf.Output { 124 case flags.InteractiveOutput: 125 PrintImportWalletResponse(w, resp) 126 case flags.JSONOutput: 127 return printer.FprintJSON(w, resp) 128 } 129 130 return nil 131 }, 132 } 133 134 cmd.Flags().StringVarP(&f.Wallet, 135 "wallet", "w", 136 "", 137 "Name of the wallet to use", 138 ) 139 cmd.Flags().StringVarP(&f.PassphraseFile, 140 "passphrase-file", "p", 141 "", 142 "Path to the file containing the passphrase to access the wallet", 143 ) 144 cmd.Flags().StringVar(&f.RecoveryPhraseFile, 145 "recovery-phrase-file", 146 "", 147 "Path to the file containing the recovery phrase of the wallet", 148 ) 149 cmd.Flags().Uint32Var(&f.Version, 150 "version", 151 wallet.LatestVersion, 152 fmt.Sprintf("Version of the wallet to import: %v", wallet.SupportedKeyDerivationVersions), 153 ) 154 155 _ = cmd.RegisterFlagCompletionFunc("version", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { 156 vs := make([]string, 0, len(wallet.SupportedKeyDerivationVersions)) 157 for i, v := range wallet.SupportedKeyDerivationVersions { 158 vs[i] = strconv.FormatUint(uint64(v), 10) //nolint:gomnd 159 } 160 return vgzap.SupportedLogLevels, cobra.ShellCompDirectiveDefault 161 }) 162 163 return cmd 164 } 165 166 type ImportWalletFlags struct { 167 Wallet string 168 PassphraseFile string 169 RecoveryPhraseFile string 170 Version uint32 171 } 172 173 func (f *ImportWalletFlags) Validate() (api.AdminImportWalletParams, error) { 174 params := api.AdminImportWalletParams{ 175 KeyDerivationVersion: f.Version, 176 } 177 178 if len(f.Wallet) == 0 { 179 return api.AdminImportWalletParams{}, flags.MustBeSpecifiedError("wallet") 180 } 181 params.Wallet = f.Wallet 182 183 if len(f.RecoveryPhraseFile) == 0 { 184 return api.AdminImportWalletParams{}, flags.MustBeSpecifiedError("recovery-phrase-file") 185 } 186 recoveryPhrase, err := vgfs.ReadFile(f.RecoveryPhraseFile) 187 if err != nil { 188 return api.AdminImportWalletParams{}, fmt.Errorf("couldn't read recovery phrase file: %w", err) 189 } 190 params.RecoveryPhrase = strings.Trim(string(recoveryPhrase), "\n") 191 192 passphrase, err := flags.GetConfirmedPassphrase(f.PassphraseFile) 193 if err != nil { 194 return api.AdminImportWalletParams{}, err 195 } 196 params.Passphrase = passphrase 197 198 return params, nil 199 } 200 201 func PrintImportWalletResponse(w io.Writer, resp importWalletResult) { 202 p := printer.NewInteractivePrinter(w) 203 204 str := p.String() 205 defer p.Print(str) 206 207 str.CheckMark().Text("Wallet ").Bold(resp.Wallet.Name).Text(" has been imported at: ").SuccessText(resp.Wallet.FilePath).NextLine() 208 str.CheckMark().Text("First key pair has been generated for the wallet ").Bold(resp.Wallet.Name).Text(" at: ").SuccessText(resp.Wallet.FilePath).NextLine() 209 str.CheckMark().SuccessText("Importing the wallet succeeded").NextSection() 210 211 str.Text("First public key:").NextLine() 212 str.WarningText(resp.Key.PublicKey).NextLine() 213 str.NextSection() 214 215 str.BlueArrow().InfoText("Run the service").NextLine() 216 str.Text("Now, you can run the service. See the following command:").NextSection() 217 str.Code(fmt.Sprintf("%s service run --help", os.Args[0])).NextLine() 218 }