Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: let sign-batch read multiple files #13454

Merged
merged 7 commits into from
Oct 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/auth) [#13210](https://github.com/cosmos/cosmos-sdk/pull/13210) Add `Query/AccountInfo` endpoint for simplified access to basic account info.
* (cli) [#13147](https://github.com/cosmos/cosmos-sdk/pull/13147) Add the `--append` flag to the `sign-batch` CLI cmd to combine the messages and sign those txs which are created with `--generate-only`.
* (x/consensus) [#12905](https://github.com/cosmos/cosmos-sdk/pull/12905) Create a new `x/consensus` module that is now responsible for maintaining Tendermint consensus parameters instead of `x/param`. Legacy types remain in order to facilitate parameter migration from the deprecated `x/params`. App developers should ensure that they execute `baseapp.MigrateParams` during their chain upgrade. These legacy types will be removed in a future release.
* (cli) [#13454](https://github.com/cosmos/cosmos-sdk/pull/13454) `sign-batch` CLI can now read multiple transaction files.

### Improvements

Expand Down
20 changes: 5 additions & 15 deletions x/auth/client/cli/tx_multisign.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type BroadcastReq struct {
Mode string `json:"mode" yaml:"mode"`
}

// GetSignCommand returns the sign command
// GetMultiSignCommand returns the multi-sign command
func GetMultiSignCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "multi-sign [file] [name] [[signature]...]",
Expand Down Expand Up @@ -258,21 +258,11 @@ func makeBatchMultisignCmd() func(cmd *cobra.Command, args []string) error {
txFactory = txFactory.WithSignMode(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
}

infile := os.Stdin
if args[0] != "-" {
infile, err = os.Open(args[0])
defer func() {
err2 := infile.Close()
if err == nil {
err = err2
}
}()

if err != nil {
return fmt.Errorf("couldn't open %s: %w", args[0], err)
}
// reads tx from args[0]
scanner, err := authclient.ReadTxsFromInput(txCfg, args[0])
if err != nil {
return err
}
scanner := authclient.NewBatchScanner(txCfg, infile)

k, err := getMultisigRecord(clientCtx, args[1])
if err != nil {
Expand Down
103 changes: 52 additions & 51 deletions x/auth/client/cli/tx_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ const (
// GetSignBatchCommand returns the transaction sign-batch command.
func GetSignBatchCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "sign-batch [file]",
Use: "sign-batch [file] ([file2]...)",
Short: "Sign transaction batch files",
Long: `Sign batch files of transactions generated with --generate-only.
The command processes list of transactions from file (one StdTx each line), generate
signed transactions or signatures and print their JSON encoding, delimited by '\n'.
The command processes list of transactions from a file (one StdTx each line), or multiple files.
Then generates signed transactions or signatures and print their JSON encoding, delimited by '\n'.
As the signatures are generated, the command updates the account and sequence number accordingly.

If the --signature-only flag is set, it will output the signature parts only.
Expand All @@ -50,7 +50,7 @@ account key. It implies --signature-only.
`,
PreRun: preSignCmd,
RunE: makeSignBatchCmd(),
Args: cobra.ExactArgs(1),
Args: cobra.MinimumNArgs(1),
}

cmd.Flags().String(flagMultisig, "", "Address or key name of the multisig account on behalf of which the transaction shall be signed")
Expand All @@ -74,7 +74,6 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error {
txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags())
txCfg := clientCtx.TxConfig
printSignatureOnly, _ := cmd.Flags().GetBool(flagSigOnly)
infile := os.Stdin

ms, err := cmd.Flags().GetString(flagMultisig)
if err != nil {
Expand All @@ -86,17 +85,14 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}

defer closeFunc()
clientCtx.WithOutput(cmd.OutOrStdout())

if args[0] != "-" {
infile, err = os.Open(args[0])
if err != nil {
return err
}
// reads tx from args
scanner, err := authclient.ReadTxsFromInput(txCfg, args...)
if err != nil {
return err
}
scanner := authclient.NewBatchScanner(txCfg, infile)

if !clientCtx.Offline {
if ms == "" {
Expand All @@ -121,9 +117,9 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error {
}
}

appendMessagesToSingleMsg, _ := cmd.Flags().GetBool(flagAppend)
if appendMessagesToSingleMsg {
// It will combine all tx msgs and create single signed transaction
appendMessagesToSingleTx, _ := cmd.Flags().GetBool(flagAppend)

Check warning

Code scanning / gosec

Returned error is not propagated up the stack.

Returned error is not propagated up the stack.
// Combines all tx msgs and create single signed transaction
if appendMessagesToSingleTx {
txBuilder := clientCtx.TxConfig.NewTxBuilder()
msgs := make([]sdk.Msg, 0)
newGasLimit := uint64(0)
Expand Down Expand Up @@ -151,39 +147,24 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error {
// set the gasLimit
txBuilder.SetGasLimit(newGasLimit)

// sign the txs
if ms == "" {
from, _ := cmd.Flags().GetString(flags.FlagFrom)
_, fromName, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), from)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}
err = authclient.SignTx(txFactory, clientCtx, fromName, txBuilder, true, true)
if err != nil {
if err := sign(clientCtx, txBuilder, txFactory, from); err != nil {
return err
}
} else {
multisigAddr, _, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), ms)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}
err = authclient.SignTxWithSignerAddress(
txFactory, clientCtx, multisigAddr, clientCtx.GetFromName(), txBuilder, clientCtx.Offline, true)
if err != nil {
if err := multisigSign(clientCtx, txBuilder, txFactory, ms); err != nil {
return err
}
}

if err != nil {
return err
}

json, err := marshalSignatureJSON(txCfg, txBuilder, printSignatureOnly)
if err != nil {
return err
}

cmd.Printf("%s\n", json)

} else {
// It will generate signed tx for each tx
for sequence := txFactory.Sequence(); scanner.Scan(); sequence++ {
Expand All @@ -193,37 +174,23 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}

// sign the txs
if ms == "" {
from, _ := cmd.Flags().GetString(flags.FlagFrom)
_, fromName, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), from)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}
err = authclient.SignTx(txFactory, clientCtx, fromName, txBuilder, true, true)
if err != nil {
if err := sign(clientCtx, txBuilder, txFactory, from); err != nil {
return err
}
} else {
multisigAddr, _, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), ms)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}
err = authclient.SignTxWithSignerAddress(
txFactory, clientCtx, multisigAddr, clientCtx.GetFromName(), txBuilder, clientCtx.Offline, true)
if err != nil {
if err := multisigSign(clientCtx, txBuilder, txFactory, ms); err != nil {
return err
}
}

if err != nil {
return err
}

json, err := marshalSignatureJSON(txCfg, txBuilder, printSignatureOnly)
if err != nil {
return err
}

cmd.Printf("%s\n", json)
}
}
Expand All @@ -236,6 +203,40 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error {
}
}

func sign(clientCtx client.Context, txBuilder client.TxBuilder, txFactory tx.Factory, from string) error {
_, fromName, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), from)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}

if err = authclient.SignTx(txFactory, clientCtx, fromName, txBuilder, true, true); err != nil {
return err
}

return nil
}

func multisigSign(clientCtx client.Context, txBuilder client.TxBuilder, txFactory tx.Factory, multisig string) error {
multisigAddr, _, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), multisig)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}

if err = authclient.SignTxWithSignerAddress(
txFactory,
clientCtx,
multisigAddr,
clientCtx.GetFromName(),
txBuilder,
clientCtx.Offline,
true,
); err != nil {
return err
}

return nil
}

func setOutputFile(cmd *cobra.Command) (func(), error) {
outputDoc, _ := cmd.Flags().GetString(flags.FlagOutputDocument)
if outputDoc == "" {
Expand Down
30 changes: 29 additions & 1 deletion x/auth/client/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"strings"

"github.com/cosmos/gogoproto/jsonpb"
Expand Down Expand Up @@ -90,7 +91,7 @@ func SignTxWithSignerAddress(txFactory tx.Factory, clientCtx client.Context, add
return tx.Sign(txFactory, name, txBuilder, overwrite)
}

// Read and decode a StdTx from the given filename. Can pass "-" to read from stdin.
// Read and decode a StdTx from the given filename. Can pass "-" to read from stdin.
func ReadTxFromFile(ctx client.Context, filename string) (tx sdk.Tx, err error) {
var bytes []byte

Expand All @@ -107,6 +108,33 @@ func ReadTxFromFile(ctx client.Context, filename string) (tx sdk.Tx, err error)
return ctx.TxConfig.TxJSONDecoder()(bytes)
}

// ReadTxsFromInput reads multiples txs from the given filename(s). Can pass "-" to read from stdin.
// Unlike ReadTxFromFile, this function does not decode the txs.
func ReadTxsFromInput(txCfg client.TxConfig, filenames ...string) (scanner *BatchScanner, err error) {
if len(filenames) == 0 {
return nil, fmt.Errorf("no file name provided")
}

var infile io.Reader = os.Stdin
if filenames[0] != "-" {
buf := new(bytes.Buffer)
for _, f := range filenames {
bytes, err := os.ReadFile(filepath.Clean(f))
if err != nil {
return nil, fmt.Errorf("couldn't read %s: %w", f, err)
}

if _, err := buf.WriteString(string(bytes)); err != nil {
return nil, fmt.Errorf("couldn't write to merged file: %w", err)
}
}

infile = buf
}

return NewBatchScanner(txCfg, infile), nil
}

// NewBatchScanner returns a new BatchScanner to read newline-delimited StdTx transactions from r.
func NewBatchScanner(cfg client.TxConfig, r io.Reader) *BatchScanner {
return &BatchScanner{Scanner: bufio.NewScanner(r), cfg: cfg}
Expand Down