github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/auth/client/cli/tx_multisign.go (about)

     1  package cli
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"strings"
     9  
    10  	"github.com/spf13/cobra"
    11  	"github.com/spf13/viper"
    12  
    13  	"github.com/fibonacci-chain/fbc/libs/tendermint/crypto/multisig"
    14  
    15  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context"
    16  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/flags"
    17  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    18  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/crypto/keys"
    19  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    20  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/version"
    21  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/client/utils"
    22  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types"
    23  )
    24  
    25  // GetSignCommand returns the sign command
    26  func GetMultiSignCommand(cdc *codec.Codec) *cobra.Command {
    27  	cmd := &cobra.Command{
    28  		Use:   "multisign [file] [name] [[signature]...]",
    29  		Short: "Generate multisig signatures for transactions generated offline",
    30  		Long: strings.TrimSpace(
    31  			fmt.Sprintf(`Sign transactions created with the --generate-only flag that require multisig signatures.
    32  
    33  Read signature(s) from [signature] file(s), generate a multisig signature compliant to the
    34  multisig key [name], and attach it to the transaction read from [file].
    35  
    36  Example:
    37  $ %s multisign transaction.json k1k2k3 k1sig.json k2sig.json k3sig.json
    38  
    39  If the flag --signature-only flag is on, it outputs a JSON representation
    40  of the generated signature only.
    41  
    42  The --offline flag makes sure that the client will not reach out to an external node.
    43  Thus account number or sequence number lookups will not be performed and it is
    44  recommended to set such parameters manually.
    45  `,
    46  				version.ClientName,
    47  			),
    48  		),
    49  		RunE: makeMultiSignCmd(cdc),
    50  		Args: cobra.MinimumNArgs(3),
    51  	}
    52  
    53  	cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit")
    54  	cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query a full node")
    55  	cmd.Flags().String(flagOutfile, "", "The document will be written to the given file instead of STDOUT")
    56  
    57  	// Add the flags here and return the command
    58  	return flags.PostCommands(cmd)[0]
    59  }
    60  
    61  func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) error {
    62  	return func(cmd *cobra.Command, args []string) (err error) {
    63  		stdTx, err := utils.ReadStdTxFromFile(cdc, args[0])
    64  		if err != nil {
    65  			return
    66  		}
    67  
    68  		inBuf := bufio.NewReader(cmd.InOrStdin())
    69  		kb, err := keys.NewKeyring(sdk.KeyringServiceName(),
    70  			viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), inBuf)
    71  		if err != nil {
    72  			return
    73  		}
    74  
    75  		multisigInfo, err := kb.Get(args[1])
    76  		if err != nil {
    77  			return
    78  		}
    79  		if multisigInfo.GetType() != keys.TypeMulti {
    80  			return fmt.Errorf("%q must be of type %s: %s", args[1], keys.TypeMulti, multisigInfo.GetType())
    81  		}
    82  
    83  		multisigPub := multisigInfo.GetPubKey().(multisig.PubKeyMultisigThreshold)
    84  		multisigSig := multisig.NewMultisig(len(multisigPub.PubKeys))
    85  		cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
    86  		txBldr := types.NewTxBuilderFromCLI(inBuf)
    87  
    88  		if !viper.GetBool(flagOffline) {
    89  			accnum, seq, err := types.NewAccountRetriever(cliCtx).GetAccountNumberSequence(multisigInfo.GetAddress())
    90  			if err != nil {
    91  				return err
    92  			}
    93  
    94  			txBldr = txBldr.WithAccountNumber(accnum).WithSequence(seq)
    95  		}
    96  
    97  		// read each signature and add it to the multisig if valid
    98  		for i := 2; i < len(args); i++ {
    99  			stdSig, err := readAndUnmarshalStdSignature(cdc, args[i])
   100  			if err != nil {
   101  				return err
   102  			}
   103  
   104  			// Validate each signature
   105  			sigBytes := types.StdSignBytes(
   106  				txBldr.ChainID(), txBldr.AccountNumber(), txBldr.Sequence(),
   107  				stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(),
   108  			)
   109  			if ok := stdSig.PubKey.VerifyBytes(sigBytes, stdSig.Signature); !ok {
   110  				return fmt.Errorf("couldn't verify signature")
   111  			}
   112  			if err := multisigSig.AddSignatureFromPubKey(stdSig.Signature, stdSig.PubKey, multisigPub.PubKeys); err != nil {
   113  				return err
   114  			}
   115  		}
   116  
   117  		newStdSig := types.StdSignature{Signature: cdc.MustMarshalBinaryBare(multisigSig), PubKey: multisigPub}
   118  		newTx := types.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, []types.StdSignature{newStdSig}, stdTx.GetMemo())
   119  
   120  		sigOnly := viper.GetBool(flagSigOnly)
   121  		var json []byte
   122  		switch {
   123  		case sigOnly && cliCtx.Indent:
   124  			json, err = cdc.MarshalJSONIndent(newTx.Signatures[0], "", "  ")
   125  		case sigOnly && !cliCtx.Indent:
   126  			json, err = cdc.MarshalJSON(newTx.Signatures[0])
   127  		case !sigOnly && cliCtx.Indent:
   128  			json, err = cdc.MarshalJSONIndent(newTx, "", "  ")
   129  		default:
   130  			json, err = cdc.MarshalJSON(newTx)
   131  		}
   132  		if err != nil {
   133  			return err
   134  		}
   135  
   136  		if viper.GetString(flagOutfile) == "" {
   137  			fmt.Printf("%s\n", json)
   138  			return
   139  		}
   140  
   141  		fp, err := os.OpenFile(
   142  			viper.GetString(flagOutfile), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644,
   143  		)
   144  		if err != nil {
   145  			return err
   146  		}
   147  		defer fp.Close()
   148  
   149  		fmt.Fprintf(fp, "%s\n", json)
   150  
   151  		return
   152  	}
   153  }
   154  
   155  func readAndUnmarshalStdSignature(cdc *codec.Codec, filename string) (stdSig types.StdSignature, err error) {
   156  	var bytes []byte
   157  	if bytes, err = ioutil.ReadFile(filename); err != nil {
   158  		return
   159  	}
   160  	if err = cdc.UnmarshalJSON(bytes, &stdSig); err != nil {
   161  		return
   162  	}
   163  	return
   164  }