diff --git a/types/errors/errors.go b/types/errors/errors.go index ece008e10da1..62dab18adf8c 100644 --- a/types/errors/errors.go +++ b/types/errors/errors.go @@ -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 diff --git a/x/group/errors/math.go b/x/group/errors/math.go new file mode 100644 index 000000000000..ff753ed220c8 --- /dev/null +++ b/x/group/errors/math.go @@ -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") diff --git a/x/group/errors/orm.go b/x/group/errors/orm.go new file mode 100644 index 000000000000..84dab68eeba5 --- /dev/null +++ b/x/group/errors/orm.go @@ -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") +) diff --git a/x/group/go.mod b/x/group/go.mod index 5941713b48c5..3146302ddafa 100644 --- a/x/group/go.mod +++ b/x/group/go.mod @@ -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 diff --git a/x/group/go.sum b/x/group/go.sum index fdb0cd5ac51a..bff845d6c9a3 100644 --- a/x/group/go.sum +++ b/x/group/go.sum @@ -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= diff --git a/x/group/internal/math/dec.go b/x/group/internal/math/dec.go index dbdd9843354b..831d4430ca59 100644 --- a/x/group/internal/math/dec.go +++ b/x/group/internal/math/dec.go @@ -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) { diff --git a/x/group/internal/orm/auto_uint64.go b/x/group/internal/orm/auto_uint64.go index 62eb84cbbabc..78cfcd844a78 100644 --- a/x/group/internal/orm/auto_uint64.go +++ b/x/group/internal/orm/auto_uint64.go @@ -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)) +} diff --git a/x/group/internal/orm/auto_uint64_test.go b/x/group/internal/orm/auto_uint64_test.go new file mode 100644 index 000000000000..529fce314e04 --- /dev/null +++ b/x/group/internal/orm/auto_uint64_test.go @@ -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) + }) + } +} diff --git a/x/group/internal/orm/example_test.go b/x/group/internal/orm/example_test.go index a6bddf0b855b..d182efeb03c8 100644 --- a/x/group/internal/orm/example_test.go +++ b/x/group/internal/orm/example_test.go @@ -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.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.autoUInt64Table = autoUInt64Table - primaryKeyTable, err := NewPrimaryKeyTable(PrimaryKeyTablePrefix, &testdata.TableModel{}, cdc) + 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 } diff --git a/x/group/internal/orm/index.go b/x/group/internal/orm/index.go index 871d5e415fd9..55458fa03aee 100644 --- a/x/group/internal/orm/index.go +++ b/x/group/internal/orm/index.go @@ -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} diff --git a/x/group/internal/orm/index_test.go b/x/group/internal/orm/index_test.go index 01e43a221a21..521bb9d6c917 100644 --- a/x/group/internal/orm/index_test.go +++ b/x/group/internal/orm/index_test.go @@ -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 diff --git a/x/group/internal/orm/indexer.go b/x/group/internal/orm/indexer.go new file mode 100644 index 000000000000..67b3f13d2cc1 --- /dev/null +++ b/x/group/internal/orm/indexer.go @@ -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 + } +} diff --git a/x/group/internal/orm/indexer_test.go b/x/group/internal/orm/indexer_test.go new file mode 100644 index 000000000000..d574fb63fd04 --- /dev/null +++ b/x/group/internal/orm/indexer_test.go @@ -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 +} diff --git a/x/group/internal/orm/iterator.go b/x/group/internal/orm/iterator.go new file mode 100644 index 000000000000..ae61d61c48e4 --- /dev/null +++ b/x/group/internal/orm/iterator.go @@ -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 +} diff --git a/x/group/internal/orm/iterator_property_test.go b/x/group/internal/orm/iterator_property_test.go new file mode 100644 index 000000000000..1f30fcb2278a --- /dev/null +++ b/x/group/internal/orm/iterator_property_test.go @@ -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) + }) +} diff --git a/x/group/internal/orm/iterator_test.go b/x/group/internal/orm/iterator_test.go new file mode 100644 index 000000000000..b2abbaaa403c --- /dev/null +++ b/x/group/internal/orm/iterator_test.go @@ -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 + }) +} diff --git a/x/group/internal/orm/key_codec.go b/x/group/internal/orm/key_codec.go index 099ed1571cf1..074b67b3ebc4 100644 --- a/x/group/internal/orm/key_codec.go +++ b/x/group/internal/orm/key_codec.go @@ -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) + } +} diff --git a/x/group/internal/orm/orm_scenario_test.go b/x/group/internal/orm/orm_scenario_test.go index ad934349ff82..1f17e66095d1 100644 --- a/x/group/internal/orm/orm_scenario_test.go +++ b/x/group/internal/orm/orm_scenario_test.go @@ -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 +} diff --git a/x/group/internal/orm/primary_key.go b/x/group/internal/orm/primary_key.go index 8bfeae402dea..f347cd0d14ee 100644 --- a/x/group/internal/orm/primary_key.go +++ b/x/group/internal/orm/primary_key.go @@ -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) +} diff --git a/x/group/internal/orm/primary_key_test.go b/x/group/internal/orm/primary_key_test.go index 6338fd5ce5b8..498e973c0c98 100644 --- a/x/group/internal/orm/primary_key_test.go +++ b/x/group/internal/orm/primary_key_test.go @@ -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{ diff --git a/x/group/internal/orm/sequence.go b/x/group/internal/orm/sequence.go index a8d70f7243ac..5e7e986ae167 100644 --- a/x/group/internal/orm/sequence.go +++ b/x/group/internal/orm/sequence.go @@ -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 diff --git a/x/group/internal/orm/sequence_test.go b/x/group/internal/orm/sequence_test.go index 023f7318dae2..a1a3ddef20ca 100644 --- a/x/group/internal/orm/sequence_test.go +++ b/x/group/internal/orm/sequence_test.go @@ -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" ) diff --git a/x/group/internal/orm/spec/01_table.md b/x/group/internal/orm/spec/01_table.md index 7b159b482dc1..1c769cbcdd37 100644 --- a/x/group/internal/orm/spec/01_table.md +++ b/x/group/internal/orm/spec/01_table.md @@ -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. diff --git a/x/group/internal/orm/spec/02_secondary_index.md b/x/group/internal/orm/spec/02_secondary_index.md new file mode 100644 index 000000000000..e104999a9d16 --- /dev/null +++ b/x/group/internal/orm/spec/02_secondary_index.md @@ -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. \ No newline at end of file diff --git a/x/group/internal/orm/spec/03_iterator_pagination.md b/x/group/internal/orm/spec/03_iterator_pagination.md new file mode 100644 index 000000000000..ac0839623321 --- /dev/null +++ b/x/group/internal/orm/spec/03_iterator_pagination.md @@ -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`. diff --git a/x/group/internal/orm/spec/README.md b/x/group/internal/orm/spec/README.md index c8eccb01f596..5c77f712e94e 100644 --- a/x/group/internal/orm/spec/README.md +++ b/x/group/internal/orm/spec/README.md @@ -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) + diff --git a/x/group/internal/orm/table.go b/x/group/internal/orm/table.go index f3611850045e..468077709d3e 100644 --- a/x/group/internal/orm/table.go +++ b/x/group/internal/orm/table.go @@ -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 +} diff --git a/x/group/internal/orm/table_test.go b/x/group/internal/orm/table_test.go index aff91e22417e..44146c7fea8f 100644 --- a/x/group/internal/orm/table_test.go +++ b/x/group/internal/orm/table_test.go @@ -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) } }) } diff --git a/x/group/internal/orm/types.go b/x/group/internal/orm/types.go index c06bbebd6de0..b5ca93b0ee95 100644 --- a/x/group/internal/orm/types.go +++ b/x/group/internal/orm/types.go @@ -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 } diff --git a/x/group/internal/orm/types_test.go b/x/group/internal/orm/types_test.go index bf8e3648c913..9ef2472611c9 100644 --- a/x/group/internal/orm/types_test.go +++ b/x/group/internal/orm/types_test.go @@ -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{},