2020-05-04 06:55:16 -07:00
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
2020-06-01 05:46:03 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
2020-05-04 06:55:16 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
2020-06-04 03:38:24 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
2020-05-04 06:55:16 -07:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2020-06-01 05:46:03 -07:00
|
|
|
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
|
2020-05-04 06:55:16 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
|
|
)
|
|
|
|
|
2020-07-14 11:37:14 -07:00
|
|
|
func GetValidateSignaturesCommand() *cobra.Command {
|
2020-05-04 06:55:16 -07:00
|
|
|
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,
|
2020-07-14 11:37:14 -07:00
|
|
|
RunE: makeValidateSignaturesCmd(),
|
2020-05-04 06:55:16 -07:00
|
|
|
Args: cobra.ExactArgs(1),
|
|
|
|
}
|
|
|
|
|
2020-07-14 11:37:14 -07:00
|
|
|
cmd.Flags().String(flags.FlagChainID, "", "The network chain ID")
|
2020-07-11 01:13:46 -07:00
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
|
|
|
|
return cmd
|
2020-05-04 06:55:16 -07:00
|
|
|
}
|
|
|
|
|
2020-07-14 11:37:14 -07:00
|
|
|
func makeValidateSignaturesCmd() func(cmd *cobra.Command, args []string) error {
|
2020-05-04 06:55:16 -07:00
|
|
|
return func(cmd *cobra.Command, args []string) error {
|
2020-07-14 11:37:14 -07:00
|
|
|
clientCtx := client.GetClientContextFromCmd(cmd)
|
2020-06-18 13:29:41 -07:00
|
|
|
clientCtx, txBldr, tx, err := readStdTxAndInitContexts(clientCtx, cmd, args[0])
|
2020-05-04 06:55:16 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-06-18 13:29:41 -07:00
|
|
|
stdTx := tx.(types.StdTx)
|
2020-05-04 06:55:16 -07:00
|
|
|
|
2020-06-01 05:46:03 -07:00
|
|
|
if !printAndValidateSigs(cmd, clientCtx, txBldr.ChainID(), stdTx, clientCtx.Offline) {
|
2020-05-04 06:55:16 -07:00
|
|
|
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(
|
2020-06-01 05:46:03 -07:00
|
|
|
cmd *cobra.Command, clientCtx client.Context, chainID string, stdTx types.StdTx, offline bool,
|
2020-05-04 06:55:16 -07:00
|
|
|
) bool {
|
|
|
|
cmd.Println("Signers:")
|
|
|
|
signers := stdTx.GetSigners()
|
|
|
|
|
|
|
|
for i, signer := range signers {
|
|
|
|
cmd.Printf(" %v: %v\n", i, signer.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
success := true
|
|
|
|
sigs := stdTx.Signatures
|
|
|
|
cmd.Println("")
|
|
|
|
cmd.Println("Signatures:")
|
|
|
|
|
|
|
|
if len(sigs) != len(signers) {
|
|
|
|
success = false
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, sig := range sigs {
|
|
|
|
var (
|
|
|
|
multiSigHeader string
|
|
|
|
multiSigMsg string
|
|
|
|
sigAddr = sdk.AccAddress(sig.GetPubKey().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 {
|
2020-07-14 11:37:14 -07:00
|
|
|
acc, err := types.NewAccountRetriever(clientCtx.JSONMarshaler).GetAccount(clientCtx, sigAddr)
|
2020-05-04 06:55:16 -07:00
|
|
|
if err != nil {
|
|
|
|
cmd.Printf("failed to get account: %s\n", sigAddr)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
sigBytes := types.StdSignBytes(
|
|
|
|
chainID, acc.GetAccountNumber(), acc.GetSequence(),
|
|
|
|
stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(),
|
|
|
|
)
|
|
|
|
|
|
|
|
if ok := sig.GetPubKey().VerifyBytes(sigBytes, sig.Signature); !ok {
|
|
|
|
sigSanity = "ERROR: signature invalid"
|
|
|
|
success = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
multiPK, ok := sig.GetPubKey().(multisig.PubKeyMultisigThreshold)
|
|
|
|
if ok {
|
2020-06-12 11:13:20 -07:00
|
|
|
var multiSig multisig.AminoMultisignature
|
2020-06-01 05:46:03 -07:00
|
|
|
clientCtx.Codec.MustUnmarshalBinaryBare(sig.Signature, &multiSig)
|
2020-05-04 06:55:16 -07:00
|
|
|
|
|
|
|
var b strings.Builder
|
|
|
|
b.WriteString("\n MultiSig Signatures:\n")
|
|
|
|
|
2020-07-06 10:03:45 -07:00
|
|
|
for i := 0; i < multiSig.BitArray.Count(); i++ {
|
2020-05-04 06:55:16 -07:00
|
|
|
if multiSig.BitArray.GetIndex(i) {
|
|
|
|
addr := sdk.AccAddress(multiPK.PubKeys[i].Address().Bytes())
|
|
|
|
b.WriteString(fmt.Sprintf(" %d: %s (weight: %d)\n", i, addr, 1))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
multiSigHeader = fmt.Sprintf(" [multisig threshold: %d/%d]", multiPK.K, len(multiPK.PubKeys))
|
|
|
|
multiSigMsg = b.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Printf(" %d: %s\t\t\t[%s]%s%s\n", i, sigAddr.String(), sigSanity, multiSigHeader, multiSigMsg)
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Println("")
|
|
|
|
|
|
|
|
return success
|
|
|
|
}
|
|
|
|
|
2020-06-18 13:29:41 -07:00
|
|
|
func readStdTxAndInitContexts(clientCtx client.Context, cmd *cobra.Command, filename string) (
|
|
|
|
client.Context, types.TxBuilder, sdk.Tx, error,
|
2020-05-04 06:55:16 -07:00
|
|
|
) {
|
2020-06-18 13:29:41 -07:00
|
|
|
stdTx, err := authclient.ReadTxFromFile(clientCtx, filename)
|
2020-05-04 06:55:16 -07:00
|
|
|
if err != nil {
|
2020-06-01 05:46:03 -07:00
|
|
|
return client.Context{}, types.TxBuilder{}, types.StdTx{}, err
|
2020-05-04 06:55:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
2020-07-10 02:05:35 -07:00
|
|
|
clientCtx = clientCtx.WithInput(inBuf)
|
|
|
|
|
|
|
|
txBldr, err := types.NewTxBuilderFromFlags(inBuf, cmd.Flags(), clientCtx.HomeDir)
|
|
|
|
if err != nil {
|
|
|
|
return client.Context{}, types.TxBuilder{}, types.StdTx{}, err
|
|
|
|
}
|
2020-05-04 06:55:16 -07:00
|
|
|
|
2020-06-01 05:46:03 -07:00
|
|
|
return clientCtx, txBldr, stdTx, nil
|
2020-05-04 06:55:16 -07:00
|
|
|
}
|