139 lines
4.0 KiB
Go
139 lines
4.0 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
|
"github.com/cosmos/cosmos-sdk/client/tx"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
|
|
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
|
)
|
|
|
|
func GetValidateSignaturesCommand() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "validate-signatures [file]",
|
|
Short: "Validate transactions signatures",
|
|
Long: `Print the addresses that must sign the transaction, those who have already
|
|
signed it, and make sure that signatures are in the correct order.
|
|
|
|
The command would check whether all required signers have signed the transactions, whether
|
|
the signatures were collected in the right order, and if the signature is valid over the
|
|
given transaction. If the --offline flag is also set, signature validation over the
|
|
transaction will be not be performed as that will require RPC communication with a full node.
|
|
`,
|
|
PreRun: preSignCmd,
|
|
RunE: makeValidateSignaturesCmd(),
|
|
Args: cobra.ExactArgs(1),
|
|
}
|
|
|
|
cmd.Flags().String(flags.FlagChainID, "", "The network chain ID")
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
func makeValidateSignaturesCmd() func(cmd *cobra.Command, args []string) error {
|
|
return func(cmd *cobra.Command, args []string) error {
|
|
clientCtx := client.GetClientContextFromCmd(cmd)
|
|
clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientCtx, txBldr, stdTx, err := readTxAndInitContexts(clientCtx, cmd, args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !printAndValidateSigs(cmd, clientCtx, txBldr.ChainID(), stdTx, clientCtx.Offline) {
|
|
return fmt.Errorf("signatures validation failed")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// printAndValidateSigs will validate the signatures of a given transaction over its
|
|
// expected signers. In addition, if offline has not been supplied, the signature is
|
|
// verified over the transaction sign bytes. Returns false if the validation fails.
|
|
func printAndValidateSigs(
|
|
cmd *cobra.Command, clientCtx client.Context, chainID string, tx sdk.Tx, offline bool,
|
|
) bool {
|
|
sigTx := tx.(authsigning.SigVerifiableTx)
|
|
signModeHandler := clientCtx.TxConfig.SignModeHandler()
|
|
|
|
cmd.Println("Signers:")
|
|
signers := sigTx.GetSigners()
|
|
for i, signer := range signers {
|
|
cmd.Printf(" %v: %v\n", i, signer.String())
|
|
}
|
|
|
|
success := true
|
|
sigs, err := sigTx.GetSignaturesV2()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
cmd.Println("")
|
|
cmd.Println("Signatures:")
|
|
|
|
if len(sigs) != len(signers) {
|
|
success = false
|
|
}
|
|
|
|
for i, sig := range sigs {
|
|
var (
|
|
pubKey = sig.PubKey
|
|
multiSigHeader string
|
|
multiSigMsg string
|
|
sigAddr = sdk.AccAddress(pubKey.Address())
|
|
sigSanity = "OK"
|
|
)
|
|
|
|
if i >= len(signers) || !sigAddr.Equals(signers[i]) {
|
|
sigSanity = "ERROR: signature does not match its respective signer"
|
|
success = false
|
|
}
|
|
|
|
// Validate the actual signature over the transaction bytes since we can
|
|
// reach out to a full node to query accounts.
|
|
if !offline && success {
|
|
accNum, accSeq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, sigAddr)
|
|
if err != nil {
|
|
cmd.Printf("failed to get account: %s\n", sigAddr)
|
|
return false
|
|
}
|
|
|
|
signingData := authsigning.SignerData{
|
|
ChainID: chainID,
|
|
AccountNumber: accNum,
|
|
Sequence: accSeq,
|
|
}
|
|
err = authsigning.VerifySignature(pubKey, signingData, sig.Data, signModeHandler, sigTx)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
}
|
|
|
|
cmd.Printf(" %d: %s\t\t\t[%s]%s%s\n", i, sigAddr.String(), sigSanity, multiSigHeader, multiSigMsg)
|
|
}
|
|
|
|
cmd.Println("")
|
|
|
|
return success
|
|
}
|
|
|
|
func readTxAndInitContexts(clientCtx client.Context, cmd *cobra.Command, filename string) (client.Context, tx.Factory, sdk.Tx, error) {
|
|
stdTx, err := authclient.ReadTxFromFile(clientCtx, filename)
|
|
if err != nil {
|
|
return clientCtx, tx.Factory{}, nil, err
|
|
}
|
|
|
|
txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags())
|
|
|
|
return clientCtx, txFactory, stdTx, nil
|
|
}
|