feat: Add Table-Store (aka ORM) package - Index and Iterator (#10451)

<!--
The default pull request template is for types feat, fix, or refactor.
For other templates, add one of the following parameters to the url:
- template=docs.md
- template=other.md
-->

## Description

ref: #9237, #9156

This PR is a follow-up of #10415 and #9751.
It adds multi-key secondary indexes, iterator and pagination support.

There will be one last follow-up PR for adding import/export genesis features.

---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [x] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [x] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [x] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [x] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
This commit is contained in:
Marie Gauthier 2021-11-09 19:01:27 +01:00 committed by GitHub
parent b9c5a0578b
commit c5b879a03d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 3232 additions and 155 deletions

View File

@ -13,12 +13,6 @@ const RootCodespace = "sdk"
// UndefinedCodespace when we explicitly declare no codespace
const UndefinedCodespace = "undefined"
// mathCodespace is the codespace for all errors defined in math package
const mathCodespace = "math"
// mathCodespace is the codespace for all errors defined in orm package
const ormCodespace = "orm"
var (
// errInternal should never be exposed, but we reserve this code for non-specified errors
errInternal = Register(UndefinedCodespace, 1, "internal")
@ -153,27 +147,6 @@ var (
// ErrAppConfig defines an error occurred if min-gas-prices field in BaseConfig is empty.
ErrAppConfig = Register(RootCodespace, 40, "error in app.toml")
// ErrInvalidDecString defines an error for an invalid decimal string
ErrInvalidDecString = Register(mathCodespace, 41, "invalid decimal string")
// ErrORMIteratorDone defines an error when an iterator is done
ErrORMIteratorDone = Register(ormCodespace, 42, "iterator done")
// ErrORMInvalidIterator defines an error for an invalid iterator
ErrORMInvalidIterator = Register(ormCodespace, 43, "invalid iterator")
// ErrORMUniqueConstraint defines an error when a value already exists at a given key
ErrORMUniqueConstraint = Register(ormCodespace, 44, "unique constraint violation")
// ErrORMEmptyModel defines an error when an empty model is provided for building a table
ErrORMEmptyModel = Register(ormCodespace, 45, "invalid argument")
// ErrORMKeyMaxLength defines an error when a key exceeds max length
ErrORMKeyMaxLength = Register(ormCodespace, 46, "key exceeds max length")
// ErrORMEmptyKey defines an error for an empty key
ErrORMEmptyKey = Register(ormCodespace, 47, "cannot use empty key")
)
// Register returns an error instance that should be used as the base for

9
x/group/errors/math.go Normal file
View File

@ -0,0 +1,9 @@
package errors
import "github.com/cosmos/cosmos-sdk/types/errors"
// mathCodespace is the codespace for all errors defined in math package
const mathCodespace = "math"
// ErrInvalidDecString defines an error for an invalid decimal string
var ErrInvalidDecString = errors.Register(mathCodespace, 10, "invalid decimal string")

28
x/group/errors/orm.go Normal file
View File

@ -0,0 +1,28 @@
package errors
import (
"github.com/cosmos/cosmos-sdk/types/errors"
)
// mathCodespace is the codespace for all errors defined in orm package
const ormCodespace = "orm"
var (
// ErrORMIteratorDone defines an error when an iterator is done
ErrORMIteratorDone = errors.Register(ormCodespace, 11, "iterator done")
// ErrORMInvalidIterator defines an error for an invalid iterator
ErrORMInvalidIterator = errors.Register(ormCodespace, 12, "invalid iterator")
// ErrORMUniqueConstraint defines an error when a value already exists at a given key
ErrORMUniqueConstraint = errors.Register(ormCodespace, 13, "unique constraint violation")
// ErrORMInvalidArgument defines an error when an invalid argument is provided as part of ORM functions
ErrORMInvalidArgument = errors.Register(ormCodespace, 14, "invalid argument")
// ErrORMKeyMaxLength defines an error when a key exceeds max length
ErrORMKeyMaxLength = errors.Register(ormCodespace, 15, "key exceeds max length")
// ErrORMEmptyKey defines an error for an empty key
ErrORMEmptyKey = errors.Register(ormCodespace, 47, "cannot use empty key")
)

View File

@ -16,7 +16,6 @@ require (
require github.com/tendermint/tm-db v0.6.4
require (
github.com/DataDog/zstd v1.4.5 // indirect
github.com/armon/go-metrics v0.3.10 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.22.0-beta // indirect
@ -26,7 +25,7 @@ require (
github.com/cosmos/btcutil v1.0.4 // indirect
github.com/cosmos/iavl v0.17.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/badger/v2 v2.2007.2 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
@ -35,16 +34,18 @@ require (
github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.0.0 // indirect
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/hashicorp/go-immutable-radix v1.0.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/klauspost/compress v1.12.3 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lazyledger/smt v0.2.1-0.20210709230900-03ea40719554 // indirect
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
github.com/magiconair/properties v1.8.5 // indirect
@ -68,7 +69,7 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.9.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
github.com/tendermint/go-amino v0.16.0 // indirect
github.com/tendermint/tendermint v0.34.14 // indirect
@ -78,6 +79,7 @@ require (
golang.org/x/sys v0.0.0-20210903071746-97244b99971b // indirect
golang.org/x/text v0.3.6 // indirect
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.63.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect

View File

@ -1,11 +1,13 @@
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
@ -29,6 +31,7 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
@ -42,6 +45,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.0.0-beta.2 h1:/BZRNzm8N4K4eWfK28dL4yescorxtO7YG1yun8fy+pI=
filippo.io/edwards25519 v1.0.0-beta.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o=
@ -66,10 +70,10 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg=
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
@ -79,7 +83,7 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw=
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI=
@ -88,16 +92,18 @@ github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6/go.mod h1:
github.com/adlio/schema v1.1.13/go.mod h1:L5Z7tw+7lRK1Fnpi/LT/ooCP1elkXn0krMWBQHUhEDE=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@ -107,9 +113,17 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
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/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo=
github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y=
github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8=
github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4=
github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0=
github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM=
github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -119,7 +133,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0=
github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
@ -140,6 +155,7 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -156,18 +172,20 @@ github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E=
github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw=
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/coinbase/rosetta-sdk-go v0.7.0 h1:lmTO/JEpCvZgpbkOITL95rA80CPKb5CtMzLaqF2mCNg=
github.com/coinbase/rosetta-sdk-go v0.7.0/go.mod h1:7nD3oBPIiHqhRprqvMgPoGxe/nyq3yftRmpsy29coWE=
github.com/coinbase/rosetta-sdk-go v0.7.1 h1:J1erjPiDnyDIHwscxXSrx9GnnFhzb8zDhx/On2j7JF8=
github.com/coinbase/rosetta-sdk-go v0.7.1/go.mod h1:MZX7tpDNCZOHm1WpydIfNwpHUDXJR1Pt4xeuABqfvQo=
github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
github.com/confio/ics23/go v0.6.3/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
github.com/confio/ics23/go v0.6.6 h1:pkOy18YxxJ/r0XFDCnrl4Bjv6h4LkBSpLS6F38mrKL8=
github.com/confio/ics23/go v0.6.6/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
@ -203,62 +221,68 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU=
github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U=
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
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=
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
github.com/dgraph-io/badger/v3 v3.2103.1/go.mod h1:dULbq6ehJ5K0cGW/1TQ9iSfUk0gbSiToDWmWmTsJ53E=
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ=
github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM=
github.com/ethereum/go-ethereum v1.10.11/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
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=
@ -269,9 +293,14 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
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-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -285,12 +314,15 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
@ -300,8 +332,11 @@ github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/E
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0=
github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
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=
@ -339,12 +374,14 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
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/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/flatbuffers v1.12.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
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=
@ -387,6 +424,7 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
@ -401,12 +439,11 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
@ -434,6 +471,7 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
@ -454,8 +492,8 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
@ -467,10 +505,11 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 h1:uUjLpLt6bVvZ72SQc/B4dXcPBw4Vgd7soowdRl52qEM=
github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87/go.mod h1:XGsKKeXxeRr95aEOgipvluMPlgjr7dGlk9ZTWOjcUcg=
github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
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/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
github.com/huin/goupnp v1.0.2/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM=
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@ -478,14 +517,26 @@ github.com/improbable-eng/grpc-web v0.14.1 h1:NrN4PY71A6tAz2sKDvC5JCauENWp0ykG8O
github.com/improbable-eng/grpc-web v0.14.1/go.mod h1:zEjGHa8DAlkoOXmswrNvhUGEYQA9UI7DhrGeHR1DMGU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY=
github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI=
github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk=
github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE=
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8=
github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE=
github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0=
github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.10.1/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
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/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@ -499,37 +550,49 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM=
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/lazyledger/smt v0.2.1-0.20210709230900-03ea40719554 h1:nDOkLO7klmnEw1s4AyKt1Arvpgyh33uj1JmkYlJaDsk=
github.com/lazyledger/smt v0.2.1-0.20210709230900-03ea40719554/go.mod h1:9+Pb2/tg1PvEgW7aFx4bFhDE4bvbI03zuJ8kb7nJ9Jc=
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs=
github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
@ -539,17 +602,21 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@ -557,7 +624,9 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@ -576,16 +645,17 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@ -600,8 +670,7 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/neilotoole/errgroup v0.1.5/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/neilotoole/errgroup v0.1.6/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@ -611,8 +680,7 @@ github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtb
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
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/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
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=
@ -637,6 +705,7 @@ github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xA
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
@ -647,16 +716,18 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
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=
@ -665,6 +736,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
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=
@ -690,6 +762,7 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
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.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
@ -708,7 +781,6 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
@ -719,17 +791,16 @@ github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzy
github.com/regen-network/cosmos-proto v0.3.1/go.mod h1:jO0sVX6a1B36nmE8C9xBFXpNwWejXC7QqCOnH3O0+YM=
github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4=
github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI=
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.25.0 h1:Rj7XygbUHKUlDPcVdoLyR91fJBsduXj5fRxyqIQj/II=
github.com/rs/zerolog v1.25.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI=
github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE=
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
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=
@ -741,7 +812,10 @@ github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F7
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
@ -785,14 +859,13 @@ github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH
github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk=
github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -803,8 +876,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk=
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok=
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s=
@ -823,10 +897,14 @@ github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs
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=
github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/sjson v1.1.4/go.mod h1:wXpKXu8CtDjKAZ+3DrKY5ROCorDFahq8l0tey/Lx1fg=
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs=
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
@ -838,12 +916,17 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vmihailenco/msgpack/v5 v5.1.4/go.mod h1:C5gboKD0TJPqWDTVTtrQNfRbiBwHZGo8UTqP/9/XvLI=
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -851,6 +934,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8=
github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@ -878,6 +962,7 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
@ -902,12 +987,16 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
@ -916,6 +1005,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -931,12 +1021,10 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
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.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@ -945,7 +1033,6 @@ golang.org/x/mod v0.4.2/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=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -983,19 +1070,20 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg=
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -1047,6 +1135,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1061,6 +1150,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1081,7 +1171,7 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1095,9 +1185,12 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1108,6 +1201,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210903071746-97244b99971b h1:3Dq0eVHn0uaQJmPO+/aYPI/fRMqdrVDbu7MQcku54gg=
golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1127,7 +1222,11 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@ -1154,7 +1253,7 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@ -1189,11 +1288,18 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
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=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@ -1237,6 +1343,7 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
@ -1244,6 +1351,7 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
@ -1314,8 +1422,9 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
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/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=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
@ -1352,11 +1461,13 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
pgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g=
pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@ -6,7 +6,8 @@ import (
"github.com/cockroachdb/apd/v2"
"github.com/cosmos/cosmos-sdk/types/errors"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
)
// Dec is a wrapper struct around apd.Decimal that does no mutation of apd.Decimal's when performing
@ -38,7 +39,7 @@ func NewDecFromInt64(x int64) Dec {
func (x Dec) Add(y Dec) (Dec, error) {
var z Dec
_, err := apd.BaseContext.Add(&z.dec, &x.dec, &y.dec)
return z, errors.Wrap(err, "decimal addition error")
return z, sdkerrors.Wrap(err, "decimal addition error")
}
// Sub returns a new Dec with value `x-y` without mutating any argument and error if
@ -46,7 +47,7 @@ func (x Dec) Add(y Dec) (Dec, error) {
func (x Dec) Sub(y Dec) (Dec, error) {
var z Dec
_, err := apd.BaseContext.Sub(&z.dec, &x.dec, &y.dec)
return z, errors.Wrap(err, "decimal subtraction error")
return z, sdkerrors.Wrap(err, "decimal subtraction error")
}
func (x Dec) Int64() (int64, error) {

View File

@ -73,3 +73,36 @@ func (a AutoUInt64Table) GetOne(store sdk.KVStore, rowID uint64, dest codec.Prot
}
return rawRowID, nil
}
// PrefixScan returns an Iterator over a domain of keys in ascending order. End is exclusive.
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned.
// Iterator must be closed by caller.
// To iterate over entire domain, use PrefixScan(1, math.MaxUint64)
//
// WARNING: The use of a PrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
// this as an endpoint to the public without further limits.
// Example:
// it, err := idx.PrefixScan(ctx, start, end)
// if err !=nil {
// return err
// }
// const defaultLimit = 20
// it = LimitIterator(it, defaultLimit)
//
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
func (a AutoUInt64Table) PrefixScan(store sdk.KVStore, start, end uint64) (Iterator, error) {
return a.table.PrefixScan(store, EncodeSequence(start), EncodeSequence(end))
}
// ReversePrefixScan returns an Iterator over a domain of keys in descending order. End is exclusive.
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned.
// Iterator must be closed by caller.
// To iterate over entire domain, use PrefixScan(1, math.MaxUint64)
//
// WARNING: The use of a ReversePrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
// this as an endpoint to the public without further limits. See `LimitIterator`
//
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
func (a AutoUInt64Table) ReversePrefixScan(store sdk.KVStore, start uint64, end uint64) (Iterator, error) {
return a.table.ReversePrefixScan(store, EncodeSequence(start), EncodeSequence(end))
}

View File

@ -0,0 +1,164 @@
package orm
import (
"math"
"testing"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAutoUInt64PrefixScan(t *testing.T) {
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
tb, err := NewAutoUInt64Table(AutoUInt64TablePrefix, AutoUInt64TableSeqPrefix, &testdata.TableModel{}, cdc)
require.NoError(t, err)
ctx := NewMockContext()
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
metadata := []byte("metadata")
t1 := testdata.TableModel{
Id: 1,
Name: "my test 1",
Metadata: metadata,
}
t2 := testdata.TableModel{
Id: 2,
Name: "my test 2",
Metadata: metadata,
}
t3 := testdata.TableModel{
Id: 3,
Name: "my test 3",
Metadata: metadata,
}
for _, g := range []testdata.TableModel{t1, t2, t3} {
_, err := tb.Create(store, &g)
require.NoError(t, err)
}
specs := map[string]struct {
start, end uint64
expResult []testdata.TableModel
expRowIDs []RowID
expError *sdkerrors.Error
method func(store sdk.KVStore, start uint64, end uint64) (Iterator, error)
}{
"first element": {
start: 1,
end: 2,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1},
expRowIDs: []RowID{EncodeSequence(1)},
},
"first 2 elements": {
start: 1,
end: 3,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1, t2},
expRowIDs: []RowID{EncodeSequence(1), EncodeSequence(2)},
},
"first 3 elements": {
start: 1,
end: 4,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1, t2, t3},
expRowIDs: []RowID{EncodeSequence(1), EncodeSequence(2), EncodeSequence(3)},
},
"search with max end": {
start: 1,
end: math.MaxUint64,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1, t2, t3},
expRowIDs: []RowID{EncodeSequence(1), EncodeSequence(2), EncodeSequence(3)},
},
"2 to end": {
start: 2,
end: 5,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t2, t3},
expRowIDs: []RowID{EncodeSequence(2), EncodeSequence(3)},
},
"start after end should fail": {
start: 2,
end: 1,
method: tb.PrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"start equals end should fail": {
start: 1,
end: 1,
method: tb.PrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"reverse first element": {
start: 1,
end: 2,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t1},
expRowIDs: []RowID{EncodeSequence(1)},
},
"reverse first 2 elements": {
start: 1,
end: 3,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t2, t1},
expRowIDs: []RowID{EncodeSequence(2), EncodeSequence(1)},
},
"reverse first 3 elements": {
start: 1,
end: 4,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t3, t2, t1},
expRowIDs: []RowID{EncodeSequence(3), EncodeSequence(2), EncodeSequence(1)},
},
"reverse search with max end": {
start: 1,
end: math.MaxUint64,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t3, t2, t1},
expRowIDs: []RowID{EncodeSequence(3), EncodeSequence(2), EncodeSequence(1)},
},
"reverse 2 to end": {
start: 2,
end: 5,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t3, t2},
expRowIDs: []RowID{EncodeSequence(3), EncodeSequence(2)},
},
"reverse start after end should fail": {
start: 2,
end: 1,
method: tb.ReversePrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"reverse start equals end should fail": {
start: 1,
end: 1,
method: tb.ReversePrefixScan,
expError: errors.ErrORMInvalidArgument,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
it, err := spec.method(store, spec.start, spec.end)
require.True(t, spec.expError.Is(err), "expected #+v but got #+v", spec.expError, err)
if spec.expError != nil {
return
}
var loaded []testdata.TableModel
rowIDs, err := ReadAll(it, &loaded)
require.NoError(t, err)
assert.Equal(t, spec.expResult, loaded)
assert.Equal(t, spec.expRowIDs, rowIDs)
})
}
}

View File

@ -6,30 +6,61 @@ import (
)
type TestKeeper struct {
autoUInt64Table *AutoUInt64Table
primaryKeyTable *PrimaryKeyTable
autoUInt64Table *AutoUInt64Table
primaryKeyTable *PrimaryKeyTable
autoUInt64TableModelByMetadataIndex Index
primaryKeyTableModelByNameIndex Index
primaryKeyTableModelByNumberIndex Index
primaryKeyTableModelByMetadataIndex Index
}
var (
AutoUInt64TableTablePrefix [2]byte = [2]byte{0x0}
PrimaryKeyTablePrefix [2]byte = [2]byte{0x1}
AutoUInt64TableSeqPrefix byte = 0x2
AutoUInt64TablePrefix [2]byte = [2]byte{0x0}
PrimaryKeyTablePrefix [2]byte = [2]byte{0x1}
AutoUInt64TableSeqPrefix byte = 0x2
AutoUInt64TableModelByMetadataPrefix byte = 0x4
PrimaryKeyTableModelByNamePrefix byte = 0x5
PrimaryKeyTableModelByNumberPrefix byte = 0x6
PrimaryKeyTableModelByMetadataPrefix byte = 0x7
)
func NewTestKeeper(cdc codec.Codec) TestKeeper {
k := TestKeeper{}
var err error
autoUInt64Table, err := NewAutoUInt64Table(AutoUInt64TableTablePrefix, AutoUInt64TableSeqPrefix, &testdata.TableModel{}, cdc)
k.autoUInt64Table, err = NewAutoUInt64Table(AutoUInt64TablePrefix, AutoUInt64TableSeqPrefix, &testdata.TableModel{}, cdc)
if err != nil {
panic(err.Error())
}
k.autoUInt64Table = autoUInt64Table
primaryKeyTable, err := NewPrimaryKeyTable(PrimaryKeyTablePrefix, &testdata.TableModel{}, cdc)
k.autoUInt64TableModelByMetadataIndex, err = NewIndex(k.autoUInt64Table, AutoUInt64TableModelByMetadataPrefix, func(val interface{}) ([]interface{}, error) {
return []interface{}{val.(*testdata.TableModel).Metadata}, nil
}, testdata.TableModel{}.Metadata)
if err != nil {
panic(err.Error())
}
k.primaryKeyTable, err = NewPrimaryKeyTable(PrimaryKeyTablePrefix, &testdata.TableModel{}, cdc)
if err != nil {
panic(err.Error())
}
k.primaryKeyTableModelByNameIndex, err = NewIndex(k.primaryKeyTable, PrimaryKeyTableModelByNamePrefix, func(val interface{}) ([]interface{}, error) {
return []interface{}{val.(*testdata.TableModel).Name}, nil
}, testdata.TableModel{}.Name)
if err != nil {
panic(err.Error())
}
k.primaryKeyTableModelByNumberIndex, err = NewIndex(k.primaryKeyTable, PrimaryKeyTableModelByNumberPrefix, func(val interface{}) ([]interface{}, error) {
return []interface{}{val.(*testdata.TableModel).Number}, nil
}, testdata.TableModel{}.Number)
if err != nil {
panic(err.Error())
}
k.primaryKeyTableModelByMetadataIndex, err = NewIndex(k.primaryKeyTable, PrimaryKeyTableModelByMetadataPrefix, func(val interface{}) ([]interface{}, error) {
return []interface{}{val.(*testdata.TableModel).Metadata}, nil
}, testdata.TableModel{}.Metadata)
if err != nil {
panic(err.Error())
}
k.primaryKeyTable = primaryKeyTable
return k
}

View File

@ -1,5 +1,259 @@
package orm
import (
"bytes"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
"github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
"github.com/cosmos/cosmos-sdk/types/query"
)
// indexer creates and modifies the second MultiKeyIndex based on the operations and changes on the primary object.
type indexer interface {
OnCreate(store sdk.KVStore, rowID RowID, value interface{}) error
OnDelete(store sdk.KVStore, rowID RowID, value interface{}) error
OnUpdate(store sdk.KVStore, rowID RowID, newValue, oldValue interface{}) error
}
var _ Index = &MultiKeyIndex{}
// MultiKeyIndex is an index where multiple entries can point to the same underlying object as opposite to a unique index
// where only one entry is allowed.
type MultiKeyIndex struct {
prefix byte
rowGetter RowGetter
indexer indexer
indexerFunc IndexerFunc
indexKey interface{}
}
// NewIndex builds a MultiKeyIndex.
// Only single-field indexes are supported and `indexKey` represents such a field value,
// which can be []byte, string or uint64.
func NewIndex(tb Indexable, prefix byte, indexerF IndexerFunc, indexKey interface{}) (MultiKeyIndex, error) {
indexer, err := NewIndexer(indexerF)
if err != nil {
return MultiKeyIndex{}, err
}
return newIndex(tb, prefix, indexer, indexer.IndexerFunc(), indexKey)
}
func newIndex(tb Indexable, prefix byte, indexer *Indexer, indexerF IndexerFunc, indexKey interface{}) (MultiKeyIndex, error) {
rowGetter := tb.RowGetter()
if rowGetter == nil {
return MultiKeyIndex{}, errors.ErrORMInvalidArgument.Wrap("rowGetter must not be nil")
}
if indexKey == nil {
return MultiKeyIndex{}, errors.ErrORMInvalidArgument.Wrap("indexKey must not be nil")
}
// Verify indexKey type is bytes, string or uint64
switch indexKey.(type) {
case []byte, string, uint64:
default:
return MultiKeyIndex{}, errors.ErrORMInvalidArgument.Wrap("indexKey must be []byte, string or uint64")
}
idx := MultiKeyIndex{
prefix: prefix,
rowGetter: rowGetter,
indexer: indexer,
indexerFunc: indexerF,
indexKey: indexKey,
}
tb.AddAfterSetInterceptor(idx.onSet)
tb.AddAfterDeleteInterceptor(idx.onDelete)
return idx, nil
}
// Has checks if a key exists. Returns an error on nil key.
func (i MultiKeyIndex) Has(store sdk.KVStore, key interface{}) (bool, error) {
pStore := prefix.NewStore(store, []byte{i.prefix})
encodedKey, err := keyPartBytes(key, false)
if err != nil {
return false, err
}
it := pStore.Iterator(PrefixRange(encodedKey))
defer it.Close()
return it.Valid(), nil
}
// Get returns a result iterator for the searchKey. Parameters must not be nil.
func (i MultiKeyIndex) Get(store sdk.KVStore, searchKey interface{}) (Iterator, error) {
pStore := prefix.NewStore(store, []byte{i.prefix})
encodedKey, err := keyPartBytes(searchKey, false)
if err != nil {
return nil, err
}
it := pStore.Iterator(PrefixRange(encodedKey))
return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, nil
}
// GetPaginated creates an iterator for the searchKey
// starting from pageRequest.Key if provided.
// The pageRequest.Key is the rowID while searchKey is a MultiKeyIndex key.
func (i MultiKeyIndex) GetPaginated(store sdk.KVStore, searchKey interface{}, pageRequest *query.PageRequest) (Iterator, error) {
pStore := prefix.NewStore(store, []byte{i.prefix})
encodedKey, err := keyPartBytes(searchKey, false)
if err != nil {
return nil, err
}
start, end := PrefixRange(encodedKey)
if pageRequest != nil && len(pageRequest.Key) != 0 {
var err error
start, err = buildKeyFromParts([]interface{}{searchKey, pageRequest.Key})
if err != nil {
return nil, err
}
}
it := pStore.Iterator(start, end)
return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, nil
}
// PrefixScan returns an Iterator over a domain of keys in ascending order. End is exclusive.
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned.
// Iterator must be closed by caller.
// To iterate over entire domain, use PrefixScan(nil, nil)
//
// WARNING: The use of a PrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
// this as an endpoint to the public without further limits.
// Example:
// it, err := idx.PrefixScan(ctx, start, end)
// if err !=nil {
// return err
// }
// const defaultLimit = 20
// it = LimitIterator(it, defaultLimit)
//
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
func (i MultiKeyIndex) PrefixScan(store sdk.KVStore, startI interface{}, endI interface{}) (Iterator, error) {
start, err := getPrefixScanKeyBytes(startI)
if err != nil {
return nil, err
}
end, err := getPrefixScanKeyBytes(endI)
if err != nil {
return nil, err
}
if start != nil && end != nil && bytes.Compare(start, end) >= 0 {
return NewInvalidIterator(), sdkerrors.Wrap(errors.ErrORMInvalidArgument, "start must be less than end")
}
pStore := prefix.NewStore(store, []byte{i.prefix})
it := pStore.Iterator(start, end)
return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, nil
}
// ReversePrefixScan returns an Iterator over a domain of keys in descending order. End is exclusive.
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned.
// Iterator must be closed by caller.
// To iterate over entire domain, use PrefixScan(nil, nil)
//
// WARNING: The use of a ReversePrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
// this as an endpoint to the public without further limits. See `LimitIterator`
//
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
func (i MultiKeyIndex) ReversePrefixScan(store sdk.KVStore, startI interface{}, endI interface{}) (Iterator, error) {
start, err := getPrefixScanKeyBytes(startI)
if err != nil {
return nil, err
}
end, err := getPrefixScanKeyBytes(endI)
if err != nil {
return nil, err
}
if start != nil && end != nil && bytes.Compare(start, end) >= 0 {
return NewInvalidIterator(), sdkerrors.Wrap(errors.ErrORMInvalidArgument, "start must be less than end")
}
pStore := prefix.NewStore(store, []byte{i.prefix})
it := pStore.ReverseIterator(start, end)
return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, nil
}
func getPrefixScanKeyBytes(keyI interface{}) ([]byte, error) {
var (
key []byte
err error
)
// nil value are accepted in the context of PrefixScans
if keyI == nil {
return nil, nil
}
key, err = keyPartBytes(keyI, false)
if err != nil {
return nil, err
}
return key, nil
}
func (i MultiKeyIndex) onSet(store sdk.KVStore, rowID RowID, newValue, oldValue codec.ProtoMarshaler) error {
pStore := prefix.NewStore(store, []byte{i.prefix})
if oldValue == nil {
return i.indexer.OnCreate(pStore, rowID, newValue)
}
return i.indexer.OnUpdate(pStore, rowID, newValue, oldValue)
}
func (i MultiKeyIndex) onDelete(store sdk.KVStore, rowID RowID, oldValue codec.ProtoMarshaler) error {
pStore := prefix.NewStore(store, []byte{i.prefix})
return i.indexer.OnDelete(pStore, rowID, oldValue)
}
type UniqueIndex struct {
MultiKeyIndex
}
// NewUniqueIndex create a new Index object where duplicate keys are prohibited.
func NewUniqueIndex(tb Indexable, prefix byte, uniqueIndexerFunc UniqueIndexerFunc, indexKey interface{}) (UniqueIndex, error) {
uniqueIndexer, err := NewUniqueIndexer(uniqueIndexerFunc)
if err != nil {
return UniqueIndex{}, err
}
multiKeyIndex, err := newIndex(tb, prefix, uniqueIndexer, uniqueIndexer.IndexerFunc(), indexKey)
if err != nil {
return UniqueIndex{}, err
}
return UniqueIndex{
MultiKeyIndex: multiKeyIndex,
}, nil
}
// indexIterator uses rowGetter to lazy load new model values on request.
type indexIterator struct {
store sdk.KVStore
rowGetter RowGetter
it types.Iterator
indexKey interface{}
}
// LoadNext loads the next value in the sequence into the pointer passed as dest and returns the key. If there
// are no more items the errors.ErrORMIteratorDone error is returned
// The key is the rowID and not any MultiKeyIndex key.
func (i indexIterator) LoadNext(dest codec.ProtoMarshaler) (RowID, error) {
if !i.it.Valid() {
return nil, errors.ErrORMIteratorDone
}
indexPrefixKey := i.it.Key()
rowID, err := stripRowID(indexPrefixKey, i.indexKey)
if err != nil {
return nil, err
}
i.it.Next()
return rowID, i.rowGetter(i.store, rowID, dest)
}
// Close releases the iterator and should be called at the end of iteration
func (i indexIterator) Close() error {
i.it.Close()
return nil
}
// PrefixRange turns a prefix into a (start, end) range. The start is the given prefix value and
// the end is calculated by adding 1 bit to the start value. Nil is not allowed as prefix.
// Example: []byte{1, 3, 4} becomes []byte{1, 3, 5}

View File

@ -3,10 +3,405 @@ package orm
import (
"testing"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/group/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var _ Indexable = &nilRowGetterBuilder{}
type nilRowGetterBuilder struct{}
func (b *nilRowGetterBuilder) RowGetter() RowGetter {
return nil
}
func (b *nilRowGetterBuilder) AddAfterSetInterceptor(AfterSetInterceptor) {}
func (b *nilRowGetterBuilder) AddAfterDeleteInterceptor(AfterDeleteInterceptor) {}
func TestNewIndex(t *testing.T) {
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
myTable, err := NewAutoUInt64Table(AutoUInt64TablePrefix, AutoUInt64TableSeqPrefix, &testdata.TableModel{}, cdc)
require.NoError(t, err)
indexer := func(val interface{}) ([]interface{}, error) {
return []interface{}{val.(*testdata.TableModel).Metadata}, nil
}
testCases := []struct {
name string
table Indexable
expectErr bool
expectedErr string
indexKey interface{}
}{
{
name: "nil indexKey",
table: myTable,
expectErr: true,
expectedErr: "indexKey must not be nil",
indexKey: nil,
},
{
name: "nil rowGetter",
table: &nilRowGetterBuilder{},
expectErr: true,
expectedErr: "rowGetter must not be nil",
indexKey: []byte{},
},
{
name: "all not nil",
table: myTable,
expectErr: false,
indexKey: []byte{},
},
{
name: "index key type not allowed",
table: myTable,
expectErr: true,
indexKey: 1,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
index, err := NewIndex(tc.table, AutoUInt64TableSeqPrefix, indexer, tc.indexKey)
if tc.expectErr {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
} else {
require.NoError(t, err)
require.NotEmpty(t, index)
}
})
}
}
func TestIndexPrefixScan(t *testing.T) {
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
tb, err := NewAutoUInt64Table(AutoUInt64TablePrefix, AutoUInt64TableSeqPrefix, &testdata.TableModel{}, cdc)
require.NoError(t, err)
idx, err := NewIndex(tb, AutoUInt64TableModelByMetadataPrefix, func(val interface{}) ([]interface{}, error) {
i := []interface{}{val.(*testdata.TableModel).Metadata}
return i, nil
}, testdata.TableModel{}.Metadata)
require.NoError(t, err)
strIdx, err := NewIndex(tb, 0x1, func(val interface{}) ([]interface{}, error) {
i := []interface{}{val.(*testdata.TableModel).Name}
return i, nil
}, testdata.TableModel{}.Name)
require.NoError(t, err)
ctx := NewMockContext()
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
g1 := testdata.TableModel{
Id: 1,
Name: "my test 1",
Metadata: []byte("metadata-a"),
}
g2 := testdata.TableModel{
Id: 2,
Name: "my test 2",
Metadata: []byte("metadata-b"),
}
g3 := testdata.TableModel{
Id: 3,
Name: "my test 3",
Metadata: []byte("metadata-b"),
}
for _, g := range []testdata.TableModel{g1, g2, g3} {
_, err := tb.Create(store, &g)
require.NoError(t, err)
}
specs := map[string]struct {
start, end interface{}
expResult []testdata.TableModel
expRowIDs []RowID
expError *sdkerrors.Error
method func(store sdk.KVStore, start, end interface{}) (Iterator, error)
}{
"exact match with a single result": {
start: []byte("metadata-a"),
end: []byte("metadata-b"),
method: idx.PrefixScan,
expResult: []testdata.TableModel{g1},
expRowIDs: []RowID{EncodeSequence(1)},
},
"one result by prefix": {
start: []byte("metadata"),
end: []byte("metadata-b"),
method: idx.PrefixScan,
expResult: []testdata.TableModel{g1},
expRowIDs: []RowID{EncodeSequence(1)},
},
"multi key elements by exact match": {
start: []byte("metadata-b"),
end: []byte("metadata-c"),
method: idx.PrefixScan,
expResult: []testdata.TableModel{g2, g3},
expRowIDs: []RowID{EncodeSequence(2), EncodeSequence(3)},
},
"open end query": {
start: []byte("metadata-b"),
end: nil,
method: idx.PrefixScan,
expResult: []testdata.TableModel{g2, g3},
expRowIDs: []RowID{EncodeSequence(2), EncodeSequence(3)},
},
"open start query": {
start: nil,
end: []byte("metadata-b"),
method: idx.PrefixScan,
expResult: []testdata.TableModel{g1},
expRowIDs: []RowID{EncodeSequence(1)},
},
"open start and end query": {
start: nil,
end: nil,
method: idx.PrefixScan,
expResult: []testdata.TableModel{g1, g2, g3},
expRowIDs: []RowID{EncodeSequence(1), EncodeSequence(2), EncodeSequence(3)},
},
"all matching prefix": {
start: []byte("admin"),
end: nil,
method: idx.PrefixScan,
expResult: []testdata.TableModel{g1, g2, g3},
expRowIDs: []RowID{EncodeSequence(1), EncodeSequence(2), EncodeSequence(3)},
},
"non matching prefix": {
start: []byte("metadata-c"),
end: nil,
method: idx.PrefixScan,
expResult: []testdata.TableModel{},
},
"start equals end": {
start: []byte("any"),
end: []byte("any"),
method: idx.PrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"start after end": {
start: []byte("b"),
end: []byte("a"),
method: idx.PrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"reverse: exact match with a single result": {
start: []byte("metadata-a"),
end: []byte("metadata-b"),
method: idx.ReversePrefixScan,
expResult: []testdata.TableModel{g1},
expRowIDs: []RowID{EncodeSequence(1)},
},
"reverse: one result by prefix": {
start: []byte("metadata"),
end: []byte("metadata-b"),
method: idx.ReversePrefixScan,
expResult: []testdata.TableModel{g1},
expRowIDs: []RowID{EncodeSequence(1)},
},
"reverse: multi key elements by exact match": {
start: []byte("metadata-b"),
end: []byte("metadata-c"),
method: idx.ReversePrefixScan,
expResult: []testdata.TableModel{g3, g2},
expRowIDs: []RowID{EncodeSequence(3), EncodeSequence(2)},
},
"reverse: open end query": {
start: []byte("metadata-b"),
end: nil,
method: idx.ReversePrefixScan,
expResult: []testdata.TableModel{g3, g2},
expRowIDs: []RowID{EncodeSequence(3), EncodeSequence(2)},
},
"reverse: open start query": {
start: nil,
end: []byte("metadata-b"),
method: idx.ReversePrefixScan,
expResult: []testdata.TableModel{g1},
expRowIDs: []RowID{EncodeSequence(1)},
},
"reverse: open start and end query": {
start: nil,
end: nil,
method: idx.ReversePrefixScan,
expResult: []testdata.TableModel{g3, g2, g1},
expRowIDs: []RowID{EncodeSequence(3), EncodeSequence(2), EncodeSequence(1)},
},
"reverse: all matching prefix": {
start: []byte("admin"),
end: nil,
method: idx.ReversePrefixScan,
expResult: []testdata.TableModel{g3, g2, g1},
expRowIDs: []RowID{EncodeSequence(3), EncodeSequence(2), EncodeSequence(1)},
},
"reverse: non matching prefix": {
start: []byte("metadata-c"),
end: nil,
method: idx.ReversePrefixScan,
expResult: []testdata.TableModel{},
},
"reverse: start equals end": {
start: []byte("any"),
end: []byte("any"),
method: idx.ReversePrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"reverse: start after end": {
start: []byte("b"),
end: []byte("a"),
method: idx.ReversePrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"exact match with a single result using string based index": {
start: "my test 1",
end: "my test 2",
method: strIdx.PrefixScan,
expResult: []testdata.TableModel{g1},
expRowIDs: []RowID{EncodeSequence(1)},
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
it, err := spec.method(store, spec.start, spec.end)
require.True(t, spec.expError.Is(err), "expected #+v but got #+v", spec.expError, err)
if spec.expError != nil {
return
}
var loaded []testdata.TableModel
rowIDs, err := ReadAll(it, &loaded)
require.NoError(t, err)
assert.Equal(t, spec.expResult, loaded)
assert.Equal(t, spec.expRowIDs, rowIDs)
})
}
}
func TestUniqueIndex(t *testing.T) {
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
myTable, err := NewPrimaryKeyTable(PrimaryKeyTablePrefix, &testdata.TableModel{}, cdc)
require.NoError(t, err)
uniqueIdx, err := NewUniqueIndex(myTable, 0x10, func(val interface{}) (interface{}, error) {
return []byte{val.(*testdata.TableModel).Metadata[0]}, nil
}, []byte{})
require.NoError(t, err)
ctx := NewMockContext()
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
m := testdata.TableModel{
Id: 1,
Name: "my test",
Metadata: []byte("metadata"),
}
err = myTable.Create(store, &m)
require.NoError(t, err)
indexedKey := []byte{'m'}
// Has
exists, err := uniqueIdx.Has(store, indexedKey)
require.NoError(t, err)
assert.True(t, exists)
// Get
it, err := uniqueIdx.Get(store, indexedKey)
require.NoError(t, err)
var loaded testdata.TableModel
rowID, err := it.LoadNext(&loaded)
require.NoError(t, err)
require.Equal(t, RowID(PrimaryKey(&m)), rowID)
require.Equal(t, m, loaded)
// GetPaginated
cases := map[string]struct {
pageReq *query.PageRequest
expErr bool
}{
"nil key": {
pageReq: &query.PageRequest{Key: nil},
expErr: false,
},
"after indexed key": {
pageReq: &query.PageRequest{Key: indexedKey},
expErr: true,
},
}
for testName, tc := range cases {
t.Run(testName, func(t *testing.T) {
it, err := uniqueIdx.GetPaginated(store, indexedKey, tc.pageReq)
require.NoError(t, err)
rowID, err := it.LoadNext(&loaded)
if tc.expErr { // iterator done
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, RowID(PrimaryKey(&m)), rowID)
require.Equal(t, m, loaded)
}
})
}
// PrefixScan match
it, err = uniqueIdx.PrefixScan(store, indexedKey, nil)
require.NoError(t, err)
rowID, err = it.LoadNext(&loaded)
require.NoError(t, err)
require.Equal(t, RowID(PrimaryKey(&m)), rowID)
require.Equal(t, m, loaded)
// PrefixScan no match
it, err = uniqueIdx.PrefixScan(store, []byte{byte('n')}, nil)
require.NoError(t, err)
_, err = it.LoadNext(&loaded)
require.Error(t, errors.ErrORMIteratorDone, err)
// ReversePrefixScan match
it, err = uniqueIdx.ReversePrefixScan(store, indexedKey, nil)
require.NoError(t, err)
rowID, err = it.LoadNext(&loaded)
require.NoError(t, err)
require.Equal(t, RowID(PrimaryKey(&m)), rowID)
require.Equal(t, m, loaded)
// ReversePrefixScan no match
it, err = uniqueIdx.ReversePrefixScan(store, []byte{byte('l')}, nil)
require.NoError(t, err)
_, err = it.LoadNext(&loaded)
require.Error(t, errors.ErrORMIteratorDone, err)
// create with same index key should fail
new := testdata.TableModel{
Id: 1,
Name: "my test",
Metadata: []byte("my-metadata"),
}
err = myTable.Create(store, &new)
require.Error(t, errors.ErrORMUniqueConstraint, err)
// and when delete
err = myTable.Delete(store, &m)
require.NoError(t, err)
// then no persistent element
exists, err = uniqueIdx.Has(store, indexedKey)
require.NoError(t, err)
assert.False(t, exists)
}
func TestPrefixRange(t *testing.T) {
cases := map[string]struct {
src []byte

View File

@ -0,0 +1,211 @@
package orm
import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
)
// IndexerFunc creates one or multiple index keys for the source object.
type IndexerFunc func(value interface{}) ([]interface{}, error)
// IndexerFunc creates exactly one index key for the source object.
type UniqueIndexerFunc func(value interface{}) (interface{}, error)
// Indexer manages the persistence of an Index based on searchable keys and operations.
type Indexer struct {
indexerFunc IndexerFunc
addFunc func(store sdk.KVStore, secondaryIndexKey interface{}, rowID RowID) error
}
// NewIndexer returns an indexer that supports multiple reference keys for an entity.
func NewIndexer(indexerFunc IndexerFunc) (*Indexer, error) {
if indexerFunc == nil {
return nil, errors.ErrORMInvalidArgument.Wrap("Indexer func must not be nil")
}
return &Indexer{
indexerFunc: pruneEmptyKeys(indexerFunc),
addFunc: multiKeyAddFunc,
}, nil
}
// NewUniqueIndexer returns an indexer that requires exactly one reference keys for an entity.
func NewUniqueIndexer(f UniqueIndexerFunc) (*Indexer, error) {
if f == nil {
return nil, errors.ErrORMInvalidArgument.Wrap("Indexer func must not be nil")
}
adaptor := func(indexerFunc UniqueIndexerFunc) IndexerFunc {
return func(v interface{}) ([]interface{}, error) {
k, err := indexerFunc(v)
return []interface{}{k}, err
}
}
idx, err := NewIndexer(adaptor(f))
if err != nil {
return nil, err
}
idx.addFunc = uniqueKeysAddFunc
return idx, nil
}
// IndexerFunc returns the indexer IndexerFunc,
// ensuring it has been prune from empty keys.
func (i Indexer) IndexerFunc() IndexerFunc {
return i.indexerFunc
}
// OnCreate persists the secondary index entries for the new object.
func (i Indexer) OnCreate(store sdk.KVStore, rowID RowID, value interface{}) error {
secondaryIndexKeys, err := i.indexerFunc(value)
if err != nil {
return err
}
for _, secondaryIndexKey := range secondaryIndexKeys {
if err := i.addFunc(store, secondaryIndexKey, []byte(rowID)); err != nil {
return err
}
}
return nil
}
// OnDelete removes the secondary index entries for the deleted object.
func (i Indexer) OnDelete(store sdk.KVStore, rowID RowID, value interface{}) error {
secondaryIndexKeys, err := i.indexerFunc(value)
if err != nil {
return err
}
for _, secondaryIndexKey := range secondaryIndexKeys {
indexKey, err := buildKeyFromParts([]interface{}{secondaryIndexKey, []byte(rowID)})
if err != nil {
return err
}
store.Delete(indexKey)
}
return nil
}
// OnUpdate rebuilds the secondary index entries for the updated object.
func (i Indexer) OnUpdate(store sdk.KVStore, rowID RowID, newValue, oldValue interface{}) error {
oldSecIdxKeys, err := i.indexerFunc(oldValue)
if err != nil {
return err
}
newSecIdxKeys, err := i.indexerFunc(newValue)
if err != nil {
return err
}
oldKeys, err := difference(oldSecIdxKeys, newSecIdxKeys)
if err != nil {
return err
}
for _, oldIdxKey := range oldKeys {
indexKey, err := buildKeyFromParts([]interface{}{oldIdxKey, []byte(rowID)})
if err != nil {
return err
}
store.Delete(indexKey)
}
newKeys, err := difference(newSecIdxKeys, oldSecIdxKeys)
if err != nil {
return err
}
for _, newIdxKey := range newKeys {
if err := i.addFunc(store, newIdxKey, rowID); err != nil {
return err
}
}
return nil
}
// uniqueKeysAddFunc enforces keys to be unique
func uniqueKeysAddFunc(store sdk.KVStore, secondaryIndexKey interface{}, rowID RowID) error {
secondaryIndexKeyBytes, err := keyPartBytes(secondaryIndexKey, false)
if err != nil {
return err
}
if len(secondaryIndexKeyBytes) == 0 {
return sdkerrors.Wrap(errors.ErrORMInvalidArgument, "empty index key")
}
it := store.Iterator(PrefixRange(secondaryIndexKeyBytes))
defer it.Close()
if it.Valid() {
return errors.ErrORMUniqueConstraint
}
indexKey, err := buildKeyFromParts([]interface{}{secondaryIndexKey, []byte(rowID)})
if err != nil {
return err
}
store.Set(indexKey, []byte{})
return nil
}
// multiKeyAddFunc allows multiple entries for a key
func multiKeyAddFunc(store sdk.KVStore, secondaryIndexKey interface{}, rowID RowID) error {
secondaryIndexKeyBytes, err := keyPartBytes(secondaryIndexKey, false)
if err != nil {
return err
}
if len(secondaryIndexKeyBytes) == 0 {
return sdkerrors.Wrap(errors.ErrORMInvalidArgument, "empty index key")
}
encodedKey, err := buildKeyFromParts([]interface{}{secondaryIndexKey, []byte(rowID)})
if err != nil {
return err
}
if len(encodedKey) == 0 {
return sdkerrors.Wrap(errors.ErrORMInvalidArgument, "empty index key")
}
store.Set(encodedKey, []byte{})
return nil
}
// difference returns the list of elements that are in a but not in b.
func difference(a []interface{}, b []interface{}) ([]interface{}, error) {
set := make(map[interface{}]struct{}, len(b))
for _, v := range b {
bt, err := keyPartBytes(v, true)
if err != nil {
return nil, err
}
set[string(bt)] = struct{}{}
}
var result []interface{}
for _, v := range a {
bt, err := keyPartBytes(v, true)
if err != nil {
return nil, err
}
if _, ok := set[string(bt)]; !ok {
result = append(result, v)
}
}
return result, nil
}
// pruneEmptyKeys drops any empty key from IndexerFunc f returned
func pruneEmptyKeys(f IndexerFunc) IndexerFunc {
return func(v interface{}) ([]interface{}, error) {
keys, err := f(v)
if err != nil || keys == nil {
return keys, err
}
r := make([]interface{}, 0, len(keys))
for i := range keys {
key, err := keyPartBytes(keys[i], true)
if err != nil {
return nil, err
}
if len(key) != 0 {
r = append(r, keys[i])
}
}
return r, nil
}
}

View File

@ -0,0 +1,570 @@
package orm
import (
stdErrors "errors"
"fmt"
"testing"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewIndexer(t *testing.T) {
testCases := []struct {
name string
indexerFunc IndexerFunc
expectErr bool
expectedErr string
}{
{
name: "nil indexer func",
indexerFunc: nil,
expectErr: true,
expectedErr: "Indexer func must not be nil",
},
{
name: "all not nil",
indexerFunc: func(interface{}) ([]interface{}, error) { return nil, nil },
expectErr: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
indexer, err := NewIndexer(tc.indexerFunc)
if tc.expectErr {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
} else {
require.NoError(t, err)
require.NotNil(t, indexer)
}
})
}
}
func TestNewUniqueIndexer(t *testing.T) {
testCases := []struct {
name string
indexerFunc UniqueIndexerFunc
expectErr bool
expectedErr string
}{
{
name: "nil indexer func",
indexerFunc: nil,
expectErr: true,
expectedErr: "Indexer func must not be nil",
},
{
name: "all not nil",
indexerFunc: func(interface{}) (interface{}, error) { return nil, nil },
expectErr: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
indexer, err := NewUniqueIndexer(tc.indexerFunc)
if tc.expectErr {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
} else {
require.NoError(t, err)
require.NotNil(t, indexer)
}
})
}
}
func TestIndexerOnCreate(t *testing.T) {
var myRowID RowID = EncodeSequence(1)
specs := map[string]struct {
srcFunc IndexerFunc
expIndexKeys []interface{}
expRowIDs []RowID
expAddFuncCalled bool
expErr error
}{
"single key": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{uint64(1)}, nil
},
expAddFuncCalled: true,
expIndexKeys: []interface{}{uint64(1)},
expRowIDs: []RowID{myRowID},
},
"multi key": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{uint64(1), uint64(128)}, nil
},
expAddFuncCalled: true,
expIndexKeys: []interface{}{uint64(1), uint64(128)},
expRowIDs: []RowID{myRowID, myRowID},
},
"empty key in slice": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{[]byte{}}, nil
},
expAddFuncCalled: false,
},
"nil key in slice": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{nil}, nil
},
expErr: fmt.Errorf("type %T not allowed as key part", nil),
expAddFuncCalled: false,
},
"empty key": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{}, nil
},
expAddFuncCalled: false,
},
"nil key": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return nil, nil
},
expAddFuncCalled: false,
},
"error case": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return nil, stdErrors.New("test")
},
expErr: stdErrors.New("test"),
expAddFuncCalled: false,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
mockPolicy := &addFuncRecorder{}
idx, err := NewIndexer(spec.srcFunc)
require.NoError(t, err)
idx.addFunc = mockPolicy.add
err = idx.OnCreate(nil, myRowID, nil)
if spec.expErr != nil {
require.Equal(t, spec.expErr, err)
return
}
require.NoError(t, err)
assert.Equal(t, spec.expIndexKeys, mockPolicy.secondaryIndexKeys)
assert.Equal(t, spec.expRowIDs, mockPolicy.rowIDs)
assert.Equal(t, spec.expAddFuncCalled, mockPolicy.called)
})
}
}
func TestIndexerOnDelete(t *testing.T) {
myRowID := EncodeSequence(1)
var multiKeyIndex MultiKeyIndex
ctx := NewMockContext()
storeKey := sdk.NewKVStoreKey("test")
store := prefix.NewStore(ctx.KVStore(storeKey), []byte{multiKeyIndex.prefix})
specs := map[string]struct {
srcFunc IndexerFunc
expDeletedKeys []RowID
expErr error
}{
"single key": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{uint64(1)}, nil
},
expDeletedKeys: []RowID{append(EncodeSequence(1), myRowID...)},
},
"multi key": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{uint64(1), uint64(128)}, nil
},
expDeletedKeys: []RowID{
append(EncodeSequence(1), myRowID...),
append(EncodeSequence(128), myRowID...),
},
},
"empty key": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{}, nil
},
},
"nil key": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return nil, nil
},
},
"empty key in slice": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{[]byte{}}, nil
},
},
"nil key in slice": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{nil}, nil
},
expErr: fmt.Errorf("type %T not allowed as key part", nil),
},
"error case": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return nil, stdErrors.New("test")
},
expErr: stdErrors.New("test"),
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
idx, err := NewIndexer(spec.srcFunc)
require.NoError(t, err)
if spec.expErr == nil {
err = idx.OnCreate(store, myRowID, nil)
require.NoError(t, err)
for _, key := range spec.expDeletedKeys {
require.Equal(t, true, store.Has(key))
}
}
err = idx.OnDelete(store, myRowID, nil)
if spec.expErr != nil {
require.Equal(t, spec.expErr, err)
return
}
require.NoError(t, err)
for _, key := range spec.expDeletedKeys {
require.Equal(t, false, store.Has(key))
}
})
}
}
func TestIndexerOnUpdate(t *testing.T) {
myRowID := EncodeSequence(1)
var multiKeyIndex MultiKeyIndex
ctx := NewMockContext()
storeKey := sdk.NewKVStoreKey("test")
store := prefix.NewStore(ctx.KVStore(storeKey), []byte{multiKeyIndex.prefix})
specs := map[string]struct {
srcFunc IndexerFunc
expAddedKeys []RowID
expDeletedKeys []RowID
expErr error
addFunc func(sdk.KVStore, interface{}, RowID) error
}{
"single key - same key, no update": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{uint64(1)}, nil
},
},
"single key - different key, replaced": {
srcFunc: func(value interface{}) ([]interface{}, error) {
keys := []uint64{1, 2}
return []interface{}{keys[value.(int)]}, nil
},
expAddedKeys: []RowID{
append(EncodeSequence(2), myRowID...),
},
expDeletedKeys: []RowID{
append(EncodeSequence(1), myRowID...),
},
},
"multi key - same key, no update": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{uint64(1), uint64(2)}, nil
},
},
"multi key - replaced": {
srcFunc: func(value interface{}) ([]interface{}, error) {
keys := []uint64{1, 2, 3, 4}
return []interface{}{keys[value.(int)], keys[value.(int)+2]}, nil
},
expAddedKeys: []RowID{
append(EncodeSequence(2), myRowID...),
append(EncodeSequence(4), myRowID...),
},
expDeletedKeys: []RowID{
append(EncodeSequence(1), myRowID...),
append(EncodeSequence(3), myRowID...),
},
},
"empty key": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{}, nil
},
},
"nil key": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return nil, nil
},
},
"empty key in slice": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{[]byte{}}, nil
},
},
"nil key in slice": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return []interface{}{nil}, nil
},
expErr: fmt.Errorf("type %T not allowed as key part", nil),
},
"error case with new value": {
srcFunc: func(value interface{}) ([]interface{}, error) {
return nil, stdErrors.New("test")
},
expErr: stdErrors.New("test"),
},
"error case with old value": {
srcFunc: func(value interface{}) ([]interface{}, error) {
var err error
if value.(int)%2 == 1 {
err = stdErrors.New("test")
}
return []interface{}{uint64(1)}, err
},
expErr: stdErrors.New("test"),
},
"error case on persisting new keys": {
srcFunc: func(value interface{}) ([]interface{}, error) {
keys := []uint64{1, 2}
return []interface{}{keys[value.(int)]}, nil
},
addFunc: func(_ sdk.KVStore, _ interface{}, _ RowID) error {
return stdErrors.New("test")
},
expErr: stdErrors.New("test"),
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
idx, err := NewIndexer(spec.srcFunc)
require.NoError(t, err)
if spec.expErr == nil {
err = idx.OnCreate(store, myRowID, 0)
require.NoError(t, err)
}
if spec.addFunc != nil {
idx.addFunc = spec.addFunc
}
err = idx.OnUpdate(store, myRowID, 1, 0)
if spec.expErr != nil {
require.Equal(t, spec.expErr, err)
return
}
require.NoError(t, err)
for _, key := range spec.expAddedKeys {
require.Equal(t, true, store.Has(key))
}
for _, key := range spec.expDeletedKeys {
require.Equal(t, false, store.Has(key))
}
})
}
}
func TestUniqueKeyAddFunc(t *testing.T) {
myRowID := EncodeSequence(1)
presetKeyPart := []byte("my-preset-key")
presetKey := append(AddLengthPrefix(presetKeyPart), myRowID...)
specs := map[string]struct {
srcKey []byte
expErr *sdkerrors.Error
expExistingEntry []byte
}{
"create when not exists": {
srcKey: []byte("my-index-key"),
expExistingEntry: append(AddLengthPrefix([]byte("my-index-key")), myRowID...),
},
"error when exists already": {
srcKey: presetKeyPart,
expErr: errors.ErrORMUniqueConstraint,
},
"nil key not allowed": {
srcKey: nil,
expErr: errors.ErrORMInvalidArgument,
},
"empty key not allowed": {
srcKey: []byte{},
expErr: errors.ErrORMInvalidArgument,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
storeKey := sdk.NewKVStoreKey("test")
store := NewMockContext().KVStore(storeKey)
store.Set(presetKey, []byte{})
err := uniqueKeysAddFunc(store, spec.srcKey, myRowID)
require.True(t, spec.expErr.Is(err))
if spec.expErr != nil {
return
}
assert.True(t, store.Has(spec.expExistingEntry), "not found")
})
}
}
func TestMultiKeyAddFunc(t *testing.T) {
myRowID := EncodeSequence(1)
presetKeyPart := []byte("my-preset-key")
presetKey := append(AddLengthPrefix(presetKeyPart), myRowID...)
specs := map[string]struct {
srcKey []byte
expErr *sdkerrors.Error
expExistingEntry []byte
}{
"create when not exists": {
srcKey: []byte("my-index-key"),
expExistingEntry: append(AddLengthPrefix([]byte("my-index-key")), myRowID...),
},
"noop when exists already": {
srcKey: presetKeyPart,
expExistingEntry: presetKey,
},
"nil key not allowed": {
srcKey: nil,
expErr: errors.ErrORMInvalidArgument,
},
"empty key not allowed": {
srcKey: []byte{},
expErr: errors.ErrORMInvalidArgument,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
storeKey := sdk.NewKVStoreKey("test")
store := NewMockContext().KVStore(storeKey)
store.Set(presetKey, []byte{})
err := multiKeyAddFunc(store, spec.srcKey, myRowID)
require.True(t, spec.expErr.Is(err))
if spec.expErr != nil {
return
}
assert.True(t, store.Has(spec.expExistingEntry))
})
}
}
func TestDifference(t *testing.T) {
specs := map[string]struct {
srcA []interface{}
srcB []interface{}
expResult []interface{}
expErr bool
}{
"all of A": {
srcA: []interface{}{"a", "b"},
srcB: []interface{}{"c"},
expResult: []interface{}{"a", "b"},
},
"A - B": {
srcA: []interface{}{"a", "b"},
srcB: []interface{}{"b", "c", "d"},
expResult: []interface{}{"a"},
},
"type in A not allowed": {
srcA: []interface{}{1},
srcB: []interface{}{"b", "c", "d"},
expErr: true,
},
"type in B not allowed": {
srcA: []interface{}{"b", "c", "d"},
srcB: []interface{}{1},
expErr: true,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
got, err := difference(spec.srcA, spec.srcB)
if spec.expErr {
require.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, spec.expResult, got)
}
})
}
}
func TestPruneEmptyKeys(t *testing.T) {
specs := map[string]struct {
srcFunc IndexerFunc
expResult []interface{}
expError error
}{
"non empty": {
srcFunc: func(v interface{}) ([]interface{}, error) {
return []interface{}{uint64(0), uint64(1)}, nil
},
expResult: []interface{}{uint64(0), uint64(1)},
},
"empty": {
srcFunc: func(v interface{}) ([]interface{}, error) {
return []interface{}{}, nil
},
expResult: []interface{}{},
},
"nil": {
srcFunc: func(v interface{}) ([]interface{}, error) {
return nil, nil
},
},
"empty in the beginning": {
srcFunc: func(v interface{}) ([]interface{}, error) {
return []interface{}{[]byte{}, uint64(0), uint64(1)}, nil
},
expResult: []interface{}{uint64(0), uint64(1)},
},
"empty in the middle": {
srcFunc: func(v interface{}) ([]interface{}, error) {
return []interface{}{uint64(0), []byte{}, uint64(1)}, nil
},
expResult: []interface{}{uint64(0), uint64(1)},
},
"empty at the end": {
srcFunc: func(v interface{}) ([]interface{}, error) {
return []interface{}{uint64(0), uint64(1), []byte{}}, nil
},
expResult: []interface{}{uint64(0), uint64(1)},
},
"error passed": {
srcFunc: func(v interface{}) ([]interface{}, error) {
return nil, stdErrors.New("test")
},
expError: stdErrors.New("test"),
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
r, err := pruneEmptyKeys(spec.srcFunc)(nil)
require.Equal(t, spec.expError, err)
if spec.expError != nil {
return
}
assert.Equal(t, spec.expResult, r)
})
}
}
type addFuncRecorder struct {
secondaryIndexKeys []interface{}
rowIDs []RowID
called bool
}
func (c *addFuncRecorder) add(_ sdk.KVStore, key interface{}, rowID RowID) error {
c.secondaryIndexKeys = append(c.secondaryIndexKeys, key)
c.rowIDs = append(c.rowIDs, rowID)
c.called = true
return nil
}

View File

@ -0,0 +1,308 @@
package orm
import (
"fmt"
"reflect"
"github.com/cosmos/cosmos-sdk/codec"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/group/errors"
)
// defaultPageLimit is the default limit value for pagination requests.
const defaultPageLimit = 100
// IteratorFunc is a function type that satisfies the Iterator interface
// The passed function is called on LoadNext operations.
type IteratorFunc func(dest codec.ProtoMarshaler) (RowID, error)
// LoadNext loads the next value in the sequence into the pointer passed as dest and returns the key. If there
// are no more items the errors.ErrORMIteratorDone error is returned
// The key is the rowID and not any MultiKeyIndex key.
func (i IteratorFunc) LoadNext(dest codec.ProtoMarshaler) (RowID, error) {
return i(dest)
}
// Close always returns nil
func (i IteratorFunc) Close() error {
return nil
}
func NewSingleValueIterator(rowID RowID, val []byte) Iterator {
var closed bool
return IteratorFunc(func(dest codec.ProtoMarshaler) (RowID, error) {
if dest == nil {
return nil, sdkerrors.Wrap(errors.ErrORMInvalidArgument, "destination object must not be nil")
}
if closed || val == nil {
return nil, errors.ErrORMIteratorDone
}
closed = true
return rowID, dest.Unmarshal(val)
})
}
// Iterator that return ErrORMInvalidIterator only.
func NewInvalidIterator() Iterator {
return IteratorFunc(func(dest codec.ProtoMarshaler) (RowID, error) {
return nil, errors.ErrORMInvalidIterator
})
}
// LimitedIterator returns up to defined maximum number of elements.
type LimitedIterator struct {
remainingCount int
parentIterator Iterator
}
// LimitIterator returns a new iterator that returns max number of elements.
// The parent iterator must not be nil
// max can be 0 or any positive number
func LimitIterator(parent Iterator, max int) (*LimitedIterator, error) {
if max < 0 {
return nil, errors.ErrORMInvalidArgument.Wrap("quantity must not be negative")
}
if parent == nil {
return nil, errors.ErrORMInvalidArgument.Wrap("parent iterator must not be nil")
}
return &LimitedIterator{remainingCount: max, parentIterator: parent}, nil
}
// LoadNext loads the next value in the sequence into the pointer passed as dest and returns the key. If there
// are no more items or the defined max number of elements was returned the `errors.ErrORMIteratorDone` error is returned
// The key is the rowID and not any MultiKeyIndex key.
func (i *LimitedIterator) LoadNext(dest codec.ProtoMarshaler) (RowID, error) {
if i.remainingCount == 0 {
return nil, errors.ErrORMIteratorDone
}
i.remainingCount--
return i.parentIterator.LoadNext(dest)
}
// Close releases the iterator and should be called at the end of iteration
func (i LimitedIterator) Close() error {
return i.parentIterator.Close()
}
// First loads the first element into the given destination type and closes the iterator.
// When the iterator is closed or has no elements the according error is passed as return value.
func First(it Iterator, dest codec.ProtoMarshaler) (RowID, error) {
if it == nil {
return nil, sdkerrors.Wrap(errors.ErrORMInvalidArgument, "iterator must not be nil")
}
defer it.Close()
binKey, err := it.LoadNext(dest)
if err != nil {
return nil, err
}
return binKey, nil
}
// Paginate does pagination with a given Iterator based on the provided
// PageRequest and unmarshals the results into the dest interface that must be
// an non-nil pointer to a slice.
//
// If pageRequest is nil, then we will use these default values:
// - Offset: 0
// - Key: nil
// - Limit: 100
// - CountTotal: true
//
// If pageRequest.Key was provided, it got used beforehand to instantiate the Iterator,
// using for instance UInt64Index.GetPaginated method. Only one of pageRequest.Offset or
// pageRequest.Key should be set. Using pageRequest.Key is more efficient for querying
// the next page.
//
// If pageRequest.CountTotal is set, we'll visit all iterators elements.
// pageRequest.CountTotal is only respected when offset is used.
//
// This function will call it.Close().
func Paginate(
it Iterator,
pageRequest *query.PageRequest,
dest ModelSlicePtr,
) (*query.PageResponse, error) {
// if the PageRequest is nil, use default PageRequest
if pageRequest == nil {
pageRequest = &query.PageRequest{}
}
offset := pageRequest.Offset
key := pageRequest.Key
limit := pageRequest.Limit
countTotal := pageRequest.CountTotal
if offset > 0 && key != nil {
return nil, fmt.Errorf("invalid request, either offset or key is expected, got both")
}
if limit == 0 {
limit = defaultPageLimit
// count total results when the limit is zero/not supplied
countTotal = true
}
if it == nil {
return nil, sdkerrors.Wrap(errors.ErrORMInvalidArgument, "iterator must not be nil")
}
defer it.Close()
var destRef, tmpSlice reflect.Value
elemType, err := assertDest(dest, &destRef, &tmpSlice)
if err != nil {
return nil, err
}
var end = offset + limit
var count uint64
var nextKey []byte
for {
obj := reflect.New(elemType)
val := obj.Elem()
model := obj
if elemType.Kind() == reflect.Ptr {
val.Set(reflect.New(elemType.Elem()))
// if elemType is already a pointer (e.g. dest being some pointer to a slice of pointers,
// like []*GroupMember), then obj is a pointer to a pointer which might cause issues
// if we try to do obj.Interface().(codec.ProtoMarshaler).
// For that reason, we copy obj into model if we have a simple pointer
// but in case elemType.Kind() == reflect.Ptr, we overwrite it with model = val
// so we can safely call model.Interface().(codec.ProtoMarshaler) afterwards.
model = val
}
modelProto, ok := model.Interface().(codec.ProtoMarshaler)
if !ok {
return nil, sdkerrors.Wrapf(errors.ErrORMInvalidArgument, "%s should implement codec.ProtoMarshaler", elemType)
}
binKey, err := it.LoadNext(modelProto)
if err != nil {
if errors.ErrORMIteratorDone.Is(err) {
break
}
return nil, err
}
count++
// During the first loop, count value at this point will be 1,
// so if offset is >= 1, it will continue to load the next value until count > offset
// else (offset = 0, key might be set or not),
// it will start to append values to tmpSlice.
if count <= offset {
continue
}
if count <= end {
tmpSlice = reflect.Append(tmpSlice, val)
} else if count == end+1 {
nextKey = binKey
// countTotal is set to true to indicate that the result set should include
// a count of the total number of items available for pagination in UIs.
// countTotal is only respected when offset is used. It is ignored when key
// is set.
if !countTotal || len(key) != 0 {
break
}
}
}
destRef.Set(tmpSlice)
res := &query.PageResponse{NextKey: nextKey}
if countTotal && len(key) == 0 {
res.Total = count
}
return res, nil
}
// ModelSlicePtr represents a pointer to a slice of models. Think of it as
// *[]Model Because of Go's type system, using []Model type would not work for us.
// Instead we use a placeholder type and the validation is done during the
// runtime.
type ModelSlicePtr interface{}
// ReadAll consumes all values for the iterator and stores them in a new slice at the passed ModelSlicePtr.
// The slice can be empty when the iterator does not return any values but not nil. The iterator
// is closed afterwards.
// Example:
// var loaded []testdata.GroupInfo
// rowIDs, err := ReadAll(it, &loaded)
// require.NoError(t, err)
//
func ReadAll(it Iterator, dest ModelSlicePtr) ([]RowID, error) {
if it == nil {
return nil, sdkerrors.Wrap(errors.ErrORMInvalidArgument, "iterator must not be nil")
}
defer it.Close()
var destRef, tmpSlice reflect.Value
elemType, err := assertDest(dest, &destRef, &tmpSlice)
if err != nil {
return nil, err
}
var rowIDs []RowID
for {
obj := reflect.New(elemType)
val := obj.Elem()
model := obj
if elemType.Kind() == reflect.Ptr {
val.Set(reflect.New(elemType.Elem()))
model = val
}
binKey, err := it.LoadNext(model.Interface().(codec.ProtoMarshaler))
switch {
case err == nil:
tmpSlice = reflect.Append(tmpSlice, val)
case errors.ErrORMIteratorDone.Is(err):
destRef.Set(tmpSlice)
return rowIDs, nil
default:
return nil, err
}
rowIDs = append(rowIDs, binKey)
}
}
// assertDest checks that the provided dest is not nil and a pointer to a slice.
// It also verifies that the slice elements implement *codec.ProtoMarshaler.
// It overwrites destRef and tmpSlice using reflection.
func assertDest(dest ModelSlicePtr, destRef *reflect.Value, tmpSlice *reflect.Value) (reflect.Type, error) {
if dest == nil {
return nil, sdkerrors.Wrap(errors.ErrORMInvalidArgument, "destination must not be nil")
}
tp := reflect.ValueOf(dest)
if tp.Kind() != reflect.Ptr {
return nil, sdkerrors.Wrap(errors.ErrORMInvalidArgument, "destination must be a pointer to a slice")
}
if tp.Elem().Kind() != reflect.Slice {
return nil, sdkerrors.Wrap(errors.ErrORMInvalidArgument, "destination must point to a slice")
}
// Since dest is just an interface{}, we overwrite destRef using reflection
// to have an assignable copy of it.
*destRef = tp.Elem()
// We need to verify that we can call Set() on destRef.
if !destRef.CanSet() {
return nil, sdkerrors.Wrap(errors.ErrORMInvalidArgument, "destination not assignable")
}
elemType := reflect.TypeOf(dest).Elem().Elem()
protoMarshaler := reflect.TypeOf((*codec.ProtoMarshaler)(nil)).Elem()
if !elemType.Implements(protoMarshaler) &&
!reflect.PtrTo(elemType).Implements(protoMarshaler) {
return nil, sdkerrors.Wrapf(errors.ErrORMInvalidArgument, "unsupported type :%s", elemType)
}
// tmpSlice is a slice value for the specified type
// that we'll use for appending new elements.
*tmpSlice = reflect.MakeSlice(reflect.SliceOf(elemType), 0, 0)
return elemType, nil
}

View File

@ -0,0 +1,121 @@
package orm
import (
"testing"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/group/errors"
"github.com/stretchr/testify/require"
"pgregory.net/rapid"
)
func TestPaginationProperty(t *testing.T) {
t.Run("TestPagination", rapid.MakeCheck(func(t *rapid.T) {
// Create a slice of group members
tableModels := rapid.SliceOf(genTableModel).Draw(t, "tableModels").([]*testdata.TableModel)
// Choose a random limit for paging
upperLimit := uint64(len(tableModels))
if upperLimit == 0 {
upperLimit = 1
}
limit := rapid.Uint64Range(1, upperLimit).Draw(t, "limit").(uint64)
// Reconstruct the slice from offset pages
reconstructedTableModels := make([]*testdata.TableModel, 0, len(tableModels))
for offset := uint64(0); offset < uint64(len(tableModels)); offset += limit {
pageRequest := &query.PageRequest{
Key: nil,
Offset: offset,
Limit: limit,
CountTotal: false,
Reverse: false,
}
end := offset + limit
if end > uint64(len(tableModels)) {
end = uint64(len(tableModels))
}
dest := reconstructedTableModels[offset:end]
tableModelsIt := testTableModelIterator(tableModels, nil)
Paginate(tableModelsIt, pageRequest, &dest)
reconstructedTableModels = append(reconstructedTableModels, dest...)
}
// Should be the same slice
require.Equal(t, len(tableModels), len(reconstructedTableModels))
for i, gm := range tableModels {
require.Equal(t, *gm, *reconstructedTableModels[i])
}
// Reconstruct the slice from keyed pages
reconstructedTableModels = make([]*testdata.TableModel, 0, len(tableModels))
var start uint64 = 0
key := EncodeSequence(0)
for key != nil {
pageRequest := &query.PageRequest{
Key: key,
Offset: 0,
Limit: limit,
CountTotal: false,
Reverse: false,
}
end := start + limit
if end > uint64(len(tableModels)) {
end = uint64(len(tableModels))
}
dest := reconstructedTableModels[start:end]
tableModelsIt := testTableModelIterator(tableModels, key)
resp, err := Paginate(tableModelsIt, pageRequest, &dest)
require.NoError(t, err)
key = resp.NextKey
reconstructedTableModels = append(reconstructedTableModels, dest...)
start += limit
}
// Should be the same slice
require.Equal(t, len(tableModels), len(reconstructedTableModels))
for i, gm := range tableModels {
require.Equal(t, *gm, *reconstructedTableModels[i])
}
}))
}
func testTableModelIterator(tms []*testdata.TableModel, key RowID) Iterator {
var closed bool
var index int
if key != nil {
index = int(DecodeSequence(key))
}
return IteratorFunc(func(dest codec.ProtoMarshaler) (RowID, error) {
if dest == nil {
return nil, sdkerrors.Wrap(errors.ErrORMInvalidArgument, "destination object must not be nil")
}
if index == len(tms) {
closed = true
}
if closed {
return nil, errors.ErrORMIteratorDone
}
rowID := EncodeSequence(uint64(index))
bytes, err := tms[index].Marshal()
if err != nil {
return nil, err
}
index++
return rowID, dest.Unmarshal(bytes)
})
}

View File

@ -0,0 +1,358 @@
package orm
import (
"testing"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/group/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestReadAll(t *testing.T) {
specs := map[string]struct {
srcIT Iterator
destSlice func() ModelSlicePtr
expErr *sdkerrors.Error
expIDs []RowID
expResult ModelSlicePtr
}{
"all good with object slice": {
srcIT: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
destSlice: func() ModelSlicePtr {
x := make([]testdata.TableModel, 1)
return &x
},
expIDs: []RowID{EncodeSequence(1)},
expResult: &[]testdata.TableModel{{Name: "test"}},
},
"all good with pointer slice": {
srcIT: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
destSlice: func() ModelSlicePtr {
x := make([]*testdata.TableModel, 1)
return &x
},
expIDs: []RowID{EncodeSequence(1)},
expResult: &[]*testdata.TableModel{{Name: "test"}},
},
"dest slice empty": {
srcIT: mockIter(EncodeSequence(1), &testdata.TableModel{}),
destSlice: func() ModelSlicePtr {
x := make([]testdata.TableModel, 0)
return &x
},
expIDs: []RowID{EncodeSequence(1)},
expResult: &[]testdata.TableModel{{}},
},
"dest pointer with nil value": {
srcIT: mockIter(EncodeSequence(1), &testdata.TableModel{}),
destSlice: func() ModelSlicePtr {
return (*[]testdata.TableModel)(nil)
},
expErr: errors.ErrORMInvalidArgument,
},
"iterator is nil": {
srcIT: nil,
destSlice: func() ModelSlicePtr { return new([]testdata.TableModel) },
expErr: errors.ErrORMInvalidArgument,
},
"dest slice is nil": {
srcIT: noopIter(),
destSlice: func() ModelSlicePtr { return nil },
expErr: errors.ErrORMInvalidArgument,
},
"dest slice is not a pointer": {
srcIT: IteratorFunc(nil),
destSlice: func() ModelSlicePtr { return make([]testdata.TableModel, 1) },
expErr: errors.ErrORMInvalidArgument,
},
"error on loadNext is returned": {
srcIT: NewInvalidIterator(),
destSlice: func() ModelSlicePtr {
x := make([]testdata.TableModel, 1)
return &x
},
expErr: errors.ErrORMInvalidIterator,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
loaded := spec.destSlice()
ids, err := ReadAll(spec.srcIT, loaded)
require.True(t, spec.expErr.Is(err), "expected %s but got %s", spec.expErr, err)
assert.Equal(t, spec.expIDs, ids)
if err == nil {
assert.Equal(t, spec.expResult, loaded)
}
})
}
}
func TestLimitedIterator(t *testing.T) {
specs := map[string]struct {
parent Iterator
max int
expectErr bool
expectedErr string
exp []testdata.TableModel
}{
"nil parent": {
parent: nil,
max: 0,
expectErr: true,
expectedErr: "parent iterator must not be nil",
},
"negative max": {
parent: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
max: -1,
expectErr: true,
expectedErr: "quantity must not be negative",
},
"all from range with max > length": {
parent: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
max: 2,
exp: []testdata.TableModel{{Name: "test"}},
},
"up to max": {
parent: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
max: 1,
exp: []testdata.TableModel{{Name: "test"}},
},
"none when max = 0": {
parent: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
max: 0,
exp: []testdata.TableModel{},
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
src, err := LimitIterator(spec.parent, spec.max)
if spec.expectErr {
require.Error(t, err)
require.Contains(t, err.Error(), spec.expectedErr)
} else {
require.NoError(t, err)
var loaded []testdata.TableModel
_, err := ReadAll(src, &loaded)
require.NoError(t, err)
assert.EqualValues(t, spec.exp, loaded)
}
})
}
}
func TestFirst(t *testing.T) {
testCases := []struct {
name string
iterator Iterator
dest codec.ProtoMarshaler
expectErr bool
expectedErr string
expectedRowID RowID
expectedDest codec.ProtoMarshaler
}{
{
name: "nil iterator",
iterator: nil,
dest: &testdata.TableModel{},
expectErr: true,
expectedErr: "iterator must not be nil",
},
{
name: "nil dest",
iterator: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
dest: nil,
expectErr: true,
expectedErr: "destination object must not be nil",
},
{
name: "all not nil",
iterator: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
dest: &testdata.TableModel{},
expectErr: false,
expectedRowID: EncodeSequence(1),
expectedDest: &testdata.TableModel{Name: "test"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
rowID, err := First(tc.iterator, tc.dest)
if tc.expectErr {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
} else {
require.NoError(t, err)
require.Equal(t, tc.expectedRowID, rowID)
require.Equal(t, tc.expectedDest, tc.dest)
}
})
}
}
func TestPaginate(t *testing.T) {
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
tb, err := NewAutoUInt64Table(AutoUInt64TablePrefix, AutoUInt64TableSeqPrefix, &testdata.TableModel{}, cdc)
require.NoError(t, err)
idx, err := NewIndex(tb, AutoUInt64TableModelByMetadataPrefix, func(val interface{}) ([]interface{}, error) {
return []interface{}{[]byte(val.(*testdata.TableModel).Metadata)}, nil
}, testdata.TableModel{}.Metadata)
require.NoError(t, err)
ctx := NewMockContext()
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
metadata := []byte("metadata")
t1 := testdata.TableModel{
Id: 1,
Name: "my test 1",
Metadata: metadata,
}
t2 := testdata.TableModel{
Id: 2,
Name: "my test 2",
Metadata: metadata,
}
t3 := testdata.TableModel{
Id: 3,
Name: "my test 3",
Metadata: []byte("other-metadata"),
}
t4 := testdata.TableModel{
Id: 4,
Name: "my test 4",
Metadata: metadata,
}
t5 := testdata.TableModel{
Id: 5,
Name: "my test 5",
Metadata: []byte("other-metadata"),
}
for _, g := range []testdata.TableModel{t1, t2, t3, t4, t5} {
_, err := tb.Create(store, &g)
require.NoError(t, err)
}
specs := map[string]struct {
pageReq *query.PageRequest
expPageRes *query.PageResponse
exp []testdata.TableModel
key []byte
expErr bool
}{
"one item": {
pageReq: &query.PageRequest{Key: nil, Limit: 1},
exp: []testdata.TableModel{t1},
expPageRes: &query.PageResponse{Total: 0, NextKey: EncodeSequence(2)},
key: metadata,
},
"with both key and offset": {
pageReq: &query.PageRequest{Key: EncodeSequence(2), Offset: 1},
expErr: true,
key: metadata,
},
"up to max": {
pageReq: &query.PageRequest{Key: nil, Limit: 3, CountTotal: true},
exp: []testdata.TableModel{t1, t2, t4},
expPageRes: &query.PageResponse{Total: 3, NextKey: nil},
key: metadata,
},
"no results": {
pageReq: &query.PageRequest{Key: nil, Limit: 2, CountTotal: true},
exp: []testdata.TableModel{},
expPageRes: &query.PageResponse{Total: 0, NextKey: nil},
key: sdk.AccAddress([]byte("no-group-address")),
},
"with offset and count total": {
pageReq: &query.PageRequest{Key: nil, Offset: 1, Limit: 2, CountTotal: true},
exp: []testdata.TableModel{t2, t4},
expPageRes: &query.PageResponse{Total: 3, NextKey: nil},
key: metadata,
},
"nil/default page req (limit = 100 > number of items)": {
pageReq: nil,
exp: []testdata.TableModel{t1, t2, t4},
expPageRes: &query.PageResponse{Total: 3, NextKey: nil},
key: metadata,
},
"with key and limit < number of elem (count total is ignored in this case)": {
pageReq: &query.PageRequest{Key: EncodeSequence(2), Limit: 1, CountTotal: true},
exp: []testdata.TableModel{t2},
expPageRes: &query.PageResponse{Total: 0, NextKey: EncodeSequence(4)},
key: metadata,
},
"with key and limit >= number of elem": {
pageReq: &query.PageRequest{Key: EncodeSequence(2), Limit: 2},
exp: []testdata.TableModel{t2, t4},
expPageRes: &query.PageResponse{Total: 0, NextKey: nil},
key: metadata,
},
"with nothing left to iterate from key": {
pageReq: &query.PageRequest{Key: EncodeSequence(5)},
exp: []testdata.TableModel{},
expPageRes: &query.PageResponse{Total: 0, NextKey: nil},
key: metadata,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
var loaded []testdata.TableModel
it, err := idx.GetPaginated(store, spec.key, spec.pageReq)
require.NoError(t, err)
res, err := Paginate(it, spec.pageReq, &loaded)
if spec.expErr {
require.Error(t, err)
} else {
require.NoError(t, err)
assert.EqualValues(t, spec.exp, loaded)
assert.EqualValues(t, spec.expPageRes.Total, res.Total)
assert.EqualValues(t, spec.expPageRes.NextKey, res.NextKey)
}
})
}
t.Run("nil iterator", func(t *testing.T) {
var loaded []testdata.TableModel
res, err := Paginate(nil, &query.PageRequest{}, &loaded)
require.Error(t, err)
require.Contains(t, err.Error(), "iterator must not be nil")
require.Nil(t, res)
})
t.Run("non-slice destination", func(t *testing.T) {
var loaded testdata.TableModel
res, err := Paginate(
mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
&query.PageRequest{},
&loaded,
)
require.Error(t, err)
require.Contains(t, err.Error(), "destination must point to a slice")
require.Nil(t, res)
})
}
// mockIter encodes + decodes value object.
func mockIter(rowID RowID, val codec.ProtoMarshaler) Iterator {
b, err := val.Marshal()
if err != nil {
panic(err)
}
return NewSingleValueIterator(rowID, b)
}
func noopIter() Iterator {
return IteratorFunc(func(dest codec.ProtoMarshaler) (RowID, error) {
return nil, nil
})
}

View File

@ -3,7 +3,8 @@ package orm
import (
"fmt"
"github.com/cosmos/cosmos-sdk/types/errors"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
)
// MaxBytesLen is the maximum allowed length for a key part of type []byte
@ -58,7 +59,7 @@ func keyPartBytes(part interface{}, last bool) ([]byte, error) {
func AddLengthPrefix(bytes []byte) []byte {
byteLen := len(bytes)
if byteLen > MaxBytesLen {
panic(errors.Wrap(errors.ErrORMKeyMaxLength, "Cannot create key part with an []byte of length greater than 255 bytes. Try again with a smaller []byte."))
panic(sdkerrors.Wrap(errors.ErrORMKeyMaxLength, "Cannot create key part with an []byte of length greater than 255 bytes. Try again with a smaller []byte."))
}
prefixedBytes := make([]byte, 1+len(bytes))
@ -73,3 +74,27 @@ func NullTerminatedBytes(s string) []byte {
copy(bytes, s)
return bytes
}
// stripRowID returns the RowID from the indexKey based on secondaryIndexKey type.
// It is the reverse operation to buildKeyFromParts for index keys
// where the first part is the encoded secondaryIndexKey and the second part is the RowID.
func stripRowID(indexKey []byte, secondaryIndexKey interface{}) (RowID, error) {
switch v := secondaryIndexKey.(type) {
case []byte:
searchableKeyLen := indexKey[0]
return indexKey[1+searchableKeyLen:], nil
case string:
searchableKeyLen := 0
for i, b := range indexKey {
if b == 0 {
searchableKeyLen = i
break
}
}
return indexKey[1+searchableKeyLen:], nil
case uint64:
return indexKey[EncodedSeqLength:], nil
default:
return nil, fmt.Errorf("type %T not allowed as index key", v)
}
}

View File

@ -8,7 +8,8 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
@ -45,8 +46,22 @@ func TestKeeperEndToEndWithAutoUInt64Table(t *testing.T) {
require.Equal(t, rowID, binary.BigEndian.Uint64(binKey))
require.Equal(t, tm, loaded)
// and exists in MultiKeyIndex
exists, err = k.autoUInt64TableModelByMetadataIndex.Has(store, []byte("metadata"))
require.NoError(t, err)
require.True(t, exists)
// and when loaded
it, err := k.autoUInt64TableModelByMetadataIndex.Get(store, []byte("metadata"))
require.NoError(t, err)
// then
binKey, loaded = first(t, it)
assert.Equal(t, rowID, binary.BigEndian.Uint64(binKey))
assert.Equal(t, tm, loaded)
// when updated
tm.Name = "new name"
tm.Metadata = []byte("new-metadata")
err = k.autoUInt64Table.Update(store, rowID, &tm)
require.NoError(t, err)
@ -56,12 +71,26 @@ func TestKeeperEndToEndWithAutoUInt64Table(t *testing.T) {
require.Equal(t, rowID, binary.BigEndian.Uint64(binKey))
require.Equal(t, tm, loaded)
// then indexes are updated, too
exists, err = k.autoUInt64TableModelByMetadataIndex.Has(store, []byte("new-metadata"))
require.NoError(t, err)
require.True(t, exists)
exists, err = k.autoUInt64TableModelByMetadataIndex.Has(store, []byte("metadata"))
require.NoError(t, err)
require.False(t, exists)
// when deleted
err = k.autoUInt64Table.Delete(store, rowID)
require.NoError(t, err)
exists = k.autoUInt64Table.Has(store, rowID)
require.False(t, exists)
// and also removed from secondary MultiKeyIndex
exists, err = k.autoUInt64TableModelByMetadataIndex.Has(store, []byte("new-metadata"))
require.NoError(t, err)
require.False(t, exists)
}
func TestKeeperEndToEndWithPrimaryKeyTable(t *testing.T) {
@ -95,6 +124,20 @@ func TestKeeperEndToEndWithPrimaryKeyTable(t *testing.T) {
// then values should match expectations
require.Equal(t, tm, loaded)
// and then the data should exists in MultiKeyIndex
exists, err = k.primaryKeyTableModelByNumberIndex.Has(store, tm.Number)
require.NoError(t, err)
require.True(t, exists)
// and when loaded from MultiKeyIndex
it, err := k.primaryKeyTableModelByNumberIndex.Get(store, tm.Number)
require.NoError(t, err)
// then values should match as before
_, err = First(it, &loaded)
require.NoError(t, err)
assert.Equal(t, tm, loaded)
// and when we create another entry with the same primary key
err = k.primaryKeyTable.Create(store, &tm)
// then it should fail as the primary key must be unique
@ -126,8 +169,14 @@ func TestKeeperEndToEndWithPrimaryKeyTable(t *testing.T) {
err = k.primaryKeyTable.Delete(store, &tm)
require.NoError(t, err)
// it is removed from primaryKeyTable
exists = k.primaryKeyTable.Has(store, primaryKey)
require.False(t, exists)
// and removed from secondary MultiKeyIndex
exists, err = k.primaryKeyTableModelByNumberIndex.Has(store, tm.Number)
require.NoError(t, err)
require.False(t, exists)
}
func TestGasCostsPrimaryKeyTable(t *testing.T) {
@ -161,6 +210,16 @@ func TestGasCostsPrimaryKeyTable(t *testing.T) {
require.NoError(t, err)
t.Logf("gas consumed on get by primary key: %d", gCtx.GasConsumed())
// get by secondary index
gCtx.ResetGasMeter()
// and when loaded from MultiKeyIndex
it, err := k.primaryKeyTableModelByNumberIndex.Get(gCtx.KVStore(store), tm.Number)
require.NoError(t, err)
var loadedSlice []testdata.TableModel
_, err = ReadAll(it, &loadedSlice)
require.NoError(t, err)
t.Logf("gas consumed on get by multi index key: %d", gCtx.GasConsumed())
// delete
gCtx.ResetGasMeter()
err = k.primaryKeyTable.Delete(gCtx.KVStore(store), &tm)
@ -196,6 +255,16 @@ func TestGasCostsPrimaryKeyTable(t *testing.T) {
t.Logf("%d: gas consumed on get by primary key: %d", i, gCtx.GasConsumed())
}
// get by secondary index
gCtx.ResetGasMeter()
// and when loaded from MultiKeyIndex
it, err = k.primaryKeyTableModelByNumberIndex.Get(gCtx.KVStore(store), tm.Number)
require.NoError(t, err)
_, err = ReadAll(it, &loadedSlice)
require.NoError(t, err)
require.Len(t, loadedSlice, 3)
t.Logf("gas consumed on get by multi index key: %d", gCtx.GasConsumed())
// delete
for i, m := range tms {
gCtx.ResetGasMeter()
@ -205,3 +274,10 @@ func TestGasCostsPrimaryKeyTable(t *testing.T) {
t.Logf("%d: gas consumed on delete: %d", i, gCtx.GasConsumed())
}
}
func first(t *testing.T, it Iterator) ([]byte, testdata.TableModel) {
var loaded testdata.TableModel
key, err := First(it, &loaded)
require.NoError(t, err)
return key, loaded
}

View File

@ -111,3 +111,36 @@ func (a PrimaryKeyTable) Contains(store sdk.KVStore, obj PrimaryKeyed) bool {
func (a PrimaryKeyTable) GetOne(store sdk.KVStore, primKey RowID, dest codec.ProtoMarshaler) error {
return a.table.GetOne(store, primKey, dest)
}
// PrefixScan returns an Iterator over a domain of keys in ascending order. End is exclusive.
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned.
// Iterator must be closed by caller.
// To iterate over entire domain, use PrefixScan(nil, nil)
//
// WARNING: The use of a PrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
// this as an endpoint to the public without further limits.
// Example:
// it, err := idx.PrefixScan(ctx, start, end)
// if err !=nil {
// return err
// }
// const defaultLimit = 20
// it = LimitIterator(it, defaultLimit)
//
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
func (a PrimaryKeyTable) PrefixScan(store sdk.KVStore, start, end []byte) (Iterator, error) {
return a.table.PrefixScan(store, start, end)
}
// ReversePrefixScan returns an Iterator over a domain of keys in descending order. End is exclusive.
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned.
// Iterator must be closed by caller.
// To iterate over entire domain, use PrefixScan(nil, nil)
//
// WARNING: The use of a ReversePrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
// this as an endpoint to the public without further limits. See `LimitIterator`
//
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
func (a PrimaryKeyTable) ReversePrefixScan(store sdk.KVStore, start, end []byte) (Iterator, error) {
return a.table.ReversePrefixScan(store, start, end)
}

View File

@ -6,12 +6,202 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
)
func TestPrimaryKeyTablePrefixScan(t *testing.T) {
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
tb, err := NewPrimaryKeyTable(PrimaryKeyTablePrefix, &testdata.TableModel{}, cdc)
require.NoError(t, err)
ctx := NewMockContext()
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
metadata := []byte("metadata")
t1 := testdata.TableModel{
Id: 1,
Name: "my test 1",
Metadata: metadata,
}
t2 := testdata.TableModel{
Id: 2,
Name: "my test 2",
Metadata: metadata,
}
t3 := testdata.TableModel{
Id: 3,
Name: "my test 3",
Metadata: metadata,
}
for _, g := range []testdata.TableModel{t1, t2, t3} {
require.NoError(t, tb.Create(store, &g))
}
specs := map[string]struct {
start, end []byte
expResult []testdata.TableModel
expRowIDs []RowID
expError *sdkerrors.Error
method func(store sdk.KVStore, start, end []byte) (Iterator, error)
}{
"exact match with a single result": {
start: EncodeSequence(1), // == PrimaryKey(&t1)
end: EncodeSequence(2), // == PrimaryKey(&t2)
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1},
expRowIDs: []RowID{PrimaryKey(&t1)},
},
"one result by 1st byte": {
start: []byte{0},
end: EncodeSequence(2), // == PrimaryKey(&t2)
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1},
expRowIDs: []RowID{PrimaryKey(&t1)},
},
"open end query": {
start: EncodeSequence(3),
end: nil,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t3},
expRowIDs: []RowID{PrimaryKey(&t3)},
},
"open end query with all": {
start: EncodeSequence(1),
end: nil,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1, t2, t3},
expRowIDs: []RowID{PrimaryKey(&t1), PrimaryKey(&t2), PrimaryKey(&t3)},
},
"open start query": {
start: nil,
end: EncodeSequence(3),
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1, t2},
expRowIDs: []RowID{PrimaryKey(&t1), PrimaryKey(&t2)},
},
"open start and end query": {
start: nil,
end: nil,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1, t2, t3},
expRowIDs: []RowID{PrimaryKey(&t1), PrimaryKey(&t2), PrimaryKey(&t3)},
},
"all matching 1st byte": {
start: []byte{0},
end: nil,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1, t2, t3},
expRowIDs: []RowID{PrimaryKey(&t1), PrimaryKey(&t2), PrimaryKey(&t3)},
},
"non matching 1st byte": {
start: []byte{1},
end: nil,
method: tb.PrefixScan,
expResult: []testdata.TableModel{},
},
"start equals end": {
start: EncodeSequence(1),
end: EncodeSequence(1),
method: tb.PrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"start after end": {
start: EncodeSequence(2),
end: EncodeSequence(1),
method: tb.PrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"reverse: exact match with a single result": {
start: EncodeSequence(1), // == PrimaryKey(&t1)
end: EncodeSequence(2), // == PrimaryKey(&t2)
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t1},
expRowIDs: []RowID{PrimaryKey(&t1)},
},
"reverse: one result by 1st byte": {
start: []byte{0},
end: EncodeSequence(2), // == PrimaryKey(&t2)
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t1},
expRowIDs: []RowID{PrimaryKey(&t1)},
},
"reverse: open end query": {
start: EncodeSequence(3),
end: nil,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t3},
expRowIDs: []RowID{PrimaryKey(&t3)},
},
"reverse: open end query with all": {
start: EncodeSequence(1),
end: nil,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t3, t2, t1},
expRowIDs: []RowID{PrimaryKey(&t3), PrimaryKey(&t2), PrimaryKey(&t1)},
},
"reverse: open start query": {
start: nil,
end: EncodeSequence(3),
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t2, t1},
expRowIDs: []RowID{PrimaryKey(&t2), PrimaryKey(&t1)},
},
"reverse: open start and end query": {
start: nil,
end: nil,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t3, t2, t1},
expRowIDs: []RowID{PrimaryKey(&t3), PrimaryKey(&t2), PrimaryKey(&t1)},
},
"reverse: all matching 1st byte": {
start: []byte{0},
end: nil,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t3, t2, t1},
expRowIDs: []RowID{PrimaryKey(&t3), PrimaryKey(&t2), PrimaryKey(&t1)},
},
"reverse: non matching prefix": {
start: []byte{1},
end: nil,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{},
},
"reverse: start equals end": {
start: EncodeSequence(1),
end: EncodeSequence(1),
method: tb.ReversePrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"reverse: start after end": {
start: EncodeSequence(2),
end: EncodeSequence(1),
method: tb.ReversePrefixScan,
expError: errors.ErrORMInvalidArgument,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
it, err := spec.method(store, spec.start, spec.end)
require.True(t, spec.expError.Is(err), "expected #+v but got #+v", spec.expError, err)
if spec.expError != nil {
return
}
var loaded []testdata.TableModel
rowIDs, err := ReadAll(it, &loaded)
require.NoError(t, err)
assert.Equal(t, spec.expResult, loaded)
assert.Equal(t, spec.expRowIDs, rowIDs)
})
}
}
func TestContains(t *testing.T) {
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
@ -19,7 +209,7 @@ func TestContains(t *testing.T) {
ctx := NewMockContext()
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
tb, err := NewPrimaryKeyTable([2]byte{0x1}, &testdata.TableModel{}, cdc)
tb, err := NewPrimaryKeyTable(PrimaryKeyTablePrefix, &testdata.TableModel{}, cdc)
require.NoError(t, err)
obj := testdata.TableModel{

View File

@ -5,7 +5,8 @@ import (
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
)
// sequenceStorageKey is a fix key to read/ write data on the storage layer
@ -55,7 +56,7 @@ func (s Sequence) PeekNextVal(store sdk.KVStore) uint64 {
func (s Sequence) InitVal(store sdk.KVStore, seq uint64) error {
pStore := prefix.NewStore(store, []byte{s.prefix})
if pStore.Has(sequenceStorageKey) {
return errors.Wrap(errors.ErrORMUniqueConstraint, "already initialized")
return sdkerrors.Wrap(errors.ErrORMUniqueConstraint, "already initialized")
}
pStore.Set(sequenceStorageKey, EncodeSequence(seq))
return nil

View File

@ -4,7 +4,7 @@ import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

View File

@ -32,9 +32,11 @@ The model provided for creating a `PrimaryKeyTable` should implement the `Primar
+++ https://github.com/cosmos/cosmos-sdk/blob/9f78f16ae75cc42fc5fe636bde18a453ba74831f/x/group/internal/orm/primary_key.go#L28-L41
`PrimaryKeyFields()` method returns the list of key parts for a given object.
The primary key parts can be []byte, string, and `uint64` types.
Key parts, except the last part, follow these rules:
The primary key parts can be []byte, string, and `uint64` types.
- []byte is encoded with a single byte length prefix
- strings are null-terminated
- `uint64` are encoded using 8 byte big endian.
### Key codec
Key parts, except the last part, follow these rules:
- []byte is encoded with a single byte length prefix
- strings are null-terminated
- `uint64` are encoded using 8 byte big endian.

View File

@ -0,0 +1,24 @@
# Secondary Index
Secondary indexes can be used on `Indexable` [tables](01_table.md). Indeed, those tables implement the `Indexable` interface that provides a set of functions that can be called by indexes to register and interact with the tables, like callback functions that are called on entries creation, update or deletion to create, update or remove corresponding entries in the table secondary indexes.
+++ https://github.com/cosmos/cosmos-sdk/blob/430163ed4eefcc0d67b706411ffc0b7c5414cd90/x/group/internal/orm/types.go#L88-L92
## MultiKeyIndex
A `MultiKeyIndex` is an index where multiple entries can point to the same underlying object.
+++ https://github.com/cosmos/cosmos-sdk/blob/430163ed4eefcc0d67b706411ffc0b7c5414cd90/x/group/internal/orm/index.go#L25-L31
Internally, it uses an `Indexer` that manages the persistence of the index based on searchable keys and create/update/delete operations.
+++ https://github.com/cosmos/cosmos-sdk/blob/430163ed4eefcc0d67b706411ffc0b7c5414cd90/x/group/internal/orm/index.go#L15-L19
+++ https://github.com/cosmos/cosmos-sdk/blob/430163ed4eefcc0d67b706411ffc0b7c5414cd90/x/group/internal/orm/indexer.go#L15-L18
The currently used implementation of an `indexer`, `Indexer`, relies on an `IndexerFunc` that should be provided when instantiating the index. Based on the source object, this function returns one or multiple index keys as `[]interface{}`. Such secondary index keys should be bytes, string or `uint64` in order to be handled properly by the [key codec](01_table.md#key-codec) which defines specific encoding for those types.
In the index prefix store, the keys are built based on the source object's `RowID` and its secondary index key(s) using the key codec and the values are set as empty bytes.
## UniqueIndex
As opposed to `MultiKeyIndex`, `UniqueIndex` is an index where duplicate keys are prohibited.

View File

@ -0,0 +1,28 @@
# Iterator and Pagination
Both [tables](01_table.md) and [secondary indexes](02_secondary_index.md) support iterating over a domain of keys, through `PrefixScan` or `ReversePrefixScan`, as well pagination.
## Iterator
An `Iterator` allows iteration through a sequence of key value pairs.
+++ https://github.com/cosmos/cosmos-sdk/blob/430163ed4eefcc0d67b706411ffc0b7c5414cd90/x/group/internal/orm/types.go#L77-L83
Tables rely on a `typeSafeIterator` that is used by `PrefixScan` and `ReversePrefixScan` `table` methods to iterate through a range of `RowID`s.
+++ https://github.com/cosmos/cosmos-sdk/blob/430163ed4eefcc0d67b706411ffc0b7c5414cd90/x/group/internal/orm/table.go#235-L239
Secondary indexes rely on an `indexIterator` that can strip the `RowID` from the full index key in order to get the underlying value in the table prefix store.
+++ https://github.com/cosmos/cosmos-sdk/blob/430163ed4eefcc0d67b706411ffc0b7c5414cd90/x/group/internal/orm/index.go#L227-L232
Under the hood, both use a prefix store `Iterator` (alias for tm-db `Iterator`).
## Pagination
The `Paginate` function does pagination given an [`Iterator`](#iterator) and a `query.PageRequest`, and returns a `query.PageResponse`.
It unmarshals the results into the provided dest interface that should be a pointer to a slice of models.
+++ https://github.com/cosmos/cosmos-sdk/blob/430163ed4eefcc0d67b706411ffc0b7c5414cd90/x/group/internal/orm/iterator.go#L117-L216
Secondary indexes have a `GetPaginated` method that returns an `Iterator` for the given searched secondary index key, starting from the `query.PageRequest` key if provided. It's important to note that this `query.PageRequest` key should be a `RowID` (that could have been returned by a previous paginated request). The returned `Iterator` can then be used with the `Paginate` function and the same `query.PageRequest`.

View File

@ -7,3 +7,10 @@ The orm package provides a framework for creating relational database tables wit
1. **[Table](01_table.md)**
- [AutoUInt64Table](01_table.md#autouint64table)
- [PrimaryKeyTable](01_table.md#primarykeytable)
2. **[Secondary Index](02_secondary_index.md)**
- [MultiKeyIndex](02_secondary_index.md#multikeyindex)
- [UniqueIndex](02_secondary_index.md#uniqueindex)
3. **[Iterator and Pagination](03_iterator_pagination.md)**
- [Iterator](03_iterator_pagination.md#iterator)
- [Pagination](03_iterator_pagination.md#pagination)

View File

@ -1,12 +1,15 @@
package orm
import (
"bytes"
"reflect"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
"github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
)
var _ Indexable = &table{}
@ -32,7 +35,7 @@ type table struct {
// newTable creates a new table
func newTable(prefix [2]byte, model codec.ProtoMarshaler, cdc codec.Codec) (*table, error) {
if model == nil {
return nil, errors.ErrORMEmptyModel.Wrap("Model must not be nil")
return nil, errors.ErrORMInvalidArgument.Wrap("Model must not be nil")
}
tp := reflect.TypeOf(model)
if tp.Kind() == reflect.Ptr {
@ -74,14 +77,14 @@ func (a table) Create(store sdk.KVStore, rowID RowID, obj codec.ProtoMarshaler)
}
// Update updates the given object under the rowID key. It expects the key to
// exists already and fails with an `errors.ErrNotFound` otherwise. Any caller must
// exists already and fails with an `sdkerrors.ErrNotFound` otherwise. Any caller must
// therefore make sure that this contract is fulfilled. Parameters must not be
// nil.
//
// Update triggers all "after set" hooks that may add or remove secondary index keys.
func (a table) Update(store sdk.KVStore, rowID RowID, newValue codec.ProtoMarshaler) error {
if !a.Has(store, rowID) {
return errors.ErrNotFound
return sdkerrors.ErrNotFound
}
return a.Set(store, rowID, newValue)
@ -113,13 +116,13 @@ func (a table) Set(store sdk.KVStore, rowID RowID, newValue codec.ProtoMarshaler
newValueEncoded, err := a.cdc.Marshal(newValue)
if err != nil {
return errors.Wrapf(err, "failed to serialize %T", newValue)
return sdkerrors.Wrapf(err, "failed to serialize %T", newValue)
}
pStore.Set(rowID, newValueEncoded)
for i, itc := range a.afterSet {
if err := itc(store, rowID, newValue, oldValue); err != nil {
return errors.Wrapf(err, "interceptor %d failed", i)
return sdkerrors.Wrapf(err, "interceptor %d failed", i)
}
}
return nil
@ -135,7 +138,7 @@ func assertValid(obj codec.ProtoMarshaler) error {
}
// Delete removes the object under the rowID key. It expects the key to exists
// already and fails with a `errors.ErrNotFound` otherwise. Any caller must therefore
// already and fails with a `sdkerrors.ErrNotFound` otherwise. Any caller must therefore
// make sure that this contract is fulfilled.
//
// Delete iterates through the registered callbacks that remove secondary index
@ -145,13 +148,13 @@ func (a table) Delete(store sdk.KVStore, rowID RowID) error {
var oldValue = reflect.New(a.model).Interface().(codec.ProtoMarshaler)
if err := a.GetOne(store, rowID, oldValue); err != nil {
return errors.Wrap(err, "load old value")
return sdkerrors.Wrap(err, "load old value")
}
pStore.Delete(rowID)
for i, itc := range a.afterDelete {
if err := itc(store, rowID, oldValue); err != nil {
return errors.Wrapf(err, "delete interceptor %d failed", i)
return sdkerrors.Wrapf(err, "delete interceptor %d failed", i)
}
}
return nil
@ -170,12 +173,82 @@ func (a table) Has(store sdk.KVStore, key RowID) bool {
}
// GetOne load the object persisted for the given RowID into the dest parameter.
// If none exists or `rowID==nil` then `errors.ErrNotFound` is returned instead.
// If none exists or `rowID==nil` then `sdkerrors.ErrNotFound` is returned instead.
// Parameters must not be nil - we don't allow creation of values with empty keys.
func (a table) GetOne(store sdk.KVStore, rowID RowID, dest codec.ProtoMarshaler) error {
if len(rowID) == 0 {
return errors.ErrNotFound
return sdkerrors.ErrNotFound
}
x := NewTypeSafeRowGetter(a.prefix, a.model, a.cdc)
return x(store, rowID, dest)
}
// PrefixScan returns an Iterator over a domain of keys in ascending order. End is exclusive.
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid.
// Iterator must be closed by caller.
// To iterate over entire domain, use PrefixScan(nil, nil)
//
// WARNING: The use of a PrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
// this as an endpoint to the public without further limits.
// Example:
// it, err := idx.PrefixScan(ctx, start, end)
// if err !=nil {
// return err
// }
// const defaultLimit = 20
// it = LimitIterator(it, defaultLimit)
//
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
func (a table) PrefixScan(store sdk.KVStore, start, end RowID) (Iterator, error) {
if start != nil && end != nil && bytes.Compare(start, end) >= 0 {
return NewInvalidIterator(), sdkerrors.Wrap(errors.ErrORMInvalidArgument, "start must be before end")
}
pStore := prefix.NewStore(store, a.prefix[:])
return &typeSafeIterator{
store: store,
rowGetter: NewTypeSafeRowGetter(a.prefix, a.model, a.cdc),
it: pStore.Iterator(start, end),
}, nil
}
// ReversePrefixScan returns an Iterator over a domain of keys in descending order. End is exclusive.
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned.
// Iterator must be closed by caller.
// To iterate over entire domain, use PrefixScan(nil, nil)
//
// WARNING: The use of a ReversePrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
// this as an endpoint to the public without further limits. See `LimitIterator`
//
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
func (a table) ReversePrefixScan(store sdk.KVStore, start, end RowID) (Iterator, error) {
if start != nil && end != nil && bytes.Compare(start, end) >= 0 {
return NewInvalidIterator(), sdkerrors.Wrap(errors.ErrORMInvalidArgument, "start must be before end")
}
pStore := prefix.NewStore(store, a.prefix[:])
return &typeSafeIterator{
store: store,
rowGetter: NewTypeSafeRowGetter(a.prefix, a.model, a.cdc),
it: pStore.ReverseIterator(start, end),
}, nil
}
// typeSafeIterator is initialized with a type safe RowGetter only.
type typeSafeIterator struct {
store sdk.KVStore
rowGetter RowGetter
it types.Iterator
}
func (i typeSafeIterator) LoadNext(dest codec.ProtoMarshaler) (RowID, error) {
if !i.it.Valid() {
return nil, errors.ErrORMIteratorDone
}
rowID := i.it.Key()
i.it.Next()
return rowID, i.rowGetter(i.store, rowID, dest)
}
func (i typeSafeIterator) Close() error {
i.it.Close()
return nil
}

View File

@ -8,7 +8,8 @@ import (
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -54,7 +55,7 @@ func TestCreate(t *testing.T) {
specs := map[string]struct {
rowID RowID
src codec.ProtoMarshaler
expErr *errors.Error
expErr *sdkerrors.Error
}{
"empty rowID": {
rowID: []byte{},
@ -77,7 +78,7 @@ func TestCreate(t *testing.T) {
Moniker: "cat moniker",
Lives: 10,
},
expErr: errors.ErrInvalidType,
expErr: sdkerrors.ErrInvalidType,
},
"model validation fails": {
rowID: EncodeSequence(1),
@ -110,7 +111,7 @@ func TestCreate(t *testing.T) {
var loaded testdata.TableModel
err = myTable.GetOne(store, spec.rowID, &loaded)
if spec.expErr != nil {
require.True(t, errors.ErrNotFound.Is(err))
require.True(t, sdkerrors.ErrNotFound.Is(err))
return
}
require.NoError(t, err)
@ -122,7 +123,7 @@ func TestCreate(t *testing.T) {
func TestUpdate(t *testing.T) {
specs := map[string]struct {
src codec.ProtoMarshaler
expErr *errors.Error
expErr *sdkerrors.Error
}{
"happy path": {
src: &testdata.TableModel{
@ -135,7 +136,7 @@ func TestUpdate(t *testing.T) {
Moniker: "cat moniker",
Lives: 10,
},
expErr: errors.ErrInvalidType,
expErr: sdkerrors.ErrInvalidType,
},
"model validation fails": {
src: &testdata.TableModel{
@ -184,14 +185,14 @@ func TestUpdate(t *testing.T) {
func TestDelete(t *testing.T) {
specs := map[string]struct {
rowId []byte
expErr *errors.Error
expErr *sdkerrors.Error
}{
"happy path": {
rowId: EncodeSequence(1),
},
"not found": {
rowId: []byte("not-found"),
expErr: errors.ErrNotFound,
expErr: sdkerrors.ErrNotFound,
},
}
for msg, spec := range specs {
@ -220,13 +221,13 @@ func TestDelete(t *testing.T) {
// then
var loaded testdata.TableModel
if spec.expErr == errors.ErrNotFound {
if spec.expErr == sdkerrors.ErrNotFound {
require.NoError(t, myTable.GetOne(store, EncodeSequence(1), &loaded))
assert.Equal(t, initValue, loaded)
} else {
err := myTable.GetOne(store, EncodeSequence(1), &loaded)
require.Error(t, err)
require.Equal(t, err, errors.ErrNotFound)
require.Equal(t, err, sdkerrors.ErrNotFound)
}
})
}

View File

@ -10,7 +10,9 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/group/errors"
)
// Unique identifier of a persistent table.
@ -27,12 +29,57 @@ type Validateable interface {
ValidateBasic() error
}
// Index allows efficient prefix scans is stored as key = concat(indexKeyBytes, rowIDUint64) with value empty
// so that the row PrimaryKey is allows a fixed with 8 byte integer. This allows the MultiKeyIndex key bytes to be
// variable length and scanned iteratively.
type Index interface {
// Has checks if a key exists. Panics on nil key.
Has(store sdk.KVStore, key interface{}) (bool, error)
// Get returns a result iterator for the searchKey.
// searchKey must not be nil.
Get(store sdk.KVStore, searchKey interface{}) (Iterator, error)
// GetPaginated returns a result iterator for the searchKey and optional pageRequest.
// searchKey must not be nil.
GetPaginated(store sdk.KVStore, searchKey interface{}, pageRequest *query.PageRequest) (Iterator, error)
// PrefixScan returns an Iterator over a domain of keys in ascending order. End is exclusive.
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned.
// Iterator must be closed by caller.
// To iterate over entire domain, use PrefixScan(nil, nil)
//
// WARNING: The use of a PrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
// this as an endpoint to the public without further limits.
// Example:
// it, err := idx.PrefixScan(ctx, start, end)
// if err !=nil {
// return err
// }
// const defaultLimit = 20
// it = LimitIterator(it, defaultLimit)
//
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
PrefixScan(store sdk.KVStore, startI interface{}, endI interface{}) (Iterator, error)
// ReversePrefixScan returns an Iterator over a domain of keys in descending order. End is exclusive.
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned.
// Iterator must be closed by caller.
// To iterate over entire domain, use PrefixScan(nil, nil)
//
// WARNING: The use of a ReversePrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
// this as an endpoint to the public without further limits. See `LimitIterator`
//
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
ReversePrefixScan(store sdk.KVStore, startI interface{}, endI interface{}) (Iterator, error)
}
// Iterator allows iteration through a sequence of key value pairs
type Iterator interface {
// LoadNext loads the next value in the sequence into the pointer passed as dest and returns the key. If there
// are no more items the ErrORMIteratorDone error is returned
// The key is the rowID.
LoadNext(store sdk.KVStore, dest codec.ProtoMarshaler) (RowID, error)
LoadNext(dest codec.ProtoMarshaler) (RowID, error)
// Close releases the iterator and should be called at the end of iteration
io.Closer
}
@ -52,14 +99,14 @@ type AfterSetInterceptor func(store sdk.KVStore, rowID RowID, newValue, oldValue
type AfterDeleteInterceptor func(store sdk.KVStore, rowID RowID, value codec.ProtoMarshaler) error
// RowGetter loads a persistent object by row ID into the destination object. The dest parameter must therefore be a pointer.
// Any implementation must return `errors.ErrNotFound` when no object for the rowID exists
// Any implementation must return `sdkerrors.ErrNotFound` when no object for the rowID exists
type RowGetter func(store sdk.KVStore, rowID RowID, dest codec.ProtoMarshaler) error
// NewTypeSafeRowGetter returns a `RowGetter` with type check on the dest parameter.
func NewTypeSafeRowGetter(prefixKey [2]byte, model reflect.Type, cdc codec.Codec) RowGetter {
return func(store sdk.KVStore, rowID RowID, dest codec.ProtoMarshaler) error {
if len(rowID) == 0 {
return errors.Wrap(errors.ErrORMEmptyKey, "key must not be nil")
return sdkerrors.Wrap(errors.ErrORMEmptyKey, "key must not be nil")
}
if err := assertCorrectType(model, dest); err != nil {
return err
@ -69,7 +116,7 @@ func NewTypeSafeRowGetter(prefixKey [2]byte, model reflect.Type, cdc codec.Codec
it := pStore.Iterator(PrefixRange(rowID))
defer it.Close()
if !it.Valid() {
return errors.ErrNotFound
return sdkerrors.ErrNotFound
}
return cdc.Unmarshal(it.Value(), dest)
}
@ -78,10 +125,10 @@ func NewTypeSafeRowGetter(prefixKey [2]byte, model reflect.Type, cdc codec.Codec
func assertCorrectType(model reflect.Type, obj codec.ProtoMarshaler) error {
tp := reflect.TypeOf(obj)
if tp.Kind() != reflect.Ptr {
return errors.Wrap(errors.ErrInvalidType, "model destination must be a pointer")
return sdkerrors.Wrap(sdkerrors.ErrInvalidType, "model destination must be a pointer")
}
if model != tp.Elem() {
return errors.Wrapf(errors.ErrInvalidType, "can not use %T with this bucket", obj)
return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "can not use %T with this bucket", obj)
}
return nil
}

View File

@ -9,7 +9,8 @@ import (
"github.com/cosmos/cosmos-sdk/store/prefix"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/group/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -31,22 +32,22 @@ func TestTypeSafeRowGetter(t *testing.T) {
srcRowID RowID
srcModelType reflect.Type
expObj interface{}
expErr *errors.Error
expErr *sdkerrors.Error
}{
"happy path": {
srcRowID: EncodeSequence(1),
srcModelType: reflect.TypeOf(testdata.TableModel{}),
expObj: md,
},
"unknown rowID should return errors.ErrNotFound": {
"unknown rowID should return sdkerrors.ErrNotFound": {
srcRowID: EncodeSequence(2),
srcModelType: reflect.TypeOf(testdata.TableModel{}),
expErr: errors.ErrNotFound,
expErr: sdkerrors.ErrNotFound,
},
"wrong type should cause errors.ErrInvalidType": {
"wrong type should cause sdkerrors.ErrInvalidType": {
srcRowID: EncodeSequence(1),
srcModelType: reflect.TypeOf(testdata.Cat{}),
expErr: errors.ErrInvalidType,
expErr: sdkerrors.ErrInvalidType,
},
"empty rowID not allowed": {
srcRowID: []byte{},