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

Update Query Txs by Events Command #5482

Merged
merged 7 commits into from
Jan 6, 2020
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
68 changes: 43 additions & 25 deletions docs/core/events.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<!--
order: 7
synopsis: "`Event`s are objects that contain information about the execution of the application. They are mainly used by service providers like block explorers and wallet to track the execution of various messages and index transactions."
synopsis: "`Event`s are objects that contain information about the execution of the application.
They are mainly used by service providers like block explorers and wallet to track the execution of
various messages and index transactions."
-->

# Events
Expand All @@ -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'"
}
}
```
Expand All @@ -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",
Expand All @@ -88,4 +106,4 @@ where `senderAddress` is an address following the [`AccAddress`](../basics/accou

## Next {hide}

Learn about [object-capabilities](./ocap.md) {hide}
Learn about [object-capabilities](./ocap.md) {hide}
65 changes: 37 additions & 28 deletions x/auth/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@ 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"

tmtypes "github.com/tendermint/tendermint/types"
)

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
Expand Down Expand Up @@ -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:
$ <appcli> query txs --tags '<tag1>:<value1>&<tag2>:<value2>' --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 <key>:<value>", tagsStr)
} else if strings.Count(tag, ":") > 1 {
return fmt.Errorf("%s should only contain one <key>:<value> 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
}
Expand All @@ -135,13 +143,14 @@ $ <appcli> query txs --tags '<tag1>:<value1>&<tag2>:<value2>' --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
}
Expand Down