From 25be589af6c9704e5dd43061e6440e7d5d46c620 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Mon, 6 Jan 2020 11:49:31 -0500 Subject: [PATCH] Merge PR #5482: Update Query Txs by Events Command --- CHANGELOG.md | 3 ++ docs/core/events.md | 68 ++++++++++++++++++++++++-------------- x/auth/client/cli/query.go | 65 ++++++++++++++++++++---------------- 3 files changed, 83 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b489664dedd6..830178fb9d13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -242,6 +242,9 @@ to detail this new feature and how state transitions occur. * (docs/interfaces/) Add documentation on building interfaces for the Cosmos SDK. * Redesigned user interface that features new dynamically generated sidebar, build-time code embedding from GitHub, new homepage as well as many other improvements. * (types) [\#5428](https://github.com/cosmos/cosmos-sdk/pull/5428) Add `Mod` (modulo) method and `RelativePow` (exponentation) function for `Uint`. +* (cli) [\#5482](https://github.com/cosmos/cosmos-sdk/pull/5482) Remove old "tags" nomenclature from the `q txs` command in + favor of the new events system. Functionality remains unchanged except that `=` is used instead of `:` to be + consistent with the API's use of event queries. ### Bug Fixes diff --git a/docs/core/events.md b/docs/core/events.md index 11b6ad1e59b0..aeb0efa1dea8 100644 --- a/docs/core/events.md +++ b/docs/core/events.md @@ -1,6 +1,8 @@ # Events @@ -11,54 +13,69 @@ synopsis: "`Event`s are objects that contain information about the execution of ## Events -`Event`s are implemented in the Cosmos SDK as an alias of the ABCI `event` type. +Events are implemented in the Cosmos SDK as an alias of the ABCI `Event` type and +take the form of: `{eventType}.{eventAttribute}={value}`. +++ https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/abci/types/types.pb.go#L2661-L2667 -They contain: +Events contain: -- A **`type`** of type `string`, which can refer to the type of action that led to the `event`'s emission (e.g. a certain value going above a threshold), or to the type of `message` if the event is triggered at the end of that `message` processing. -- A list of `attributes`, which are key-value pairs that give more information about the `event`. - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/events.go#L51-L56 +- A `type`, which is meant to categorize an event at a high-level (e.g. by module or action). +- A list of `attributes`, which are key-value pairs that give more information about + the categorized `event`. + +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/events.go#L51-L56 -`Event`s are returned to the underlying consensus engine in the response of the following ABCI messages: [`CheckTx`](./baseapp.md#checktx), [`DeliverTx`](./baseapp.md#delivertx), [`BeginBlock`](./baseapp.md#beginblock) and [`EndBlock`](./baseapp.md#endblock). +Events are returned to the underlying consensus engine in the response of the following ABCI messages: -Typically, `event` `type`s and `attributes` are defined on a **per-module basis** in the module's `/internal/types/events.go` file, and triggered from the module's [`handler`](../building-modules/handler.md) via the [`EventManager`](#eventmanager). +- [`BeginBlock`](./baseapp.md#beginblock) +- [`EndBlock`](./baseapp.md#endblock) +- [`CheckTx`](./baseapp.md#checktx) +- [`DeliverTx`](./baseapp.md#delivertx) + +Events, the `type` and `attributes`, are defined on a **per-module basis** in the module's +`/internal/types/events.go` file, and triggered from the module's [`handler`](../building-modules/handler.md) +via the [`EventManager`](#eventmanager). In addition, each module documents its events under +`spec/xx_events.md`. ## EventManager -In Cosmos SDK applications, `event`s are generally managed by an object called the `EventManager`. It is implemented as a simple wrapper around a slice of `event`s: +In Cosmos SDK applications, events are managed by an abstraction called the `EventManager`. +Internally, the `EventManager` tracks a list of `Events` for the entire execution flow of a +transaction or `BeginBlock`/`EndBlock`. +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/events.go#L16-L20 -The `EventManager` comes with a set of useful methods to manage `event`s. Among them, the one that is used the most by module and application developers is the `EmitEvent` method, which registers an `event` in the `EventManager`. +The `EventManager` comes with a set of useful methods to manage events. Among them, the one that is +used the most by module and application developers is the `EmitEvent` method, which tracks +an `event` in the `EventManager`. +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/events.go#L29-L31 -Typically, module developers will implement event emission via the `EventManager` in the [`handler`](../building-modules/handler.md) of modules, as well as in the [`BeginBlocker` and/or`EndBlocker` functions](../building-modules/beginblock-endblock.md). The `EventManager` is accessed via the context [`ctx`](./context.md), and event emission generally follows this pattern: +Module developers should handle event emission via the `EventManager#EmitEvent` in each message +`Handler` and in each `BeginBlock`/`EndBlock` handler. The `EventManager` is accessed via +the [`Context`](./context.md), where event emission generally follows this pattern: ```go ctx.EventManager().EmitEvent( - sdk.NewEvent( - eventType, // e.g. sdk.EventTypeMessage for a message, types.CustomEventType for a custom event defined in the module - sdk.NewAttribute(attributeKey, attributeValue), - ), - ) + sdk.NewEvent(eventType, sdk.NewAttribute(attributeKey, attributeValue)), +) ``` -See the [`handler` concept doc](../building-modules/handler.md) for a more detailed view on how to typically implement `events` and use the `EventManager` in modules. +See the [`Handler`](../building-modules/handler.md) concept doc for a more detailed +view on how to typically implement `Events` and use the `EventManager` in modules. -## Subscribing to `events` +## Subscribing to Events -It is possible to subscribe to `events` via [Tendermint's Websocket](https://tendermint.com/docs/app-dev/subscribing-to-events-via-websocket.html#subscribing-to-events-via-websocket). This is done by calling the `subscribe` RPC method via Websocket: +It is possible to subscribe to `Events` via Tendermint's [Websocket](https://tendermint.com/docs/app-dev/subscribing-to-events-via-websocket.html#subscribing-to-events-via-websocket). +This is done by calling the `subscribe` RPC method via Websocket: -``` +```json { "jsonrpc": "2.0", "method": "subscribe", "id": "0", "params": { - "query": "tm.event='eventCategory' AND type.attribute='attributeValue'" + "query": "tm.event='eventCategory' AND eventType.eventAttribute='attributeValue'" } } ``` @@ -67,13 +84,14 @@ The main `eventCategory` you can subscribe to are: - `NewBlock`: Contains `events` triggered during `BeginBlock` and `EndBlock`. - `Tx`: Contains `events` triggered during `DeliverTx` (i.e. transaction processing). -- `ValidatorSetUpdates`: Contains validator set updates for the block. +- `ValidatorSetUpdates`: Contains validator set updates for the block. -These events are triggered from the `state` package after a block is committed. You can get the full list of `event` categories [here](https://godoc.org/github.com/tendermint/tendermint/types#pkg-constants). +These events are triggered from the `state` package after a block is committed. You can get the +full list of `event` categories [here](https://godoc.org/github.com/tendermint/tendermint/types#pkg-constants). The `type` and `attribute` value of the `query` allow you to filter the specific `event` you are looking for. For example, a `transfer` transaction triggers an `event` of type `Transfer` and has `Recipient` and `Sender` as `attributes` (as defined in the [`events` file of the `bank` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/bank/internal/types/events.go)). Subscribing to this `event` would be done like so: -``` +```json { "jsonrpc": "2.0", "method": "subscribe", @@ -88,4 +106,4 @@ where `senderAddress` is an address following the [`AccAddress`](../basics/accou ## Next {hide} -Learn about [object-capabilities](./ocap.md) {hide} \ No newline at end of file +Learn about [object-capabilities](./ocap.md) {hide} diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 9eee22127c50..8688d0e8d6df 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth/client/utils" "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -20,9 +21,11 @@ import ( ) const ( - flagTags = "tags" - flagPage = "page" - flagLimit = "limit" + flagEvents = "events" + flagPage = "page" + flagLimit = "limit" + + eventFormat = "{eventType}.{eventAttribute}={value}" ) // GetQueryCmd returns the transaction commands for this module @@ -72,47 +75,52 @@ func GetAccountCmd(cdc *codec.Codec) *cobra.Command { func QueryTxsByEventsCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "txs", - Short: "Query for paginated transactions that match a set of tags", - Long: strings.TrimSpace(` -Search for transactions that match the exact given tags where results are paginated. + Short: "Query for paginated transactions that match a set of events", + Long: strings.TrimSpace( + fmt.Sprintf(` +Search for transactions that match the exact given events where results are paginated. +Each event takes the form of '%s'. Please refer +to each module's documentation for the full set of events to query for. Each module +documents its respective events under 'xx_events.md'. Example: -$ query txs --tags ':&:' --page 1 --limit 30 -`), +$ %s query txs --%s 'message.sender=cosmos1...&message.action=withdraw_delegator_reward' --page 1 --limit 30 +`, eventFormat, version.ClientName, flagEvents), + ), RunE: func(cmd *cobra.Command, args []string) error { - tagsStr := viper.GetString(flagTags) - tagsStr = strings.Trim(tagsStr, "'") + eventsStr := strings.Trim(viper.GetString(flagEvents), "'") - var tags []string - if strings.Contains(tagsStr, "&") { - tags = strings.Split(tagsStr, "&") + var events []string + if strings.Contains(eventsStr, "&") { + events = strings.Split(eventsStr, "&") } else { - tags = append(tags, tagsStr) + events = append(events, eventsStr) } - var tmTags []string - for _, tag := range tags { - if !strings.Contains(tag, ":") { - return fmt.Errorf("%s should be of the format :", tagsStr) - } else if strings.Count(tag, ":") > 1 { - return fmt.Errorf("%s should only contain one : pair", tagsStr) + var tmEvents []string + + for _, event := range events { + if !strings.Contains(event, "=") { + return fmt.Errorf("invalid event; event %s should be of the format: %s", event, eventFormat) + } else if strings.Count(event, "=") > 1 { + return fmt.Errorf("invalid event; event %s should be of the format: %s", event, eventFormat) } - keyValue := strings.Split(tag, ":") - if keyValue[0] == tmtypes.TxHeightKey { - tag = fmt.Sprintf("%s=%s", keyValue[0], keyValue[1]) + tokens := strings.Split(event, "=") + if tokens[0] == tmtypes.TxHeightKey { + event = fmt.Sprintf("%s=%s", tokens[0], tokens[1]) } else { - tag = fmt.Sprintf("%s='%s'", keyValue[0], keyValue[1]) + event = fmt.Sprintf("%s='%s'", tokens[0], tokens[1]) } - tmTags = append(tmTags, tag) + tmEvents = append(tmEvents, event) } page := viper.GetInt(flagPage) limit := viper.GetInt(flagLimit) cliCtx := context.NewCLIContext().WithCodec(cdc) - txs, err := utils.QueryTxsByEvents(cliCtx, tmTags, page, limit) + txs, err := utils.QueryTxsByEvents(cliCtx, tmEvents, page, limit) if err != nil { return err } @@ -135,13 +143,14 @@ $ query txs --tags ':&:' --page 1 --limit 3 cmd.Flags().StringP(flags.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") viper.BindPFlag(flags.FlagNode, cmd.Flags().Lookup(flags.FlagNode)) + cmd.Flags().Bool(flags.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") viper.BindPFlag(flags.FlagTrustNode, cmd.Flags().Lookup(flags.FlagTrustNode)) - cmd.Flags().String(flagTags, "", "tag:value list of tags that must match") + cmd.Flags().String(flagEvents, "", fmt.Sprintf("list of transaction events in the form of %s", eventFormat)) cmd.Flags().Uint32(flagPage, rest.DefaultPage, "Query a specific page of paginated results") cmd.Flags().Uint32(flagLimit, rest.DefaultLimit, "Query number of transactions results per page returned") - cmd.MarkFlagRequired(flagTags) + cmd.MarkFlagRequired(flagEvents) return cmd }