Merge PR #2995: Fully verify the signature in gaiacli tx sign
* Implement auxiliary methods/functions for just building a tx sig bytes * Undo auxiliary methods/functions * Validate signature * Remove redundant comment * Add pending log entry * Minor cleanup * Update sign cli doc * Update cli sign offline flag doc * Update printAndValidateSigs * Implement TestGaiaCLIValidateSignatures * Minor cleanup * Fix linting * Update x/auth/client/cli/sign.go Co-Authored-By: alexanderbez <alexanderbez@users.noreply.github.com> * Minor reformatting
This commit is contained in:
committed by
frog power 4000
parent
cfab1ad6ee
commit
5b42e83bc2
+54
-13
@@ -1,6 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tendermint/go-amino"
|
||||
@@ -37,10 +37,12 @@ If the flag --signature-only flag is on, it outputs a JSON representation
|
||||
of the generated signature only.
|
||||
|
||||
If the flag --validate-signatures is on, then the command would check whether all required
|
||||
signers have signed the transactions and whether the signatures were collected in the right
|
||||
order.
|
||||
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 provided, signature validation over the transaction will be not be
|
||||
performed as that will require communication with a full node.
|
||||
|
||||
The --offline flag makes sure that the client will not reach out to the local cache.
|
||||
The --offline flag makes sure that the client will not reach out to an external node.
|
||||
Thus account number or sequence number lookups will not be performed and it is
|
||||
recommended to set such parameters manually.`,
|
||||
RunE: makeSignCmd(codec),
|
||||
@@ -52,7 +54,7 @@ recommended to set such parameters manually.`,
|
||||
cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit.")
|
||||
cmd.Flags().Bool(flagValidateSigs, false, "Print the addresses that must sign the transaction, "+
|
||||
"those who have already signed it, and make sure that signatures are in the correct order.")
|
||||
cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.")
|
||||
cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query a full node.")
|
||||
cmd.Flags().String(flagOutfile, "",
|
||||
"The document will be written to the given file instead of STDOUT")
|
||||
|
||||
@@ -67,10 +69,15 @@ func makeSignCmd(cdc *amino.Codec) func(cmd *cobra.Command, args []string) error
|
||||
return
|
||||
}
|
||||
|
||||
offline := viper.GetBool(flagOffline)
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(cdc)
|
||||
txBldr := authtxb.NewTxBuilderFromCLI()
|
||||
|
||||
if viper.GetBool(flagValidateSigs) {
|
||||
if !printSignatures(stdTx) {
|
||||
if !printAndValidateSigs(cliCtx, txBldr.ChainID, stdTx, offline) {
|
||||
return fmt.Errorf("signatures validation failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -78,13 +85,11 @@ func makeSignCmd(cdc *amino.Codec) func(cmd *cobra.Command, args []string) error
|
||||
if name == "" {
|
||||
return errors.New("required flag \"name\" has not been set")
|
||||
}
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(cdc)
|
||||
txBldr := authtxb.NewTxBuilderFromCLI()
|
||||
|
||||
// if --signature-only is on, then override --append
|
||||
generateSignatureOnly := viper.GetBool(flagSigOnly)
|
||||
appendSig := viper.GetBool(flagAppend) && !generateSignatureOnly
|
||||
newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, appendSig, viper.GetBool(flagOffline))
|
||||
newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, appendSig, offline)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -107,6 +112,7 @@ func makeSignCmd(cdc *amino.Codec) func(cmd *cobra.Command, args []string) error
|
||||
json, err = cdc.MarshalJSON(newTx)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -122,35 +128,70 @@ func makeSignCmd(cdc *amino.Codec) func(cmd *cobra.Command, args []string) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer fp.Close()
|
||||
fmt.Fprintf(fp, "%s\n", json)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func printSignatures(stdTx auth.StdTx) bool {
|
||||
// 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.
|
||||
func printAndValidateSigs(
|
||||
cliCtx context.CLIContext, chainID string, stdTx auth.StdTx, offline bool,
|
||||
) bool {
|
||||
|
||||
fmt.Println("Signers:")
|
||||
|
||||
signers := stdTx.GetSigners()
|
||||
for i, signer := range signers {
|
||||
fmt.Printf(" %v: %v\n", i, signer.String())
|
||||
}
|
||||
|
||||
success := true
|
||||
sigs := stdTx.GetSignatures()
|
||||
|
||||
fmt.Println("")
|
||||
fmt.Println("Signatures:")
|
||||
success := true
|
||||
|
||||
if len(sigs) != len(signers) {
|
||||
success = false
|
||||
}
|
||||
for i, sig := range stdTx.GetSignatures() {
|
||||
|
||||
for i, sig := range sigs {
|
||||
sigAddr := sdk.AccAddress(sig.Address())
|
||||
sigSanity := "OK"
|
||||
|
||||
if i >= len(signers) || !sigAddr.Equals(signers[i]) {
|
||||
sigSanity = fmt.Sprintf("ERROR: signature %d does not match its respective signer", 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 {
|
||||
acc, err := cliCtx.GetAccount(sigAddr)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to get account: %s\n", sigAddr)
|
||||
return false
|
||||
}
|
||||
|
||||
sigBytes := auth.StdSignBytes(
|
||||
chainID, acc.GetAccountNumber(), acc.GetSequence(),
|
||||
stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(),
|
||||
)
|
||||
|
||||
if ok := sig.VerifyBytes(sigBytes, sig.Signature); !ok {
|
||||
sigSanity = "ERROR: signature invalid"
|
||||
success = false
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf(" %v: %v\t[%s]\n", i, sigAddr.String(), sigSanity)
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
return success
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user