code.vegaprotocol.io/vega@v0.79.0/cmd/vegawallet/commands/key_annotate.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  
    24  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/cli"
    25  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/flags"
    26  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/printer"
    27  	"code.vegaprotocol.io/vega/wallet/api"
    28  	"code.vegaprotocol.io/vega/wallet/wallets"
    29  
    30  	"github.com/spf13/cobra"
    31  )
    32  
    33  var (
    34  	annotateKeyLong = cli.LongDesc(`
    35  		Add metadata to a key pair. All existing metadata is removed and replaced
    36  		by the specified new metadata.
    37  
    38  		The metadata is a list of key-value pairs. A key-value is colon-separated,
    39  		and the key-values are comma-separated.
    40  
    41  		It is possible to give a name to a key pair, that is recognised by user
    42  		interfaces, by setting the metadata "name".
    43  	`)
    44  
    45  	annotateKeyExample = cli.Examples(`
    46  		Given the following metadata to be added:
    47  			- name: my-wallet
    48  			- type: validation
    49  
    50  		# Annotate a key pair
    51  		{{.Software}} key annotate --wallet WALLET --pubkey PUBKEY --meta "name:my-wallet,type:validation"
    52  
    53  		# Remove all metadata from a key pair
    54  		{{.Software}} key annotate --wallet WALLET --pubkey PUBKEY --clear
    55  	`)
    56  )
    57  
    58  type AnnotateKeyHandler func(api.AdminAnnotateKeyParams, string) (api.AdminAnnotateKeyResult, error)
    59  
    60  func NewCmdAnnotateKey(w io.Writer, rf *RootFlags) *cobra.Command {
    61  	h := func(params api.AdminAnnotateKeyParams, passphrase string) (api.AdminAnnotateKeyResult, error) {
    62  		ctx := context.Background()
    63  
    64  		walletStore, err := wallets.InitialiseStore(rf.Home, false)
    65  		if err != nil {
    66  			return api.AdminAnnotateKeyResult{}, fmt.Errorf("couldn't initialise wallets store: %w", err)
    67  		}
    68  		defer walletStore.Close()
    69  
    70  		if _, errDetails := api.NewAdminUnlockWallet(walletStore).Handle(ctx, api.AdminUnlockWalletParams{
    71  			Wallet:     params.Wallet,
    72  			Passphrase: passphrase,
    73  		}); errDetails != nil {
    74  			return api.AdminAnnotateKeyResult{}, errors.New(errDetails.Data)
    75  		}
    76  
    77  		rawResult, errDetails := api.NewAdminAnnotateKey(walletStore).Handle(ctx, params)
    78  		if errDetails != nil {
    79  			return api.AdminAnnotateKeyResult{}, errors.New(errDetails.Data)
    80  		}
    81  		return rawResult.(api.AdminAnnotateKeyResult), nil
    82  	}
    83  
    84  	return BuildCmdAnnotateKey(w, h, rf)
    85  }
    86  
    87  func BuildCmdAnnotateKey(w io.Writer, handler AnnotateKeyHandler, rf *RootFlags) *cobra.Command {
    88  	f := AnnotateKeyFlags{}
    89  
    90  	cmd := &cobra.Command{
    91  		Use:     "annotate",
    92  		Short:   "Add metadata to a key pair",
    93  		Long:    annotateKeyLong,
    94  		Example: annotateKeyExample,
    95  		RunE: func(_ *cobra.Command, _ []string) error {
    96  			req, pass, err := f.Validate()
    97  			if err != nil {
    98  				return err
    99  			}
   100  
   101  			resp, err := handler(req, pass)
   102  			if err != nil {
   103  				return err
   104  			}
   105  
   106  			switch rf.Output {
   107  			case flags.InteractiveOutput:
   108  				PrintAnnotateKeyResponse(w, f, resp)
   109  			case flags.JSONOutput:
   110  				return printer.FprintJSON(w, resp)
   111  			}
   112  
   113  			return nil
   114  		},
   115  	}
   116  
   117  	cmd.Flags().StringVarP(&f.Wallet,
   118  		"wallet", "w",
   119  		"",
   120  		"Wallet holding the public key",
   121  	)
   122  	cmd.Flags().StringVarP(&f.PassphraseFile,
   123  		"passphrase-file", "p",
   124  		"",
   125  		"Path to the file containing the wallet's passphrase",
   126  	)
   127  	cmd.Flags().StringVarP(&f.PubKey,
   128  		"pubkey", "k",
   129  		"",
   130  		"Public key to annotate (hex-encoded)",
   131  	)
   132  	cmd.Flags().StringSliceVarP(&f.RawMetadata,
   133  		"meta", "m",
   134  		[]string{},
   135  		`A list of metadata e.g: "my-key1:my-value1,my-key2:my-value2"`,
   136  	)
   137  	cmd.Flags().BoolVar(&f.Clear,
   138  		"clear",
   139  		false,
   140  		"Clear the metadata",
   141  	)
   142  
   143  	autoCompleteWallet(cmd, rf.Home, "wallet")
   144  
   145  	return cmd
   146  }
   147  
   148  type AnnotateKeyFlags struct {
   149  	Wallet         string
   150  	PubKey         string
   151  	PassphraseFile string
   152  	Clear          bool
   153  	RawMetadata    []string
   154  }
   155  
   156  func (f *AnnotateKeyFlags) Validate() (api.AdminAnnotateKeyParams, string, error) {
   157  	if len(f.Wallet) == 0 {
   158  		return api.AdminAnnotateKeyParams{}, "", flags.MustBeSpecifiedError("wallet")
   159  	}
   160  
   161  	if len(f.PubKey) == 0 {
   162  		return api.AdminAnnotateKeyParams{}, "", flags.MustBeSpecifiedError("pubkey")
   163  	}
   164  
   165  	if len(f.RawMetadata) == 0 && !f.Clear {
   166  		return api.AdminAnnotateKeyParams{}, "", flags.OneOfFlagsMustBeSpecifiedError("meta", "clear")
   167  	}
   168  	if len(f.RawMetadata) != 0 && f.Clear {
   169  		return api.AdminAnnotateKeyParams{}, "", flags.MutuallyExclusiveError("meta", "clear")
   170  	}
   171  
   172  	metadata, err := cli.ParseMetadata(f.RawMetadata)
   173  	if err != nil {
   174  		return api.AdminAnnotateKeyParams{}, "", err
   175  	}
   176  
   177  	passphrase, err := flags.GetPassphrase(f.PassphraseFile)
   178  	if err != nil {
   179  		return api.AdminAnnotateKeyParams{}, "", err
   180  	}
   181  
   182  	return api.AdminAnnotateKeyParams{
   183  		Wallet:    f.Wallet,
   184  		PublicKey: f.PubKey,
   185  		Metadata:  metadata,
   186  	}, passphrase, nil
   187  }
   188  
   189  func PrintAnnotateKeyResponse(w io.Writer, f AnnotateKeyFlags, res api.AdminAnnotateKeyResult) {
   190  	p := printer.NewInteractivePrinter(w)
   191  	str := p.String()
   192  	defer p.Print(str)
   193  	if f.Clear {
   194  		str.CheckMark().SuccessText("Annotation cleared").NextLine()
   195  	} else {
   196  		str.CheckMark().SuccessText("Annotation succeeded").NextSection()
   197  	}
   198  	str.Text("New metadata:").NextLine()
   199  	printMeta(str, res.Metadata)
   200  }