code.vegaprotocol.io/vega@v0.79.0/cmd/vegawallet/commands/message_sign.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  	"encoding/base64"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  
    26  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/cli"
    27  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/flags"
    28  	"code.vegaprotocol.io/vega/cmd/vegawallet/commands/printer"
    29  	"code.vegaprotocol.io/vega/wallet/api"
    30  	"code.vegaprotocol.io/vega/wallet/wallets"
    31  
    32  	"github.com/spf13/cobra"
    33  )
    34  
    35  var (
    36  	signMessageLong = cli.LongDesc(`
    37  		Sign any message using a Vega wallet key.
    38  	`)
    39  
    40  	signMessageExample = cli.Examples(`
    41  		# Sign a message
    42  		{{.Software}} message sign --message MESSAGE --wallet WALLET --pubkey PUBKEY
    43  	`)
    44  )
    45  
    46  type SignMessageHandler func(api.AdminSignMessageParams, string) (api.AdminSignMessageResult, error)
    47  
    48  func NewCmdSignMessage(w io.Writer, rf *RootFlags) *cobra.Command {
    49  	h := func(params api.AdminSignMessageParams, passphrase string) (api.AdminSignMessageResult, error) {
    50  		ctx := context.Background()
    51  
    52  		walletStore, err := wallets.InitialiseStore(rf.Home, false)
    53  		if err != nil {
    54  			return api.AdminSignMessageResult{}, fmt.Errorf("couldn't initialise wallets store: %w", err)
    55  		}
    56  		defer walletStore.Close()
    57  
    58  		if _, errDetails := api.NewAdminUnlockWallet(walletStore).Handle(ctx, api.AdminUnlockWalletParams{
    59  			Wallet:     params.Wallet,
    60  			Passphrase: passphrase,
    61  		}); errDetails != nil {
    62  			return api.AdminSignMessageResult{}, errors.New(errDetails.Data)
    63  		}
    64  
    65  		rawResult, errorDetails := api.NewAdminSignMessage(walletStore).Handle(ctx, params)
    66  		if errorDetails != nil {
    67  			return api.AdminSignMessageResult{}, errors.New(errorDetails.Data)
    68  		}
    69  		return rawResult.(api.AdminSignMessageResult), nil
    70  	}
    71  	return BuildCmdSignMessage(w, h, rf)
    72  }
    73  
    74  func BuildCmdSignMessage(w io.Writer, handler SignMessageHandler, rf *RootFlags) *cobra.Command {
    75  	f := &SignMessageFlags{}
    76  
    77  	cmd := &cobra.Command{
    78  		Use:     "sign",
    79  		Short:   "Sign a message using a Vega wallet key",
    80  		Long:    signMessageLong,
    81  		Example: signMessageExample,
    82  		RunE: func(_ *cobra.Command, _ []string) error {
    83  			req, pass, err := f.Validate()
    84  			if err != nil {
    85  				return err
    86  			}
    87  
    88  			resp, err := handler(req, pass)
    89  			if err != nil {
    90  				return err
    91  			}
    92  
    93  			switch rf.Output {
    94  			case flags.InteractiveOutput:
    95  				PrintSignMessageResponse(w, resp)
    96  			case flags.JSONOutput:
    97  				return printer.FprintJSON(w, struct {
    98  					Signature string `json:"signature"`
    99  				}{
   100  					Signature: resp.EncodedSignature,
   101  				})
   102  			}
   103  
   104  			return nil
   105  		},
   106  	}
   107  
   108  	cmd.Flags().StringVarP(&f.Wallet,
   109  		"wallet", "w",
   110  		"",
   111  		"Wallet holding the public key",
   112  	)
   113  	cmd.Flags().StringVarP(&f.PubKey,
   114  		"pubkey", "k",
   115  		"",
   116  		"Public key to use to the sign the message (hex-encoded)",
   117  	)
   118  	cmd.Flags().StringVarP(&f.Message,
   119  		"message", "m",
   120  		"",
   121  		"Message to be verified (base64-encoded)",
   122  	)
   123  	cmd.Flags().StringVarP(&f.PassphraseFile,
   124  		"passphrase-file", "p",
   125  		"",
   126  		"Path to the file containing the wallet's passphrase",
   127  	)
   128  
   129  	autoCompleteWallet(cmd, rf.Home, "wallet")
   130  
   131  	return cmd
   132  }
   133  
   134  type SignMessageFlags struct {
   135  	Wallet         string
   136  	PubKey         string
   137  	Message        string
   138  	PassphraseFile string
   139  }
   140  
   141  func (f *SignMessageFlags) Validate() (api.AdminSignMessageParams, string, error) {
   142  	req := api.AdminSignMessageParams{}
   143  
   144  	if len(f.Wallet) == 0 {
   145  		return api.AdminSignMessageParams{}, "", flags.MustBeSpecifiedError("wallet")
   146  	}
   147  	req.Wallet = f.Wallet
   148  
   149  	if len(f.PubKey) == 0 {
   150  		return api.AdminSignMessageParams{}, "", flags.MustBeSpecifiedError("pubkey")
   151  	}
   152  	req.PublicKey = f.PubKey
   153  
   154  	if len(f.Message) == 0 {
   155  		return api.AdminSignMessageParams{}, "", flags.MustBeSpecifiedError("message")
   156  	}
   157  	_, err := base64.StdEncoding.DecodeString(f.Message)
   158  	if err != nil {
   159  		return api.AdminSignMessageParams{}, "", flags.MustBase64EncodedError("message")
   160  	}
   161  	req.EncodedMessage = f.Message
   162  
   163  	passphrase, err := flags.GetPassphrase(f.PassphraseFile)
   164  	if err != nil {
   165  		return api.AdminSignMessageParams{}, "", err
   166  	}
   167  
   168  	return req, passphrase, nil
   169  }
   170  
   171  func PrintSignMessageResponse(w io.Writer, req api.AdminSignMessageResult) {
   172  	p := printer.NewInteractivePrinter(w)
   173  
   174  	str := p.String()
   175  	defer p.Print(str)
   176  
   177  	str.CheckMark().SuccessText("Message signature successful").NextSection()
   178  	str.Text("Signature (base64-encoded):").NextLine().WarningText(req.EncodedSignature).NextSection()
   179  
   180  	str.BlueArrow().InfoText("Sign a message").NextLine()
   181  	str.Text("To verify a message, see the following command:").NextSection()
   182  	str.Code(fmt.Sprintf("%s verify --help", os.Args[0])).NextLine()
   183  }