github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/client/cli/validate_sigs.go (about)

     1  package cli
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/spf13/cobra"
     8  	"google.golang.org/protobuf/types/known/anypb"
     9  
    10  	txsigning "cosmossdk.io/x/tx/signing"
    11  
    12  	"github.com/cosmos/cosmos-sdk/client"
    13  	"github.com/cosmos/cosmos-sdk/client/flags"
    14  	"github.com/cosmos/cosmos-sdk/client/tx"
    15  	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
    16  	sdk "github.com/cosmos/cosmos-sdk/types"
    17  	authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
    18  	authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
    19  )
    20  
    21  func GetValidateSignaturesCommand() *cobra.Command {
    22  	cmd := &cobra.Command{
    23  		Use:   "validate-signatures [file]",
    24  		Short: "validate transactions signatures",
    25  		Long: `Print the addresses that must sign the transaction, those who have already
    26  signed it, and make sure that signatures are in the correct order.
    27  
    28  The command would check whether all required signers have signed the transactions, whether
    29  the signatures were collected in the right order, and if the signature is valid over the
    30  given transaction. If the --offline flag is also set, signature validation over the
    31  transaction will be not be performed as that will require RPC communication with a full node.
    32  `,
    33  		PreRun: preSignCmd,
    34  		RunE:   makeValidateSignaturesCmd(),
    35  		Args:   cobra.ExactArgs(1),
    36  	}
    37  
    38  	flags.AddTxFlagsToCmd(cmd)
    39  
    40  	return cmd
    41  }
    42  
    43  func makeValidateSignaturesCmd() func(cmd *cobra.Command, args []string) error {
    44  	return func(cmd *cobra.Command, args []string) error {
    45  		clientCtx, err := client.GetClientTxContext(cmd)
    46  		if err != nil {
    47  			return err
    48  		}
    49  		clientCtx, txBldr, stdTx, err := readTxAndInitContexts(clientCtx, cmd, args[0])
    50  		if err != nil {
    51  			return err
    52  		}
    53  
    54  		if !printAndValidateSigs(cmd, clientCtx, txBldr.ChainID(), stdTx, clientCtx.Offline) {
    55  			return fmt.Errorf("signatures validation failed")
    56  		}
    57  
    58  		return nil
    59  	}
    60  }
    61  
    62  // printAndValidateSigs will validate the signatures of a given transaction over its
    63  // expected signers. In addition, if offline has not been supplied, the signature is
    64  // verified over the transaction sign bytes. Returns false if the validation fails.
    65  func printAndValidateSigs(
    66  	cmd *cobra.Command, clientCtx client.Context, chainID string, tx sdk.Tx, offline bool,
    67  ) bool {
    68  	sigTx := tx.(authsigning.SigVerifiableTx)
    69  	signModeHandler := clientCtx.TxConfig.SignModeHandler()
    70  	addrCdc := clientCtx.TxConfig.SigningContext().AddressCodec()
    71  
    72  	cmd.Println("Signers:")
    73  	signers, err := sigTx.GetSigners()
    74  	if err != nil {
    75  		panic(err)
    76  	}
    77  
    78  	for i, signer := range signers {
    79  		signerStr, err := addrCdc.BytesToString(signer)
    80  		if err != nil {
    81  			panic(err)
    82  		}
    83  		cmd.Printf("  %v: %v\n", i, signerStr)
    84  	}
    85  
    86  	success := true
    87  	sigs, err := sigTx.GetSignaturesV2()
    88  	if err != nil {
    89  		panic(err)
    90  	}
    91  	cmd.Println("")
    92  	cmd.Println("Signatures:")
    93  
    94  	if len(sigs) != len(signers) {
    95  		success = false
    96  	}
    97  
    98  	for i, sig := range sigs {
    99  		var (
   100  			pubKey         = sig.PubKey
   101  			multiSigHeader string
   102  			multiSigMsg    string
   103  			sigAddr        = sdk.AccAddress(pubKey.Address())
   104  			sigSanity      = "OK"
   105  		)
   106  
   107  		if i >= len(signers) || !bytes.Equal(sigAddr, signers[i]) {
   108  			sigSanity = "ERROR: signature does not match its respective signer"
   109  			success = false
   110  		}
   111  
   112  		// validate the actual signature over the transaction bytes since we can
   113  		// reach out to a full node to query accounts.
   114  		if !offline && success {
   115  			accNum, accSeq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, sigAddr)
   116  			if err != nil {
   117  				cmd.PrintErrf("failed to get account: %s\n", sigAddr)
   118  				return false
   119  			}
   120  
   121  			signingData := authsigning.SignerData{
   122  				Address:       sigAddr.String(),
   123  				ChainID:       chainID,
   124  				AccountNumber: accNum,
   125  				Sequence:      accSeq,
   126  				PubKey:        pubKey,
   127  			}
   128  			anyPk, err := codectypes.NewAnyWithValue(pubKey)
   129  			if err != nil {
   130  				cmd.PrintErrf("failed to pack public key: %v", err)
   131  				return false
   132  			}
   133  			txSignerData := txsigning.SignerData{
   134  				ChainID:       signingData.ChainID,
   135  				AccountNumber: signingData.AccountNumber,
   136  				Sequence:      signingData.Sequence,
   137  				Address:       signingData.Address,
   138  				PubKey: &anypb.Any{
   139  					TypeUrl: anyPk.TypeUrl,
   140  					Value:   anyPk.Value,
   141  				},
   142  			}
   143  
   144  			adaptableTx, ok := tx.(authsigning.V2AdaptableTx)
   145  			if !ok {
   146  				cmd.PrintErrf("expected V2AdaptableTx, got %T", tx)
   147  				return false
   148  			}
   149  			txData := adaptableTx.GetSigningTxData()
   150  
   151  			err = authsigning.VerifySignature(cmd.Context(), pubKey, txSignerData, sig.Data, signModeHandler, txData)
   152  			if err != nil {
   153  				cmd.PrintErrf("failed to verify signature: %v", err)
   154  				return false
   155  			}
   156  		}
   157  
   158  		cmd.Printf("  %d: %s\t\t\t[%s]%s%s\n", i, sigAddr.String(), sigSanity, multiSigHeader, multiSigMsg)
   159  	}
   160  
   161  	cmd.Println("")
   162  
   163  	return success
   164  }
   165  
   166  func readTxAndInitContexts(clientCtx client.Context, cmd *cobra.Command, filename string) (client.Context, tx.Factory, sdk.Tx, error) {
   167  	stdTx, err := authclient.ReadTxFromFile(clientCtx, filename)
   168  	if err != nil {
   169  		return clientCtx, tx.Factory{}, nil, err
   170  	}
   171  
   172  	txFactory, err := tx.NewFactoryCLI(clientCtx, cmd.Flags())
   173  	if err != nil {
   174  		return clientCtx, tx.Factory{}, nil, err
   175  	}
   176  
   177  	return clientCtx, txFactory, stdTx, nil
   178  }