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: add event-query-tx-for cmd to subscribe and wait for transaction #17274

Merged
merged 14 commits into from
Aug 17, 2023
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Features

* (x/bank) [#16795](https://github.com/cosmos/cosmos-sdk/pull/16852) Add `DenomMetadataByQueryString` query in bank module to support metadata query by query string.
* (x/auth) [#17274](https://github.com/cosmos/cosmos-sdk/pull/17274) Add `QueryEventForTxCmd` cmd to subscribe and wait event for transaction by hash.

### Improvements

Expand Down
126 changes: 126 additions & 0 deletions x/auth/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package cli

import (
"context"
"encoding/hex"
"fmt"
"strings"
"time"

"github.com/spf13/cobra"

rpchttp "github.com/cometbft/cometbft/rpc/client/http"
coretypes "github.com/cometbft/cometbft/rpc/core/types"
tmtypes "github.com/cometbft/cometbft/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -179,6 +185,126 @@ $ %s query tx --%s=%s <sig1_base64>,<sig2_base64...>
return cmd
}

func newTxResponseCheckTx(res *coretypes.ResultBroadcastTxCommit) *sdk.TxResponse {
if res == nil {
return nil
}

var txHash string
if res.Hash != nil {
txHash = res.Hash.String()
}

parsedLogs, _ := sdk.ParseABCILogs(res.CheckTx.Log)

return &sdk.TxResponse{
Height: res.Height,
TxHash: txHash,
Codespace: res.CheckTx.Codespace,
Code: res.CheckTx.Code,
Data: strings.ToUpper(hex.EncodeToString(res.CheckTx.Data)),
RawLog: res.CheckTx.Log,
Logs: parsedLogs,
Info: res.CheckTx.Info,
GasWanted: res.CheckTx.GasWanted,
GasUsed: res.CheckTx.GasUsed,
Events: res.CheckTx.Events,
}
}

func newTxResponseDeliverTx(res *coretypes.ResultBroadcastTxCommit) *sdk.TxResponse {
if res == nil {
return nil
}

var txHash string
if res.Hash != nil {
txHash = res.Hash.String()
}

parsedLogs, _ := sdk.ParseABCILogs(res.TxResult.Log)

return &sdk.TxResponse{
Height: res.Height,
TxHash: txHash,
Codespace: res.TxResult.Codespace,
Code: res.TxResult.Code,
Data: strings.ToUpper(hex.EncodeToString(res.TxResult.Data)),
RawLog: res.TxResult.Log,
Logs: parsedLogs,
Info: res.TxResult.Info,
GasWanted: res.TxResult.GasWanted,
GasUsed: res.TxResult.GasUsed,
Events: res.TxResult.Events,
}
}

func newResponseFormatBroadcastTxCommit(res *coretypes.ResultBroadcastTxCommit) *sdk.TxResponse {
if res == nil {
return nil
}

if !res.CheckTx.IsOK() {
return newTxResponseCheckTx(res)
}

return newTxResponseDeliverTx(res)
}

// QueryEventForTxCmd returns a CLI command that subscribes to a WebSocket connection and waits for a transaction event with the given hash.
func QueryEventForTxCmd() *cobra.Command {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add this command in simapp?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, added

cmd := &cobra.Command{
Use: "event-query-tx-for [hash]",
Short: "event-query-tx-for [hash]",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
c, err := rpchttp.New(clientCtx.NodeURI, "/websocket")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tightly coupled to CometBFT, maybe we should say something about that in Use or Short. Ideally the client shouldn't make too many assumptions about the server implementation, but when it's a unavoidable let's at least make them clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated, see if it's ok

if err != nil {
return err
}
if err := c.Start(); err != nil {
return err
}
defer c.Stop() //nolint:errcheck // ignore stop error

ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
defer cancel()

hash := args[0]
query := fmt.Sprintf("%s='%s' AND %s='%s'", tmtypes.EventTypeKey, tmtypes.EventTx, tmtypes.TxHashKey, hash)
const subscriber = "subscriber"
eventCh, err := c.Subscribe(ctx, subscriber, query)
if err != nil {
return fmt.Errorf("failed to subscribe to tx: %w", err)
}
defer c.UnsubscribeAll(context.Background(), subscriber) //nolint:errcheck // ignore unsubscribe error

select {
case evt := <-eventCh:
if txe, ok := evt.Data.(tmtypes.EventDataTx); ok {
res := &coretypes.ResultBroadcastTxCommit{
TxResult: txe.Result,
Hash: tmtypes.Tx(txe.Tx).Hash(),
Height: txe.Height,
}
return clientCtx.PrintProto(newResponseFormatBroadcastTxCommit(res))
}
case <-ctx.Done():
return errors.ErrLogic.Wrapf("timed out waiting for event")
}
return nil
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}

// ParseSigArgs parses comma-separated signatures from the CLI arguments.
func ParseSigArgs(args []string) ([]string, error) {
if len(args) != 1 || args[0] == "" {
Expand Down