mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-03 03:35:19 +00:00
This commit is contained in:
@@ -21,6 +21,8 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
|
||||
|
||||
### FEATURES
|
||||
|
||||
- [rpc] \#6226 Index block events and expose a new RPC method, `/block_search`, to allow querying for blocks by `BeginBlock` and `EndBlock` events. (@alexanderbez)
|
||||
|
||||
### IMPROVEMENTS
|
||||
|
||||
### BUG FIXES
|
||||
|
||||
@@ -4,12 +4,13 @@ order: 6
|
||||
|
||||
# Indexing Transactions
|
||||
|
||||
Tendermint allows you to index transactions and later query or subscribe to their results.
|
||||
|
||||
Events can be used to index transactions and blocks according to what happened
|
||||
during their execution. Note that the set of events returned for a block from
|
||||
`BeginBlock` and `EndBlock` are merged. In case both methods return the same
|
||||
type, only the key-value pairs defined in `EndBlock` are used.
|
||||
Tendermint allows you to index transactions and blocks and later query or
|
||||
subscribe to their results. Transactions are indexed by `TxResult.Events` and
|
||||
blocks are indexed by `Response(Begin|End)Block.Events`. However, transactions
|
||||
are also indexed by a primary key which includes the transaction hash and maps
|
||||
to and stores the corresponding `TxResult`. Blocks are indexed by a primary key
|
||||
which includes the block height and maps to and stores the block height, i.e.
|
||||
the block itself is never stored.
|
||||
|
||||
Each event contains a type and a list of attributes, which are key-value pairs
|
||||
denoting something about what happened during the method's execution. For more
|
||||
@@ -17,7 +18,7 @@ details on `Events`, see the
|
||||
[ABCI](https://github.com/tendermint/spec/blob/master/spec/abci/abci.md#events)
|
||||
documentation.
|
||||
|
||||
An Event has a composite key associated with it. A `compositeKey` is
|
||||
An `Event` has a composite key associated with it. A `compositeKey` is
|
||||
constructed by its type and key separated by a dot.
|
||||
|
||||
For example:
|
||||
@@ -44,11 +45,29 @@ Let's take a look at the `[tx_index]` config section:
|
||||
indexer = "kv"
|
||||
```
|
||||
|
||||
By default, Tendermint will index all transactions by their respective
|
||||
hashes and height using an embedded simple indexer.
|
||||
By default, Tendermint will index all transactions by their respective hashes
|
||||
and height and blocks by their height.
|
||||
|
||||
You can turn off indexing completely by setting `tx_index` to `null`.
|
||||
|
||||
## Default Indexes
|
||||
|
||||
The Tendermint tx and block event indexer indexes a few select reserved events
|
||||
by default.
|
||||
|
||||
### Transactions
|
||||
|
||||
The following indexes are indexed by default:
|
||||
|
||||
- `tx.height`
|
||||
- `tx.hash`
|
||||
|
||||
### Blocks
|
||||
|
||||
The following indexes are indexed by default:
|
||||
|
||||
- `block.height`
|
||||
|
||||
## Adding Events
|
||||
|
||||
Applications are free to define which events to index. Tendermint does not
|
||||
@@ -77,19 +96,21 @@ func (app *KVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.Resul
|
||||
}
|
||||
```
|
||||
|
||||
The transaction will be indexed (if the indexer is not `null`) with a certain attribute if the attribute's `Index` field is set to `true`.
|
||||
In the above example, all attributes will be indexed.
|
||||
If the indexer is not `null`, the transaction will be indexed. Each event is
|
||||
indexed using a composite key in the form of `{eventType}.{eventAttribute}={eventValue}`,
|
||||
e.g. `transfer.sender=bob`.
|
||||
|
||||
## Querying Transactions
|
||||
## Querying Transactions Events
|
||||
|
||||
You can query the transaction results by calling `/tx_search` RPC endpoint:
|
||||
You can query for a paginated set of transaction by their events by calling the
|
||||
`/tx_search` RPC endpoint:
|
||||
|
||||
```bash
|
||||
curl "localhost:26657/tx_search?query=\"account.name='igor'\"&prove=true"
|
||||
curl "localhost:26657/tx_search?query=\"message.sender='cosmos1...'\"&prove=true"
|
||||
```
|
||||
|
||||
Check out [API docs](https://docs.tendermint.com/master/rpc/#/Info/tx_search) for more information
|
||||
on query syntax and other options.
|
||||
Check out [API docs](https://docs.tendermint.com/master/rpc/#/Info/tx_search)
|
||||
for more information on query syntax and other options.
|
||||
|
||||
## Subscribing to Transactions
|
||||
|
||||
@@ -102,10 +123,22 @@ a query to `/subscribe` RPC endpoint.
|
||||
"method": "subscribe",
|
||||
"id": "0",
|
||||
"params": {
|
||||
"query": "account.name='igor'"
|
||||
"query": "message.sender='cosmos1...'"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Check out [API docs](https://docs.tendermint.com/master/rpc/#subscribe) for more information
|
||||
on query syntax and other options.
|
||||
|
||||
## Querying Blocks Events
|
||||
|
||||
You can query for a paginated set of blocks by their events by calling the
|
||||
`/block_search` RPC endpoint:
|
||||
|
||||
```bash
|
||||
curl "localhost:26657/block_search?query=\"block.height > 10 AND val_set.num_changed > 0\""
|
||||
```
|
||||
|
||||
Check out [API docs](https://docs.tendermint.com/master/rpc/#/Info/block_search)
|
||||
for more information on query syntax and other options.
|
||||
|
||||
1
go.mod
1
go.mod
@@ -15,6 +15,7 @@ require (
|
||||
github.com/go-logfmt/logfmt v0.5.0
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/google/orderedcode v0.0.1
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/gtank/merlin v0.1.1
|
||||
github.com/libp2p/go-buffer-pool v0.0.2
|
||||
|
||||
80
go.sum
80
go.sum
@@ -45,13 +45,10 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
@@ -81,13 +78,11 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb h1:+7FsS1gZ1Km5LRjGV2hztpier/5i6ngNjvNpxbWP5I0=
|
||||
github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
|
||||
github.com/confio/ics23/go v0.6.3 h1:PuGK2V1NJWZ8sSkNDq91jgT/cahFEW9RGp4Y5jxulf0=
|
||||
github.com/confio/ics23/go v0.6.3/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@@ -98,9 +93,7 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU=
|
||||
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
|
||||
github.com/cosmos/iavl v0.15.0-rc3.0.20201009144442-230e9bdf52cd h1:K3bmPkMDnd2KVQ7xoGmgp+pxoXcBW58vMWaMl9ZWx3c=
|
||||
github.com/cosmos/iavl v0.15.0-rc3.0.20201009144442-230e9bdf52cd/go.mod h1:3xOIaNNX19p0QrX0VqWa6voPRoJRGGYtny+DH8NEPvE=
|
||||
github.com/cosmos/iavl v0.15.0-rc5 h1:AMKgaAjXwGANWv56NL4q4hV+a0puSkLYD6cCQAv3i44=
|
||||
github.com/cosmos/iavl v0.15.0-rc5/go.mod h1:WqoPL9yPTQ85QBMT45OOUzPxG/U/JcJoN7uMjgxke/I=
|
||||
github.com/cosmos/iavl v0.15.3 h1:xE9r6HW8GeKeoYJN4zefpljZ1oukVScP/7M8oj6SUts=
|
||||
github.com/cosmos/iavl v0.15.3/go.mod h1:OLjQiAQ4fGD2KDZooyJG9yz+p2ao2IAYSbke8mVvSA4=
|
||||
@@ -113,7 +106,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.1 h1:t36VcBCpo4SsmAD5M8wVv1ieVzcALyGfaJ92z4ccULM=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.1/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=
|
||||
@@ -147,22 +139,17 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
@@ -173,13 +160,10 @@ github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -189,17 +173,14 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
@@ -209,18 +190,16 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us=
|
||||
github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
@@ -232,7 +211,6 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
@@ -246,11 +224,9 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.7 h1:Nk5kuHrnWUTf/0GL1a/vchH/om9Ap2/HnVna+jYZgTY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.7/go.mod h1:oYZKL012gGh6LMyg/xA7Q2yq6j8bu0wa+9w14EEthWU=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f h1:8N8XWLZelZNibkhM1FuF+3Ad3YIbgirjdMiVA0eUkaM=
|
||||
github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
||||
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
|
||||
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
||||
@@ -280,7 +256,6 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
@@ -291,7 +266,6 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
|
||||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
@@ -311,9 +285,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
@@ -339,7 +311,6 @@ github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz
|
||||
github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
@@ -369,13 +340,11 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
@@ -400,7 +369,6 @@ github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCr
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -408,54 +376,38 @@ github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6J
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1 h1:K47Rk0v/fkEfwfQet2KWhscE0cJzjgCCDBG2KHZoVno=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc=
|
||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw=
|
||||
github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.1.0 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE=
|
||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4=
|
||||
github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
@@ -468,7 +420,6 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y=
|
||||
github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10=
|
||||
github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4=
|
||||
github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
|
||||
@@ -492,17 +443,14 @@ github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@@ -517,13 +465,10 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -536,9 +481,7 @@ github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7
|
||||
github.com/tendermint/tendermint v0.34.0-rc4/go.mod h1:yotsojf2C1QBOw4dZrTcxbyxmPUrT4hNuOQWX9XUwB4=
|
||||
github.com/tendermint/tendermint v0.34.0-rc6/go.mod h1:ugzyZO5foutZImv0Iyx/gOFCX6mjJTgbLHTwi17VDVg=
|
||||
github.com/tendermint/tendermint v0.34.0/go.mod h1:Aj3PIipBFSNO21r+Lq3TtzQ+uKESxkbA3yo/INM4QwQ=
|
||||
github.com/tendermint/tm-db v0.6.2 h1:DOn8jwCdjJblrCFJbtonEIPD1IuJWpbRUUdR8GWE4RM=
|
||||
github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI=
|
||||
github.com/tendermint/tm-db v0.6.3 h1:ZkhQcKnB8/2jr5EaZwGndN4owkPsGezW2fSisS9zGbg=
|
||||
github.com/tendermint/tm-db v0.6.3/go.mod h1:lfA1dL9/Y/Y8wwyPp2NMLyn5P5Ptr/gvDFNWtrCWSf8=
|
||||
github.com/tendermint/tm-db v0.6.4 h1:3N2jlnYQkXNQclQwd/eKV/NzlqPlfK21cpRRIx80XXQ=
|
||||
github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw=
|
||||
@@ -553,11 +496,9 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
@@ -576,21 +517,16 @@ golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 h1:phUcVbl53swtrUN8kQEXFhUxPlIlWyBfKmidCu7P95o=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
@@ -616,7 +552,6 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -643,9 +578,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
@@ -694,13 +627,11 @@ golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed h1:J22ig1FUekjjkmZUM7pTKixYm8DvrYsvrBZdunYeIuQ=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
@@ -736,7 +667,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -767,9 +697,7 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6 h1:iRN4+t0lvZX/l9gH14ARF9i58tsVa5a97k6aH95rC3Y=
|
||||
google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4 h1:Rt0FRalMgdSlXAVJvX4pr65KfqaxHXSLkSJRD9pw6g0=
|
||||
google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
@@ -789,10 +717,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
|
||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
@@ -804,7 +730,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
@@ -812,7 +737,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -29,6 +29,7 @@ func RPCRoutes(c *lrpc.Client) map[string]*rpcserver.RPCFunc {
|
||||
"commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"),
|
||||
"tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove"),
|
||||
"tx_search": rpcserver.NewRPCFunc(makeTxSearchFunc(c), "query,prove,page,per_page,order_by"),
|
||||
"block_search": rpcserver.NewRPCFunc(makeBlockSearchFunc(c), "query,page,per_page,order_by"),
|
||||
"validators": rpcserver.NewRPCFunc(makeValidatorsFunc(c), "height,page,per_page"),
|
||||
"dump_consensus_state": rpcserver.NewRPCFunc(makeDumpConsensusStateFunc(c), ""),
|
||||
"consensus_state": rpcserver.NewRPCFunc(makeConsensusStateFunc(c), ""),
|
||||
@@ -131,16 +132,46 @@ func makeTxFunc(c *lrpc.Client) rpcTxFunc {
|
||||
}
|
||||
}
|
||||
|
||||
type rpcTxSearchFunc func(ctx *rpctypes.Context, query string, prove bool,
|
||||
page, perPage *int, orderBy string) (*ctypes.ResultTxSearch, error)
|
||||
type rpcTxSearchFunc func(
|
||||
ctx *rpctypes.Context,
|
||||
query string,
|
||||
prove bool,
|
||||
page, perPage *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultTxSearch, error)
|
||||
|
||||
func makeTxSearchFunc(c *lrpc.Client) rpcTxSearchFunc {
|
||||
return func(ctx *rpctypes.Context, query string, prove bool, page, perPage *int, orderBy string) (
|
||||
*ctypes.ResultTxSearch, error) {
|
||||
return func(
|
||||
ctx *rpctypes.Context,
|
||||
query string,
|
||||
prove bool,
|
||||
page, perPage *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultTxSearch, error) {
|
||||
return c.TxSearch(ctx.Context(), query, prove, page, perPage, orderBy)
|
||||
}
|
||||
}
|
||||
|
||||
type rpcBlockSearchFunc func(
|
||||
ctx *rpctypes.Context,
|
||||
query string,
|
||||
prove bool,
|
||||
page, perPage *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultBlockSearch, error)
|
||||
|
||||
func makeBlockSearchFunc(c *lrpc.Client) rpcBlockSearchFunc {
|
||||
return func(
|
||||
ctx *rpctypes.Context,
|
||||
query string,
|
||||
prove bool,
|
||||
page, perPage *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultBlockSearch, error) {
|
||||
return c.BlockSearch(ctx.Context(), query, page, perPage, orderBy)
|
||||
}
|
||||
}
|
||||
|
||||
type rpcValidatorsFunc func(ctx *rpctypes.Context, height *int64,
|
||||
page, perPage *int) (*ctypes.ResultValidators, error)
|
||||
|
||||
|
||||
@@ -35,15 +35,18 @@ type LightClient interface {
|
||||
TrustedLightBlock(height int64) (*types.LightBlock, error)
|
||||
}
|
||||
|
||||
var _ rpcclient.Client = (*Client)(nil)
|
||||
|
||||
// Client is an RPC client, which uses light#Client to verify data (if it can
|
||||
// be proved!). merkle.DefaultProofRuntime is used to verify values returned by
|
||||
// ABCIQuery.
|
||||
// be proved). Note, merkle.DefaultProofRuntime is used to verify values
|
||||
// returned by ABCI#Query.
|
||||
type Client struct {
|
||||
service.BaseService
|
||||
|
||||
next rpcclient.Client
|
||||
lc LightClient
|
||||
// Proof runtime used to verify values returned by ABCIQuery
|
||||
|
||||
// proof runtime used to verify values returned by ABCIQuery
|
||||
prt *merkle.ProofRuntime
|
||||
keyPathFn KeyPathFunc
|
||||
}
|
||||
@@ -470,16 +473,34 @@ func (c *Client) Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.Resul
|
||||
return res, res.Proof.Validate(l.DataHash)
|
||||
}
|
||||
|
||||
func (c *Client) TxSearch(ctx context.Context, query string, prove bool, page, perPage *int, orderBy string) (
|
||||
*ctypes.ResultTxSearch, error) {
|
||||
func (c *Client) TxSearch(
|
||||
ctx context.Context,
|
||||
query string,
|
||||
prove bool,
|
||||
page, perPage *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultTxSearch, error) {
|
||||
return c.next.TxSearch(ctx, query, prove, page, perPage, orderBy)
|
||||
}
|
||||
|
||||
func (c *Client) BlockSearch(
|
||||
ctx context.Context,
|
||||
query string,
|
||||
page, perPage *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultBlockSearch, error) {
|
||||
return c.next.BlockSearch(ctx, query, page, perPage, orderBy)
|
||||
}
|
||||
|
||||
// Validators fetches and verifies validators.
|
||||
func (c *Client) Validators(ctx context.Context, height *int64, pagePtr, perPagePtr *int) (*ctypes.ResultValidators,
|
||||
error) {
|
||||
// Update the light client if we're behind and retrieve the light block at the requested height
|
||||
// or at the latest height if no height is provided.
|
||||
func (c *Client) Validators(
|
||||
ctx context.Context,
|
||||
height *int64,
|
||||
pagePtr, perPagePtr *int,
|
||||
) (*ctypes.ResultValidators, error) {
|
||||
|
||||
// Update the light client if we're behind and retrieve the light block at the
|
||||
// requested height or at the latest height if no height is provided.
|
||||
l, err := c.updateLightClientIfNeededTo(ctx, height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -493,7 +514,6 @@ func (c *Client) Validators(ctx context.Context, height *int64, pagePtr, perPage
|
||||
}
|
||||
|
||||
skipCount := validateSkipCount(page, perPage)
|
||||
|
||||
v := l.ValidatorSet.Validators[skipCount : skipCount+tmmath.MinInt(perPage, totalCount-skipCount)]
|
||||
|
||||
return &ctypes.ResultValidators{
|
||||
|
||||
37
node/node.go
37
node/node.go
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/rs/cors"
|
||||
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@@ -39,6 +38,9 @@ import (
|
||||
grpccore "github.com/tendermint/tendermint/rpc/grpc"
|
||||
rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/state/indexer"
|
||||
blockidxkv "github.com/tendermint/tendermint/state/indexer/block/kv"
|
||||
blockidxnull "github.com/tendermint/tendermint/state/indexer/block/null"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/state/txindex/kv"
|
||||
"github.com/tendermint/tendermint/state/txindex/null"
|
||||
@@ -201,6 +203,7 @@ type Node struct {
|
||||
proxyApp proxy.AppConns // connection to the application
|
||||
rpcListeners []net.Listener // rpc servers
|
||||
txIndexer txindex.TxIndexer
|
||||
blockIndexer indexer.BlockIndexer
|
||||
indexerService *txindex.IndexerService
|
||||
prometheusSrv *http.Server
|
||||
}
|
||||
@@ -239,27 +242,40 @@ func createAndStartEventBus(logger log.Logger) (*types.EventBus, error) {
|
||||
return eventBus, nil
|
||||
}
|
||||
|
||||
func createAndStartIndexerService(config *cfg.Config, dbProvider DBProvider,
|
||||
eventBus *types.EventBus, logger log.Logger) (*txindex.IndexerService, txindex.TxIndexer, error) {
|
||||
func createAndStartIndexerService(
|
||||
config *cfg.Config,
|
||||
dbProvider DBProvider,
|
||||
eventBus *types.EventBus,
|
||||
logger log.Logger,
|
||||
) (*txindex.IndexerService, txindex.TxIndexer, indexer.BlockIndexer, error) {
|
||||
|
||||
var (
|
||||
txIndexer txindex.TxIndexer
|
||||
blockIndexer indexer.BlockIndexer
|
||||
)
|
||||
|
||||
var txIndexer txindex.TxIndexer
|
||||
switch config.TxIndex.Indexer {
|
||||
case "kv":
|
||||
store, err := dbProvider(&DBContext{"tx_index", config})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
txIndexer = kv.NewTxIndex(store)
|
||||
blockIndexer = blockidxkv.New(dbm.NewPrefixDB(store, []byte("block_events")))
|
||||
default:
|
||||
txIndexer = &null.TxIndex{}
|
||||
blockIndexer = &blockidxnull.BlockerIndexer{}
|
||||
}
|
||||
|
||||
indexerService := txindex.NewIndexerService(txIndexer, eventBus)
|
||||
indexerService := txindex.NewIndexerService(txIndexer, blockIndexer, eventBus)
|
||||
indexerService.SetLogger(logger.With("module", "txindex"))
|
||||
|
||||
if err := indexerService.Start(); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return indexerService, txIndexer, nil
|
||||
|
||||
return indexerService, txIndexer, blockIndexer, nil
|
||||
}
|
||||
|
||||
func doHandshake(
|
||||
@@ -650,8 +666,7 @@ func NewNode(config *cfg.Config,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Transaction indexing
|
||||
indexerService, txIndexer, err := createAndStartIndexerService(config, dbProvider, eventBus, logger)
|
||||
indexerService, txIndexer, blockIndexer, err := createAndStartIndexerService(config, dbProvider, eventBus, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -828,6 +843,7 @@ func NewNode(config *cfg.Config,
|
||||
proxyApp: proxyApp,
|
||||
txIndexer: txIndexer,
|
||||
indexerService: indexerService,
|
||||
blockIndexer: blockIndexer,
|
||||
eventBus: eventBus,
|
||||
}
|
||||
node.BaseService = *service.NewBaseService(logger, "Node", node)
|
||||
@@ -984,6 +1000,7 @@ func (n *Node) ConfigureRPC() error {
|
||||
PubKey: pubKey,
|
||||
GenDoc: n.genesisDoc,
|
||||
TxIndexer: n.txIndexer,
|
||||
BlockIndexer: n.blockIndexer,
|
||||
ConsensusReactor: n.consensusReactor,
|
||||
EventBus: n.eventBus,
|
||||
Mempool: n.mempool,
|
||||
|
||||
@@ -468,24 +468,55 @@ func (c *baseRPCClient) TxSearch(
|
||||
page,
|
||||
perPage *int,
|
||||
orderBy string,
|
||||
) (
|
||||
*ctypes.ResultTxSearch, error) {
|
||||
) (*ctypes.ResultTxSearch, error) {
|
||||
|
||||
result := new(ctypes.ResultTxSearch)
|
||||
params := map[string]interface{}{
|
||||
"query": query,
|
||||
"prove": prove,
|
||||
"order_by": orderBy,
|
||||
}
|
||||
|
||||
if page != nil {
|
||||
params["page"] = page
|
||||
}
|
||||
if perPage != nil {
|
||||
params["per_page"] = perPage
|
||||
}
|
||||
|
||||
_, err := c.caller.Call(ctx, "tx_search", params, result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *baseRPCClient) BlockSearch(
|
||||
ctx context.Context,
|
||||
query string,
|
||||
page, perPage *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultBlockSearch, error) {
|
||||
|
||||
result := new(ctypes.ResultBlockSearch)
|
||||
params := map[string]interface{}{
|
||||
"query": query,
|
||||
"order_by": orderBy,
|
||||
}
|
||||
|
||||
if page != nil {
|
||||
params["page"] = page
|
||||
}
|
||||
if perPage != nil {
|
||||
params["per_page"] = perPage
|
||||
}
|
||||
|
||||
_, err := c.caller.Call(ctx, "block_search", params, result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -70,8 +70,25 @@ type SignClient interface {
|
||||
Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error)
|
||||
Validators(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error)
|
||||
Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error)
|
||||
TxSearch(ctx context.Context, query string, prove bool, page, perPage *int,
|
||||
orderBy string) (*ctypes.ResultTxSearch, error)
|
||||
|
||||
// TxSearch defines a method to search for a paginated set of transactions by
|
||||
// DeliverTx event search criteria.
|
||||
TxSearch(
|
||||
ctx context.Context,
|
||||
query string,
|
||||
prove bool,
|
||||
page, perPage *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultTxSearch, error)
|
||||
|
||||
// BlockSearch defines a method to search for a paginated set of blocks by
|
||||
// BeginBlock and EndBlock event search criteria.
|
||||
BlockSearch(
|
||||
ctx context.Context,
|
||||
query string,
|
||||
page, perPage *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultBlockSearch, error)
|
||||
}
|
||||
|
||||
// HistoryClient provides access to data from genesis to now in large chunks.
|
||||
|
||||
@@ -178,7 +178,7 @@ func (c *Local) Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.Result
|
||||
}
|
||||
|
||||
func (c *Local) TxSearch(
|
||||
ctx context.Context,
|
||||
_ context.Context,
|
||||
query string,
|
||||
prove bool,
|
||||
page,
|
||||
@@ -188,6 +188,15 @@ func (c *Local) TxSearch(
|
||||
return core.TxSearch(c.ctx, query, prove, page, perPage, orderBy)
|
||||
}
|
||||
|
||||
func (c *Local) BlockSearch(
|
||||
_ context.Context,
|
||||
query string,
|
||||
page, perPage *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultBlockSearch, error) {
|
||||
return core.BlockSearch(c.ctx, query, page, perPage, orderBy)
|
||||
}
|
||||
|
||||
func (c *Local) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
|
||||
return core.BroadcastEvidence(c.ctx, ev)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v2.3.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.6.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -160,6 +160,29 @@ func (_m *Client) BlockResults(ctx context.Context, height *int64) (*coretypes.R
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// BlockSearch provides a mock function with given fields: ctx, query, page, perPage, orderBy
|
||||
func (_m *Client) BlockSearch(ctx context.Context, query string, page *int, perPage *int, orderBy string) (*coretypes.ResultBlockSearch, error) {
|
||||
ret := _m.Called(ctx, query, page, perPage, orderBy)
|
||||
|
||||
var r0 *coretypes.ResultBlockSearch
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *int, *int, string) *coretypes.ResultBlockSearch); ok {
|
||||
r0 = rf(ctx, query, page, perPage, orderBy)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*coretypes.ResultBlockSearch)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, *int, *int, string) error); ok {
|
||||
r1 = rf(ctx, query, page, perPage, orderBy)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// BlockchainInfo provides a mock function with given fields: ctx, minHeight, maxHeight
|
||||
func (_m *Client) BlockchainInfo(ctx context.Context, minHeight int64, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) {
|
||||
ret := _m.Called(ctx, minHeight, maxHeight)
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
tmquery "github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||
blockidxnull "github.com/tendermint/tendermint/state/indexer/block/null"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -154,3 +158,68 @@ func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockR
|
||||
ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BlockSearch searches for a paginated set of blocks matching BeginBlock and
|
||||
// EndBlock event search criteria.
|
||||
func BlockSearch(
|
||||
ctx *rpctypes.Context,
|
||||
query string,
|
||||
pagePtr, perPagePtr *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultBlockSearch, error) {
|
||||
|
||||
// skip if block indexing is disabled
|
||||
if _, ok := env.BlockIndexer.(*blockidxnull.BlockerIndexer); ok {
|
||||
return nil, errors.New("block indexing is disabled")
|
||||
}
|
||||
|
||||
q, err := tmquery.New(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results, err := env.BlockIndexer.Search(ctx.Context(), q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// sort results (must be done before pagination)
|
||||
switch orderBy {
|
||||
case "desc", "":
|
||||
sort.Slice(results, func(i, j int) bool { return results[i] > results[j] })
|
||||
|
||||
case "asc":
|
||||
sort.Slice(results, func(i, j int) bool { return results[i] < results[j] })
|
||||
|
||||
default:
|
||||
return nil, errors.New("expected order_by to be either `asc` or `desc` or empty")
|
||||
}
|
||||
|
||||
// paginate results
|
||||
totalCount := len(results)
|
||||
perPage := validatePerPage(perPagePtr)
|
||||
|
||||
page, err := validatePage(pagePtr, perPage, totalCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
skipCount := validateSkipCount(page, perPage)
|
||||
pageSize := tmmath.MinInt(perPage, totalCount-skipCount)
|
||||
|
||||
apiResults := make([]*ctypes.ResultBlock, 0, pageSize)
|
||||
for i := skipCount; i < skipCount+pageSize; i++ {
|
||||
block := env.BlockStore.LoadBlock(results[i])
|
||||
if block != nil {
|
||||
blockMeta := env.BlockStore.LoadBlockMeta(block.Height)
|
||||
if blockMeta != nil {
|
||||
apiResults = append(apiResults, &ctypes.ResultBlock{
|
||||
Block: block,
|
||||
BlockID: blockMeta.BlockID,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &ctypes.ResultBlockSearch{Blocks: apiResults, TotalCount: totalCount}, nil
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/state/indexer"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
@@ -82,6 +83,7 @@ type Environment struct {
|
||||
PubKey crypto.PubKey
|
||||
GenDoc *types.GenesisDoc // cache the genesis structure
|
||||
TxIndexer txindex.TxIndexer
|
||||
BlockIndexer indexer.BlockIndexer
|
||||
ConsensusReactor *consensus.Reactor
|
||||
EventBus *types.EventBus // thread safe
|
||||
Mempool mempl.Mempool
|
||||
|
||||
@@ -26,6 +26,7 @@ var Routes = map[string]*rpc.RPCFunc{
|
||||
"check_tx": rpc.NewRPCFunc(CheckTx, "tx"),
|
||||
"tx": rpc.NewRPCFunc(Tx, "hash,prove"),
|
||||
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by"),
|
||||
"block_search": rpc.NewRPCFunc(BlockSearch, "query,page,per_page,order_by"),
|
||||
"validators": rpc.NewRPCFunc(Validators, "height,page,per_page"),
|
||||
"dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""),
|
||||
"consensus_state": rpc.NewRPCFunc(ConsensusState, ""),
|
||||
|
||||
@@ -54,8 +54,14 @@ func Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error
|
||||
// TxSearch allows you to query for multiple transactions results. It returns a
|
||||
// list of transactions (maximum ?per_page entries) and the total count.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/tx_search
|
||||
func TxSearch(ctx *rpctypes.Context, query string, prove bool, pagePtr, perPagePtr *int, orderBy string) (
|
||||
*ctypes.ResultTxSearch, error) {
|
||||
func TxSearch(
|
||||
ctx *rpctypes.Context,
|
||||
query string,
|
||||
prove bool,
|
||||
pagePtr, perPagePtr *int,
|
||||
orderBy string,
|
||||
) (*ctypes.ResultTxSearch, error) {
|
||||
|
||||
// if index is disabled, return error
|
||||
if _, ok := env.TxIndexer.(*null.TxIndex); ok {
|
||||
return nil, errors.New("transaction indexing is disabled")
|
||||
@@ -94,10 +100,12 @@ func TxSearch(ctx *rpctypes.Context, query string, prove bool, pagePtr, perPageP
|
||||
// paginate results
|
||||
totalCount := len(results)
|
||||
perPage := validatePerPage(perPagePtr)
|
||||
|
||||
page, err := validatePage(pagePtr, perPage, totalCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
skipCount := validateSkipCount(page, perPage)
|
||||
pageSize := tmmath.MinInt(perPage, totalCount-skipCount)
|
||||
|
||||
|
||||
@@ -195,6 +195,12 @@ type ResultTxSearch struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
}
|
||||
|
||||
// ResultBlockSearch defines the RPC response type for a block search by events.
|
||||
type ResultBlockSearch struct {
|
||||
Blocks []*ResultBlock `json:"blocks"`
|
||||
TotalCount int `json:"total_count"`
|
||||
}
|
||||
|
||||
// List of mempool txs
|
||||
type ResultUnconfirmedTxs struct {
|
||||
Count int `json:"n_txs"`
|
||||
|
||||
@@ -1016,6 +1016,62 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
/block_search:
|
||||
get:
|
||||
summary: Search for blocks by BeginBlock and EndBlock events
|
||||
description: |
|
||||
Search for blocks by BeginBlock and EndBlock events.
|
||||
|
||||
See /subscribe for the query syntax.
|
||||
operationId: block_search
|
||||
parameters:
|
||||
- in: query
|
||||
name: query
|
||||
description: Query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: "block.height > 1000 AND valset.changed > 0"
|
||||
- in: query
|
||||
name: page
|
||||
description: "Page number (1-based)"
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
example: 1
|
||||
- in: query
|
||||
name: per_page
|
||||
description: "Number of entries per page (max: 100)"
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 30
|
||||
example: 30
|
||||
- in: query
|
||||
name: order_by
|
||||
description: Order in which blocks are sorted ("asc" or "desc"), by height. If empty, default sorting will be still applied.
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
default: "desc"
|
||||
example: "asc"
|
||||
tags:
|
||||
- Info
|
||||
responses:
|
||||
"200":
|
||||
description: List of paginated blocks matching the search criteria.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BlockSearchResponse"
|
||||
"500":
|
||||
description: Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
|
||||
/tx:
|
||||
get:
|
||||
summary: Get transactions by hash
|
||||
|
||||
22
state/indexer/block.go
Normal file
22
state/indexer/block.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// BlockIndexer defines an interface contract for indexing block events.
|
||||
type BlockIndexer interface {
|
||||
// Has returns true if the given height has been indexed. An error is returned
|
||||
// upon database query failure.
|
||||
Has(height int64) (bool, error)
|
||||
|
||||
// Index indexes BeginBlock and EndBlock events for a given block by its height.
|
||||
Index(types.EventDataNewBlockHeader) error
|
||||
|
||||
// Search performs a query for block heights that match a given BeginBlock
|
||||
// and Endblock event search criteria.
|
||||
Search(ctx context.Context, q *query.Query) ([]int64, error)
|
||||
}
|
||||
489
state/indexer/block/kv/kv.go
Normal file
489
state/indexer/block/kv/kv.go
Normal file
@@ -0,0 +1,489 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/orderedcode"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
"github.com/tendermint/tendermint/state/indexer"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var _ indexer.BlockIndexer = (*BlockerIndexer)(nil)
|
||||
|
||||
// BlockerIndexer implements a block indexer, indexing BeginBlock and EndBlock
|
||||
// events with an underlying KV store. Block events are indexed by their height,
|
||||
// such that matching search criteria returns the respective block height(s).
|
||||
type BlockerIndexer struct {
|
||||
store dbm.DB
|
||||
}
|
||||
|
||||
func New(store dbm.DB) *BlockerIndexer {
|
||||
return &BlockerIndexer{
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
// Has returns true if the given height has been indexed. An error is returned
|
||||
// upon database query failure.
|
||||
func (idx *BlockerIndexer) Has(height int64) (bool, error) {
|
||||
key, err := heightKey(height)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to create block height index key: %w", err)
|
||||
}
|
||||
|
||||
return idx.store.Has(key)
|
||||
}
|
||||
|
||||
// Index indexes BeginBlock and EndBlock events for a given block by its height.
|
||||
// The following is indexed:
|
||||
//
|
||||
// primary key: encode(block.height | height) => encode(height)
|
||||
// BeginBlock events: encode(eventType.eventAttr|eventValue|height|begin_block) => encode(height)
|
||||
// EndBlock events: encode(eventType.eventAttr|eventValue|height|end_block) => encode(height)
|
||||
func (idx *BlockerIndexer) Index(bh types.EventDataNewBlockHeader) error {
|
||||
batch := idx.store.NewBatch()
|
||||
defer batch.Close()
|
||||
|
||||
height := bh.Header.Height
|
||||
|
||||
// 1. index by height
|
||||
key, err := heightKey(height)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create block height index key: %w", err)
|
||||
}
|
||||
if err := batch.Set(key, int64ToBytes(height)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. index BeginBlock events
|
||||
if err := idx.indexEvents(batch, bh.ResultBeginBlock.Events, "begin_block", height); err != nil {
|
||||
return fmt.Errorf("failed to index BeginBlock events: %w", err)
|
||||
}
|
||||
|
||||
// 3. index EndBlock events
|
||||
if err := idx.indexEvents(batch, bh.ResultEndBlock.Events, "end_block", height); err != nil {
|
||||
return fmt.Errorf("failed to index EndBlock events: %w", err)
|
||||
}
|
||||
|
||||
return batch.WriteSync()
|
||||
}
|
||||
|
||||
// Search performs a query for block heights that match a given BeginBlock
|
||||
// and Endblock event search criteria. The given query can match against zero,
|
||||
// one or more block heights. In the case of height queries, i.e. block.height=H,
|
||||
// if the height is indexed, that height alone will be returned. An error and
|
||||
// nil slice is returned. Otherwise, a non-nil slice and nil error is returned.
|
||||
func (idx *BlockerIndexer) Search(ctx context.Context, q *query.Query) ([]int64, error) {
|
||||
results := make([]int64, 0)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return results, nil
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
conditions, err := q.Conditions()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse query conditions: %w", err)
|
||||
}
|
||||
|
||||
// If there is an exact height query, return the result immediately
|
||||
// (if it exists).
|
||||
height, ok := lookForHeight(conditions)
|
||||
if ok {
|
||||
ok, err := idx.Has(height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ok {
|
||||
return []int64{height}, nil
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
var heightsInitialized bool
|
||||
filteredHeights := make(map[string][]byte)
|
||||
|
||||
// conditions to skip because they're handled before "everything else"
|
||||
skipIndexes := make([]int, 0)
|
||||
|
||||
// Extract ranges. If both upper and lower bounds exist, it's better to get
|
||||
// them in order as to not iterate over kvs that are not within range.
|
||||
ranges, rangeIndexes := indexer.LookForRanges(conditions)
|
||||
if len(ranges) > 0 {
|
||||
skipIndexes = append(skipIndexes, rangeIndexes...)
|
||||
|
||||
for _, qr := range ranges {
|
||||
prefix, err := orderedcode.Append(nil, qr.Key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create prefix key: %w", err)
|
||||
}
|
||||
|
||||
if !heightsInitialized {
|
||||
filteredHeights, err = idx.matchRange(ctx, qr, prefix, filteredHeights, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
heightsInitialized = true
|
||||
|
||||
// Ignore any remaining conditions if the first condition resulted in no
|
||||
// matches (assuming implicit AND operand).
|
||||
if len(filteredHeights) == 0 {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
filteredHeights, err = idx.matchRange(ctx, qr, prefix, filteredHeights, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for all other conditions
|
||||
for i, c := range conditions {
|
||||
if intInSlice(i, skipIndexes) {
|
||||
continue
|
||||
}
|
||||
|
||||
startKey, err := orderedcode.Append(nil, c.CompositeKey, fmt.Sprintf("%v", c.Operand))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !heightsInitialized {
|
||||
filteredHeights, err = idx.match(ctx, c, startKey, filteredHeights, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
heightsInitialized = true
|
||||
|
||||
// Ignore any remaining conditions if the first condition resulted in no
|
||||
// matches (assuming implicit AND operand).
|
||||
if len(filteredHeights) == 0 {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
filteredHeights, err = idx.match(ctx, c, startKey, filteredHeights, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fetch matching heights
|
||||
results = make([]int64, 0, len(filteredHeights))
|
||||
for _, hBz := range filteredHeights {
|
||||
h := int64FromBytes(hBz)
|
||||
|
||||
ok, err := idx.Has(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
results = append(results, h)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(results, func(i, j int) bool { return results[i] < results[j] })
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// matchRange returns all matching block heights that match a given QueryRange
|
||||
// and start key. An already filtered result (filteredHeights) is provided such
|
||||
// that any non-intersecting matches are removed.
|
||||
//
|
||||
// NOTE: The provided filteredHeights may be empty if no previous condition has
|
||||
// matched.
|
||||
func (idx *BlockerIndexer) matchRange(
|
||||
ctx context.Context,
|
||||
qr indexer.QueryRange,
|
||||
startKey []byte,
|
||||
filteredHeights map[string][]byte,
|
||||
firstRun bool,
|
||||
) (map[string][]byte, error) {
|
||||
|
||||
// A previous match was attempted but resulted in no matches, so we return
|
||||
// no matches (assuming AND operand).
|
||||
if !firstRun && len(filteredHeights) == 0 {
|
||||
return filteredHeights, nil
|
||||
}
|
||||
|
||||
tmpHeights := make(map[string][]byte)
|
||||
lowerBound := qr.LowerBoundValue()
|
||||
upperBound := qr.UpperBoundValue()
|
||||
|
||||
it, err := dbm.IteratePrefix(idx.store, startKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create prefix iterator: %w", err)
|
||||
}
|
||||
defer it.Close()
|
||||
|
||||
LOOP:
|
||||
for ; it.Valid(); it.Next() {
|
||||
var (
|
||||
eventValue string
|
||||
err error
|
||||
)
|
||||
|
||||
if qr.Key == types.BlockHeightKey {
|
||||
eventValue, err = parseValueFromPrimaryKey(it.Key())
|
||||
} else {
|
||||
eventValue, err = parseValueFromEventKey(it.Key())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := qr.AnyBound().(int64); ok {
|
||||
v, err := strconv.ParseInt(eventValue, 10, 64)
|
||||
if err != nil {
|
||||
continue LOOP
|
||||
}
|
||||
|
||||
include := true
|
||||
if lowerBound != nil && v < lowerBound.(int64) {
|
||||
include = false
|
||||
}
|
||||
|
||||
if upperBound != nil && v > upperBound.(int64) {
|
||||
include = false
|
||||
}
|
||||
|
||||
if include {
|
||||
tmpHeights[string(it.Value())] = it.Value()
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
if err := it.Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(tmpHeights) == 0 || firstRun {
|
||||
// Either:
|
||||
//
|
||||
// 1. Regardless if a previous match was attempted, which may have had
|
||||
// results, but no match was found for the current condition, then we
|
||||
// return no matches (assuming AND operand).
|
||||
//
|
||||
// 2. A previous match was not attempted, so we return all results.
|
||||
return tmpHeights, nil
|
||||
}
|
||||
|
||||
// Remove/reduce matches in filteredHashes that were not found in this
|
||||
// match (tmpHashes).
|
||||
for k := range filteredHeights {
|
||||
if tmpHeights[k] == nil {
|
||||
delete(filteredHeights, k)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filteredHeights, nil
|
||||
}
|
||||
|
||||
// match returns all matching heights that meet a given query condition and start
|
||||
// key. An already filtered result (filteredHeights) is provided such that any
|
||||
// non-intersecting matches are removed.
|
||||
//
|
||||
// NOTE: The provided filteredHeights may be empty if no previous condition has
|
||||
// matched.
|
||||
func (idx *BlockerIndexer) match(
|
||||
ctx context.Context,
|
||||
c query.Condition,
|
||||
startKeyBz []byte,
|
||||
filteredHeights map[string][]byte,
|
||||
firstRun bool,
|
||||
) (map[string][]byte, error) {
|
||||
|
||||
// A previous match was attempted but resulted in no matches, so we return
|
||||
// no matches (assuming AND operand).
|
||||
if !firstRun && len(filteredHeights) == 0 {
|
||||
return filteredHeights, nil
|
||||
}
|
||||
|
||||
tmpHeights := make(map[string][]byte)
|
||||
|
||||
switch {
|
||||
case c.Op == query.OpEqual:
|
||||
it, err := dbm.IteratePrefix(idx.store, startKeyBz)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create prefix iterator: %w", err)
|
||||
}
|
||||
defer it.Close()
|
||||
|
||||
for ; it.Valid(); it.Next() {
|
||||
tmpHeights[string(it.Value())] = it.Value()
|
||||
|
||||
if err := ctx.Err(); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := it.Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case c.Op == query.OpExists:
|
||||
prefix, err := orderedcode.Append(nil, c.CompositeKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
it, err := dbm.IteratePrefix(idx.store, prefix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create prefix iterator: %w", err)
|
||||
}
|
||||
defer it.Close()
|
||||
|
||||
for ; it.Valid(); it.Next() {
|
||||
tmpHeights[string(it.Value())] = it.Value()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
if err := it.Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case c.Op == query.OpContains:
|
||||
prefix, err := orderedcode.Append(nil, c.CompositeKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
it, err := dbm.IteratePrefix(idx.store, prefix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create prefix iterator: %w", err)
|
||||
}
|
||||
defer it.Close()
|
||||
|
||||
for ; it.Valid(); it.Next() {
|
||||
eventValue, err := parseValueFromEventKey(it.Key())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(eventValue, c.Operand.(string)) {
|
||||
tmpHeights[string(it.Value())] = it.Value()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
if err := it.Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New("other operators should be handled already")
|
||||
}
|
||||
|
||||
if len(tmpHeights) == 0 || firstRun {
|
||||
// Either:
|
||||
//
|
||||
// 1. Regardless if a previous match was attempted, which may have had
|
||||
// results, but no match was found for the current condition, then we
|
||||
// return no matches (assuming AND operand).
|
||||
//
|
||||
// 2. A previous match was not attempted, so we return all results.
|
||||
return tmpHeights, nil
|
||||
}
|
||||
|
||||
// Remove/reduce matches in filteredHeights that were not found in this
|
||||
// match (tmpHeights).
|
||||
for k := range filteredHeights {
|
||||
if tmpHeights[k] == nil {
|
||||
delete(filteredHeights, k)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filteredHeights, nil
|
||||
}
|
||||
|
||||
func (idx *BlockerIndexer) indexEvents(batch dbm.Batch, events []abci.Event, typ string, height int64) error {
|
||||
heightBz := int64ToBytes(height)
|
||||
|
||||
for _, event := range events {
|
||||
// only index events with a non-empty type
|
||||
if len(event.Type) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, attr := range event.Attributes {
|
||||
if len(attr.Key) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// index iff the event specified index:true and it's not a reserved event
|
||||
compositeKey := fmt.Sprintf("%s.%s", event.Type, string(attr.Key))
|
||||
if compositeKey == types.TxHashKey || compositeKey == types.TxHeightKey {
|
||||
return fmt.Errorf("event type and attribute key \"%s\" is reserved; please use a different key", compositeKey)
|
||||
}
|
||||
if attr.GetIndex() {
|
||||
key, err := eventKey(compositeKey, typ, string(attr.Value), height)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create block index key: %w", err)
|
||||
}
|
||||
|
||||
if err := batch.Set(key, heightBz); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
141
state/indexer/block/kv/kv_test.go
Normal file
141
state/indexer/block/kv/kv_test.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package kv_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
blockidxkv "github.com/tendermint/tendermint/state/indexer/block/kv"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
db "github.com/tendermint/tm-db"
|
||||
)
|
||||
|
||||
func TestBlockIndexer(t *testing.T) {
|
||||
store := db.NewPrefixDB(db.NewMemDB(), []byte("block_events"))
|
||||
indexer := blockidxkv.New(store)
|
||||
|
||||
require.NoError(t, indexer.Index(types.EventDataNewBlockHeader{
|
||||
Header: types.Header{Height: 1},
|
||||
ResultBeginBlock: abci.ResponseBeginBlock{
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "begin_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: []byte("proposer"),
|
||||
Value: []byte("FCAA001"),
|
||||
Index: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ResultEndBlock: abci.ResponseEndBlock{
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "end_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte("100"),
|
||||
Index: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
for i := 2; i < 12; i++ {
|
||||
var index bool
|
||||
if i%2 == 0 {
|
||||
index = true
|
||||
}
|
||||
|
||||
require.NoError(t, indexer.Index(types.EventDataNewBlockHeader{
|
||||
Header: types.Header{Height: int64(i)},
|
||||
ResultBeginBlock: abci.ResponseBeginBlock{
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "begin_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: []byte("proposer"),
|
||||
Value: []byte("FCAA001"),
|
||||
Index: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ResultEndBlock: abci.ResponseEndBlock{
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "end_event",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: []byte("foo"),
|
||||
Value: []byte(fmt.Sprintf("%d", i)),
|
||||
Index: index,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
q *query.Query
|
||||
results []int64
|
||||
}{
|
||||
"block.height = 100": {
|
||||
q: query.MustParse("block.height = 100"),
|
||||
results: []int64{},
|
||||
},
|
||||
"block.height = 5": {
|
||||
q: query.MustParse("block.height = 5"),
|
||||
results: []int64{5},
|
||||
},
|
||||
"begin_event.key1 = 'value1'": {
|
||||
q: query.MustParse("begin_event.key1 = 'value1'"),
|
||||
results: []int64{},
|
||||
},
|
||||
"begin_event.proposer = 'FCAA001'": {
|
||||
q: query.MustParse("begin_event.proposer = 'FCAA001'"),
|
||||
results: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
|
||||
},
|
||||
"end_event.foo <= 5": {
|
||||
q: query.MustParse("end_event.foo <= 5"),
|
||||
results: []int64{2, 4},
|
||||
},
|
||||
"end_event.foo >= 100": {
|
||||
q: query.MustParse("end_event.foo >= 100"),
|
||||
results: []int64{1},
|
||||
},
|
||||
"block.height > 2 AND end_event.foo <= 8": {
|
||||
q: query.MustParse("block.height > 2 AND end_event.foo <= 8"),
|
||||
results: []int64{4, 6, 8},
|
||||
},
|
||||
"begin_event.proposer CONTAINS 'FFFFFFF'": {
|
||||
q: query.MustParse("begin_event.proposer CONTAINS 'FFFFFFF'"),
|
||||
results: []int64{},
|
||||
},
|
||||
"begin_event.proposer CONTAINS 'FCAA001'": {
|
||||
q: query.MustParse("begin_event.proposer CONTAINS 'FCAA001'"),
|
||||
results: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(name, func(t *testing.T) {
|
||||
results, err := indexer.Search(context.Background(), tc.q)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.results, results)
|
||||
})
|
||||
}
|
||||
}
|
||||
96
state/indexer/block/kv/util.go
Normal file
96
state/indexer/block/kv/util.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/orderedcode"
|
||||
"github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func intInSlice(a int, list []int) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func int64FromBytes(bz []byte) int64 {
|
||||
v, _ := binary.Varint(bz)
|
||||
return v
|
||||
}
|
||||
|
||||
func int64ToBytes(i int64) []byte {
|
||||
buf := make([]byte, binary.MaxVarintLen64)
|
||||
n := binary.PutVarint(buf, i)
|
||||
return buf[:n]
|
||||
}
|
||||
|
||||
func heightKey(height int64) ([]byte, error) {
|
||||
return orderedcode.Append(
|
||||
nil,
|
||||
types.BlockHeightKey,
|
||||
height,
|
||||
)
|
||||
}
|
||||
|
||||
func eventKey(compositeKey, typ, eventValue string, height int64) ([]byte, error) {
|
||||
return orderedcode.Append(
|
||||
nil,
|
||||
compositeKey,
|
||||
eventValue,
|
||||
height,
|
||||
typ,
|
||||
)
|
||||
}
|
||||
|
||||
func parseValueFromPrimaryKey(key []byte) (string, error) {
|
||||
var (
|
||||
compositeKey string
|
||||
height int64
|
||||
)
|
||||
|
||||
remaining, err := orderedcode.Parse(string(key), &compositeKey, &height)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse event key: %w", err)
|
||||
}
|
||||
|
||||
if len(remaining) != 0 {
|
||||
return "", fmt.Errorf("unexpected remainder in key: %s", remaining)
|
||||
}
|
||||
|
||||
return strconv.FormatInt(height, 10), nil
|
||||
}
|
||||
|
||||
func parseValueFromEventKey(key []byte) (string, error) {
|
||||
var (
|
||||
compositeKey, typ, eventValue string
|
||||
height int64
|
||||
)
|
||||
|
||||
remaining, err := orderedcode.Parse(string(key), &compositeKey, &eventValue, &height, &typ)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse event key: %w", err)
|
||||
}
|
||||
|
||||
if len(remaining) != 0 {
|
||||
return "", fmt.Errorf("unexpected remainder in key: %s", remaining)
|
||||
}
|
||||
|
||||
return eventValue, nil
|
||||
}
|
||||
|
||||
func lookForHeight(conditions []query.Condition) (int64, bool) {
|
||||
for _, c := range conditions {
|
||||
if c.CompositeKey == types.BlockHeightKey && c.Op == query.OpEqual {
|
||||
return c.Operand.(int64), true
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
27
state/indexer/block/null/null.go
Normal file
27
state/indexer/block/null/null.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package null
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
"github.com/tendermint/tendermint/state/indexer"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var _ indexer.BlockIndexer = (*BlockerIndexer)(nil)
|
||||
|
||||
// TxIndex implements a no-op block indexer.
|
||||
type BlockerIndexer struct{}
|
||||
|
||||
func (idx *BlockerIndexer) Has(height int64) (bool, error) {
|
||||
return false, errors.New(`indexing is disabled (set 'tx_index = "kv"' in config)`)
|
||||
}
|
||||
|
||||
func (idx *BlockerIndexer) Index(types.EventDataNewBlockHeader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (idx *BlockerIndexer) Search(ctx context.Context, q *query.Query) ([]int64, error) {
|
||||
return []int64{}, nil
|
||||
}
|
||||
123
state/indexer/query_range.go
Normal file
123
state/indexer/query_range.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
)
|
||||
|
||||
// QueryRanges defines a mapping between a composite event key and a QueryRange.
|
||||
//
|
||||
// e.g.account.number => queryRange{lowerBound: 1, upperBound: 5}
|
||||
type QueryRanges map[string]QueryRange
|
||||
|
||||
// QueryRange defines a range within a query condition.
|
||||
type QueryRange struct {
|
||||
LowerBound interface{} // int || time.Time
|
||||
UpperBound interface{} // int || time.Time
|
||||
Key string
|
||||
IncludeLowerBound bool
|
||||
IncludeUpperBound bool
|
||||
}
|
||||
|
||||
// AnyBound returns either the lower bound if non-nil, otherwise the upper bound.
|
||||
func (qr QueryRange) AnyBound() interface{} {
|
||||
if qr.LowerBound != nil {
|
||||
return qr.LowerBound
|
||||
}
|
||||
|
||||
return qr.UpperBound
|
||||
}
|
||||
|
||||
// LowerBoundValue returns the value for the lower bound. If the lower bound is
|
||||
// nil, nil will be returned.
|
||||
func (qr QueryRange) LowerBoundValue() interface{} {
|
||||
if qr.LowerBound == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if qr.IncludeLowerBound {
|
||||
return qr.LowerBound
|
||||
}
|
||||
|
||||
switch t := qr.LowerBound.(type) {
|
||||
case int64:
|
||||
return t + 1
|
||||
|
||||
case time.Time:
|
||||
return t.Unix() + 1
|
||||
|
||||
default:
|
||||
panic("not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
// UpperBoundValue returns the value for the upper bound. If the upper bound is
|
||||
// nil, nil will be returned.
|
||||
func (qr QueryRange) UpperBoundValue() interface{} {
|
||||
if qr.UpperBound == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if qr.IncludeUpperBound {
|
||||
return qr.UpperBound
|
||||
}
|
||||
|
||||
switch t := qr.UpperBound.(type) {
|
||||
case int64:
|
||||
return t - 1
|
||||
|
||||
case time.Time:
|
||||
return t.Unix() - 1
|
||||
|
||||
default:
|
||||
panic("not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
// LookForRanges returns a mapping of QueryRanges and the matching indexes in
|
||||
// the provided query conditions.
|
||||
func LookForRanges(conditions []query.Condition) (ranges QueryRanges, indexes []int) {
|
||||
ranges = make(QueryRanges)
|
||||
for i, c := range conditions {
|
||||
if IsRangeOperation(c.Op) {
|
||||
r, ok := ranges[c.CompositeKey]
|
||||
if !ok {
|
||||
r = QueryRange{Key: c.CompositeKey}
|
||||
}
|
||||
|
||||
switch c.Op {
|
||||
case query.OpGreater:
|
||||
r.LowerBound = c.Operand
|
||||
|
||||
case query.OpGreaterEqual:
|
||||
r.IncludeLowerBound = true
|
||||
r.LowerBound = c.Operand
|
||||
|
||||
case query.OpLess:
|
||||
r.UpperBound = c.Operand
|
||||
|
||||
case query.OpLessEqual:
|
||||
r.IncludeUpperBound = true
|
||||
r.UpperBound = c.Operand
|
||||
}
|
||||
|
||||
ranges[c.CompositeKey] = r
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
}
|
||||
|
||||
return ranges, indexes
|
||||
}
|
||||
|
||||
// IsRangeOperation returns a boolean signifying if a query Operator is a range
|
||||
// operation or not.
|
||||
func IsRangeOperation(op query.Operator) bool {
|
||||
switch op {
|
||||
case query.OpGreater, query.OpGreaterEqual, query.OpLess, query.OpLessEqual:
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,10 @@ import (
|
||||
"github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
)
|
||||
|
||||
// XXX/TODO: These types should be moved to the indexer package.
|
||||
|
||||
// TxIndexer interface defines methods to index and search transactions.
|
||||
type TxIndexer interface {
|
||||
|
||||
// AddBatch analyzes, indexes and stores a batch of transactions.
|
||||
AddBatch(b *Batch) error
|
||||
|
||||
@@ -25,9 +26,6 @@ type TxIndexer interface {
|
||||
Search(ctx context.Context, q *query.Query) ([]*abci.TxResult, error)
|
||||
}
|
||||
|
||||
//----------------------------------------------------
|
||||
// Txs are written as a batch
|
||||
|
||||
// Batch groups together multiple Index operations to be performed at the same time.
|
||||
// NOTE: Batch is NOT thread-safe and must not be modified after starting its execution.
|
||||
type Batch struct {
|
||||
@@ -52,8 +50,5 @@ func (b *Batch) Size() int {
|
||||
return len(b.Ops)
|
||||
}
|
||||
|
||||
//----------------------------------------------------
|
||||
// Errors
|
||||
|
||||
// ErrorEmptyHash indicates empty hash
|
||||
var ErrorEmptyHash = errors.New("transaction hash cannot be empty")
|
||||
|
||||
@@ -4,26 +4,34 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
|
||||
"github.com/tendermint/tendermint/state/indexer"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// XXX/TODO: These types should be moved to the indexer package.
|
||||
|
||||
const (
|
||||
subscriber = "IndexerService"
|
||||
)
|
||||
|
||||
// IndexerService connects event bus and transaction indexer together in order
|
||||
// to index transactions coming from event bus.
|
||||
// IndexerService connects event bus, transaction and block indexers together in
|
||||
// order to index transactions and blocks coming from the event bus.
|
||||
type IndexerService struct {
|
||||
service.BaseService
|
||||
|
||||
idr TxIndexer
|
||||
eventBus *types.EventBus
|
||||
txIdxr TxIndexer
|
||||
blockIdxr indexer.BlockIndexer
|
||||
eventBus *types.EventBus
|
||||
}
|
||||
|
||||
// NewIndexerService returns a new service instance.
|
||||
func NewIndexerService(idr TxIndexer, eventBus *types.EventBus) *IndexerService {
|
||||
is := &IndexerService{idr: idr, eventBus: eventBus}
|
||||
func NewIndexerService(
|
||||
txIdxr TxIndexer,
|
||||
blockIdxr indexer.BlockIndexer,
|
||||
eventBus *types.EventBus,
|
||||
) *IndexerService {
|
||||
|
||||
is := &IndexerService{txIdxr: txIdxr, blockIdxr: blockIdxr, eventBus: eventBus}
|
||||
is.BaseService = *service.NewBaseService(nil, "IndexerService", is)
|
||||
return is
|
||||
}
|
||||
@@ -34,7 +42,6 @@ func (is *IndexerService) OnStart() error {
|
||||
// Use SubscribeUnbuffered here to ensure both subscriptions does not get
|
||||
// cancelled due to not pulling messages fast enough. Cause this might
|
||||
// sometimes happen when there are no other subscribers.
|
||||
|
||||
blockHeadersSub, err := is.eventBus.SubscribeUnbuffered(
|
||||
context.Background(),
|
||||
subscriber,
|
||||
@@ -54,20 +61,31 @@ func (is *IndexerService) OnStart() error {
|
||||
eventDataHeader := msg.Data().(types.EventDataNewBlockHeader)
|
||||
height := eventDataHeader.Header.Height
|
||||
batch := NewBatch(eventDataHeader.NumTxs)
|
||||
|
||||
for i := int64(0); i < eventDataHeader.NumTxs; i++ {
|
||||
msg2 := <-txsSub.Out()
|
||||
txResult := msg2.Data().(types.EventDataTx).TxResult
|
||||
|
||||
if err = batch.Add(&txResult); err != nil {
|
||||
is.Logger.Error("Can't add tx to batch",
|
||||
is.Logger.Error(
|
||||
"failed to add tx to batch",
|
||||
"height", height,
|
||||
"index", txResult.Index,
|
||||
"err", err)
|
||||
"err", err,
|
||||
)
|
||||
}
|
||||
}
|
||||
if err = is.idr.AddBatch(batch); err != nil {
|
||||
is.Logger.Error("Failed to index block", "height", height, "err", err)
|
||||
|
||||
if err := is.blockIdxr.Index(eventDataHeader); err != nil {
|
||||
is.Logger.Error("failed to index block", "height", height, "err", err)
|
||||
} else {
|
||||
is.Logger.Debug("indexed block", "height", height)
|
||||
is.Logger.Error("indexed block", "height", height)
|
||||
}
|
||||
|
||||
if err = is.txIdxr.AddBatch(batch); err != nil {
|
||||
is.Logger.Error("failed to index block txs", "height", height, "err", err)
|
||||
} else {
|
||||
is.Logger.Debug("indexed block txs", "height", height, "num_txs", eventDataHeader.NumTxs)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -4,13 +4,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
db "github.com/tendermint/tm-db"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
blockidxkv "github.com/tendermint/tendermint/state/indexer/block/kv"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/state/txindex/kv"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
@@ -31,8 +30,9 @@ func TestIndexerServiceIndexesBlocks(t *testing.T) {
|
||||
// tx indexer
|
||||
store := db.NewMemDB()
|
||||
txIndexer := kv.NewTxIndex(store)
|
||||
blockIndexer := blockidxkv.New(db.NewPrefixDB(store, []byte("block_events")))
|
||||
|
||||
service := txindex.NewIndexerService(txIndexer, eventBus)
|
||||
service := txindex.NewIndexerService(txIndexer, blockIndexer, eventBus)
|
||||
service.SetLogger(log.TestingLogger())
|
||||
err = service.Start()
|
||||
require.NoError(t, err)
|
||||
@@ -67,11 +67,15 @@ func TestIndexerServiceIndexesBlocks(t *testing.T) {
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// check the result
|
||||
res, err := txIndexer.Get(types.Tx("foo").Hash())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, txResult1, res)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, txResult1, res)
|
||||
|
||||
ok, err := blockIndexer.Has(1)
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
|
||||
res, err = txIndexer.Get(types.Tx("bar").Hash())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, txResult2, res)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, txResult2, res)
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/pubsub/query"
|
||||
"github.com/tendermint/tendermint/state/indexer"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
@@ -170,11 +170,10 @@ func (txi *TxIndex) indexEvents(result *abci.TxResult, hash []byte, store dbm.Ba
|
||||
// Search will exit early and return any result fetched so far,
|
||||
// when a message is received on the context chan.
|
||||
func (txi *TxIndex) Search(ctx context.Context, q *query.Query) ([]*abci.TxResult, error) {
|
||||
// Potentially exit early.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
results := make([]*abci.TxResult, 0)
|
||||
return results, nil
|
||||
return make([]*abci.TxResult, 0), nil
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -209,13 +208,13 @@ func (txi *TxIndex) Search(ctx context.Context, q *query.Query) ([]*abci.TxResul
|
||||
// extract ranges
|
||||
// if both upper and lower bounds exist, it's better to get them in order not
|
||||
// no iterate over kvs that are not within range.
|
||||
ranges, rangeIndexes := lookForRanges(conditions)
|
||||
ranges, rangeIndexes := indexer.LookForRanges(conditions)
|
||||
if len(ranges) > 0 {
|
||||
skipIndexes = append(skipIndexes, rangeIndexes...)
|
||||
|
||||
for _, r := range ranges {
|
||||
for _, qr := range ranges {
|
||||
if !hashesInitialized {
|
||||
filteredHashes = txi.matchRange(ctx, r, startKey(r.key), filteredHashes, true)
|
||||
filteredHashes = txi.matchRange(ctx, qr, startKey(qr.Key), filteredHashes, true)
|
||||
hashesInitialized = true
|
||||
|
||||
// Ignore any remaining conditions if the first condition resulted
|
||||
@@ -224,7 +223,7 @@ func (txi *TxIndex) Search(ctx context.Context, q *query.Query) ([]*abci.TxResul
|
||||
break
|
||||
}
|
||||
} else {
|
||||
filteredHashes = txi.matchRange(ctx, r, startKey(r.key), filteredHashes, false)
|
||||
filteredHashes = txi.matchRange(ctx, qr, startKey(qr.Key), filteredHashes, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,100 +290,6 @@ func lookForHeight(conditions []query.Condition) (height int64) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// special map to hold range conditions
|
||||
// Example: account.number => queryRange{lowerBound: 1, upperBound: 5}
|
||||
type queryRanges map[string]queryRange
|
||||
|
||||
type queryRange struct {
|
||||
lowerBound interface{} // int || time.Time
|
||||
upperBound interface{} // int || time.Time
|
||||
key string
|
||||
includeLowerBound bool
|
||||
includeUpperBound bool
|
||||
}
|
||||
|
||||
func (r queryRange) lowerBoundValue() interface{} {
|
||||
if r.lowerBound == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.includeLowerBound {
|
||||
return r.lowerBound
|
||||
}
|
||||
|
||||
switch t := r.lowerBound.(type) {
|
||||
case int64:
|
||||
return t + 1
|
||||
case time.Time:
|
||||
return t.Unix() + 1
|
||||
default:
|
||||
panic("not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
func (r queryRange) AnyBound() interface{} {
|
||||
if r.lowerBound != nil {
|
||||
return r.lowerBound
|
||||
}
|
||||
|
||||
return r.upperBound
|
||||
}
|
||||
|
||||
func (r queryRange) upperBoundValue() interface{} {
|
||||
if r.upperBound == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.includeUpperBound {
|
||||
return r.upperBound
|
||||
}
|
||||
|
||||
switch t := r.upperBound.(type) {
|
||||
case int64:
|
||||
return t - 1
|
||||
case time.Time:
|
||||
return t.Unix() - 1
|
||||
default:
|
||||
panic("not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
func lookForRanges(conditions []query.Condition) (ranges queryRanges, indexes []int) {
|
||||
ranges = make(queryRanges)
|
||||
for i, c := range conditions {
|
||||
if isRangeOperation(c.Op) {
|
||||
r, ok := ranges[c.CompositeKey]
|
||||
if !ok {
|
||||
r = queryRange{key: c.CompositeKey}
|
||||
}
|
||||
switch c.Op {
|
||||
case query.OpGreater:
|
||||
r.lowerBound = c.Operand
|
||||
case query.OpGreaterEqual:
|
||||
r.includeLowerBound = true
|
||||
r.lowerBound = c.Operand
|
||||
case query.OpLess:
|
||||
r.upperBound = c.Operand
|
||||
case query.OpLessEqual:
|
||||
r.includeUpperBound = true
|
||||
r.upperBound = c.Operand
|
||||
}
|
||||
ranges[c.CompositeKey] = r
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
}
|
||||
return ranges, indexes
|
||||
}
|
||||
|
||||
func isRangeOperation(op query.Operator) bool {
|
||||
switch op {
|
||||
case query.OpGreater, query.OpGreaterEqual, query.OpLess, query.OpLessEqual:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// match returns all matching txs by hash that meet a given condition and start
|
||||
// key. An already filtered result (filteredHashes) is provided such that any
|
||||
// non-intersecting matches are removed.
|
||||
@@ -519,7 +424,7 @@ func (txi *TxIndex) match(
|
||||
// NOTE: filteredHashes may be empty if no previous condition has matched.
|
||||
func (txi *TxIndex) matchRange(
|
||||
ctx context.Context,
|
||||
r queryRange,
|
||||
qr indexer.QueryRange,
|
||||
startKey []byte,
|
||||
filteredHashes map[string][]byte,
|
||||
firstRun bool,
|
||||
@@ -531,8 +436,8 @@ func (txi *TxIndex) matchRange(
|
||||
}
|
||||
|
||||
tmpHashes := make(map[string][]byte)
|
||||
lowerBound := r.lowerBoundValue()
|
||||
upperBound := r.upperBoundValue()
|
||||
lowerBound := qr.LowerBoundValue()
|
||||
upperBound := qr.UpperBoundValue()
|
||||
|
||||
it, err := dbm.IteratePrefix(txi.store, startKey)
|
||||
if err != nil {
|
||||
@@ -546,7 +451,7 @@ LOOP:
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := r.AnyBound().(int64); ok {
|
||||
if _, ok := qr.AnyBound().(int64); ok {
|
||||
v, err := strconv.ParseInt(extractValueFromKey(it.Key()), 10, 64)
|
||||
if err != nil {
|
||||
continue LOOP
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/code"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@@ -98,12 +99,29 @@ func (app *Application) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDelive
|
||||
|
||||
// EndBlock implements ABCI.
|
||||
func (app *Application) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
|
||||
var err error
|
||||
resp := abci.ResponseEndBlock{}
|
||||
if resp.ValidatorUpdates, err = app.validatorUpdates(uint64(req.Height)); err != nil {
|
||||
valUpdates, err := app.validatorUpdates(uint64(req.Height))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return resp
|
||||
|
||||
return abci.ResponseEndBlock{
|
||||
ValidatorUpdates: valUpdates,
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "val_updates",
|
||||
Attributes: []abci.EventAttribute{
|
||||
{
|
||||
Key: []byte("size"),
|
||||
Value: []byte(strconv.Itoa(valUpdates.Len())),
|
||||
},
|
||||
{
|
||||
Key: []byte("height"),
|
||||
Value: []byte(strconv.Itoa(int(req.Height))),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Commit implements ABCI.
|
||||
|
||||
@@ -40,6 +40,9 @@ import (
|
||||
grpccore "github.com/tendermint/tendermint/rpc/grpc"
|
||||
rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/state/indexer"
|
||||
blockidxkv "github.com/tendermint/tendermint/state/indexer/block/kv"
|
||||
blockidxnull "github.com/tendermint/tendermint/state/indexer/block/null"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/state/txindex/kv"
|
||||
"github.com/tendermint/tendermint/state/txindex/null"
|
||||
@@ -248,6 +251,7 @@ type Node struct {
|
||||
proxyApp proxy.AppConns // connection to the application
|
||||
rpcListeners []net.Listener // rpc servers
|
||||
txIndexer txindex.TxIndexer
|
||||
blockIndexer indexer.BlockIndexer
|
||||
indexerService *txindex.IndexerService
|
||||
prometheusSrv *http.Server
|
||||
}
|
||||
@@ -286,27 +290,40 @@ func createAndStartEventBus(logger log.Logger) (*types.EventBus, error) {
|
||||
return eventBus, nil
|
||||
}
|
||||
|
||||
func createAndStartIndexerService(config *cfg.Config, dbProvider DBProvider,
|
||||
eventBus *types.EventBus, logger log.Logger) (*txindex.IndexerService, txindex.TxIndexer, error) {
|
||||
func createAndStartIndexerService(
|
||||
config *cfg.Config,
|
||||
dbProvider DBProvider,
|
||||
eventBus *types.EventBus,
|
||||
logger log.Logger,
|
||||
) (*txindex.IndexerService, txindex.TxIndexer, indexer.BlockIndexer, error) {
|
||||
|
||||
var (
|
||||
txIndexer txindex.TxIndexer
|
||||
blockIndexer indexer.BlockIndexer
|
||||
)
|
||||
|
||||
var txIndexer txindex.TxIndexer
|
||||
switch config.TxIndex.Indexer {
|
||||
case "kv":
|
||||
store, err := dbProvider(&DBContext{"tx_index", config})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
txIndexer = kv.NewTxIndex(store)
|
||||
blockIndexer = blockidxkv.New(dbm.NewPrefixDB(store, []byte("block_events")))
|
||||
default:
|
||||
txIndexer = &null.TxIndex{}
|
||||
blockIndexer = &blockidxnull.BlockerIndexer{}
|
||||
}
|
||||
|
||||
indexerService := txindex.NewIndexerService(txIndexer, eventBus)
|
||||
indexerService := txindex.NewIndexerService(txIndexer, blockIndexer, eventBus)
|
||||
indexerService.SetLogger(logger.With("module", "txindex"))
|
||||
|
||||
if err := indexerService.Start(); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return indexerService, txIndexer, nil
|
||||
|
||||
return indexerService, txIndexer, blockIndexer, nil
|
||||
}
|
||||
|
||||
func doHandshake(
|
||||
@@ -700,8 +717,7 @@ func NewNode(config *cfg.Config,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Transaction indexing
|
||||
indexerService, txIndexer, err := createAndStartIndexerService(config, dbProvider, eventBus, logger)
|
||||
indexerService, txIndexer, blockIndexer, err := createAndStartIndexerService(config, dbProvider, eventBus, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -877,6 +893,7 @@ func NewNode(config *cfg.Config,
|
||||
proxyApp: proxyApp,
|
||||
txIndexer: txIndexer,
|
||||
indexerService: indexerService,
|
||||
blockIndexer: blockIndexer,
|
||||
eventBus: eventBus,
|
||||
}
|
||||
node.BaseService = *service.NewBaseService(logger, "Node", node)
|
||||
@@ -1033,6 +1050,7 @@ func (n *Node) ConfigureRPC() error {
|
||||
PubKey: pubKey,
|
||||
GenDoc: n.genesisDoc,
|
||||
TxIndexer: n.txIndexer,
|
||||
BlockIndexer: n.blockIndexer,
|
||||
ConsensusReactor: &consensus.Reactor{},
|
||||
EventBus: n.eventBus,
|
||||
Mempool: n.mempool,
|
||||
|
||||
@@ -136,6 +136,10 @@ const (
|
||||
// TxHeightKey is a reserved key, used to specify transaction block's height.
|
||||
// see EventBus#PublishEventTx
|
||||
TxHeightKey = "tx.height"
|
||||
|
||||
// BlockHeightKey is a reserved key used for indexing BeginBlock and Endblock
|
||||
// events.
|
||||
BlockHeightKey = "block.height"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
Reference in New Issue
Block a user