diff --git a/.github/workflows/atlas.yml b/.github/workflows/atlas.yml new file mode 100644 index 000000000..25e749439 --- /dev/null +++ b/.github/workflows/atlas.yml @@ -0,0 +1,58 @@ +name: Atlas +# Atlas checks if a modules atlas manifest has been touched, if so it publishes the updated version +on: + push: + branches: + - master + paths: + - "x/**/atlas/*" + pull_request: + paths: + - "x/**/atlas/*" + +jobs: + auth: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v4 + id: git_diff + with: + PATTERNS: | + x/auth/atlas/** + - uses: marbar3778/atlas_action@main + with: + token: ${{ secrets.ATLAS_TOKEN }} + path: ./x/auth/atlas/atlas.toml + dry-run: ${{ github.event_name != 'pull_request' }} + if: env.GIT_DIFF + bank: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v4 + id: git_diff + with: + PATTERNS: | + x/bank/atlas/** + - uses: marbar3778/atlas_action@main + with: + token: ${{ secrets.ATLAS_TOKEN }} + path: ./x/bank/atlas/atlas.toml + dry-run: ${{ github.event_name != 'pull_request' }} + if: env.GIT_DIFF + evidence: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v4 + id: git_diff + with: + PATTERNS: | + x/evidence/atlas/** + - uses: marbar3778/atlas_action@main + with: + token: ${{ secrets.ATLAS_TOKEN }} + path: ./x/evidence/atlas/manifest.toml + dry-run: ${{ github.event_name != 'pull_request' }} + if: env.GIT_DIFF diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 157f0d217..e917d2ecb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -23,7 +23,7 @@ jobs: - uses: golangci/golangci-lint-action@master with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: v1.28 + version: v1.37 args: --timeout 10m github-token: ${{ secrets.github_token }} if: env.GIT_DIFF diff --git a/.mergify.yml b/.mergify.yml index fbf9d7015..129fb0090 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -8,3 +8,19 @@ pull_request_rules: merge: method: squash strict: true + - name: backport patches to v0.41.x branch + conditions: + - base=master + - label=backport/0.41.x (Stargate) + actions: + backport: + branches: + - release/v0.41.x + - name: backport patches to v0.39.x branch + conditions: + - base=master + - label=backport/0.39.x (Launchpad) + actions: + backport: + branches: + - launchpad/backports diff --git a/CHANGELOG.md b/CHANGELOG.md index e745b5796..c1138a121 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,35 +38,85 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Client Breaking Changes -* [\#8363](https://github.com/cosmos/cosmos-sdk/issues/8363) Addresses no longer have a fixed 20-byte length. From the SDK modules' point of view, any 1-255 bytes-long byte array is a valid address. +* [\#8363](https://github.com/cosmos/cosmos-sdk/pull/8363) Addresses no longer have a fixed 20-byte length. From the SDK modules' point of view, any 1-255 bytes-long byte array is a valid address. +* [\#8346](https://github.com/cosmos/cosmos-sdk/pull/8346) All CLI `tx` commands generate ServiceMsgs by default. Graceful Amino support has been added to ServiceMsgs to support signing legacy Msgs. ### API Breaking Changes +* (keyring) [#\8662](https://github.com/cosmos/cosmos-sdk/pull/8662) `NewMnemonic` now receives an additional `passphrase` argument to secure the key generated by the bip39 mnemonic. +* (x/bank) [\#8473](https://github.com/cosmos/cosmos-sdk/pull/8473) Bank keeper does not expose unsafe balance changing methods such as `SetBalance`, `SetSupply` etc. +* (x/staking) [\#8473](https://github.com/cosmos/cosmos-sdk/pull/8473) On genesis init, if non bonded pool and bonded pool balance, coming from the bank module, does not match what is saved in the staking state, the initialization will panic. +* (x/gov) [\#8473](https://github.com/cosmos/cosmos-sdk/pull/8473) On genesis init, if the gov module account balance, coming from bank module state, does not match the one in gov module state, the initialization will panic. +* (x/distribution) [\#8473](https://github.com/cosmos/cosmos-sdk/pull/8473) On genesis init, if the distribution module account balance, coming from bank module state, does not match the one in distribution module state, the initialization will panic. * (client/keys) [\#8500](https://github.com/cosmos/cosmos-sdk/pull/8500) `InfoImporter` interface is removed from legacy keybase. +* [\#8629](https://github.com/cosmos/cosmos-sdk/pull/8629) Deprecated `SetFullFundraiserPath` from `Config` in favor of `SetPurpose` and `SetCoinType`. ### State Machine Breaking * (x/{bank,distrib,gov,slashing,staking}) [\#8363](https://github.com/cosmos/cosmos-sdk/issues/8363) Store keys have been modified to allow for variable-length addresses. * (x/ibc) [\#8266](https://github.com/cosmos/cosmos-sdk/issues/8266) Add amino JSON for IBC messages in order to support Ledger text signing. +* (x/ibc) [\#8405](https://github.com/cosmos/cosmos-sdk/pull/8405) Refactor IBC client update governance proposals to use a substitute client to update a frozen or expired client. * (x/evidence) [\#8502](https://github.com/cosmos/cosmos-sdk/pull/8502) `HandleEquivocationEvidence` persists the evidence to state. +* (x/gov) [\#7733](https://github.com/cosmos/cosmos-sdk/pull/7733) ADR 037 Implementation: Governance Split Votes +* (x/bank) [\#8656](https://github.com/cosmos/cosmos-sdk/pull/8656) balance and supply are now correctly tracked via `coin_spent`, `coin_received`, `coinbase` and `burn` events. + +### Improvements + +* (x/bank) [\#8614](https://github.com/cosmos/cosmos-sdk/issues/8614) Add `Name` and `Symbol` fields to denom metadata +* (x/auth) [\#8522](https://github.com/cosmos/cosmos-sdk/pull/8522) Allow to query all stored accounts +* (crypto/types) [\#8600](https://github.com/cosmos/cosmos-sdk/pull/8600) `CompactBitArray`: optimize the `NumTrueBitsBefore` method and add an `Equal` method. + + +### Bug Fixes + +* (keyring) [#\8635](https://github.com/cosmos/cosmos-sdk/issues/8635) Remove hardcoded default passphrase value on `NewMnemonic` +* (x/evidence) [#8461](https://github.com/cosmos/cosmos-sdk/pull/8461) Fix bech32 prefix in evidence validator address conversion +* (x/slashing) [\#8427](https://github.com/cosmos/cosmos-sdk/pull/8427) Fix query signing infos command +* (server) [\#8399](https://github.com/cosmos/cosmos-sdk/pull/8399) fix gRPC-web flag default value +* (server) [\#8641](https://github.com/cosmos/cosmos-sdk/pull/8641) Fix Tendermint and application configuration reading from file + +## [v0.41.3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.3) - 2021-02-18 + +**IMPORTANT:** Due to a bug in the `v0.41.x` series with how evidence handles validator consensus addresses [\#8461](https://github.com/cosmos/cosmos-sdk/issues/8461), SDK based chains that are not using the default bech32 prefix (`cosmos`, aka all chains except for the Cosmos Hub) **should not** use this release or any release in the `v0.41.x` series. Please see [\#8668](https://github.com/cosmos/cosmos-sdk/issues/8668) for tracking & timeline for the v0.42.0 release, which will include a fix for this issue. + +### Bug Fixes + +* [\#8617](https://github.com/cosmos/cosmos-sdk/pull/8617) Fix build failures caused by a small API breakage introduced in tendermint v0.34.7. + +## [v0.41.2](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.2) - 2021-02-18 + +### Improvements + +* Bump tendermint dependency to v0.34.7. + +## [v0.41.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.1) - 2021-02-17 + +### Bug Fixes + +* (grpc) [\#8549](https://github.com/cosmos/cosmos-sdk/pull/8549) Make gRPC requests go through ABCI and disallow concurrency. +* (x/staking) [\#8546](https://github.com/cosmos/cosmos-sdk/pull/8546) Fix caching bug where concurrent calls to GetValidator could cause a node to crash +* (server) [\#8481](https://github.com/cosmos/cosmos-sdk/pull/8481) Don't create files when running `{appd} tendermint show-*` subcommands. +* (client/keys) [\#8436](https://github.com/cosmos/cosmos-sdk/pull/8436) Fix keybase->keyring keys migration. +* (crypto/hd) [\#8607](https://github.com/cosmos/cosmos-sdk/pull/8607) Make DerivePrivateKeyForPath error and not panic on trailing slashes. ### Improvements * (x/ibc) [\#8458](https://github.com/cosmos/cosmos-sdk/pull/8458) Add `packet_connection` attribute to ibc events to enable relayer filtering -* (x/bank) [\#8479](https://github.com/cosmos/cosmos-sdk/pull/8479) Adittional client denom metadata validation for `base` and `display` denoms. -* (x/ibc) [\#8404](https://github.com/cosmos/cosmos-sdk/pull/8404) Reorder IBC `ChanOpenAck` and `ChanOpenConfirm` handler execution to perform core handler first, followed by application callbacks. * [\#8396](https://github.com/cosmos/cosmos-sdk/pull/8396) Add support for ARM platform +* (x/bank) [\#8479](https://github.com/cosmos/cosmos-sdk/pull/8479) Aditional client denom metadata validation for `base` and `display` denoms. +* (codec/types) [\#8605](https://github.com/cosmos/cosmos-sdk/pull/8605) Avoid unnecessary allocations for NewAnyWithCustomTypeURL on error. + +## [v0.41.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.0) - 2021-01-26 + +### State Machine Breaking + +* (x/ibc) [\#8266](https://github.com/cosmos/cosmos-sdk/issues/8266) Add amino JSON support for IBC MsgTransfer in order to support Ledger text signing transfer transactions. +* (x/ibc) [\#8404](https://github.com/cosmos/cosmos-sdk/pull/8404) Reorder IBC `ChanOpenAck` and `ChanOpenConfirm` handler execution to perform core handler first, followed by application callbacks. ### Bug Fixes -* (x/evidence) [#8461](https://github.com/cosmos/cosmos-sdk/pull/8461) Fix bech32 prefix in evidence validator address conversion -* (x/slashing) [\#8427](https://github.com/cosmos/cosmos-sdk/pull/8427) Fix query signing infos command * (simapp) [\#8418](https://github.com/cosmos/cosmos-sdk/pull/8418) Add balance coin to supply when adding a new genesis account * (x/bank) [\#8417](https://github.com/cosmos/cosmos-sdk/pull/8417) Validate balances and coin denom metadata on genesis -* (server) [\#8399](https://github.com/cosmos/cosmos-sdk/pull/8399) fix gRPC-web flag default value -* (client/keys) [\#8436](https://github.com/cosmos/cosmos-sdk/pull/8436) Fix key migration issue -* (server) [\#8481](https://github.com/cosmos/cosmos-sdk/pull/8481) Don't create - files when running `{appd} tendermint show-*` subcommands ## [v0.40.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.1) - 2021-01-19 diff --git a/Makefile b/Makefile index f49f95e73..87c842b71 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation') PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation') VERSION := $(shell echo $(shell git describe --always) | sed 's/^v//') +TMVERSION := $(shell go list -m github.com/tendermint/tendermint | sed 's:.* ::') COMMIT := $(shell git log -1 --format='%H') LEDGER_ENABLED ?= true BINDIR ?= $(GOPATH)/bin @@ -58,7 +59,8 @@ ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=sim \ -X github.com/cosmos/cosmos-sdk/version.AppName=simd \ -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \ - -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep)" + -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep)" \ + -X github.com/tendermint/tendermint/version.TMCoreSemVer=$(TMVERSION) # DB backend selection ifeq (cleveldb,$(findstring cleveldb,$(COSMOS_BUILD_OPTIONS))) diff --git a/baseapp/grpcrouter.go b/baseapp/grpcrouter.go index de553589c..95186f0b1 100644 --- a/baseapp/grpcrouter.go +++ b/baseapp/grpcrouter.go @@ -2,6 +2,7 @@ package baseapp import ( "fmt" + "reflect" gogogrpc "github.com/gogo/protobuf/grpc" abci "github.com/tendermint/tendermint/abci/types" @@ -12,13 +13,19 @@ import ( "github.com/cosmos/cosmos-sdk/client/grpc/reflection" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) var protoCodec = encoding.GetCodec(proto.Name) // GRPCQueryRouter routes ABCI Query requests to GRPC handlers type GRPCQueryRouter struct { - routes map[string]GRPCQueryHandler + routes map[string]GRPCQueryHandler + // returnTypes is a map of FQ method name => its return type. It is used + // for cache purposes: the first time a method handler is run, we save its + // return type in this map. Then, on subsequent method handler calls, we + // decode the ABCI response bytes using the cached return type. + returnTypes map[string]reflect.Type interfaceRegistry codectypes.InterfaceRegistry serviceData []serviceData } @@ -34,7 +41,8 @@ var _ gogogrpc.Server = &GRPCQueryRouter{} // NewGRPCQueryRouter creates a new GRPCQueryRouter func NewGRPCQueryRouter() *GRPCQueryRouter { return &GRPCQueryRouter{ - routes: map[string]GRPCQueryHandler{}, + returnTypes: map[string]reflect.Type{}, + routes: map[string]GRPCQueryHandler{}, } } @@ -89,8 +97,17 @@ func (qrt *GRPCQueryRouter) RegisterService(sd *grpc.ServiceDesc, handler interf if qrt.interfaceRegistry != nil { return codectypes.UnpackInterfaces(i, qrt.interfaceRegistry) } + return nil }, nil) + + // If it's the first time we call this handler, then we save + // the return type of the handler in the `returnTypes` map. + // The return type will be used for decoding subsequent requests. + if _, found := qrt.returnTypes[fqName]; !found { + qrt.returnTypes[fqName] = reflect.TypeOf(res) + } + if err != nil { return abci.ResponseQuery{}, err } @@ -127,3 +144,16 @@ func (qrt *GRPCQueryRouter) SetInterfaceRegistry(interfaceRegistry codectypes.In reflection.NewReflectionServiceServer(interfaceRegistry), ) } + +// returnTypeOf returns the return type of a gRPC method handler. With the way the +// `returnTypes` cache map is set up, the return type of a method handler is +// guaranteed to be found if it's retrieved **after** the method handler ran at +// least once. If not, then a logic error is return. +func (qrt *GRPCQueryRouter) returnTypeOf(method string) (reflect.Type, error) { + returnType, found := qrt.returnTypes[method] + if !found { + return nil, sdkerrors.Wrapf(sdkerrors.ErrLogic, "cannot find %s return type", method) + } + + return returnType, nil +} diff --git a/baseapp/grpcserver.go b/baseapp/grpcserver.go index a4342e0b8..c1db08a55 100644 --- a/baseapp/grpcserver.go +++ b/baseapp/grpcserver.go @@ -2,67 +2,78 @@ package baseapp import ( "context" - "strconv" + "reflect" gogogrpc "github.com/gogo/protobuf/grpc" grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware" grpcrecovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery" "google.golang.org/grpc" - "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/client" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/cosmos/cosmos-sdk/types/tx" ) // GRPCQueryRouter returns the GRPCQueryRouter of a BaseApp. func (app *BaseApp) GRPCQueryRouter() *GRPCQueryRouter { return app.grpcQueryRouter } // RegisterGRPCServer registers gRPC services directly with the gRPC server. -func (app *BaseApp) RegisterGRPCServer(server gogogrpc.Server) { - // Define an interceptor for all gRPC queries: this interceptor will create - // a new sdk.Context, and pass it into the query handler. - interceptor := func(grpcCtx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { - // If there's some metadata in the context, retrieve it. - md, ok := metadata.FromIncomingContext(grpcCtx) - if !ok { - return nil, status.Error(codes.Internal, "unable to retrieve metadata") +func (app *BaseApp) RegisterGRPCServer(clientCtx client.Context, server gogogrpc.Server) { + // Define an interceptor for all gRPC queries: this interceptor will route + // the query through the `clientCtx`, which itself queries Tendermint. + interceptor := func(grpcCtx context.Context, req interface{}, info *grpc.UnaryServerInfo, _ grpc.UnaryHandler) (interface{}, error) { + // Two things can happen here: + // 1. either we're broadcasting a Tx, in which case we call Tendermint's broadcast endpoint directly, + // 2. or we are querying for state, in which case we call ABCI's Query. + + // Case 1. Broadcasting a Tx. + if reqProto, ok := req.(*tx.BroadcastTxRequest); ok { + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxRequest)(nil), req) + } + + return client.TxServiceBroadcast(grpcCtx, clientCtx, reqProto) } - // Get height header from the request context, if present. - var height int64 - if heightHeaders := md.Get(grpctypes.GRPCBlockHeightHeader); len(heightHeaders) > 0 { - height, err = strconv.ParseInt(heightHeaders[0], 10, 64) - if err != nil { - return nil, sdkerrors.Wrapf( - sdkerrors.ErrInvalidRequest, - "Baseapp.RegisterGRPCServer: invalid height header %q: %v", grpctypes.GRPCBlockHeightHeader, err) - } - if err := checkNegativeHeight(height); err != nil { - return nil, err - } - } - - // Create the sdk.Context. Passing false as 2nd arg, as we can't - // actually support proofs with gRPC right now. - sdkCtx, err := app.createQueryContext(height, false) + // Case 2. Querying state. + inMd, _ := metadata.FromIncomingContext(grpcCtx) + abciRes, outMd, err := client.RunGRPCQuery(clientCtx, grpcCtx, info.FullMethod, req, inMd) if err != nil { return nil, err } - // Attach the sdk.Context into the gRPC's context.Context. - grpcCtx = context.WithValue(grpcCtx, sdk.SdkContextKey, sdkCtx) - - // Add relevant gRPC headers - if height == 0 { - height = sdkCtx.BlockHeight() // If height was not set in the request, set it to the latest + // We need to know the return type of the grpc method for + // unmarshalling abciRes.Value. + // + // When we call each method handler for the first time, we save its + // return type in the `returnTypes` map (see the method handler in + // `grpcrouter.go`). By this time, the method handler has already run + // at least once (in the RunGRPCQuery call), so we're sure the + // returnType maps is populated for this method. We're retrieving it + // for decoding. + returnType, err := app.GRPCQueryRouter().returnTypeOf(info.FullMethod) + if err != nil { + return nil, err } - md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(height, 10)) - grpc.SetHeader(grpcCtx, md) - return handler(grpcCtx, req) + // returnType is a pointer to a struct. Here, we're creating res which + // is a new pointer to the underlying struct. + res := reflect.New(returnType.Elem()).Interface() + + err = protoCodec.Unmarshal(abciRes.Value, res) + if err != nil { + return nil, err + } + + // Send the metadata header back. The metadata currently includes: + // - block height. + err = grpc.SendHeader(grpcCtx, outMd) + if err != nil { + return nil, err + } + + return res, nil } // Loop through all services and methods, add the interceptor, and register diff --git a/client/docs/statik/init.go b/client/docs/statik/init.go index 7d91b40fc..db70716be 100644 --- a/client/docs/statik/init.go +++ b/client/docs/statik/init.go @@ -1,3 +1,3 @@ package statik -//This just for fixing the error in importing empty github.com/cosmos/cosmos-sdk/client/docs/statik +// This just for fixing the error in importing empty github.com/cosmos/cosmos-sdk/client/docs/statik diff --git a/client/grpc/reflection/reflection.pb.gw.go b/client/grpc/reflection/reflection.pb.gw.go index ab486750e..7bb8a5e12 100644 --- a/client/grpc/reflection/reflection.pb.gw.go +++ b/client/grpc/reflection/reflection.pb.gw.go @@ -234,9 +234,9 @@ func RegisterReflectionServiceHandlerClient(ctx context.Context, mux *runtime.Se } var ( - pattern_ReflectionService_ListAllInterfaces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"cosmos", "base", "reflection", "v1beta1", "interfaces"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_ReflectionService_ListAllInterfaces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"cosmos", "base", "reflection", "v1beta1", "interfaces"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_ReflectionService_ListImplementations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"cosmos", "base", "reflection", "v1beta1", "interfaces", "interface_name", "implementations"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_ReflectionService_ListImplementations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"cosmos", "base", "reflection", "v1beta1", "interfaces", "interface_name", "implementations"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/client/grpc/tmservice/query.pb.gw.go b/client/grpc/tmservice/query.pb.gw.go index 90135d79b..a53a31fcc 100644 --- a/client/grpc/tmservice/query.pb.gw.go +++ b/client/grpc/tmservice/query.pb.gw.go @@ -538,17 +538,17 @@ func RegisterServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, cl } var ( - pattern_Service_GetNodeInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"cosmos", "base", "tendermint", "v1beta1", "node_info"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Service_GetNodeInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"cosmos", "base", "tendermint", "v1beta1", "node_info"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Service_GetSyncing_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"cosmos", "base", "tendermint", "v1beta1", "syncing"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Service_GetSyncing_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"cosmos", "base", "tendermint", "v1beta1", "syncing"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Service_GetLatestBlock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "blocks", "latest"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Service_GetLatestBlock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "blocks", "latest"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Service_GetBlockByHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "blocks", "height"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Service_GetBlockByHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "blocks", "height"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Service_GetLatestValidatorSet_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "validatorsets", "latest"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Service_GetLatestValidatorSet_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "validatorsets", "latest"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Service_GetValidatorSetByHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "validatorsets", "height"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Service_GetValidatorSetByHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "validatorsets", "height"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/client/grpc/tmservice/service.go b/client/grpc/tmservice/service.go index b621dff57..57668c0d7 100644 --- a/client/grpc/tmservice/service.go +++ b/client/grpc/tmservice/service.go @@ -104,6 +104,9 @@ func (s queryServer) GetLatestValidatorSet(ctx context.Context, req *GetLatestVa outputValidatorsRes := &GetLatestValidatorSetResponse{ BlockHeight: validatorsRes.BlockHeight, Validators: make([]*Validator, len(validatorsRes.Validators)), + Pagination: &qtypes.PageResponse{ + Total: validatorsRes.Total, + }, } for i, validator := range validatorsRes.Validators { @@ -156,6 +159,7 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida outputValidatorsRes := &GetValidatorSetByHeightResponse{ BlockHeight: validatorsRes.BlockHeight, Validators: make([]*Validator, len(validatorsRes.Validators)), + Pagination: &qtypes.PageResponse{Total: validatorsRes.Total}, } for i, validator := range validatorsRes.Validators { diff --git a/client/grpc/tmservice/service_test.go b/client/grpc/tmservice/service_test.go index dd8f0dd50..fb83ab9c2 100644 --- a/client/grpc/tmservice/service_test.go +++ b/client/grpc/tmservice/service_test.go @@ -131,32 +131,126 @@ func (s IntegrationTestSuite) TestQueryLatestValidatorSet() { s.Require().Equal(validatorSetRes.Validators[0].PubKey, anyPub) } -func (s IntegrationTestSuite) TestQueryValidatorSetByHeight() { - val := s.network.Validators[0] +func (s IntegrationTestSuite) TestLatestValidatorSet_GRPC() { + vals := s.network.Validators + testCases := []struct { + name string + req *tmservice.GetLatestValidatorSetRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "cannot be nil"}, + {"no pagination", &tmservice.GetLatestValidatorSetRequest{}, false, ""}, + {"with pagination", &tmservice.GetLatestValidatorSetRequest{Pagination: &qtypes.PageRequest{Offset: 0, Limit: uint64(len(vals))}}, false, ""}, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + grpcRes, err := s.queryClient.GetLatestValidatorSet(context.Background(), tc.req) + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + s.Require().Len(grpcRes.Validators, len(vals)) + s.Require().Equal(grpcRes.Pagination.Total, uint64(len(vals))) + content, ok := grpcRes.Validators[0].PubKey.GetCachedValue().(cryptotypes.PubKey) + s.Require().Equal(true, ok) + s.Require().Equal(content, vals[0].PubKey) + } + }) + } +} - // nil pagination - _, err := s.queryClient.GetValidatorSetByHeight(context.Background(), &tmservice.GetValidatorSetByHeightRequest{ - Height: 1, - Pagination: nil, - }) - s.Require().NoError(err) +func (s IntegrationTestSuite) TestLatestValidatorSet_GRPCGateway() { + vals := s.network.Validators + testCases := []struct { + name string + url string + expErr bool + expErrMsg string + }{ + {"no pagination", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/latest", vals[0].APIAddress), false, ""}, + {"pagination invalid fields", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/latest?pagination.offset=-1&pagination.limit=-2", vals[0].APIAddress), true, "strconv.ParseUint"}, + {"with pagination", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/latest?pagination.offset=0&pagination.limit=2", vals[0].APIAddress), false, ""}, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + res, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + if tc.expErr { + s.Require().Contains(string(res), tc.expErrMsg) + } else { + var result tmservice.GetLatestValidatorSetResponse + err = vals[0].ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) + s.Require().NoError(err) + s.Require().Equal(uint64(len(vals)), result.Pagination.Total) + anyPub, err := codectypes.NewAnyWithValue(vals[0].PubKey) + s.Require().NoError(err) + s.Require().Equal(result.Validators[0].PubKey, anyPub) + } + }) + } +} - _, err = s.queryClient.GetValidatorSetByHeight(context.Background(), &tmservice.GetValidatorSetByHeightRequest{ - Height: 1, - Pagination: &qtypes.PageRequest{ - Offset: 0, - Limit: 10, - }}) - s.Require().NoError(err) +func (s IntegrationTestSuite) TestValidatorSetByHeight_GRPC() { + vals := s.network.Validators + testCases := []struct { + name string + req *tmservice.GetValidatorSetByHeightRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "request cannot be nil"}, + {"empty request", &tmservice.GetValidatorSetByHeightRequest{}, true, "height must be greater than 0"}, + {"no pagination", &tmservice.GetValidatorSetByHeightRequest{Height: 1}, false, ""}, + {"with pagination", &tmservice.GetValidatorSetByHeightRequest{Height: 1, Pagination: &qtypes.PageRequest{Offset: 0, Limit: 1}}, false, ""}, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + grpcRes, err := s.queryClient.GetValidatorSetByHeight(context.Background(), tc.req) + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + s.Require().Len(grpcRes.Validators, len(vals)) + s.Require().Equal(grpcRes.Pagination.Total, uint64(len(vals))) + } + }) + } +} - // no pagination rest - _, err = rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d", val.APIAddress, 1)) - s.Require().NoError(err) - - // rest query with pagination - restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d?pagination.offset=%d&pagination.limit=%d", val.APIAddress, 1, 0, 1)) - var validatorSetRes tmservice.GetValidatorSetByHeightResponse - s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &validatorSetRes)) +func (s IntegrationTestSuite) TestValidatorSetByHeight_GRPCGateway() { + vals := s.network.Validators + testCases := []struct { + name string + url string + expErr bool + expErrMsg string + }{ + {"invalid height", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d", vals[0].APIAddress, -1), true, "height must be greater than 0"}, + {"no pagination", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d", vals[0].APIAddress, 1), false, ""}, + {"pagination invalid fields", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d?pagination.offset=-1&pagination.limit=-2", vals[0].APIAddress, 1), true, "strconv.ParseUint"}, + {"with pagination", fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d?pagination.offset=0&pagination.limit=2", vals[0].APIAddress, 1), false, ""}, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + res, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + if tc.expErr { + s.Require().Contains(string(res), tc.expErrMsg) + } else { + var result tmservice.GetValidatorSetByHeightResponse + err = vals[0].ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) + s.Require().NoError(err) + s.Require().Equal(uint64(len(vals)), result.Pagination.Total) + } + }) + } } func TestIntegrationTestSuite(t *testing.T) { diff --git a/client/grpc_query.go b/client/grpc_query.go index fc8cdeeb8..011523944 100644 --- a/client/grpc_query.go +++ b/client/grpc_query.go @@ -24,86 +24,54 @@ var _ gogogrpc.ClientConn = Context{} var protoCodec = encoding.GetCodec(proto.Name) // Invoke implements the grpc ClientConn.Invoke method -func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, args, reply interface{}, opts ...grpc.CallOption) (err error) { +func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) { // Two things can happen here: // 1. either we're broadcasting a Tx, in which call we call Tendermint's broadcast endpoint directly, // 2. or we are querying for state, in which case we call ABCI's Query. - // In both cases, we don't allow empty request args (it will panic unexpectedly). - if reflect.ValueOf(args).IsNil() { + // In both cases, we don't allow empty request req (it will panic unexpectedly). + if reflect.ValueOf(req).IsNil() { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "request cannot be nil") } // Case 1. Broadcasting a Tx. - if isBroadcast(method) { - req, ok := args.(*tx.BroadcastTxRequest) + if reqProto, ok := req.(*tx.BroadcastTxRequest); ok { if !ok { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxRequest)(nil), args) + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxRequest)(nil), req) } - res, ok := reply.(*tx.BroadcastTxResponse) + resProto, ok := reply.(*tx.BroadcastTxResponse) if !ok { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxResponse)(nil), args) + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxResponse)(nil), req) } - broadcastRes, err := TxServiceBroadcast(grpcCtx, ctx, req) + broadcastRes, err := TxServiceBroadcast(grpcCtx, ctx, reqProto) if err != nil { return err } - *res = *broadcastRes + *resProto = *broadcastRes return err } // Case 2. Querying state. - reqBz, err := protoCodec.Marshal(args) + inMd, _ := metadata.FromOutgoingContext(grpcCtx) + abciRes, outMd, err := RunGRPCQuery(ctx, grpcCtx, method, req, inMd) if err != nil { return err } - // parse height header - md, _ := metadata.FromOutgoingContext(grpcCtx) - if heights := md.Get(grpctypes.GRPCBlockHeightHeader); len(heights) > 0 { - height, err := strconv.ParseInt(heights[0], 10, 64) - if err != nil { - return err - } - if height < 0 { - return sdkerrors.Wrapf( - sdkerrors.ErrInvalidRequest, - "client.Context.Invoke: height (%d) from %q must be >= 0", height, grpctypes.GRPCBlockHeightHeader) - } - - ctx = ctx.WithHeight(height) - } - - req := abci.RequestQuery{ - Path: method, - Data: reqBz, - } - - res, err := ctx.QueryABCI(req) + err = protoCodec.Unmarshal(abciRes.Value, reply) if err != nil { return err } - err = protoCodec.Unmarshal(res.Value, reply) - if err != nil { - return err - } - - // Create header metadata. For now the headers contain: - // - block height - // We then parse all the call options, if the call option is a - // HeaderCallOption, then we manually set the value of that header to the - // metadata. - md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(res.Height, 10)) for _, callOpt := range opts { header, ok := callOpt.(grpc.HeaderCallOption) if !ok { continue } - *header.HeaderAddr = md + *header.HeaderAddr = outMd } if ctx.InterfaceRegistry != nil { @@ -118,6 +86,47 @@ func (Context) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.Ca return nil, fmt.Errorf("streaming rpc not supported") } -func isBroadcast(method string) bool { - return method == "/cosmos.tx.v1beta1.Service/BroadcastTx" +// RunGRPCQuery runs a gRPC query from the clientCtx, given all necessary +// arguments for the gRPC method, and returns the ABCI response. It is used +// to factorize code between client (Invoke) and server (RegisterGRPCServer) +// gRPC handlers. +func RunGRPCQuery(ctx Context, grpcCtx gocontext.Context, method string, req interface{}, md metadata.MD) (abci.ResponseQuery, metadata.MD, error) { + reqBz, err := protoCodec.Marshal(req) + if err != nil { + return abci.ResponseQuery{}, nil, err + } + + // parse height header + if heights := md.Get(grpctypes.GRPCBlockHeightHeader); len(heights) > 0 { + height, err := strconv.ParseInt(heights[0], 10, 64) + if err != nil { + return abci.ResponseQuery{}, nil, err + } + if height < 0 { + return abci.ResponseQuery{}, nil, sdkerrors.Wrapf( + sdkerrors.ErrInvalidRequest, + "client.Context.Invoke: height (%d) from %q must be >= 0", height, grpctypes.GRPCBlockHeightHeader) + } + + ctx = ctx.WithHeight(height) + } + + abciReq := abci.RequestQuery{ + Path: method, + Data: reqBz, + } + + abciRes, err := ctx.QueryABCI(abciReq) + if err != nil { + return abci.ResponseQuery{}, nil, err + } + + // Create header metadata. For now the headers contain: + // - block height + // We then parse all the call options, if the call option is a + // HeaderCallOption, then we manually set the value of that header to the + // metadata. + md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(abciRes.Height, 10)) + + return abciRes, md, nil } diff --git a/client/keys/add_ledger_test.go b/client/keys/add_ledger_test.go index b8ba1ec7f..5e32f755f 100644 --- a/client/keys/add_ledger_test.go +++ b/client/keys/add_ledger_test.go @@ -29,8 +29,8 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) { bech32PrefixConsAddr := "terravalcons" bech32PrefixConsPub := "terravalconspub" + config.SetPurpose(44) config.SetCoinType(330) - config.SetFullFundraiserPath("44'/330'/0'/0/0") config.SetBech32PrefixForAccount(bech32PrefixAccAddr, bech32PrefixAccPub) config.SetBech32PrefixForValidator(bech32PrefixValAddr, bech32PrefixValPub) config.SetBech32PrefixForConsensusNode(bech32PrefixConsAddr, bech32PrefixConsPub) @@ -77,8 +77,8 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) { "terrapub1addwnpepqvpg7r26nl2pvqqern00m6s9uaax3hauu2rzg8qpjzq9hy6xve7sw0d84m6", sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, key1.GetPubKey())) + config.SetPurpose(44) config.SetCoinType(118) - config.SetFullFundraiserPath("44'/118'/0'/0/0") config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) diff --git a/client/keys/delete_test.go b/client/keys/delete_test.go index 5a0013967..76f8c211f 100644 --- a/client/keys/delete_test.go +++ b/client/keys/delete_test.go @@ -31,7 +31,7 @@ func Test_runDeleteCmd(t *testing.T) { fakeKeyName1 := "runDeleteCmd_Key1" fakeKeyName2 := "runDeleteCmd_Key2" - path := sdk.GetConfig().GetFullFundraiserPath() + path := sdk.GetConfig().GetFullBIP44Path() kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) require.NoError(t, err) @@ -39,7 +39,7 @@ func Test_runDeleteCmd(t *testing.T) { _, err = kb.NewAccount(fakeKeyName1, testutil.TestMnemonic, "", path, hd.Secp256k1) require.NoError(t, err) - _, _, err = kb.NewMnemonic(fakeKeyName2, keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err = kb.NewMnemonic(fakeKeyName2, keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) cmd.SetArgs([]string{"blah", fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome)}) diff --git a/client/keys/export_test.go b/client/keys/export_test.go index b01bbf823..782af7540 100644 --- a/client/keys/export_test.go +++ b/client/keys/export_test.go @@ -31,7 +31,7 @@ func Test_runExportCmd(t *testing.T) { kb.Delete("keyname1") // nolint:errcheck }) - path := sdk.GetConfig().GetFullFundraiserPath() + path := sdk.GetConfig().GetFullBIP44Path() _, err = kb.NewAccount("keyname1", testutil.TestMnemonic, "", path, hd.Secp256k1) require.NoError(t, err) diff --git a/client/keys/list_test.go b/client/keys/list_test.go index 5904f74bf..17f8dd8e4 100644 --- a/client/keys/list_test.go +++ b/client/keys/list_test.go @@ -30,7 +30,7 @@ func Test_runListCmd(t *testing.T) { clientCtx := client.Context{}.WithKeyring(kb) ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) - path := "" //sdk.GetConfig().GetFullFundraiserPath() + path := "" //sdk.GetConfig().GetFullBIP44Path() _, err = kb.NewAccount("something", testutil.TestMnemonic, "", path, hd.Secp256k1) require.NoError(t, err) diff --git a/client/rest/rest.go b/client/rest/rest.go index ac05891e0..035b0f5db 100644 --- a/client/rest/rest.go +++ b/client/rest/rest.go @@ -25,6 +25,7 @@ func addHTTPDeprecationHeaders(h http.Handler) http.Handler { // WithHTTPDeprecationHeaders returns a new *mux.Router, identical to its input // but with the addition of HTTP Deprecation headers. This is used to mark legacy // amino REST endpoints as deprecated in the REST API. +// nolint: gocritic func WithHTTPDeprecationHeaders(r *mux.Router) *mux.Router { subRouter := r.NewRoute().Subrouter() subRouter.Use(addHTTPDeprecationHeaders) diff --git a/client/rpc/block.go b/client/rpc/block.go index d6b79c9fe..530f7c45d 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -15,7 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/rest" ) -//BlockCommand returns the verified block data for a given heights +// BlockCommand returns the verified block data for a given heights func BlockCommand() *cobra.Command { cmd := &cobra.Command{ Use: "block [height]", diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 175e7ff91..7fb9cf957 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -22,7 +22,7 @@ import ( // TODO these next two functions feel kinda hacky based on their placement -//ValidatorCommand returns the validator set for a given height +// ValidatorCommand returns the validator set for a given height func ValidatorCommand() *cobra.Command { cmd := &cobra.Command{ Use: "tendermint-validator-set [height]", @@ -79,12 +79,14 @@ type ValidatorOutput struct { type ResultValidatorsOutput struct { BlockHeight int64 `json:"block_height"` Validators []ValidatorOutput `json:"validators"` + Total uint64 `json:"total"` } func (rvo ResultValidatorsOutput) String() string { var b strings.Builder b.WriteString(fmt.Sprintf("block height: %d\n", rvo.BlockHeight)) + b.WriteString(fmt.Sprintf("total count: %d\n", rvo.Total)) for _, val := range rvo.Validators { b.WriteString( @@ -129,9 +131,15 @@ func GetValidators(ctx context.Context, clientCtx client.Context, height *int64, return ResultValidatorsOutput{}, err } + total := validatorsRes.Total + if validatorsRes.Total < 0 { + total = 0 + } + outputValidatorsRes := ResultValidatorsOutput{ BlockHeight: validatorsRes.BlockHeight, Validators: make([]ValidatorOutput, len(validatorsRes.Validators)), + Total: uint64(total), } for i := 0; i < len(validatorsRes.Validators); i++ { diff --git a/client/tx/legacy_test.go b/client/tx/legacy_test.go index b10c51e34..5dcab771c 100644 --- a/client/tx/legacy_test.go +++ b/client/tx/legacy_test.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/testutil/testdata" "github.com/cosmos/cosmos-sdk/types" + sdk "github.com/cosmos/cosmos-sdk/types" signing2 "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" "github.com/cosmos/cosmos-sdk/x/auth/signing" @@ -23,14 +24,13 @@ import ( const ( memo = "waboom" gas = uint64(10000) - timeoutHeight = 5 + timeoutHeight = uint64(5) ) var ( fee = types.NewCoins(types.NewInt64Coin("bam", 100)) _, pub1, addr1 = testdata.KeyTestPubAddr() _, _, addr2 = testdata.KeyTestPubAddr() - msg = banktypes.NewMsgSend(addr1, addr2, types.NewCoins(types.NewInt64Coin("wack", 10000))) sig = signing2.SignatureV2{ PubKey: pub1, Data: &signing2.SingleSignatureData{ @@ -38,13 +38,18 @@ var ( Signature: []byte("dummy"), }, } + msg0 = banktypes.NewMsgSend(addr1, addr2, types.NewCoins(types.NewInt64Coin("wack", 1))) + msg1 = sdk.ServiceMsg{ + MethodName: "/cosmos.bank.v1beta1.Msg/Send", + Request: banktypes.NewMsgSend(addr1, addr2, types.NewCoins(types.NewInt64Coin("wack", 2))), + } ) func buildTestTx(t *testing.T, builder client.TxBuilder) { builder.SetMemo(memo) builder.SetGasLimit(gas) builder.SetFeeAmount(fee) - err := builder.SetMsgs(msg) + err := builder.SetMsgs(msg0, msg1) require.NoError(t, err) err = builder.SetSignatures(sig) require.NoError(t, err) @@ -75,11 +80,15 @@ func (s *TestSuite) TestCopyTx() { protoBuilder2 := s.protoCfg.NewTxBuilder() err = tx2.CopyTx(aminoBuilder.GetTx(), protoBuilder2, false) s.Require().NoError(err) - bz, err := s.protoCfg.TxEncoder()(protoBuilder.GetTx()) + // Check sigs, signers and msgs. + sigsV2_1, err := protoBuilder.GetTx().GetSignaturesV2() s.Require().NoError(err) - bz2, err := s.protoCfg.TxEncoder()(protoBuilder2.GetTx()) + sigsV2_2, err := protoBuilder2.GetTx().GetSignaturesV2() s.Require().NoError(err) - s.Require().Equal(bz, bz2) + s.Require().Equal(sigsV2_1, sigsV2_2) + s.Require().Equal(protoBuilder.GetTx().GetSigners(), protoBuilder2.GetTx().GetSigners()) + s.Require().Equal(protoBuilder.GetTx().GetMsgs()[0], protoBuilder2.GetTx().GetMsgs()[0]) + s.Require().Equal(protoBuilder.GetTx().GetMsgs()[1].(sdk.ServiceMsg).Request, protoBuilder2.GetTx().GetMsgs()[1]) // We lose the "ServiceMsg" information // amino -> proto -> amino aminoBuilder = s.aminoCfg.NewTxBuilder() @@ -90,11 +99,15 @@ func (s *TestSuite) TestCopyTx() { aminoBuilder2 := s.aminoCfg.NewTxBuilder() err = tx2.CopyTx(protoBuilder.GetTx(), aminoBuilder2, false) s.Require().NoError(err) - bz, err = s.aminoCfg.TxEncoder()(aminoBuilder.GetTx()) + // Check sigs, signers, and msgs + sigsV2_1, err = aminoBuilder.GetTx().GetSignaturesV2() s.Require().NoError(err) - bz2, err = s.aminoCfg.TxEncoder()(aminoBuilder2.GetTx()) + sigsV2_2, err = aminoBuilder2.GetTx().GetSignaturesV2() s.Require().NoError(err) - s.Require().Equal(bz, bz2) + s.Require().Equal(sigsV2_1, sigsV2_2) + s.Require().Equal(aminoBuilder.GetTx().GetSigners(), aminoBuilder2.GetTx().GetSigners()) + s.Require().Equal(aminoBuilder.GetTx().GetMsgs()[0], aminoBuilder2.GetTx().GetMsgs()[0]) + s.Require().Equal(aminoBuilder.GetTx().GetMsgs()[1], aminoBuilder2.GetTx().GetMsgs()[1]) // We lose the "ServiceMsg" information } func (s *TestSuite) TestConvertTxToStdTx() { @@ -106,7 +119,8 @@ func (s *TestSuite) TestConvertTxToStdTx() { s.Require().Equal(memo, stdTx.Memo) s.Require().Equal(gas, stdTx.Fee.Gas) s.Require().Equal(fee, stdTx.Fee.Amount) - s.Require().Equal(msg, stdTx.Msgs[0]) + s.Require().Equal(msg0, stdTx.Msgs[0]) + s.Require().Equal(msg1.Request, stdTx.Msgs[1]) s.Require().Equal(timeoutHeight, stdTx.TimeoutHeight) s.Require().Equal(sig.PubKey, stdTx.Signatures[0].PubKey) s.Require().Equal(sig.Data.(*signing2.SingleSignatureData).Signature, stdTx.Signatures[0].Signature) @@ -125,7 +139,8 @@ func (s *TestSuite) TestConvertTxToStdTx() { s.Require().Equal(memo, stdTx.Memo) s.Require().Equal(gas, stdTx.Fee.Gas) s.Require().Equal(fee, stdTx.Fee.Amount) - s.Require().Equal(msg, stdTx.Msgs[0]) + s.Require().Equal(msg0, stdTx.Msgs[0]) + s.Require().Equal(msg1.Request, stdTx.Msgs[1]) s.Require().Equal(timeoutHeight, stdTx.TimeoutHeight) s.Require().Empty(stdTx.Signatures) @@ -158,3 +173,7 @@ func (s *TestSuite) TestConvertAndEncodeStdTx() { s.Require().NoError(err) s.Require().Equal(stdTx, decodedTx) } + +func TestTestSuite(t *testing.T) { + suite.Run(t, new(TestSuite)) +} diff --git a/client/tx/tx_test.go b/client/tx/tx_test.go index 3c7a4bebe..58edbc0ea 100644 --- a/client/tx/tx_test.go +++ b/client/tx/tx_test.go @@ -132,10 +132,10 @@ func TestSign(t *testing.T) { var from2 = "test_key2" // create a new key using a mnemonic generator and test if we can reuse seed to recreate that account - _, seed, err := kr.NewMnemonic(from1, keyring.English, path, hd.Secp256k1) + _, seed, err := kr.NewMnemonic(from1, keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1) requireT.NoError(err) requireT.NoError(kr.Delete(from1)) - info1, _, err := kr.NewMnemonic(from1, keyring.English, path, hd.Secp256k1) + info1, _, err := kr.NewMnemonic(from1, keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1) requireT.NoError(err) info2, err := kr.NewAccount(from2, seed, "", path, hd.Secp256k1) diff --git a/codec/amino.go b/codec/amino.go index 78fd650ca..ea4c8d4fd 100644 --- a/codec/amino.go +++ b/codec/amino.go @@ -13,8 +13,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec/types" ) -// deprecated: LegacyAmino defines a wrapper for an Amino codec that properly handles protobuf -// types with Any's +// LegacyAmino defines a wrapper for an Amino codec that properly +// handles protobuf types with Any's. Deprecated. type LegacyAmino struct { Amino *amino.Codec } diff --git a/codec/types/any.go b/codec/types/any.go index c33931f0e..1d9d34ff2 100644 --- a/codec/types/any.go +++ b/codec/types/any.go @@ -71,11 +71,14 @@ func NewAnyWithValue(v proto.Message) (*Any, error) { // into the protobuf Any serialization. For simple marshaling you should use NewAnyWithValue. func NewAnyWithCustomTypeURL(v proto.Message, typeURL string) (*Any, error) { bz, err := proto.Marshal(v) + if err != nil { + return nil, err + } return &Any{ TypeUrl: typeURL, Value: bz, cachedValue: v, - }, err + }, nil } // UnsafePackAny packs the value x in the Any and instead of returning the error diff --git a/codec/types/any_test.go b/codec/types/any_test.go new file mode 100644 index 000000000..cea3e0444 --- /dev/null +++ b/codec/types/any_test.go @@ -0,0 +1,68 @@ +package types_test + +import ( + "fmt" + "runtime" + "testing" + + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +type errOnMarshal struct { + testdata.Dog +} + +var _ proto.Message = (*errOnMarshal)(nil) + +var errAlways = fmt.Errorf("always erroring") + +func (eom *errOnMarshal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return nil, errAlways +} + +const fauxURL = "/anyhere" + +var eom = &errOnMarshal{} + +// Ensure that returning an error doesn't suddenly allocate and waste bytes. +// See https://github.com/cosmos/cosmos-sdk/issues/8537 +func TestNewAnyWithCustomTypeURLWithErrorNoAllocation(t *testing.T) { + var ms1, ms2 runtime.MemStats + runtime.ReadMemStats(&ms1) + any, err := types.NewAnyWithCustomTypeURL(eom, fauxURL) + runtime.ReadMemStats(&ms2) + // Ensure that no fresh allocation was made. + if diff := ms2.HeapAlloc - ms1.HeapAlloc; diff > 0 { + t.Errorf("Unexpected allocation of %d bytes", diff) + } + if err == nil { + t.Fatal("err wasn't returned") + } + if any != nil { + t.Fatalf("Unexpectedly got a non-nil Any value: %v", any) + } +} + +var sink interface{} + +func BenchmarkNewAnyWithCustomTypeURLWithErrorReturned(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + any, err := types.NewAnyWithCustomTypeURL(eom, fauxURL) + if err == nil { + b.Fatal("err wasn't returned") + } + if any != nil { + b.Fatalf("Unexpectedly got a non-nil Any value: %v", any) + } + sink = any + } + if sink == nil { + b.Fatal("benchmark didn't run") + } + sink = (interface{})(nil) +} diff --git a/crypto/armor_test.go b/crypto/armor_test.go index 70854f6dc..fb80b5266 100644 --- a/crypto/armor_test.go +++ b/crypto/armor_test.go @@ -73,7 +73,7 @@ func TestArmorUnarmorPubKey(t *testing.T) { cstore := keyring.NewInMemory() // Add keys and see they return in alphabetical order - info, _, err := cstore.NewMnemonic("Bob", keyring.English, types.FullFundraiserPath, hd.Secp256k1) + info, _, err := cstore.NewMnemonic("Bob", keyring.English, types.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) armored := crypto.ArmorPubKeyBytes(legacy.Cdc.Amino.MustMarshalBinaryBare(info.GetPubKey()), "") pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armored) diff --git a/crypto/codec/amino.go b/crypto/codec/amino.go index d50a08864..50119ed19 100644 --- a/crypto/codec/amino.go +++ b/crypto/codec/amino.go @@ -26,7 +26,7 @@ func RegisterCrypto(cdc *codec.LegacyAmino) { cdc.RegisterInterface((*cryptotypes.PrivKey)(nil), nil) cdc.RegisterConcrete(sr25519.PrivKey{}, sr25519.PrivKeyName, nil) - cdc.RegisterConcrete(&ed25519.PrivKey{}, + cdc.RegisterConcrete(&ed25519.PrivKey{}, //nolint:staticcheck ed25519.PrivKeyName, nil) cdc.RegisterConcrete(&secp256k1.PrivKey{}, secp256k1.PrivKeyName, nil) diff --git a/crypto/hd/hdpath.go b/crypto/hd/hdpath.go index 04469d730..058ddcc45 100644 --- a/crypto/hd/hdpath.go +++ b/crypto/hd/hdpath.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "fmt" "math/big" + "path/filepath" "strconv" "strings" @@ -177,6 +178,9 @@ func ComputeMastersFromSeed(seed []byte) (secret [32]byte, chainCode [32]byte) { // DerivePrivateKeyForPath derives the private key by following the BIP 32/44 path from privKeyBytes, // using the given chainCode. func DerivePrivateKeyForPath(privKeyBytes, chainCode [32]byte, path string) ([]byte, error) { + // First step is to trim the right end path separator lest we panic. + // See issue https://github.com/cosmos/cosmos-sdk/issues/8557 + path = strings.TrimRightFunc(path, func(r rune) bool { return r == filepath.Separator }) data := privKeyBytes parts := strings.Split(path, "/") @@ -187,7 +191,10 @@ func DerivePrivateKeyForPath(privKeyBytes, chainCode [32]byte, path string) ([]b parts = parts[1:] } - for _, part := range parts { + for i, part := range parts { + if part == "" { + return nil, fmt.Errorf("path %q with split element #%d is an empty string", part, i) + } // do we have an apostrophe? harden := part[len(part)-1:] == "'" // harden == private derivation, else public derivation: diff --git a/crypto/hd/hdpath_test.go b/crypto/hd/hdpath_test.go index 6dfda043e..8b1c40692 100644 --- a/crypto/hd/hdpath_test.go +++ b/crypto/hd/hdpath_test.go @@ -281,3 +281,26 @@ func ExampleSomeBIP32TestVecs() { // // c4c11d8c03625515905d7e89d25dfc66126fbc629ecca6db489a1a72fc4bda78 } + +// Ensuring that we don't crash if values have trailing slashes +// See issue https://github.com/cosmos/cosmos-sdk/issues/8557. +func TestDerivePrivateKeyForPathDoNotCrash(t *testing.T) { + paths := []string{ + "m/5/", + "m/5", + "/44", + "m//5", + "m/0/7", + "/", + " m  /0/7", // Test case from fuzzer + "  /  ", // Test case from fuzzer + "m///7//////", + } + + for _, path := range paths { + path := path + t.Run(path, func(t *testing.T) { + hd.DerivePrivateKeyForPath([32]byte{}, [32]byte{}, path) + }) + } +} diff --git a/crypto/keyring/keyring.go b/crypto/keyring/keyring.go index 164e6bfc1..14dd4581a 100644 --- a/crypto/keyring/keyring.go +++ b/crypto/keyring/keyring.go @@ -64,14 +64,17 @@ type Keyring interface { Delete(uid string) error DeleteByAddress(address sdk.Address) error - // NewMnemonic generates a new mnemonic, derives a hierarchical deterministic - // key from that, and persists it to the storage. Returns the generated mnemonic and the key - // Info. It returns an error if it fails to generate a key for the given algo type, or if - // another key is already stored under the same name. - NewMnemonic(uid string, language Language, hdPath string, algo SignatureAlgo) (Info, string, error) + // NewMnemonic generates a new mnemonic, derives a hierarchical deterministic key from it, and + // persists the key to storage. Returns the generated mnemonic and the key Info. + // It returns an error if it fails to generate a key for the given algo type, or if + // another key is already stored under the same name or address. + // + // A passphrase set to the empty string will set the passphrase to the DefaultBIP39Passphrase value. + NewMnemonic(uid string, language Language, hdPath, bip39Passphrase string, algo SignatureAlgo) (Info, string, error) // NewAccount converts a mnemonic to a private key and BIP-39 HD Path and persists it. - NewAccount(uid, mnemonic, bip39Passwd, hdPath string, algo SignatureAlgo) (Info, error) + // It fails if there is an existing key Info with the same address. + NewAccount(uid, mnemonic, bip39Passphrase, hdPath string, algo SignatureAlgo) (Info, error) // SaveLedgerKey retrieves a public key reference from a Ledger device and persists it. SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (Info, error) @@ -477,7 +480,7 @@ func (ks keystore) List() ([]Info, error) { return res, nil } -func (ks keystore) NewMnemonic(uid string, language Language, hdPath string, algo SignatureAlgo) (Info, string, error) { +func (ks keystore) NewMnemonic(uid string, language Language, hdPath, bip39Passphrase string, algo SignatureAlgo) (Info, string, error) { if language != English { return nil, "", ErrUnsupportedLanguage } @@ -498,12 +501,16 @@ func (ks keystore) NewMnemonic(uid string, language Language, hdPath string, alg return nil, "", err } - info, err := ks.NewAccount(uid, mnemonic, DefaultBIP39Passphrase, hdPath, algo) + if bip39Passphrase == "" { + bip39Passphrase = DefaultBIP39Passphrase + } + + info, err := ks.NewAccount(uid, mnemonic, bip39Passphrase, hdPath, algo) if err != nil { return nil, "", err } - return info, mnemonic, err + return info, mnemonic, nil } func (ks keystore) NewAccount(uid string, mnemonic string, bip39Passphrase string, hdPath string, algo SignatureAlgo) (Info, error) { @@ -519,6 +526,13 @@ func (ks keystore) NewAccount(uid string, mnemonic string, bip39Passphrase strin privKey := algo.Generate()(derivedPriv) + // check if the a key already exists with the same address and return an error + // if found + address := sdk.AccAddress(privKey.PubKey().Address()) + if _, err := ks.KeyByAddress(address); err == nil { + return nil, fmt.Errorf("account with address %s already exists in keyring, delete the key first if you want to recreate it", address) + } + return ks.writeLocalKey(uid, privKey, algo.Name()) } diff --git a/crypto/keyring/keyring_ledger_test.go b/crypto/keyring/keyring_ledger_test.go index eb7e85f9e..5cdac5069 100644 --- a/crypto/keyring/keyring_ledger_test.go +++ b/crypto/keyring/keyring_ledger_test.go @@ -80,7 +80,7 @@ func TestSignVerifyKeyRingWithLedger(t *testing.T) { require.True(t, i1.GetPubKey().VerifySignature(d1, s1)) require.True(t, bytes.Equal(s1, s2)) - localInfo, _, err := kb.NewMnemonic("test", English, types.FullFundraiserPath, hd.Secp256k1) + localInfo, _, err := kb.NewMnemonic("test", English, types.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) _, _, err = SignWithLedger(localInfo, d1) require.Error(t, err) diff --git a/crypto/keyring/keyring_test.go b/crypto/keyring/keyring_test.go index bcd7eddf3..c9f387e78 100644 --- a/crypto/keyring/keyring_test.go +++ b/crypto/keyring/keyring_test.go @@ -42,7 +42,7 @@ func TestNewKeyring(t *testing.T) { require.Equal(t, "unknown keyring backend fuzzy", err.Error()) mockIn.Reset("password\npassword\n") - info, _, err := kr.NewMnemonic("foo", English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := kr.NewMnemonic("foo", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) require.Equal(t, "foo", info.GetName()) } @@ -59,17 +59,17 @@ func TestKeyManagementKeyRing(t *testing.T) { require.Nil(t, err) require.Empty(t, l) - _, _, err = kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, notSupportedAlgo{}) + _, _, err = kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, notSupportedAlgo{}) require.Error(t, err, "ed25519 keys are currently not supported by keybase") // create some keys _, err = kb.Key(n1) require.Error(t, err) - i, _, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + i, _, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.NoError(t, err) require.Equal(t, n1, i.GetName()) - _, _, err = kb.NewMnemonic(n2, English, sdk.FullFundraiserPath, algo) + _, _, err = kb.NewMnemonic(n2, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.NoError(t, err) // we can get these keys @@ -137,10 +137,10 @@ func TestSignVerifyKeyRing(t *testing.T) { n1, n2, n3 := "some dude", "a dudette", "dude-ish" // create two users and get their info - i1, _, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + i1, _, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err) - i2, _, err := kb.NewMnemonic(n2, English, sdk.FullFundraiserPath, algo) + i2, _, err := kb.NewMnemonic(n2, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err) // let's try to sign some messages @@ -209,7 +209,7 @@ func TestExportImportKeyRing(t *testing.T) { kb, err := New("keybasename", "test", t.TempDir(), nil) require.NoError(t, err) - info, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) require.Equal(t, info.GetName(), "john") @@ -243,7 +243,7 @@ func TestExportImportPubKeyKeyRing(t *testing.T) { algo := hd.Secp256k1 // CreateMnemonic a private-public key pair and ensure consistency - info, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, algo) + info, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err) require.NotEqual(t, info, "") require.Equal(t, info.GetName(), "john") @@ -285,7 +285,7 @@ func TestAdvancedKeyManagementKeyRing(t *testing.T) { n1, n2 := "old-name", "new name" // make sure key works with initial password - _, _, err = kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + _, _, err = kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err, "%+v", err) _, err = kb.ExportPubKeyArmor(n1 + ".notreal") @@ -320,7 +320,7 @@ func TestSeedPhraseKeyRing(t *testing.T) { n1, n2 := "lost-key", "found-again" // make sure key works with initial password - info, mnemonic, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + info, mnemonic, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err, "%+v", err) require.Equal(t, n1, info.GetName()) require.NotEmpty(t, mnemonic) @@ -345,7 +345,7 @@ func TestKeyringKeybaseExportImportPrivKey(t *testing.T) { kb, err := New("keybasename", "test", t.TempDir(), nil) require.NoError(t, err) - _, _, err = kb.NewMnemonic("john", English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err = kb.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) keystr, err := kb.ExportPrivKeyArmor("john", "somepassword") @@ -372,7 +372,7 @@ func TestKeyringKeybaseExportImportPrivKey(t *testing.T) { func TestInMemoryLanguage(t *testing.T) { kb := NewInMemory() - _, _, err := kb.NewMnemonic("something", Japanese, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err := kb.NewMnemonic("something", Japanese, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.Error(t, err) require.Equal(t, "unsupported language: only english is supported", err.Error()) } @@ -412,17 +412,17 @@ func TestInMemoryKeyManagement(t *testing.T) { require.Nil(t, err) require.Empty(t, l) - _, _, err = cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, notSupportedAlgo{}) + _, _, err = cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, notSupportedAlgo{}) require.Error(t, err, "ed25519 keys are currently not supported by keybase") // create some keys _, err = cstore.Key(n1) require.Error(t, err) - i, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + i, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.NoError(t, err) require.Equal(t, n1, i.GetName()) - _, _, err = cstore.NewMnemonic(n2, English, sdk.FullFundraiserPath, algo) + _, _, err = cstore.NewMnemonic(n2, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.NoError(t, err) // we can get these keys @@ -492,10 +492,10 @@ func TestInMemorySignVerify(t *testing.T) { n1, n2, n3 := "some dude", "a dudette", "dude-ish" // create two users and get their info - i1, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + i1, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err) - i2, _, err := cstore.NewMnemonic(n2, English, sdk.FullFundraiserPath, algo) + i2, _, err := cstore.NewMnemonic(n2, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err) // let's try to sign some messages @@ -566,7 +566,7 @@ func TestInMemoryExportImport(t *testing.T) { // make the storage with reasonable defaults cstore := NewInMemory() - info, _, err := cstore.NewMnemonic("john", English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := cstore.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) require.Equal(t, info.GetName(), "john") @@ -596,7 +596,7 @@ func TestInMemoryExportImport(t *testing.T) { func TestInMemoryExportImportPrivKey(t *testing.T) { kb := NewInMemory() - info, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) require.Equal(t, info.GetName(), "john") priv1, err := kb.Key("john") @@ -624,7 +624,7 @@ func TestInMemoryExportImportPubKey(t *testing.T) { cstore := NewInMemory() // CreateMnemonic a private-public key pair and ensure consistency - info, _, err := cstore.NewMnemonic("john", English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := cstore.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.Nil(t, err) require.NotEqual(t, info, "") require.Equal(t, info.GetName(), "john") @@ -663,7 +663,7 @@ func TestInMemoryAdvancedKeyManagement(t *testing.T) { n1, n2 := "old-name", "new name" // make sure key works with initial password - _, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + _, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err, "%+v", err) // exporting requires the proper name and passphrase @@ -698,7 +698,7 @@ func TestInMemorySeedPhrase(t *testing.T) { n1, n2 := "lost-key", "found-again" // make sure key works with initial password - info, mnemonic, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + info, mnemonic, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err, "%+v", err) require.Equal(t, n1, info.GetName()) require.NotEmpty(t, mnemonic) @@ -724,7 +724,7 @@ func TestKeyChain_ShouldFailWhenAddingSameGeneratedAccount(t *testing.T) { require.NoError(t, err) // Given we create a mnemonic - _, seed, err := kr.NewMnemonic("test", English, "", hd.Secp256k1) + _, seed, err := kr.NewMnemonic("test", English, "", DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) require.NoError(t, kr.Delete("test")) @@ -745,7 +745,7 @@ func ExampleNew() { sec := hd.Secp256k1 // Add keys and see they return in alphabetical order - bob, _, err := cstore.NewMnemonic("Bob", English, sdk.FullFundraiserPath, sec) + bob, _, err := cstore.NewMnemonic("Bob", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, sec) if err != nil { // this should never happen fmt.Println(err) @@ -753,8 +753,8 @@ func ExampleNew() { // return info here just like in List fmt.Println(bob.GetName()) } - _, _, _ = cstore.NewMnemonic("Alice", English, sdk.FullFundraiserPath, sec) - _, _, _ = cstore.NewMnemonic("Carl", English, sdk.FullFundraiserPath, sec) + _, _, _ = cstore.NewMnemonic("Alice", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, sec) + _, _, _ = cstore.NewMnemonic("Carl", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, sec) info, _ := cstore.List() for _, i := range info { fmt.Println(i.GetName()) @@ -799,16 +799,16 @@ func TestAltKeyring_List(t *testing.T) { require.Empty(t, list) // Fails on creating unsupported pubKeyType - _, _, err = keyring.NewMnemonic("failing", English, sdk.FullFundraiserPath, notSupportedAlgo{}) + _, _, err = keyring.NewMnemonic("failing", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, notSupportedAlgo{}) require.EqualError(t, err, ErrUnsupportedSigningAlgo.Error()) // Create 3 keys uid1, uid2, uid3 := "Zkey", "Bkey", "Rkey" - _, _, err = keyring.NewMnemonic(uid1, English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err = keyring.NewMnemonic(uid1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) - _, _, err = keyring.NewMnemonic(uid2, English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err = keyring.NewMnemonic(uid2, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) - _, _, err = keyring.NewMnemonic(uid3, English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err = keyring.NewMnemonic(uid3, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) list, err = keyring.List() @@ -852,7 +852,7 @@ func TestAltKeyring_Get(t *testing.T) { require.NoError(t, err) uid := someKey - mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) key, err := keyring.Key(uid) @@ -865,7 +865,7 @@ func TestAltKeyring_KeyByAddress(t *testing.T) { require.NoError(t, err) uid := someKey - mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) key, err := keyring.KeyByAddress(mnemonic.GetAddress()) @@ -878,7 +878,7 @@ func TestAltKeyring_Delete(t *testing.T) { require.NoError(t, err) uid := someKey - _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) list, err := keyring.List() @@ -898,7 +898,7 @@ func TestAltKeyring_DeleteByAddress(t *testing.T) { require.NoError(t, err) uid := someKey - mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) list, err := keyring.List() @@ -940,9 +940,9 @@ func TestAltKeyring_SaveMultisig(t *testing.T) { keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) require.NoError(t, err) - mnemonic1, _, err := keyring.NewMnemonic("key1", English, sdk.FullFundraiserPath, hd.Secp256k1) + mnemonic1, _, err := keyring.NewMnemonic("key1", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) - mnemonic2, _, err := keyring.NewMnemonic("key2", English, sdk.FullFundraiserPath, hd.Secp256k1) + mnemonic2, _, err := keyring.NewMnemonic("key2", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) key := "multi" @@ -969,7 +969,7 @@ func TestAltKeyring_Sign(t *testing.T) { require.NoError(t, err) uid := "jack" - _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) msg := []byte("some message") @@ -985,7 +985,7 @@ func TestAltKeyring_SignByAddress(t *testing.T) { require.NoError(t, err) uid := "jack" - mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) msg := []byte("some message") @@ -1001,7 +1001,7 @@ func TestAltKeyring_ImportExportPrivKey(t *testing.T) { require.NoError(t, err) uid := theID - _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) passphrase := "somePass" @@ -1027,7 +1027,7 @@ func TestAltKeyring_ImportExportPrivKey_ByAddress(t *testing.T) { require.NoError(t, err) uid := theID - mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) passphrase := "somePass" @@ -1054,7 +1054,7 @@ func TestAltKeyring_ImportExportPubKey(t *testing.T) { require.NoError(t, err) uid := theID - _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) armor, err := keyring.ExportPubKeyArmor(uid) @@ -1076,7 +1076,7 @@ func TestAltKeyring_ImportExportPubKey_ByAddress(t *testing.T) { require.NoError(t, err) uid := theID - mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) armor, err := keyring.ExportPubKeyArmorByAddress(mnemonic.GetAddress()) @@ -1099,7 +1099,7 @@ func TestAltKeyring_UnsafeExportPrivKeyHex(t *testing.T) { uid := theID - _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) unsafeKeyring := NewUnsafe(keyring) @@ -1121,11 +1121,11 @@ func TestAltKeyring_ConstructorSupportedAlgos(t *testing.T) { require.NoError(t, err) // should fail when using unsupported signing algorythm. - _, _, err = keyring.NewMnemonic("test", English, sdk.FullFundraiserPath, notSupportedAlgo{}) + _, _, err = keyring.NewMnemonic("test", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, notSupportedAlgo{}) require.EqualError(t, err, "unsupported signing algo") // but works with default signing algo. - _, _, err = keyring.NewMnemonic("test", English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err = keyring.NewMnemonic("test", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) // but we can create a new keybase with our provided algos. @@ -1137,7 +1137,7 @@ func TestAltKeyring_ConstructorSupportedAlgos(t *testing.T) { require.NoError(t, err) // now this new keyring does not fail when signing with provided algo - _, _, err = keyring2.NewMnemonic("test", English, sdk.FullFundraiserPath, notSupportedAlgo{}) + _, _, err = keyring2.NewMnemonic("test", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, notSupportedAlgo{}) require.NoError(t, err) } diff --git a/crypto/keyring/legacy.go b/crypto/keyring/legacy.go index d6653e956..94bff8232 100644 --- a/crypto/keyring/legacy.go +++ b/crypto/keyring/legacy.go @@ -42,7 +42,7 @@ var _ LegacyKeybase = dbKeybase{} // dbKeybase combines encryption and storage implementation to provide a // full-featured key manager. // -// NOTE: dbKeybase will be deprecated in favor of keyringKeybase. +// Deprecated: dbKeybase will be removed in favor of keyringKeybase. type dbKeybase struct { db dbm.DB } diff --git a/crypto/keys/ed25519/ed25519.go b/crypto/keys/ed25519/ed25519.go index 17368c4b1..7c70d573e 100644 --- a/crypto/keys/ed25519/ed25519.go +++ b/crypto/keys/ed25519/ed25519.go @@ -116,7 +116,8 @@ func (privKey *PrivKey) UnmarshalAminoJSON(bz []byte) error { return privKey.UnmarshalAmino(bz) } -// GenPrivKey generates a new ed25519 private key. +// GenPrivKey generates a new ed25519 private key. These ed25519 keys must not +// be used in SDK apps except in a tendermint validator context. // It uses OS randomness in conjunction with the current global random seed // in tendermint/libs/common to generate the private key. func GenPrivKey() *PrivKey { @@ -137,6 +138,7 @@ func genPrivKey(rand io.Reader) *PrivKey { // GenPrivKeyFromSecret hashes the secret with SHA2, and uses // that 32 byte output to create the private key. +// NOTE: ed25519 keys must not be used in SDK apps except in a tendermint validator context. // NOTE: secret should be the output of a KDF like bcrypt, // if it's derived from user input. func GenPrivKeyFromSecret(secret []byte) *PrivKey { @@ -151,10 +153,14 @@ var _ cryptotypes.PubKey = &PubKey{} var _ codec.AminoMarshaler = &PubKey{} // Address is the SHA256-20 of the raw pubkey bytes. +// It doesn't implement ADR-28 addresses and it must not be used +// in SDK except in a tendermint validator context. func (pubKey *PubKey) Address() crypto.Address { if len(pubKey.Key) != PubKeySize { panic("pubkey is incorrect size") } + // For ADR-28 compatible address we would need to + // return address.Hash(proto.MessageName(pubKey), pubKey.Key) return crypto.Address(tmhash.SumTruncated(pubKey.Key)) } diff --git a/crypto/keys/ed25519/ed25519_test.go b/crypto/keys/ed25519/ed25519_test.go index 59cce4066..27cef37f9 100644 --- a/crypto/keys/ed25519/ed25519_test.go +++ b/crypto/keys/ed25519/ed25519_test.go @@ -84,6 +84,12 @@ func TestPubKeyEquals(t *testing.T) { } } +func TestAddressEd25519(t *testing.T) { + pk := ed25519.PubKey{[]byte{125, 80, 29, 208, 159, 53, 119, 198, 73, 53, 187, 33, 199, 144, 62, 255, 1, 235, 117, 96, 128, 211, 17, 45, 34, 64, 189, 165, 33, 182, 54, 206}} + addr := pk.Address() + require.Len(t, addr, 20, "Address must be 20 bytes long") +} + func TestPrivKeyEquals(t *testing.T) { ed25519PrivKey := ed25519.GenPrivKey() diff --git a/crypto/keys/ed25519/keys.pb.go b/crypto/keys/ed25519/keys.pb.go index 35a98cf05..227b18701 100644 --- a/crypto/keys/ed25519/keys.pb.go +++ b/crypto/keys/ed25519/keys.pb.go @@ -24,11 +24,11 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// PubKey defines a ed25519 public key -// Key is the compressed form of the pubkey. The first byte depends is a 0x02 byte -// if the y-coordinate is the lexicographically largest of the two associated with -// the x-coordinate. Otherwise the first byte is a 0x03. -// This prefix is followed with the x-coordinate. +// PubKey is an ed25519 public key for handling Tendermint keys in SDK. +// It's needed for Any serialization and SDK compatibility. +// It must not be used in a non Tendermint key context because it doesn't implement +// ADR-28. Nevertheless, you will like to use ed25519 in app user level +// then you must create a new proto message and follow ADR-28 for Address construction. type PubKey struct { Key crypto_ed25519.PublicKey `protobuf:"bytes,1,opt,name=key,proto3,casttype=crypto/ed25519.PublicKey" json:"key,omitempty"` } @@ -72,7 +72,8 @@ func (m *PubKey) GetKey() crypto_ed25519.PublicKey { return nil } -// PrivKey defines a ed25519 private key. +// Deprecated: PrivKey defines a ed25519 private key. +// NOTE: ed25519 keys must not be used in SDK apps except in a tendermint validator context. type PrivKey struct { Key crypto_ed25519.PrivateKey `protobuf:"bytes,1,opt,name=key,proto3,casttype=crypto/ed25519.PrivateKey" json:"key,omitempty"` } diff --git a/crypto/keys/multisig/multisig.go b/crypto/keys/multisig/multisig.go index 590e5ba11..b3d8d8fab 100644 --- a/crypto/keys/multisig/multisig.go +++ b/crypto/keys/multisig/multisig.go @@ -15,6 +15,8 @@ var _ multisigtypes.PubKey = &LegacyAminoPubKey{} var _ types.UnpackInterfacesMessage = &LegacyAminoPubKey{} // NewLegacyAminoPubKey returns a new LegacyAminoPubKey. +// Multisig can be constructed with multiple same keys - it will increase the power of +// the owner of that key (he will still need to add multiple signatures in the right order). // Panics if len(pubKeys) < k or 0 >= k. func NewLegacyAminoPubKey(k int, pubKeys []cryptotypes.PubKey) *LegacyAminoPubKey { if k <= 0 { @@ -40,7 +42,11 @@ func (m *LegacyAminoPubKey) Bytes() []byte { return AminoCdc.MustMarshalBinaryBare(m) } -// VerifyMultisignature implements the multisigtypes.PubKey VerifyMultisignature method +// VerifyMultisignature implements the multisigtypes.PubKey VerifyMultisignature method. +// The signatures must be added in an order corresponding to the public keys order in +// LegacyAminoPubKey. It's OK to have multiple same keys in the multisig - it will increase +// the power of the owner of that key - in that case the signer will still need to append +// multiple same signatures in the right order. func (m *LegacyAminoPubKey) VerifyMultisignature(getSignBytes multisigtypes.GetSignBytesFunc, sig *signing.MultiSignatureData) error { bitarray := sig.BitArray sigs := sig.Signatures @@ -48,7 +54,7 @@ func (m *LegacyAminoPubKey) VerifyMultisignature(getSignBytes multisigtypes.GetS pubKeys := m.GetPubKeys() // ensure bit array is the correct size if len(pubKeys) != size { - return fmt.Errorf("bit array size is incorrect %d", len(pubKeys)) + return fmt.Errorf("bit array size is incorrect, expecting: %d", len(pubKeys)) } // ensure size of signature list if len(sigs) < int(m.Threshold) || len(sigs) > size { @@ -56,7 +62,7 @@ func (m *LegacyAminoPubKey) VerifyMultisignature(getSignBytes multisigtypes.GetS } // ensure at least k signatures are set if bitarray.NumTrueBitsBefore(size) < int(m.Threshold) { - return fmt.Errorf("minimum number of signatures not set, have %d, expected %d", bitarray.NumTrueBitsBefore(size), int(m.Threshold)) + return fmt.Errorf("not enough signatures set, have %d, expected %d", bitarray.NumTrueBitsBefore(size), int(m.Threshold)) } // index in the list of signatures which we are concerned with. sigIndex := 0 diff --git a/crypto/keys/multisig/multisig_test.go b/crypto/keys/multisig/multisig_test.go index 2b91e74eb..69b4f10d2 100644 --- a/crypto/keys/multisig/multisig_test.go +++ b/crypto/keys/multisig/multisig_test.go @@ -15,6 +15,15 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" ) +func TestNewMultiSig(t *testing.T) { + require := require.New(t) + pk1 := secp256k1.GenPrivKey().PubKey() + pks := []cryptotypes.PubKey{pk1, pk1} + + require.NotNil(kmultisig.NewLegacyAminoPubKey(1, pks), + "Should support not unique public keys") +} + func TestAddress(t *testing.T) { msg := []byte{1, 2, 3, 4} pubKeys, _ := generatePubKeysAndSignatures(5, msg) @@ -85,21 +94,20 @@ func TestVerifyMultisignature(t *testing.T) { testCases := []struct { msg string - malleate func() + malleate func(*require.Assertions) expectPass bool }{ { "nested multisignature", - func() { + func(require *require.Assertions) { genPk, genSig := generateNestedMultiSignature(3, msg) sig = genSig pk = genPk }, true, - }, - { + }, { "wrong size for sig bit array", - func() { + func(require *require.Assertions) { pubKeys, _ := generatePubKeysAndSignatures(3, msg) pk = kmultisig.NewLegacyAminoPubKey(3, pubKeys) sig = multisig.NewMultisig(1) @@ -108,7 +116,7 @@ func TestVerifyMultisignature(t *testing.T) { }, { "single signature data, expects the first k signatures to be valid", - func() { + func(require *require.Assertions) { k := 2 signingIndices := []int{0, 3, 1} pubKeys, sigs := generatePubKeysAndSignatures(5, msg) @@ -119,32 +127,26 @@ func TestVerifyMultisignature(t *testing.T) { for i := 0; i < k-1; i++ { signingIndex := signingIndices[i] require.NoError( - t, multisig.AddSignatureFromPubKey(sig, sigs[signingIndex], pubKeys[signingIndex], pubKeys), ) require.Error( - t, pk.VerifyMultisignature(signBytesFn, sig), "multisig passed when i < k, i %d", i, ) require.NoError( - t, multisig.AddSignatureFromPubKey(sig, sigs[signingIndex], pubKeys[signingIndex], pubKeys), ) require.Equal( - t, i+1, len(sig.Signatures), "adding a signature for the same pubkey twice increased signature count by 2, index %d", i, ) } require.Error( - t, pk.VerifyMultisignature(signBytesFn, sig), "multisig passed with k - 1 sigs", ) require.NoError( - t, multisig.AddSignatureFromPubKey( sig, sigs[signingIndices[k]], @@ -153,30 +155,50 @@ func TestVerifyMultisignature(t *testing.T) { ), ) require.NoError( - t, pk.VerifyMultisignature(signBytesFn, sig), "multisig failed after k good signatures", ) }, true, - }, - { + }, { "duplicate signatures", - func() { + func(require *require.Assertions) { pubKeys, sigs := generatePubKeysAndSignatures(5, msg) pk = kmultisig.NewLegacyAminoPubKey(2, pubKeys) sig = multisig.NewMultisig(5) - require.Error(t, pk.VerifyMultisignature(signBytesFn, sig)) + require.Error(pk.VerifyMultisignature(signBytesFn, sig)) multisig.AddSignatureFromPubKey(sig, sigs[0], pubKeys[0], pubKeys) // Add second signature manually sig.Signatures = append(sig.Signatures, sigs[0]) }, false, - }, - { + }, { + "duplicated key", + func(require *require.Assertions) { + // here we test an edge case where we create a multi sig with two same + // keys. It should work. + pubkeys, sigs := generatePubKeysAndSignatures(3, msg) + pubkeys[1] = pubkeys[0] + pk = kmultisig.NewLegacyAminoPubKey(2, pubkeys) + sig = multisig.NewMultisig(len(pubkeys)) + multisig.AddSignature(sig, sigs[0], 0) + multisig.AddSignature(sig, sigs[0], 1) + }, + true, + }, { + "same key used twice", + func(require *require.Assertions) { + pubkeys, sigs := generatePubKeysAndSignatures(3, msg) + pk = kmultisig.NewLegacyAminoPubKey(2, pubkeys) + sig = multisig.NewMultisig(len(pubkeys)) + multisig.AddSignature(sig, sigs[0], 0) + multisig.AddSignature(sig, sigs[0], 1) + }, + false, + }, { "unable to verify signature", - func() { + func(require *require.Assertions) { pubKeys, _ := generatePubKeysAndSignatures(2, msg) _, sigs := generatePubKeysAndSignatures(2, msg) pk = kmultisig.NewLegacyAminoPubKey(2, pubKeys) @@ -190,7 +212,7 @@ func TestVerifyMultisignature(t *testing.T) { for _, tc := range testCases { t.Run(tc.msg, func(t *testing.T) { - tc.malleate() + tc.malleate(require.New(t)) err := pk.VerifyMultisignature(signBytesFn, sig) if tc.expectPass { require.NoError(t, err) diff --git a/crypto/keys/secp256k1/secp256k1.go b/crypto/keys/secp256k1/secp256k1.go index eebe72a45..231a895a6 100644 --- a/crypto/keys/secp256k1/secp256k1.go +++ b/crypto/keys/secp256k1/secp256k1.go @@ -151,12 +151,9 @@ func (pubKey *PubKey) Address() crypto.Address { panic("length of pubkey is incorrect") } - hasherSHA256 := sha256.New() - hasherSHA256.Write(pubKey.Key) // does not error - sha := hasherSHA256.Sum(nil) - + sha := sha256.Sum256(pubKey.Key) hasherRIPEMD160 := ripemd160.New() - hasherRIPEMD160.Write(sha) // does not error + hasherRIPEMD160.Write(sha[:]) // does not error return crypto.Address(hasherRIPEMD160.Sum(nil)) } diff --git a/crypto/types/compact_bit_array.go b/crypto/types/compact_bit_array.go index 7c7188f6a..2d81636f4 100644 --- a/crypto/types/compact_bit_array.go +++ b/crypto/types/compact_bit_array.go @@ -30,14 +30,14 @@ func NewCompactBitArray(bits int) *CompactBitArray { func (bA *CompactBitArray) Count() int { if bA == nil { return 0 - } else if bA.ExtraBitsStored == uint32(0) { + } else if bA.ExtraBitsStored == 0 { return len(bA.Elems) * 8 } return (len(bA.Elems)-1)*8 + int(bA.ExtraBitsStored) } -// GetIndex returns the bit at index i within the bit array. +// GetIndex returns true if the bit at index i is set; returns false otherwise. // The behavior is undefined if i >= bA.Count() func (bA *CompactBitArray) GetIndex(i int) bool { if bA == nil { @@ -47,11 +47,11 @@ func (bA *CompactBitArray) GetIndex(i int) bool { return false } - return bA.Elems[i>>3]&(uint8(1)< 0 + return bA.Elems[i>>3]&(1< 0 } -// SetIndex sets the bit at index i within the bit array. -// The behavior is undefined if i >= bA.Count() +// SetIndex sets the bit at index i within the bit array. Returns true if and only if the +// operation succeeded. The behavior is undefined if i >= bA.Count() func (bA *CompactBitArray) SetIndex(i int, v bool) bool { if bA == nil { return false @@ -62,9 +62,9 @@ func (bA *CompactBitArray) SetIndex(i int, v bool) bool { } if v { - bA.Elems[i>>3] |= (uint8(1) << uint8(7-(i%8))) + bA.Elems[i>>3] |= (1 << uint8(7-(i%8))) } else { - bA.Elems[i>>3] &= ^(uint8(1) << uint8(7-(i%8))) + bA.Elems[i>>3] &= ^(1 << uint8(7-(i%8))) } return true @@ -75,13 +75,23 @@ func (bA *CompactBitArray) SetIndex(i int, v bool) bool { // there are two bits set to true before index 4. func (bA *CompactBitArray) NumTrueBitsBefore(index int) int { numTrueValues := 0 - for i := 0; i < index; i++ { - if bA.GetIndex(i) { - numTrueValues++ + max := bA.Count() + if index > max { + index = max + } + // below we iterate over the bytes then over bits (in low endian) and count bits set to 1 + var i = 0 + for elem := 0; ; elem++ { + for b := 7; b >= 0; b-- { + if i >= index { + return numTrueValues + } + i++ + if (bA.Elems[elem]>>b)&1 == 1 { + numTrueValues++ + } } } - - return numTrueValues } // Copy returns a copy of the provided bit array. @@ -99,6 +109,18 @@ func (bA *CompactBitArray) Copy() *CompactBitArray { } } +// Equal checks if both bit arrays are equal. If both arrays are nil then it returns true. +func (bA *CompactBitArray) Equal(other *CompactBitArray) bool { + if bA == other { + return true + } + if bA == nil || other == nil { + return false + } + return bA.ExtraBitsStored == other.ExtraBitsStored && + bytes.Equal(bA.Elems, other.Elems) +} + // String returns a string representation of CompactBitArray: BA{}, // where is a sequence of 'x' (1) and '_' (0). // The includes spaces and newlines to help people. diff --git a/crypto/types/compact_bit_array_test.go b/crypto/types/compact_bit_array_test.go index 44408e43a..be4b87a23 100644 --- a/crypto/types/compact_bit_array_test.go +++ b/crypto/types/compact_bit_array_test.go @@ -36,6 +36,34 @@ func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) { } } +func TestBitArrayEqual(t *testing.T) { + empty := new(CompactBitArray) + big1, _ := randCompactBitArray(1000) + big1Cpy := *big1 + big2, _ := randCompactBitArray(1000) + big2.SetIndex(500, !big1.GetIndex(500)) // ensure they are different + cases := []struct { + name string + b1 *CompactBitArray + b2 *CompactBitArray + eq bool + }{ + {name: "both nil are equal", b1: nil, b2: nil, eq: true}, + {name: "if one is nil then not equal", b1: nil, b2: empty, eq: false}, + {name: "nil and empty not equal", b1: empty, b2: nil, eq: false}, + {name: "empty and empty equal", b1: empty, b2: new(CompactBitArray), eq: true}, + {name: "same bits should be equal", b1: big1, b2: &big1Cpy, eq: true}, + {name: "different should not be equal", b1: big1, b2: big2, eq: false}, + } + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + eq := tc.b1.Equal(tc.b2) + require.Equal(t, tc.eq, eq) + }) + } +} + func TestJSONMarshalUnmarshal(t *testing.T) { bA1 := NewCompactBitArray(0) @@ -200,3 +228,13 @@ func TestCompactBitArrayGetSetIndex(t *testing.T) { } } } + +func BenchmarkNumTrueBitsBefore(b *testing.B) { + ba, _ := randCompactBitArray(100) + + b.Run("new", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ba.NumTrueBitsBefore(90) + } + }) +} diff --git a/crypto/types/multisig/multisignature.go b/crypto/types/multisig/multisignature.go index 20e3ef112..362fa4693 100644 --- a/crypto/types/multisig/multisignature.go +++ b/crypto/types/multisig/multisignature.go @@ -34,7 +34,8 @@ func getIndex(pk types.PubKey, keys []types.PubKey) int { return -1 } -// AddSignature adds a signature to the multisig, at the corresponding index. +// AddSignature adds a signature to the multisig, at the corresponding index. The index must +// represent the pubkey index in the LegacyAmingPubKey structure, which verifies this signature. // If the signature already exists, replace it. func AddSignature(mSig *signing.MultiSignatureData, sig signing.SignatureData, index int) { newSigIndex := mSig.BitArray.NumTrueBitsBefore(index) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index ce9454271..59e33da23 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -42,6 +42,10 @@ module.exports = { "label": "v0.39", "key": "v0.39" }, + { + "label": "v0.41", + "key": "v0.41" + }, { "label": "master", "key": "master" diff --git a/docs/architecture/README.md b/docs/architecture/README.md index da4f77898..f62d50891 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -72,6 +72,7 @@ Read about the [PROCESS](./PROCESS.md). - [ADR 027: Deterministic Protobuf Serialization](./adr-027-deterministic-protobuf-serialization.md) - [ADR 028: Public Key Addresses](./adr-028-public-key-addresses.md) - [ADR 032: Typed Events](./adr-032-typed-events.md) +- [ADR 033: Inter-module RPC](./adr-033-protobuf-inter-module-comm.md) - [ADR 035: Rosetta API Support](./adr-035-rosetta-api-support.md) - [ADR 037: Governance Split Votes](./adr-037-gov-split-vote.md) - [ADR 038: State Listening](./adr-038-state-listening.md) diff --git a/docs/architecture/adr-019-protobuf-state-encoding.md b/docs/architecture/adr-019-protobuf-state-encoding.md index 2a4d8b046..e69621e66 100644 --- a/docs/architecture/adr-019-protobuf-state-encoding.md +++ b/docs/architecture/adr-019-protobuf-state-encoding.md @@ -7,6 +7,7 @@ - 2020 Apr 27: Convert usages of `oneof` for interfaces to `Any` - 2020 May 15: Describe `cosmos_proto` extensions and amino compatibility - 2020 Dec 4: Move and rename `MarshalAny` and `UnmarshalAny` into the `codec.Marshaler` interface. +- 2021 Feb 24: Remove mentions of `HybridCodec`, which has been abandoned in [#6843](https://github.com/cosmos/cosmos-sdk/pull/6843). ## Status @@ -59,24 +60,26 @@ We will adopt [Protocol Buffers](https://developers.google.com/protocol-buffers) persisted structured data in the Cosmos SDK while providing a clean mechanism and developer UX for applications wishing to continue to use Amino. We will provide this mechanism by updating modules to accept a codec interface, `Marshaler`, instead of a concrete Amino codec. Furthermore, the Cosmos SDK -will provide three concrete implementations of the `Marshaler` interface: `AminoCodec`, `ProtoCodec`, -and `HybridCodec`. +will provide two concrete implementations of the `Marshaler` interface: `AminoCodec` and `ProtoCodec`. - `AminoCodec`: Uses Amino for both binary and JSON encoding. -- `ProtoCodec`: Uses Protobuf for or both binary and JSON encoding. -- `HybridCodec`: Uses Amino for JSON encoding and Protobuf for binary encoding. +- `ProtoCodec`: Uses Protobuf for both binary and JSON encoding. -Until the client migration landscape is fully understood and designed, modules will use a `HybridCodec` -as the concrete codec it accepts and/or extends. This means that all client JSON encoding, including -genesis state, will still use Amino. The ultimate goal will be to replace Amino JSON encoding with -Protbuf encoding and thus have modules accept and/or extend `ProtoCodec`. +Modules will use whichever codec that is instantiated in the app. By default, the SDK's `simapp` +instantiates a `ProtoCodec` as the concrete implementation of `Marshaler`, inside the `MakeTestEncodingConfig` +function. This can be easily overwritten by app developers if they so desire. + +The ultimate goal will be to replace Amino JSON encoding with Protobuf encoding and thus have +modules accept and/or extend `ProtoCodec`. Until then, Amino JSON is still provided for legacy use-cases. +A handful of places in the SDK still have Amino JSON hardcoded, such as the Legacy API REST endpoints +and the `x/params` store. They are planned to be converted to Protobuf in a gradual manner. ### Module Codecs Modules that do not require the ability to work with and serialize interfaces, the path to Protobuf migration is pretty straightforward. These modules are to simply migrate any existing types that are encoded and persisted via their concrete Amino codec to Protobuf and have their keeper accept a -`Marshaler` that will be a `HybridCodec`. This migration is simple as things will just work as-is. +`Marshaler` that will be a `ProtoCodec`. This migration is simple as things will just work as-is. Note, any business logic that needs to encode primitive types like `bool` or `int64` should use [gogoprotobuf](https://github.com/gogo/protobuf) Value types. @@ -207,7 +210,7 @@ Note that `InterfaceRegistry` usage does not deviate from standard protobuf usage of `Any`, it just introduces a security and introspection layer for golang usage. -`InterfaceRegistry` will be a member of `ProtoCodec` and `HybridCodec` as +`InterfaceRegistry` will be a member of `ProtoCodec` described above. In order for modules to register interface types, app modules can optionally implement the following interface: diff --git a/docs/architecture/adr-020-protobuf-transaction-encoding.md b/docs/architecture/adr-020-protobuf-transaction-encoding.md index 0c050c5d1..062dac13e 100644 --- a/docs/architecture/adr-020-protobuf-transaction-encoding.md +++ b/docs/architecture/adr-020-protobuf-transaction-encoding.md @@ -12,6 +12,7 @@ - 2020 August 19: Move sequence field from `SignDoc` to `SignerInfo`, as discussed in [#6966](https://github.com/cosmos/cosmos-sdk/issues/6966). - 2020 September 25: Remove `PublicKey` type in favor of `secp256k1.PubKey`, `ed25519.PubKey` and `multisig.LegacyAminoPubKey`. - 2020 October 15: Add `GetAccount` and `GetAccountWithHeight` methods to the `AccountRetriever` interface. +- 2021 Feb 24: The SDK does not use Tendermint's `PubKey` interface anymore, but its own `cryptotypes.PubKey`. Updates to reflect this. ## Status @@ -286,7 +287,7 @@ and `FileDescriptor`s and returns a boolean result. ### Public Key Encoding -Public keys in the Cosmos SDK implement Tendermint's `crypto.PubKey` interface. +Public keys in the Cosmos SDK implement the `cryptotypes.PubKey` interface. We propose to use `Any` for protobuf encoding as we are doing with other interfaces (e.g. in `BaseAccount` `PubKey` or `SignerInfo` `PublicKey`). Following public keys are implemented: secp256k1, ed25519 and multisignature. diff --git a/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md index 424f0415f..66eeaa13e 100644 --- a/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md +++ b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md @@ -44,7 +44,7 @@ We elect not to deal with chains which have actually halted, which is necessaril 1. Add a new governance proposal type, `ClientUpdateProposal`, in the `x/ibc` module 1. Extend the base `Proposal` with two client identifiers (`string`) and an initial height ('exported.Height'). 1. The first client identifier is the proposed client to be updated. This client must be either frozen or expired. - 1. The second client is a substitute client. It carries all the state for the client which may be updated. It must have identitical client and chain parameters to the client which may be updated (except for latest height and frozen height). It should be continually updated during the voting period. + 1. The second client is a substitute client. It carries all the state for the client which may be updated. It must have identitical client and chain parameters to the client which may be updated (except for latest height, frozen height, and chain-id). It should be continually updated during the voting period. 1. The initial height represents the starting height consensus states which will be copied from the substitute client to the frozen/expired client. 1. If this governance proposal passes, the client on trial will be updated with all the state of the substitute, if and only if: 1. `allow_governance_override_after_expiry` is true and the client has expired (`Expired()` returns true) diff --git a/docs/architecture/adr-030-authz-module.md b/docs/architecture/adr-030-authz-module.md index c61940fce..2a30167b9 100644 --- a/docs/architecture/adr-030-authz-module.md +++ b/docs/architecture/adr-030-authz-module.md @@ -4,7 +4,7 @@ - 2019-11-06: Initial Draft - 2020-10-12: Updated Draft -- 2021-11-13: Accepted +- 2020-11-13: Accepted ## Status @@ -55,7 +55,7 @@ type Authorization interface { MethodName() string // Accept determines whether this grant permits the provided sdk.ServiceMsg to be performed, and if - // so provides an upgraded authorization instance. + // so provides an upgraded authorization instance. // Returns: // + allow: true if msg is authorized // + updated: new Authorization instance which should overwrite the current one with new state @@ -146,7 +146,7 @@ to the router based on `Authorization` grants: ```go type Keeper interface { // DispatchActions routes the provided msgs to their respective handlers if the grantee was granted an authorization - // to send those messages by the first (and only) signer of each msg. + // to send those messages by the first (and only) signer of each msg. DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, msgs []sdk.ServiceMsg) sdk.Result` } ``` @@ -216,7 +216,7 @@ message GenericAuthorization { - Users will be able to authorize arbitrary actions on behalf of their accounts to other users, improving key management for many use cases - The solution is more generic than previously considered approaches and the -`Authorization` interface approach can be extended to cover other use cases by +`Authorization` interface approach can be extended to cover other use cases by SDK users ### Negative diff --git a/docs/architecture/adr-033-protobuf-inter-module-comm.md b/docs/architecture/adr-033-protobuf-inter-module-comm.md new file mode 100644 index 000000000..584ee2382 --- /dev/null +++ b/docs/architecture/adr-033-protobuf-inter-module-comm.md @@ -0,0 +1,395 @@ +# ADR 033: Protobuf-based Inter-Module Communication + +## Changelog + +- 2020-10-05: Initial Draft + +## Status + +Proposed + +## Abstract + +This ADR introduces a system for permissioned inter-module communication leveraging the protobuf `Query` and `Msg` +service definitions defined in [ADR 021](./adr-021-protobuf-query-encoding.md) and +[ADR 031](./adr-031-msg-service.md) which provides: +- stable protobuf based module interfaces to potentially later replace the keeper paradigm +- stronger inter-module object capabilities (OCAPs) guarantees +- module accounts and sub-account authorization + +## Context + +In the current Cosmos SDK documentation on the [Object-Capability Model](../docs/core/ocap.md), it is stated that: + +> We assume that a thriving ecosystem of Cosmos-SDK modules that are easy to compose into a blockchain application will contain faulty or malicious modules. + +There is currently not a thriving ecosystem of Cosmos SDK modules. We hypothesize that this is in part due to: +1. lack of a stable v1.0 Cosmos SDK to build modules off of. Module interfaces are changing, sometimes dramatically, from +point release to point release, often for good reasons, but this does not create a stable foundation to build on. +2. lack of a properly implemented object capability or even object-oriented encapsulation system which makes refactors +of module keeper interfaces inevitable because the current interfaces are poorly constrained. + +### `x/bank` Case Study + +Currently the `x/bank` keeper gives pretty much unrestricted access to any module which references it. For instance, the +`SetBalance` method allows the caller to set the balance of any account to anything, bypassing even proper tracking of supply. + +There appears to have been some later attempts to implement some semblance of OCAPs using module-level minting, staking +and burning permissions. These permissions allow a module to mint, burn or delegate tokens with reference to the module’s +own account. These permissions are actually stored as a `[]string` array on the `ModuleAccount` type in state. + +However, these permissions don’t really do much. They control what modules can be referenced in the `MintCoins`, +`BurnCoins` and `DelegateCoins***` methods, but for one there is no unique object capability token that controls access — +just a simple string. So the `x/upgrade` module could mint tokens for the `x/staking` module simple by calling +`MintCoins(“staking”)`. Furthermore, all modules which have access to these keeper methods, also have access to +`SetBalance` negating any other attempt at OCAPs and breaking even basic object-oriented encapsulation. + +## Decision + +Based on [ADR-021](./adr-021-protobuf-query-encoding.md) and [ADR-031](./adr-031-msg-service.md), we introduce the +Inter-Module Communication framework for secure module authorization and OCAPs. +When implemented, this could also serve as an alternative to the existing paradigm of passing keepers between +modules. The approach outlined here-in is intended to form the basis of a Cosmos SDK v1.0 that provides the necessary +stability and encapsulation guarantees that allow a thriving module ecosystem to emerge. + +Of particular note — the decision is to _enable_ this functionality for modules to adopt at their own discretion. +Proposals to migrate existing modules to this new paradigm will have to be a separate conversation, potentially +addressed as amendments to this ADR. + +### New "Keeper" Paradigm + +In [ADR 021](./adr-021-protobuf-query-encoding.md), a mechanism for using protobuf service definitions to define queriers +was introduced and in [ADR 31](./adr-031-msg-service.md), a mechanism for using protobuf service to define `Msg`s was added. +Protobuf service definitions generate two golang interfaces representing the client and server sides of a service plus +some helper code. Here is a minimal example for the bank `cosmos.bank.Msg/Send` message type: + +```go +package bank + +type MsgClient interface { + Send(context.Context, *MsgSend, opts ...grpc.CallOption) (*MsgSendResponse, error) +} + +type MsgServer interface { + Send(context.Context, *MsgSend) (*MsgSendResponse, error) +} +``` + +[ADR 021](./adr-021-protobuf-query-encoding.md) and [ADR 31](./adr-031-msg-service.md) specifies how modules can implement the generated `QueryServer` +and `MsgServer` interfaces as replacements for the legacy queriers and `Msg` handlers respectively. + +In this ADR we explain how modules can make queries and send `Msg`s to other modules using the generated `QueryClient` +and `MsgClient` interfaces and propose this mechanism as a replacement for the existing `Keeper` paradigm. To be clear, +this ADR does not necessitate the creation of new protobuf definitions or services. Rather, it leverages the same proto +based service interfaces already used by clients for inter-module communication. + +Using this `QueryClient`/`MsgClient` approach has the following key benefits over exposing keepers to external modules: +1. Protobuf types are checked for breaking changes using [buf](https://buf.build/docs/breaking-overview) and because of +the way protobuf is designed this will give us strong backwards compatibility guarantees while allowing for forward +evolution. +2. The separation between the client and server interfaces will allow us to insert permission checking code in between +the two which checks if one module is authorized to send the specified `Msg` to the other module providing a proper +object capability system (see below). +3. The router for inter-module communication gives us a convenient place to handle rollback of transactions, +enabling atomicy of operations ([currently a problem](https://github.com/cosmos/cosmos-sdk/issues/8030)). Any failure within a module-to-module call would result in a failure of the entire +transaction + +This mechanism has the added benefits of: +- reducing boilerplate through code generation, and +- allowing for modules in other languages either via a VM like CosmWasm or sub-processes using gRPC + +### Inter-module Communication + +To use the `Client` generated by the protobuf compiler we need a `grpc.ClientConn` [interface](https://github.com/regen-network/protobuf/blob/cosmos/grpc/types.go#L12) +implementation. For this we introduce +a new type, `ModuleKey`, which implements the `grpc.ClientConn` interface. `ModuleKey` can be thought of as the "private +key" corresponding to a module account, where authentication is provided through use of a special `Invoker()` function, +described in more detail below. + +Blockchain users (external clients) use their account's private key to sign transactions containing `Msg`s where they are listed as signers (each +message specifies required signers with `Msg.GetSigner`). The authentication checks is performed by `AnteHandler`. + +Here, we extend this process, by allowing modules to be identified in `Msg.GetSigners`. When a module wants to trigger the execution a `Msg` in another module, +its `ModuleKey` acts as the sender (through the `ClientConn` interface we describe below) and is set as a sole "signer". It's worth to note +that we don't use any cryptographic signature in this case. +For example, module `A` could use its `A.ModuleKey` to create `MsgSend` object for `/cosmos.bank.Msg/Send` transaction. `MsgSend` validation +will assure that the `from` account (`A.ModuleKey` in this case) is the signer. + +Here's an example of a hypothetical module `foo` interacting with `x/bank`: +```go +package foo + + +type FooMsgServer { + // ... + + bankQuery bank.QueryClient + bankMsg bank.MsgClient +} + +func NewFooMsgServer(moduleKey RootModuleKey, ...) FooMsgServer { + // ... + + return FooMsgServer { + // ... + modouleKey: moduleKey, + bankQuery: bank.NewQueryClient(moduleKey), + bankMsg: bank.NewMsgClient(moduleKey), + } +} + +func (foo *FooMsgServer) Bar(ctx context.Context, req *MsgBarRequest) (*MsgBarResponse, error) { + balance, err := foo.bankQuery.Balance(&bank.QueryBalanceRequest{Address: fooMsgServer.moduleKey.Address(), Denom: "foo"}) + + ... + + res, err := foo.bankMsg.Send(ctx, &bank.MsgSendRequest{FromAddress: fooMsgServer.moduleKey.Address(), ...}) + + ... +} +``` + +This design is also intended to be extensible to cover use cases of more fine grained permissioning like minting by +denom prefix being restricted to certain modules (as discussed in +[#7459](https://github.com/cosmos/cosmos-sdk/pull/7459#discussion_r529545528)). + +### `ModuleKey`s and `ModuleID`s + +A `ModuleKey` can be thought of as a "private key" for a module account and a `ModuleID` can be thought of as the +corresponding "public key". From the [ADR 028](./adr-028-public-key-addresses.md), modules can have both a root module account and any number of sub-accounts +or derived accounts that can be used for different pools (ex. staking pools) or managed accounts (ex. group +accounts). We can also think of module sub-accounts as similar to derived keys - there is a root key and then some +derivation path. `ModuleID` is a simple struct which contains the module name and optional "derivation" path, +and forms its address based on the `AddressHash` method from [the ADR-028](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-028-public-key-addresses.md): + +```go +type ModuleID struct { + ModuleName string + Path []byte +} + +func (key ModuleID) Address() []byte { + return AddressHash(key.ModuleName, key.Path) +} +``` + +In addition to being able to generate a `ModuleID` and address, a `ModuleKey` contains a special function called +`Invoker` which is the key to safe inter-module access. The `Invoker` creates an `InvokeFn` closure which is used as an `Invoke` method in +the `grpc.ClientConn` interface and under the hood is able to route messages to the appropriate `Msg` and `Query` handlers +performing appropriate security checks on `Msg`s. This allows for even safer inter-module access than keeper's whose +private member variables could be manipulated through reflection. Golang does not support reflection on a function +closure's captured variables and direct manipulation of memory would be needed for a truly malicious module to bypass +the `ModuleKey` security. + +The two `ModuleKey` types are `RootModuleKey` and `DerivedModuleKey`: + +```go +type Invoker func(callInfo CallInfo) func(ctx context.Context, request, response interface{}, opts ...interface{}) error + +type CallInfo { + Method string + Caller ModuleID +} + +type RootModuleKey struct { + moduleName string + invoker Invoker +} + +func (rm RootModuleKey) Derive(path []byte) DerivedModuleKey { /* ... */} + +type DerivedModuleKey struct { + moduleName string + path []byte + invoker Invoker +} +``` + +A module can get access to a `DerivedModuleKey`, using the `Derive(path []byte)` method on `RootModuleKey` and then +would use this key to authenticate `Msg`s from a sub-account. Ex: + +```go +package foo + +func (fooMsgServer *MsgServer) Bar(ctx context.Context, req *MsgBar) (*MsgBarResponse, error) { + derivedKey := fooMsgServer.moduleKey.Derive(req.SomePath) + bankMsgClient := bank.NewMsgClient(derivedKey) + res, err := bankMsgClient.Balance(ctx, &bank.MsgSend{FromAddress: derivedKey.Address(), ...}) + ... +} +``` + +In this way, a module can gain permissioned access to a root account and any number of sub-accounts and send +authenticated `Msg`s from these accounts. The `Invoker` `callInfo.Caller` parameter is used under the hood to +distinguish between different module accounts, but either way the function returned by `Invoker` only allows `Msg`s +from either the root or a derived module account to pass through. + +Note that `Invoker` itself returns a function closure based on the `CallInfo` passed in. This will allow client implementations +in the future that cache the invoke function for each method type avoiding the overhead of hash table lookup. +This would reduce the performance overhead of this inter-module communication method to the bare minimum required for +checking permissions. + +To re-iterate, the closure only allows access to authorized calls. There is no access to anything else regardless of any +name impersonation. + +Below is a rough sketch of the implementation of `grpc.ClientConn.Invoke` for `RootModuleKey`: + +```go +func (key RootModuleKey) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...grpc.CallOption) error { + f := key.invoker(CallInfo {Method: method, Caller: ModuleID {ModuleName: key.moduleName}}) + return f(ctx, args, reply) +} +``` + +### `AppModule` Wiring and Requirements + +In [ADR 031](./adr-031-msg-service.md), the `AppModule.RegisterService(Configurator)` method was introduced. To support +inter-module communication, we extend the `Configurator` interface to pass in the `ModuleKey` and to allow modules to +specify their dependencies on other modules using `RequireServer()`: + + +```go +type Configurator interface { + MsgServer() grpc.Server + QueryServer() grpc.Server + + ModuleKey() ModuleKey + RequireServer(msgServer interface{}) +} +``` + +The `ModuleKey` is passed to modules in the `RegisterService` method itself so that `RegisterServices` serves as a single +entry point for configuring module services. This is intended to also have the side-effect of greatly reducing boilerplate in +`app.go`. For now, `ModuleKey`s will be created based on `AppModuleBasic.Name()`, but a more flexible system may be +introduced in the future. The `ModuleManager` will handle creation of module accounts behind the scenes. + +Because modules do not get direct access to each other anymore, modules may have unfulfilled dependencies. To make sure +that module dependencies are resolved at startup, the `Configurator.RequireServer` method should be added. The `ModuleManager` +will make sure that all dependencies declared with `RequireServer` can be resolved before the app starts. An example +module `foo` could declare it's dependency on `x/bank` like this: + +```go +package foo + +func (am AppModule) RegisterServices(cfg Configurator) { + cfg.RequireServer((*bank.QueryServer)(nil)) + cfg.RequireServer((*bank.MsgServer)(nil)) +} +``` + +### Security Considerations + +In addition to checking for `ModuleKey` permissions, a few additional security precautions will need to be taken by +the underlying router infrastructure. + +#### Recursion and Re-entry + +Recursive or re-entrant method invocations pose a potential security threat. This can be a problem if Module A +calls Module B and Module B calls module A again in the same call. + +One basic way for the router system to deal with this is to maintain a call stack which prevents a module from +being referenced more than once in the call stack so that there is no re-entry. A `map[string]interface{}` table +in the router could be used to perform this security check. + +#### Queries + +Queries in Cosmos SDK are generally un-permissioned so allowing one module to query another module should not pose +any major security threats assuming basic precautions are taken. The basic precaution that the router system will +need to take is making sure that the `sdk.Context` passed to query methods does not allow writing to the store. This +can be done for now with a `CacheMultiStore` as is currently done for `BaseApp` queries. + +### Internal Methods + +In many cases, we may wish for modules to call methods on other modules which are not exposed to clients at all. For this +purpose, we add the `InternalServer` method to `Configurator`: + +```go +type Configurator interface { + MsgServer() grpc.Server + QueryServer() grpc.Server + InternalServer() grpc.Server +} +``` + +As an example, x/slashing's Slash must call x/staking's Slash, but we don't want to expose x/staking's Slash to end users +and clients. + +Internal protobuf services will be defined in a corresponding `internal.proto` file in the given module's +proto package. + +Services registered against `InternalServer` will be callable from other modules but not by external clients. + +An alternative solution to internal-only methods could involve hooks / plugins as discussed [here](https://github.com/cosmos/cosmos-sdk/pull/7459#issuecomment-733807753). +A more detailed evaluation of a hooks / plugin system will be addressed later in follow-ups to this ADR or as a separate +ADR. + +### Authorization + +By default, the inter-module router requires that messages are sent by the first signer returned by `GetSigners`. The +inter-module router should also accept authorization middleware such as that provided by [ADR 030](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-030-authz-module.md). +This middleware will allow accounts to otherwise specific module accounts to perform actions on their behalf. +Authorization middleware should take into account the need to grant certain modules effectively "admin" privileges to +other modules. This will be addressed in separate ADRs or updates to this ADR. + +### Future Work + +Other future improvements may include: +* custom code generation that: + * simplifies interfaces (ex. generates code with `sdk.Context` instead of `context.Context`) + * optimizes inter-module calls - for instance caching resolved methods after first invocation +* combining `StoreKey`s and `ModuleKey`s into a single interface so that modules have a single OCAPs handle +* code generation which makes inter-module communication more performant +* decoupling `ModuleKey` creation from `AppModuleBasic.Name()` so that app's can override root module account names +* inter-module hooks and plugins + +## Alternatives + +### MsgServices vs `x/capability` + +The `x/capability` module does provide a proper object-capability implementation that can be used by any module in the +SDK and could even be used for inter-module OCAPs as described in [\#5931](https://github.com/cosmos/cosmos-sdk/issues/5931). + +The advantages of the approach described in this ADR are mostly around how it integrates with other parts of the SDK, +specifically: + +* protobuf so that: + * code generation of interfaces can be leveraged for a better dev UX + * module interfaces are versioned and checked for breakage using [buf](https://docs.buf.build/breaking-overview) +* sub-module accounts as per ADR 028 +* the general `Msg` passing paradigm and the way signers are specified by `GetSigners` + +Also, this is a complete replacement for keepers and could be applied to _all_ inter-module communication whereas the +`x/capability` approach in #5931 would need to be applied method by method. + +## Consequences + +### Backwards Compatibility + +This ADR is intended to provide a pathway to a scenario where there is greater long term compatibility between modules. +In the short-term, this will likely result in breaking certain `Keeper` interfaces which are too permissive and/or +replacing `Keeper` interfaces altogether. + +### Positive + +- an alternative to keepers which can more easily lead to stable inter-module interfaces +- proper inter-module OCAPs +- improved module developer DevX, as commented on by several particpants on + [Architecture Review Call, Dec 3](https://hackmd.io/E0wxxOvRQ5qVmTf6N_k84Q) +- lays the groundwork for what can be a greatly simplified `app.go` +- router can be setup to enforce atomic transactions for moule-to-module calls + +### Negative + +- modules which adopt this will need significant refactoring + +### Neutral + +## Test Cases [optional] + +## References + +- [ADR 021](./adr-021-protobuf-query-encoding.md) +- [ADR 031](./adr-031-msg-service.md) +- [ADR 028](./adr-028-public-key-addresses.md) +- [ADR 030 draft](https://github.com/cosmos/cosmos-sdk/pull/7105) +- [Object-Capability Model](../docs/core/ocap.md) diff --git a/docs/architecture/adr-037-gov-split-vote.md b/docs/architecture/adr-037-gov-split-vote.md index af9cd8d6f..3dfa59cca 100644 --- a/docs/architecture/adr-037-gov-split-vote.md +++ b/docs/architecture/adr-037-gov-split-vote.md @@ -6,7 +6,7 @@ ## Status -Proposed +Accepted ## Abstract @@ -35,7 +35,7 @@ type Vote struct { } ``` -And for backwards compatibility, we introduce `MsgWeightedVote` while keeping `MsgVote`. +And for backwards compatibility, we introduce `MsgVoteWeighted` while keeping `MsgVote`. ``` type MsgVote struct { ProposalID int64 @@ -43,14 +43,14 @@ type MsgVote struct { Option Option } -type MsgWeightedVote struct { +type MsgVoteWeighted struct { ProposalID int64 Voter sdk.Address Options []WeightedVoteOption } ``` -The `ValidateBasic` of a `MsgWeightedVote` struct would require that +The `ValidateBasic` of a `MsgVoteWeighted` struct would require that 1. The sum of all the Rates is equal to 1.0 2. No Option is repeated diff --git a/docs/basics/app-anatomy.md b/docs/basics/app-anatomy.md index 225da66c3..b75ecf8e7 100644 --- a/docs/basics/app-anatomy.md +++ b/docs/basics/app-anatomy.md @@ -33,7 +33,7 @@ The blockchain full-node presents itself as a binary, generally suffixed by `-d` Once the main binary is built, the node can be started by running the [`start` command](../core/node.md#start-command). This command function primarily does three things: 1. Create an instance of the state-machine defined in [`app.go`](#core-application-file). -2. Initialize the state-machine with the latest known state, extracted from the `db` stored in the `~/.appd/data` folder. At this point, the state-machine is at height `appBlockHeight`. +2. Initialize the state-machine with the latest known state, extracted from the `db` stored in the `~/.app/data` folder. At this point, the state-machine is at height `appBlockHeight`. 3. Create and start a new Tendermint instance. Among other things, the node will perform a handshake with its peers. It will get the latest `blockHeight` from them, and replay blocks to sync to this height if it is greater than the local `appBlockHeight`. If `appBlockHeight` is `0`, the node is starting from genesis and Tendermint sends an `InitChain` message via the ABCI to the `app`, which triggers the [`InitChainer`](#initchainer). ## Core Application File @@ -77,7 +77,7 @@ Here are the main actions performed by this function: - Mount the stores. - Return the application. -Note that this function only creates an instance of the app, while the actual state is either carried over from the `~/.appd/data` folder if the node is restarted, or generated from the genesis file if the node is started for the first time. +Note that this function only creates an instance of the app, while the actual state is either carried over from the `~/.app/data` folder if the node is restarted, or generated from the genesis file if the node is started for the first time. See an example of application constructor from `simapp`: @@ -237,6 +237,18 @@ See an example of an application's main command-line file from the [nameservice ## Dependencies and Makefile +::: warning +A patch introduced in `go-grpc v1.34.0` made gRPC incompatible with the `gogoproto` library, making some [gRPC queries](https://github.com/cosmos/cosmos-sdk/issues/8426) panic. As such, the SDK requires that `go-grpc <=v1.33.2` is installed in your `go.mod`. + +To make sure that gRPC is working properly, it is **highly recommended** to add the following line in your application's `go.mod`: + +``` +replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 +``` + +Please see [issue #8392](https://github.com/cosmos/cosmos-sdk/issues/8392) for more info. +::: + This section is optional, as developers are free to choose their dependency manager and project building method. That said, the current most used framework for versioning control is [`go.mod`](https://github.com/golang/go/wiki/Modules). It ensures each of the libraries used throughout the application are imported with the correct version. See an example from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice): +++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/go.mod#L1-L18 diff --git a/docs/basics/query-lifecycle.md b/docs/basics/query-lifecycle.md index 4b4c509bb..55902e698 100644 --- a/docs/basics/query-lifecycle.md +++ b/docs/basics/query-lifecycle.md @@ -38,6 +38,18 @@ The CLI understands a specific set of commands, defined in a hierarchical struct ### gRPC +::: warning +A patch introduced in `go-grpc v1.34.0` made gRPC incompatible with the `gogoproto` library, making some [gRPC queries](https://github.com/cosmos/cosmos-sdk/issues/8426) panic. As such, the SDK requires that `go-grpc <=v1.33.2` is installed in your `go.mod`. + +To make sure that gRPC is working properly, it is **highly recommended** to add the following line in your application's `go.mod`: + +``` +replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 +``` + +Please see [issue #8392](https://github.com/cosmos/cosmos-sdk/issues/8392) for more info. +::: + Another interface through which users can make queries, introduced in Cosmos SDK v0.40, is [gRPC](https://grpc.io) requests to a [gRPC server](../core/grpc_rest.md#grpc-server). The endpoints are defined as [Protocol Buffers](https://developers.google.com/protocol-buffers) service methods inside `.proto` files, written in Protobuf's own language-agnostic interface definition language (IDL). The Protobuf ecosystem developed tools for code-generation from `*.proto` files into various languages. These tools allow to build gRPC clients easily. One such tool is [grpcurl](https://github.com/fullstorydev/grpcurl), and a gRPC request for `MyQuery` using this client looks like: diff --git a/docs/cn/basics/app-anatomy.md b/docs/cn/basics/app-anatomy.md index 813e9c6c4..9ac8feb82 100644 --- a/docs/cn/basics/app-anatomy.md +++ b/docs/cn/basics/app-anatomy.md @@ -28,7 +28,7 @@ Blockchain Node | | Consensus | | 1. [`app.go`] 创建了一个状态机实例。 -2. 用最新的已知状态初始化状态机,该状态机是从存储在 `~/.appd/data` 文件夹中的 db 中提取的。 此时,状态机的高度为:`appBlockHeight`。 +2. 用最新的已知状态初始化状态机,该状态机是从存储在 `~/.app/data` 文件夹中的 db 中提取的。 此时,状态机的高度为:`appBlockHeight`。 3. 创建并启动一个新的 Tendermint 实例。 该节点将与对等节点进行连接交换信息。 它将从他们那里获取最新的 `blockHeight`,如果它大于本地的 `appBlockHeight`,则重播块以同步到该高度。 如果 `appBlockHeight` 为 `0`,则该节点从创世开始,并且 Tendermint 通过 ABCI 接口向 `app` 发送 `InitChain` 初始化链命令,从而触发 [`InitChainer`](https://docs.cosmos.network/master/basics/app-anatomy.html#initchainer)。 @@ -73,7 +73,7 @@ Blockchain Node | | Consensus | | - 挂载存储. - 返回应用实例. -请注意,此函数仅创建该应用的一个实例,而如果重新启动节点,则状态将从 `〜/.appd/data` 文件夹中保留下来状态加载,如果节点是第一次启动,则从创世文件生成。See an example of application constructor from [`gaia`](https://github.com/cosmos/gaia): +请注意,此函数仅创建该应用的一个实例,而如果重新启动节点,则状态将从 `〜/.app/data` 文件夹中保留下来状态加载,如果节点是第一次启动,则从创世文件生成。See an example of application constructor from [`gaia`](https://github.com/cosmos/gaia): +++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L110-L222 diff --git a/docs/core/baseapp.md b/docs/core/baseapp.md index 8e36a7363..5d0fd115f 100644 --- a/docs/core/baseapp.md +++ b/docs/core/baseapp.md @@ -286,7 +286,7 @@ Before the first transaction of a given block is processed, a [volatile state](# 1. The `AnteHandler` does **not** check that the transaction's `gas-prices` is sufficient. That is because the `min-gas-prices` value `gas-prices` is checked against is local to the node, and therefore what is enough for one full-node might not be for another. This means that the proposer can potentially include transactions for free, although they are not incentivised to do so, as they earn a bonus on the total fee of the block they propose. 2. For each `Msg` in the transaction, route to the appropriate module's [`Msg` service](../building-modules/msg-services.md). Additional _stateful_ checks are performed, and the branched multistore held in `deliverState`'s `context` is updated by the module's `keeper`. If the `Msg` service returns successfully, the branched multistore held in `context` is written to `deliverState` `CacheMultiStore`. -During step 5., each read/write to the store increases the value of `GasConsumed`. You can find the default cost of each operation: +During the additional fifth step outlined in (2), each read/write to the store increases the value of `GasConsumed`. You can find the default cost of each operation: +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/gas.go#L153-L162 diff --git a/docs/core/cli.md b/docs/core/cli.md index 10203e769..a09b81407 100644 --- a/docs/core/cli.md +++ b/docs/core/cli.md @@ -96,7 +96,7 @@ This `queryCmd` function adds all the queries available to end-users for the app Here is an example of a `queryCmd` aggregating subcommands from the `simapp` application: -+++ https://github.com/cosmos/cosmos-sdk/blob/0.40.0/simapp/simd/cmd/root.go#L99-L121 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L99-L121 ## Flags diff --git a/docs/core/events.md b/docs/core/events.md index 87bf87066..d199d94ce 100644 --- a/docs/core/events.md +++ b/docs/core/events.md @@ -19,7 +19,7 @@ take the form of: `{eventType}.{eventAttribute}={value}`. Events contain: -- A `type`, which is meant to categorize an event at a high-level (e.g. by module or action). +- A `type`, which is meant to categorize an event at a high-level (e.g. by module (e.g. `module=bank`) or action (e.g. `action=/cosmos.bank.v1beta1.Msg/Send`)). - A list of `attributes`, which are key-value pairs that give more information about the categorized `event`. +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/events.go#L51-L56 diff --git a/docs/core/grpc_rest.md b/docs/core/grpc_rest.md index c78da2d94..cea6c0ed3 100644 --- a/docs/core/grpc_rest.md +++ b/docs/core/grpc_rest.md @@ -20,16 +20,28 @@ The node also exposes some other endpoints, such as the Tendermint P2P endpoint, ## gRPC Server +::: warning +A patch introduced in `go-grpc v1.34.0` made gRPC incompatible with the `gogoproto` library, making some [gRPC queries](https://github.com/cosmos/cosmos-sdk/issues/8426) panic. As such, the SDK requires that `go-grpc <=v1.33.2` is installed in your `go.mod`. + +To make sure that gRPC is working properly, it is **highly recommended** to add the following line in your application's `go.mod`: + +``` +replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 +``` + +Please see [issue #8392](https://github.com/cosmos/cosmos-sdk/issues/8392) for more info. +::: + Cosmos SDK v0.40 introduced Protobuf as the main [encoding](./encoding) library, and this brings a wide range of Protobuf-based tools that can be plugged into the SDK. One such tool is [gRPC](https://grpc.io), a modern open source high performance RPC framework that has decent client support in several languages. Each module exposes [`Msg` and `Query` Protobuf services](../building-modules/messages-and-queries.md) to define state transitions and state queries. These services are hooked up to gRPC via the following function inside the application: -https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/server/types/app.go#L39-L41 + The `grpc.Server` is a concrete gRPC server, which spawns and serves any gRPC requests. This server can be configured inside `~/.simapp/config/app.toml`: - `grpc.enable = true|false` field defines if the gRPC server should be enabled. Defaults to `true`. -- `grpc.address = {string}` field defines the address (really, the port, since the host should be kept at `0.0.0.0`) the server should bind to. Defaults to `0.0.0.0:9000`. +- `grpc.address = {string}` field defines the address (really, the port, since the host should be kept at `0.0.0.0`) the server should bind to. Defaults to `0.0.0.0:9090`. ::tip `~/.simapp` is the directory where the node's configuration and databases are stored. By default, it's set to `~/.{app_name}`. @@ -45,7 +57,7 @@ In Cosmos SDK v0.40, the node continues to serve a REST server. However, the exi All routes are configured under the following fields in `~/.simapp/config/app.toml`: -- `api.enable = true|false` field defines if the REST server should be enabled. Defaults to `true`. +- `api.enable = true|false` field defines if the REST server should be enabled. Defaults to `false`. - `api.address = {string}` field defines the address (really, the port, since the host should be kept at `0.0.0.0`) the server should bind to. Defaults to `tcp://0.0.0.0:1317`. - some additional API configuration options are defined in `~/.simapp/config/app.toml`, along with comments, please refer to that file directly. @@ -55,7 +67,7 @@ If, for various reasons, you cannot use gRPC (for example, you are building a we [gRPC-gateway](https://grpc-ecosystem.github.io/grpc-gateway/) is a tool to expose gRPC endpoints as REST endpoints. For each RPC endpoint defined in a Protobuf service, the SDK offers a REST equivalent. For instance, querying a balance could be done via the `/cosmos.bank.v1beta1.Query/AllBalances` gRPC endpoint, or alternatively via the gRPC-gateway `"/cosmos/bank/v1beta1/balances/{address}"` REST endpoint: both will return the same result. For each RPC method defined in a Protobuf service, the corresponding REST endpoint is defined as an option: -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/proto/cosmos/bank/v1beta1/query.proto#L19-L22 ++++ For application developers, gRPC-gateway REST routes needs to be wired up to the REST server, this is done by calling the `RegisterGRPCGatewayRoutes` function on the ModuleManager. diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index b06aaf246..10e309d9e 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -12,24 +12,35 @@ - [cosmos/auth/v1beta1/genesis.proto](#cosmos/auth/v1beta1/genesis.proto) - [GenesisState](#cosmos.auth.v1beta1.GenesisState) +- [cosmos/base/query/v1beta1/pagination.proto](#cosmos/base/query/v1beta1/pagination.proto) + - [PageRequest](#cosmos.base.query.v1beta1.PageRequest) + - [PageResponse](#cosmos.base.query.v1beta1.PageResponse) + - [cosmos/auth/v1beta1/query.proto](#cosmos/auth/v1beta1/query.proto) - [QueryAccountRequest](#cosmos.auth.v1beta1.QueryAccountRequest) - [QueryAccountResponse](#cosmos.auth.v1beta1.QueryAccountResponse) + - [QueryAccountsRequest](#cosmos.auth.v1beta1.QueryAccountsRequest) + - [QueryAccountsResponse](#cosmos.auth.v1beta1.QueryAccountsResponse) - [QueryParamsRequest](#cosmos.auth.v1beta1.QueryParamsRequest) - [QueryParamsResponse](#cosmos.auth.v1beta1.QueryParamsResponse) - [Query](#cosmos.auth.v1beta1.Query) -- [cosmos/base/v1beta1/coin.proto](#cosmos/base/v1beta1/coin.proto) - - [Coin](#cosmos.base.v1beta1.Coin) - - [DecCoin](#cosmos.base.v1beta1.DecCoin) - - [DecProto](#cosmos.base.v1beta1.DecProto) - - [IntProto](#cosmos.base.v1beta1.IntProto) - - [cosmos/authz/v1beta1/authz.proto](#cosmos/authz/v1beta1/authz.proto) - [AuthorizationGrant](#cosmos.authz.v1beta1.AuthorizationGrant) - [GenericAuthorization](#cosmos.authz.v1beta1.GenericAuthorization) - - [SendAuthorization](#cosmos.authz.v1beta1.SendAuthorization) + +- [cosmos/authz/v1beta1/genesis.proto](#cosmos/authz/v1beta1/genesis.proto) + - [GenesisState](#cosmos.authz.v1beta1.GenesisState) + - [GrantAuthorization](#cosmos.authz.v1beta1.GrantAuthorization) + +- [cosmos/authz/v1beta1/query.proto](#cosmos/authz/v1beta1/query.proto) + - [QueryAuthorizationRequest](#cosmos.authz.v1beta1.QueryAuthorizationRequest) + - [QueryAuthorizationResponse](#cosmos.authz.v1beta1.QueryAuthorizationResponse) + - [QueryAuthorizationsRequest](#cosmos.authz.v1beta1.QueryAuthorizationsRequest) + - [QueryAuthorizationsResponse](#cosmos.authz.v1beta1.QueryAuthorizationsResponse) + + - [Query](#cosmos.authz.v1beta1.Query) - [cosmos/base/abci/v1beta1/abci.proto](#cosmos/base/abci/v1beta1/abci.proto) - [ABCIMessageLog](#cosmos.base.abci.v1beta1.ABCIMessageLog) @@ -53,21 +64,14 @@ - [Msg](#cosmos.authz.v1beta1.Msg) -- [cosmos/authz/v1beta1/genesis.proto](#cosmos/authz/v1beta1/genesis.proto) - - [GenesisState](#cosmos.authz.v1beta1.GenesisState) - - [GrantAuthorization](#cosmos.authz.v1beta1.GrantAuthorization) +- [cosmos/base/v1beta1/coin.proto](#cosmos/base/v1beta1/coin.proto) + - [Coin](#cosmos.base.v1beta1.Coin) + - [DecCoin](#cosmos.base.v1beta1.DecCoin) + - [DecProto](#cosmos.base.v1beta1.DecProto) + - [IntProto](#cosmos.base.v1beta1.IntProto) -- [cosmos/base/query/v1beta1/pagination.proto](#cosmos/base/query/v1beta1/pagination.proto) - - [PageRequest](#cosmos.base.query.v1beta1.PageRequest) - - [PageResponse](#cosmos.base.query.v1beta1.PageResponse) - -- [cosmos/authz/v1beta1/query.proto](#cosmos/authz/v1beta1/query.proto) - - [QueryAuthorizationRequest](#cosmos.authz.v1beta1.QueryAuthorizationRequest) - - [QueryAuthorizationResponse](#cosmos.authz.v1beta1.QueryAuthorizationResponse) - - [QueryAuthorizationsRequest](#cosmos.authz.v1beta1.QueryAuthorizationsRequest) - - [QueryAuthorizationsResponse](#cosmos.authz.v1beta1.QueryAuthorizationsResponse) - - - [Query](#cosmos.authz.v1beta1.Query) +- [cosmos/bank/v1beta1/authz.proto](#cosmos/bank/v1beta1/authz.proto) + - [SendAuthorization](#cosmos.bank.v1beta1.SendAuthorization) - [cosmos/bank/v1beta1/bank.proto](#cosmos/bank/v1beta1/bank.proto) - [DenomUnit](#cosmos.bank.v1beta1.DenomUnit) @@ -302,6 +306,7 @@ - [TextProposal](#cosmos.gov.v1beta1.TextProposal) - [Vote](#cosmos.gov.v1beta1.Vote) - [VotingParams](#cosmos.gov.v1beta1.VotingParams) + - [WeightedVoteOption](#cosmos.gov.v1beta1.WeightedVoteOption) - [ProposalStatus](#cosmos.gov.v1beta1.ProposalStatus) - [VoteOption](#cosmos.gov.v1beta1.VoteOption) @@ -336,6 +341,8 @@ - [MsgSubmitProposalResponse](#cosmos.gov.v1beta1.MsgSubmitProposalResponse) - [MsgVote](#cosmos.gov.v1beta1.MsgVote) - [MsgVoteResponse](#cosmos.gov.v1beta1.MsgVoteResponse) + - [MsgVoteWeighted](#cosmos.gov.v1beta1.MsgVoteWeighted) + - [MsgVoteWeightedResponse](#cosmos.gov.v1beta1.MsgVoteWeightedResponse) - [Msg](#cosmos.gov.v1beta1.Msg) @@ -392,6 +399,12 @@ - [Msg](#cosmos.slashing.v1beta1.Msg) +- [cosmos/staking/v1beta1/authz.proto](#cosmos/staking/v1beta1/authz.proto) + - [StakeAuthorization](#cosmos.staking.v1beta1.StakeAuthorization) + - [StakeAuthorization.Validators](#cosmos.staking.v1beta1.StakeAuthorization.Validators) + + - [AuthorizationType](#cosmos.staking.v1beta1.AuthorizationType) + - [cosmos/staking/v1beta1/staking.proto](#cosmos/staking/v1beta1/staking.proto) - [Commission](#cosmos.staking.v1beta1.Commission) - [CommissionRates](#cosmos.staking.v1beta1.CommissionRates) @@ -837,6 +850,68 @@ GenesisState defines the auth module's genesis state. + + + + + + + + + + + +

Top

+ +## cosmos/base/query/v1beta1/pagination.proto + + + + + +### PageRequest +PageRequest is to be embedded in gRPC request messages for efficient +pagination. Ex: + + message SomeRequest { + Foo some_parameter = 1; + PageRequest pagination = 2; + } + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [bytes](#bytes) | | key is a value returned in PageResponse.next_key to begin querying the next page most efficiently. Only one of offset or key should be set. | +| `offset` | [uint64](#uint64) | | offset is a numeric offset that can be used when key is unavailable. It is less efficient than using key. Only one of offset or key should be set. | +| `limit` | [uint64](#uint64) | | limit is the total number of results to be returned in the result page. If left empty it will default to a value to be set by each app. | +| `count_total` | [bool](#bool) | | count_total 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. count_total is only respected when offset is used. It is ignored when key is set. | + + + + + + + + +### PageResponse +PageResponse is to be embedded in gRPC response messages where the +corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `next_key` | [bytes](#bytes) | | next_key is the key to be passed to PageRequest.key to query the next page most efficiently | +| `total` | [uint64](#uint64) | | total is total number of results available if PageRequest.count_total was set, its value is undefined otherwise | + + + + + @@ -884,6 +959,37 @@ QueryAccountResponse is the response type for the Query/Account RPC method. + + +### QueryAccountsRequest +QueryAccountsRequest is the request type for the Query/Accounts RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | + + + + + + + + +### QueryAccountsResponse +QueryAccountsResponse is the response type for the Query/Accounts RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `accounts` | [google.protobuf.Any](#google.protobuf.Any) | repeated | accounts are the existing accounts | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + ### QueryParamsRequest @@ -922,6 +1028,7 @@ Query defines the gRPC querier service. | Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | | ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Accounts` | [QueryAccountsRequest](#cosmos.auth.v1beta1.QueryAccountsRequest) | [QueryAccountsResponse](#cosmos.auth.v1beta1.QueryAccountsResponse) | Accounts returns all the existing accounts | GET|/cosmos/auth/v1beta1/accounts| | `Account` | [QueryAccountRequest](#cosmos.auth.v1beta1.QueryAccountRequest) | [QueryAccountResponse](#cosmos.auth.v1beta1.QueryAccountResponse) | Account returns account details based on address. | GET|/cosmos/auth/v1beta1/accounts/{address}| | `Params` | [QueryParamsRequest](#cosmos.auth.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.auth.v1beta1.QueryParamsResponse) | Params queries all parameters. | GET|/cosmos/auth/v1beta1/params| @@ -929,90 +1036,6 @@ Query defines the gRPC querier service. - -

Top

- -## cosmos/base/v1beta1/coin.proto - - - - - -### Coin -Coin defines a token with a denomination and an amount. - -NOTE: The amount field is an Int which implements the custom method -signatures required by gogoproto. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `denom` | [string](#string) | | | -| `amount` | [string](#string) | | | - - - - - - - - -### DecCoin -DecCoin defines a token with a denomination and a decimal amount. - -NOTE: The amount field is an Dec which implements the custom method -signatures required by gogoproto. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `denom` | [string](#string) | | | -| `amount` | [string](#string) | | | - - - - - - - - -### DecProto -DecProto defines a Protobuf wrapper around a Dec object. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `dec` | [string](#string) | | | - - - - - - - - -### IntProto -IntProto defines a Protobuf wrapper around an Int object. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `int` | [string](#string) | | | - - - - - - - - - - - - - - -

Top

@@ -1052,17 +1075,50 @@ the provided method on behalf of the granter's account. + - + -### SendAuthorization -SendAuthorization allows the grantee to spend up to spend_limit coins from -the granter's account. + + + + + + + +

Top

+ +## cosmos/authz/v1beta1/genesis.proto + + + + + +### GenesisState +GenesisState defines the authz module's genesis state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `spend_limit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `authorization` | [GrantAuthorization](#cosmos.authz.v1beta1.GrantAuthorization) | repeated | | + + + + + + + + +### GrantAuthorization +GrantAuthorization defines the GenesisState/GrantAuthorization type. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | +| `authorization` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `expiration` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | @@ -1078,6 +1134,98 @@ the granter's account. + +

Top

+ +## cosmos/authz/v1beta1/query.proto + + + + + +### QueryAuthorizationRequest +QueryAuthorizationRequest is the request type for the Query/Authorization RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | +| `method_name` | [string](#string) | | | + + + + + + + + +### QueryAuthorizationResponse +QueryAuthorizationResponse is the response type for the Query/Authorization RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `authorization` | [AuthorizationGrant](#cosmos.authz.v1beta1.AuthorizationGrant) | | authorization is a authorization granted for grantee by granter. | + + + + + + + + +### QueryAuthorizationsRequest +QueryAuthorizationsRequest is the request type for the Query/Authorizations RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an pagination for the request. | + + + + + + + + +### QueryAuthorizationsResponse +QueryAuthorizationsResponse is the response type for the Query/Authorizations RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `authorizations` | [AuthorizationGrant](#cosmos.authz.v1beta1.AuthorizationGrant) | repeated | authorizations is a list of grants granted for grantee by granter. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines an pagination for the response. | + + + + + + + + + + + + + + +### Query +Query defines the gRPC querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Authorization` | [QueryAuthorizationRequest](#cosmos.authz.v1beta1.QueryAuthorizationRequest) | [QueryAuthorizationResponse](#cosmos.authz.v1beta1.QueryAuthorizationResponse) | Returns any `Authorization` (or `nil`), with the expiration time, granted to the grantee by the granter for the provided msg type. | GET|/cosmos/authz/v1beta1/granters/{granter}/grantees/{grantee}/grant| +| `Authorizations` | [QueryAuthorizationsRequest](#cosmos.authz.v1beta1.QueryAuthorizationsRequest) | [QueryAuthorizationsResponse](#cosmos.authz.v1beta1.QueryAuthorizationsResponse) | Returns list of `Authorization`, granted to the grantee by the granter. | GET|/cosmos/authz/v1beta1/granters/{granter}/grantees/{grantee}/grants| + + + + +

Top

@@ -1393,40 +1541,75 @@ Msg defines the authz Msg service. - +

Top

-## cosmos/authz/v1beta1/genesis.proto +## cosmos/base/v1beta1/coin.proto - + -### GenesisState -GenesisState defines the authz module's genesis state. +### Coin +Coin defines a token with a denomination and an amount. + +NOTE: The amount field is an Int which implements the custom method +signatures required by gogoproto. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `authorization` | [GrantAuthorization](#cosmos.authz.v1beta1.GrantAuthorization) | repeated | | +| `denom` | [string](#string) | | | +| `amount` | [string](#string) | | | - + -### GrantAuthorization -GrantAuthorization defines the GenesisState/GrantAuthorization type. +### DecCoin +DecCoin defines a token with a denomination and a decimal amount. + +NOTE: The amount field is an Dec which implements the custom method +signatures required by gogoproto. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `granter` | [string](#string) | | | -| `grantee` | [string](#string) | | | -| `authorization` | [google.protobuf.Any](#google.protobuf.Any) | | | -| `expiration` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | +| `denom` | [string](#string) | | | +| `amount` | [string](#string) | | | + + + + + + + + +### DecProto +DecProto defines a Protobuf wrapper around a Dec object. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `dec` | [string](#string) | | | + + + + + + + + +### IntProto +IntProto defines a Protobuf wrapper around an Int object. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `int` | [string](#string) | | | @@ -1442,53 +1625,23 @@ GrantAuthorization defines the GenesisState/GrantAuthorization type. - +

Top

-## cosmos/base/query/v1beta1/pagination.proto +## cosmos/bank/v1beta1/authz.proto - + -### PageRequest -PageRequest is to be embedded in gRPC request messages for efficient -pagination. Ex: - - message SomeRequest { - Foo some_parameter = 1; - PageRequest pagination = 2; - } +### SendAuthorization +SendAuthorization allows the grantee to spend up to spend_limit coins from +the granter's account. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `key` | [bytes](#bytes) | | key is a value returned in PageResponse.next_key to begin querying the next page most efficiently. Only one of offset or key should be set. | -| `offset` | [uint64](#uint64) | | offset is a numeric offset that can be used when key is unavailable. It is less efficient than using key. Only one of offset or key should be set. | -| `limit` | [uint64](#uint64) | | limit is the total number of results to be returned in the result page. If left empty it will default to a value to be set by each app. | -| `count_total` | [bool](#bool) | | count_total 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. count_total is only respected when offset is used. It is ignored when key is set. | - - - - - - - - -### PageResponse -PageResponse is to be embedded in gRPC response messages where the -corresponding request message has used PageRequest. - - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `next_key` | [bytes](#bytes) | | next_key is the key to be passed to PageRequest.key to query the next page most efficiently | -| `total` | [uint64](#uint64) | | total is total number of results available if PageRequest.count_total was set, its value is undefined otherwise | +| `spend_limit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | @@ -1504,98 +1657,6 @@ corresponding request message has used PageRequest. - -

Top

- -## cosmos/authz/v1beta1/query.proto - - - - - -### QueryAuthorizationRequest -QueryAuthorizationRequest is the request type for the Query/Authorization RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `granter` | [string](#string) | | | -| `grantee` | [string](#string) | | | -| `method_name` | [string](#string) | | | - - - - - - - - -### QueryAuthorizationResponse -QueryAuthorizationResponse is the response type for the Query/Authorization RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `authorization` | [AuthorizationGrant](#cosmos.authz.v1beta1.AuthorizationGrant) | | authorization is a authorization granted for grantee by granter. | - - - - - - - - -### QueryAuthorizationsRequest -QueryAuthorizationsRequest is the request type for the Query/Authorizations RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `granter` | [string](#string) | | | -| `grantee` | [string](#string) | | | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an pagination for the request. | - - - - - - - - -### QueryAuthorizationsResponse -QueryAuthorizationsResponse is the response type for the Query/Authorizations RPC method. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `authorizations` | [AuthorizationGrant](#cosmos.authz.v1beta1.AuthorizationGrant) | repeated | authorizations is a list of grants granted for grantee by granter. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines an pagination for the response. | - - - - - - - - - - - - - - -### Query -Query defines the gRPC querier service. - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `Authorization` | [QueryAuthorizationRequest](#cosmos.authz.v1beta1.QueryAuthorizationRequest) | [QueryAuthorizationResponse](#cosmos.authz.v1beta1.QueryAuthorizationResponse) | Returns any `Authorization` (or `nil`), with the expiration time, granted to the grantee by the granter for the provided msg type. | GET|/cosmos/authz/v1beta1/granters/{granter}/grantees/{grantee}/grant| -| `Authorizations` | [QueryAuthorizationsRequest](#cosmos.authz.v1beta1.QueryAuthorizationsRequest) | [QueryAuthorizationsResponse](#cosmos.authz.v1beta1.QueryAuthorizationsResponse) | Returns list of `Authorization`, granted to the grantee by the granter. | GET|/cosmos/authz/v1beta1/granters/{granter}/grantees/{grantee}/grants| - - - - -

Top

@@ -1650,6 +1711,8 @@ a basic token. | `denom_units` | [DenomUnit](#cosmos.bank.v1beta1.DenomUnit) | repeated | denom_units represents the list of DenomUnit's for a given coin | | `base` | [string](#string) | | base represents the base denom (should be the DenomUnit with exponent = 0). | | `display` | [string](#string) | | display indicates the suggested denom that should be displayed in clients. | +| `name` | [string](#string) | | name defines the name of the token (eg: Cosmos Atom) | +| `symbol` | [string](#string) | | symbol is the token symbol usually shown on exchanges (eg: ATOM). This can be the same as the display. | @@ -2882,7 +2945,8 @@ Msg defines the bank Msg service. ### PrivKey -PrivKey defines a ed25519 private key. +Deprecated: PrivKey defines a ed25519 private key. +NOTE: ed25519 keys must not be used in SDK apps except in a tendermint validator context. | Field | Type | Label | Description | @@ -2897,11 +2961,11 @@ PrivKey defines a ed25519 private key. ### PubKey -PubKey defines a ed25519 public key -Key is the compressed form of the pubkey. The first byte depends is a 0x02 byte -if the y-coordinate is the lexicographically largest of the two associated with -the x-coordinate. Otherwise the first byte is a 0x03. -This prefix is followed with the x-coordinate. +PubKey is an ed25519 public key for handling Tendermint keys in SDK. +It's needed for Any serialization and SDK compatibility. +It must not be used in a non Tendermint key context because it doesn't implement +ADR-28. Nevertheless, you will like to use ed25519 in app user level +then you must create a new proto message and follow ADR-28 for Address construction. | Field | Type | Label | Description | @@ -4587,7 +4651,7 @@ A Vote consists of a proposal ID, the voter, and the vote option. | ----- | ---- | ----- | ----------- | | `proposal_id` | [uint64](#uint64) | | | | `voter` | [string](#string) | | | -| `option` | [VoteOption](#cosmos.gov.v1beta1.VoteOption) | | | +| `options` | [WeightedVoteOption](#cosmos.gov.v1beta1.WeightedVoteOption) | repeated | | @@ -4608,6 +4672,22 @@ VotingParams defines the params for voting on governance proposals. + + + +### WeightedVoteOption +WeightedVoteOption defines a unit of vote for vote split. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `option` | [VoteOption](#cosmos.gov.v1beta1.VoteOption) | | | +| `weight` | [string](#string) | | | + + + + + @@ -5065,6 +5145,33 @@ MsgVoteResponse defines the Msg/Vote response type. + + + +### MsgVoteWeighted +MsgVote defines a message to cast a vote. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | | +| `voter` | [string](#string) | | | +| `options` | [WeightedVoteOption](#cosmos.gov.v1beta1.WeightedVoteOption) | repeated | | + + + + + + + + +### MsgVoteWeightedResponse +MsgVoteWeightedResponse defines the Msg/VoteWeighted response type. + + + + + @@ -5081,6 +5188,7 @@ Msg defines the bank Msg service. | ----------- | ------------ | ------------- | ------------| ------- | -------- | | `SubmitProposal` | [MsgSubmitProposal](#cosmos.gov.v1beta1.MsgSubmitProposal) | [MsgSubmitProposalResponse](#cosmos.gov.v1beta1.MsgSubmitProposalResponse) | SubmitProposal defines a method to create new proposal given a content. | | | `Vote` | [MsgVote](#cosmos.gov.v1beta1.MsgVote) | [MsgVoteResponse](#cosmos.gov.v1beta1.MsgVoteResponse) | Vote defines a method to add a vote on a specific proposal. | | +| `VoteWeighted` | [MsgVoteWeighted](#cosmos.gov.v1beta1.MsgVoteWeighted) | [MsgVoteWeightedResponse](#cosmos.gov.v1beta1.MsgVoteWeightedResponse) | WeightedVote defines a method to add a weighted vote on a specific proposal. | | | `Deposit` | [MsgDeposit](#cosmos.gov.v1beta1.MsgDeposit) | [MsgDepositResponse](#cosmos.gov.v1beta1.MsgDepositResponse) | Deposit defines a method to add deposit on a specific proposal. | | @@ -5692,6 +5800,69 @@ Msg defines the slashing Msg service. + +

Top

+ +## cosmos/staking/v1beta1/authz.proto + + + + + +### StakeAuthorization +StakeAuthorization defines authorization for delegate/undelegate/redelegate. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `max_tokens` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | max_tokens specifies the maximum amount of tokens can be delegate to a validator. If it is empty, there is no spend limit and any amount of coins can be delegated. | +| `allow_list` | [StakeAuthorization.Validators](#cosmos.staking.v1beta1.StakeAuthorization.Validators) | | allow_list specifies list of validator addresses to whom grantee can delegate tokens on behalf of granter's account. | +| `deny_list` | [StakeAuthorization.Validators](#cosmos.staking.v1beta1.StakeAuthorization.Validators) | | deny_list specifies list of validator addresses to whom grantee can not delegate tokens. | +| `authorization_type` | [AuthorizationType](#cosmos.staking.v1beta1.AuthorizationType) | | authorization_type defines one of AuthorizationType. | + + + + + + + + +### StakeAuthorization.Validators +Validators defines list of validator addresses. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | repeated | | + + + + + + + + + + +### AuthorizationType +AuthorizationType defines the type of staking module authorization type + +| Name | Number | Description | +| ---- | ------ | ----------- | +| AUTHORIZATION_TYPE_UNSPECIFIED | 0 | AUTHORIZATION_TYPE_UNSPECIFIED specifies an unknown authorization type | +| AUTHORIZATION_TYPE_DELEGATE | 1 | AUTHORIZATION_TYPE_DELEGATE defines an authorization type for Msg/Delegate | +| AUTHORIZATION_TYPE_UNDELEGATE | 2 | AUTHORIZATION_TYPE_UNDELEGATE defines an authorization type for Msg/Undelegate | +| AUTHORIZATION_TYPE_REDELEGATE | 3 | AUTHORIZATION_TYPE_REDELEGATE defines an authorization type for Msg/BeginRedelegate | + + + + + + + + + +

Top

@@ -7901,17 +8072,20 @@ client. ### ClientUpdateProposal -ClientUpdateProposal is a governance proposal. If it passes, the client is -updated with the provided header. The update may fail if the header is not -valid given certain conditions specified by the client implementation. +ClientUpdateProposal is a governance proposal. If it passes, the substitute client's +consensus states starting from the 'initial height' are copied over to the subjects +client state. The proposal handler may fail if the subject and the substitute do not +match in client and chain parameters (with exception to latest height, frozen height, and chain-id). +The updated client must also be valid (cannot be expired). | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `title` | [string](#string) | | the title of the update proposal | | `description` | [string](#string) | | the description of the proposal | -| `client_id` | [string](#string) | | the client identifier for the client to be updated if the proposal passes | -| `header` | [google.protobuf.Any](#google.protobuf.Any) | | the header used to update the client if the proposal passes | +| `subject_client_id` | [string](#string) | | the client identifier for the client to be updated if the proposal passes | +| `substitute_client_id` | [string](#string) | | the substitute client identifier for the client standing in for the subject client | +| `initial_height` | [Height](#ibc.core.client.v1.Height) | | the intital height to copy consensus states from the substitute to the subject | diff --git a/docs/ibc/README.md b/docs/ibc/README.md index 22061db94..6298c1196 100644 --- a/docs/ibc/README.md +++ b/docs/ibc/README.md @@ -12,6 +12,7 @@ This repository contains reference documentation for the IBC protocol integratio 2. [Integration](./integration.md) 3. [Customization](./custom.md) 4. [Relayer](./relayer.md) +5. [Governance Proposals](./proposals.md) After reading about IBC, head on to the [Building Modules documentation](../building-modules/README.md) to learn more about the process of building modules. diff --git a/docs/ibc/proposals.md b/docs/ibc/proposals.md new file mode 100644 index 000000000..6bdf9f705 --- /dev/null +++ b/docs/ibc/proposals.md @@ -0,0 +1,42 @@ + + +# Governance Proposals + +In uncommon situations, a highly valued client may become frozen due to uncontrollable +circumstances. A highly valued client might have hundreds of channels being actively used. +Some of those channels might have a significant amount of locked tokens used for ICS 20. + +If the one third of the validator set of the chain the client represents decides to collude, +they can sign off on two valid but conflicting headers each signed by the other one third +of the honest validator set. The light client can now be updated with two valid, but conflicting +headers at the same height. The light client cannot know which header is trustworthy and therefore +evidence of such misbehaviour is likely to be submitted resulting in a frozen light client. + +Frozen light clients cannot be updated under any circumstance except via a governance proposal. +Since a quorum of validators can sign arbitrary state roots which may not be valid executions +of the state machine, a governance proposal has been added to ease the complexity of unfreezing +or updating clients which have become "stuck". Without this mechanism, validator sets would need +to construct a state root to unfreeze the client. Unfreezing clients, re-enables all of the channels +built upon that client. This may result in recovery of otherwise lost funds. + +Tendermint light clients may become expired if the trusting period has passed since their +last update. This may occur if relayers stop submitting headers to update the clients. + +An unplanned upgrade by the counterparty chain may also result in expired clients. If the counterparty +chain undergoes an unplanned upgrade, there may be no commitment to that upgrade signed by the validator +set before the chain-id changes. In this situation, the validator set of the last valid update for the +light client is never expected to produce another valid header since the chain-id has changed, which will +ultimately lead the on-chain light client to become expired. + +In the case that a highly valued light client is frozen, expired, or rendered non-updateable, a +governance proposal may be submitted to update this client, known as the subject client. The +proposal includes the client identifier for the subject, the client identifier for a substitute +client, and an initial height to reference the substitute client from. Light client implementations +may implement custom updating logic, but in most cases, the subject will be updated with information +from the substitute client, if the proposal passes. The substitute client is used as a "stand in" +while the subject is on trial. It is best practice to create a substitute client *after* the subject +has become frozen to avoid the substitute from also becoming frozen. An active substitute client +allows headers to be submitted during the voting period to prevent accidental expiry once the proposal +passes. diff --git a/docs/migrations/rest.md b/docs/migrations/rest.md index b1675543e..b3ff87ba9 100644 --- a/docs/migrations/rest.md +++ b/docs/migrations/rest.md @@ -32,11 +32,11 @@ Some important information concerning all legacy REST endpoints: ## Migrating to New REST Endpoints -Thanks to the Protocol Buffers migration in v0.40, we are able to take advantage of a vast number of gRPC tools and solutions. For most of the legacy REST endpoints, Cosmos SDK v0.40 provides new REST endpoints generated from [gRPC `Query` services](../building-modules/query-services.md) using [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/). We usually call them _gGPC-gateway REST endpoints_. +Thanks to the Protocol Buffers migration in v0.40, we are able to take advantage of a vast number of gRPC tools and solutions. For most of the legacy REST endpoints, Cosmos SDK v0.40 provides new REST endpoints generated from [gRPC `Query` services](../building-modules/query-services.md) using [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/). We usually call them _gRPC-gateway REST endpoints_. Some modules expose legacy `POST` endpoints to generate unsigned transactions for their `Msg`s. These `POST` endpoints have been removed. We recommend to use [service `Msg`s](../building-modules/msg-services.md) directly, and use Protobuf to do client-side transaction generation. A guide can be found [here](../run-node/txs.md). -| Legacy REST Endpoint | Description | New gGPC-gateway REST Endpoint | +| Legacy REST Endpoint | Description | New gRPC-gateway REST Endpoint | | ------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | `GET /txs/{hash}` | Query tx by hash | `GET /cosmos/tx/v1beta1/txs/{hash}` | | `GET /txs` | Query tx by events | `GET /cosmos/tx/v1beta1/txs` | @@ -44,7 +44,7 @@ Some modules expose legacy `POST` endpoints to generate unsigned transactions fo | `POST /txs/encode` | Encodes an Amino JSON tx to an Amino binary tx | N/A, use Protobuf directly | | `POST /txs/decode` | Decodes an Amino binary tx into an Amino JSON tx | N/A, use Protobuf directly | | `POST /bank/*` | Create unsigned `Msg`s for bank tx | N/A, use Protobuf directly | -| `GET /bank/balances/{address}` | Get the balance of an address | `GET /cosmos/bank/v1beta1/balances/{address}/{denom}` | +| `GET /bank/balances/{address}` | Get the balance of an address | `GET /cosmos/bank/v1beta1/balances/{address}` | | `GET /bank/total` | Get the total supply of all coins | `GET /cosmos/bank/v1beta1/supply` | | `GET /bank/total/{denom}` | Get the total supply of one coin | `GET /cosmos/bank/v1beta1/supply/{denom}` | | `POST /distribution/delegators/{delegatorAddr}/rewards` | Withdraw all delegator rewards | N/A, use Protobuf directly | @@ -52,8 +52,8 @@ Some modules expose legacy `POST` endpoints to generate unsigned transactions fo | `GET /distribution/delegators/{delegatorAddr}/rewards` | Get the total rewards balance from all delegations | `GET /cosmos/distribution/v1beta1/v1beta1/delegators/{delegator_address}/rewards` | | `GET /distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}` | Query a delegation reward | `GET /cosmos/distribution/v1beta1/delegators/{delegatorAddr}/rewards/{validatorAddr}` | | `GET /distribution/delegators/{delegatorAddr}/withdraw_address` | Get the rewards withdrawal address | `GET /cosmos/distribution/v1beta1/delegators/{delegatorAddr}/withdraw_address` | -| `GET /distribution/validators/{validatorAddr}` | Validator distribution information | `GET /cosmos/distribution/v1beta1/validators/{validatorAddr}` | -| `GET /distribution/validators/{validatorAddr}/rewards` | Commission and self-delegation rewards of a single a validator | `GET /cosmos/distribution/v1beta1/validators/{validatorAddr}/rewards` | +| `GET /distribution/validators/{validatorAddr}` | Validator distribution information | N/A | +| `GET /distribution/validators/{validatorAddr}/rewards` | Commission and outstanding rewards of a single a validator | `GET /cosmos/distribution/v1beta1/validators/{validatorAddr}/commission`
`GET /cosmos/distribution/v1beta1/validators/{validatorAddr}/outstanding_rewards` | | `GET /distribution/validators/{validatorAddr}/outstanding_rewards` | Outstanding rewards of a single validator | `GET /cosmos/distribution/v1beta1/validators/{validatorAddr}/outstanding_rewards` | | `GET /distribution/parameters` | Get the current distribution parameter values | `GET /cosmos/distribution/v1beta1/params` | | `GET /distribution/community_pool` | Get the amount held in the community pool | `GET /cosmos/distribution/v1beta1/community_pool` | @@ -77,12 +77,12 @@ Some modules expose legacy `POST` endpoints to generate unsigned transactions fo | `GET /slashing/signing_infos` | Get all signing infos | `GET /cosmos/slashing/v1beta1/signing_infos` | | `GET /slashing/parameters` | Get slashing parameters | `GET /cosmos/slashing/v1beta1/params` | | `POST /staking/*` | Create unsigned `Msg`s for staking | N/A, use Protobuf directly | -| `GET /staking/delegators/{delegatorAddr}/delegations` | Get all delegations from a delegator | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/delegations` | +| `GET /staking/delegators/{delegatorAddr}/delegations` | Get all delegations from a delegator | `GET /cosmos/staking/v1beta1/delegations/{delegatorAddr}` | | `GET /staking/delegators/{delegatorAddr}/unbonding_delegations` | Get all unbonding delegations from a delegator | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/unbonding_delegations` | | `GET /staking/delegators/{delegatorAddr}/txs` | Get all staking txs (i.e msgs) from a delegator | Removed | | `GET /staking/delegators/{delegatorAddr}/validators` | Query all validators that a delegator is bonded to | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/validators` | | `GET /staking/delegators/{delegatorAddr}/validators/{validatorAddr}` | Query a validator that a delegator is bonded to | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/validators/{validatorAddr}` | -| `GET /staking/delegators/{delegatorAddr}/delegations/{validatorAddr}` | Query a delegation between a delegator and a validator | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/delegations/{validatorAddr}` | +| `GET /staking/delegators/{delegatorAddr}/delegations/{validatorAddr}` | Query a delegation between a delegator and a validator | `GET /cosmos/staking/v1beta1/validators/{validatorAddr}/delegations/{delegatorAddr}` | | `GET /staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}` | Query all unbonding delegations between a delegator and a validator | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}` | | `GET /staking/redelegations` | Query redelegations | `GET /cosmos/staking/v1beta1/v1beta1/delegators/{delegator_addr}/redelegations` | | `GET /staking/validators` | Get all validators | `GET /cosmos/staking/v1beta1/validators` | diff --git a/docs/package-lock.json b/docs/package-lock.json index d6bc25133..7e6044815 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -5,118 +5,118 @@ "requires": true, "dependencies": { "@algolia/cache-browser-local-storage": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.8.3.tgz", - "integrity": "sha512-Cwc03hikHSUI+xvgUdN+H+f6jFyoDsC9fegzXzJ2nPn1YSN9EXzDMBnbrgl0sbl9iLGXe0EIGMYqR2giCv1wMQ==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.8.4.tgz", + "integrity": "sha512-qSS3VMP3oMhcLrYIFveRyt3F5XB6MqWogF4Vooj8KvOvqv6jBmYwkAueSXCF5pkJEaA72VL9+9NbBpfC8ez2ww==", "requires": { - "@algolia/cache-common": "4.8.3" + "@algolia/cache-common": "4.8.4" } }, "@algolia/cache-common": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.8.3.tgz", - "integrity": "sha512-Cf7zZ2i6H+tLSBTkFePHhYvlgc9fnMPKsF9qTmiU38kFIGORy/TN2Fx5n1GBuRLIzaSXvcf+oHv1HvU0u1gE1g==" + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.8.4.tgz", + "integrity": "sha512-5+dLmj6qFy4WOtnNQuFRfWTIIDdpUigv+dXaKMFplNPBvZHGFy3hcRjWqYzGcqaeLqcXbN8cU5r75mvrlJIxcw==" }, "@algolia/cache-in-memory": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.8.3.tgz", - "integrity": "sha512-+N7tkvmijXiDy2E7u1mM73AGEgGPWFmEmPeJS96oT46I98KXAwVPNYbcAqBE79YlixdXpkYJk41cFcORzNh+Iw==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.8.4.tgz", + "integrity": "sha512-PBN4YKxn/L+HjVKqUE5rtLiFKqzm4qnUoF7QvCFFmFAViCdYwZSMFVmDobstqWY3KULfsEqaeD4eU4jxZbKhEA==", "requires": { - "@algolia/cache-common": "4.8.3" + "@algolia/cache-common": "4.8.4" } }, "@algolia/client-account": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.8.3.tgz", - "integrity": "sha512-Uku8LqnXBwfDCtsTCDYTUOz2/2oqcAQCKgaO0uGdIR8DTQENBXFQvzziambHdn9KuFuY+6Et9k1+cjpTPBDTBg==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.8.4.tgz", + "integrity": "sha512-mrsOnGV4O2b+t1CumUH72+Psw9d9qwngBEp2le7IMSceJQywQvNCyJ4B4qyoozHsIGapXfcVAOhRxqUsNQ6U6g==", "requires": { - "@algolia/client-common": "4.8.3", - "@algolia/client-search": "4.8.3", - "@algolia/transporter": "4.8.3" + "@algolia/client-common": "4.8.4", + "@algolia/client-search": "4.8.4", + "@algolia/transporter": "4.8.4" } }, "@algolia/client-analytics": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.8.3.tgz", - "integrity": "sha512-9ensIWmjYJprZ+YjAVSZdWUG05xEnbytENXp508X59tf34IMIX8BR2xl0RjAQODtxBdAteGxuKt5THX6U9tQLA==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.8.4.tgz", + "integrity": "sha512-Xy70njSUgG/QTv5+rPjsTIzBF/bjxseS5h9SawrQGzovTosbJbu9JBlg4YwVJnYvjovzpr7S39+gPIPc8M7+Rg==", "requires": { - "@algolia/client-common": "4.8.3", - "@algolia/client-search": "4.8.3", - "@algolia/requester-common": "4.8.3", - "@algolia/transporter": "4.8.3" + "@algolia/client-common": "4.8.4", + "@algolia/client-search": "4.8.4", + "@algolia/requester-common": "4.8.4", + "@algolia/transporter": "4.8.4" } }, "@algolia/client-common": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.8.3.tgz", - "integrity": "sha512-TU3623AEFAWUQlDTznkgAMSYo8lfS9pNs5QYDQzkvzWdqK0GBDWthwdRfo9iIsfxiR9qdCMHqwEu+AlZMVhNSA==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.8.4.tgz", + "integrity": "sha512-sQlRa+KWFn+D8AOEZb4kj6RE/i6DnPwVOF4AnNf9IjNB0mUUhLWw96cQN6GDx0KE4lhW67t+qR39ZuuDBgR9ww==", "requires": { - "@algolia/requester-common": "4.8.3", - "@algolia/transporter": "4.8.3" + "@algolia/requester-common": "4.8.4", + "@algolia/transporter": "4.8.4" } }, "@algolia/client-recommendation": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/client-recommendation/-/client-recommendation-4.8.3.tgz", - "integrity": "sha512-qysGbmkcc6Agt29E38KWJq9JuxjGsyEYoKuX9K+P5HyQh08yR/BlRYrA8mB7vT/OIUHRGFToGO6Vq/rcg0NIOQ==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/client-recommendation/-/client-recommendation-4.8.4.tgz", + "integrity": "sha512-CE0CVqLGWotVOaUXyU33FVD9FZ/7rqcbwFPH5MgSjVdE0B1YWVedhR0s2BNKodXLcIGVLVYfXR05CLdvOlTw+A==", "requires": { - "@algolia/client-common": "4.8.3", - "@algolia/requester-common": "4.8.3", - "@algolia/transporter": "4.8.3" + "@algolia/client-common": "4.8.4", + "@algolia/requester-common": "4.8.4", + "@algolia/transporter": "4.8.4" } }, "@algolia/client-search": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.8.3.tgz", - "integrity": "sha512-rAnvoy3GAhbzOQVniFcKVn1eM2NX77LearzYNCbtFrFYavG+hJI187bNVmajToiuGZ10FfJvK99X2OB1AzzezQ==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.8.4.tgz", + "integrity": "sha512-eH2tRPnDU3tqpp0BSqP6coRRQe8fceqsupuf/1ho+Mcs5DM13mEuFmNOyPywHRlYLVPmbbCPRhDr5rB8QoN7XQ==", "requires": { - "@algolia/client-common": "4.8.3", - "@algolia/requester-common": "4.8.3", - "@algolia/transporter": "4.8.3" + "@algolia/client-common": "4.8.4", + "@algolia/requester-common": "4.8.4", + "@algolia/transporter": "4.8.4" } }, "@algolia/logger-common": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.8.3.tgz", - "integrity": "sha512-03wksHRbhl2DouEKnqWuUb64s1lV6kDAAabMCQ2Du1fb8X/WhDmxHC4UXMzypeOGlH5BZBsgVwSB7vsZLP3MZg==" + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.8.4.tgz", + "integrity": "sha512-6hOaFG75Onmant9adcaeCZgvPYfnif7n0H1ycbixm6/WH3SmxqPMG+CMiW8mTNTRrrAEceQVrq6tDHD8jdnOOw==" }, "@algolia/logger-console": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.8.3.tgz", - "integrity": "sha512-Npt+hI4UF8t3TLMluL5utr9Gc11BjL5kDnGZOhDOAz5jYiSO2nrHMFmnpLT4Cy/u7a5t7EB5dlypuC4/AGStkA==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.8.4.tgz", + "integrity": "sha512-+9T3t/eB9vseANFz9YbFHG0cHjzVP/DVfGqzTAkeSlvMHP69JzJga9Wb0Ai6J3xXE3d4k9K+k6t+kkjCQjzEqg==", "requires": { - "@algolia/logger-common": "4.8.3" + "@algolia/logger-common": "4.8.4" } }, "@algolia/requester-browser-xhr": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.8.3.tgz", - "integrity": "sha512-/LTTIpgEmEwkyhn8yXxDdBWqXqzlgw5w2PtTpIwkSlP2/jDwdR/9w1TkFzhNbJ81ki6LAEQM5mSwoTTnbIIecg==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.8.4.tgz", + "integrity": "sha512-BYa8O/pht0UL2bcm0ZkLZiyC+5dHrbc6gvKIo+OgqxmDb/K4KrVo6RIof3BVpR8fgcfxQJohjNVHKXHxEUhBCQ==", "requires": { - "@algolia/requester-common": "4.8.3" + "@algolia/requester-common": "4.8.4" } }, "@algolia/requester-common": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.8.3.tgz", - "integrity": "sha512-+Yo9vBkofoKR1SCqqtMnmnfq9yt/BiaDewY/6bYSMNxSYCnu2Fw1JKSIaf/4zos09PMSsxGpLohZwGas3+0GDQ==" + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.8.4.tgz", + "integrity": "sha512-br3LXb6srfAy7F04axwExmrkPOlXCDckgTFoLFv/RT9Oo28SpoyvHqktyBovQLdzdTs+Laglf+LtOHr0iUrZJg==" }, "@algolia/requester-node-http": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.8.3.tgz", - "integrity": "sha512-k2fiKIeMIFqgC01FnzII6kqC2GQBAfbNaUX4k7QCPa6P8t4sp2xE6fImOUiztLnnL3C9X9ZX6Fw3L+cudi7jvQ==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.8.4.tgz", + "integrity": "sha512-o5Cc4UxYPn3IBHQSDBNFFhq1LQLv40eYvCvK0FPJ8xZkrnNXhjPvaLCu/lQTHpk/HX7DaE6fQ/KboU0OSPKevQ==", "requires": { - "@algolia/requester-common": "4.8.3" + "@algolia/requester-common": "4.8.4" } }, "@algolia/transporter": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.8.3.tgz", - "integrity": "sha512-nU7fy2iU8snxATlsks0MjMyv97QJWQmOVwTjDc+KZ4+nue8CLcgm4LA4dsTBqvxeCQIoEtt3n72GwXcaqiJSjQ==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.8.4.tgz", + "integrity": "sha512-EvXFYICxrr9QEO6m6awUeNOBstOxePQ2Fy0jtYlS1v9TY2P5HqKRzkxmaZjeYRBsXOImpVjgQIzTzj1Au4br2w==", "requires": { - "@algolia/cache-common": "4.8.3", - "@algolia/logger-common": "4.8.3", - "@algolia/requester-common": "4.8.3" + "@algolia/cache-common": "4.8.4", + "@algolia/logger-common": "4.8.4", + "@algolia/requester-common": "4.8.4" } }, "@babel/code-frame": { @@ -1241,9 +1241,9 @@ } }, "@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==" + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" }, "@types/minimatch": { "version": "3.0.3", @@ -1251,9 +1251,9 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, "@types/node": { - "version": "14.14.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", - "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==" + "version": "14.14.22", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz", + "integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==" }, "@types/q": { "version": "1.5.4", @@ -1337,9 +1337,9 @@ }, "dependencies": { "core-js": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.2.tgz", - "integrity": "sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A==" + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.3.tgz", + "integrity": "sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q==" } } }, @@ -1510,9 +1510,9 @@ }, "dependencies": { "core-js": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.2.tgz", - "integrity": "sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A==" + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.3.tgz", + "integrity": "sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q==" } } }, @@ -1861,24 +1861,24 @@ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "algoliasearch": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.8.3.tgz", - "integrity": "sha512-pljX9jEE2TQ3i1JayhG8afNdE8UuJg3O9c7unW6QO67yRWCKr6b0t5aKC3hSVtjt7pA2TQXLKoAISb4SHx9ozQ==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.8.4.tgz", + "integrity": "sha512-QbXpFvBKj/QhKWE7xBoqaWOWyw7ni6W6THSuFJHOcADRrInhjFCBYjrv+YsIhv9huCepKXWpfV4UJup9BslVhQ==", "requires": { - "@algolia/cache-browser-local-storage": "4.8.3", - "@algolia/cache-common": "4.8.3", - "@algolia/cache-in-memory": "4.8.3", - "@algolia/client-account": "4.8.3", - "@algolia/client-analytics": "4.8.3", - "@algolia/client-common": "4.8.3", - "@algolia/client-recommendation": "4.8.3", - "@algolia/client-search": "4.8.3", - "@algolia/logger-common": "4.8.3", - "@algolia/logger-console": "4.8.3", - "@algolia/requester-browser-xhr": "4.8.3", - "@algolia/requester-common": "4.8.3", - "@algolia/requester-node-http": "4.8.3", - "@algolia/transporter": "4.8.3" + "@algolia/cache-browser-local-storage": "4.8.4", + "@algolia/cache-common": "4.8.4", + "@algolia/cache-in-memory": "4.8.4", + "@algolia/client-account": "4.8.4", + "@algolia/client-analytics": "4.8.4", + "@algolia/client-common": "4.8.4", + "@algolia/client-recommendation": "4.8.4", + "@algolia/client-search": "4.8.4", + "@algolia/logger-common": "4.8.4", + "@algolia/logger-console": "4.8.4", + "@algolia/requester-browser-xhr": "4.8.4", + "@algolia/requester-common": "4.8.4", + "@algolia/requester-node-http": "4.8.4", + "@algolia/transporter": "4.8.4" } }, "align-text": { @@ -2864,9 +2864,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001178", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001178.tgz", - "integrity": "sha512-VtdZLC0vsXykKni8Uztx45xynytOi71Ufx9T8kHptSw9AL4dpqailUJJHavttuzUe1KYuBYtChiWv+BAb7mPmQ==" + "version": "1.0.30001179", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001179.tgz", + "integrity": "sha512-blMmO0QQujuUWZKyVrD1msR4WNDAqb/UPO1Sw2WWsQ7deoM5bJiicKnWJ1Y0NS/aGINSnKPIWBMw5luX+NDUCA==" }, "caseless": { "version": "0.12.0", @@ -3406,11 +3406,11 @@ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" }, "core-js-compat": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.2.tgz", - "integrity": "sha512-LO8uL9lOIyRRrQmZxHZFl1RV+ZbcsAkFWTktn5SmH40WgLtSNYN4m4W2v9ONT147PxBY/XrRhrWq8TlvObyUjQ==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.3.tgz", + "integrity": "sha512-1sCb0wBXnBIL16pfFG1Gkvei6UzvKyTNYpiC41yrdjEv0UoJoq9E/abTMzyYJ6JpTkAj15dLjbqifIzEBDVvog==", "requires": { - "browserslist": "^4.16.0", + "browserslist": "^4.16.1", "semver": "7.0.0" }, "dependencies": { @@ -4123,9 +4123,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.641", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.641.tgz", - "integrity": "sha512-b0DLhsHSHESC1I+Nx6n4w4Lr61chMd3m/av1rZQhS2IXTzaS5BMM5N+ldWdMIlni9CITMRM09m8He4+YV/92TA==" + "version": "1.3.642", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.642.tgz", + "integrity": "sha512-cev+jOrz/Zm1i+Yh334Hed6lQVOkkemk2wRozfMF4MtTR7pxf3r3L5Rbd7uX1zMcEqVJ7alJBnJL7+JffkC6FQ==" }, "elliptic": { "version": "6.5.3", @@ -6515,9 +6515,9 @@ } }, "node-releases": { - "version": "1.1.69", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.69.tgz", - "integrity": "sha512-DGIjo79VDEyAnRlfSqYTsy+yoHd2IOjJiKUozD2MV2D85Vso6Bug56mb9tT/fY5Urt0iqk01H7x+llAruDR2zA==" + "version": "1.1.70", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.70.tgz", + "integrity": "sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw==" }, "nopt": { "version": "1.0.10", diff --git a/docs/versions b/docs/versions index d4be17964..31c880a13 100644 --- a/docs/versions +++ b/docs/versions @@ -1,3 +1,3 @@ master master launchpad/backports v0.39 -release/v0.40.x v0.40 +release/v0.41.x v0.41 diff --git a/go.mod b/go.mod index f0dd2a1a9..e5d258eb4 100644 --- a/go.mod +++ b/go.mod @@ -28,17 +28,17 @@ require ( github.com/improbable-eng/grpc-web v0.14.0 github.com/magiconair/properties v1.8.4 github.com/mattn/go-isatty v0.0.12 - github.com/otiai10/copy v1.4.2 + github.com/otiai10/copy v1.5.0 github.com/pelletier/go-toml v1.8.1 // indirect github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.8.0 - github.com/prometheus/common v0.15.0 + github.com/prometheus/client_golang v1.9.0 + github.com/prometheus/common v0.17.0 github.com/rakyll/statik v0.1.7 github.com/regen-network/cosmos-proto v0.3.1 github.com/rs/zerolog v1.20.0 github.com/spf13/afero v1.3.4 // indirect github.com/spf13/cast v1.3.1 - github.com/spf13/cobra v1.1.2 + github.com/spf13/cobra v1.1.3 github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.7.1 @@ -47,11 +47,11 @@ require ( github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc2 github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 github.com/tendermint/go-amino v0.16.0 - github.com/tendermint/tendermint v0.34.3 + github.com/tendermint/tendermint v0.34.8 github.com/tendermint/tm-db v0.6.4 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f - google.golang.org/grpc v1.35.0 + google.golang.org/grpc v1.36.0 google.golang.org/protobuf v1.25.0 gopkg.in/ini.v1 v1.61.0 // indirect gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index b295f12d8..bf6adda80 100644 --- a/go.sum +++ b/go.sum @@ -505,8 +505,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/otiai10/copy v1.4.2 h1:RTiz2sol3eoXPLF4o+YWqEybwfUa/Q2Nkc4ZIUs3fwI= -github.com/otiai10/copy v1.4.2/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= +github.com/otiai10/copy v1.5.0 h1:SoXDGnlTUZoqB/wSuj/Y5L6T5i6iN4YRAcMCd+JnLNU= +github.com/otiai10/copy v1.5.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= @@ -546,6 +546,8 @@ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3O github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw= github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= +github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU= +github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -561,8 +563,9 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2 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= github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.17.0 h1:kDIZLI74SS+3tedSvEkykgBkD7txMxaJAPj8DtJUKYA= +github.com/prometheus/common v0.17.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -599,6 +602,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y= github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= +github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4= +github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -627,8 +632,8 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.1.2 h1:frHO75w/dH7kEc+e2KYZZKY4+PLrp39OqI77oB8m0KQ= -github.com/spf13/cobra v1.1.2/go.mod h1:ZjwqWkCg0LnXvLRIfTLdB4Y/MCO3gMHHJ2KFxQZy4xE= +github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -675,8 +680,8 @@ github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoM github.com/tendermint/tendermint v0.34.0-rc4/go.mod h1:yotsojf2C1QBOw4dZrTcxbyxmPUrT4hNuOQWX9XUwB4= github.com/tendermint/tendermint v0.34.0-rc6/go.mod h1:ugzyZO5foutZImv0Iyx/gOFCX6mjJTgbLHTwi17VDVg= github.com/tendermint/tendermint v0.34.0/go.mod h1:Aj3PIipBFSNO21r+Lq3TtzQ+uKESxkbA3yo/INM4QwQ= -github.com/tendermint/tendermint v0.34.3 h1:9yEsf3WO5VAwPVwrmM+RffDMiijmNfWaBwNttHm0q5w= -github.com/tendermint/tendermint v0.34.3/go.mod h1:h57vnXeOlrdvvNFCqPBSaOrpOivl+2swWEtlUAqStYE= +github.com/tendermint/tendermint v0.34.8 h1:PMWgUx47FrNTsfhxCWzoiIlVAC1SE9+WBlnsF9oQW0I= +github.com/tendermint/tendermint v0.34.8/go.mod h1:JVuu3V1ZexOaZG8VJMRl8lnfrGw6hEB2TVnoUwKRbss= github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI= github.com/tendermint/tm-db v0.6.3 h1:ZkhQcKnB8/2jr5EaZwGndN4owkPsGezW2fSisS9zGbg= github.com/tendermint/tm-db v0.6.3/go.mod h1:lfA1dL9/Y/Y8wwyPp2NMLyn5P5Ptr/gvDFNWtrCWSf8= @@ -859,6 +864,8 @@ golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/proto/cosmos/auth/v1beta1/query.proto b/proto/cosmos/auth/v1beta1/query.proto index a88579268..76d30dd61 100644 --- a/proto/cosmos/auth/v1beta1/query.proto +++ b/proto/cosmos/auth/v1beta1/query.proto @@ -1,6 +1,7 @@ syntax = "proto3"; package cosmos.auth.v1beta1; +import "cosmos/base/query/v1beta1/pagination.proto"; import "gogoproto/gogo.proto"; import "google/protobuf/any.proto"; import "google/api/annotations.proto"; @@ -11,6 +12,11 @@ option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types"; // Query defines the gRPC querier service. service Query { + // Accounts returns all the existing accounts + rpc Accounts(QueryAccountsRequest) returns (QueryAccountsResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/accounts"; + } + // Account returns account details based on address. rpc Account(QueryAccountRequest) returns (QueryAccountResponse) { option (google.api.http).get = "/cosmos/auth/v1beta1/accounts/{address}"; @@ -22,6 +28,21 @@ service Query { } } +// QueryAccountsRequest is the request type for the Query/Accounts RPC method. +message QueryAccountsRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryAccountsResponse is the response type for the Query/Accounts RPC method. +message QueryAccountsResponse { + // accounts are the existing accounts + repeated google.protobuf.Any accounts = 1 [(cosmos_proto.accepts_interface) = "AccountI"]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + // QueryAccountRequest is the request type for the Query/Account RPC method. message QueryAccountRequest { option (gogoproto.equal) = false; diff --git a/proto/cosmos/authz/v1beta1/authz.proto b/proto/cosmos/authz/v1beta1/authz.proto index 56c403a79..43078f866 100644 --- a/proto/cosmos/authz/v1beta1/authz.proto +++ b/proto/cosmos/authz/v1beta1/authz.proto @@ -1,7 +1,6 @@ syntax = "proto3"; package cosmos.authz.v1beta1; -import "cosmos/base/v1beta1/coin.proto"; import "cosmos_proto/cosmos.proto"; import "google/protobuf/timestamp.proto"; import "gogoproto/gogo.proto"; @@ -9,15 +8,6 @@ import "google/protobuf/any.proto"; option go_package = "github.com/cosmos/cosmos-sdk/x/authz/types"; -// SendAuthorization allows the grantee to spend up to spend_limit coins from -// the granter's account. -message SendAuthorization { - option (cosmos_proto.implements_interface) = "Authorization"; - - repeated cosmos.base.v1beta1.Coin spend_limit = 1 - [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; -} - // GenericAuthorization gives the grantee unrestricted permissions to execute // the provided method on behalf of the granter's account. message GenericAuthorization { diff --git a/proto/cosmos/authz/v1beta1/genesis.proto b/proto/cosmos/authz/v1beta1/genesis.proto index 940d42e1f..e593c43f2 100644 --- a/proto/cosmos/authz/v1beta1/genesis.proto +++ b/proto/cosmos/authz/v1beta1/genesis.proto @@ -5,7 +5,6 @@ import "google/protobuf/timestamp.proto"; import "google/protobuf/any.proto"; import "gogoproto/gogo.proto"; import "cosmos_proto/cosmos.proto"; -import "cosmos/authz/v1beta1/tx.proto"; option go_package = "github.com/cosmos/cosmos-sdk/x/authz/types"; diff --git a/proto/cosmos/authz/v1beta1/query.proto b/proto/cosmos/authz/v1beta1/query.proto index 8c7d50a8d..7a7332581 100644 --- a/proto/cosmos/authz/v1beta1/query.proto +++ b/proto/cosmos/authz/v1beta1/query.proto @@ -1,10 +1,7 @@ syntax = "proto3"; package cosmos.authz.v1beta1; -import "gogoproto/gogo.proto"; -import "google/protobuf/any.proto"; import "google/api/annotations.proto"; -import "cosmos_proto/cosmos.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; import "cosmos/authz/v1beta1/authz.proto"; diff --git a/proto/cosmos/bank/v1beta1/authz.proto b/proto/cosmos/bank/v1beta1/authz.proto new file mode 100644 index 000000000..f3505ad41 --- /dev/null +++ b/proto/cosmos/bank/v1beta1/authz.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package cosmos.bank.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/bank/types"; + +// SendAuthorization allows the grantee to spend up to spend_limit coins from +// the granter's account. +message SendAuthorization { + option (cosmos_proto.implements_interface) = "Authorization"; + + repeated cosmos.base.v1beta1.Coin spend_limit = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} diff --git a/proto/cosmos/bank/v1beta1/bank.proto b/proto/cosmos/bank/v1beta1/bank.proto index 5a9383362..fb9069214 100644 --- a/proto/cosmos/bank/v1beta1/bank.proto +++ b/proto/cosmos/bank/v1beta1/bank.proto @@ -82,4 +82,9 @@ message Metadata { // display indicates the suggested denom that should be // displayed in clients. string display = 4; + // name defines the name of the token (eg: Cosmos Atom) + string name = 5; + // symbol is the token symbol usually shown on exchanges (eg: ATOM). This can + // be the same as the display. + string symbol = 6; } diff --git a/proto/cosmos/crypto/ed25519/keys.proto b/proto/cosmos/crypto/ed25519/keys.proto index bed9c29cc..6ffec3448 100644 --- a/proto/cosmos/crypto/ed25519/keys.proto +++ b/proto/cosmos/crypto/ed25519/keys.proto @@ -5,18 +5,19 @@ import "gogoproto/gogo.proto"; option go_package = "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"; -// PubKey defines a ed25519 public key -// Key is the compressed form of the pubkey. The first byte depends is a 0x02 byte -// if the y-coordinate is the lexicographically largest of the two associated with -// the x-coordinate. Otherwise the first byte is a 0x03. -// This prefix is followed with the x-coordinate. +// PubKey is an ed25519 public key for handling Tendermint keys in SDK. +// It's needed for Any serialization and SDK compatibility. +// It must not be used in a non Tendermint key context because it doesn't implement +// ADR-28. Nevertheless, you will like to use ed25519 in app user level +// then you must create a new proto message and follow ADR-28 for Address construction. message PubKey { option (gogoproto.goproto_stringer) = false; bytes key = 1 [(gogoproto.casttype) = "crypto/ed25519.PublicKey"]; } -// PrivKey defines a ed25519 private key. +// Deprecated: PrivKey defines a ed25519 private key. +// NOTE: ed25519 keys must not be used in SDK apps except in a tendermint validator context. message PrivKey { bytes key = 1 [(gogoproto.casttype) = "crypto/ed25519.PrivateKey"]; } diff --git a/proto/cosmos/gov/v1beta1/gov.proto b/proto/cosmos/gov/v1beta1/gov.proto index 1d72e6432..392534c53 100644 --- a/proto/cosmos/gov/v1beta1/gov.proto +++ b/proto/cosmos/gov/v1beta1/gov.proto @@ -29,6 +29,16 @@ enum VoteOption { VOTE_OPTION_NO_WITH_VETO = 4 [(gogoproto.enumvalue_customname) = "OptionNoWithVeto"]; } +// WeightedVoteOption defines a unit of vote for vote split. +message WeightedVoteOption { + VoteOption option = 1; + string weight = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"weight\"" + ]; +} + // TextProposal defines a standard text proposal whose changes need to be // manually updated in case of approval. message TextProposal { @@ -119,9 +129,11 @@ message Vote { option (gogoproto.goproto_stringer) = false; option (gogoproto.equal) = false; - uint64 proposal_id = 1 [(gogoproto.moretags) = "yaml:\"proposal_id\""]; - string voter = 2; - VoteOption option = 3; + uint64 proposal_id = 1 [(gogoproto.moretags) = "yaml:\"proposal_id\""]; + string voter = 2; + reserved 3; + reserved "option"; + repeated WeightedVoteOption options = 4 [(gogoproto.nullable) = false]; } // DepositParams defines the params for deposits on governance proposals. diff --git a/proto/cosmos/gov/v1beta1/tx.proto b/proto/cosmos/gov/v1beta1/tx.proto index d4f0c1f99..8b68c4be4 100644 --- a/proto/cosmos/gov/v1beta1/tx.proto +++ b/proto/cosmos/gov/v1beta1/tx.proto @@ -17,6 +17,9 @@ service Msg { // Vote defines a method to add a vote on a specific proposal. rpc Vote(MsgVote) returns (MsgVoteResponse); + // WeightedVote defines a method to add a weighted vote on a specific proposal. + rpc VoteWeighted(MsgVoteWeighted) returns (MsgVoteWeightedResponse); + // Deposit defines a method to add deposit on a specific proposal. rpc Deposit(MsgDeposit) returns (MsgDepositResponse); } @@ -55,9 +58,24 @@ message MsgVote { VoteOption option = 3; } +// MsgVote defines a message to cast a vote. +message MsgVoteWeighted { + option (gogoproto.equal) = false; + option (gogoproto.goproto_stringer) = false; + option (gogoproto.stringer) = false; + option (gogoproto.goproto_getters) = false; + + uint64 proposal_id = 1 [(gogoproto.jsontag) = "proposal_id", (gogoproto.moretags) = "yaml:\"proposal_id\""]; + string voter = 2; + repeated WeightedVoteOption options = 3 [(gogoproto.nullable) = false]; +} + // MsgVoteResponse defines the Msg/Vote response type. message MsgVoteResponse {} +// MsgVoteWeightedResponse defines the Msg/VoteWeighted response type. +message MsgVoteWeightedResponse {} + // MsgDeposit defines a message to submit a deposit to an existing proposal. message MsgDeposit { option (gogoproto.equal) = false; diff --git a/proto/cosmos/staking/v1beta1/authz.proto b/proto/cosmos/staking/v1beta1/authz.proto new file mode 100644 index 000000000..6345612f2 --- /dev/null +++ b/proto/cosmos/staking/v1beta1/authz.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; +package cosmos.staking.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/staking/types"; + +// StakeAuthorization defines authorization for delegate/undelegate/redelegate. +message StakeAuthorization { + option (cosmos_proto.implements_interface) = "Authorization"; + + // max_tokens specifies the maximum amount of tokens can be delegate to a validator. If it is + // empty, there is no spend limit and any amount of coins can be delegated. + cosmos.base.v1beta1.Coin max_tokens = 1 [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coin"]; + // validators is the oneof that represents either allow_list or deny_list + oneof validators { + // allow_list specifies list of validator addresses to whom grantee can delegate tokens on behalf of granter's + // account. + Validators allow_list = 2; + // deny_list specifies list of validator addresses to whom grantee can not delegate tokens. + Validators deny_list = 3; + } + // Validators defines list of validator addresses. + message Validators { + repeated string address = 1; + } + // authorization_type defines one of AuthorizationType. + AuthorizationType authorization_type = 4; +} + +// AuthorizationType defines the type of staking module authorization type +enum AuthorizationType { + // AUTHORIZATION_TYPE_UNSPECIFIED specifies an unknown authorization type + AUTHORIZATION_TYPE_UNSPECIFIED = 0; + // AUTHORIZATION_TYPE_DELEGATE defines an authorization type for Msg/Delegate + AUTHORIZATION_TYPE_DELEGATE = 1; + // AUTHORIZATION_TYPE_UNDELEGATE defines an authorization type for Msg/Undelegate + AUTHORIZATION_TYPE_UNDELEGATE = 2; + // AUTHORIZATION_TYPE_REDELEGATE defines an authorization type for Msg/BeginRedelegate + AUTHORIZATION_TYPE_REDELEGATE = 3; +} diff --git a/proto/cosmos/staking/v1beta1/staking.proto b/proto/cosmos/staking/v1beta1/staking.proto index e37b28b6d..76e9599e2 100644 --- a/proto/cosmos/staking/v1beta1/staking.proto +++ b/proto/cosmos/staking/v1beta1/staking.proto @@ -28,7 +28,7 @@ message CommissionRates { option (gogoproto.goproto_stringer) = false; // rate is the commission rate charged to delegators, as a fraction. - string rate = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; + string rate = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; // max_rate defines the maximum commission rate which validator can ever charge, as a fraction. string max_rate = 2 [ (gogoproto.moretags) = "yaml:\"max_rate\"", @@ -49,9 +49,9 @@ message Commission { option (gogoproto.goproto_stringer) = false; // commission_rates defines the initial commission rates to be used for creating a validator. - CommissionRates commission_rates = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; + CommissionRates commission_rates = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; // update_time is the last time the commission rate was changed. - google.protobuf.Timestamp update_time = 2 + google.protobuf.Timestamp update_time = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"update_time\""]; } @@ -61,15 +61,15 @@ message Description { option (gogoproto.goproto_stringer) = false; // moniker defines a human-readable name for the validator. - string moniker = 1; + string moniker = 1; // identity defines an optional identity signature (ex. UPort or Keybase). - string identity = 2; + string identity = 2; // website defines an optional website link. - string website = 3; + string website = 3; // security_contact defines an optional email for security contact. string security_contact = 4 [(gogoproto.moretags) = "yaml:\"security_contact\""]; // details define other optional details. - string details = 5; + string details = 5; } // Validator defines a validator, together with the total amount of the @@ -86,12 +86,12 @@ message Validator { option (gogoproto.goproto_getters) = false; // operator_address defines the address of the validator's operator; bech encoded in JSON. - string operator_address = 1 [(gogoproto.moretags) = "yaml:\"operator_address\""]; + string operator_address = 1 [(gogoproto.moretags) = "yaml:\"operator_address\""]; // consensus_pubkey is the consensus public key of the validator, as a Protobuf Any. google.protobuf.Any consensus_pubkey = 2 [(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey", (gogoproto.moretags) = "yaml:\"consensus_pubkey\""]; // jailed defined whether the validator has been jailed from bonded status or not. - bool jailed = 3; + bool jailed = 3; // status is the validator status (bonded/unbonding/unbonded). BondStatus status = 4; // tokens define the delegated tokens (incl. self-delegation). @@ -103,16 +103,16 @@ message Validator { (gogoproto.nullable) = false ]; // description defines the description terms for the validator. - Description description = 7 [(gogoproto.nullable) = false]; + Description description = 7 [(gogoproto.nullable) = false]; // unbonding_height defines, if unbonding, the height at which this validator has begun unbonding. - int64 unbonding_height = 8 [(gogoproto.moretags) = "yaml:\"unbonding_height\""]; + int64 unbonding_height = 8 [(gogoproto.moretags) = "yaml:\"unbonding_height\""]; // unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. - google.protobuf.Timestamp unbonding_time = 9 + google.protobuf.Timestamp unbonding_time = 9 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"unbonding_time\""]; // commission defines the commission parameters. - Commission commission = 10 [(gogoproto.nullable) = false]; + Commission commission = 10 [(gogoproto.nullable) = false]; // min_self_delegation is the validator's self declared minimum self delegation. - string min_self_delegation = 11 [ + string min_self_delegation = 11 [ (gogoproto.moretags) = "yaml:\"min_self_delegation\"", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false @@ -201,9 +201,9 @@ message UnbondingDelegation { option (gogoproto.goproto_stringer) = false; // delegator_address is the bech32-encoded address of the delegator. - string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; // validator_address is the bech32-encoded address of the validator. - string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; // entries are the unbonding delegation entries. repeated UnbondingDelegationEntry entries = 3 [(gogoproto.nullable) = false]; // unbonding delegation entries } @@ -214,7 +214,7 @@ message UnbondingDelegationEntry { option (gogoproto.goproto_stringer) = false; // creation_height is the height which the unbonding took place. - int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""]; + int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""]; // completion_time is the unix time for unbonding completion. google.protobuf.Timestamp completion_time = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"completion_time\""]; @@ -234,7 +234,7 @@ message RedelegationEntry { option (gogoproto.goproto_stringer) = false; // creation_height defines the height which the redelegation took place. - int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""]; + int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""]; // completion_time defines the unix time for redelegation completion. google.protobuf.Timestamp completion_time = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"completion_time\""]; @@ -257,13 +257,13 @@ message Redelegation { option (gogoproto.goproto_stringer) = false; // delegator_address is the bech32-encoded address of the delegator. - string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; // validator_src_address is the validator redelegation source operator address. - string validator_src_address = 2 [(gogoproto.moretags) = "yaml:\"validator_src_address\""]; + string validator_src_address = 2 [(gogoproto.moretags) = "yaml:\"validator_src_address\""]; // validator_dst_address is the validator redelegation destination operator address. - string validator_dst_address = 3 [(gogoproto.moretags) = "yaml:\"validator_dst_address\""]; + string validator_dst_address = 3 [(gogoproto.moretags) = "yaml:\"validator_dst_address\""]; // entries are the redelegation entries. - repeated RedelegationEntry entries = 4 [(gogoproto.nullable) = false]; // redelegation entries + repeated RedelegationEntry entries = 4 [(gogoproto.nullable) = false]; // redelegation entries } // Params defines the parameters for the staking module. @@ -275,13 +275,13 @@ message Params { google.protobuf.Duration unbonding_time = 1 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (gogoproto.moretags) = "yaml:\"unbonding_time\""]; // max_validators is the maximum number of validators. - uint32 max_validators = 2 [(gogoproto.moretags) = "yaml:\"max_validators\""]; + uint32 max_validators = 2 [(gogoproto.moretags) = "yaml:\"max_validators\""]; // max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio). - uint32 max_entries = 3 [(gogoproto.moretags) = "yaml:\"max_entries\""]; + uint32 max_entries = 3 [(gogoproto.moretags) = "yaml:\"max_entries\""]; // historical_entries is the number of historical entries to persist. uint32 historical_entries = 4 [(gogoproto.moretags) = "yaml:\"historical_entries\""]; // bond_denom defines the bondable coin denomination. - string bond_denom = 5 [(gogoproto.moretags) = "yaml:\"bond_denom\""]; + string bond_denom = 5 [(gogoproto.moretags) = "yaml:\"bond_denom\""]; } // DelegationResponse is equivalent to Delegation except that it contains a diff --git a/proto/ibc/core/client/v1/client.proto b/proto/ibc/core/client/v1/client.proto index 11d2195aa..4c6308bd5 100644 --- a/proto/ibc/core/client/v1/client.proto +++ b/proto/ibc/core/client/v1/client.proto @@ -33,9 +33,11 @@ message ClientConsensusStates { [(gogoproto.moretags) = "yaml:\"consensus_states\"", (gogoproto.nullable) = false]; } -// ClientUpdateProposal is a governance proposal. If it passes, the client is -// updated with the provided header. The update may fail if the header is not -// valid given certain conditions specified by the client implementation. +// ClientUpdateProposal is a governance proposal. If it passes, the substitute client's +// consensus states starting from the 'initial height' are copied over to the subjects +// client state. The proposal handler may fail if the subject and the substitute do not +// match in client and chain parameters (with exception to latest height, frozen height, and chain-id). +// The updated client must also be valid (cannot be expired). message ClientUpdateProposal { option (gogoproto.goproto_getters) = false; // the title of the update proposal @@ -43,9 +45,11 @@ message ClientUpdateProposal { // the description of the proposal string description = 2; // the client identifier for the client to be updated if the proposal passes - string client_id = 3 [(gogoproto.moretags) = "yaml:\"client_id\""]; - // the header used to update the client if the proposal passes - google.protobuf.Any header = 4; + string subject_client_id = 3 [(gogoproto.moretags) = "yaml:\"subject_client_id\""]; + // the substitute client identifier for the client standing in for the subject client + string substitute_client_id = 4 [(gogoproto.moretags) = "yaml:\"susbtitute_client_id\""]; + // the intital height to copy consensus states from the substitute to the subject + Height initial_height = 5 [(gogoproto.moretags) = "yaml:\"initial_height\"", (gogoproto.nullable) = false]; } // Height is a monotonically increasing data type diff --git a/scripts/protoc-swagger-gen.sh b/scripts/protoc-swagger-gen.sh index bf62cd31e..d1d2bf96e 100755 --- a/scripts/protoc-swagger-gen.sh +++ b/scripts/protoc-swagger-gen.sh @@ -10,11 +10,11 @@ for dir in $proto_dirs; do query_file=$(find "${dir}" -maxdepth 1 \( -name 'query.proto' -o -name 'service.proto' \)) if [[ ! -z "$query_file" ]]; then buf protoc \ - -I "proto" \ - -I "third_party/proto" \ - "$query_file" \ - --swagger_out=./tmp-swagger-gen \ - --swagger_opt=logtostderr=true --swagger_opt=fqn_for_swagger_name=true --swagger_opt=simple_operation_ids=true + -I "proto" \ + -I "third_party/proto" \ + "$query_file" \ + --swagger_out=./tmp-swagger-gen \ + --swagger_opt=logtostderr=true --swagger_opt=fqn_for_swagger_name=true --swagger_opt=simple_operation_ids=true fi done diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index bd3e40097..20b8d7020 100755 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -16,22 +16,22 @@ protoc_gen_gocosmos proto_dirs=$(find ./proto -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) for dir in $proto_dirs; do buf protoc \ - -I "proto" \ - -I "third_party/proto" \ - --gocosmos_out=plugins=interfacetype+grpc,\ + -I "proto" \ + -I "third_party/proto" \ + --gocosmos_out=plugins=interfacetype+grpc,\ Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types:. \ - --grpc-gateway_out=logtostderr=true:. \ + --grpc-gateway_out=logtostderr=true,allow_colon_final_segments=true:. \ $(find "${dir}" -maxdepth 1 -name '*.proto') done # command to generate docs using protoc-gen-doc buf protoc \ --I "proto" \ --I "third_party/proto" \ ---doc_out=./docs/core \ ---doc_opt=./docs/protodoc-markdown.tmpl,proto-docs.md \ -$(find "$(pwd)/proto" -maxdepth 5 -name '*.proto') + -I "proto" \ + -I "third_party/proto" \ + --doc_out=./docs/core \ + --doc_opt=./docs/protodoc-markdown.tmpl,proto-docs.md \ + $(find "$(pwd)/proto" -maxdepth 5 -name '*.proto') go mod tidy # generate codec/testdata proto code diff --git a/server/grpc/server.go b/server/grpc/server.go index f03c1d87b..d9d0be1a0 100644 --- a/server/grpc/server.go +++ b/server/grpc/server.go @@ -8,13 +8,14 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/reflection" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server/types" ) // StartGRPCServer starts a gRPC server on the given address. -func StartGRPCServer(app types.Application, address string) (*grpc.Server, error) { +func StartGRPCServer(clientCtx client.Context, app types.Application, address string) (*grpc.Server, error) { grpcSrv := grpc.NewServer() - app.RegisterGRPCServer(grpcSrv) + app.RegisterGRPCServer(clientCtx, grpcSrv) // Reflection allows external clients to see what services and methods // the gRPC server exposes. diff --git a/server/grpc/server_test.go b/server/grpc/server_test.go index 846ce31a5..6766d9c97 100644 --- a/server/grpc/server_test.go +++ b/server/grpc/server_test.go @@ -13,15 +13,13 @@ import ( "google.golang.org/grpc/metadata" rpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha" - clienttx "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/testutil/network" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" "github.com/cosmos/cosmos-sdk/types/tx" txtypes "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -37,6 +35,7 @@ func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") s.cfg = network.DefaultConfig() + s.cfg.NumValidators = 1 s.network = network.New(s.T(), s.cfg) s.Require().NotNil(s.network) @@ -92,7 +91,7 @@ func (s *IntegrationTestSuite) TestGRPCServer_BankBalance() { grpc.Header(&header), ) blockHeight = header.Get(grpctypes.GRPCBlockHeightHeader) - s.Require().Equal([]string{"1"}, blockHeight) + s.Require().NotEmpty(blockHeight[0]) // blockHeight is []string, first element is block height. } func (s *IntegrationTestSuite) TestGRPCServer_Reflection() { @@ -124,50 +123,15 @@ func (s *IntegrationTestSuite) TestGRPCServer_GetTxsEvent() { Events: []string{"message.action=send"}, }, ) - // TODO Once https://github.com/cosmos/cosmos-sdk/pull/8029 is merged, this - // should not error anymore. s.Require().NoError(err) } func (s *IntegrationTestSuite) TestGRPCServer_BroadcastTx() { val0 := s.network.Validators[0] - // prepare txBuilder with msg - txBuilder := val0.ClientCtx.TxConfig.NewTxBuilder() - feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)} - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError( - txBuilder.SetMsgs(&banktypes.MsgSend{ - FromAddress: val0.Address.String(), - ToAddress: val0.Address.String(), - Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, - }), - ) - txBuilder.SetFeeAmount(feeAmount) - txBuilder.SetGasLimit(gasLimit) - - // setup txFactory - txFactory := clienttx.Factory{}. - WithChainID(val0.ClientCtx.ChainID). - WithKeybase(val0.ClientCtx.Keyring). - WithTxConfig(val0.ClientCtx.TxConfig). - WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) - - // Sign Tx. - err := authclient.SignTx(txFactory, val0.ClientCtx, val0.Moniker, txBuilder, false, true) - s.Require().NoError(err) - - txBytes, err := val0.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) - s.Require().NoError(err) - - // Broadcast the tx via gRPC. - queryClient := tx.NewServiceClient(s.conn) - grpcRes, err := queryClient.BroadcastTx( - context.Background(), - &tx.BroadcastTxRequest{ - Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, - TxBytes: txBytes, - }, + grpcRes, err := banktestutil.LegacyGRPCProtoMsgSend(val0.ClientCtx, + val0.Moniker, val0.Address, val0.Address, + sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, ) s.Require().NoError(err) s.Require().Equal(uint32(0), grpcRes.TxResponse.Code) @@ -185,9 +149,9 @@ func (s *IntegrationTestSuite) TestGRPCServerInvalidHeaderHeights() { value string wantErr string }{ - {"-1", "height < 0"}, + {"-1", "\"x-cosmos-block-height\" must be >= 0"}, {"9223372036854775808", "value out of range"}, // > max(int64) by 1 - {"-10", "height < 0"}, + {"-10", "\"x-cosmos-block-height\" must be >= 0"}, {"18446744073709551615", "value out of range"}, // max uint64, which is > max(int64) {"-9223372036854775809", "value out of range"}, // Out of the range of for negative int64 } diff --git a/server/init.go b/server/init.go index f14e236cd..389b231e9 100644 --- a/server/init.go +++ b/server/init.go @@ -12,7 +12,7 @@ import ( // phrase to recover the private key. func GenerateCoinKey(algo keyring.SignatureAlgo) (sdk.AccAddress, string, error) { // generate a private key, with recovery phrase - info, secret, err := keyring.NewInMemory().NewMnemonic("name", keyring.English, sdk.FullFundraiserPath, algo) + info, secret, err := keyring.NewInMemory().NewMnemonic("name", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, algo) if err != nil { return sdk.AccAddress([]byte{}), "", err } @@ -43,7 +43,7 @@ func GenerateSaveCoinKey(keybase keyring.Keyring, keyName string, overwrite bool } } - info, secret, err := keybase.NewMnemonic(keyName, keyring.English, sdk.FullFundraiserPath, algo) + info, secret, err := keybase.NewMnemonic(keyName, keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, algo) if err != nil { return sdk.AccAddress([]byte{}), "", err } diff --git a/server/start.go b/server/start.go index 764a67be5..eef353fa7 100644 --- a/server/start.go +++ b/server/start.go @@ -318,7 +318,7 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App grpcWebSrv *http.Server ) if config.GRPC.Enable { - grpcSrv, err = servergrpc.StartGRPCServer(app, config.GRPC.Address) + grpcSrv, err = servergrpc.StartGRPCServer(clientCtx, app, config.GRPC.Address) if err != nil { return err } diff --git a/server/types/app.go b/server/types/app.go index bac708729..ba702074b 100644 --- a/server/types/app.go +++ b/server/types/app.go @@ -38,7 +38,7 @@ type ( // RegisterGRPCServer registers gRPC services directly with the gRPC // server. - RegisterGRPCServer(grpc.Server) + RegisterGRPCServer(client.Context, grpc.Server) // RegisterTxService registers the gRPC Query service for tx (such as tx // simulation, fetching txs by hash...). diff --git a/server/util.go b/server/util.go index b431b715d..68f1c4828 100644 --- a/server/util.go +++ b/server/util.go @@ -184,11 +184,11 @@ func SetCmdServerContext(cmd *cobra.Command, serverCtx *Context) error { func interceptConfigs(rootViper *viper.Viper) (*tmcfg.Config, error) { rootDir := rootViper.GetString(flags.FlagHome) configPath := filepath.Join(rootDir, "config") - configFile := filepath.Join(configPath, "config.toml") + tmCfgFile := filepath.Join(configPath, "config.toml") conf := tmcfg.DefaultConfig() - switch _, err := os.Stat(configFile); { + switch _, err := os.Stat(tmCfgFile); { case os.IsNotExist(err): tmcfg.EnsureRoot(rootDir) @@ -200,7 +200,7 @@ func interceptConfigs(rootViper *viper.Viper) (*tmcfg.Config, error) { conf.P2P.RecvRate = 5120000 conf.P2P.SendRate = 5120000 conf.Consensus.TimeoutCommit = 5 * time.Second - tmcfg.WriteConfigFile(configFile, conf) + tmcfg.WriteConfigFile(tmCfgFile, conf) case err != nil: return nil, err @@ -209,34 +209,37 @@ func interceptConfigs(rootViper *viper.Viper) (*tmcfg.Config, error) { rootViper.SetConfigType("toml") rootViper.SetConfigName("config") rootViper.AddConfigPath(configPath) + if err := rootViper.ReadInConfig(); err != nil { - return nil, fmt.Errorf("failed to read in app.toml: %w", err) + return nil, fmt.Errorf("failed to read in %s: %w", tmCfgFile, err) } } - // Read into the configuration whatever data the viper instance has for it - // This may come from the configuration file above but also any of the other sources - // viper uses + // Read into the configuration whatever data the viper instance has for it. + // This may come from the configuration file above but also any of the other + // sources viper uses. if err := rootViper.Unmarshal(conf); err != nil { return nil, err } + conf.SetRoot(rootDir) - appConfigFilePath := filepath.Join(configPath, "app.toml") - if _, err := os.Stat(appConfigFilePath); os.IsNotExist(err) { + appCfgFilePath := filepath.Join(configPath, "app.toml") + if _, err := os.Stat(appCfgFilePath); os.IsNotExist(err) { appConf, err := config.ParseConfig(rootViper) if err != nil { - return nil, fmt.Errorf("failed to parse app.toml: %w", err) + return nil, fmt.Errorf("failed to parse %s: %w", appCfgFilePath, err) } - config.WriteConfigFile(appConfigFilePath, appConf) + config.WriteConfigFile(appCfgFilePath, appConf) } rootViper.SetConfigType("toml") rootViper.SetConfigName("app") rootViper.AddConfigPath(configPath) - if err := rootViper.ReadInConfig(); err != nil { - return nil, fmt.Errorf("failed to read in app.toml: %w", err) + + if err := rootViper.MergeInConfig(); err != nil { + return nil, fmt.Errorf("failed to merge configuration: %w", err) } return conf, nil diff --git a/server/util_test.go b/server/util_test.go index a0ff6479b..0800b59a3 100644 --- a/server/util_test.go +++ b/server/util_test.go @@ -162,76 +162,6 @@ func TestInterceptConfigsPreRunHandlerReadsAppToml(t *testing.T) { } } -func TestInterceptConfigsPreRunHandlerDoesNotMixConfigFiles(t *testing.T) { - // The goal of this test is to make sure that app.toml and config.toml - // are separate files and that mixing values does not work - const testDbBackend = "awesome_test_db" - const testHaltTime = 1337 - const testHaltHeight = 2001 - - tempDir := t.TempDir() - err := os.Mkdir(path.Join(tempDir, "config"), os.ModePerm) - if err != nil { - t.Fatalf("creating config dir failed: %v", err) - } - configTomlPath := path.Join(tempDir, "config", "config.toml") - writer, err := os.Create(configTomlPath) - if err != nil { - t.Fatalf("creating config.toml file failed: %v", err) - } - - // Put a value in config.toml that should be in app.toml - _, err = writer.WriteString(fmt.Sprintf("halt-time = %d\ndb_backend = \"%s\"\n", testHaltTime, testDbBackend)) - if err != nil { - t.Fatalf("Failed writing string to config.toml: %v", err) - } - - if err := writer.Close(); err != nil { - t.Fatalf("Failed closing config.toml: %v", err) - } - - appTomlPath := path.Join(tempDir, "config", "app.toml") - writer, err = os.Create(appTomlPath) - if err != nil { - t.Fatalf("creating app.toml file failed %v", err) - } - - // Put a different value in app.toml - _, err = writer.WriteString(fmt.Sprintf("halt-height = %d\n", testHaltHeight)) - if err != nil { - t.Fatalf("Failed writing string to app.toml: %v", err) - } - - if err := writer.Close(); err != nil { - t.Fatalf("Failed closing app.toml: %v", err) - } - - cmd := StartCmd(nil, tempDir) - cmd.PreRunE = preRunETestImpl - - serverCtx := &Context{} - ctx := context.WithValue(context.Background(), ServerContextKey, serverCtx) - - if err := cmd.ExecuteContext(ctx); err != CancelledInPreRun { - t.Fatalf("function failed with [%T] %v", err, err) - } - - // check that the intended value from config.toml is used - if testDbBackend != serverCtx.Config.DBBackend { - t.Error("DBPath was not set from config.toml") - } - - // The value from app.toml should be used for this - if testHaltHeight != serverCtx.Viper.GetInt("halt-height") { - t.Error("Halt height is not using provided value") - } - - // The value from config.toml should not be used, default is used instead - if 0 != serverCtx.Viper.GetInt("halt-time") { - t.Error("Halt time is not using default") - } -} - func TestInterceptConfigsPreRunHandlerReadsFlags(t *testing.T) { const testAddr = "tcp://127.1.2.3:12345" tempDir := t.TempDir() diff --git a/simapp/app.go b/simapp/app.go index 4db7d53d7..41376c8ee 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -1,6 +1,7 @@ package simapp import ( + "encoding/json" "io" "net/http" "os" @@ -10,7 +11,6 @@ import ( "github.com/rakyll/statik/fs" "github.com/spf13/cast" abci "github.com/tendermint/tendermint/abci/types" - tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -69,6 +69,7 @@ import ( ibctransfertypes "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" ibc "github.com/cosmos/cosmos-sdk/x/ibc/core" ibcclient "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client" + ibcclienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" porttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/types" ibchost "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" ibckeeper "github.com/cosmos/cosmos-sdk/x/ibc/core/keeper" @@ -309,7 +310,7 @@ func NewSimApp( AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). AddRoute(distrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper)). AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)). - AddRoute(ibchost.RouterKey, ibcclient.NewClientUpdateProposalHandler(app.IBCKeeper.ClientKeeper)) + AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientUpdateProposalHandler(app.IBCKeeper.ClientKeeper)) app.GovKeeper = govkeeper.NewKeeper( appCodec, keys[govtypes.StoreKey], app.GetSubspace(govtypes.ModuleName), app.AccountKeeper, app.BankKeeper, &stakingKeeper, govRouter, @@ -483,7 +484,7 @@ func (app *SimApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.Re // InitChainer application update at chain initialization func (app *SimApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { var genesisState GenesisState - if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil { + if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil { panic(err) } return app.mm.InitGenesis(ctx, app.appCodec, genesisState) diff --git a/simapp/app_test.go b/simapp/app_test.go index 2adcf2e7e..8f596f5ec 100644 --- a/simapp/app_test.go +++ b/simapp/app_test.go @@ -11,8 +11,27 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/vesting" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/capability" + "github.com/cosmos/cosmos-sdk/x/crisis" + "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/evidence" + feegrant "github.com/cosmos/cosmos-sdk/x/feegrant" + "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/cosmos/cosmos-sdk/x/gov" + transfer "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer" + ibc "github.com/cosmos/cosmos-sdk/x/ibc/core" + "github.com/cosmos/cosmos-sdk/x/mint" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/upgrade" ) func TestSimAppExportAndBlockedAddrs(t *testing.T) { @@ -52,11 +71,33 @@ func TestGetMaccPerms(t *testing.T) { func TestRunMigrations(t *testing.T) { db := dbm.NewMemDB() encCfg := MakeTestEncodingConfig() - app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{}) + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{}) - // Create a new configurator for the purpose of this test. + // Create a new baseapp and configurator for the purpose of this test. + bApp := baseapp.NewBaseApp(appName, logger, db, encCfg.TxConfig.TxDecoder()) + bApp.SetCommitMultiStoreTracer(nil) + bApp.SetInterfaceRegistry(encCfg.InterfaceRegistry) + app.BaseApp = bApp app.configurator = module.NewConfigurator(app.MsgServiceRouter(), app.GRPCQueryRouter()) + // We register all modules on the Configurator, except x/bank. x/bank will + // serve as the test subject on which we run the migration tests. + // + // The loop below is the same as calling `RegisterServices` on + // ModuleManager, except that we skip x/bank. + for _, module := range app.mm.Modules { + if module.Name() == banktypes.ModuleName { + continue + } + + module.RegisterServices(app.configurator) + } + + // Initialize the chain + app.InitChain(abci.RequestInitChain{}) + app.Commit() + testCases := []struct { name string moduleName string @@ -94,7 +135,7 @@ func TestRunMigrations(t *testing.T) { var err error // Since it's very hard to test actual in-place store migrations in - // tests (due to the difficulty of maintaing multiple versions of a + // tests (due to the difficulty of maintaining multiple versions of a // module), we're just testing here that the migration logic is // called. called := 0 @@ -115,12 +156,30 @@ func TestRunMigrations(t *testing.T) { } require.NoError(t, err) + // Run migrations only for bank. That's why we put the initial + // version for bank as 1, and for all other modules, we put as + // their latest ConsensusVersion. err = app.RunMigrations( app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}), module.MigrationMap{ - "auth": 1, "authz": 1, "bank": 1, "staking": 1, "mint": 1, "distribution": 1, - "slashing": 1, "gov": 1, "params": 1, "ibc": 1, "upgrade": 1, "vesting": 1, - "feegrant": 1, "transfer": 1, "evidence": 1, "crisis": 1, "genutil": 1, "capability": 1, + "bank": 1, + "auth": auth.AppModule{}.ConsensusVersion(), + "authz": authz.AppModule{}.ConsensusVersion(), + "staking": staking.AppModule{}.ConsensusVersion(), + "mint": mint.AppModule{}.ConsensusVersion(), + "distribution": distribution.AppModule{}.ConsensusVersion(), + "slashing": slashing.AppModule{}.ConsensusVersion(), + "gov": gov.AppModule{}.ConsensusVersion(), + "params": params.AppModule{}.ConsensusVersion(), + "ibc": ibc.AppModule{}.ConsensusVersion(), + "upgrade": upgrade.AppModule{}.ConsensusVersion(), + "vesting": vesting.AppModule{}.ConsensusVersion(), + "feegrant": feegrant.AppModule{}.ConsensusVersion(), + "transfer": transfer.AppModule{}.ConsensusVersion(), + "evidence": evidence.AppModule{}.ConsensusVersion(), + "crisis": crisis.AppModule{}.ConsensusVersion(), + "genutil": genutil.AppModule{}.ConsensusVersion(), + "capability": capability.AppModule{}.ConsensusVersion(), }, ) if tc.expRunErr { diff --git a/simapp/params/weights.go b/simapp/params/weights.go index a7ea85ef7..81400a2fc 100644 --- a/simapp/params/weights.go +++ b/simapp/params/weights.go @@ -10,6 +10,7 @@ const ( DefaultWeightMsgFundCommunityPool int = 50 DefaultWeightMsgDeposit int = 100 DefaultWeightMsgVote int = 67 + DefaultWeightMsgVoteWeighted int = 33 DefaultWeightMsgUnjail int = 100 DefaultWeightMsgCreateValidator int = 100 DefaultWeightMsgEditValidator int = 5 diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index f76948d59..836036bcf 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -136,6 +136,7 @@ func txCommand() *cobra.Command { authcmd.GetSignCommand(), authcmd.GetSignBatchCommand(), authcmd.GetMultiSignCommand(), + authcmd.GetMultiSignBatchCmd(), authcmd.GetValidateSignaturesCommand(), flags.LineBreak, authcmd.GetBroadcastCommand(), diff --git a/simapp/state.go b/simapp/state.go index 4c3773813..e90bb0257 100644 --- a/simapp/state.go +++ b/simapp/state.go @@ -14,9 +14,12 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // AppStateFn returns the initial application state using a genesis or the simulation parameters. @@ -68,6 +71,57 @@ func AppStateFn(cdc codec.JSONMarshaler, simManager *module.SimulationManager) s appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) } + rawState := make(map[string]json.RawMessage) + err := json.Unmarshal(appState, &rawState) + if err != nil { + panic(err) + } + + stakingStateBz, ok := rawState[stakingtypes.ModuleName] + if !ok { + panic("staking genesis state is missing") + } + + stakingState := new(stakingtypes.GenesisState) + err = cdc.UnmarshalJSON(stakingStateBz, stakingState) + if err != nil { + panic(err) + } + // compute not bonded balance + notBondedTokens := sdk.ZeroInt() + for _, val := range stakingState.Validators { + if val.Status != stakingtypes.Unbonded { + continue + } + notBondedTokens = notBondedTokens.Add(val.GetTokens()) + } + notBondedCoins := sdk.NewCoin(stakingState.Params.BondDenom, notBondedTokens) + // edit bank state to make it have the not bonded pool tokens + bankStateBz, ok := rawState[banktypes.ModuleName] + // TODO(fdymylja/jonathan): should we panic in this case + if !ok { + panic("bank genesis state is missing") + } + bankState := new(banktypes.GenesisState) + err = cdc.UnmarshalJSON(bankStateBz, bankState) + if err != nil { + panic(err) + } + + bankState.Balances = append(bankState.Balances, banktypes.Balance{ + Address: authtypes.NewModuleAddress(stakingtypes.NotBondedPoolName).String(), + Coins: sdk.NewCoins(notBondedCoins), + }) + + // change appState back + rawState[stakingtypes.ModuleName] = cdc.MustMarshalJSON(stakingState) + rawState[banktypes.ModuleName] = cdc.MustMarshalJSON(bankState) + + // replace appstate + appState, err = json.Marshal(rawState) + if err != nil { + panic(err) + } return appState, simAccs, chainID, genesisTimestamp } } diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index 83f2ea453..df80e4fcc 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -27,6 +27,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/errors" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -119,7 +120,6 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) } - // set validators and delegations stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis) @@ -130,6 +130,12 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs totalSupply = totalSupply.Add(b.Coins.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt))...) } + // add bonded amount to bonded pool module account + balances = append(balances, banktypes.Balance{ + Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), + Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, + }) + // update total supply bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}) genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) @@ -213,9 +219,9 @@ func createIncrementalAccounts(accNum int) []sdk.AccAddress { // start at 100 so we can make up to 999 test addresses with valid test addresses for i := 100; i < (accNum + 100); i++ { numString := strconv.Itoa(i) - buffer.WriteString("A58856F0FD53BF058B4909A21AEC019107BA6") //base address string + buffer.WriteString("A58856F0FD53BF058B4909A21AEC019107BA6") // base address string - buffer.WriteString(numString) //adding on final two digits to make addresses unique + buffer.WriteString(numString) // adding on final two digits to make addresses unique res, _ := sdk.AccAddressFromHex(buffer.String()) bech := res.String() addr, _ := TestAddr(buffer.String(), bech) @@ -231,21 +237,11 @@ func createIncrementalAccounts(accNum int) []sdk.AccAddress { func AddTestAddrsFromPubKeys(app *SimApp, ctx sdk.Context, pubKeys []cryptotypes.PubKey, accAmt sdk.Int) { initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) - setTotalSupply(app, ctx, accAmt, len(pubKeys)) - - // fill all the addresses with some coins, set the loose pool tokens simultaneously - for _, pubKey := range pubKeys { - saveAccount(app, ctx, sdk.AccAddress(pubKey.Address()), initCoins) + for _, pk := range pubKeys { + initAccountWithCoins(app, ctx, sdk.AccAddress(pk.Address()), initCoins) } } -// setTotalSupply provides the total supply based on accAmt * totalAccounts. -func setTotalSupply(app *SimApp, ctx sdk.Context, accAmt sdk.Int, totalAccounts int) { - totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt.MulRaw(int64(totalAccounts)))) - prevSupply := app.BankKeeper.GetSupply(ctx) - app.BankKeeper.SetSupply(ctx, banktypes.NewSupply(prevSupply.GetTotal().Add(totalSupply...))) -} - // AddTestAddrs constructs and returns accNum amount of accounts with an // initial balance of accAmt in random order func AddTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int) []sdk.AccAddress { @@ -262,21 +258,21 @@ func addTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int, stra testAddrs := strategy(accNum) initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) - setTotalSupply(app, ctx, accAmt, accNum) - // fill all the addresses with some coins, set the loose pool tokens simultaneously for _, addr := range testAddrs { - saveAccount(app, ctx, addr, initCoins) + initAccountWithCoins(app, ctx, addr, initCoins) } return testAddrs } -// saveAccount saves the provided account into the simapp with balance based on initCoins. -func saveAccount(app *SimApp, ctx sdk.Context, addr sdk.AccAddress, initCoins sdk.Coins) { - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - app.AccountKeeper.SetAccount(ctx, acc) - err := app.BankKeeper.AddCoins(ctx, addr, initCoins) +func initAccountWithCoins(app *SimApp, ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins) { + err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, coins) + if err != nil { + panic(err) + } + + err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, coins) if err != nil { panic(err) } @@ -440,3 +436,13 @@ type EmptyAppOptions struct{} func (ao EmptyAppOptions) Get(o string) interface{} { return nil } + +// FundAccount is a utility function that funds an account by minting and sending the coins to the address +// TODO(fdymylja): instead of using the mint module account, which has the permission of minting, create a "faucet" account +func FundAccount(app *SimApp, ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins) error { + err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts) + if err != nil { + return err + } + return app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, amounts) +} diff --git a/store/gaskv/store.go b/store/gaskv/store.go index d5c1f86c2..06b55f5ea 100644 --- a/store/gaskv/store.go +++ b/store/gaskv/store.go @@ -19,7 +19,6 @@ type Store struct { } // NewStore returns a reference to a new GasKVStore. -// nolint func NewStore(parent types.KVStore, gasMeter types.GasMeter, gasConfig types.GasConfig) *Store { kvs := &Store{ gasMeter: gasMeter, diff --git a/testutil/network/network.go b/testutil/network/network.go index c73b69d18..2475e36e0 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -242,7 +242,7 @@ func New(t *testing.T, cfg Config) *Network { logger := log.NewNopLogger() if cfg.EnableLogging { logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) - logger, _ = tmflags.ParseLogLevel("info", logger, tmcfg.DefaultLogLevel()) + logger, _ = tmflags.ParseLogLevel("info", logger, tmcfg.DefaultLogLevel) } ctx.Logger = logger diff --git a/testutil/network/util.go b/testutil/network/util.go index 3a6603917..4f36d4640 100644 --- a/testutil/network/util.go +++ b/testutil/network/util.go @@ -93,7 +93,7 @@ func startInProcess(cfg Config, val *Validator) error { } if val.AppConfig.GRPC.Enable { - grpcSrv, err := servergrpc.StartGRPCServer(app, val.AppConfig.GRPC.Address) + grpcSrv, err := servergrpc.StartGRPCServer(val.ClientCtx, app, val.AppConfig.GRPC.Address) if err != nil { return err } diff --git a/types/address.go b/types/address.go index eabff6fa9..8e6a8103f 100644 --- a/types/address.go +++ b/types/address.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "strings" + "sync" yaml "gopkg.in/yaml.v2" @@ -26,13 +27,16 @@ const ( // config.SetBech32PrefixForAccount(yourBech32PrefixAccAddr, yourBech32PrefixAccPub) // config.SetBech32PrefixForValidator(yourBech32PrefixValAddr, yourBech32PrefixValPub) // config.SetBech32PrefixForConsensusNode(yourBech32PrefixConsAddr, yourBech32PrefixConsPub) + // config.SetPurpose(yourPurpose) // config.SetCoinType(yourCoinType) - // config.SetFullFundraiserPath(yourFullFundraiserPath) // config.Seal() // Bech32MainPrefix defines the main SDK Bech32 prefix of an account's address Bech32MainPrefix = "cosmos" + // Purpose is the ATOM purpose as defined in SLIP44 (https://github.com/satoshilabs/slips/blob/master/slip-0044.md) + Purpose = 44 + // CoinType is the ATOM coin type as defined in SLIP44 (https://github.com/satoshilabs/slips/blob/master/slip-0044.md) CoinType = 118 @@ -233,8 +237,28 @@ func (aa AccAddress) Bytes() []byte { return aa } +// AccAddress.String() is expensive and if unoptimized dominantly showed up in profiles, +// yet has no mechanisms to trivially cache the result given that AccAddress is a []byte type. +// With the string interning below, we are able to achieve zero cost allocations for string->[]byte +// because the Go compiler recognizes the special case of map[string([]byte)] when used exactly +// in that pattern. See https://github.com/cosmos/cosmos-sdk/issues/8693. +var addMu sync.Mutex +var addrStrMemoize = make(map[string]string) + // String implements the Stringer interface. -func (aa AccAddress) String() string { +func (aa AccAddress) String() (str string) { + addMu.Lock() + defer addMu.Unlock() + + if str, ok := addrStrMemoize[string(aa)]; ok { + return str + } + + // Otherwise cache it for later memoization. + defer func() { + addrStrMemoize[string(aa)] = str + }() + if aa.Empty() { return "" } diff --git a/types/address/README.md b/types/address/README.md new file mode 100644 index 000000000..ebc647ec0 --- /dev/null +++ b/types/address/README.md @@ -0,0 +1,7 @@ +# Account + +This package defines Cosmos SDK address related functions. + +## References + ++ [ADR-028](../../docs/architecture/adr-028-public-key-addresses.md) diff --git a/types/address/hash.go b/types/address/hash.go new file mode 100644 index 000000000..ddfec81df --- /dev/null +++ b/types/address/hash.go @@ -0,0 +1,73 @@ +package address + +import ( + "bytes" + "crypto/sha256" + "fmt" + "reflect" + "sort" + "unsafe" + + "github.com/cosmos/cosmos-sdk/types/errors" +) + +// Len is the length of base addresses +const Len = sha256.Size + +type Addressable interface { + Address() []byte +} + +// Hash creates a new address from address type and key +func Hash(typ string, key []byte) []byte { + hasher := sha256.New() + hasher.Write(unsafeStrToByteArray(typ)) + th := hasher.Sum(nil) + + hasher.Reset() + _, err := hasher.Write(th) + // the error always nil, it's here only to satisfy the io.Writer interface + errors.AssertNil(err) + _, err = hasher.Write(key) + errors.AssertNil(err) + return hasher.Sum(nil) +} + +// NewComposed creates a new address based on sub addresses. +func NewComposed(typ string, subAddresses []Addressable) ([]byte, error) { + as := make([][]byte, len(subAddresses)) + totalLen := 0 + var err error + for i := range subAddresses { + a := subAddresses[i].Address() + as[i], err = LengthPrefix(a) + if err != nil { + return nil, fmt.Errorf("not compatible sub-adddress=%v at index=%d [%w]", a, i, err) + } + totalLen += len(as[i]) + } + + sort.Slice(as, func(i, j int) bool { return bytes.Compare(as[i], as[j]) <= 0 }) + key := make([]byte, totalLen) + offset := 0 + for i := range as { + copy(key[offset:], as[i]) + offset += len(as[i]) + } + return Hash(typ, key), nil +} + +// Module is a specialized version of a composed address for modules. Each module account +// is constructed from a module name and module account key. +func Module(moduleName string, key []byte) []byte { + mKey := append([]byte(moduleName), 0) + return Hash("module", append(mKey, key...)) +} + +// unsafeStrToByteArray uses unsafe to convert string into byte array. Returned bytes +// must not be altered after this function is called as it will cause a segmentation fault. +func unsafeStrToByteArray(s string) []byte { + var buf = *(*[]byte)(unsafe.Pointer(&s)) + (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Cap = len(s) + return buf +} diff --git a/types/address/hash_test.go b/types/address/hash_test.go new file mode 100644 index 000000000..4cc6c6880 --- /dev/null +++ b/types/address/hash_test.go @@ -0,0 +1,109 @@ +package address + +import ( + "crypto/sha256" + "runtime" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestAddressSuite(t *testing.T) { + suite.Run(t, new(AddressSuite)) +} + +type AddressSuite struct{ suite.Suite } + +func (suite *AddressSuite) TestHash() { + assert := suite.Assert() + typ := "1" + key := []byte{1} + part1 := sha256.Sum256([]byte(typ)) + expected := sha256.Sum256(append(part1[:], key...)) + received := Hash(typ, key) + assert.Equal(expected[:], received, "must create a correct address") + + received = Hash("other", key) + assert.NotEqual(expected[:], received, "must create a correct address") + assert.Len(received, Len, "must have correct length") +} + +func (suite *AddressSuite) TestComposed() { + assert := suite.Assert() + a1 := addrMock{[]byte{11, 12}} + a2 := addrMock{[]byte{21, 22}} + + typ := "multisig" + ac, err := NewComposed(typ, []Addressable{a1, a2}) + assert.NoError(err) + assert.Len(ac, Len) + + // check if optimizations work + checkingKey := append([]byte{}, a1.AddressWithLen(suite.T())...) + checkingKey = append(checkingKey, a2.AddressWithLen(suite.T())...) + ac2 := Hash(typ, checkingKey) + assert.Equal(ac, ac2, "NewComposed works correctly") + + // changing order of addresses shouldn't impact a composed address + ac2, err = NewComposed(typ, []Addressable{a2, a1}) + assert.NoError(err) + assert.Len(ac2, Len) + assert.Equal(ac, ac2, "NewComposed is not sensitive for order") + + // changing a type should change composed address + ac2, err = NewComposed(typ+"other", []Addressable{a2, a1}) + assert.NoError(err) + assert.NotEqual(ac, ac2, "NewComposed must be sensitive to type") + + // changing order of addresses shouldn't impact a composed address + ac2, err = NewComposed(typ, []Addressable{a1, addrMock{make([]byte, 300, 300)}}) + assert.Error(err) + assert.Contains(err.Error(), "should be max 255 bytes, got 300") +} + +func (suite *AddressSuite) TestModule() { + assert := suite.Assert() + var modName, key = "myModule", []byte{1, 2} + addr := Module(modName, key) + assert.Len(addr, Len, "must have address length") + + addr2 := Module("myModule2", key) + assert.NotEqual(addr, addr2, "changing module name must change address") + + addr3 := Module(modName, []byte{1, 2, 3}) + assert.NotEqual(addr, addr3, "changing key must change address") + assert.NotEqual(addr2, addr3, "changing key must change address") +} + +func unsafeConvertABC() []byte { + return unsafeStrToByteArray("abc") +} + +func (suite *AddressSuite) TestUnsafeStrToBytes() { + // we convert in other function to trigger GC. We want to check that + // the underlying array in []bytes is accessible after GC will finish swapping. + for i := 0; i < 5; i++ { + b := unsafeConvertABC() + runtime.GC() + <-time.NewTimer(2 * time.Millisecond).C + b2 := append(b, 'd') + suite.Equal("abc", string(b)) + suite.Equal("abcd", string(b2)) + } +} + +type addrMock struct { + Addr []byte +} + +func (a addrMock) Address() []byte { + return a.Addr +} + +func (a addrMock) AddressWithLen(t *testing.T) []byte { + addr, err := LengthPrefix(a.Addr) + assert.NoError(t, err) + return addr +} diff --git a/types/address/store_key_test.go b/types/address/store_key_test.go index 3bb00bd02..ac28f814c 100644 --- a/types/address/store_key_test.go +++ b/types/address/store_key_test.go @@ -3,12 +3,19 @@ package address_test import ( "testing" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "github.com/cosmos/cosmos-sdk/types/address" ) -func TestLengthPrefixedAddressStoreKey(t *testing.T) { +func TestStoreKeySuite(t *testing.T) { + suite.Run(t, new(StoreKeySuite)) +} + +type StoreKeySuite struct{ suite.Suite } + +func (suite *StoreKeySuite) TestLengthPrefix() { + require := suite.Require() addr10byte := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} addr20byte := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19} addr256byte := make([]byte, 256) @@ -23,15 +30,16 @@ func TestLengthPrefixedAddressStoreKey(t *testing.T) { {"20-byte address", addr20byte, append([]byte{byte(20)}, addr20byte...), false}, {"256-byte address (too long)", addr256byte, nil, true}, } + for _, tt := range tests { tt := tt - t.Run(tt.name, func(t *testing.T) { + suite.Run(tt.name, func() { storeKey, err := address.LengthPrefix(tt.addr) if tt.expErr { - require.Error(t, err) + require.Error(err) } else { - require.NoError(t, err) - require.Equal(t, tt.expStoreKey, storeKey) + require.NoError(err) + require.Equal(tt.expStoreKey, storeKey) } }) } diff --git a/types/bench_test.go b/types/bench_test.go index 6c1cbc88c..e9ed9fa12 100644 --- a/types/bench_test.go +++ b/types/bench_test.go @@ -28,3 +28,53 @@ func BenchmarkParseCoin(b *testing.B) { } } } + +func BenchmarkUintMarshal(b *testing.B) { + var values = []uint64{ + 0, + 1, + 1 << 10, + 1<<10 - 3, + 1<<63 - 1, + 1<<32 - 7, + 1<<22 - 8, + } + + var scratch [20]byte + b.ReportAllocs() + for i := 0; i < b.N; i++ { + for _, value := range values { + u := types.NewUint(value) + n, err := u.MarshalTo(scratch[:]) + if err != nil { + b.Fatal(err) + } + b.SetBytes(int64(n)) + } + } +} + +func BenchmarkIntMarshal(b *testing.B) { + var values = []int64{ + 0, + 1, + 1 << 10, + 1<<10 - 3, + 1<<63 - 1, + 1<<32 - 7, + 1<<22 - 8, + } + + var scratch [20]byte + b.ReportAllocs() + for i := 0; i < b.N; i++ { + for _, value := range values { + in := types.NewInt(value) + n, err := in.MarshalTo(scratch[:]) + if err != nil { + b.Fatal(err) + } + b.SetBytes(int64(n)) + } + } +} diff --git a/types/config.go b/types/config.go index a3181703d..00b701a02 100644 --- a/types/config.go +++ b/types/config.go @@ -2,6 +2,7 @@ package types import ( "context" + "fmt" "sync" "github.com/cosmos/cosmos-sdk/version" @@ -18,9 +19,13 @@ type Config struct { txEncoder TxEncoder addressVerifier func([]byte) error mtx sync.RWMutex - coinType uint32 - sealed bool - sealedch chan struct{} + + // SLIP-44 related + purpose uint32 + coinType uint32 + + sealed bool + sealedch chan struct{} } // cosmos-sdk wide global singleton @@ -41,9 +46,11 @@ func NewConfig() *Config { "validator_pub": Bech32PrefixValPub, "consensus_pub": Bech32PrefixConsPub, }, - coinType: CoinType, fullFundraiserPath: FullFundraiserPath, - txEncoder: nil, + + purpose: Purpose, + coinType: CoinType, + txEncoder: nil, } } @@ -112,18 +119,26 @@ func (config *Config) SetAddressVerifier(addressVerifier func([]byte) error) { config.addressVerifier = addressVerifier } +// Set the FullFundraiserPath (BIP44Prefix) on the config. +// +// Deprecated: This method is supported for backward compatibility only and will be removed in a future release. Use SetPurpose and SetCoinType instead. +func (config *Config) SetFullFundraiserPath(fullFundraiserPath string) { + config.assertNotSealed() + config.fullFundraiserPath = fullFundraiserPath +} + +// Set the BIP-0044 Purpose code on the config +func (config *Config) SetPurpose(purpose uint32) { + config.assertNotSealed() + config.purpose = purpose +} + // Set the BIP-0044 CoinType code on the config func (config *Config) SetCoinType(coinType uint32) { config.assertNotSealed() config.coinType = coinType } -// Set the FullFundraiserPath (BIP44Prefix) on the config -func (config *Config) SetFullFundraiserPath(fullFundraiserPath string) { - config.assertNotSealed() - config.fullFundraiserPath = fullFundraiserPath -} - // Seal seals the config such that the config state could not be modified further func (config *Config) Seal() *Config { config.mtx.Lock() @@ -181,16 +196,28 @@ func (config *Config) GetAddressVerifier() func([]byte) error { return config.addressVerifier } +// GetPurpose returns the BIP-0044 Purpose code on the config. +func (config *Config) GetPurpose() uint32 { + return config.purpose +} + // GetCoinType returns the BIP-0044 CoinType code on the config. func (config *Config) GetCoinType() uint32 { return config.coinType } // GetFullFundraiserPath returns the BIP44Prefix. +// +// Deprecated: This method is supported for backward compatibility only and will be removed in a future release. Use GetFullBIP44Path instead. func (config *Config) GetFullFundraiserPath() string { return config.fullFundraiserPath } +// GetFullBIP44Path returns the BIP44Prefix. +func (config *Config) GetFullBIP44Path() string { + return fmt.Sprintf("m/%d'/%d'/0'/0/0", config.purpose, config.coinType) +} + func KeyringServiceName() string { if len(version.Name) == 0 { return DefaultKeyringServiceName diff --git a/types/config_test.go b/types/config_test.go index e2027f385..df281b1f8 100644 --- a/types/config_test.go +++ b/types/config_test.go @@ -17,6 +17,18 @@ func TestConfigTestSuite(t *testing.T) { suite.Run(t, new(configTestSuite)) } +func (s *contextTestSuite) TestConfig_SetPurpose() { + config := sdk.NewConfig() + config.SetPurpose(44) + s.Require().Equal(uint32(44), config.GetPurpose()) + + config.SetPurpose(0) + s.Require().Equal(uint32(0), config.GetPurpose()) + + config.Seal() + s.Require().Panics(func() { config.SetPurpose(10) }) +} + func (s *configTestSuite) TestConfig_SetCoinType() { config := sdk.NewConfig() config.SetCoinType(1) diff --git a/types/decimal.go b/types/decimal.go index bf93f43c3..706859a85 100644 --- a/types/decimal.go +++ b/types/decimal.go @@ -80,8 +80,6 @@ func precisionMultiplier(prec int64) *big.Int { return precisionMultipliers[prec] } -//______________________________________________________________________________________________ - // create a new Dec from integer assuming whole number func NewDec(i int64) Dec { return NewDecWithPrec(i, 0) @@ -195,8 +193,6 @@ func MustNewDecFromStr(s string) Dec { return dec } -//______________________________________________________________________________________________ -//nolint func (d Dec) IsNil() bool { return d.i == nil } // is decimal nil func (d Dec) IsZero() bool { return (d.i).Sign() == 0 } // is equal to zero func (d Dec) IsNegative() bool { return (d.i).Sign() == -1 } // is negative @@ -215,8 +211,8 @@ func (d Dec) BigInt() *big.Int { return nil } - copy := new(big.Int) - return copy.Set(d.i) + cp := new(big.Int) + return cp.Set(d.i) } // addition @@ -561,8 +557,6 @@ func (d Dec) RoundInt() Int { return NewIntFromBigInt(chopPrecisionAndRoundNonMutative(d.i)) } -//___________________________________________________________________________________ - // similar to chopPrecisionAndRound, but always rounds down func chopPrecisionAndTruncate(d *big.Int) *big.Int { return d.Quo(d, precisionReuse) @@ -612,8 +606,6 @@ func (d Dec) Ceil() Dec { return NewDecFromBigInt(quo.Add(quo, oneInt)) } -//___________________________________________________________________________________ - // MaxSortableDec is the largest Dec that can be passed into SortableDecBytes() // Its negative form is the least Dec that can be passed in. var MaxSortableDec = OneDec().Quo(SmallestDec()) @@ -648,8 +640,6 @@ func SortableDecBytes(dec Dec) []byte { return []byte(fmt.Sprintf(fmt.Sprintf("%%0%ds", Precision*2+1), dec.String())) } -//___________________________________________________________________________________ - // reuse nil values var nilJSON []byte @@ -758,7 +748,6 @@ func (dp DecProto) String() string { return dp.Dec.String() } -//___________________________________________________________________________________ // helpers // test if two decimal arrays are equal diff --git a/types/decimal_test.go b/types/decimal_test.go index 8e62275a0..050d19f50 100644 --- a/types/decimal_test.go +++ b/types/decimal_test.go @@ -29,8 +29,6 @@ func (s *decimalTestSuite) mustNewDecFromStr(str string) (d sdk.Dec) { return d } -//_______________________________________ - func (s *decimalTestSuite) TestNewDecFromStr() { largeBigInt, success := new(big.Int).SetString("3144605511029693144278234343371835", 10) s.Require().True(success) diff --git a/types/errors/errors.go b/types/errors/errors.go index 6c540a1d6..1dcc63e65 100644 --- a/types/errors/errors.go +++ b/types/errors/errors.go @@ -15,7 +15,6 @@ const UndefinedCodespace = "undefined" var ( // errInternal should never be exposed, but we reserve this code for non-specified errors - //nolint errInternal = Register(UndefinedCodespace, 1, "internal") // ErrTxDecode is returned if we cannot parse a transaction diff --git a/types/errors/handle.go b/types/errors/handle.go new file mode 100644 index 000000000..33c3fbfde --- /dev/null +++ b/types/errors/handle.go @@ -0,0 +1,12 @@ +package errors + +import "fmt" + +// AssertNil panics on error +// Should be only used with interface methods, which require return error, but the +// error is always nil +func AssertNil(err error) { + if err != nil { + panic(fmt.Errorf("logic error - this should never happen. %w", err)) + } +} diff --git a/types/int.go b/types/int.go index 81f6b1c04..c33ff040a 100644 --- a/types/int.go +++ b/types/int.go @@ -375,7 +375,7 @@ func (i *Int) MarshalTo(data []byte) (n int, err error) { if i.i == nil { i.i = new(big.Int) } - if len(i.i.Bytes()) == 0 { + if i.i.BitLen() == 0 { // The value 0 copy(data, []byte{0x30}) return 1, nil } diff --git a/types/int_test.go b/types/int_test.go index 4b26ddb8a..2992f938b 100644 --- a/types/int_test.go +++ b/types/int_test.go @@ -1,6 +1,7 @@ package types_test import ( + "fmt" "math/big" "math/rand" "strconv" @@ -385,3 +386,36 @@ func (s *intTestSuite) TestIntEq() { _, resp, _, _, _ = sdk.IntEq(s.T(), sdk.OneInt(), sdk.ZeroInt()) s.Require().False(resp) } + +func TestRoundTripMarshalToInt(t *testing.T) { + var values = []int64{ + 0, + 1, + 1 << 10, + 1<<10 - 3, + 1<<63 - 1, + 1<<32 - 7, + 1<<22 - 8, + } + + for _, value := range values { + value := value + t.Run(fmt.Sprintf("%d", value), func(t *testing.T) { + t.Parallel() + + var scratch [20]byte + iv := sdk.NewInt(value) + n, err := iv.MarshalTo(scratch[:]) + if err != nil { + t.Fatal(err) + } + rt := new(sdk.Int) + if err := rt.Unmarshal(scratch[:n]); err != nil { + t.Fatal(err) + } + if !rt.Equal(iv) { + t.Fatalf("roundtrip=%q != original=%q", rt, iv) + } + }) + } +} diff --git a/types/module/module.go b/types/module/module.go index 58bb4927b..9123c9af7 100644 --- a/types/module/module.go +++ b/types/module/module.go @@ -43,8 +43,6 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -//__________________________________________________________________________________________ - // AppModuleBasic is the standard form for basic non-dependant elements of an application module. type AppModuleBasic interface { Name() string @@ -146,8 +144,6 @@ func (bm BasicManager) AddQueryCommands(rootQueryCmd *cobra.Command) { } } -//_________________________________________________________ - // AppModuleGenesis is the standard form for an application module genesis functions type AppModuleGenesis interface { AppModuleBasic @@ -186,8 +182,6 @@ type AppModule interface { EndBlock(sdk.Context, abci.RequestEndBlock) []abci.ValidatorUpdate } -//___________________________ - // GenesisOnlyAppModule is an AppModule that only has import/export functionality type GenesisOnlyAppModule struct { AppModuleGenesis @@ -226,8 +220,6 @@ func (GenesisOnlyAppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []ab return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // Manager defines a module manager that provides the high level utility for managing and executing // operations for a group of modules type Manager struct { @@ -339,7 +331,7 @@ func (m *Manager) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) map[st } // MigrationHandler is the migration function that each module registers. -type MigrationHandler func(store sdk.Context) error +type MigrationHandler func(sdk.Context) error // MigrationMap is a map of moduleName -> version, where version denotes the // version from which we should perform the migration for each module. diff --git a/types/query/filtered_pagination_test.go b/types/query/filtered_pagination_test.go index e0685fbaf..1ba2da21d 100644 --- a/types/query/filtered_pagination_test.go +++ b/types/query/filtered_pagination_test.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/address" @@ -27,10 +28,11 @@ func (s *paginationTestSuite) TestFilteredPaginations() { balances = append(balances, sdk.NewInt64Coin(denom, 250)) } + balances = balances.Sort() addr1 := sdk.AccAddress([]byte("addr1")) acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) app.AccountKeeper.SetAccount(ctx, acc1) - s.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, balances)) + s.Require().NoError(simapp.FundAccount(app, ctx, addr1, balances)) store := ctx.KVStore(app.GetKey(types.StoreKey)) // verify pagination with limit > total values @@ -100,10 +102,12 @@ func ExampleFilteredPaginate() { denom := fmt.Sprintf("test%ddenom", i) balances = append(balances, sdk.NewInt64Coin(denom, 250)) } + + balances = balances.Sort() addr1 := sdk.AccAddress([]byte("addr1")) acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) app.AccountKeeper.SetAccount(ctx, acc1) - err := app.BankKeeper.SetBalances(ctx, addr1, balances) + err := simapp.FundAccount(app, ctx, addr1, balances) if err != nil { // should return no error fmt.Println(err) } diff --git a/types/query/pagination_test.go b/types/query/pagination_test.go index 74a7c959e..bd9dc0af1 100644 --- a/types/query/pagination_test.go +++ b/types/query/pagination_test.go @@ -73,10 +73,11 @@ func (s *paginationTestSuite) TestPagination() { balances = append(balances, sdk.NewInt64Coin(denom, 100)) } + balances = balances.Sort() addr1 := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) app.AccountKeeper.SetAccount(ctx, acc1) - s.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, balances)) + s.Require().NoError(simapp.FundAccount(app, ctx, addr1, balances)) s.T().Log("verify empty page request results a max of defaultLimit records and counts total records") pageReq := &query.PageRequest{} @@ -178,11 +179,12 @@ func ExamplePaginate() { balances = append(balances, sdk.NewInt64Coin(denom, 100)) } + balances = balances.Sort() addr1 := sdk.AccAddress([]byte("addr1")) acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) app.AccountKeeper.SetAccount(ctx, acc1) - err := app.BankKeeper.SetBalances(ctx, addr1, balances) - if err != nil { + err := simapp.FundAccount(app, ctx, addr1, balances) + if err != nil { // should return no error fmt.Println(err) } // Paginate example diff --git a/types/service_msg.go b/types/service_msg.go index 61c0f15ab..ee6cede2e 100644 --- a/types/service_msg.go +++ b/types/service_msg.go @@ -1,6 +1,8 @@ package types import ( + "fmt" + "github.com/gogo/protobuf/proto" ) @@ -18,7 +20,7 @@ type MsgRequest interface { } // ServiceMsg is the struct into which an Any whose typeUrl matches a service -// method format (ex. `/cosmos.gov.Msg/SubmitProposal`) unpacks. +// method format (ex. `/cosmos.gov.v1beta1.Msg/SubmitProposal`) unpacks. type ServiceMsg struct { // MethodName is the fully-qualified service method name. MethodName string @@ -44,7 +46,17 @@ func (msg ServiceMsg) ValidateBasic() error { // GetSignBytes implements Msg.GetSignBytes method. func (msg ServiceMsg) GetSignBytes() []byte { - panic("ServiceMsg does not have a GetSignBytes method") + // Here, we're gracefully supporting Amino JSON for service + // Msgs. + // ref: https://github.com/cosmos/cosmos-sdk/issues/8346 + // If `msg` is a service Msg, then we cast its `Request` to a sdk.Msg + // and call GetSignBytes on the `Request`. + msgRequest, ok := msg.Request.(Msg) + if !ok { + panic(fmt.Errorf("cannot convert ServiceMsg request to sdk.Msg, got %T", msgRequest)) + } + + return msgRequest.GetSignBytes() } // GetSigners implements Msg.GetSigners method. diff --git a/types/simulation/config.go b/types/simulation/config.go index 58a39bb17..8bad70954 100644 --- a/types/simulation/config.go +++ b/types/simulation/config.go @@ -6,8 +6,8 @@ type Config struct { ParamsFile string // custom simulation params file which overrides any random params; cannot be used with genesis ExportParamsPath string // custom file path to save the exported params JSON - ExportParamsHeight int //height to which export the randomly generated params - ExportStatePath string //custom file path to save the exported app state JSON + ExportParamsHeight int // height to which export the randomly generated params + ExportStatePath string // custom file path to save the exported app state JSON ExportStatsPath string // custom file path to save the exported simulation statistics JSON Seed int64 // simulation random seed diff --git a/types/simulation/types.go b/types/simulation/types.go index 46ede62a3..fabbcd475 100644 --- a/types/simulation/types.go +++ b/types/simulation/types.go @@ -125,8 +125,6 @@ func (om OperationMsg) LogEvent(eventLogger func(route, op, evResult string)) { eventLogger(om.Route, om.Name, pass) } -//________________________________________________________________________ - // FutureOperation is an operation which will be ran at the beginning of the // provided BlockHeight. If both a BlockHeight and BlockTime are specified, it // will use the BlockHeight. In the (likely) event that multiple operations diff --git a/types/tx/service.pb.gw.go b/types/tx/service.pb.gw.go index 6d94c423e..25560e1cd 100644 --- a/types/tx/service.pb.gw.go +++ b/types/tx/service.pb.gw.go @@ -400,13 +400,13 @@ func RegisterServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, cl } var ( - pattern_Service_Simulate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "simulate"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Service_Simulate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "simulate"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Service_GetTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "tx", "v1beta1", "txs", "hash"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Service_GetTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "tx", "v1beta1", "txs", "hash"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Service_BroadcastTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "txs"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Service_BroadcastTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "txs"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Service_GetTxsEvent_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "txs"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Service_GetTxsEvent_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "txs"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/types/uint.go b/types/uint.go index 2305d7386..dbdd17bd5 100644 --- a/types/uint.go +++ b/types/uint.go @@ -161,7 +161,7 @@ func (u *Uint) MarshalTo(data []byte) (n int, err error) { if u.i == nil { u.i = new(big.Int) } - if len(u.i.Bytes()) == 0 { + if u.i.BitLen() == 0 { // The value 0 copy(data, []byte{0x30}) return 1, nil } @@ -207,8 +207,6 @@ func (u *Uint) Size() int { func (u Uint) MarshalAmino() ([]byte, error) { return u.Marshal() } func (u *Uint) UnmarshalAmino(bz []byte) error { return u.Unmarshal(bz) } -//__________________________________________________________________________ - // UintOverflow returns true if a given unsigned integer overflows and false // otherwise. func UintOverflow(i *big.Int) error { diff --git a/types/uint_test.go b/types/uint_test.go index 36705c890..b91f9ab4a 100644 --- a/types/uint_test.go +++ b/types/uint_test.go @@ -1,6 +1,7 @@ package types_test import ( + "fmt" "math" "math/big" "math/rand" @@ -290,3 +291,36 @@ func maxuint(i1, i2 uint64) uint64 { } return i2 } + +func TestRoundTripMarshalToUint(t *testing.T) { + var values = []uint64{ + 0, + 1, + 1 << 10, + 1<<10 - 3, + 1<<63 - 1, + 1<<32 - 7, + 1<<22 - 8, + } + + for _, value := range values { + value := value + t.Run(fmt.Sprintf("%d", value), func(t *testing.T) { + t.Parallel() + + var scratch [20]byte + uv := sdk.NewUint(value) + n, err := uv.MarshalTo(scratch[:]) + if err != nil { + t.Fatal(err) + } + rt := new(sdk.Uint) + if err := rt.Unmarshal(scratch[:n]); err != nil { + t.Fatal(err) + } + if !rt.Equal(uv) { + t.Fatalf("roundtrip=%q != original=%q", rt, uv) + } + }) + } +} diff --git a/x/README.md b/x/README.md index e90ee59ce..7ca27e617 100644 --- a/x/README.md +++ b/x/README.md @@ -15,7 +15,7 @@ Here are some production-grade modules that can be used in Cosmos SDK applicatio - [Distribution](distribution/spec/README.md) - Fee distribution, and staking token provision distribution. - [Evidence](evidence/spec/README.md) - Evidence handling for double signing, misbehaviour, etc. - [Governance](gov/spec/README.md) - On-chain proposals and voting. -- [IBC](ibc/spec/README.md) - IBC protocol for transport, authentication adn ordering. +- [IBC](ibc/spec/README.md) - IBC protocol for transport, authentication and ordering. - [IBC Transfer](ibc/spec/README.md) - Cross-chain fungible token transfer implementation through IBC. - [Mint](mint/spec/README.md) - Creation of new units of staking token. - [Params](params/spec/README.md) - Globally available parameter store. diff --git a/x/auth/ante/ante_test.go b/x/auth/ante/ante_test.go index 7659f3e10..9b268b1ec 100644 --- a/x/auth/ante/ante_test.go +++ b/x/auth/ante/ante_test.go @@ -7,6 +7,10 @@ import ( "strings" "testing" + "github.com/cosmos/cosmos-sdk/simapp" + + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" @@ -23,7 +27,7 @@ import ( // Test that simulate transaction accurately estimates gas cost func (suite *AnteTestSuite) TestSimulateGasCost() { - suite.SetupTest(true) // reset + suite.SetupTest(false) // reset // Same data for every test cases accounts := suite.CreateTestAccounts(3) @@ -76,7 +80,7 @@ func (suite *AnteTestSuite) TestSimulateGasCost() { // Test various error cases in the AnteHandler control flow. func (suite *AnteTestSuite) TestAnteHandlerSigErrors() { - suite.SetupTest(true) // reset + suite.SetupTest(false) // reset // Same data for every test cases priv0, _, addr0 := testdata.KeyTestPubAddr() @@ -137,7 +141,9 @@ func (suite *AnteTestSuite) TestAnteHandlerSigErrors() { func() { acc1 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr0) suite.app.AccountKeeper.SetAccount(suite.ctx, acc1) - err := suite.app.BankKeeper.SetBalances(suite.ctx, addr0, feeAmount) + err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, feeAmount) + suite.Require().NoError(err) + err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, minttypes.ModuleName, addr0, feeAmount) suite.Require().NoError(err) }, false, @@ -435,7 +441,7 @@ func (suite *AnteTestSuite) TestAnteHandlerSequences() { // Test logic around fee deduction. func (suite *AnteTestSuite) TestAnteHandlerFees() { - suite.SetupTest(true) // setup + suite.SetupTest(false) // setup // Same data for every test cases priv0, _, addr0 := testdata.KeyTestPubAddr() @@ -466,7 +472,8 @@ func (suite *AnteTestSuite) TestAnteHandlerFees() { { "signer does not have enough funds to pay the fee", func() { - suite.app.BankKeeper.SetBalances(suite.ctx, addr0, sdk.NewCoins(sdk.NewInt64Coin("atom", 149))) + err := simapp.FundAccount(suite.app, suite.ctx, addr0, sdk.NewCoins(sdk.NewInt64Coin("atom", 149))) + suite.Require().NoError(err) }, false, false, @@ -475,12 +482,14 @@ func (suite *AnteTestSuite) TestAnteHandlerFees() { { "signer as enough funds, should pass", func() { + accNums = []uint64{7} modAcc := suite.app.AccountKeeper.GetModuleAccount(suite.ctx, types.FeeCollectorName) suite.Require().True(suite.app.BankKeeper.GetAllBalances(suite.ctx, modAcc.GetAddress()).Empty()) require.True(sdk.IntEq(suite.T(), suite.app.BankKeeper.GetAllBalances(suite.ctx, addr0).AmountOf("atom"), sdk.NewInt(149))) - suite.app.BankKeeper.SetBalances(suite.ctx, addr0, sdk.NewCoins(sdk.NewInt64Coin("atom", 150))) + err := simapp.FundAccount(suite.app, suite.ctx, addr0, sdk.NewCoins(sdk.NewInt64Coin("atom", 1))) + suite.Require().NoError(err) }, false, true, @@ -960,7 +969,7 @@ func TestCountSubkeys(t *testing.T) { } func (suite *AnteTestSuite) TestAnteHandlerSigLimitExceeded() { - suite.SetupTest(true) // setup + suite.SetupTest(false) // setup // Same data for every test cases accounts := suite.CreateTestAccounts(8) @@ -997,7 +1006,7 @@ func (suite *AnteTestSuite) TestAnteHandlerSigLimitExceeded() { // Test custom SignatureVerificationGasConsumer func (suite *AnteTestSuite) TestCustomSignatureVerificationGasConsumer() { - suite.SetupTest(true) // setup + suite.SetupTest(false) // setup // setup an ante handler that only accepts PubKeyEd25519 suite.anteHandler = ante.NewAnteHandler(suite.app.AccountKeeper, suite.app.BankKeeper, func(meter sdk.GasMeter, sig signing.SignatureV2, params types.Params) error { @@ -1047,7 +1056,7 @@ func (suite *AnteTestSuite) TestCustomSignatureVerificationGasConsumer() { } func (suite *AnteTestSuite) TestAnteHandlerReCheck() { - suite.SetupTest(true) // setup + suite.SetupTest(false) // setup // Set recheck=true suite.ctx = suite.ctx.WithIsReCheckTx(true) suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() @@ -1120,7 +1129,9 @@ func (suite *AnteTestSuite) TestAnteHandlerReCheck() { // remove funds for account so antehandler fails on recheck suite.app.AccountKeeper.SetAccount(suite.ctx, accounts[0].acc) - suite.app.BankKeeper.SetBalances(suite.ctx, accounts[0].acc.GetAddress(), sdk.NewCoins()) + balances := suite.app.BankKeeper.GetAllBalances(suite.ctx, accounts[0].acc.GetAddress()) + err = suite.app.BankKeeper.SendCoinsFromAccountToModule(suite.ctx, accounts[0].acc.GetAddress(), minttypes.ModuleName, balances) + suite.Require().NoError(err) _, err = suite.anteHandler(suite.ctx, tx, false) suite.Require().NotNil(err, "antehandler on recheck did not fail once feePayer no longer has sufficient funds") diff --git a/x/auth/ante/fee_test.go b/x/auth/ante/fee_test.go index 84e5adb60..343e81458 100644 --- a/x/auth/ante/fee_test.go +++ b/x/auth/ante/fee_test.go @@ -2,8 +2,8 @@ package ante_test import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/ante" ) @@ -61,7 +61,7 @@ func (suite *AnteTestSuite) TestEnsureMempoolFees() { } func (suite *AnteTestSuite) TestDeductFees() { - suite.SetupTest(true) // setup + suite.SetupTest(false) // setup suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // keys and addresses @@ -82,7 +82,9 @@ func (suite *AnteTestSuite) TestDeductFees() { // Set account with insufficient funds acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - suite.app.BankKeeper.SetBalances(suite.ctx, addr1, sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(10)))) + coins := sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(10))) + err = simapp.FundAccount(suite.app, suite.ctx, addr1, coins) + suite.Require().NoError(err) dfd := ante.NewDeductFeeDecorator(suite.app.AccountKeeper, suite.app.BankKeeper) antehandler := sdk.ChainAnteDecorators(dfd) @@ -93,7 +95,8 @@ func (suite *AnteTestSuite) TestDeductFees() { // Set account with sufficient funds suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - suite.app.BankKeeper.SetBalances(suite.ctx, addr1, sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(200)))) + err = simapp.FundAccount(suite.app, suite.ctx, addr1, sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(200)))) + suite.Require().NoError(err) _, err = antehandler(suite.ctx, tx, false) diff --git a/x/auth/ante/testutil_test.go b/x/auth/ante/testutil_test.go index 5a1cfc4ec..78af9743e 100644 --- a/x/auth/ante/testutil_test.go +++ b/x/auth/ante/testutil_test.go @@ -5,6 +5,8 @@ import ( "fmt" "testing" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -75,9 +77,14 @@ func (suite *AnteTestSuite) CreateTestAccounts(numAccs int) []TestAccount { err := acc.SetAccountNumber(uint64(i)) suite.Require().NoError(err) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - suite.app.BankKeeper.SetBalances(suite.ctx, addr, sdk.Coins{ + someCoins := sdk.Coins{ sdk.NewInt64Coin("atom", 10000000), - }) + } + err = suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, someCoins) + suite.Require().NoError(err) + + err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, minttypes.ModuleName, addr, someCoins) + suite.Require().NoError(err) accounts = append(accounts, TestAccount{acc, priv}) } diff --git a/x/auth/client/cli/cli_test.go b/x/auth/client/cli/cli_test.go index d8913d1ac..6d9d776f1 100644 --- a/x/auth/client/cli/cli_test.go +++ b/x/auth/client/cli/cli_test.go @@ -52,13 +52,13 @@ func (s *IntegrationTestSuite) SetupSuite() { s.network = network.New(s.T(), cfg) kb := s.network.Validators[0].ClientCtx.Keyring - _, _, err := kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err := kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) s.Require().NoError(err) - account1, _, err := kb.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + account1, _, err := kb.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) s.Require().NoError(err) - account2, _, err := kb.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + account2, _, err := kb.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) s.Require().NoError(err) multi := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{account1.GetPubKey(), account2.GetPubKey()}) @@ -230,23 +230,19 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd() { sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) // Send coins, try both with legacy Msg and with Msg service. - // Legacy Msg. - legacyMsgOut, err := bankcli.MsgSendExec( - val.ClientCtx, + // Legacy proto Msg. + legacyTxRes, err := bankcli.LegacyGRPCProtoMsgSend( + val.ClientCtx, val.Moniker, val.Address, account2.GetAddress(), + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))), sdk.NewCoins(sendTokens), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), ) s.Require().NoError(err) - var legacyMsgTxRes sdk.TxResponse - s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(legacyMsgOut.Bytes(), &legacyMsgTxRes)) + s.Require().NoError(s.network.WaitForNextBlock()) // Service Msg. - out, err := bankcli.ServiceMsgSendExec( + out, err := bankcli.MsgSendExec( val.ClientCtx, val.Address, account2.GetAddress(), @@ -259,7 +255,6 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd() { s.Require().NoError(err) var txRes sdk.TxResponse s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txRes)) - s.Require().NoError(s.network.WaitForNextBlock()) testCases := []struct { @@ -282,7 +277,7 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd() { }, { "happy case (legacy Msg)", - []string{legacyMsgTxRes.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + []string{legacyTxRes.TxResponse.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", }, @@ -737,7 +732,7 @@ func (s *IntegrationTestSuite) TestCLIMultisign() { // Does not work in offline mode. _, err = authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), "--offline", sign1File.Name(), sign2File.Name()) - s.Require().EqualError(err, "couldn't verify signature: unable to verify single signer signature") + s.Require().EqualError(err, fmt.Sprintf("couldn't verify signature for address %s", account1.GetAddress())) val1.ClientCtx.Offline = false multiSigWith2Signatures, err := authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name()) @@ -817,6 +812,83 @@ func (s *IntegrationTestSuite) TestSignBatchMultisig() { s.Require().NoError(err) } +func (s *IntegrationTestSuite) TestMultisignBatch() { + val := s.network.Validators[0] + + // Fetch 2 accounts and a multisig. + account1, err := val.ClientCtx.Keyring.Key("newAccount1") + s.Require().NoError(err) + account2, err := val.ClientCtx.Keyring.Key("newAccount2") + s.Require().NoError(err) + multisigInfo, err := val.ClientCtx.Keyring.Key("multi") + + // Send coins from validator to multisig. + sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 1000) + _, err = bankcli.MsgSendExec( + val.ClientCtx, + val.Address, + multisigInfo.GetAddress(), + sdk.NewCoins(sendTokens), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + ) + s.Require().NoError(err) + s.Require().NoError(s.network.WaitForNextBlock()) + + generatedStd, err := bankcli.MsgSendExec( + val.ClientCtx, + multisigInfo.GetAddress(), + val.Address, + sdk.NewCoins( + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(1)), + ), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + ) + s.Require().NoError(err) + + // Write the output to disk + filename := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String(), 3)) + val.ClientCtx.HomeDir = strings.Replace(val.ClientCtx.HomeDir, "simd", "simcli", 1) + + queryResJSON, err := authtest.QueryAccountExec(val.ClientCtx, multisigInfo.GetAddress()) + s.Require().NoError(err) + var account authtypes.AccountI + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalInterfaceJSON(queryResJSON.Bytes(), &account)) + + // sign-batch file + res, err := authtest.TxSignBatchExec(val.ClientCtx, account1.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetAddress().String(), fmt.Sprintf("--%s", flags.FlagOffline), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, fmt.Sprint(account.GetAccountNumber())), fmt.Sprintf("--%s=%s", flags.FlagSequence, fmt.Sprint(account.GetSequence()))) + s.Require().NoError(err) + s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) + // write sigs to file + file1 := testutil.WriteToNewTempFile(s.T(), res.String()) + + // sign-batch file with account2 + res, err = authtest.TxSignBatchExec(val.ClientCtx, account2.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetAddress().String(), fmt.Sprintf("--%s", flags.FlagOffline), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, fmt.Sprint(account.GetAccountNumber())), fmt.Sprintf("--%s=%s", flags.FlagSequence, fmt.Sprint(account.GetSequence()))) + s.Require().NoError(err) + s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) + + // multisign the file + file2 := testutil.WriteToNewTempFile(s.T(), res.String()) + res, err = authtest.TxMultiSignBatchExec(val.ClientCtx, filename.Name(), multisigInfo.GetName(), file1.Name(), file2.Name()) + s.Require().NoError(err) + signedTxs := strings.Split(strings.Trim(res.String(), "\n"), "\n") + + // Broadcast transactions. + for _, signedTx := range signedTxs { + signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx) + val.ClientCtx.BroadcastMode = flags.BroadcastBlock + res, err = authtest.TxBroadcastExec(val.ClientCtx, signedTxFile.Name()) + s.T().Log(res) + s.Require().NoError(err) + s.Require().NoError(s.network.WaitForNextBlock()) + } +} + func (s *IntegrationTestSuite) TestGetAccountCmd() { val := s.network.Validators[0] _, _, addr1 := testdata.KeyTestPubAddr() @@ -856,6 +928,20 @@ func (s *IntegrationTestSuite) TestGetAccountCmd() { } } +func (s *IntegrationTestSuite) TestGetAccountsCmd() { + val := s.network.Validators[0] + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, authcli.GetAccountsCmd(), []string{ + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }) + s.Require().NoError(err) + + var res authtypes.QueryAccountsResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &res)) + s.Require().NotEmpty(res.Accounts) +} + func TestGetBroadcastCommand_OfflineFlag(t *testing.T) { clientCtx := client.Context{}.WithOffline(true) clientCtx = clientCtx.WithTxConfig(simapp.MakeTestEncodingConfig().TxConfig) @@ -1016,7 +1102,7 @@ func (s *IntegrationTestSuite) TestSignWithMultiSigners_AminoJSON() { banktypes.NewMsgSend(val1.Address, addr1, sdk.NewCoins(val1Coin)), ) txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)))) - txBuilder.SetGasLimit(testdata.NewTestGasLimit()) + txBuilder.SetGasLimit(testdata.NewTestGasLimit()) // min required is 101892 require.Equal([]sdk.AccAddress{val0.Address, val1.Address}, txBuilder.GetTx().GetSigners()) // Write the unsigned tx into a file. @@ -1040,7 +1126,12 @@ func (s *IntegrationTestSuite) TestSignWithMultiSigners_AminoJSON() { signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String()) // Now let's try to send this tx. - res, err := authtest.TxBroadcastExec(val0.ClientCtx, signedTxFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock)) + res, err := authtest.TxBroadcastExec( + val0.ClientCtx, + signedTxFile.Name(), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + ) + require.NoError(err) var txRes sdk.TxResponse require.NoError(val0.ClientCtx.JSONMarshaler.UnmarshalJSON(res.Bytes(), &txRes)) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index a236e900f..8ee3a0575 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -34,6 +34,7 @@ func GetQueryCmd() *cobra.Command { cmd.AddCommand( GetAccountCmd(), + GetAccountsCmd(), QueryParamsCmd(), ) @@ -103,6 +104,38 @@ func GetAccountCmd() *cobra.Command { return cmd } +// GetAccountsCmd returns a query command that will display a list of accounts +func GetAccountsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "accounts", + Short: "Query all the accounts", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.Accounts(cmd.Context(), &types.QueryAccountsRequest{Pagination: pageReq}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "all-accounts") + + return cmd +} + // QueryTxsByEventsCmd returns a command to search through transactions by events. func QueryTxsByEventsCmd() *cobra.Command { cmd := &cobra.Command{ diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index 2910244f6..61ec84bdc 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -1,13 +1,13 @@ package cli import ( - "bufio" "fmt" "io/ioutil" "os" "strings" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -16,6 +16,7 @@ import ( kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" "github.com/cosmos/cosmos-sdk/crypto/types/multisig" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/version" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" @@ -35,7 +36,7 @@ Read signature(s) from [signature] file(s), generate a multisig signature compli multisig key [name], and attach it to the transaction read from [file]. Example: -$ %s multisign transaction.json k1k2k3 k1sig.json k2sig.json k3sig.json +$ %s tx multisign transaction.json k1k2k3 k1sig.json k2sig.json k3sig.json If the flag --signature-only flag is on, it outputs a JSON representation of the generated signature only. @@ -43,6 +44,9 @@ of the generated signature only. The --offline flag makes sure that the client will not reach out to an external node. Thus account number or sequence number lookups will not be performed and it is recommended to set such parameters manually. + +The current multisig implementation doesn't support SIGN_MORE_DIRECT and defaults +to amino-json sign mode.' `, version.AppName, ), @@ -82,20 +86,9 @@ func makeMultiSignCmd() func(cmd *cobra.Command, args []string) (err error) { return err } - backend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) - - inBuf := bufio.NewReader(cmd.InOrStdin()) - kb, err := keyring.New(sdk.KeyringServiceName(), backend, clientCtx.HomeDir, inBuf) + multisigInfo, err := getMultisigInfo(clientCtx, args[1]) if err != nil { - return - } - - multisigInfo, err := kb.Key(args[1]) - if err != nil { - return - } - if multisigInfo.GetType() != keyring.TypeMulti { - return fmt.Errorf("%q must be of type %s: %s", args[1], keyring.TypeMulti, multisigInfo.GetType()) + return err } multisigPub := multisigInfo.GetPubKey().(*kmultisig.LegacyAminoPubKey) @@ -125,7 +118,8 @@ func makeMultiSignCmd() func(cmd *cobra.Command, args []string) (err error) { for _, sig := range sigs { err = signing.VerifySignature(sig.PubKey, signingData, sig.Data, txCfg.SignModeHandler(), txBuilder.GetTx()) if err != nil { - return fmt.Errorf("couldn't verify signature: %w", err) + addr, _ := sdk.AccAddressFromHex(sig.PubKey.Address().String()) + return fmt.Errorf("couldn't verify signature for address %s", addr) } if err := multisig.AddSignatureV2(multisigSig, sig, multisigPub.GetPubKeys()); err != nil { @@ -195,6 +189,171 @@ func makeMultiSignCmd() func(cmd *cobra.Command, args []string) (err error) { } } +func GetMultiSignBatchCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "multisign-batch [file] [name] [[signature-file]...]", + Short: "Assemble multisig transactions in batch from batch signatures", + Long: strings.TrimSpace( + fmt.Sprintf(`Assemble a batch of multisig transactions generated by batch sign command. + +Read signature(s) from [signature] file(s), generates multisig signatures compliant to the +multisig key [name], and attach it to the transactions read from [file]. + +Example: +$ %s tx multisign-batch transactions.json multisigk1k2k3 k1sigs.json k2sigs.json k3sig.json + +The current multisig implementation doesn't support sign_mode_direct and defaults +to amino-json sign mode.' +`, version.AppName, + ), + ), + PreRun: preSignCmd, + RunE: makeBatchMultisignCmd(), + Args: cobra.MinimumNArgs(3), + } + + cmd.Flags().Bool(flagNoAutoIncrement, false, "disable sequence auto increment") + cmd.Flags().String( + flagMultisig, "", + "Address of the multisig account on behalf of which the transaction shall be signed", + ) + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func makeBatchMultisignCmd() func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) (err error) { + var clientCtx client.Context + + clientCtx, err = client.GetClientTxContext(cmd) + if err != nil { + return err + } + + txCfg := clientCtx.TxConfig + txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + if txFactory.SignMode() == signingtypes.SignMode_SIGN_MODE_UNSPECIFIED { + txFactory = txFactory.WithSignMode(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON) + } + + var infile = os.Stdin + if args[0] != "-" { + infile, err = os.Open(args[0]) + defer func() { + err2 := infile.Close() + if err == nil { + err = err2 + } + }() + + if err != nil { + return fmt.Errorf("couldn't open %s: %w", args[0], err) + } + } + scanner := authclient.NewBatchScanner(txCfg, infile) + + multisigInfo, err := getMultisigInfo(clientCtx, args[1]) + if err != nil { + return err + } + + var signatureBatch [][]signingtypes.SignatureV2 + for i := 2; i < len(args); i++ { + sigs, err := readSignaturesFromFile(clientCtx, args[i]) + if err != nil { + return err + } + + signatureBatch = append(signatureBatch, sigs) + } + + if !clientCtx.Offline { + accnum, seq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, multisigInfo.GetAddress()) + if err != nil { + return err + } + + txFactory = txFactory.WithAccountNumber(accnum).WithSequence(seq) + } + + for i := 0; scanner.Scan(); i++ { + txBldr, err := txCfg.WrapTxBuilder(scanner.Tx()) + if err != nil { + return err + } + + multisigPub := multisigInfo.GetPubKey().(*kmultisig.LegacyAminoPubKey) + multisigSig := multisig.NewMultisig(len(multisigPub.PubKeys)) + signingData := signing.SignerData{ + ChainID: txFactory.ChainID(), + AccountNumber: txFactory.AccountNumber(), + Sequence: txFactory.Sequence(), + } + + for _, sig := range signatureBatch { + err = signing.VerifySignature(sig[i].PubKey, signingData, sig[i].Data, txCfg.SignModeHandler(), txBldr.GetTx()) + if err != nil { + return fmt.Errorf("couldn't verify signature: %w %v", err, sig) + } + + if err := multisig.AddSignatureV2(multisigSig, sig[i], multisigPub.GetPubKeys()); err != nil { + return err + } + } + + sigV2 := signingtypes.SignatureV2{ + PubKey: multisigPub, + Data: multisigSig, + Sequence: txFactory.Sequence(), + } + + err = txBldr.SetSignatures(sigV2) + if err != nil { + return err + } + + sigOnly, _ := cmd.Flags().GetBool(flagSigOnly) + aminoJSON, _ := cmd.Flags().GetBool(flagAmino) + + var json []byte + + if aminoJSON { + stdTx, err := tx.ConvertTxToStdTx(clientCtx.LegacyAmino, txBldr.GetTx()) + if err != nil { + return err + } + + req := rest.BroadcastReq{ + Tx: stdTx, + Mode: "block|sync|async", + } + + json, _ = clientCtx.LegacyAmino.MarshalJSON(req) + + } else { + json, err = marshalSignatureJSON(txCfg, txBldr, sigOnly) + if err != nil { + return err + } + } + + err = clientCtx.PrintString(fmt.Sprintf("%s\n", json)) + if err != nil { + return err + } + + if viper.GetBool(flagNoAutoIncrement) { + continue + } + sequence := txFactory.Sequence() + 1 + txFactory = txFactory.WithSequence(sequence) + } + + return nil + } +} + func unmarshalSignatureJSON(clientCtx client.Context, filename string) (sigs []signingtypes.SignatureV2, err error) { var bytes []byte if bytes, err = ioutil.ReadFile(filename); err != nil { @@ -202,3 +361,36 @@ func unmarshalSignatureJSON(clientCtx client.Context, filename string) (sigs []s } return clientCtx.TxConfig.UnmarshalSignatureJSON(bytes) } + +func readSignaturesFromFile(ctx client.Context, filename string) (sigs []signingtypes.SignatureV2, err error) { + bz, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + newString := strings.TrimSuffix(string(bz), "\n") + lines := strings.Split(newString, "\n") + + for _, bz := range lines { + sig, err := ctx.TxConfig.UnmarshalSignatureJSON([]byte(bz)) + if err != nil { + return nil, err + } + + sigs = append(sigs, sig...) + } + return sigs, nil +} + +func getMultisigInfo(clientCtx client.Context, name string) (keyring.Info, error) { + kb := clientCtx.Keyring + multisigInfo, err := kb.Key(name) + if err != nil { + return nil, errors.Wrap(err, "error getting keybase multisig account") + } + if multisigInfo.GetType() != keyring.TypeMulti { + return nil, fmt.Errorf("%q must be of type %s: %s", name, keyring.TypeMulti, multisigInfo.GetType()) + } + + return multisigInfo, nil +} diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index 251282bb8..955554512 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -16,10 +16,11 @@ import ( ) const ( - flagMultisig = "multisig" - flagOverwrite = "overwrite" - flagSigOnly = "signature-only" - flagAmino = "amino" + flagMultisig = "multisig" + flagOverwrite = "overwrite" + flagSigOnly = "signature-only" + flagAmino = "amino" + flagNoAutoIncrement = "no-auto-increment" ) // GetSignBatchCommand returns the transaction sign-batch command. @@ -203,8 +204,10 @@ func preSignCmd(cmd *cobra.Command, _ []string) { } func makeSignCmd() func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) + return func(cmd *cobra.Command, args []string) (err error) { + var clientCtx client.Context + + clientCtx, err = client.GetClientTxContext(cmd) if err != nil { return err } diff --git a/x/auth/client/rest/rest_test.go b/x/auth/client/rest/rest_test.go index 1196e52e4..cfca52750 100644 --- a/x/auth/client/rest/rest_test.go +++ b/x/auth/client/rest/rest_test.go @@ -49,7 +49,7 @@ func (s *IntegrationTestSuite) SetupSuite() { s.network = network.New(s.T(), cfg) kb := s.network.Validators[0].ClientCtx.Keyring - _, _, err := kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + _, _, err := kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) s.Require().NoError(err) _, err = s.network.WaitForHeight(1) @@ -156,6 +156,18 @@ func (s *IntegrationTestSuite) TestEncodeDecode() { require.Equal(stdTx, legacytx.StdTx(decodeResp)) } +func (s *IntegrationTestSuite) TestQueryAccountWithColon() { + val := s.network.Validators[0] + // This address is not a valid simapp address! It is only used to test that addresses with + // colon don't 501. See + // https://github.com/cosmos/cosmos-sdk/issues/8650 + addrWithColon := "cosmos:1m4f6lwd9eh8e5nxt0h00d46d3fr03apfh8qf4g" + + res, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/auth/v1beta1/accounts/%s", val.APIAddress, addrWithColon)) + s.Require().NoError(err) + s.Require().Contains(string(res), "decoding bech32 failed") +} + func (s *IntegrationTestSuite) TestEncodeIBCTx() { val := s.network.Validators[0] @@ -269,7 +281,7 @@ func (s *IntegrationTestSuite) TestQueryTxWithStdTx() { s.testQueryTx(s.stdTxRes.Height, s.stdTxRes.TxHash, val0.Address.String()) } -func (s *IntegrationTestSuite) TestQueryTxWithServiceMessage() { +func (s *IntegrationTestSuite) TestQueryTxWithServiceMsg() { val := s.network.Validators[0] sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) @@ -278,7 +290,7 @@ func (s *IntegrationTestSuite) TestQueryTxWithServiceMessage() { // Might need to wait a block to refresh sequences from previous setups. s.Require().NoError(s.network.WaitForNextBlock()) - out, err := bankcli.ServiceMsgSendExec( + out, err := bankcli.MsgSendExec( val.ClientCtx, val.Address, addr, diff --git a/x/auth/client/testutil/helpers.go b/x/auth/client/testutil/helpers.go index e4f317277..6d68ef923 100644 --- a/x/auth/client/testutil/helpers.go +++ b/x/auth/client/testutil/helpers.go @@ -92,4 +92,18 @@ func QueryAccountExec(clientCtx client.Context, address fmt.Stringer, extraArgs return clitestutil.ExecTestCLICmd(clientCtx, cli.GetAccountCmd(), append(args, extraArgs...)) } +func TxMultiSignBatchExec(clientCtx client.Context, filename string, from string, sigFile1 string, sigFile2 string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + filename, + from, + sigFile1, + sigFile2, + } + + args = append(args, extraArgs...) + + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetMultiSignBatchCmd(), args) +} + // DONTCOVER diff --git a/x/auth/keeper/grpc_query.go b/x/auth/keeper/grpc_query.go index cf3c7b844..4305c1cd0 100644 --- a/x/auth/keeper/grpc_query.go +++ b/x/auth/keeper/grpc_query.go @@ -3,6 +3,9 @@ package keeper import ( "context" + "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/types/query" + "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -13,6 +16,34 @@ import ( var _ types.QueryServer = AccountKeeper{} +func (ak AccountKeeper) Accounts(c context.Context, req *types.QueryAccountsRequest) (*types.QueryAccountsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(c) + store := ctx.KVStore(ak.key) + accountsStore := prefix.NewStore(store, types.AddressStoreKeyPrefix) + + var accounts []*codectypes.Any + pageRes, err := query.Paginate(accountsStore, req.Pagination, func(key, value []byte) error { + account := ak.decodeAccount(value) + any, err := codectypes.NewAnyWithValue(account) + if err != nil { + return err + } + + accounts = append(accounts, any) + return nil + }) + + if err != nil { + return nil, status.Errorf(codes.Internal, "paginate: %v", err) + } + + return &types.QueryAccountsResponse{Accounts: accounts, Pagination: pageRes}, err +} + // Account returns account details based on address func (ak AccountKeeper) Account(c context.Context, req *types.QueryAccountRequest) (*types.QueryAccountResponse, error) { if req == nil { diff --git a/x/auth/keeper/grpc_query_test.go b/x/auth/keeper/grpc_query_test.go index 47c48f027..e593829a7 100644 --- a/x/auth/keeper/grpc_query_test.go +++ b/x/auth/keeper/grpc_query_test.go @@ -8,6 +8,64 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/types" ) +func (suite *KeeperTestSuite) TestGRPCQueryAccounts() { + var ( + req *types.QueryAccountsRequest + ) + _, _, first := testdata.KeyTestPubAddr() + _, _, second := testdata.KeyTestPubAddr() + + testCases := []struct { + msg string + malleate func() + expPass bool + posttests func(res *types.QueryAccountsResponse) + }{ + { + "success", + func() { + suite.app.AccountKeeper.SetAccount(suite.ctx, + suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, first)) + suite.app.AccountKeeper.SetAccount(suite.ctx, + suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, second)) + req = &types.QueryAccountsRequest{} + }, + true, + func(res *types.QueryAccountsResponse) { + for _, acc := range res.Accounts { + var account types.AccountI + err := suite.app.InterfaceRegistry().UnpackAny(acc, &account) + suite.Require().NoError(err) + + suite.Require().True( + first.Equals(account.GetAddress()) || second.Equals(account.GetAddress())) + } + }, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.ctx) + + res, err := suite.queryClient.Accounts(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + + tc.posttests(res) + }) + } +} + func (suite *KeeperTestSuite) TestGRPCQueryAccount() { var ( req *types.QueryAccountRequest diff --git a/x/auth/legacy/legacytx/stdsign.go b/x/auth/legacy/legacytx/stdsign.go index 2ba194dad..466981369 100644 --- a/x/auth/legacy/legacytx/stdsign.go +++ b/x/auth/legacy/legacytx/stdsign.go @@ -35,6 +35,9 @@ type StdSignDoc struct { func StdSignBytes(chainID string, accnum, sequence, timeout uint64, fee StdFee, msgs []sdk.Msg, memo string) []byte { msgsBytes := make([]json.RawMessage, 0, len(msgs)) for _, msg := range msgs { + // If msg is a legacy Msg, then GetSignBytes is implemented. + // If msg is a ServiceMsg, then GetSignBytes has graceful support of + // calling GetSignBytes from its underlying Msg. msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes())) } diff --git a/x/auth/legacy/v034/types.go b/x/auth/legacy/v034/types.go index c5df67101..83fa2d36e 100644 --- a/x/auth/legacy/v034/types.go +++ b/x/auth/legacy/v034/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v034 import ( diff --git a/x/auth/legacy/v036/migrate.go b/x/auth/legacy/v036/migrate.go index c651df699..2ab4aa143 100644 --- a/x/auth/legacy/v036/migrate.go +++ b/x/auth/legacy/v036/migrate.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v036 import ( diff --git a/x/auth/legacy/v036/types.go b/x/auth/legacy/v036/types.go index 2490f4baa..908165f26 100644 --- a/x/auth/legacy/v036/types.go +++ b/x/auth/legacy/v036/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v036 import v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034" diff --git a/x/auth/legacy/v038/types.go b/x/auth/legacy/v038/types.go index e9977c1d2..b7dedf003 100644 --- a/x/auth/legacy/v038/types.go +++ b/x/auth/legacy/v038/types.go @@ -1,7 +1,6 @@ package v038 // DONTCOVER -// nolint import ( "bytes" diff --git a/x/auth/legacy/v039/types.go b/x/auth/legacy/v039/types.go index 55c3014eb..3de576f27 100644 --- a/x/auth/legacy/v039/types.go +++ b/x/auth/legacy/v039/types.go @@ -1,7 +1,6 @@ package v039 // DONTCOVER -// nolint import ( "bytes" diff --git a/x/auth/module.go b/x/auth/module.go index 9e4af71ff..ff053d8c7 100644 --- a/x/auth/module.go +++ b/x/auth/module.go @@ -67,7 +67,9 @@ func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Rout // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the auth module. func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { - types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) + if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil { + panic(err) + } } // GetTxCmd returns the root tx command for the auth module. @@ -85,8 +87,6 @@ func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) types.RegisterInterfaces(registry) } -//____________________________________________________________________________ - // AppModule implements an application module for the auth module. type AppModule struct { AppModuleBasic @@ -159,8 +159,6 @@ func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.Validato return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the auth module diff --git a/x/auth/signing/verify_test.go b/x/auth/signing/verify_test.go index 7e842d135..22cd3eb27 100644 --- a/x/auth/signing/verify_test.go +++ b/x/auth/signing/verify_test.go @@ -40,9 +40,9 @@ func TestVerifySignature(t *testing.T) { _ = app.AccountKeeper.NewAccountWithAddress(ctx, addr1) app.AccountKeeper.SetAccount(ctx, acc1) balances := sdk.NewCoins(sdk.NewInt64Coin("atom", 200)) - require.NoError(t, app.BankKeeper.SetBalances(ctx, addr, balances)) + require.NoError(t, simapp.FundAccount(app, ctx, addr, balances)) acc, err := ante.GetSignerAcc(ctx, app.AccountKeeper, addr) - require.NoError(t, app.BankKeeper.SetBalances(ctx, addr, balances)) + require.NoError(t, simapp.FundAccount(app, ctx, addr, balances)) msgs := []sdk.Msg{testdata.NewTestMsg(addr)} fee := legacytx.NewStdFee(50000, sdk.Coins{sdk.NewInt64Coin("atom", 150)}) diff --git a/x/auth/testutil/suite.go b/x/auth/testutil/suite.go index f8002e68e..15f6334b4 100644 --- a/x/auth/testutil/suite.go +++ b/x/auth/testutil/suite.go @@ -15,8 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/signing" ) -// TxConfigTestSuite provides a test suite that can be used to test that a TxConfig implementation is correct -//nolint:golint // type name will be used as tx.TxConfigTestSuite by other packages, and that stutters; consider calling this GeneratorTestSuite +// TxConfigTestSuite provides a test suite that can be used to test that a TxConfig implementation is correct. type TxConfigTestSuite struct { suite.Suite TxConfig client.TxConfig @@ -200,16 +199,7 @@ func sigDataEquals(data1, data2 signingtypes.SignatureData) bool { if !ok { return false } - - if data1.BitArray.ExtraBitsStored != data2.BitArray.ExtraBitsStored { - return false - } - - if !bytes.Equal(data1.BitArray.Elems, data2.BitArray.Elems) { - return false - } - - if len(data1.Signatures) != len(data2.Signatures) { + if !data1.BitArray.Equal(data2.BitArray) || len(data1.Signatures) != len(data2.Signatures) { return false } diff --git a/x/auth/tx/service_test.go b/x/auth/tx/service_test.go index b7b3322dc..58b21c139 100644 --- a/x/auth/tx/service_test.go +++ b/x/auth/tx/service_test.go @@ -5,13 +5,14 @@ import ( "fmt" "testing" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + "github.com/stretchr/testify/suite" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" clienttx "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/testutil/network" - "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" query "github.com/cosmos/cosmos-sdk/types/query" @@ -106,7 +107,7 @@ func (s IntegrationTestSuite) TestSimulateTx_GRPC() { } else { s.Require().NoError(err) // Check the result and gas used are correct. - s.Require().Equal(len(res.GetResult().GetEvents()), 4) // 1 transfer, 3 messages. + s.Require().Equal(len(res.GetResult().GetEvents()), 6) // 1 coin recv 1 coin spent, 1 transfer, 3 messages. s.Require().True(res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. } }) @@ -143,7 +144,7 @@ func (s IntegrationTestSuite) TestSimulateTx_GRPCGateway() { err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) s.Require().NoError(err) // Check the result and gas used are correct. - s.Require().Equal(len(result.GetResult().GetEvents()), 4) // 1 transfer, 3 messages. + s.Require().Equal(len(result.GetResult().GetEvents()), 6) // 1 coin recv, 1 coin spent,1 transfer, 3 messages. s.Require().True(result.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. } }) @@ -175,14 +176,14 @@ func (s IntegrationTestSuite) TestGetTxEvents_GRPC() { { "without pagination", &tx.GetTxsEventRequest{ - Events: []string{"message.action=send"}, + Events: []string{"message.action=/cosmos.bank.v1beta1.Msg/Send"}, }, false, "", }, { "with pagination", &tx.GetTxsEventRequest{ - Events: []string{"message.action=send"}, + Events: []string{"message.action=/cosmos.bank.v1beta1.Msg/Send"}, Pagination: &query.PageRequest{ CountTotal: false, Offset: 0, @@ -194,7 +195,7 @@ func (s IntegrationTestSuite) TestGetTxEvents_GRPC() { { "with multi events", &tx.GetTxsEventRequest{ - Events: []string{"message.action=send", "message.module=bank"}, + Events: []string{"message.action=/cosmos.bank.v1beta1.Msg/Send", "message.module=bank"}, }, false, "", }, @@ -231,25 +232,25 @@ func (s IntegrationTestSuite) TestGetTxEvents_GRPCGateway() { }, { "without pagination", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, "message.action=send"), + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, "message.action=/cosmos.bank.v1beta1.Msg/Send"), false, "", }, { "with pagination", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&pagination.offset=%d&pagination.limit=%d", val.APIAddress, "message.action=send", 0, 10), + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&pagination.offset=%d&pagination.limit=%d", val.APIAddress, "message.action=/cosmos.bank.v1beta1.Msg/Send", 0, 10), false, "", }, { "expect pass with multiple-events", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s", val.APIAddress, "message.action=send", "message.module=bank"), + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s", val.APIAddress, "message.action=/cosmos.bank.v1beta1.Msg/Send", "message.module=bank"), false, "", }, { "expect pass with escape event", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, "message.action%3Dsend"), + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, "message.action%3D%2Fcosmos.bank.v1beta1.Msg%2FSend"), false, "", }, @@ -412,7 +413,7 @@ func (s IntegrationTestSuite) TestBroadcastTx_GRPCGateway() { var result tx.BroadcastTxResponse err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) s.Require().NoError(err) - s.Require().Equal(uint32(0), result.TxResponse.Code) + s.Require().Equal(uint32(0), result.TxResponse.Code, "rawlog", result.TxResponse.RawLog) } }) } diff --git a/x/auth/types/account.go b/x/auth/types/account.go index eb9939ffc..653f89bca 100644 --- a/x/auth/types/account.go +++ b/x/auth/types/account.go @@ -48,6 +48,7 @@ func ProtoBaseAccount() AccountI { } // NewBaseAccountWithAddress - returns a new base account with a given address +// leaving AccountNumber and Sequence to zero. func NewBaseAccountWithAddress(addr sdk.AccAddress) *BaseAccount { return &BaseAccount{ Address: addr.String(), diff --git a/x/auth/types/account_retriever.go b/x/auth/types/account_retriever.go index f84c74418..792524fff 100644 --- a/x/auth/types/account_retriever.go +++ b/x/auth/types/account_retriever.go @@ -32,7 +32,6 @@ func (ar AccountRetriever) GetAccount(clientCtx client.Context, addr sdk.AccAddr // GetAccountWithHeight queries for an account given an address. Returns the // height of the query with the account. An error is returned if the query // or decoding fails. -//nolint:interfacer func (ar AccountRetriever) GetAccountWithHeight(clientCtx client.Context, addr sdk.AccAddress) (client.Account, int64, error) { var header metadata.MD diff --git a/x/auth/types/params.go b/x/auth/types/params.go index 710ee963b..13db369d2 100644 --- a/x/auth/types/params.go +++ b/x/auth/types/params.go @@ -48,7 +48,6 @@ func ParamKeyTable() paramtypes.KeyTable { // ParamSetPairs implements the ParamSet interface and returns all the key/value pairs // pairs of auth module's parameters. -// nolint func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{ paramtypes.NewParamSetPair(KeyMaxMemoCharacters, &p.MaxMemoCharacters, validateMaxMemoCharacters), diff --git a/x/auth/types/query.pb.go b/x/auth/types/query.pb.go index fcb0efa96..25c0a1363 100644 --- a/x/auth/types/query.pb.go +++ b/x/auth/types/query.pb.go @@ -7,6 +7,7 @@ import ( context "context" fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" + query "github.com/cosmos/cosmos-sdk/types/query" _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" @@ -31,6 +32,107 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// QueryAccountsRequest is the request type for the Query/Accounts RPC method. +type QueryAccountsRequest struct { + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAccountsRequest) Reset() { *m = QueryAccountsRequest{} } +func (m *QueryAccountsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAccountsRequest) ProtoMessage() {} +func (*QueryAccountsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c451370b3929a27c, []int{0} +} +func (m *QueryAccountsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAccountsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAccountsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryAccountsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAccountsRequest.Merge(m, src) +} +func (m *QueryAccountsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAccountsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAccountsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAccountsRequest proto.InternalMessageInfo + +func (m *QueryAccountsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryAccountsResponse is the response type for the Query/Accounts RPC method. +type QueryAccountsResponse struct { + // accounts are the existing accounts + Accounts []*types.Any `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts,omitempty"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAccountsResponse) Reset() { *m = QueryAccountsResponse{} } +func (m *QueryAccountsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAccountsResponse) ProtoMessage() {} +func (*QueryAccountsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c451370b3929a27c, []int{1} +} +func (m *QueryAccountsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAccountsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAccountsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryAccountsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAccountsResponse.Merge(m, src) +} +func (m *QueryAccountsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAccountsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAccountsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAccountsResponse proto.InternalMessageInfo + +func (m *QueryAccountsResponse) GetAccounts() []*types.Any { + if m != nil { + return m.Accounts + } + return nil +} + +func (m *QueryAccountsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + // QueryAccountRequest is the request type for the Query/Account RPC method. type QueryAccountRequest struct { // address defines the address to query for. @@ -41,7 +143,7 @@ func (m *QueryAccountRequest) Reset() { *m = QueryAccountRequest{} } func (m *QueryAccountRequest) String() string { return proto.CompactTextString(m) } func (*QueryAccountRequest) ProtoMessage() {} func (*QueryAccountRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_c451370b3929a27c, []int{0} + return fileDescriptor_c451370b3929a27c, []int{2} } func (m *QueryAccountRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -80,7 +182,7 @@ func (m *QueryAccountResponse) Reset() { *m = QueryAccountResponse{} } func (m *QueryAccountResponse) String() string { return proto.CompactTextString(m) } func (*QueryAccountResponse) ProtoMessage() {} func (*QueryAccountResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_c451370b3929a27c, []int{1} + return fileDescriptor_c451370b3929a27c, []int{3} } func (m *QueryAccountResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -124,7 +226,7 @@ func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } func (*QueryParamsRequest) ProtoMessage() {} func (*QueryParamsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_c451370b3929a27c, []int{2} + return fileDescriptor_c451370b3929a27c, []int{4} } func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -163,7 +265,7 @@ func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } func (*QueryParamsResponse) ProtoMessage() {} func (*QueryParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_c451370b3929a27c, []int{3} + return fileDescriptor_c451370b3929a27c, []int{5} } func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -200,6 +302,8 @@ func (m *QueryParamsResponse) GetParams() Params { } func init() { + proto.RegisterType((*QueryAccountsRequest)(nil), "cosmos.auth.v1beta1.QueryAccountsRequest") + proto.RegisterType((*QueryAccountsResponse)(nil), "cosmos.auth.v1beta1.QueryAccountsResponse") proto.RegisterType((*QueryAccountRequest)(nil), "cosmos.auth.v1beta1.QueryAccountRequest") proto.RegisterType((*QueryAccountResponse)(nil), "cosmos.auth.v1beta1.QueryAccountResponse") proto.RegisterType((*QueryParamsRequest)(nil), "cosmos.auth.v1beta1.QueryParamsRequest") @@ -209,34 +313,41 @@ func init() { func init() { proto.RegisterFile("cosmos/auth/v1beta1/query.proto", fileDescriptor_c451370b3929a27c) } var fileDescriptor_c451370b3929a27c = []byte{ - // 424 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x31, 0x6f, 0xda, 0x40, - 0x14, 0xc7, 0x6d, 0xd4, 0x02, 0xbd, 0x76, 0x3a, 0x3c, 0x50, 0xd3, 0xda, 0x95, 0x3b, 0x00, 0x03, - 0x77, 0x82, 0x4e, 0x54, 0x5d, 0xa0, 0x53, 0x37, 0x6a, 0x75, 0xea, 0x52, 0x9d, 0xcd, 0xd5, 0xa0, - 0x16, 0x9f, 0xf1, 0x9d, 0xab, 0xa2, 0xaa, 0x52, 0x94, 0x29, 0x5b, 0x22, 0x65, 0xcd, 0xc0, 0x87, - 0xc8, 0x87, 0x40, 0x99, 0x90, 0xb2, 0x64, 0x8a, 0x22, 0xc8, 0x90, 0x8f, 0x11, 0x71, 0x77, 0x1e, - 0x90, 0x1c, 0x25, 0x13, 0xbc, 0xf7, 0xfe, 0xff, 0xff, 0xfb, 0xf9, 0x1d, 0x70, 0x43, 0xc6, 0x67, - 0x8c, 0x63, 0x92, 0x89, 0x09, 0xfe, 0xd3, 0x0d, 0xa8, 0x20, 0x5d, 0x3c, 0xcf, 0x68, 0xba, 0x40, - 0x49, 0xca, 0x04, 0x83, 0x35, 0x25, 0x40, 0x3b, 0x01, 0xd2, 0x02, 0xdb, 0x8a, 0x58, 0xc4, 0xe4, - 0x1c, 0xef, 0xfe, 0x29, 0xa9, 0xfd, 0x3a, 0x62, 0x2c, 0xfa, 0x4d, 0xb1, 0xac, 0x82, 0xec, 0x27, - 0x26, 0xb1, 0x4e, 0xb1, 0xdf, 0xe8, 0x11, 0x49, 0xa6, 0x98, 0xc4, 0x31, 0x13, 0x44, 0x4c, 0x59, - 0xcc, 0xf5, 0xd4, 0x29, 0x82, 0x90, 0x0b, 0x75, 0xb0, 0x9a, 0xff, 0x50, 0x1b, 0x35, 0x90, 0x2c, - 0xbc, 0x3e, 0xa8, 0x7d, 0xdd, 0xd1, 0x0e, 0xc2, 0x90, 0x65, 0xb1, 0xf0, 0xe9, 0x3c, 0xa3, 0x5c, - 0xc0, 0x3a, 0xa8, 0x90, 0xf1, 0x38, 0xa5, 0x9c, 0xd7, 0xcd, 0x77, 0x66, 0xeb, 0x85, 0x9f, 0x97, - 0x1f, 0xab, 0x47, 0x4b, 0xd7, 0xb8, 0x5b, 0xba, 0x86, 0xf7, 0x0d, 0x58, 0xfb, 0x56, 0x9e, 0xb0, - 0x98, 0x53, 0xf8, 0x09, 0x54, 0x88, 0x6a, 0x49, 0xef, 0xcb, 0x9e, 0x85, 0x14, 0x3d, 0xca, 0x3f, - 0x0c, 0x0d, 0xe2, 0xc5, 0xf0, 0xd5, 0xc5, 0x79, 0xa7, 0xaa, 0xbd, 0x5f, 0xfc, 0xdc, 0xe2, 0x59, - 0x00, 0xca, 0xd4, 0x11, 0x49, 0xc9, 0x8c, 0x6b, 0x1e, 0x6f, 0xa4, 0x31, 0xf3, 0xae, 0x5e, 0xd5, - 0x07, 0xe5, 0x44, 0x76, 0xf4, 0xa6, 0x06, 0x2a, 0xb8, 0x36, 0x52, 0xa6, 0xe1, 0xb3, 0xd5, 0xb5, - 0x6b, 0xf8, 0xda, 0xd0, 0x3b, 0x2b, 0x81, 0xe7, 0x32, 0x12, 0x1e, 0x9b, 0xa0, 0xa2, 0x39, 0x60, - 0xab, 0x30, 0xa0, 0xe0, 0x42, 0x76, 0xfb, 0x09, 0x4a, 0x45, 0xe9, 0xe1, 0xc3, 0xcb, 0xdb, 0xd3, - 0x52, 0x1b, 0x36, 0x71, 0xe1, 0x3b, 0x29, 0x35, 0xc7, 0xff, 0xf4, 0x89, 0xff, 0xc3, 0x03, 0x13, - 0x94, 0x15, 0x34, 0x6c, 0x3e, 0xbc, 0x66, 0xef, 0x42, 0x76, 0xeb, 0x71, 0xa1, 0xc6, 0x79, 0x2f, - 0x71, 0xde, 0xc2, 0x46, 0x21, 0x8e, 0x3a, 0xcf, 0xf0, 0xf3, 0x6a, 0xe3, 0x98, 0xeb, 0x8d, 0x63, - 0xde, 0x6c, 0x1c, 0xf3, 0x64, 0xeb, 0x18, 0xeb, 0xad, 0x63, 0x5c, 0x6d, 0x1d, 0xe3, 0x7b, 0x3b, - 0x9a, 0x8a, 0x49, 0x16, 0xa0, 0x90, 0xcd, 0xf2, 0x00, 0xf5, 0xd3, 0xe1, 0xe3, 0x5f, 0xf8, 0xaf, - 0x4a, 0x13, 0x8b, 0x84, 0xf2, 0xa0, 0x2c, 0x1f, 0xfc, 0xc3, 0x7d, 0x00, 0x00, 0x00, 0xff, 0xff, - 0xef, 0x40, 0xd8, 0x2e, 0x25, 0x03, 0x00, 0x00, + // 537 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x41, 0x6b, 0x13, 0x4f, + 0x18, 0xc6, 0x77, 0xda, 0xff, 0x3f, 0x89, 0x53, 0x4f, 0xd3, 0x08, 0x71, 0x6b, 0x77, 0xcb, 0x8a, + 0x26, 0x29, 0x74, 0x86, 0xc6, 0x53, 0x45, 0x84, 0x46, 0x50, 0xbc, 0xc5, 0xc5, 0x93, 0x07, 0x65, + 0x36, 0x19, 0xb7, 0x41, 0xb3, 0xb3, 0xcd, 0xec, 0x8a, 0x41, 0x04, 0xf1, 0xd4, 0x9b, 0x82, 0x5f, + 0x20, 0x37, 0xbf, 0x80, 0x1f, 0xa2, 0x78, 0x2a, 0x78, 0xf1, 0x24, 0x92, 0x78, 0xf0, 0x63, 0x48, + 0x66, 0xde, 0x89, 0x8d, 0xac, 0x26, 0xa7, 0xdd, 0x99, 0x79, 0x9f, 0xe7, 0xf9, 0xbd, 0xef, 0x0c, + 0xf6, 0xbb, 0x52, 0x0d, 0xa4, 0x62, 0x3c, 0xcf, 0x8e, 0xd8, 0x8b, 0xfd, 0x48, 0x64, 0x7c, 0x9f, + 0x1d, 0xe7, 0x62, 0x38, 0xa2, 0xe9, 0x50, 0x66, 0x92, 0x6c, 0x9a, 0x02, 0x3a, 0x2b, 0xa0, 0x50, + 0xe0, 0xee, 0x82, 0x2a, 0xe2, 0x4a, 0x98, 0xea, 0xb9, 0x36, 0xe5, 0x71, 0x3f, 0xe1, 0x59, 0x5f, + 0x26, 0xc6, 0xc0, 0xad, 0xc6, 0x32, 0x96, 0xfa, 0x97, 0xcd, 0xfe, 0x60, 0xf7, 0x72, 0x2c, 0x65, + 0xfc, 0x5c, 0x30, 0xbd, 0x8a, 0xf2, 0xa7, 0x8c, 0x27, 0x90, 0xe8, 0x5e, 0x81, 0x23, 0x9e, 0xf6, + 0x19, 0x4f, 0x12, 0x99, 0x69, 0x37, 0x05, 0xa7, 0x5e, 0x11, 0xb0, 0x86, 0x03, 0x63, 0x73, 0xfe, + 0xc4, 0x24, 0x02, 0xbc, 0x5e, 0x04, 0x8f, 0x71, 0xf5, 0xc1, 0x8c, 0xf5, 0xb0, 0xdb, 0x95, 0x79, + 0x92, 0xa9, 0x50, 0x1c, 0xe7, 0x42, 0x65, 0xe4, 0x2e, 0xc6, 0xbf, 0xa9, 0x6b, 0x68, 0x07, 0x35, + 0x36, 0x5a, 0xd7, 0x29, 0x48, 0x67, 0x2d, 0x52, 0x33, 0x10, 0x48, 0xa3, 0x1d, 0x1e, 0x0b, 0xd0, + 0x86, 0xe7, 0x94, 0xc1, 0x18, 0xe1, 0x4b, 0x7f, 0x04, 0xa8, 0x54, 0x26, 0x4a, 0x90, 0xdb, 0xb8, + 0xc2, 0x61, 0xaf, 0x86, 0x76, 0xd6, 0x1b, 0x1b, 0xad, 0x2a, 0x35, 0x5d, 0x52, 0x3b, 0x00, 0x7a, + 0x98, 0x8c, 0xda, 0x17, 0x3f, 0x7f, 0xda, 0xab, 0x80, 0xfa, 0x7e, 0x38, 0xd7, 0x90, 0x7b, 0x0b, + 0x84, 0x6b, 0x9a, 0xb0, 0xbe, 0x94, 0xd0, 0x84, 0x2f, 0x20, 0x1e, 0xe0, 0xcd, 0xf3, 0x84, 0x76, + 0x02, 0x35, 0x5c, 0xe6, 0xbd, 0xde, 0x50, 0x28, 0xa5, 0xdb, 0xbf, 0x10, 0xda, 0xe5, 0xcd, 0xca, + 0xc9, 0xd8, 0x77, 0x7e, 0x8e, 0x7d, 0x27, 0x78, 0xb8, 0x38, 0xbd, 0x79, 0x6f, 0xb7, 0x70, 0x19, + 0x38, 0x61, 0x74, 0xab, 0xb4, 0x66, 0x25, 0x41, 0x15, 0x13, 0xed, 0xda, 0xe1, 0x43, 0x3e, 0xb0, + 0x37, 0x12, 0x74, 0x00, 0xd3, 0xee, 0x42, 0xd4, 0x01, 0x2e, 0xa5, 0x7a, 0x07, 0x92, 0xb6, 0x68, + 0xc1, 0xe3, 0xa4, 0x46, 0xd4, 0xfe, 0xef, 0xf4, 0x9b, 0xef, 0x84, 0x20, 0x68, 0x7d, 0x5c, 0xc7, + 0xff, 0x6b, 0x4b, 0x72, 0x82, 0xb0, 0xe5, 0x50, 0xa4, 0x59, 0xe8, 0x50, 0xf4, 0x4a, 0xdc, 0xdd, + 0x55, 0x4a, 0x0d, 0x68, 0x70, 0xed, 0xed, 0x97, 0x1f, 0x1f, 0xd6, 0x7c, 0xb2, 0xcd, 0x0a, 0x5f, + 0xab, 0x4d, 0x7f, 0x87, 0x70, 0x19, 0xb4, 0xa4, 0xb1, 0xd4, 0xde, 0x82, 0x34, 0x57, 0xa8, 0x04, + 0x0e, 0xa6, 0x39, 0x9a, 0xa4, 0xfe, 0x4f, 0x0e, 0xf6, 0x0a, 0x6e, 0xfb, 0x35, 0x79, 0x83, 0x70, + 0xc9, 0xcc, 0x8f, 0xd4, 0xff, 0x1e, 0xb3, 0x70, 0x59, 0x6e, 0x63, 0x79, 0x21, 0xe0, 0x5c, 0xd5, + 0x38, 0xdb, 0x64, 0xab, 0x10, 0xc7, 0xdc, 0x54, 0xfb, 0xce, 0xe9, 0xc4, 0x43, 0x67, 0x13, 0x0f, + 0x7d, 0x9f, 0x78, 0xe8, 0xfd, 0xd4, 0x73, 0xce, 0xa6, 0x9e, 0xf3, 0x75, 0xea, 0x39, 0x8f, 0x9a, + 0x71, 0x3f, 0x3b, 0xca, 0x23, 0xda, 0x95, 0x03, 0x6b, 0x60, 0x3e, 0x7b, 0xaa, 0xf7, 0x8c, 0xbd, + 0x34, 0x6e, 0xd9, 0x28, 0x15, 0x2a, 0x2a, 0xe9, 0xb7, 0x77, 0xe3, 0x57, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x2a, 0xe1, 0x81, 0xd3, 0xdf, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -251,6 +362,8 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type QueryClient interface { + // Accounts returns all the existing accounts + Accounts(ctx context.Context, in *QueryAccountsRequest, opts ...grpc.CallOption) (*QueryAccountsResponse, error) // Account returns account details based on address. Account(ctx context.Context, in *QueryAccountRequest, opts ...grpc.CallOption) (*QueryAccountResponse, error) // Params queries all parameters. @@ -265,6 +378,15 @@ func NewQueryClient(cc grpc1.ClientConn) QueryClient { return &queryClient{cc} } +func (c *queryClient) Accounts(ctx context.Context, in *QueryAccountsRequest, opts ...grpc.CallOption) (*QueryAccountsResponse, error) { + out := new(QueryAccountsResponse) + err := c.cc.Invoke(ctx, "/cosmos.auth.v1beta1.Query/Accounts", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *queryClient) Account(ctx context.Context, in *QueryAccountRequest, opts ...grpc.CallOption) (*QueryAccountResponse, error) { out := new(QueryAccountResponse) err := c.cc.Invoke(ctx, "/cosmos.auth.v1beta1.Query/Account", in, out, opts...) @@ -285,6 +407,8 @@ func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts . // QueryServer is the server API for Query service. type QueryServer interface { + // Accounts returns all the existing accounts + Accounts(context.Context, *QueryAccountsRequest) (*QueryAccountsResponse, error) // Account returns account details based on address. Account(context.Context, *QueryAccountRequest) (*QueryAccountResponse, error) // Params queries all parameters. @@ -295,6 +419,9 @@ type QueryServer interface { type UnimplementedQueryServer struct { } +func (*UnimplementedQueryServer) Accounts(ctx context.Context, req *QueryAccountsRequest) (*QueryAccountsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Accounts not implemented") +} func (*UnimplementedQueryServer) Account(ctx context.Context, req *QueryAccountRequest) (*QueryAccountResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Account not implemented") } @@ -306,6 +433,24 @@ func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) } +func _Query_Accounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAccountsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Accounts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.auth.v1beta1.Query/Accounts", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Accounts(ctx, req.(*QueryAccountsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Query_Account_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryAccountRequest) if err := dec(in); err != nil { @@ -346,6 +491,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "cosmos.auth.v1beta1.Query", HandlerType: (*QueryServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "Accounts", + Handler: _Query_Accounts_Handler, + }, { MethodName: "Account", Handler: _Query_Account_Handler, @@ -359,6 +508,90 @@ var _Query_serviceDesc = grpc.ServiceDesc{ Metadata: "cosmos/auth/v1beta1/query.proto", } +func (m *QueryAccountsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryAccountsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAccountsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAccountsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryAccountsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAccountsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Accounts) > 0 { + for iNdEx := len(m.Accounts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Accounts[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *QueryAccountRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -491,6 +724,38 @@ func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } +func (m *QueryAccountsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAccountsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Accounts) > 0 { + for _, e := range m.Accounts { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func (m *QueryAccountRequest) Size() (n int) { if m == nil { return 0 @@ -543,6 +808,212 @@ func sovQuery(x uint64) (n int) { func sozQuery(x uint64) (n int) { return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (m *QueryAccountsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryAccountsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAccountsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryAccountsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryAccountsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAccountsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Accounts", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Accounts = append(m.Accounts, &types.Any{}) + if err := m.Accounts[len(m.Accounts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryAccountRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/auth/types/query.pb.gw.go b/x/auth/types/query.pb.gw.go index 0525b9c15..f6d93f292 100644 --- a/x/auth/types/query.pb.gw.go +++ b/x/auth/types/query.pb.gw.go @@ -31,6 +31,42 @@ var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage +var ( + filter_Query_Accounts_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_Accounts_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAccountsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Accounts_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Accounts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Accounts_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAccountsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Accounts_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Accounts(ctx, &protoReq) + return msg, metadata, err + +} + func request_Query_Account_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryAccountRequest var metadata runtime.ServerMetadata @@ -109,6 +145,26 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal // Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + mux.Handle("GET", pattern_Query_Accounts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Accounts_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Accounts_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_Account_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -190,6 +246,26 @@ func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc // "QueryClient" to call the correct interceptors. func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + mux.Handle("GET", pattern_Query_Accounts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Accounts_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Accounts_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_Account_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -234,12 +310,16 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Account_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "auth", "v1beta1", "accounts", "address"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Accounts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "auth", "v1beta1", "accounts"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "auth", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Account_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "auth", "v1beta1", "accounts", "address"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "auth", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( + forward_Query_Accounts_0 = runtime.ForwardResponseMessage + forward_Query_Account_0 = runtime.ForwardResponseMessage forward_Query_Params_0 = runtime.ForwardResponseMessage diff --git a/x/auth/vesting/client/cli/tx.go b/x/auth/vesting/client/cli/tx.go index 9cacdfdaa..c6c2ac143 100644 --- a/x/auth/vesting/client/cli/tx.go +++ b/x/auth/vesting/client/cli/tx.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" ) @@ -69,11 +70,14 @@ timestamp.`, delayed, _ := cmd.Flags().GetBool(FlagDelayed) msg := types.NewMsgCreateVestingAccount(clientCtx.GetFromAddress(), toAddr, amount, endTime, delayed) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.CreateVestingAccount(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } diff --git a/x/auth/vesting/handler_test.go b/x/auth/vesting/handler_test.go index 490fc5997..48be156b8 100644 --- a/x/auth/vesting/handler_test.go +++ b/x/auth/vesting/handler_test.go @@ -37,7 +37,7 @@ func (suite *HandlerTestSuite) TestMsgCreateVestingAccount() { acc1 := suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr1) suite.app.AccountKeeper.SetAccount(ctx, acc1) - suite.Require().NoError(suite.app.BankKeeper.SetBalances(ctx, addr1, balances)) + suite.Require().NoError(simapp.FundAccount(suite.app, ctx, addr1, balances)) testCases := []struct { name string diff --git a/x/auth/vesting/types/vesting_account.go b/x/auth/vesting/types/vesting_account.go index e011bd6ef..0438a338b 100644 --- a/x/auth/vesting/types/vesting_account.go +++ b/x/auth/vesting/types/vesting_account.go @@ -19,7 +19,6 @@ var ( _ vestexported.VestingAccount = (*DelayedVestingAccount)(nil) ) -//----------------------------------------------------------------------------- // Base Vesting Account // NewBaseVestingAccount creates a new BaseVestingAccount object. It is the @@ -216,7 +215,6 @@ func (bva BaseVestingAccount) MarshalYAML() (interface{}, error) { return string(bz), err } -//----------------------------------------------------------------------------- // Continuous Vesting Account var _ vestexported.VestingAccount = (*ContinuousVestingAccount)(nil) @@ -345,7 +343,6 @@ func (cva ContinuousVestingAccount) MarshalYAML() (interface{}, error) { return string(bz), err } -//----------------------------------------------------------------------------- // Periodic Vesting Account var _ vestexported.VestingAccount = (*PeriodicVestingAccount)(nil) @@ -504,7 +501,6 @@ func (pva PeriodicVestingAccount) MarshalYAML() (interface{}, error) { return string(bz), err } -//----------------------------------------------------------------------------- // Delayed Vesting Account var _ vestexported.VestingAccount = (*DelayedVestingAccount)(nil) diff --git a/x/authz/client/cli/query.go b/x/authz/client/cli/query.go index ef7f326f7..269a68e09 100644 --- a/x/authz/client/cli/query.go +++ b/x/authz/client/cli/query.go @@ -12,6 +12,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/authz/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" ) // GetQueryCmd returns the cli query commands for this module @@ -97,7 +98,7 @@ func GetCmdQueryAuthorization() *cobra.Command { fmt.Sprintf(`Query authorization for a granter-grantee pair that matches the given msg-type: Example: $ %s query %s authorization cosmos1skjw.. cosmos1skjwj.. %s -`, version.AppName, types.ModuleName, types.SendAuthorization{}.MethodName()), +`, version.AppName, types.ModuleName, bank.SendAuthorization{}.MethodName()), ), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) diff --git a/x/authz/client/cli/query_test.go b/x/authz/client/cli/query_test.go new file mode 100644 index 000000000..e011d990d --- /dev/null +++ b/x/authz/client/cli/query_test.go @@ -0,0 +1,186 @@ +// +build norace + +package cli_test + +import ( + "fmt" + "strings" + "time" + + tmcli "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz/client/cli" + + authztestutil "github.com/cosmos/cosmos-sdk/x/authz/client/testutil" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +func (s *IntegrationTestSuite) TestQueryAuthorizations() { + val := s.network.Validators[0] + + grantee := s.grantee + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := authztestutil.ExecGrantAuthorization( + val, + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expErrMsg string + }{ + { + "Error: Invalid grantee", + []string{ + val.Address.String(), + "invalid grantee", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + "decoding bech32 failed: invalid character in string: ' '", + }, + { + "Error: Invalid granter", + []string{ + "invalid granter", + grantee.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + "decoding bech32 failed: invalid character in string: ' '", + }, + { + "Valid txn (json)", + []string{ + val.Address.String(), + grantee.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + ``, + }, + } + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryAuthorizations() + clientCtx := val.ClientCtx + resp, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(string(resp.Bytes()), tc.expErrMsg) + } else { + s.Require().NoError(err) + var grants types.QueryAuthorizationsResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &grants) + s.Require().NoError(err) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryAuthorization() { + val := s.network.Validators[0] + + grantee := s.grantee + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := authztestutil.ExecGrantAuthorization( + val, + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput string + }{ + { + "Error: Invalid grantee", + []string{ + val.Address.String(), + "invalid grantee", + typeMsgSend, + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + "", + }, + { + "Error: Invalid granter", + []string{ + "invalid granter", + grantee.String(), + typeMsgSend, + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + "", + }, + { + "no authorization found", + []string{ + val.Address.String(), + grantee.String(), + "typeMsgSend", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + "", + }, + { + "Valid txn (json)", + []string{ + val.Address.String(), + grantee.String(), + typeMsgSend, + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + `{"@type":"/cosmos.bank.v1beta1.SendAuthorization","spend_limit":[{"denom":"steak","amount":"100"}]}`, + }, + } + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryAuthorization() + clientCtx := val.ClientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Contains(strings.TrimSpace(out.String()), tc.expectedOutput) + } + }) + } +} diff --git a/x/authz/client/cli/tx.go b/x/authz/client/cli/tx.go index ba2fd2101..44df6f5e9 100644 --- a/x/authz/client/cli/tx.go +++ b/x/authz/client/cli/tx.go @@ -16,12 +16,20 @@ import ( "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/version" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + "github.com/cosmos/cosmos-sdk/x/authz/exported" "github.com/cosmos/cosmos-sdk/x/authz/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + staking "github.com/cosmos/cosmos-sdk/x/staking/types" ) const FlagSpendLimit = "spend-limit" const FlagMsgType = "msg-type" const FlagExpiration = "expiration" +const FlagAllowedValidators = "allowed-validators" +const FlagDenyValidators = "deny-validators" +const delegate = "delegate" +const redelegate = "redelegate" +const unbond = "unbond" // GetTxCmd returns the transaction commands for this module func GetTxCmd() *cobra.Command { @@ -45,7 +53,7 @@ func GetTxCmd() *cobra.Command { func NewCmdGrantAuthorization() *cobra.Command { cmd := &cobra.Command{ - Use: "grant --from ", + Use: "grant --from ", Short: "Grant authorization to an address", Long: strings.TrimSpace( fmt.Sprintf(`Grant authorization to an address to execute a transaction on your behalf: @@ -53,20 +61,26 @@ func NewCmdGrantAuthorization() *cobra.Command { Examples: $ %s tx %s grant cosmos1skjw.. send %s --spend-limit=1000stake --from=cosmos1skl.. $ %s tx %s grant cosmos1skjw.. generic --msg-type=/cosmos.gov.v1beta1.Msg/Vote --from=cosmos1sk.. - `, version.AppName, types.ModuleName, types.SendAuthorization{}.MethodName(), version.AppName, types.ModuleName), + `, version.AppName, types.ModuleName, bank.SendAuthorization{}.MethodName(), version.AppName, types.ModuleName), ), - Args: cobra.RangeArgs(2, 3), + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) if err != nil { return err } + grantee, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } - var authorization types.Authorization + exp, err := cmd.Flags().GetInt64(FlagExpiration) + if err != nil { + return err + } + + var authorization exported.Authorization switch args[1] { case "send": limit, err := cmd.Flags().GetString(FlagSpendLimit) @@ -83,9 +97,7 @@ Examples: return fmt.Errorf("spend-limit should be greater than zero") } - authorization = &types.SendAuthorization{ - SpendLimit: spendLimit, - } + authorization = bank.NewSendAuthorization(spendLimit) case "generic": msgType, err := cmd.Flags().GetString(FlagMsgType) if err != nil { @@ -93,23 +105,69 @@ Examples: } authorization = types.NewGenericAuthorization(msgType) + case delegate, unbond, redelegate: + limit, err := cmd.Flags().GetString(FlagSpendLimit) + if err != nil { + return err + } + + allowValidators, err := cmd.Flags().GetStringSlice(FlagAllowedValidators) + if err != nil { + return err + } + + denyValidators, err := cmd.Flags().GetStringSlice(FlagDenyValidators) + if err != nil { + return err + } + + var delegateLimit *sdk.Coin + if limit != "" { + spendLimit, err := sdk.ParseCoinsNormalized(limit) + if err != nil { + return err + } + + if !spendLimit.IsAllPositive() { + return fmt.Errorf("spend-limit should be greater than zero") + } + delegateLimit = &spendLimit[0] + } + + allowed, err := bech32toValidatorAddresses(allowValidators) + if err != nil { + return err + } + + denied, err := bech32toValidatorAddresses(denyValidators) + if err != nil { + return err + } + + switch args[1] { + case delegate: + authorization, err = staking.NewStakeAuthorization(allowed, denied, staking.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, delegateLimit) + case unbond: + authorization, err = staking.NewStakeAuthorization(allowed, denied, staking.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, delegateLimit) + default: + authorization, err = staking.NewStakeAuthorization(allowed, denied, staking.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, delegateLimit) + } + if err != nil { + return err + } + default: return fmt.Errorf("invalid authorization type, %s", args[1]) } - exp, err := cmd.Flags().GetInt64(FlagExpiration) - if err != nil { - return err - } - msg, err := types.NewMsgGrantAuthorization(clientCtx.GetFromAddress(), grantee, authorization, time.Unix(exp, 0)) if err != nil { return err } svcMsgClientConn := &msgservice.ServiceMsgClientConn{} - authzMsgClient := types.NewMsgClient(svcMsgClientConn) - _, err = authzMsgClient.GrantAuthorization(context.Background(), msg) + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.GrantAuthorization(context.Background(), msg) if err != nil { return err } @@ -120,6 +178,8 @@ Examples: flags.AddTxFlagsToCmd(cmd) cmd.Flags().String(FlagMsgType, "", "The Msg method name for which we are creating a GenericAuthorization") cmd.Flags().String(FlagSpendLimit, "", "SpendLimit for Send Authorization, an array of Coins allowed spend") + cmd.Flags().StringSlice(FlagAllowedValidators, []string{}, "Allowed validators addresses separated by ,") + cmd.Flags().StringSlice(FlagDenyValidators, []string{}, "Deny validators addresses separated by ,") cmd.Flags().Int64(FlagExpiration, time.Now().AddDate(1, 0, 0).Unix(), "The Unix timestamp. Default is one year.") return cmd } @@ -132,7 +192,7 @@ func NewCmdRevokeAuthorization() *cobra.Command { fmt.Sprintf(`revoke authorization from a granter to a grantee: Example: $ %s tx %s revoke cosmos1skj.. %s --from=cosmos1skj.. - `, version.AppName, types.ModuleName, types.SendAuthorization{}.MethodName()), + `, version.AppName, types.ModuleName, bank.SendAuthorization{}.MethodName()), ), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { @@ -153,8 +213,8 @@ Example: msg := types.NewMsgRevokeAuthorization(granter, grantee, msgAuthorized) svcMsgClientConn := &msgservice.ServiceMsgClientConn{} - authzMsgClient := types.NewMsgClient(svcMsgClientConn) - _, err = authzMsgClient.RevokeAuthorization(context.Background(), &msg) + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.RevokeAuthorization(context.Background(), &msg) if err != nil { return err } @@ -206,8 +266,8 @@ Example: msg := types.NewMsgExecAuthorized(grantee, serviceMsgs) svcMsgClientConn := &msgservice.ServiceMsgClientConn{} - authzMsgClient := types.NewMsgClient(svcMsgClientConn) - _, err = authzMsgClient.ExecAuthorized(context.Background(), &msg) + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ExecAuthorized(context.Background(), &msg) if err != nil { return err } @@ -220,3 +280,15 @@ Example: return cmd } + +func bech32toValidatorAddresses(validators []string) ([]sdk.ValAddress, error) { + vals := make([]sdk.ValAddress, len(validators)) + for i, validator := range validators { + addr, err := sdk.ValAddressFromBech32(validator) + if err != nil { + return nil, err + } + vals[i] = addr + } + return vals, nil +} diff --git a/x/authz/client/cli/cli_test.go b/x/authz/client/cli/tx_test.go similarity index 52% rename from x/authz/client/cli/cli_test.go rename to x/authz/client/cli/tx_test.go index 96f5da08b..0f604a12c 100644 --- a/x/authz/client/cli/cli_test.go +++ b/x/authz/client/cli/tx_test.go @@ -1,10 +1,7 @@ -// +build norace - package cli_test import ( "fmt" - "strings" "testing" "time" @@ -12,8 +9,6 @@ import ( "github.com/stretchr/testify/suite" - tmcli "github.com/tendermint/tendermint/libs/cli" - "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -26,10 +21,12 @@ import ( govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" govtestutil "github.com/cosmos/cosmos-sdk/x/gov/client/testutil" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + stakingcli "github.com/cosmos/cosmos-sdk/x/staking/client/cli" - "github.com/cosmos/cosmos-sdk/x/authz/types" + authztestutil "github.com/cosmos/cosmos-sdk/x/authz/client/testutil" bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" ) type IntegrationTestSuite struct { @@ -52,7 +49,7 @@ func (s *IntegrationTestSuite) SetupSuite() { val := s.network.Validators[0] // Create new account in the keyring. - info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) s.Require().NoError(err) newAddr := sdk.AccAddress(info.GetPubKey().Address()) @@ -68,6 +65,9 @@ func (s *IntegrationTestSuite) SetupSuite() { s.Require().NoError(err) s.grantee = newAddr + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) + // create a proposal with deposit _, err = govtestutil.MsgSubmitProposal(val.ClientCtx, val.Address.String(), "Text Proposal 1", "Where is the title!?", govtypes.ProposalTypeText, @@ -83,175 +83,10 @@ func (s *IntegrationTestSuite) TearDownSuite() { s.network.Cleanup() } -var typeMsgSend = types.SendAuthorization{}.MethodName() +var typeMsgSend = bank.SendAuthorization{}.MethodName() var typeMsgVote = "/cosmos.gov.v1beta1.Msg/Vote" -func (s *IntegrationTestSuite) TestQueryAuthorizations() { - val := s.network.Validators[0] - - grantee := s.grantee - twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - - _, err := execGrantAuthorization( - val, - []string{ - grantee.String(), - "send", - fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - }, - ) - s.Require().NoError(err) - - testCases := []struct { - name string - args []string - expectErr bool - expErrMsg string - }{ - { - "Error: Invalid grantee", - []string{ - val.Address.String(), - "invalid grantee", - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - }, - true, - "decoding bech32 failed: invalid character in string: ' '", - }, - { - "Error: Invalid granter", - []string{ - "invalid granter", - grantee.String(), - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - }, - true, - "decoding bech32 failed: invalid character in string: ' '", - }, - { - "Valid txn (json)", - []string{ - val.Address.String(), - grantee.String(), - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - }, - false, - ``, - }, - } - for _, tc := range testCases { - tc := tc - - s.Run(tc.name, func() { - cmd := cli.GetCmdQueryAuthorizations() - clientCtx := val.ClientCtx - resp, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) - if tc.expectErr { - s.Require().Error(err) - s.Require().Contains(string(resp.Bytes()), tc.expErrMsg) - } else { - s.Require().NoError(err) - var grants types.QueryAuthorizationsResponse - err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &grants) - s.Require().NoError(err) - } - }) - } -} - -func (s *IntegrationTestSuite) TestQueryAuthorization() { - val := s.network.Validators[0] - - grantee := s.grantee - twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - - _, err := execGrantAuthorization( - val, - []string{ - grantee.String(), - "send", - fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - }, - ) - s.Require().NoError(err) - - testCases := []struct { - name string - args []string - expectErr bool - expectedOutput string - }{ - { - "Error: Invalid grantee", - []string{ - val.Address.String(), - "invalid grantee", - typeMsgSend, - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - }, - true, - "", - }, - { - "Error: Invalid granter", - []string{ - "invalid granter", - grantee.String(), - typeMsgSend, - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - }, - true, - "", - }, - { - "no authorization found", - []string{ - val.Address.String(), - grantee.String(), - "typeMsgSend", - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - }, - true, - "", - }, - { - "Valid txn (json)", - []string{ - val.Address.String(), - grantee.String(), - typeMsgSend, - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - }, - false, - `{"@type":"/cosmos.authz.v1beta1.SendAuthorization","spend_limit":[{"denom":"steak","amount":"100"}]}`, - }, - } - for _, tc := range testCases { - tc := tc - - s.Run(tc.name, func() { - cmd := cli.GetCmdQueryAuthorization() - clientCtx := val.ClientCtx - out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) - if tc.expectErr { - s.Require().Error(err) - } else { - s.Require().NoError(err) - s.Require().Contains(strings.TrimSpace(out.String()), tc.expectedOutput) - } - }) - } -} +var commonFlags = []string{} func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { val := s.network.Validators[0] @@ -315,13 +150,94 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=invalid-msg-type", cli.FlagMsgType), fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), }, &sdk.TxResponse{}, 29, false, }, + { + "failed with error both validators not allowed", + []string{ + grantee.String(), + "delegate", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, fmt.Sprintf("%s", val.ValAddress.String())), + fmt.Sprintf("--%s=%s", cli.FlagDenyValidators, fmt.Sprintf("%s", val.ValAddress.String())), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + nil, 0, + true, + }, + { + "valid tx delegate authorization allowed validators", + []string{ + grantee.String(), + "delegate", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, fmt.Sprintf("%s", val.ValAddress.String())), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + &sdk.TxResponse{}, 0, + false, + }, + { + "valid tx delegate authorization deny validators", + []string{ + grantee.String(), + "delegate", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagDenyValidators, fmt.Sprintf("%s", val.ValAddress.String())), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + &sdk.TxResponse{}, 0, + false, + }, + { + "valid tx undelegate authorization", + []string{ + grantee.String(), + "unbond", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, fmt.Sprintf("%s", val.ValAddress.String())), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + &sdk.TxResponse{}, 0, + false, + }, + { + "valid tx redelegate authorization", + []string{ + grantee.String(), + "redelegate", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, fmt.Sprintf("%s", val.ValAddress.String())), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + &sdk.TxResponse{}, 0, + false, + }, { "Valid tx send authorization", []string{ @@ -358,7 +274,7 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { tc := tc s.Run(tc.name, func() { clientCtx := val.ClientCtx - out, err := execGrantAuthorization( + out, err := authztestutil.ExecGrantAuthorization( val, tc.args, ) @@ -374,8 +290,8 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { } } -func execGrantAuthorization(val *network.Validator, args []string) (testutil.BufferWriter, error) { - cmd := cli.NewCmdGrantAuthorization() +func execDelegate(val *network.Validator, args []string) (testutil.BufferWriter, error) { + cmd := stakingcli.NewDelegateCmd() clientCtx := val.ClientCtx return clitestutil.ExecTestCLICmd(clientCtx, cmd, args) } @@ -387,7 +303,7 @@ func (s *IntegrationTestSuite) TestCmdRevokeAuthorizations() { twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() // send-authorization - _, err := execGrantAuthorization( + _, err := authztestutil.ExecGrantAuthorization( val, []string{ grantee.String(), @@ -403,7 +319,7 @@ func (s *IntegrationTestSuite) TestCmdRevokeAuthorizations() { s.Require().NoError(err) // generic-authorization - _, err = execGrantAuthorization( + _, err = authztestutil.ExecGrantAuthorization( val, []string{ grantee.String(), @@ -501,7 +417,7 @@ func (s *IntegrationTestSuite) TestExecAuthorizationWithExpiration() { grantee := s.grantee tenSeconds := time.Now().Add(time.Second * time.Duration(10)).Unix() - _, err := execGrantAuthorization( + _, err := authztestutil.ExecGrantAuthorization( val, []string{ grantee.String(), @@ -541,7 +457,7 @@ func (s *IntegrationTestSuite) TestNewExecGenericAuthorized() { grantee := s.grantee twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := execGrantAuthorization( + _, err := authztestutil.ExecGrantAuthorization( val, []string{ grantee.String(), @@ -630,7 +546,7 @@ func (s *IntegrationTestSuite) TestNewExecGrantAuthorized() { grantee := s.grantee twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := execGrantAuthorization( + _, err := authztestutil.ExecGrantAuthorization( val, []string{ grantee.String(), @@ -647,7 +563,7 @@ func (s *IntegrationTestSuite) TestNewExecGrantAuthorized() { tokens := sdk.NewCoins( sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), sdk.NewInt(12)), ) - normalGeneratedTx, err := bankcli.ServiceMsgSendExec( + normalGeneratedTx, err := bankcli.MsgSendExec( val.ClientCtx, val.Address, grantee, @@ -662,33 +578,9 @@ func (s *IntegrationTestSuite) TestNewExecGrantAuthorized() { testCases := []struct { name string args []string - respType proto.Message expectedCode uint32 expectErr bool }{ - { - "fail invalid grantee", - []string{ - execMsg.Name(), - fmt.Sprintf("--%s=%s", flags.FlagFrom, "grantee"), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - }, - nil, - 0, - true, - }, - { - "fail invalid json path", - []string{ - "/invalid/file.txt", - fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - }, - nil, - 0, - true, - }, { "valid txn", []string{ @@ -698,10 +590,21 @@ func (s *IntegrationTestSuite) TestNewExecGrantAuthorized() { fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), }, - &sdk.TxResponse{}, 0, false, }, + { + "error over spent", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + 4, + false, + }, } for _, tc := range testCases { @@ -714,10 +617,403 @@ func (s *IntegrationTestSuite) TestNewExecGrantAuthorized() { if tc.expectErr { s.Require().Error(err) } else { + var response sdk.TxResponse s.Require().NoError(err) - s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) - txResp := tc.respType.(*sdk.TxResponse) - s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Equal(tc.expectedCode, response.Code, out.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestExecDelegateAuthorization() { + val := s.network.Validators[0] + grantee := s.grantee + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := authztestutil.ExecGrantAuthorization( + val, + []string{ + grantee.String(), + "delegate", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, fmt.Sprintf("%s", val.ValAddress.String())), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + tokens := sdk.NewCoins( + sdk.NewCoin("stake", sdk.NewInt(50)), + ) + + delegateTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.Msg/Delegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), + tokens.GetDenomByIndex(0), tokens[0].Amount) + execMsg := testutil.WriteToNewTempFile(s.T(), delegateTx) + + testCases := []struct { + name string + args []string + expectedCode uint32 + expectErr bool + errMsg string + }{ + { + "valid txn: (delegate half tokens)", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + 0, + false, + "", + }, + { + "valid txn: (delegate remaining half tokens)", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + 0, + false, + "", + }, + { + "failed with error no authorization found", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + 4, + false, + "authorization not found", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.NewCmdExecAuthorization() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.errMsg) + } else { + var response sdk.TxResponse + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Equal(tc.expectedCode, response.Code, out.String()) + } + }) + } + + //test delegate no spend-limit + _, err = authztestutil.ExecGrantAuthorization( + val, + []string{ + grantee.String(), + "delegate", + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, fmt.Sprintf("%s", val.ValAddress.String())), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + tokens = sdk.NewCoins( + sdk.NewCoin("stake", sdk.NewInt(50)), + ) + + delegateTx = fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.Msg/Delegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), + tokens.GetDenomByIndex(0), tokens[0].Amount) + execMsg = testutil.WriteToNewTempFile(s.T(), delegateTx) + + testCases = []struct { + name string + args []string + expectedCode uint32 + expectErr bool + errMsg string + }{ + { + "valid txn", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + 0, + false, + "", + }, + { + "valid txn", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + 0, + false, + "", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.NewCmdExecAuthorization() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.errMsg) + } else { + var response sdk.TxResponse + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Equal(tc.expectedCode, response.Code, out.String()) + } + }) + } + + // test delegating to denied validator + _, err = authztestutil.ExecGrantAuthorization( + val, + []string{ + grantee.String(), + "delegate", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagDenyValidators, fmt.Sprintf("%s", val.ValAddress.String())), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + args := []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + } + cmd := cli.NewCmdExecAuthorization() + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) + s.Require().NoError(err) + s.Contains(out.String(), fmt.Sprintf("cannot delegate/undelegate to %s validator", val.ValAddress.String())) + +} + +func (s *IntegrationTestSuite) TestExecUndelegateAuthorization() { + val := s.network.Validators[0] + grantee := s.grantee + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + // granting undelegate msg authorization + _, err := authztestutil.ExecGrantAuthorization( + val, + []string{ + grantee.String(), + "unbond", + fmt.Sprintf("--%s=100stake", cli.FlagSpendLimit), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, fmt.Sprintf("%s", val.ValAddress.String())), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + // delegating stakes to validator + _, err = execDelegate( + val, + []string{ + val.ValAddress.String(), + "100stake", + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + tokens := sdk.NewCoins( + sdk.NewCoin("stake", sdk.NewInt(50)), + ) + + undelegateTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.Msg/Undelegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), + tokens.GetDenomByIndex(0), tokens[0].Amount) + execMsg := testutil.WriteToNewTempFile(s.T(), undelegateTx) + + testCases := []struct { + name string + args []string + expectedCode uint32 + expectErr bool + errMsg string + }{ + { + "valid txn: (undelegate half tokens)", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + 0, + false, + "", + }, + { + "valid txn: (undelegate remaining half tokens)", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + 0, + false, + "", + }, + { + "failed with error no authorization found", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + 4, + false, + "authorization not found", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.NewCmdExecAuthorization() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.errMsg) + } else { + var response sdk.TxResponse + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Equal(tc.expectedCode, response.Code, out.String()) + } + }) + } + + // grant undelegate authorization without limit + _, err = authztestutil.ExecGrantAuthorization( + val, + []string{ + grantee.String(), + "unbond", + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, fmt.Sprintf("%s", val.ValAddress.String())), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + tokens = sdk.NewCoins( + sdk.NewCoin("stake", sdk.NewInt(50)), + ) + + undelegateTx = fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.Msg/Undelegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), + tokens.GetDenomByIndex(0), tokens[0].Amount) + execMsg = testutil.WriteToNewTempFile(s.T(), undelegateTx) + + testCases = []struct { + name string + args []string + expectedCode uint32 + expectErr bool + errMsg string + }{ + { + "valid txn", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + 0, + false, + "", + }, + { + "valid txn", + []string{ + execMsg.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, grantee.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + }, + 0, + false, + "", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.NewCmdExecAuthorization() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.errMsg) + } else { + var response sdk.TxResponse + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Equal(tc.expectedCode, response.Code, out.String()) } }) } diff --git a/x/authz/client/rest/grpc_query_test.go b/x/authz/client/rest/grpc_query_test.go index 8ed8e857c..c5aae6b5f 100644 --- a/x/authz/client/rest/grpc_query_test.go +++ b/x/authz/client/rest/grpc_query_test.go @@ -1,10 +1,9 @@ -// +build norace - package rest_test import ( "fmt" "testing" + "time" "github.com/stretchr/testify/suite" @@ -14,9 +13,11 @@ import ( "github.com/cosmos/cosmos-sdk/testutil/network" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/authz/client/cli" authztestutil "github.com/cosmos/cosmos-sdk/x/authz/client/testutil" types "github.com/cosmos/cosmos-sdk/x/authz/types" banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) type IntegrationTestSuite struct { @@ -26,7 +27,8 @@ type IntegrationTestSuite struct { grantee sdk.AccAddress } -var typeMsgSend = types.SendAuthorization{}.MethodName() +var typeMsgSend = banktypes.SendAuthorization{}.MethodName() +var typeMsgVote = "/cosmos.gov.v1beta1.Msg/Vote" func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") @@ -39,7 +41,7 @@ func (s *IntegrationTestSuite) SetupSuite() { val := s.network.Validators[0] // Create new account in the keyring. - info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) s.Require().NoError(err) newAddr := sdk.AccAddress(info.GetPubKey().Address()) @@ -55,7 +57,16 @@ func (s *IntegrationTestSuite) SetupSuite() { s.Require().NoError(err) // grant authorization - _, err = authztestutil.MsgGrantAuthorizationExec(val.ClientCtx, val.Address.String(), newAddr.String(), typeMsgSend, "100stake") + _, err = authztestutil.ExecGrantAuthorization(val, []string{ + newAddr.String(), + "send", + fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, time.Now().Add(time.Minute*time.Duration(120)).Unix()), + }) s.Require().NoError(err) s.grantee = newAddr @@ -79,37 +90,37 @@ func (s *IntegrationTestSuite) TestQueryAuthorizationGRPC() { }{ { "fail invalid granter address", - fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?msg_type=%s", baseURL, "invalid_granter", s.grantee.String(), typeMsgSend), + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?method_name=%s", baseURL, "invalid_granter", s.grantee.String(), typeMsgSend), true, "decoding bech32 failed: invalid index of 1: invalid request", }, { "fail invalid grantee address", - fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?msg_type=%s", baseURL, val.Address.String(), "invalid_grantee", typeMsgSend), + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?method_name=%s", baseURL, val.Address.String(), "invalid_grantee", typeMsgSend), true, "decoding bech32 failed: invalid index of 1: invalid request", }, { "fail with empty granter", - fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?msg_type=%s", baseURL, "", s.grantee.String(), typeMsgSend), + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?method_name=%s", baseURL, "", s.grantee.String(), typeMsgSend), true, "Not Implemented", }, { "fail with empty grantee", - fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?msg_type=%s", baseURL, val.Address.String(), "", typeMsgSend), + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?method_name=%s", baseURL, val.Address.String(), "", typeMsgSend), true, "Not Implemented", }, { "fail invalid msg-type", - fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?msg_type=%s", baseURL, val.Address.String(), s.grantee.String(), "invalidMsg"), + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?method_name=%s", baseURL, val.Address.String(), s.grantee.String(), "invalidMsg"), true, "rpc error: code = NotFound desc = no authorization found for invalidMsg type: key not found", }, { "valid query", - fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?msg_type=%s", baseURL, val.Address.String(), s.grantee.String(), typeMsgSend), + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grant?method_name=%s", baseURL, val.Address.String(), s.grantee.String(), typeMsgSend), false, "", }, @@ -126,7 +137,7 @@ func (s *IntegrationTestSuite) TestQueryAuthorizationGRPC() { s.Require().NoError(err) authorization.Authorization.UnpackInterfaces(val.ClientCtx.InterfaceRegistry) auth := authorization.Authorization.GetAuthorizationGrant() - s.Require().Equal(auth.MethodName(), types.SendAuthorization{}.MethodName()) + s.Require().Equal(auth.MethodName(), banktypes.SendAuthorization{}.MethodName()) } }) } @@ -184,7 +195,16 @@ func (s *IntegrationTestSuite) TestQueryAuthorizationsGRPC() { false, "", func() { - _, err := authztestutil.MsgGrantAuthorizationExec(val.ClientCtx, val.Address.String(), s.grantee.String(), "/cosmos.gov.v1beta1.Msg/Vote", "") + _, err := authztestutil.ExecGrantAuthorization(val, []string{ + s.grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgVote), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, time.Now().Add(time.Minute*time.Duration(120)).Unix()), + }) s.Require().NoError(err) }, func(authorizations *types.QueryAuthorizationsResponse) { diff --git a/x/authz/client/testutil/test_helpers.go b/x/authz/client/testutil/test_helpers.go index a56429a4d..e1b728389 100644 --- a/x/authz/client/testutil/test_helpers.go +++ b/x/authz/client/testutil/test_helpers.go @@ -1,34 +1,14 @@ package testutil import ( - "fmt" - "time" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/testutil" clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - sdk "github.com/cosmos/cosmos-sdk/types" - authzcli "github.com/cosmos/cosmos-sdk/x/authz/client/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/x/authz/client/cli" ) -var commonArgs = []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), -} - -func MsgGrantAuthorizationExec(clientCtx client.Context, granter, grantee, msgName, limit string, extraArgs ...string) (testutil.BufferWriter, error) { - args := []string{ - grantee, - msgName, - } - if limit != "" { - args = append(args, limit) - } - - args = append(args, fmt.Sprintf("--%s=%s", flags.FlagFrom, granter)) - args = append(args, fmt.Sprintf("--%s=%d", authzcli.FlagExpiration, time.Now().Add(time.Minute*time.Duration(120)).Unix())) - args = append(args, commonArgs...) - return clitestutil.ExecTestCLICmd(clientCtx, authzcli.NewCmdGrantAuthorization(), args) +func ExecGrantAuthorization(val *network.Validator, args []string) (testutil.BufferWriter, error) { + cmd := cli.NewCmdGrantAuthorization() + clientCtx := val.ClientCtx + return clitestutil.ExecTestCLICmd(clientCtx, cmd, args) } diff --git a/x/authz/exported/authorizations.go b/x/authz/exported/authorizations.go new file mode 100644 index 000000000..d7cca5ae7 --- /dev/null +++ b/x/authz/exported/authorizations.go @@ -0,0 +1,21 @@ +package exported + +import ( + "github.com/gogo/protobuf/proto" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Authorization represents the interface of various Authorization types. +type Authorization interface { + proto.Message + + // MethodName returns the fully-qualified Msg service method name as described in ADR 031. + MethodName() string + + // Accept determines whether this grant permits the provided sdk.ServiceMsg to be performed, and if + // so provides an upgraded authorization instance. + Accept(msg sdk.ServiceMsg, block tmproto.Header) (updated Authorization, delete bool, err error) +} diff --git a/x/authz/exported/keeper.go b/x/authz/exported/keeper.go index 9858ba821..0efffca81 100644 --- a/x/authz/exported/keeper.go +++ b/x/authz/exported/keeper.go @@ -3,8 +3,6 @@ package exported import ( "time" - "github.com/cosmos/cosmos-sdk/x/authz/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -14,7 +12,7 @@ type Keeper interface { // Grants the provided authorization to the grantee on the granter's account with the provided expiration time // If there is an existing authorization grant for the same sdk.Msg type, this grant overwrites that. - Grant(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, authorization types.Authorization, expiration time.Time) error + Grant(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, authorization Authorization, expiration time.Time) error // Revokes any authorization for the provided message type granted to the grantee by the granter. Revoke(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) @@ -22,5 +20,5 @@ type Keeper interface { // Returns any Authorization (or nil), with the expiration time, // granted to the grantee by the granter for the provided msg type. // If the Authorization is expired already, it will revoke the authorization and return nil - GetOrRevokeAuthorization(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) (cap types.Authorization, expiration time.Time) + GetOrRevokeAuthorization(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) (cap Authorization, expiration time.Time) } diff --git a/x/authz/genesis.go b/x/authz/genesis.go index e84ddf598..8cce4973b 100644 --- a/x/authz/genesis.go +++ b/x/authz/genesis.go @@ -2,6 +2,7 @@ package authz import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz/exported" "github.com/cosmos/cosmos-sdk/x/authz/keeper" "github.com/cosmos/cosmos-sdk/x/authz/types" ) @@ -17,7 +18,7 @@ func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, data *types.GenesisState if err != nil { panic(err) } - authorization, ok := entry.Authorization.GetCachedValue().(types.Authorization) + authorization, ok := entry.Authorization.GetCachedValue().(exported.Authorization) if !ok { panic("expected authorization") } diff --git a/x/authz/genesis_test.go b/x/authz/genesis_test.go index 2d770d4c5..5e623a606 100644 --- a/x/authz/genesis_test.go +++ b/x/authz/genesis_test.go @@ -12,7 +12,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authz "github.com/cosmos/cosmos-sdk/x/authz" "github.com/cosmos/cosmos-sdk/x/authz/keeper" - "github.com/cosmos/cosmos-sdk/x/authz/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" ) type GenesisTestSuite struct { @@ -41,7 +41,7 @@ func (suite *GenesisTestSuite) TestImportExportGenesis() { coins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1_000))) now := suite.ctx.BlockHeader().Time - grant := &types.SendAuthorization{SpendLimit: coins} + grant := &bank.SendAuthorization{SpendLimit: coins} err := suite.keeper.Grant(suite.ctx, granteeAddr, granterAddr, grant, now.Add(time.Hour)) suite.Require().NoError(err) genesis := authz.ExportGenesis(suite.ctx, suite.keeper) diff --git a/x/authz/keeper/grpc_query_test.go b/x/authz/keeper/grpc_query_test.go index cc301fbca..568cdd4af 100644 --- a/x/authz/keeper/grpc_query_test.go +++ b/x/authz/keeper/grpc_query_test.go @@ -6,14 +6,16 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz/exported" "github.com/cosmos/cosmos-sdk/x/authz/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) func (suite *TestSuite) TestGRPCQueryAuthorization() { app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs var ( req *types.QueryAuthorizationRequest - expAuthorization types.Authorization + expAuthorization exported.Authorization ) testCases := []struct { msg string @@ -55,7 +57,7 @@ func (suite *TestSuite) TestGRPCQueryAuthorization() { func() { now := ctx.BlockHeader().Time newCoins := sdk.NewCoins(sdk.NewInt64Coin("steak", 100)) - expAuthorization = &types.SendAuthorization{SpendLimit: newCoins} + expAuthorization = &banktypes.SendAuthorization{SpendLimit: newCoins} err := app.AuthzKeeper.Grant(ctx, addrs[0], addrs[1], expAuthorization, now.Add(time.Hour)) suite.Require().NoError(err) req = &types.QueryAuthorizationRequest{ @@ -66,7 +68,7 @@ func (suite *TestSuite) TestGRPCQueryAuthorization() { }, true, func(res *types.QueryAuthorizationResponse) { - var auth types.Authorization + var auth exported.Authorization err := suite.app.InterfaceRegistry().UnpackAny(res.Authorization.Authorization, &auth) suite.Require().NoError(err) suite.Require().NotNil(auth) @@ -92,7 +94,7 @@ func (suite *TestSuite) TestGRPCQueryAuthorizations() { app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs var ( req *types.QueryAuthorizationsRequest - expAuthorization types.Authorization + expAuthorization exported.Authorization ) testCases := []struct { msg string @@ -123,7 +125,7 @@ func (suite *TestSuite) TestGRPCQueryAuthorizations() { func() { now := ctx.BlockHeader().Time newCoins := sdk.NewCoins(sdk.NewInt64Coin("steak", 100)) - expAuthorization = &types.SendAuthorization{SpendLimit: newCoins} + expAuthorization = &banktypes.SendAuthorization{SpendLimit: newCoins} err := app.AuthzKeeper.Grant(ctx, addrs[0], addrs[1], expAuthorization, now.Add(time.Hour)) suite.Require().NoError(err) req = &types.QueryAuthorizationsRequest{ @@ -133,7 +135,7 @@ func (suite *TestSuite) TestGRPCQueryAuthorizations() { }, true, func(res *types.QueryAuthorizationsResponse) { - var auth types.Authorization + var auth exported.Authorization suite.Require().Equal(1, len(res.Authorizations)) err := suite.app.InterfaceRegistry().UnpackAny(res.Authorizations[0].Authorization, &auth) suite.Require().NoError(err) diff --git a/x/authz/keeper/keeper.go b/x/authz/keeper/keeper.go index 2888e1744..33d6248f7 100644 --- a/x/authz/keeper/keeper.go +++ b/x/authz/keeper/keeper.go @@ -13,6 +13,7 @@ import ( codectypes "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/authz/exported" "github.com/cosmos/cosmos-sdk/x/authz/types" ) @@ -47,7 +48,7 @@ func (k Keeper) getAuthorizationGrant(ctx sdk.Context, grantStoreKey []byte) (gr return grant, true } -func (k Keeper) update(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, updated types.Authorization) error { +func (k Keeper) update(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, updated exported.Authorization) error { grantStoreKey := types.GetAuthorizationStoreKey(grantee, granter, updated.MethodName()) grant, found := k.getAuthorizationGrant(ctx, grantStoreKey) if !found { @@ -86,9 +87,9 @@ func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, service if authorization == nil { return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "authorization not found") } - allow, updated, del := authorization.Accept(serviceMsg, ctx.BlockHeader()) - if !allow { - return nil, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "requested amount is more than spend limit") + updated, del, err := authorization.Accept(serviceMsg, ctx.BlockHeader()) + if err != nil { + return nil, err } if del { k.Revoke(ctx, grantee, granter, serviceMsg.Type()) @@ -117,7 +118,7 @@ func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, service // Grant method grants the provided authorization to the grantee on the granter's account with the provided expiration // time. If there is an existing authorization grant for the same `sdk.Msg` type, this grant // overwrites that. -func (k Keeper) Grant(ctx sdk.Context, grantee, granter sdk.AccAddress, authorization types.Authorization, expiration time.Time) error { +func (k Keeper) Grant(ctx sdk.Context, grantee, granter sdk.AccAddress, authorization exported.Authorization, expiration time.Time) error { store := ctx.KVStore(k.storeKey) grant, err := types.NewAuthorizationGrant(authorization, expiration) @@ -164,7 +165,7 @@ func (k Keeper) Revoke(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccA } // GetAuthorizations Returns list of `Authorizations` granted to the grantee by the granter. -func (k Keeper) GetAuthorizations(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress) (authorizations []types.Authorization) { +func (k Keeper) GetAuthorizations(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress) (authorizations []exported.Authorization) { store := ctx.KVStore(k.storeKey) key := types.GetAuthorizationStoreKey(grantee, granter, "") iter := sdk.KVStorePrefixIterator(store, key) @@ -180,7 +181,7 @@ func (k Keeper) GetAuthorizations(ctx sdk.Context, grantee sdk.AccAddress, grant // GetOrRevokeAuthorization Returns any `Authorization` (or `nil`), with the expiration time, // granted to the grantee by the granter for the provided msg type. // If the Authorization is expired already, it will revoke the authorization and return nil -func (k Keeper) GetOrRevokeAuthorization(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) (cap types.Authorization, expiration time.Time) { +func (k Keeper) GetOrRevokeAuthorization(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) (cap exported.Authorization, expiration time.Time) { grant, found := k.getAuthorizationGrant(ctx, types.GetAuthorizationStoreKey(grantee, granter, msgType)) if !found { return nil, time.Time{} diff --git a/x/authz/keeper/keeper_test.go b/x/authz/keeper/keeper_test.go index 43dbc3b94..161fed6e9 100644 --- a/x/authz/keeper/keeper_test.go +++ b/x/authz/keeper/keeper_test.go @@ -5,11 +5,14 @@ import ( "time" proto "github.com/gogo/protobuf/proto" - "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/baseapp" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" - "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/stretchr/testify/suite" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/authz/types" @@ -48,12 +51,9 @@ func (s *TestSuite) TestKeeper() { granterAddr := addrs[0] granteeAddr := addrs[1] recipientAddr := addrs[2] - err := app.BankKeeper.SetBalances(ctx, granterAddr, sdk.NewCoins(sdk.NewInt64Coin("steak", 10000))) - s.Require().Nil(err) - s.Require().True(app.BankKeeper.GetBalance(ctx, granterAddr, "steak").IsEqual(sdk.NewCoin("steak", sdk.NewInt(10000)))) s.T().Log("verify that no authorization returns nil") - authorization, expiration := app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + authorization, expiration := app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, banktypes.SendAuthorization{}.MethodName()) s.Require().Nil(authorization) s.Require().Equal(expiration, time.Time{}) now := s.ctx.BlockHeader().Time @@ -61,38 +61,38 @@ func (s *TestSuite) TestKeeper() { newCoins := sdk.NewCoins(sdk.NewInt64Coin("steak", 100)) s.T().Log("verify if expired authorization is rejected") - x := &types.SendAuthorization{SpendLimit: newCoins} - err = app.AuthzKeeper.Grant(ctx, granterAddr, granteeAddr, x, now.Add(-1*time.Hour)) + x := &banktypes.SendAuthorization{SpendLimit: newCoins} + err := app.AuthzKeeper.Grant(ctx, granterAddr, granteeAddr, x, now.Add(-1*time.Hour)) s.Require().NoError(err) - authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, banktypes.SendAuthorization{}.MethodName()) s.Require().Nil(authorization) s.T().Log("verify if authorization is accepted") - x = &types.SendAuthorization{SpendLimit: newCoins} + x = &banktypes.SendAuthorization{SpendLimit: newCoins} err = app.AuthzKeeper.Grant(ctx, granteeAddr, granterAddr, x, now.Add(time.Hour)) s.Require().NoError(err) - authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, banktypes.SendAuthorization{}.MethodName()) s.Require().NotNil(authorization) - s.Require().Equal(authorization.MethodName(), types.SendAuthorization{}.MethodName()) + s.Require().Equal(authorization.MethodName(), banktypes.SendAuthorization{}.MethodName()) s.T().Log("verify fetching authorization with wrong msg type fails") authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, proto.MessageName(&banktypes.MsgMultiSend{})) s.Require().Nil(authorization) s.T().Log("verify fetching authorization with wrong grantee fails") - authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, recipientAddr, granterAddr, types.SendAuthorization{}.MethodName()) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, recipientAddr, granterAddr, banktypes.SendAuthorization{}.MethodName()) s.Require().Nil(authorization) s.T().Log("verify revoke fails with wrong information") - err = app.AuthzKeeper.Revoke(ctx, recipientAddr, granterAddr, types.SendAuthorization{}.MethodName()) + err = app.AuthzKeeper.Revoke(ctx, recipientAddr, granterAddr, banktypes.SendAuthorization{}.MethodName()) s.Require().Error(err) - authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, recipientAddr, granterAddr, types.SendAuthorization{}.MethodName()) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, recipientAddr, granterAddr, banktypes.SendAuthorization{}.MethodName()) s.Require().Nil(authorization) s.T().Log("verify revoke executes with correct information") - err = app.AuthzKeeper.Revoke(ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + err = app.AuthzKeeper.Revoke(ctx, granteeAddr, granterAddr, banktypes.SendAuthorization{}.MethodName()) s.Require().NoError(err) - authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, banktypes.SendAuthorization{}.MethodName()) s.Require().Nil(authorization) } @@ -103,10 +103,6 @@ func (s *TestSuite) TestKeeperIter() { granterAddr := addrs[0] granteeAddr := addrs[1] - err := app.BankKeeper.SetBalances(ctx, granterAddr, sdk.NewCoins(sdk.NewInt64Coin("steak", 10000))) - s.Require().Nil(err) - s.Require().True(app.BankKeeper.GetBalance(ctx, granterAddr, "steak").IsEqual(sdk.NewCoin("steak", sdk.NewInt(10000)))) - s.T().Log("verify that no authorization returns nil") authorization, expiration := app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, "Abcd") s.Require().Nil(authorization) @@ -116,8 +112,8 @@ func (s *TestSuite) TestKeeperIter() { newCoins := sdk.NewCoins(sdk.NewInt64Coin("steak", 100)) s.T().Log("verify if expired authorization is rejected") - x := &types.SendAuthorization{SpendLimit: newCoins} - err = app.AuthzKeeper.Grant(ctx, granteeAddr, granterAddr, x, now.Add(-1*time.Hour)) + x := &banktypes.SendAuthorization{SpendLimit: newCoins} + err := app.AuthzKeeper.Grant(ctx, granteeAddr, granterAddr, x, now.Add(-1*time.Hour)) s.Require().NoError(err) authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, "abcd") s.Require().Nil(authorization) @@ -136,10 +132,7 @@ func (s *TestSuite) TestKeeperFees() { granterAddr := addrs[0] granteeAddr := addrs[1] recipientAddr := addrs[2] - err := app.BankKeeper.SetBalances(s.ctx, granterAddr, sdk.NewCoins(sdk.NewInt64Coin("steak", 10000))) - s.Require().Nil(err) - s.Require().True(app.BankKeeper.GetBalance(s.ctx, granterAddr, "steak").IsEqual(sdk.NewCoin("steak", sdk.NewInt(10000)))) - + s.Require().NoError(simapp.FundAccount(app, s.ctx, granterAddr, sdk.NewCoins(sdk.NewInt64Coin("steak", 10000)))) now := s.ctx.BlockHeader().Time s.Require().NotNil(now) @@ -148,7 +141,7 @@ func (s *TestSuite) TestKeeperFees() { msgs := types.NewMsgExecAuthorized(granteeAddr, []sdk.ServiceMsg{ { - MethodName: types.SendAuthorization{}.MethodName(), + MethodName: banktypes.SendAuthorization{}.MethodName(), Request: &banktypes.MsgSend{ Amount: sdk.NewCoins(sdk.NewInt64Coin("steak", 2)), FromAddress: granterAddr.String(), @@ -169,21 +162,21 @@ func (s *TestSuite) TestKeeperFees() { s.T().Log("verify dispatch executes with correct information") // grant authorization - err = app.AuthzKeeper.Grant(s.ctx, granteeAddr, granterAddr, &types.SendAuthorization{SpendLimit: smallCoin}, now) + err = app.AuthzKeeper.Grant(s.ctx, granteeAddr, granterAddr, &banktypes.SendAuthorization{SpendLimit: smallCoin}, now) s.Require().NoError(err) - authorization, _ := app.AuthzKeeper.GetOrRevokeAuthorization(s.ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + authorization, _ := app.AuthzKeeper.GetOrRevokeAuthorization(s.ctx, granteeAddr, granterAddr, banktypes.SendAuthorization{}.MethodName()) s.Require().NotNil(authorization) - s.Require().Equal(authorization.MethodName(), types.SendAuthorization{}.MethodName()) + s.Require().Equal(authorization.MethodName(), banktypes.SendAuthorization{}.MethodName()) executeMsgs, err = msgs.GetServiceMsgs() s.Require().NoError(err) result, err = app.AuthzKeeper.DispatchActions(s.ctx, granteeAddr, executeMsgs) + s.Require().NoError(err) s.Require().NotNil(result) - s.Require().Nil(err) - authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(s.ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(s.ctx, granteeAddr, granterAddr, banktypes.SendAuthorization{}.MethodName()) s.Require().NotNil(authorization) s.T().Log("verify dispatch fails with overlimit") @@ -191,7 +184,7 @@ func (s *TestSuite) TestKeeperFees() { msgs = types.NewMsgExecAuthorized(granteeAddr, []sdk.ServiceMsg{ { - MethodName: types.SendAuthorization{}.MethodName(), + MethodName: banktypes.SendAuthorization{}.MethodName(), Request: &banktypes.MsgSend{ Amount: someCoin, FromAddress: granterAddr.String(), @@ -208,7 +201,7 @@ func (s *TestSuite) TestKeeperFees() { s.Require().Nil(result) s.Require().NotNil(err) - authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(s.ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(s.ctx, granteeAddr, granterAddr, banktypes.SendAuthorization{}.MethodName()) s.Require().NotNil(authorization) } diff --git a/x/authz/simulation/decoder_test.go b/x/authz/simulation/decoder_test.go index 86c2dfd6a..8b5e71cfb 100644 --- a/x/authz/simulation/decoder_test.go +++ b/x/authz/simulation/decoder_test.go @@ -12,13 +12,14 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" "github.com/cosmos/cosmos-sdk/x/authz/simulation" "github.com/cosmos/cosmos-sdk/x/authz/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) func TestDecodeStore(t *testing.T) { cdc := simapp.MakeTestEncodingConfig().Marshaler dec := simulation.NewDecodeStore(cdc) - grant, _ := types.NewAuthorizationGrant(types.NewSendAuthorization(sdk.NewCoins(sdk.NewInt64Coin("foo", 123))), time.Now().UTC()) + grant, _ := types.NewAuthorizationGrant(banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewInt64Coin("foo", 123))), time.Now().UTC()) grantBz, err := cdc.MarshalBinaryBare(&grant) require.NoError(t, err) kvPairs := kv.Pairs{ diff --git a/x/authz/simulation/operations.go b/x/authz/simulation/operations.go index 5533d9fa6..959b0c06f 100644 --- a/x/authz/simulation/operations.go +++ b/x/authz/simulation/operations.go @@ -97,7 +97,7 @@ func SimulateMsgGrantAuthorization(ak types.AccountKeeper, bk types.BankKeeper, blockTime := ctx.BlockTime() msg, err := types.NewMsgGrantAuthorization(granter.Address, grantee.Address, - types.NewSendAuthorization(spendableCoins.Sub(fees)), blockTime.AddDate(1, 0, 0)) + banktype.NewSendAuthorization(spendableCoins.Sub(fees)), blockTime.AddDate(1, 0, 0)) if err != nil { return simtypes.NoOpMsg(types.ModuleName, TypeMsgGrantAuthorization, err.Error()), nil, err @@ -239,7 +239,7 @@ func SimulateMsgExecuteAuthorized(ak types.AccountKeeper, bk types.BankKeeper, k sendCoins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(10))) execMsg := sdk.ServiceMsg{ - MethodName: types.SendAuthorization{}.MethodName(), + MethodName: banktype.SendAuthorization{}.MethodName(), Request: banktype.NewMsgSend( granterAddr, granteeAddr, @@ -248,10 +248,10 @@ func SimulateMsgExecuteAuthorized(ak types.AccountKeeper, bk types.BankKeeper, k } msg := types.NewMsgExecAuthorized(grantee.Address, []sdk.ServiceMsg{execMsg}) - sendGrant := targetGrant.Authorization.GetCachedValue().(*types.SendAuthorization) - allow, _, _ := sendGrant.Accept(execMsg, ctx.BlockHeader()) - if !allow { - return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, "not allowed"), nil, nil + sendGrant := targetGrant.Authorization.GetCachedValue().(*banktype.SendAuthorization) + _, _, err = sendGrant.Accept(execMsg, ctx.BlockHeader()) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, err.Error()), nil, nil } txGen := simappparams.MakeTestEncodingConfig().TxConfig diff --git a/x/authz/simulation/operations_test.go b/x/authz/simulation/operations_test.go index 83be182df..3dc3dd08d 100644 --- a/x/authz/simulation/operations_test.go +++ b/x/authz/simulation/operations_test.go @@ -17,6 +17,7 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/authz/simulation" "github.com/cosmos/cosmos-sdk/x/authz/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) type SimTestSuite struct { @@ -79,8 +80,7 @@ func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Ac for _, account := range accounts { acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, account.Address) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - err := suite.app.BankKeeper.SetBalances(suite.ctx, account.Address, initCoins) - suite.Require().NoError(err) + suite.Require().NoError(simapp.FundAccount(suite.app, suite.ctx, account.Address, initCoins)) } return accounts @@ -137,7 +137,7 @@ func (suite *SimTestSuite) TestSimulateRevokeAuthorization() { granter := accounts[0] grantee := accounts[1] - authorization := types.NewSendAuthorization(initCoins) + authorization := banktypes.NewSendAuthorization(initCoins) err := suite.app.AuthzKeeper.Grant(suite.ctx, grantee.Address, granter.Address, authorization, time.Now().Add(30*time.Hour)) suite.Require().NoError(err) @@ -153,7 +153,7 @@ func (suite *SimTestSuite) TestSimulateRevokeAuthorization() { suite.Require().True(operationMsg.OK) suite.Require().Equal(granter.Address.String(), msg.Granter) suite.Require().Equal(grantee.Address.String(), msg.Grantee) - suite.Require().Equal(types.SendAuthorization{}.MethodName(), msg.MethodName) + suite.Require().Equal(banktypes.SendAuthorization{}.MethodName(), msg.MethodName) suite.Require().Len(futureOperations, 0) } @@ -172,7 +172,7 @@ func (suite *SimTestSuite) TestSimulateExecAuthorization() { granter := accounts[0] grantee := accounts[1] - authorization := types.NewSendAuthorization(initCoins) + authorization := banktypes.NewSendAuthorization(initCoins) err := suite.app.AuthzKeeper.Grant(suite.ctx, grantee.Address, granter.Address, authorization, time.Now().Add(30*time.Hour)) suite.Require().NoError(err) diff --git a/x/authz/types/authorizations.go b/x/authz/types/authorization_grant.go similarity index 54% rename from x/authz/types/authorizations.go rename to x/authz/types/authorization_grant.go index 18533fe07..03a83bdef 100644 --- a/x/authz/types/authorizations.go +++ b/x/authz/types/authorization_grant.go @@ -3,28 +3,15 @@ package types import ( "time" - "github.com/gogo/protobuf/proto" - - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + proto "github.com/gogo/protobuf/proto" "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/authz/exported" ) -type Authorization interface { - proto.Message - - // MethodName returns the fully-qualified Msg service method name as described in ADR 031. - MethodName() string - - // Accept determines whether this grant permits the provided sdk.ServiceMsg to be performed, and if - // so provides an upgraded authorization instance. - Accept(msg sdk.ServiceMsg, block tmproto.Header) (allow bool, updated Authorization, delete bool) -} - // NewAuthorizationGrant returns new AuthrizationGrant -func NewAuthorizationGrant(authorization Authorization, expiration time.Time) (AuthorizationGrant, error) { +func NewAuthorizationGrant(authorization exported.Authorization, expiration time.Time) (AuthorizationGrant, error) { auth := AuthorizationGrant{ Expiration: expiration, } @@ -49,13 +36,13 @@ var ( // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (auth AuthorizationGrant) UnpackInterfaces(unpacker types.AnyUnpacker) error { - var authorization Authorization + var authorization exported.Authorization return unpacker.UnpackAny(auth.Authorization, &authorization) } // GetAuthorizationGrant returns the cached value from the AuthorizationGrant.Authorization if present. -func (auth AuthorizationGrant) GetAuthorizationGrant() Authorization { - authorization, ok := auth.Authorization.GetCachedValue().(Authorization) +func (auth AuthorizationGrant) GetAuthorizationGrant() exported.Authorization { + authorization, ok := auth.Authorization.GetCachedValue().(exported.Authorization) if !ok { return nil } diff --git a/x/authz/types/authz.pb.go b/x/authz/types/authz.pb.go index 334308320..15c103c61 100644 --- a/x/authz/types/authz.pb.go +++ b/x/authz/types/authz.pb.go @@ -5,9 +5,7 @@ package types import ( fmt "fmt" - types1 "github.com/cosmos/cosmos-sdk/codec/types" - github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" - types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/codec/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" @@ -31,52 +29,6 @@ var _ = time.Kitchen // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// SendAuthorization allows the grantee to spend up to spend_limit coins from -// the granter's account. -type SendAuthorization struct { - SpendLimit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=spend_limit,json=spendLimit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"spend_limit"` -} - -func (m *SendAuthorization) Reset() { *m = SendAuthorization{} } -func (m *SendAuthorization) String() string { return proto.CompactTextString(m) } -func (*SendAuthorization) ProtoMessage() {} -func (*SendAuthorization) Descriptor() ([]byte, []int) { - return fileDescriptor_544dc2e84b61c637, []int{0} -} -func (m *SendAuthorization) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *SendAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_SendAuthorization.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *SendAuthorization) XXX_Merge(src proto.Message) { - xxx_messageInfo_SendAuthorization.Merge(m, src) -} -func (m *SendAuthorization) XXX_Size() int { - return m.Size() -} -func (m *SendAuthorization) XXX_DiscardUnknown() { - xxx_messageInfo_SendAuthorization.DiscardUnknown(m) -} - -var xxx_messageInfo_SendAuthorization proto.InternalMessageInfo - -func (m *SendAuthorization) GetSpendLimit() github_com_cosmos_cosmos_sdk_types.Coins { - if m != nil { - return m.SpendLimit - } - return nil -} - // GenericAuthorization gives the grantee unrestricted permissions to execute // the provided method on behalf of the granter's account. type GenericAuthorization struct { @@ -90,7 +42,7 @@ func (m *GenericAuthorization) Reset() { *m = GenericAuthorization{} } func (m *GenericAuthorization) String() string { return proto.CompactTextString(m) } func (*GenericAuthorization) ProtoMessage() {} func (*GenericAuthorization) Descriptor() ([]byte, []int) { - return fileDescriptor_544dc2e84b61c637, []int{1} + return fileDescriptor_544dc2e84b61c637, []int{0} } func (m *GenericAuthorization) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -129,15 +81,15 @@ func (m *GenericAuthorization) GetMessageName() string { // AuthorizationGrant gives permissions to execute // the provide method with expiration time. type AuthorizationGrant struct { - Authorization *types1.Any `protobuf:"bytes,1,opt,name=authorization,proto3" json:"authorization,omitempty"` - Expiration time.Time `protobuf:"bytes,2,opt,name=expiration,proto3,stdtime" json:"expiration"` + Authorization *types.Any `protobuf:"bytes,1,opt,name=authorization,proto3" json:"authorization,omitempty"` + Expiration time.Time `protobuf:"bytes,2,opt,name=expiration,proto3,stdtime" json:"expiration"` } func (m *AuthorizationGrant) Reset() { *m = AuthorizationGrant{} } func (m *AuthorizationGrant) String() string { return proto.CompactTextString(m) } func (*AuthorizationGrant) ProtoMessage() {} func (*AuthorizationGrant) Descriptor() ([]byte, []int) { - return fileDescriptor_544dc2e84b61c637, []int{2} + return fileDescriptor_544dc2e84b61c637, []int{1} } func (m *AuthorizationGrant) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -166,7 +118,7 @@ func (m *AuthorizationGrant) XXX_DiscardUnknown() { var xxx_messageInfo_AuthorizationGrant proto.InternalMessageInfo -func (m *AuthorizationGrant) GetAuthorization() *types1.Any { +func (m *AuthorizationGrant) GetAuthorization() *types.Any { if m != nil { return m.Authorization } @@ -181,7 +133,6 @@ func (m *AuthorizationGrant) GetExpiration() time.Time { } func init() { - proto.RegisterType((*SendAuthorization)(nil), "cosmos.authz.v1beta1.SendAuthorization") proto.RegisterType((*GenericAuthorization)(nil), "cosmos.authz.v1beta1.GenericAuthorization") proto.RegisterType((*AuthorizationGrant)(nil), "cosmos.authz.v1beta1.AuthorizationGrant") } @@ -189,70 +140,28 @@ func init() { func init() { proto.RegisterFile("cosmos/authz/v1beta1/authz.proto", fileDescriptor_544dc2e84b61c637) } var fileDescriptor_544dc2e84b61c637 = []byte{ - // 410 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0xbf, 0x8e, 0xd3, 0x30, - 0x1c, 0xc7, 0x63, 0x90, 0x10, 0x38, 0x3a, 0xa1, 0x46, 0x19, 0xee, 0x3a, 0x24, 0xd5, 0x4d, 0x15, - 0xd2, 0x39, 0x77, 0xc7, 0xc6, 0x76, 0xe1, 0xa4, 0x5b, 0x38, 0x86, 0xc0, 0x04, 0x43, 0xe5, 0x24, - 0x26, 0xb1, 0xa8, 0xed, 0x28, 0x76, 0xd0, 0xf5, 0x9e, 0xa2, 0x03, 0x2f, 0x01, 0x33, 0x0f, 0x51, - 0x31, 0x55, 0x4c, 0x4c, 0x2d, 0x4a, 0x5f, 0x04, 0xc5, 0x76, 0x50, 0x53, 0x10, 0x53, 0xfc, 0xfb, - 0xf3, 0xfd, 0xe4, 0x9b, 0x6f, 0x0c, 0x27, 0x99, 0x90, 0x4c, 0xc8, 0x08, 0x37, 0xaa, 0xbc, 0x8f, - 0x3e, 0x5d, 0xa4, 0x44, 0xe1, 0x0b, 0x53, 0xa1, 0xaa, 0x16, 0x4a, 0x78, 0xbe, 0xd9, 0x40, 0xa6, - 0x67, 0x37, 0xc6, 0x81, 0xd5, 0xa5, 0x58, 0x92, 0x3f, 0xb2, 0x4c, 0x50, 0x6e, 0x54, 0xe3, 0x13, - 0x33, 0x9f, 0xe9, 0x2a, 0xb2, 0x08, 0x33, 0x0a, 0x0b, 0x21, 0x8a, 0x39, 0x89, 0x74, 0x95, 0x36, - 0x1f, 0x22, 0x45, 0x19, 0x91, 0x0a, 0xb3, 0xca, 0x2e, 0xf8, 0x85, 0x28, 0x84, 0x11, 0x76, 0xa7, - 0x9e, 0x78, 0x28, 0xc3, 0x7c, 0x61, 0x46, 0xa7, 0x9f, 0x01, 0x1c, 0xbd, 0x21, 0x3c, 0xbf, 0x6a, - 0x54, 0x29, 0x6a, 0x7a, 0x8f, 0x15, 0x15, 0xdc, 0x9b, 0x43, 0x57, 0x56, 0x84, 0xe7, 0xb3, 0x39, - 0x65, 0x54, 0x1d, 0x83, 0xc9, 0xc3, 0xa9, 0x7b, 0x79, 0x82, 0xac, 0x97, 0xce, 0x78, 0xff, 0x35, - 0xe8, 0xa5, 0xa0, 0x3c, 0x3e, 0x5f, 0x6d, 0x42, 0xe7, 0xeb, 0x36, 0x9c, 0x16, 0x54, 0x95, 0x4d, - 0x8a, 0x32, 0xc1, 0xac, 0x71, 0xfb, 0x38, 0x93, 0xf9, 0xc7, 0x48, 0x2d, 0x2a, 0x22, 0xb5, 0x40, - 0x26, 0x50, 0xf3, 0x5f, 0x75, 0xf8, 0x17, 0xa3, 0x1f, 0xdf, 0xce, 0x8e, 0x06, 0x06, 0x4e, 0xdf, - 0x43, 0xff, 0x86, 0x70, 0x52, 0xd3, 0x6c, 0x68, 0xec, 0x1c, 0xba, 0x8c, 0xa8, 0x52, 0xe4, 0x33, - 0x8e, 0x19, 0x39, 0x06, 0x13, 0x30, 0x7d, 0x12, 0x3f, 0x6d, 0x37, 0xa1, 0x7b, 0x4b, 0xa4, 0xc4, - 0x05, 0x79, 0x8d, 0x19, 0x49, 0xa0, 0xd9, 0xe9, 0xce, 0xff, 0x82, 0x7f, 0x01, 0xd0, 0x1b, 0x74, - 0x6e, 0x6a, 0xcc, 0x95, 0x77, 0x0b, 0x8f, 0xf0, 0x7e, 0x57, 0xd3, 0xdd, 0x4b, 0x1f, 0x99, 0xf4, - 0x50, 0x9f, 0x1e, 0xba, 0xe2, 0x8b, 0x78, 0xf4, 0xfd, 0x10, 0x9b, 0x0c, 0xd5, 0xde, 0x35, 0x84, - 0xe4, 0xae, 0xa2, 0xb5, 0x61, 0x3d, 0xd0, 0xac, 0xf1, 0x5f, 0xac, 0xb7, 0xfd, 0x0f, 0x8c, 0x1f, - 0x77, 0x19, 0x2e, 0xb7, 0x21, 0x48, 0xf6, 0x74, 0xf1, 0xf5, 0xaa, 0x0d, 0xc0, 0xba, 0x0d, 0xc0, - 0xaf, 0x36, 0x00, 0xcb, 0x5d, 0xe0, 0xac, 0x77, 0x81, 0xf3, 0x73, 0x17, 0x38, 0xef, 0x9e, 0xfd, - 0x37, 0xeb, 0x3b, 0x7b, 0x2d, 0x75, 0xe6, 0xe9, 0x23, 0xfd, 0xbe, 0xe7, 0xbf, 0x03, 0x00, 0x00, - 0xff, 0xff, 0x87, 0x61, 0xb9, 0xa8, 0xb3, 0x02, 0x00, 0x00, -} - -func (m *SendAuthorization) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *SendAuthorization) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *SendAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.SpendLimit) > 0 { - for iNdEx := len(m.SpendLimit) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.SpendLimit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintAuthz(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil + // 333 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0xc1, 0x4e, 0xfa, 0x40, + 0x10, 0xc6, 0xbb, 0xff, 0xc3, 0x3f, 0xba, 0x84, 0x18, 0x9a, 0x1e, 0x84, 0x43, 0x4b, 0x38, 0x19, + 0x13, 0x5a, 0xd1, 0x9b, 0x37, 0x1a, 0x12, 0x4e, 0x78, 0x20, 0x9e, 0xf4, 0x40, 0xb6, 0x30, 0xb6, + 0x8d, 0x6e, 0xa7, 0xe9, 0x6e, 0x0d, 0xf0, 0x14, 0xbc, 0x86, 0x77, 0x1f, 0x82, 0x78, 0x22, 0x9e, + 0x3c, 0xa1, 0x29, 0x2f, 0x62, 0xe8, 0x2e, 0x09, 0xe0, 0x69, 0x67, 0xbe, 0xfd, 0xbe, 0xdf, 0x4e, + 0x76, 0x68, 0x73, 0x8c, 0x82, 0xa3, 0xf0, 0x58, 0x2e, 0xa3, 0xb9, 0xf7, 0xda, 0x09, 0x40, 0xb2, + 0x8e, 0xea, 0xdc, 0x34, 0x43, 0x89, 0xa6, 0xa5, 0x1c, 0xae, 0xd2, 0xb4, 0xa3, 0x51, 0x57, 0xea, + 0xa8, 0xf4, 0x78, 0xda, 0x52, 0x36, 0x0d, 0x27, 0x44, 0x0c, 0x5f, 0xc0, 0x2b, 0xbb, 0x20, 0x7f, + 0xf2, 0x64, 0xcc, 0x41, 0x48, 0xc6, 0x53, 0x6d, 0xb0, 0x42, 0x0c, 0x51, 0x05, 0xb7, 0x95, 0x56, + 0xeb, 0xc7, 0x31, 0x96, 0xcc, 0xd4, 0x55, 0xeb, 0x91, 0x5a, 0x7d, 0x48, 0x20, 0x8b, 0xc7, 0xdd, + 0x5c, 0x46, 0x98, 0xc5, 0x73, 0x26, 0x63, 0x4c, 0xcc, 0x2b, 0x5a, 0xe1, 0x20, 0x23, 0x9c, 0x8c, + 0x12, 0xc6, 0xe1, 0x9c, 0x34, 0xc9, 0xc5, 0xa9, 0x7f, 0x56, 0xac, 0x9d, 0xca, 0x00, 0x84, 0x60, + 0x21, 0xdc, 0x31, 0x0e, 0x43, 0xaa, 0x3c, 0xdb, 0xfa, 0xb6, 0xf6, 0xf9, 0xde, 0xae, 0x1e, 0x40, + 0x5a, 0x6f, 0x84, 0x9a, 0x07, 0x4a, 0x3f, 0x63, 0x89, 0x34, 0x07, 0xb4, 0xca, 0xf6, 0xd5, 0x92, + 0x5e, 0xb9, 0xb6, 0x5c, 0x35, 0xa6, 0xbb, 0x1b, 0xd3, 0xed, 0x26, 0x33, 0xbf, 0xf6, 0x71, 0x8c, + 0x1d, 0x1e, 0xa6, 0xcd, 0x1e, 0xa5, 0x30, 0x4d, 0xe3, 0x4c, 0xb1, 0xfe, 0x95, 0xac, 0xc6, 0x1f, + 0xd6, 0xfd, 0xee, 0xa7, 0xfc, 0x93, 0xe5, 0xda, 0x31, 0x16, 0xdf, 0x0e, 0x19, 0xee, 0xe5, 0xfc, + 0xde, 0xb2, 0xb0, 0xc9, 0xaa, 0xb0, 0xc9, 0x4f, 0x61, 0x93, 0xc5, 0xc6, 0x36, 0x56, 0x1b, 0xdb, + 0xf8, 0xda, 0xd8, 0xc6, 0xc3, 0x65, 0x18, 0xcb, 0x28, 0x0f, 0xdc, 0x31, 0x72, 0xbd, 0x0d, 0x7d, + 0xb4, 0xc5, 0xe4, 0xd9, 0x9b, 0xea, 0xfd, 0xca, 0x59, 0x0a, 0x22, 0xf8, 0x5f, 0xbe, 0x77, 0xf3, + 0x1b, 0x00, 0x00, 0xff, 0xff, 0x62, 0x49, 0x9d, 0xc0, 0xfc, 0x01, 0x00, 0x00, } func (m *GenericAuthorization) Marshal() (dAtA []byte, err error) { @@ -339,21 +248,6 @@ func encodeVarintAuthz(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } -func (m *SendAuthorization) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.SpendLimit) > 0 { - for _, e := range m.SpendLimit { - l = e.Size() - n += 1 + l + sovAuthz(uint64(l)) - } - } - return n -} - func (m *GenericAuthorization) Size() (n int) { if m == nil { return 0 @@ -388,90 +282,6 @@ func sovAuthz(x uint64) (n int) { func sozAuthz(x uint64) (n int) { return sovAuthz(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } -func (m *SendAuthorization) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAuthz - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: SendAuthorization: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SendAuthorization: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SpendLimit", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAuthz - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthAuthz - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthAuthz - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.SpendLimit = append(m.SpendLimit, types.Coin{}) - if err := m.SpendLimit[len(m.SpendLimit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipAuthz(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAuthz - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *GenericAuthorization) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -613,7 +423,7 @@ func (m *AuthorizationGrant) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } if m.Authorization == nil { - m.Authorization = &types1.Any{} + m.Authorization = &types.Any{} } if err := m.Authorization.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err diff --git a/x/authz/types/codec.go b/x/authz/types/codec.go index 88b30932d..c6119f2a0 100644 --- a/x/authz/types/codec.go +++ b/x/authz/types/codec.go @@ -4,6 +4,9 @@ import ( types "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" + "github.com/cosmos/cosmos-sdk/x/authz/exported" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + staking "github.com/cosmos/cosmos-sdk/x/staking/types" ) // RegisterInterfaces registers the interfaces types with the interface registry @@ -16,9 +19,10 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { registry.RegisterInterface( "cosmos.authz.v1beta1.Authorization", - (*Authorization)(nil), - &SendAuthorization{}, + (*exported.Authorization)(nil), + &bank.SendAuthorization{}, &GenericAuthorization{}, + &staking.StakeAuthorization{}, ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) diff --git a/x/authz/types/generic_authorization.go b/x/authz/types/generic_authorization.go index 242608c63..672260c67 100644 --- a/x/authz/types/generic_authorization.go +++ b/x/authz/types/generic_authorization.go @@ -4,10 +4,11 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz/exported" ) var ( - _ Authorization = &GenericAuthorization{} + _ exported.Authorization = &GenericAuthorization{} ) // NewGenericAuthorization creates a new GenericAuthorization object. @@ -23,6 +24,6 @@ func (cap GenericAuthorization) MethodName() string { } // Accept implements Authorization.Accept. -func (cap GenericAuthorization) Accept(msg sdk.ServiceMsg, block tmproto.Header) (allow bool, updated Authorization, delete bool) { - return true, &cap, false +func (cap GenericAuthorization) Accept(msg sdk.ServiceMsg, block tmproto.Header) (updated exported.Authorization, delete bool, err error) { + return &cap, false, nil } diff --git a/x/authz/types/genesis.go b/x/authz/types/genesis.go index 6ae3529d2..8d2a356f5 100644 --- a/x/authz/types/genesis.go +++ b/x/authz/types/genesis.go @@ -2,6 +2,7 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/x/authz/exported" ) // NewGenesisState creates new GenesisState object @@ -36,6 +37,6 @@ func (data GenesisState) UnpackInterfaces(unpacker types.AnyUnpacker) error { // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (msg GrantAuthorization) UnpackInterfaces(unpacker types.AnyUnpacker) error { - var authorization Authorization + var authorization exported.Authorization return unpacker.UnpackAny(msg.Authorization, &authorization) } diff --git a/x/authz/types/genesis.pb.go b/x/authz/types/genesis.pb.go index 0cf5c8557..20dfb9d23 100644 --- a/x/authz/types/genesis.pb.go +++ b/x/authz/types/genesis.pb.go @@ -153,29 +153,29 @@ func init() { } var fileDescriptor_4c2fbb971da7c892 = []byte{ - // 346 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0xbb, 0x6e, 0xc2, 0x30, - 0x14, 0x86, 0xe3, 0x82, 0x7a, 0x31, 0x65, 0x68, 0xc4, 0x90, 0x22, 0x35, 0x20, 0xa6, 0xa8, 0x12, - 0xb6, 0xa0, 0x4f, 0x40, 0x84, 0xc4, 0xd4, 0x85, 0x32, 0x75, 0xa9, 0x1c, 0x70, 0x4d, 0xd4, 0x26, - 0x8e, 0xe2, 0x43, 0x05, 0x3c, 0x05, 0x0f, 0xd3, 0x87, 0x40, 0x9d, 0x18, 0xbb, 0xf4, 0x22, 0x78, - 0x91, 0x2a, 0x71, 0xa2, 0x72, 0x9b, 0xe2, 0xf8, 0xff, 0xce, 0xf9, 0x7f, 0x9f, 0x83, 0x1b, 0x43, - 0xa9, 0x02, 0xa9, 0x28, 0x9b, 0xc0, 0x78, 0x4e, 0xdf, 0x5a, 0x1e, 0x07, 0xd6, 0xa2, 0x82, 0x87, - 0x5c, 0xf9, 0x8a, 0x44, 0xb1, 0x04, 0x69, 0x56, 0x34, 0x43, 0x52, 0x86, 0x64, 0x4c, 0xb5, 0x26, - 0xa4, 0x14, 0xaf, 0x9c, 0xa6, 0x8c, 0x37, 0x79, 0xa6, 0xe0, 0x07, 0x5c, 0x01, 0x0b, 0x22, 0x5d, - 0x56, 0xbd, 0xde, 0x07, 0x58, 0x38, 0xcb, 0xa4, 0x8a, 0x90, 0x42, 0xa6, 0x47, 0x9a, 0x9c, 0xf2, - 0x02, 0xed, 0xf3, 0xa4, 0x85, 0xcc, 0x54, 0x4b, 0x37, 0x47, 0x63, 0xc2, 0x54, 0xcb, 0x8d, 0x11, - 0xbe, 0xec, 0xe9, 0xc8, 0x0f, 0xc0, 0x80, 0x9b, 0x03, 0x5c, 0x4e, 0x48, 0x19, 0xfb, 0x73, 0x06, - 0xbe, 0x0c, 0x2d, 0x54, 0x2f, 0x38, 0xa5, 0xb6, 0x43, 0x8e, 0xbd, 0x84, 0xf4, 0x62, 0x16, 0x42, - 0x67, 0x9b, 0x77, 0x8b, 0xcb, 0xef, 0x9a, 0xd1, 0xdf, 0x6d, 0xd2, 0xf8, 0x42, 0xd8, 0x3c, 0x64, - 0x4d, 0x0b, 0x9f, 0x89, 0xe4, 0x96, 0xc7, 0x16, 0xaa, 0x23, 0xe7, 0xa2, 0x9f, 0xff, 0xfe, 0x2b, - 0xdc, 0x3a, 0xd9, 0x56, 0xb8, 0x79, 0xbf, 0x1f, 0xb0, 0x50, 0x47, 0x4e, 0xa9, 0x5d, 0x21, 0x7a, - 0x66, 0x24, 0x9f, 0x19, 0xe9, 0x84, 0x33, 0xf7, 0xea, 0xe3, 0xbd, 0x59, 0xde, 0xf1, 0xdc, 0x4b, - 0x66, 0x76, 0x31, 0xe6, 0xd3, 0xc8, 0x8f, 0x75, 0xaf, 0x62, 0xda, 0xab, 0x7a, 0xd0, 0x6b, 0x90, - 0x2f, 0xc8, 0x3d, 0x4f, 0x9e, 0xb7, 0xf8, 0xa9, 0xa1, 0xfe, 0x56, 0x9d, 0xdb, 0x5d, 0xae, 0x6d, - 0xb4, 0x5a, 0xdb, 0xe8, 0x77, 0x6d, 0xa3, 0xc5, 0xc6, 0x36, 0x56, 0x1b, 0xdb, 0xf8, 0xdc, 0xd8, - 0xc6, 0xe3, 0xad, 0xf0, 0x61, 0x3c, 0xf1, 0xc8, 0x50, 0x06, 0xd9, 0x5e, 0xb2, 0x4f, 0x53, 0x8d, - 0x5e, 0xe8, 0x34, 0x5b, 0x0b, 0xcc, 0x22, 0xae, 0xbc, 0xd3, 0xd4, 0xef, 0xee, 0x2f, 0x00, 0x00, - 0xff, 0xff, 0x52, 0x9c, 0x43, 0x7a, 0x5a, 0x02, 0x00, 0x00, + // 340 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x52, 0xbb, 0x6e, 0xc2, 0x30, + 0x14, 0x8d, 0x0b, 0xea, 0xc3, 0x94, 0xa1, 0x11, 0x43, 0xca, 0x10, 0x10, 0x53, 0x54, 0x09, 0x5b, + 0xd0, 0x2f, 0x20, 0x42, 0x62, 0xea, 0x42, 0x99, 0xba, 0x54, 0x0e, 0xb8, 0x26, 0x6a, 0x13, 0x47, + 0xb1, 0xa9, 0x80, 0xaf, 0xe0, 0x63, 0xfa, 0x11, 0xa8, 0x13, 0x63, 0x97, 0x3e, 0x04, 0x3f, 0x52, + 0xc5, 0x76, 0x54, 0x08, 0x9d, 0x72, 0x6f, 0xce, 0xb9, 0xe7, 0x1c, 0x5f, 0x1b, 0xb6, 0xc6, 0x5c, + 0x44, 0x5c, 0x60, 0x32, 0x93, 0xd3, 0x25, 0x7e, 0xed, 0x04, 0x54, 0x92, 0x0e, 0x66, 0x34, 0xa6, + 0x22, 0x14, 0x28, 0x49, 0xb9, 0xe4, 0x76, 0x4d, 0x73, 0x90, 0xe2, 0x20, 0xc3, 0xa9, 0x37, 0x18, + 0xe7, 0xec, 0x85, 0x62, 0xc5, 0x09, 0x66, 0x4f, 0x58, 0x86, 0x11, 0x15, 0x92, 0x44, 0x89, 0x1e, + 0xab, 0x5f, 0x17, 0x09, 0x24, 0x5e, 0x18, 0xa8, 0xc6, 0x38, 0xe3, 0xaa, 0xc4, 0x59, 0x95, 0x0f, + 0x68, 0x9f, 0x47, 0x0d, 0x18, 0x53, 0xd5, 0xb4, 0x26, 0xf0, 0x72, 0xa0, 0x33, 0xdd, 0x4b, 0x22, + 0xa9, 0x3d, 0x82, 0xd5, 0x2c, 0x0d, 0x4f, 0xc3, 0x25, 0x91, 0x21, 0x8f, 0x1d, 0xd0, 0x2c, 0x79, + 0x95, 0xae, 0x87, 0xfe, 0x8b, 0x8a, 0x06, 0x29, 0x89, 0x65, 0x6f, 0x9f, 0xef, 0x97, 0xd7, 0x5f, + 0x0d, 0x6b, 0x78, 0x28, 0xd2, 0xfa, 0x04, 0xd0, 0x3e, 0xe6, 0xda, 0x0e, 0x3c, 0x63, 0xd9, 0x5f, + 0x9a, 0x3a, 0xa0, 0x09, 0xbc, 0x8b, 0x61, 0xde, 0xfe, 0x21, 0xd4, 0x39, 0xd9, 0x47, 0xa8, 0x7d, + 0x57, 0x0c, 0x58, 0x6a, 0x02, 0xaf, 0xd2, 0xad, 0x21, 0xbd, 0x14, 0x94, 0x2f, 0x05, 0xf5, 0xe2, + 0x85, 0x7f, 0xf5, 0xfe, 0xd6, 0xae, 0x1e, 0x78, 0x16, 0x92, 0xd9, 0x7d, 0x08, 0xe9, 0x3c, 0x09, + 0x53, 0xad, 0x55, 0x56, 0x5a, 0xf5, 0x23, 0xad, 0x51, 0x7e, 0x03, 0xfe, 0x79, 0x76, 0xbc, 0xd5, + 0x77, 0x03, 0x0c, 0xf7, 0xe6, 0xfc, 0xfe, 0x7a, 0xeb, 0x82, 0xcd, 0xd6, 0x05, 0x3f, 0x5b, 0x17, + 0xac, 0x76, 0xae, 0xb5, 0xd9, 0xb9, 0xd6, 0xc7, 0xce, 0xb5, 0x1e, 0x6e, 0x58, 0x28, 0xa7, 0xb3, + 0x00, 0x8d, 0x79, 0x64, 0x16, 0x6f, 0x3e, 0x6d, 0x31, 0x79, 0xc6, 0x73, 0xf3, 0x3c, 0xe4, 0x22, + 0xa1, 0x22, 0x38, 0x55, 0x7e, 0xb7, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x94, 0xbe, 0x5b, 0xfa, + 0x3b, 0x02, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { diff --git a/x/authz/types/keys_test.go b/x/authz/types/keys_test.go index 1d6d7cef6..caad7ba5c 100644 --- a/x/authz/types/keys_test.go +++ b/x/authz/types/keys_test.go @@ -7,11 +7,12 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" ) var granter = sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) var grantee = sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) -var msgType = SendAuthorization{}.MethodName() +var msgType = bank.SendAuthorization{}.MethodName() func TestGrantkey(t *testing.T) { granter1, grantee1 := ExtractAddressesFromGrantKey(GetAuthorizationStoreKey(grantee, granter, msgType)) diff --git a/x/authz/types/msgs.go b/x/authz/types/msgs.go index 59ae59c20..8fa3bdd1f 100644 --- a/x/authz/types/msgs.go +++ b/x/authz/types/msgs.go @@ -8,6 +8,7 @@ import ( "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/authz/exported" ) var ( @@ -21,7 +22,7 @@ var ( // NewMsgGrantAuthorization creates a new MsgGrantAuthorization //nolint:interfacer -func NewMsgGrantAuthorization(granter sdk.AccAddress, grantee sdk.AccAddress, authorization Authorization, expiration time.Time) (*MsgGrantAuthorizationRequest, error) { +func NewMsgGrantAuthorization(granter sdk.AccAddress, grantee sdk.AccAddress, authorization exported.Authorization, expiration time.Time) (*MsgGrantAuthorizationRequest, error) { m := &MsgGrantAuthorizationRequest{ Granter: granter.String(), Grantee: grantee.String(), @@ -66,8 +67,8 @@ func (msg MsgGrantAuthorizationRequest) ValidateBasic() error { } // GetGrantAuthorization returns the cache value from the MsgGrantAuthorization.Authorization if present. -func (msg *MsgGrantAuthorizationRequest) GetGrantAuthorization() Authorization { - authorization, ok := msg.Authorization.GetCachedValue().(Authorization) +func (msg *MsgGrantAuthorizationRequest) GetGrantAuthorization() exported.Authorization { + authorization, ok := msg.Authorization.GetCachedValue().(exported.Authorization) if !ok { return nil } @@ -75,7 +76,7 @@ func (msg *MsgGrantAuthorizationRequest) GetGrantAuthorization() Authorization { } // SetAuthorization converts Authorization to any and adds it to MsgGrantAuthorization.Authorization. -func (msg *MsgGrantAuthorizationRequest) SetAuthorization(authorization Authorization) error { +func (msg *MsgGrantAuthorizationRequest) SetAuthorization(authorization exported.Authorization) error { m, ok := authorization.(proto.Message) if !ok { return sdkerrors.Wrapf(sdkerrors.ErrPackAny, "can't proto marshal %T", m) @@ -103,7 +104,7 @@ func (msg MsgExecAuthorizedRequest) UnpackInterfaces(unpacker types.AnyUnpacker) // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (msg MsgGrantAuthorizationRequest) UnpackInterfaces(unpacker types.AnyUnpacker) error { - var authorization Authorization + var authorization exported.Authorization return unpacker.UnpackAny(msg.Authorization, &authorization) } diff --git a/x/authz/types/msgs_test.go b/x/authz/types/msgs_test.go index 3c6cea890..c27c750b0 100644 --- a/x/authz/types/msgs_test.go +++ b/x/authz/types/msgs_test.go @@ -9,6 +9,7 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz/exported" "github.com/cosmos/cosmos-sdk/x/authz/types" ) @@ -29,7 +30,7 @@ func TestMsgExecAuthorized(t *testing.T) { {"zero-messages test: should fail", grantee, []sdk.ServiceMsg{}, false}, {"valid test: msg type", grantee, []sdk.ServiceMsg{ { - MethodName: types.SendAuthorization{}.MethodName(), + MethodName: banktypes.SendAuthorization{}.MethodName(), Request: &banktypes.MsgSend{ Amount: sdk.NewCoins(sdk.NewInt64Coin("steak", 2)), FromAddress: granter.String(), @@ -73,17 +74,17 @@ func TestMsgGrantAuthorization(t *testing.T) { tests := []struct { title string granter, grantee sdk.AccAddress - authorization types.Authorization + authorization exported.Authorization expiration time.Time expectErr bool expectPass bool }{ - {"nil granter address", nil, grantee, &types.SendAuthorization{SpendLimit: coinsPos}, time.Now(), false, false}, - {"nil grantee address", granter, nil, &types.SendAuthorization{SpendLimit: coinsPos}, time.Now(), false, false}, - {"nil granter and grantee address", nil, nil, &types.SendAuthorization{SpendLimit: coinsPos}, time.Now(), false, false}, + {"nil granter address", nil, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now(), false, false}, + {"nil grantee address", granter, nil, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now(), false, false}, + {"nil granter and grantee address", nil, nil, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now(), false, false}, {"nil authorization", granter, grantee, nil, time.Now(), true, false}, - {"valid test case", granter, grantee, &types.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 1, 0), false, true}, - {"past time", granter, grantee, &types.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 0, -1), false, false}, + {"valid test case", granter, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 1, 0), false, true}, + {"past time", granter, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 0, -1), false, false}, } for i, tc := range tests { msg, err := types.NewMsgGrantAuthorization( diff --git a/x/authz/types/query.pb.go b/x/authz/types/query.pb.go index 91accaa10..493412f75 100644 --- a/x/authz/types/query.pb.go +++ b/x/authz/types/query.pb.go @@ -6,12 +6,9 @@ package types import ( context "context" fmt "fmt" - _ "github.com/cosmos/cosmos-sdk/codec/types" query "github.com/cosmos/cosmos-sdk/types/query" - _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" - _ "github.com/regen-network/cosmos-proto" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -266,38 +263,36 @@ func init() { func init() { proto.RegisterFile("cosmos/authz/v1beta1/query.proto", fileDescriptor_376d714ffdeb1545) } var fileDescriptor_376d714ffdeb1545 = []byte{ - // 489 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x94, 0xbf, 0x6f, 0xd4, 0x30, - 0x14, 0xc7, 0xcf, 0x77, 0x02, 0x84, 0x4f, 0xed, 0x60, 0x75, 0x48, 0x03, 0x0a, 0xa7, 0x0c, 0x50, - 0x55, 0x22, 0xe6, 0x8e, 0xbf, 0xa0, 0xa5, 0xa2, 0x82, 0xa1, 0x2a, 0x19, 0x59, 0x2a, 0xa7, 0x7d, - 0x38, 0x11, 0x8d, 0x9d, 0xc6, 0x0e, 0xa2, 0x45, 0x2c, 0xac, 0x2c, 0x48, 0x2c, 0xfc, 0x29, 0x2c, - 0x0c, 0x6c, 0x8c, 0x95, 0x58, 0x18, 0xd1, 0x1d, 0x7f, 0x08, 0x8a, 0xed, 0xb4, 0x09, 0x0a, 0x6a, - 0xab, 0x4e, 0xb1, 0xdf, 0xaf, 0xef, 0xe7, 0x3d, 0x3f, 0x05, 0x4f, 0xf6, 0xa5, 0xca, 0xa5, 0xa2, - 0xac, 0xd2, 0xe9, 0x09, 0x7d, 0x33, 0x4d, 0x40, 0xb3, 0x29, 0x3d, 0xaa, 0xa0, 0x3c, 0x8e, 0x8a, - 0x52, 0x6a, 0x49, 0x56, 0x6c, 0x44, 0x64, 0x22, 0x22, 0x17, 0xe1, 0xaf, 0x70, 0xc9, 0xa5, 0x09, - 0xa0, 0xf5, 0xc9, 0xc6, 0xfa, 0xab, 0x5c, 0x4a, 0x7e, 0x08, 0xd4, 0xdc, 0x92, 0xea, 0x15, 0x65, - 0xc2, 0x95, 0xf1, 0xef, 0x3a, 0x17, 0x2b, 0x32, 0xca, 0x84, 0x90, 0x9a, 0xe9, 0x4c, 0x0a, 0xd5, - 0x24, 0x5a, 0x91, 0x3d, 0x5b, 0xd1, 0x29, 0x5a, 0xd7, 0xba, 0x23, 0x4c, 0x98, 0x02, 0x0b, 0x76, - 0x86, 0x59, 0x30, 0x9e, 0x09, 0x53, 0xc7, 0xc5, 0xf6, 0x77, 0x63, 0xc9, 0x4d, 0x44, 0x58, 0xe0, - 0xd5, 0x17, 0x75, 0x8d, 0x8d, 0x4a, 0xa7, 0xb2, 0xcc, 0x4e, 0x4c, 0x76, 0x0c, 0x47, 0x15, 0x28, - 0x4d, 0x3c, 0x7c, 0x8b, 0x97, 0x4c, 0x68, 0x28, 0x3d, 0x34, 0x41, 0x6b, 0xb7, 0xe3, 0xe6, 0x7a, - 0xee, 0x01, 0x6f, 0xd8, 0xf6, 0x00, 0xb9, 0x87, 0xc7, 0x39, 0xe8, 0x54, 0x1e, 0xec, 0x09, 0x96, - 0x83, 0x37, 0x32, 0x5e, 0x6c, 0x4d, 0x3b, 0x2c, 0x87, 0xf0, 0x10, 0xfb, 0x7d, 0x8a, 0xaa, 0x90, - 0x42, 0x01, 0xd9, 0xc1, 0x4b, 0xac, 0xed, 0x30, 0xc2, 0xe3, 0xd9, 0x5a, 0xd4, 0x37, 0xf5, 0xa8, - 0x53, 0x63, 0xbb, 0x26, 0x88, 0xbb, 0xe9, 0xe1, 0x17, 0xd4, 0x27, 0xa7, 0xae, 0xd3, 0xe1, 0x53, - 0x8c, 0xcf, 0x07, 0x6d, 0x1a, 0x1c, 0xcf, 0xee, 0x37, 0x7c, 0xf5, 0xab, 0x44, 0x76, 0x5d, 0x1a, - 0xc8, 0x5d, 0xc6, 0xc1, 0xe9, 0xc5, 0xad, 0xcc, 0xf0, 0x2b, 0xc2, 0x77, 0x7a, 0xd1, 0xdc, 0x28, - 0x76, 0xf1, 0x72, 0xa7, 0x17, 0xe5, 0xa1, 0xc9, 0xe8, 0x4a, 0xb3, 0xf8, 0x27, 0x9f, 0x6c, 0x77, - 0xc8, 0x87, 0x86, 0xfc, 0xc1, 0x85, 0xe4, 0x16, 0xa7, 0x8d, 0x3e, 0xfb, 0x38, 0xc2, 0x37, 0x0c, - 0x3a, 0xf9, 0x86, 0xf0, 0x52, 0x47, 0x99, 0xd0, 0x7e, 0xbc, 0xff, 0x6e, 0x99, 0xff, 0xe8, 0xf2, - 0x09, 0x16, 0x25, 0x7c, 0xf6, 0xe1, 0xe7, 0x9f, 0xcf, 0xc3, 0x27, 0x64, 0x83, 0xf6, 0xee, 0xb7, - 0x7b, 0x42, 0x45, 0xdf, 0xb9, 0xd3, 0x7b, 0x67, 0x82, 0x33, 0x13, 0x38, 0x13, 0xf9, 0x8e, 0xf0, - 0x72, 0x77, 0xfe, 0xe4, 0xd2, 0x3c, 0xcd, 0x16, 0xf9, 0xd3, 0x2b, 0x64, 0xb8, 0x16, 0x9e, 0x9b, - 0x16, 0xb6, 0xc8, 0xe6, 0xb5, 0x5b, 0x50, 0x9b, 0x5b, 0x3f, 0xe6, 0x01, 0x3a, 0x9d, 0x07, 0xe8, - 0xf7, 0x3c, 0x40, 0x9f, 0x16, 0xc1, 0xe0, 0x74, 0x11, 0x0c, 0x7e, 0x2d, 0x82, 0xc1, 0xcb, 0x75, - 0x9e, 0xe9, 0xb4, 0x4a, 0xa2, 0x7d, 0x99, 0x37, 0x3a, 0xf6, 0xf3, 0x50, 0x1d, 0xbc, 0xa6, 0x6f, - 0x9d, 0xa8, 0x3e, 0x2e, 0x40, 0x25, 0x37, 0xcd, 0x0f, 0xe1, 0xf1, 0xdf, 0x00, 0x00, 0x00, 0xff, - 0xff, 0x92, 0xc3, 0xa0, 0xea, 0x02, 0x05, 0x00, 0x00, + // 461 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x94, 0x31, 0x8f, 0xd3, 0x30, + 0x14, 0xc7, 0xeb, 0x56, 0x80, 0x70, 0x75, 0x37, 0x58, 0x0c, 0x21, 0xa0, 0x50, 0x65, 0x80, 0xd3, + 0x49, 0xc4, 0xb4, 0x7c, 0x82, 0x3b, 0x4e, 0x9c, 0x60, 0x38, 0x1d, 0x19, 0x59, 0x90, 0xd3, 0x3e, + 0x25, 0x11, 0x4d, 0x9c, 0xc6, 0x0e, 0xa2, 0x45, 0x2c, 0xac, 0x2c, 0x48, 0x2c, 0x7c, 0x14, 0x16, + 0x06, 0x36, 0xc6, 0x4a, 0x2c, 0x8c, 0xa8, 0xe5, 0x83, 0xa0, 0xda, 0x4e, 0x9b, 0xa0, 0xa0, 0x6b, + 0xd5, 0x29, 0xc9, 0x7b, 0xef, 0xff, 0xfe, 0xbf, 0xf7, 0x6c, 0x05, 0xf7, 0x86, 0x5c, 0x24, 0x5c, + 0x50, 0x56, 0xc8, 0x68, 0x46, 0xdf, 0xf4, 0x03, 0x90, 0xac, 0x4f, 0x27, 0x05, 0xe4, 0x53, 0x2f, + 0xcb, 0xb9, 0xe4, 0xe4, 0x96, 0xae, 0xf0, 0x54, 0x85, 0x67, 0x2a, 0xec, 0xbb, 0x21, 0xe7, 0xe1, + 0x18, 0x28, 0xcb, 0x62, 0xca, 0xd2, 0x94, 0x4b, 0x26, 0x63, 0x9e, 0x0a, 0xad, 0xb1, 0x8f, 0x4d, + 0xd7, 0x80, 0x09, 0xd0, 0xcd, 0xd6, 0xad, 0x33, 0x16, 0xc6, 0xa9, 0x2a, 0x36, 0xb5, 0xcd, 0x04, + 0xda, 0x4d, 0x55, 0xb8, 0x19, 0xbe, 0xfd, 0x62, 0xd5, 0xe3, 0xa4, 0x90, 0x11, 0xcf, 0xe3, 0x99, + 0x52, 0xfb, 0x30, 0x29, 0x40, 0x48, 0x62, 0xe1, 0x1b, 0x61, 0xce, 0x52, 0x09, 0xb9, 0x85, 0x7a, + 0xe8, 0xe8, 0xa6, 0x5f, 0x7e, 0x6e, 0x32, 0x60, 0xb5, 0xab, 0x19, 0x20, 0xf7, 0x70, 0x37, 0x01, + 0x19, 0xf1, 0xd1, 0xab, 0x94, 0x25, 0x60, 0x75, 0x54, 0x16, 0xeb, 0xd0, 0x05, 0x4b, 0xc0, 0x1d, + 0x63, 0xbb, 0xc9, 0x51, 0x64, 0x3c, 0x15, 0x40, 0x2e, 0xf0, 0x01, 0xab, 0x26, 0x94, 0x71, 0x77, + 0x70, 0xe4, 0x35, 0x6d, 0xca, 0xab, 0xf5, 0x38, 0x5f, 0x11, 0xf8, 0x75, 0xb9, 0xfb, 0x05, 0x35, + 0xd9, 0x89, 0x7d, 0x26, 0x7c, 0x8a, 0xf1, 0x66, 0xd1, 0x6a, 0xc0, 0xee, 0xe0, 0x7e, 0xc9, 0xb7, + 0x3a, 0x15, 0x4f, 0x1f, 0x71, 0x09, 0x79, 0xc9, 0x42, 0x30, 0x7e, 0x7e, 0x45, 0xe9, 0x7e, 0x45, + 0xf8, 0x4e, 0x23, 0x9a, 0x59, 0xc5, 0x25, 0x3e, 0xac, 0xcd, 0x22, 0x2c, 0xd4, 0xeb, 0xec, 0xb4, + 0x8b, 0x7f, 0xf4, 0xe4, 0xbc, 0x46, 0xde, 0x56, 0xe4, 0x0f, 0xae, 0x24, 0xd7, 0x38, 0x55, 0xf4, + 0xc1, 0xc7, 0x0e, 0xbe, 0xa6, 0xd0, 0xc9, 0x37, 0x84, 0x0f, 0x6a, 0xce, 0x84, 0x36, 0xe3, 0xfd, + 0xf7, 0x96, 0xd9, 0x8f, 0xb6, 0x17, 0x68, 0x14, 0xf7, 0xd9, 0x87, 0x9f, 0x7f, 0x3e, 0xb7, 0x9f, + 0x90, 0x13, 0xda, 0x78, 0xbf, 0xcd, 0x11, 0x0a, 0xfa, 0xce, 0xbc, 0xbd, 0x37, 0x21, 0x58, 0x87, + 0xc0, 0x84, 0xc8, 0x77, 0x84, 0x0f, 0xeb, 0xfb, 0x27, 0x5b, 0xf3, 0x94, 0xb7, 0xc8, 0xee, 0xef, + 0xa0, 0x30, 0x23, 0x3c, 0x57, 0x23, 0x9c, 0x91, 0xd3, 0xbd, 0x47, 0x10, 0xa7, 0x67, 0x3f, 0x16, + 0x0e, 0x9a, 0x2f, 0x1c, 0xf4, 0x7b, 0xe1, 0xa0, 0x4f, 0x4b, 0xa7, 0x35, 0x5f, 0x3a, 0xad, 0x5f, + 0x4b, 0xa7, 0xf5, 0xf2, 0x38, 0x8c, 0x65, 0x54, 0x04, 0xde, 0x90, 0x27, 0xa5, 0x8f, 0x7e, 0x3c, + 0x14, 0xa3, 0xd7, 0xf4, 0xad, 0x31, 0x95, 0xd3, 0x0c, 0x44, 0x70, 0x5d, 0xfd, 0x10, 0x1e, 0xff, + 0x0d, 0x00, 0x00, 0xff, 0xff, 0x91, 0xf5, 0x18, 0x65, 0xb6, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/authz/types/query.pb.gw.go b/x/authz/types/query.pb.gw.go index d1cd24bc0..d353c1bf0 100644 --- a/x/authz/types/query.pb.gw.go +++ b/x/authz/types/query.pb.gw.go @@ -350,9 +350,9 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Authorization_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7}, []string{"cosmos", "authz", "v1beta1", "granters", "granter", "grantees", "grantee", "grant"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Authorization_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7}, []string{"cosmos", "authz", "v1beta1", "granters", "granter", "grantees", "grantee", "grant"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Authorizations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7}, []string{"cosmos", "authz", "v1beta1", "granters", "granter", "grantees", "grantee", "grants"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Authorizations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7}, []string{"cosmos", "authz", "v1beta1", "granters", "granter", "grantees", "grantee", "grants"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/bank/app_test.go b/x/bank/app_test.go index 82d898b09..6cb3655cc 100644 --- a/x/bank/app_test.go +++ b/x/bank/app_test.go @@ -95,8 +95,7 @@ func TestSendNotEnoughBalance(t *testing.T) { app := simapp.SetupWithGenesisAccounts(genAccs) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - err := app.BankKeeper.SetBalances(ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 67))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 67)))) app.Commit() @@ -110,7 +109,7 @@ func TestSendNotEnoughBalance(t *testing.T) { sendMsg := types.NewMsgSend(addr1, addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 100)}) header := tmproto.Header{Height: app.LastBlockHeight() + 1} txGen := simapp.MakeTestEncodingConfig().TxConfig - _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{sendMsg}, "", []uint64{origAccNum}, []uint64{origSeq}, false, false, priv1) + _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{sendMsg}, "", []uint64{origAccNum}, []uint64{origSeq}, false, false, priv1) require.Error(t, err) simapp.CheckBalance(t, app, addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 67)}) @@ -169,8 +168,7 @@ func TestSendToModuleAcc(t *testing.T) { toAddr, err := sdk.AccAddressFromBech32(test.msg.ToAddress) require.NoError(t, err) - err = app.BankKeeper.SetBalances(ctx, fromAddr, test.fromBalance) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, fromAddr, test.fromBalance)) app.Commit() @@ -211,8 +209,7 @@ func TestMsgMultiSendWithAccounts(t *testing.T) { app := simapp.SetupWithGenesisAccounts(genAccs) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - err := app.BankKeeper.SetBalances(ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 67))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 67)))) app.Commit() @@ -282,11 +279,9 @@ func TestMsgMultiSendMultipleOut(t *testing.T) { app := simapp.SetupWithGenesisAccounts(genAccs) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - err := app.BankKeeper.SetBalances(ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42)))) - err = app.BankKeeper.SetBalances(ctx, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42)))) app.Commit() @@ -333,14 +328,11 @@ func TestMsgMultiSendMultipleInOut(t *testing.T) { app := simapp.SetupWithGenesisAccounts(genAccs) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - err := app.BankKeeper.SetBalances(ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42)))) - err = app.BankKeeper.SetBalances(ctx, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42)))) - err = app.BankKeeper.SetBalances(ctx, addr4, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, addr4, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42)))) app.Commit() @@ -383,8 +375,7 @@ func TestMsgMultiSendDependent(t *testing.T) { app := simapp.SetupWithGenesisAccounts(genAccs) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - err = app.BankKeeper.SetBalances(ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42)))) app.Commit() diff --git a/x/bank/bench_test.go b/x/bank/bench_test.go index 0648b3b62..c7da72c19 100644 --- a/x/bank/bench_test.go +++ b/x/bank/bench_test.go @@ -30,8 +30,7 @@ func BenchmarkOneBankSendTxPerBlock(b *testing.B) { ctx := benchmarkApp.BaseApp.NewContext(false, tmproto.Header{}) // some value conceivably higher than the benchmarks would ever go - err := benchmarkApp.BankKeeper.SetBalances(ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 100000000000))) - require.NoError(b, err) + require.NoError(b, simapp.FundAccount(benchmarkApp, ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 100000000000)))) benchmarkApp.Commit() txGen := simappparams.MakeTestEncodingConfig().TxConfig @@ -72,8 +71,7 @@ func BenchmarkOneBankMultiSendTxPerBlock(b *testing.B) { ctx := benchmarkApp.BaseApp.NewContext(false, tmproto.Header{}) // some value conceivably higher than the benchmarks would ever go - err := benchmarkApp.BankKeeper.SetBalances(ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 100000000000))) - require.NoError(b, err) + require.NoError(b, simapp.FundAccount(benchmarkApp, ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 100000000000)))) benchmarkApp.Commit() txGen := simappparams.MakeTestEncodingConfig().TxConfig diff --git a/x/bank/client/cli/cli_test.go b/x/bank/client/cli/cli_test.go index 952390fe7..19325b430 100644 --- a/x/bank/client/cli/cli_test.go +++ b/x/bank/client/cli/cli_test.go @@ -40,6 +40,8 @@ func (s *IntegrationTestSuite) SetupSuite() { bankGenesis.DenomMetadata = []types.Metadata{ { + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ { @@ -57,6 +59,8 @@ func (s *IntegrationTestSuite) SetupSuite() { Display: "atom", }, { + Name: "Ethereum", + Symbol: "ETH", Description: "Ethereum mainnet token", DenomUnits: []*types.DenomUnit{ { @@ -246,6 +250,8 @@ func (s *IntegrationTestSuite) TestGetCmdQueryDenomsMetadata() { expected: &types.QueryDenomsMetadataResponse{ Metadatas: []types.Metadata{ { + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ { @@ -263,6 +269,8 @@ func (s *IntegrationTestSuite) TestGetCmdQueryDenomsMetadata() { Display: "atom", }, { + Name: "Ethereum", + Symbol: "ETH", Description: "Ethereum mainnet token", DenomUnits: []*types.DenomUnit{ { @@ -293,6 +301,8 @@ func (s *IntegrationTestSuite) TestGetCmdQueryDenomsMetadata() { respType: &types.QueryDenomMetadataResponse{}, expected: &types.QueryDenomMetadataResponse{ Metadata: types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ { @@ -372,7 +382,10 @@ func (s *IntegrationTestSuite) TestNewSendTxCmdGenOnly() { s.Require().NoError(err) tx, err := s.cfg.TxConfig.TxJSONDecoder()(bz.Bytes()) s.Require().NoError(err) - s.Require().Equal([]sdk.Msg{types.NewMsgSend(from, to, amount)}, tx.GetMsgs()) + s.Require().Equal([]sdk.Msg{sdk.ServiceMsg{ + MethodName: "/cosmos.bank.v1beta1.Msg/Send", + Request: types.NewMsgSend(from, to, amount), + }}, tx.GetMsgs()) } func (s *IntegrationTestSuite) TestNewSendTxCmd() { @@ -461,61 +474,6 @@ func (s *IntegrationTestSuite) TestNewSendTxCmd() { } } -// TestBankMsgService does a basic test of whether or not service Msg's as defined -// in ADR 031 work in the most basic end-to-end case. -func (s *IntegrationTestSuite) TestBankMsgService() { - val := s.network.Validators[0] - - testCases := []struct { - name string - from, to sdk.AccAddress - amount sdk.Coins - args []string - expectErr bool - respType proto.Message - expectedCode uint32 - rawLogContains string - }{ - { - "valid transaction", - val.Address, - val.Address, - sdk.NewCoins( - sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), sdk.NewInt(10)), - sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), - ), - []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - }, - false, - &sdk.TxResponse{}, - 0, - "/cosmos.bank.v1beta1.Msg/Send", // indicates we are using ServiceMsg and not a regular Msg - }, - } - - for _, tc := range testCases { - tc := tc - - s.Run(tc.name, func() { - clientCtx := val.ClientCtx - bz, err := banktestutil.ServiceMsgSendExec(clientCtx, tc.from, tc.to, tc.amount, tc.args...) - if tc.expectErr { - s.Require().Error(err) - } else { - s.Require().NoError(err) - - s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(bz.Bytes(), tc.respType), bz.String()) - txResp := tc.respType.(*sdk.TxResponse) - s.Require().Equal(tc.expectedCode, txResp.Code) - s.Require().Contains(txResp.RawLog, tc.rawLogContains) - } - }) - } -} - func TestIntegrationTestSuite(t *testing.T) { suite.Run(t, new(IntegrationTestSuite)) } diff --git a/x/bank/client/cli/tx.go b/x/bank/client/cli/tx.go index d88a95fc6..4a615d504 100644 --- a/x/bank/client/cli/tx.go +++ b/x/bank/client/cli/tx.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -49,11 +50,14 @@ ignored as it is implied from [from_key_or_address].`, } msg := types.NewMsgSend(clientCtx.GetFromAddress(), toAddr, coins) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.Send(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } diff --git a/x/bank/client/rest/grpc_query_test.go b/x/bank/client/rest/grpc_query_test.go index d6290a1fe..438cca529 100644 --- a/x/bank/client/rest/grpc_query_test.go +++ b/x/bank/client/rest/grpc_query_test.go @@ -121,6 +121,8 @@ func (s *IntegrationTestSuite) TestDenomMetadataGRPCHandler() { &types.QueryDenomsMetadataResponse{ Metadatas: []types.Metadata{ { + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ { @@ -151,6 +153,8 @@ func (s *IntegrationTestSuite) TestDenomMetadataGRPCHandler() { &types.QueryDenomMetadataResponse{}, &types.QueryDenomMetadataResponse{ Metadata: types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ { diff --git a/x/bank/client/rest/query_test.go b/x/bank/client/rest/query_test.go index 11d731b6d..0201e6763 100644 --- a/x/bank/client/rest/query_test.go +++ b/x/bank/client/rest/query_test.go @@ -33,6 +33,8 @@ func (s *IntegrationTestSuite) SetupSuite() { bankGenesis.DenomMetadata = []types.Metadata{ { + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ { diff --git a/x/bank/client/testutil/cli_helpers.go b/x/bank/client/testutil/cli_helpers.go index e36f3744a..57a5a5798 100644 --- a/x/bank/client/testutil/cli_helpers.go +++ b/x/bank/client/testutil/cli_helpers.go @@ -1,18 +1,20 @@ package testutil import ( + "context" "fmt" - "github.com/spf13/cobra" "github.com/tendermint/tendermint/libs/cli" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/testutil" clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/msgservice" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/cli" "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -31,54 +33,55 @@ func QueryBalancesExec(clientCtx client.Context, address fmt.Stringer, extraArgs return clitestutil.ExecTestCLICmd(clientCtx, bankcli.GetBalancesCmd(), args) } -// newSendTxMsgServiceCmd is just for the purpose of testing ServiceMsg's in an end-to-end case. It is effectively -// NewSendTxCmd but using MsgClient. -func newSendTxMsgServiceCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "send [from_key_or_address] [to_address] [amount]", - Short: `Send funds from one account to another. Note, the'--from' flag is -ignored as it is implied from [from_key_or_address].`, - Args: cobra.ExactArgs(3), - RunE: func(cmd *cobra.Command, args []string) error { - cmd.Flags().Set(flags.FlagFrom, args[0]) - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - toAddr, err := sdk.AccAddressFromBech32(args[1]) - if err != nil { - return err - } +// LegacyGRPCProtoMsgSend is a legacy method to broadcast a legacy proto MsgSend. +// +// Deprecated. +//nolint:interfacer +func LegacyGRPCProtoMsgSend(clientCtx client.Context, keyName string, from, to sdk.Address, fee, amount []sdk.Coin, extraArgs ...string) (*txtypes.BroadcastTxResponse, error) { + // prepare txBuilder with msg + txBuilder := clientCtx.TxConfig.NewTxBuilder() + feeAmount := fee + gasLimit := testdata.NewTestGasLimit() - coins, err := sdk.ParseCoinsNormalized(args[2]) - if err != nil { - return err - } - - msg := types.NewMsgSend(clientCtx.GetFromAddress(), toAddr, coins) - svcMsgClientConn := &msgservice.ServiceMsgClientConn{} - bankMsgClient := types.NewMsgClient(svcMsgClientConn) - _, err = bankMsgClient.Send(cmd.Context(), msg) - if err != nil { - return err - } - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) - }, + // This sets a legacy Proto MsgSend. + err := txBuilder.SetMsgs(&types.MsgSend{ + FromAddress: from.String(), + ToAddress: to.String(), + Amount: amount, + }) + if err != nil { + return nil, err } - flags.AddTxFlagsToCmd(cmd) + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) - return cmd -} - -// ServiceMsgSendExec is a temporary method to test Msg services in CLI using -// x/bank's Msg/Send service. After https://github.com/cosmos/cosmos-sdk/issues/7541 -// is merged, this method should be removed, and we should prefer MsgSendExec -// instead. -func ServiceMsgSendExec(clientCtx client.Context, from, to, amount fmt.Stringer, extraArgs ...string) (testutil.BufferWriter, error) { - args := []string{from.String(), to.String(), amount.String()} - args = append(args, extraArgs...) - - return clitestutil.ExecTestCLICmd(clientCtx, newSendTxMsgServiceCmd(), args) + // setup txFactory + txFactory := tx.Factory{}. + WithChainID(clientCtx.ChainID). + WithKeybase(clientCtx.Keyring). + WithTxConfig(clientCtx.TxConfig). + WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) + + // Sign Tx. + err = authclient.SignTx(txFactory, clientCtx, keyName, txBuilder, false, true) + if err != nil { + return nil, err + } + + txBytes, err := clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return nil, err + } + + // Broadcast the tx via gRPC. + queryClient := txtypes.NewServiceClient(clientCtx) + + return queryClient.BroadcastTx( + context.Background(), + &txtypes.BroadcastTxRequest{ + Mode: txtypes.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, + }, + ) } diff --git a/x/bank/keeper/genesis.go b/x/bank/keeper/genesis.go index d30415c6a..2781645c4 100644 --- a/x/bank/keeper/genesis.go +++ b/x/bank/keeper/genesis.go @@ -20,7 +20,7 @@ func (k BaseKeeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { panic(err) } - if err := k.SetBalances(ctx, addr, balance.Coins); err != nil { + if err := k.setBalances(ctx, addr, balance.Coins); err != nil { panic(fmt.Errorf("error on setting balances %w", err)) } @@ -31,7 +31,7 @@ func (k BaseKeeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { genState.Supply = totalSupply } - k.SetSupply(ctx, types.NewSupply(genState.Supply)) + k.setSupply(ctx, types.NewSupply(genState.Supply)) for _, meta := range genState.DenomMetadata { k.SetDenomMetaData(ctx, meta) diff --git a/x/bank/keeper/genesis_test.go b/x/bank/keeper/genesis_test.go index 0e66da54e..35e56c129 100644 --- a/x/bank/keeper/genesis_test.go +++ b/x/bank/keeper/genesis_test.go @@ -3,43 +3,53 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" ) func (suite *IntegrationTestSuite) TestExportGenesis() { app, ctx := suite.app, suite.ctx expectedMetadata := suite.getTestMetadata() - expectedBalances := suite.getTestBalances() + expectedBalances, totalSupply := suite.getTestBalancesAndSupply() for i := range []int{1, 2} { app.BankKeeper.SetDenomMetaData(ctx, expectedMetadata[i]) accAddr, err1 := sdk.AccAddressFromBech32(expectedBalances[i].Address) if err1 != nil { panic(err1) } - err := app.BankKeeper.SetBalances(ctx, accAddr, expectedBalances[i].Coins) - suite.Require().NoError(err) + // set balances via mint and send + suite. + Require(). + NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, expectedBalances[i].Coins)) + suite. + Require(). + NoError(app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, accAddr, expectedBalances[i].Coins)) } - - totalSupply := types.NewSupply(sdk.NewCoins(sdk.NewInt64Coin("test", 400000000))) - app.BankKeeper.SetSupply(ctx, totalSupply) app.BankKeeper.SetParams(ctx, types.DefaultParams()) exportGenesis := app.BankKeeper.ExportGenesis(ctx) suite.Require().Len(exportGenesis.Params.SendEnabled, 0) suite.Require().Equal(types.DefaultParams().DefaultSendEnabled, exportGenesis.Params.DefaultSendEnabled) - suite.Require().Equal(totalSupply.GetTotal(), exportGenesis.Supply) + suite.Require().Equal(totalSupply.Total, exportGenesis.Supply) + // add mint module balance as nil + expectedBalances = append(expectedBalances, types.Balance{Address: "cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q", Coins: nil}) suite.Require().Equal(expectedBalances, exportGenesis.Balances) suite.Require().Equal(expectedMetadata, exportGenesis.DenomMetadata) } -func (suite *IntegrationTestSuite) getTestBalances() []types.Balance { +func (suite *IntegrationTestSuite) getTestBalancesAndSupply() ([]types.Balance, *types.Supply) { addr2, _ := sdk.AccAddressFromBech32("cosmos1f9xjhxm0plzrh9cskf4qee4pc2xwp0n0556gh0") addr1, _ := sdk.AccAddressFromBech32("cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh") + addr1Balance := sdk.Coins{sdk.NewInt64Coin("testcoin3", 10)} + addr2Balance := sdk.Coins{sdk.NewInt64Coin("testcoin1", 32), sdk.NewInt64Coin("testcoin2", 34)} + + totalSupply := types.NewSupply(addr1Balance) + totalSupply.Inflate(addr2Balance) return []types.Balance{ - {Address: addr2.String(), Coins: sdk.Coins{sdk.NewInt64Coin("testcoin1", 32), sdk.NewInt64Coin("testcoin2", 34)}}, - {Address: addr1.String(), Coins: sdk.Coins{sdk.NewInt64Coin("testcoin3", 10)}}, - } + {Address: addr2.String(), Coins: addr2Balance}, + {Address: addr1.String(), Coins: addr1Balance}, + }, totalSupply } diff --git a/x/bank/keeper/grpc_query_test.go b/x/bank/keeper/grpc_query_test.go index e178f1301..cd319b247 100644 --- a/x/bank/keeper/grpc_query_test.go +++ b/x/bank/keeper/grpc_query_test.go @@ -6,6 +6,10 @@ import ( gocontext "context" "fmt" + "github.com/cosmos/cosmos-sdk/simapp" + + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" @@ -32,7 +36,7 @@ func (suite *IntegrationTestSuite) TestQueryBalance() { acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) app.AccountKeeper.SetAccount(ctx, acc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, acc.GetAddress(), origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, acc.GetAddress(), origCoins)) res, err = queryClient.Balance(gocontext.Background(), req) suite.Require().NoError(err) @@ -64,7 +68,7 @@ func (suite *IntegrationTestSuite) TestQueryAllBalances() { acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) app.AccountKeeper.SetAccount(ctx, acc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, acc.GetAddress(), origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, acc.GetAddress(), origCoins)) res, err = queryClient.AllBalances(gocontext.Background(), req) suite.Require().NoError(err) @@ -87,7 +91,9 @@ func (suite *IntegrationTestSuite) TestQueryAllBalances() { func (suite *IntegrationTestSuite) TestQueryTotalSupply() { app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient expectedTotalSupply := types.NewSupply(sdk.NewCoins(sdk.NewInt64Coin("test", 400000000))) - app.BankKeeper.SetSupply(ctx, expectedTotalSupply) + suite. + Require(). + NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, expectedTotalSupply.Total)) res, err := queryClient.TotalSupply(gocontext.Background(), &types.QueryTotalSupplyRequest{}) suite.Require().NoError(err) @@ -102,7 +108,9 @@ func (suite *IntegrationTestSuite) TestQueryTotalSupplyOf() { test1Supply := sdk.NewInt64Coin("test1", 4000000) test2Supply := sdk.NewInt64Coin("test2", 700000000) expectedTotalSupply := types.NewSupply(sdk.NewCoins(test1Supply, test2Supply)) - app.BankKeeper.SetSupply(ctx, expectedTotalSupply) + suite. + Require(). + NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, expectedTotalSupply.Total)) _, err := queryClient.SupplyOf(gocontext.Background(), &types.QuerySupplyOfRequest{}) suite.Require().Error(err) diff --git a/x/bank/keeper/keeper.go b/x/bank/keeper/keeper.go index e5f221e4e..c65a3ab7c 100644 --- a/x/bank/keeper/keeper.go +++ b/x/bank/keeper/keeper.go @@ -1,8 +1,6 @@ package keeper import ( - "time" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" @@ -25,7 +23,6 @@ type Keeper interface { ExportGenesis(sdk.Context) *types.GenesisState GetSupply(ctx sdk.Context) exported.SupplyI - SetSupply(ctx sdk.Context, supply exported.SupplyI) GetDenomMetaData(ctx sdk.Context, denom string) (types.Metadata, bool) SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata) @@ -102,17 +99,21 @@ func (k BaseKeeper) DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr } balances = balances.Add(balance) - err := k.SetBalance(ctx, delegatorAddr, balance.Sub(coin)) + err := k.setBalance(ctx, delegatorAddr, balance.Sub(coin)) if err != nil { return err } } - if err := k.trackDelegation(ctx, delegatorAddr, ctx.BlockHeader().Time, balances, amt); err != nil { + if err := k.trackDelegation(ctx, delegatorAddr, balances, amt); err != nil { return sdkerrors.Wrap(err, "failed to track delegation") } + // emit coin spent event + ctx.EventManager().EmitEvent( + types.NewCoinSpentEvent(delegatorAddr, amt), + ) - err := k.AddCoins(ctx, moduleAccAddr, amt) + err := k.addCoins(ctx, moduleAccAddr, amt) if err != nil { return err } @@ -135,7 +136,7 @@ func (k BaseKeeper) UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAdd return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) } - err := k.SubtractCoins(ctx, moduleAccAddr, amt) + err := k.subUnlockedCoins(ctx, moduleAccAddr, amt) if err != nil { return err } @@ -144,7 +145,7 @@ func (k BaseKeeper) UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAdd return sdkerrors.Wrap(err, "failed to track undelegation") } - err = k.AddCoins(ctx, delegatorAddr, amt) + err = k.addCoins(ctx, delegatorAddr, amt) if err != nil { return err } @@ -168,8 +169,8 @@ func (k BaseKeeper) GetSupply(ctx sdk.Context) exported.SupplyI { return supply } -// SetSupply sets the Supply to store -func (k BaseKeeper) SetSupply(ctx sdk.Context, supply exported.SupplyI) { +// setSupply sets the Supply to store +func (k BaseKeeper) setSupply(ctx sdk.Context, supply exported.SupplyI) { store := ctx.KVStore(k.storeKey) bz, err := k.MarshalSupply(supply) if err != nil { @@ -332,7 +333,7 @@ func (k BaseKeeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to mint tokens", moduleName)) } - err := k.AddCoins(ctx, acc.GetAddress(), amt) + err := k.addCoins(ctx, acc.GetAddress(), amt) if err != nil { return err } @@ -341,11 +342,16 @@ func (k BaseKeeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) supply := k.GetSupply(ctx) supply.Inflate(amt) - k.SetSupply(ctx, supply) + k.setSupply(ctx, supply) logger := k.Logger(ctx) logger.Info("minted coins from module account", "amount", amt.String(), "from", moduleName) + // emit mint event + ctx.EventManager().EmitEvent( + types.NewCoinMintEvent(acc.GetAddress(), amt), + ) + return nil } @@ -361,7 +367,7 @@ func (k BaseKeeper) BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to burn tokens", moduleName)) } - err := k.SubtractCoins(ctx, acc.GetAddress(), amt) + err := k.subUnlockedCoins(ctx, acc.GetAddress(), amt) if err != nil { return err } @@ -369,15 +375,20 @@ func (k BaseKeeper) BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) // update total supply supply := k.GetSupply(ctx) supply.Deflate(amt) - k.SetSupply(ctx, supply) + k.setSupply(ctx, supply) logger := k.Logger(ctx) logger.Info("burned tokens from module account", "amount", amt.String(), "from", moduleName) + // emit burn event + ctx.EventManager().EmitEvent( + types.NewCoinBurnEvent(acc.GetAddress(), amt), + ) + return nil } -func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, blockTime time.Time, balance, amt sdk.Coins) error { +func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, balance, amt sdk.Coins) error { acc := k.ak.GetAccount(ctx, addr) if acc == nil { return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr) @@ -386,7 +397,7 @@ func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, blockT vacc, ok := acc.(vestexported.VestingAccount) if ok { // TODO: return error on account.TrackDelegation - vacc.TrackDelegation(blockTime, balance, amt) + vacc.TrackDelegation(ctx.BlockHeader().Time, balance, amt) } return nil diff --git a/x/bank/keeper/keeper_test.go b/x/bank/keeper/keeper_test.go index 80b4b070c..ffd4838d0 100644 --- a/x/bank/keeper/keeper_test.go +++ b/x/bank/keeper/keeper_test.go @@ -4,6 +4,8 @@ import ( "testing" "time" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -89,7 +91,7 @@ func (suite *IntegrationTestSuite) TestSupply() { initTokens := sdk.TokensFromConsensusPower(initialPower) totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens)) - app.BankKeeper.SetSupply(ctx, types.NewSupply(totalSupply)) + suite.NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, totalSupply)) total := app.BankKeeper.GetSupply(ctx).GetTotal() suite.Require().Equal(totalSupply, total) @@ -118,23 +120,30 @@ func (suite *IntegrationTestSuite) TestSupply_SendCoins() { ) baseAcc := authKeeper.NewAccountWithAddress(ctx, authtypes.NewModuleAddress("baseAcc")) - suite.Require().NoError(keeper.SetBalances(ctx, holderAcc.GetAddress(), initCoins)) - keeper.SetSupply(ctx, types.NewSupply(initCoins)) + // set initial balances + suite. + Require(). + NoError(keeper.MintCoins(ctx, minttypes.ModuleName, initCoins)) + + suite. + Require(). + NoError(keeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, holderAcc.GetAddress(), initCoins)) + authKeeper.SetModuleAccount(ctx, holderAcc) authKeeper.SetModuleAccount(ctx, burnerAcc) authKeeper.SetAccount(ctx, baseAcc) suite.Require().Panics(func() { - keeper.SendCoinsFromModuleToModule(ctx, "", holderAcc.GetName(), initCoins) // nolint:errcheck + _ = keeper.SendCoinsFromModuleToModule(ctx, "", holderAcc.GetName(), initCoins) // nolint:errcheck }) suite.Require().Panics(func() { - keeper.SendCoinsFromModuleToModule(ctx, authtypes.Burner, "", initCoins) // nolint:errcheck + _ = keeper.SendCoinsFromModuleToModule(ctx, authtypes.Burner, "", initCoins) // nolint:errcheck }) suite.Require().Panics(func() { - keeper.SendCoinsFromModuleToAccount(ctx, "", baseAcc.GetAddress(), initCoins) // nolint:errcheck + _ = keeper.SendCoinsFromModuleToAccount(ctx, "", baseAcc.GetAddress(), initCoins) // nolint:errcheck }) suite.Require().Error( @@ -144,17 +153,17 @@ func (suite *IntegrationTestSuite) TestSupply_SendCoins() { suite.Require().NoError( keeper.SendCoinsFromModuleToModule(ctx, holderAcc.GetName(), authtypes.Burner, initCoins), ) - suite.Require().Equal(sdk.Coins(nil), getCoinsByName(ctx, keeper, authKeeper, holderAcc.GetName())) + suite.Require().Equal(sdk.NewCoins().String(), getCoinsByName(ctx, keeper, authKeeper, holderAcc.GetName()).String()) suite.Require().Equal(initCoins, getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner)) suite.Require().NoError( keeper.SendCoinsFromModuleToAccount(ctx, authtypes.Burner, baseAcc.GetAddress(), initCoins), ) - suite.Require().Equal(sdk.Coins(nil), getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner)) + suite.Require().Equal(sdk.NewCoins().String(), getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner).String()) suite.Require().Equal(initCoins, keeper.GetAllBalances(ctx, baseAcc.GetAddress())) suite.Require().NoError(keeper.SendCoinsFromAccountToModule(ctx, baseAcc.GetAddress(), authtypes.Burner, initCoins)) - suite.Require().Equal(sdk.Coins(nil), keeper.GetAllBalances(ctx, baseAcc.GetAddress())) + suite.Require().Equal(sdk.NewCoins().String(), keeper.GetAllBalances(ctx, baseAcc.GetAddress()).String()) suite.Require().Equal(initCoins, getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner)) } @@ -234,37 +243,45 @@ func (suite *IntegrationTestSuite) TestSupply_BurnCoins() { app.GetSubspace(types.ModuleName), make(map[string]bool), ) - suite.Require().NoError(keeper.SetBalances(ctx, burnerAcc.GetAddress(), initCoins)) - keeper.SetSupply(ctx, types.NewSupply(initCoins)) + // set burnerAcc balance authKeeper.SetModuleAccount(ctx, burnerAcc) + suite. + Require(). + NoError(keeper.MintCoins(ctx, authtypes.Minter, initCoins)) + suite. + Require(). + NoError(keeper.SendCoinsFromModuleToAccount(ctx, authtypes.Minter, burnerAcc.GetAddress(), initCoins)) - initialSupply := keeper.GetSupply(ctx) - initialSupply.Inflate(initCoins) - keeper.SetSupply(ctx, initialSupply) + // inflate supply + suite. + Require(). + NoError(keeper.MintCoins(ctx, authtypes.Minter, initCoins)) + supplyAfterInflation := keeper.GetSupply(ctx) - suite.Require().Panics(func() { keeper.BurnCoins(ctx, "", initCoins) }, "no module account") // nolint:errcheck - suite.Require().Panics(func() { keeper.BurnCoins(ctx, authtypes.Minter, initCoins) }, "invalid permission") // nolint:errcheck - suite.Require().Panics(func() { keeper.BurnCoins(ctx, randomPerm, initialSupply.GetTotal()) }, "random permission") // nolint:errcheck - err := keeper.BurnCoins(ctx, authtypes.Burner, initialSupply.GetTotal()) + suite.Require().Panics(func() { keeper.BurnCoins(ctx, "", initCoins) }, "no module account") // nolint:errcheck + suite.Require().Panics(func() { keeper.BurnCoins(ctx, authtypes.Minter, initCoins) }, "invalid permission") // nolint:errcheck + suite.Require().Panics(func() { keeper.BurnCoins(ctx, randomPerm, supplyAfterInflation.GetTotal()) }, "random permission") // nolint:errcheck + err := keeper.BurnCoins(ctx, authtypes.Burner, supplyAfterInflation.GetTotal()) suite.Require().Error(err, "insufficient coins") err = keeper.BurnCoins(ctx, authtypes.Burner, initCoins) suite.Require().NoError(err) - suite.Require().Equal(sdk.Coins(nil), getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner)) - suite.Require().Equal(initialSupply.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal()) + suite.Require().Equal(sdk.NewCoins().String(), getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner).String()) + suite.Require().Equal(supplyAfterInflation.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal()) // test same functionality on module account with multiple permissions - initialSupply = keeper.GetSupply(ctx) - initialSupply.Inflate(initCoins) - keeper.SetSupply(ctx, initialSupply) + suite. + Require(). + NoError(keeper.MintCoins(ctx, authtypes.Minter, initCoins)) + supplyAfterInflation = keeper.GetSupply(ctx) - suite.Require().NoError(keeper.SetBalances(ctx, multiPermAcc.GetAddress(), initCoins)) + suite.Require().NoError(keeper.SendCoins(ctx, authtypes.NewModuleAddress(authtypes.Minter), multiPermAcc.GetAddress(), initCoins)) authKeeper.SetModuleAccount(ctx, multiPermAcc) err = keeper.BurnCoins(ctx, multiPermAcc.GetName(), initCoins) suite.Require().NoError(err) - suite.Require().Equal(sdk.Coins(nil), getCoinsByName(ctx, keeper, authKeeper, multiPermAcc.GetName())) - suite.Require().Equal(initialSupply.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal()) + suite.Require().Equal(sdk.NewCoins().String(), getCoinsByName(ctx, keeper, authKeeper, multiPermAcc.GetName()).String()) + suite.Require().Equal(supplyAfterInflation.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal()) } func (suite *IntegrationTestSuite) TestSendCoinsNewAccount() { @@ -274,7 +291,7 @@ func (suite *IntegrationTestSuite) TestSendCoinsNewAccount() { addr1 := sdk.AccAddress([]byte("addr1_______________")) acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) app.AccountKeeper.SetAccount(ctx, acc1) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, balances)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, balances)) acc1Balances := app.BankKeeper.GetAllBalances(ctx, addr1) suite.Require().Equal(balances, acc1Balances) @@ -300,7 +317,7 @@ func (suite *IntegrationTestSuite) TestInputOutputNewAccount() { addr1 := sdk.AccAddress([]byte("addr1_______________")) acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) app.AccountKeeper.SetAccount(ctx, acc1) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, balances)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, balances)) acc1Balances := app.BankKeeper.GetAllBalances(ctx, addr1) suite.Require().Equal(balances, acc1Balances) @@ -353,7 +370,7 @@ func (suite *IntegrationTestSuite) TestInputOutputCoins() { suite.Require().Error(app.BankKeeper.InputOutputCoins(ctx, inputs, []types.Output{})) suite.Require().Error(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, balances)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, balances)) insufficientInputs := []types.Input{ {Address: addr1.String(), Coins: sdk.NewCoins(newFooCoin(300), newBarCoin(100))}, @@ -381,19 +398,19 @@ func (suite *IntegrationTestSuite) TestSendCoins() { app, ctx := suite.app, suite.ctx balances := sdk.NewCoins(newFooCoin(100), newBarCoin(50)) - addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr1 := sdk.AccAddress("addr1_______________") acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) app.AccountKeeper.SetAccount(ctx, acc1) - addr2 := sdk.AccAddress([]byte("addr2_______________")) + addr2 := sdk.AccAddress("addr2_______________") acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) app.AccountKeeper.SetAccount(ctx, acc2) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr2, balances)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr2, balances)) sendAmt := sdk.NewCoins(newFooCoin(50), newBarCoin(25)) suite.Require().Error(app.BankKeeper.SendCoins(ctx, addr1, addr2, sendAmt)) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, balances)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, balances)) suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr1, addr2, sendAmt)) acc1Balances := app.BankKeeper.GetAllBalances(ctx, addr1) @@ -420,45 +437,17 @@ func (suite *IntegrationTestSuite) TestValidateBalance() { app.AccountKeeper.SetAccount(ctx, acc) balances := sdk.NewCoins(newFooCoin(100)) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, balances)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, balances)) suite.Require().NoError(app.BankKeeper.ValidateBalance(ctx, addr1)) bacc := authtypes.NewBaseAccountWithAddress(addr2) vacc := vesting.NewContinuousVestingAccount(bacc, balances.Add(balances...), now.Unix(), endTime.Unix()) app.AccountKeeper.SetAccount(ctx, vacc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr2, balances)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr2, balances)) suite.Require().Error(app.BankKeeper.ValidateBalance(ctx, addr2)) } -func (suite *IntegrationTestSuite) TestBalance() { - app, ctx := suite.app, suite.ctx - addr := sdk.AccAddress([]byte("addr1_______________")) - - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - app.AccountKeeper.SetAccount(ctx, acc) - - suite.Require().Equal(sdk.NewCoin(fooDenom, sdk.ZeroInt()), app.BankKeeper.GetBalance(ctx, addr, fooDenom)) - balances := sdk.NewCoins(newFooCoin(100)) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr, balances)) - - suite.Require().Equal(balances.AmountOf(fooDenom), app.BankKeeper.GetBalance(ctx, addr, fooDenom).Amount) - suite.Require().Equal(balances, app.BankKeeper.GetAllBalances(ctx, addr)) - - newFooBalance := newFooCoin(99) - suite.Require().NoError(app.BankKeeper.SetBalance(ctx, addr, newFooBalance)) - suite.Require().Equal(newFooBalance, app.BankKeeper.GetBalance(ctx, addr, fooDenom)) - - balances = sdk.NewCoins(newBarCoin(500)) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr, balances)) - suite.Require().Equal(sdk.NewCoin(fooDenom, sdk.ZeroInt()), app.BankKeeper.GetBalance(ctx, addr, fooDenom)) - suite.Require().Equal(balances.AmountOf(barDenom), app.BankKeeper.GetBalance(ctx, addr, barDenom).Amount) - suite.Require().Equal(balances, app.BankKeeper.GetAllBalances(ctx, addr)) - - invalidBalance := sdk.Coin{Denom: "fooDenom", Amount: sdk.NewInt(-50)} - suite.Require().Error(app.BankKeeper.SetBalance(ctx, addr, invalidBalance)) -} - func (suite *IntegrationTestSuite) TestSendEnabled() { app, ctx := suite.app, suite.ctx enabled := true @@ -512,7 +501,7 @@ func (suite *IntegrationTestSuite) TestHasBalance() { balances := sdk.NewCoins(newFooCoin(100)) suite.Require().False(app.BankKeeper.HasBalance(ctx, addr, newFooCoin(99))) - app.BankKeeper.SetBalances(ctx, addr, balances) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr, balances)) suite.Require().False(app.BankKeeper.HasBalance(ctx, addr, newFooCoin(101))) suite.Require().True(app.BankKeeper.HasBalance(ctx, addr, newFooCoin(100))) suite.Require().True(app.BankKeeper.HasBalance(ctx, addr, newFooCoin(1))) @@ -548,6 +537,7 @@ func (suite *IntegrationTestSuite) TestMsgSendEvents() { event1.Attributes, abci.EventAttribute{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins.String())}, ) + event2 := sdk.Event{ Type: sdk.EventTypeMessage, Attributes: []abci.EventAttribute{}, @@ -560,15 +550,16 @@ func (suite *IntegrationTestSuite) TestMsgSendEvents() { suite.Require().Equal(abci.Event(event1), events[0]) suite.Require().Equal(abci.Event(event2), events[1]) - app.BankKeeper.SetBalances(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50))) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr, sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)))) newCoins = sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)) suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr, addr2, newCoins)) + // events are shifted due to the funding account events events = ctx.EventManager().ABCIEvents() - suite.Require().Equal(4, len(events)) - suite.Require().Equal(abci.Event(event1), events[2]) - suite.Require().Equal(abci.Event(event2), events[3]) + suite.Require().Equal(12, len(events)) + suite.Require().Equal(abci.Event(event1), events[8]) + suite.Require().Equal(abci.Event(event2), events[9]) } func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { @@ -603,12 +594,11 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { suite.Require().Equal(0, len(events)) // Set addr's coins but not addr2's coins - app.BankKeeper.SetBalances(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50))) - + suite.Require().NoError(simapp.FundAccount(app, ctx, addr, sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)))) suite.Require().Error(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) events = ctx.EventManager().ABCIEvents() - suite.Require().Equal(1, len(events)) + suite.Require().Equal(8, len(events)) // 7 events because account funding causes extra minting + coin_spent + coin_recv events event1 := sdk.Event{ Type: sdk.EventTypeMessage, @@ -618,19 +608,19 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { event1.Attributes, abci.EventAttribute{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())}, ) - suite.Require().Equal(abci.Event(event1), events[0]) + suite.Require().Equal(abci.Event(event1), events[7]) // Set addr's coins and addr2's coins - app.BankKeeper.SetBalances(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50))) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr, sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)))) newCoins = sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)) - app.BankKeeper.SetBalances(ctx, addr2, sdk.NewCoins(sdk.NewInt64Coin(barDenom, 100))) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr2, sdk.NewCoins(sdk.NewInt64Coin(barDenom, 100)))) newCoins2 = sdk.NewCoins(sdk.NewInt64Coin(barDenom, 100)) suite.Require().NoError(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) events = ctx.EventManager().ABCIEvents() - suite.Require().Equal(5, len(events)) + suite.Require().Equal(28, len(events)) // 25 due to account funding + coin_spent + coin_recv events event2 := sdk.Event{ Type: sdk.EventTypeMessage, @@ -663,11 +653,11 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { event4.Attributes, abci.EventAttribute{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins2.String())}, ) - - suite.Require().Equal(abci.Event(event1), events[1]) - suite.Require().Equal(abci.Event(event2), events[2]) - suite.Require().Equal(abci.Event(event3), events[3]) - suite.Require().Equal(abci.Event(event4), events[4]) + // events are shifted due to the funding account events + suite.Require().Equal(abci.Event(event1), events[21]) + suite.Require().Equal(abci.Event(event2), events[23]) + suite.Require().Equal(abci.Event(event3), events[25]) + suite.Require().Equal(abci.Event(event4), events[27]) } func (suite *IntegrationTestSuite) TestSpendableCoins() { @@ -691,8 +681,8 @@ func (suite *IntegrationTestSuite) TestSpendableCoins() { app.AccountKeeper.SetAccount(ctx, macc) app.AccountKeeper.SetAccount(ctx, vacc) app.AccountKeeper.SetAccount(ctx, acc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, origCoins)) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr2, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr2, origCoins)) suite.Require().Equal(origCoins, app.BankKeeper.SpendableCoins(ctx, addr2)) @@ -717,14 +707,13 @@ func (suite *IntegrationTestSuite) TestVestingAccountSend() { vacc := vesting.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) app.AccountKeeper.SetAccount(ctx, vacc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, origCoins)) // require that no coins be sendable at the beginning of the vesting schedule suite.Require().Error(app.BankKeeper.SendCoins(ctx, addr1, addr2, sendCoins)) // receive some coins - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, origCoins.Add(sendCoins...))) - + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, sendCoins)) // require that all vested coins are spendable plus any received ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr1, addr2, sendCoins)) @@ -750,13 +739,13 @@ func (suite *IntegrationTestSuite) TestPeriodicVestingAccountSend() { vacc := vesting.NewPeriodicVestingAccount(bacc, origCoins, ctx.BlockHeader().Time.Unix(), periods) app.AccountKeeper.SetAccount(ctx, vacc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, origCoins)) // require that no coins be sendable at the beginning of the vesting schedule suite.Require().Error(app.BankKeeper.SendCoins(ctx, addr1, addr2, sendCoins)) // receive some coins - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, origCoins.Add(sendCoins...))) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, sendCoins)) // require that all vested coins are spendable plus any received ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) @@ -782,8 +771,8 @@ func (suite *IntegrationTestSuite) TestVestingAccountReceive() { app.AccountKeeper.SetAccount(ctx, vacc) app.AccountKeeper.SetAccount(ctx, acc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, origCoins)) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr2, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr2, origCoins)) // send some coins to the vesting account suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr2, addr1, sendCoins)) @@ -821,8 +810,8 @@ func (suite *IntegrationTestSuite) TestPeriodicVestingAccountReceive() { app.AccountKeeper.SetAccount(ctx, vacc) app.AccountKeeper.SetAccount(ctx, acc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, origCoins)) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr2, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr2, origCoins)) // send some coins to the vesting account suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr2, addr1, sendCoins)) @@ -858,8 +847,8 @@ func (suite *IntegrationTestSuite) TestDelegateCoins() { app.AccountKeeper.SetAccount(ctx, vacc) app.AccountKeeper.SetAccount(ctx, acc) app.AccountKeeper.SetAccount(ctx, macc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, origCoins)) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr2, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr2, origCoins)) ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) @@ -889,11 +878,8 @@ func (suite *IntegrationTestSuite) TestDelegateCoins_Invalid() { suite.Require().Error(app.BankKeeper.DelegateCoins(ctx, addr1, addrModule, invalidCoins)) app.AccountKeeper.SetAccount(ctx, macc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, origCoins)) - suite.Require().Error(app.BankKeeper.DelegateCoins(ctx, addr1, addrModule, delCoins)) app.AccountKeeper.SetAccount(ctx, acc) - suite.Require().Error(app.BankKeeper.DelegateCoins(ctx, addr1, addrModule, origCoins.Add(origCoins...))) } @@ -919,8 +905,8 @@ func (suite *IntegrationTestSuite) TestUndelegateCoins() { app.AccountKeeper.SetAccount(ctx, vacc) app.AccountKeeper.SetAccount(ctx, acc) app.AccountKeeper.SetAccount(ctx, macc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, origCoins)) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr2, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr2, origCoins)) ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) @@ -964,7 +950,7 @@ func (suite *IntegrationTestSuite) TestUndelegateCoins_Invalid() { suite.Require().Error(app.BankKeeper.UndelegateCoins(ctx, addrModule, addr1, delCoins)) app.AccountKeeper.SetAccount(ctx, macc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addr1, origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr1, origCoins)) suite.Require().Error(app.BankKeeper.UndelegateCoins(ctx, addrModule, addr1, delCoins)) app.AccountKeeper.SetAccount(ctx, acc) @@ -1016,8 +1002,107 @@ func (suite *IntegrationTestSuite) TestIterateAllDenomMetaData() { } } +func (suite *IntegrationTestSuite) TestBalanceTrackingEvents() { + // replace account keeper and bank keeper otherwise the account keeper won't be aware of the + // existence of the new module account because GetModuleAccount checks for the existence via + // permissions map and not via state... weird + maccPerms := simapp.GetMaccPerms() + maccPerms[multiPerm] = []string{authtypes.Burner, authtypes.Minter, authtypes.Staking} + + suite.app.AccountKeeper = authkeeper.NewAccountKeeper( + suite.app.AppCodec(), suite.app.GetKey(authtypes.StoreKey), suite.app.GetSubspace(authtypes.ModuleName), + authtypes.ProtoBaseAccount, maccPerms, + ) + + suite.app.BankKeeper = keeper.NewBaseKeeper(suite.app.AppCodec(), suite.app.GetKey(types.StoreKey), + suite.app.AccountKeeper, suite.app.GetSubspace(types.ModuleName), nil) + + // set account with multiple permissions + suite.app.AccountKeeper.SetModuleAccount(suite.ctx, multiPermAcc) + // mint coins + suite.Require().NoError( + suite.app.BankKeeper.MintCoins( + suite.ctx, + multiPermAcc.Name, + sdk.NewCoins(sdk.NewCoin("utxo", sdk.NewInt(100000)))), + ) + // send coins to address + addr1 := sdk.AccAddress("addr1_______________") + suite.Require().NoError( + suite.app.BankKeeper.SendCoinsFromModuleToAccount( + suite.ctx, + multiPermAcc.Name, + addr1, + sdk.NewCoins(sdk.NewCoin("utxo", sdk.NewInt(50000))), + ), + ) + + // burn coins from module account + suite.Require().NoError( + suite.app.BankKeeper.BurnCoins( + suite.ctx, + multiPermAcc.Name, + sdk.NewCoins(sdk.NewInt64Coin("utxo", 1000)), + ), + ) + + // process balances and supply from events + supply := sdk.NewCoins() + + balances := make(map[string]sdk.Coins) + + for _, e := range suite.ctx.EventManager().ABCIEvents() { + switch e.Type { + case types.EventTypeCoinBurn: + burnedCoins, err := sdk.ParseCoinsNormalized((string)(e.Attributes[1].Value)) + suite.Require().NoError(err) + supply = supply.Sub(burnedCoins) + + case types.EventTypeCoinMint: + mintedCoins, err := sdk.ParseCoinsNormalized((string)(e.Attributes[1].Value)) + suite.Require().NoError(err) + supply = supply.Add(mintedCoins...) + + case types.EventTypeCoinSpent: + coinsSpent, err := sdk.ParseCoinsNormalized((string)(e.Attributes[1].Value)) + suite.Require().NoError(err) + spender, err := sdk.AccAddressFromBech32((string)(e.Attributes[0].Value)) + suite.Require().NoError(err) + balances[spender.String()] = balances[spender.String()].Sub(coinsSpent) + + case types.EventTypeCoinReceived: + coinsRecv, err := sdk.ParseCoinsNormalized((string)(e.Attributes[1].Value)) + suite.Require().NoError(err) + receiver, err := sdk.AccAddressFromBech32((string)(e.Attributes[0].Value)) + suite.Require().NoError(err) + balances[receiver.String()] = balances[receiver.String()].Add(coinsRecv...) + } + } + + // check balance and supply tracking + savedSupply := suite.app.BankKeeper.GetSupply(suite.ctx) + utxoSupply := savedSupply.GetTotal().AmountOf("utxo") + suite.Require().Equal(utxoSupply, supply.AmountOf("utxo")) + // iterate accounts and check balances + suite.app.BankKeeper.IterateAllBalances(suite.ctx, func(address sdk.AccAddress, coin sdk.Coin) (stop bool) { + // if it's not utxo coin then skip + if coin.Denom != "utxo" { + return false + } + + balance, exists := balances[address.String()] + suite.Require().True(exists) + + expectedUtxo := sdk.NewCoin("utxo", balance.AmountOf(coin.Denom)) + suite.Require().Equal(expectedUtxo.String(), coin.String()) + return false + }) +} + func (suite *IntegrationTestSuite) getTestMetadata() []types.Metadata { return []types.Metadata{{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ {"uatom", uint32(0), []string{"microatom"}}, @@ -1028,6 +1113,8 @@ func (suite *IntegrationTestSuite) getTestMetadata() []types.Metadata { Display: "atom", }, { + Name: "Token", + Symbol: "TOKEN", Description: "The native staking token of the Token Hub.", DenomUnits: []*types.DenomUnit{ {"1token", uint32(5), []string{"decitoken"}}, diff --git a/x/bank/keeper/migrations.go b/x/bank/keeper/migrations.go index de6b61a9c..3a200fc27 100644 --- a/x/bank/keeper/migrations.go +++ b/x/bank/keeper/migrations.go @@ -5,16 +5,17 @@ import ( v042 "github.com/cosmos/cosmos-sdk/x/bank/legacy/v042" ) -// MigrationKeeper is an interface that the keeper implements for handling -// in-place store migrations. -type MigrationKeeper interface { - // Migrate1 migrates the store from version 1 to 2. - Migrate1(ctx sdk.Context) error +// Migrator is a struct for handling in-place store migrations. +type Migrator struct { + keeper BaseKeeper } -var _ MigrationKeeper = (*BaseKeeper)(nil) - -// Migrate1 implements MigrationKeeper.Migrate1 method. -func (keeper BaseKeeper) Migrate1(ctx sdk.Context) error { - return v042.MigrateStore(ctx, keeper.storeKey) +// NewMigrator returns a new Migrator. +func NewMigrator(keeper BaseKeeper) Migrator { + return Migrator{keeper: keeper} +} + +// Migrate1to2 migrates from version 1 to 2. +func (m Migrator) Migrate1to2(ctx sdk.Context) error { + return v042.MigrateStore(ctx, m.keeper.storeKey) } diff --git a/x/bank/keeper/querier_test.go b/x/bank/keeper/querier_test.go index 84ec3d095..ef1edc576 100644 --- a/x/bank/keeper/querier_test.go +++ b/x/bank/keeper/querier_test.go @@ -3,6 +3,10 @@ package keeper_test import ( "fmt" + "github.com/cosmos/cosmos-sdk/simapp" + + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/cosmos-sdk/testutil/testdata" @@ -39,7 +43,7 @@ func (suite *IntegrationTestSuite) TestQuerier_QueryBalance() { acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) app.AccountKeeper.SetAccount(ctx, acc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, acc.GetAddress(), origCoins)) + suite.Require().NoError(simapp.FundAccount(app, ctx, acc.GetAddress(), origCoins)) res, err = querier(ctx, []string{types.QueryBalance}, req) suite.Require().NoError(err) @@ -76,8 +80,7 @@ func (suite *IntegrationTestSuite) TestQuerier_QueryAllBalances() { acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) app.AccountKeeper.SetAccount(ctx, acc) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, acc.GetAddress(), origCoins)) - + suite.Require().NoError(simapp.FundAccount(app, ctx, acc.GetAddress(), origCoins)) res, err = querier(ctx, []string{types.QueryAllBalances}, req) suite.Require().NoError(err) suite.Require().NotNil(res) @@ -89,7 +92,9 @@ func (suite *IntegrationTestSuite) TestQuerier_QueryTotalSupply() { app, ctx := suite.app, suite.ctx legacyAmino := app.LegacyAmino() expectedTotalSupply := types.NewSupply(sdk.NewCoins(sdk.NewInt64Coin("test", 400000000))) - app.BankKeeper.SetSupply(ctx, expectedTotalSupply) + suite. + Require(). + NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, expectedTotalSupply.Total)) req := abci.RequestQuery{ Path: fmt.Sprintf("custom/%s/%s", types.ModuleName, types.QueryTotalSupply), @@ -118,7 +123,9 @@ func (suite *IntegrationTestSuite) TestQuerier_QueryTotalSupplyOf() { test1Supply := sdk.NewInt64Coin("test1", 4000000) test2Supply := sdk.NewInt64Coin("test2", 700000000) expectedTotalSupply := types.NewSupply(sdk.NewCoins(test1Supply, test2Supply)) - app.BankKeeper.SetSupply(ctx, expectedTotalSupply) + suite. + Require(). + NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, expectedTotalSupply.Total)) req := abci.RequestQuery{ Path: fmt.Sprintf("custom/%s/%s", types.ModuleName, types.QuerySupplyOf), diff --git a/x/bank/keeper/send.go b/x/bank/keeper/send.go index 694c43211..0093a3b80 100644 --- a/x/bank/keeper/send.go +++ b/x/bank/keeper/send.go @@ -17,12 +17,6 @@ type SendKeeper interface { InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error - SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error - AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error - - SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error - SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error - GetParams(ctx sdk.Context) types.Params SetParams(ctx sdk.Context, params types.Params) @@ -89,7 +83,7 @@ func (k BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input, return err } - err = k.SubtractCoins(ctx, inAddress, in.Coins) + err = k.subUnlockedCoins(ctx, inAddress, in.Coins) if err != nil { return err } @@ -107,7 +101,7 @@ func (k BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input, if err != nil { return err } - err = k.AddCoins(ctx, outAddress, out.Coins) + err = k.addCoins(ctx, outAddress, out.Coins) if err != nil { return err } @@ -150,12 +144,12 @@ func (k BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAd ), }) - err := k.SubtractCoins(ctx, fromAddr, amt) + err := k.subUnlockedCoins(ctx, fromAddr, amt) if err != nil { return err } - err = k.AddCoins(ctx, toAddr, amt) + err = k.addCoins(ctx, toAddr, amt) if err != nil { return err } @@ -173,9 +167,10 @@ func (k BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAd return nil } -// SubtractCoins removes amt coins the account by the given address. An error is +// subUnlockedCoins removes the unlocked amt coins of the given account. An error is // returned if the resulting balance is negative or the initial amount is invalid. -func (k BaseSendKeeper) SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error { +// A coin_spent event is emitted after. +func (k BaseSendKeeper) subUnlockedCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error { if !amt.IsValid() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) } @@ -194,19 +189,22 @@ func (k BaseSendKeeper) SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt newBalance := balance.Sub(coin) - err := k.SetBalance(ctx, addr, newBalance) + err := k.setBalance(ctx, addr, newBalance) if err != nil { return err } } + // emit coin spent event + ctx.EventManager().EmitEvent( + types.NewCoinSpentEvent(addr, amt), + ) return nil } -// AddCoins adds amt to the account balance given by the provided address. An -// error is returned if the initial amount is invalid or if any resulting new -// balance is negative. -func (k BaseSendKeeper) AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error { +// addCoins increase the addr balance by the given amt. Fails if the provided amt is invalid. +// It emits a coin received event. +func (k BaseSendKeeper) addCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error { if !amt.IsValid() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) } @@ -215,17 +213,22 @@ func (k BaseSendKeeper) AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.C balance := k.GetBalance(ctx, addr, coin.Denom) newBalance := balance.Add(coin) - err := k.SetBalance(ctx, addr, newBalance) + err := k.setBalance(ctx, addr, newBalance) if err != nil { return err } } + // emit coin received event + ctx.EventManager().EmitEvent( + types.NewCoinReceivedEvent(addr, amt), + ) + return nil } -// ClearBalances removes all balances for a given account by address. -func (k BaseSendKeeper) ClearBalances(ctx sdk.Context, addr sdk.AccAddress) { +// clearBalances removes all balances for a given account by address. +func (k BaseSendKeeper) clearBalances(ctx sdk.Context, addr sdk.AccAddress) { keys := [][]byte{} k.IterateAccountBalances(ctx, addr, func(balance sdk.Coin) bool { keys = append(keys, []byte(balance.Denom)) @@ -239,14 +242,14 @@ func (k BaseSendKeeper) ClearBalances(ctx sdk.Context, addr sdk.AccAddress) { } } -// SetBalances sets the balance (multiple coins) for an account by address. It will +// setBalances sets the balance (multiple coins) for an account by address. It will // clear out all balances prior to setting the new coins as to set existing balances // to zero if they don't exist in amt. An error is returned upon failure. -func (k BaseSendKeeper) SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error { - k.ClearBalances(ctx, addr) +func (k BaseSendKeeper) setBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error { + k.clearBalances(ctx, addr) for _, balance := range balances { - err := k.SetBalance(ctx, addr, balance) + err := k.setBalance(ctx, addr, balance) if err != nil { return err } @@ -255,8 +258,8 @@ func (k BaseSendKeeper) SetBalances(ctx sdk.Context, addr sdk.AccAddress, balanc return nil } -// SetBalance sets the coin balance for an account by address. -func (k BaseSendKeeper) SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error { +// setBalance sets the coin balance for an account by address. +func (k BaseSendKeeper) setBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error { if !balance.IsValid() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, balance.String()) } diff --git a/x/bank/keeper/view.go b/x/bank/keeper/view.go index 1b50fb81d..7271bba05 100644 --- a/x/bank/keeper/view.go +++ b/x/bank/keeper/view.go @@ -171,15 +171,23 @@ func (k BaseViewKeeper) LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Co // by address. If the account has no spendable coins, an empty Coins slice is // returned. func (k BaseViewKeeper) SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins { - balances := k.GetAllBalances(ctx, addr) + spendable, _ := k.spendableCoins(ctx, addr) + return spendable +} + +// spendableCoins returns the coins the given address can spend alongside the total amount of coins it holds. +// It exists for gas efficiency, in order to avoid to have to get balance multiple times. +func (k BaseViewKeeper) spendableCoins(ctx sdk.Context, addr sdk.AccAddress) (spendable, total sdk.Coins) { + total = k.GetAllBalances(ctx, addr) locked := k.LockedCoins(ctx, addr) - spendable, hasNeg := balances.SafeSub(locked) + spendable, hasNeg := total.SafeSub(locked) if hasNeg { - return sdk.NewCoins() + spendable = sdk.NewCoins() + return } - return spendable + return } // ValidateBalance validates all balances for a given account address returning diff --git a/x/bank/legacy/v036/types.go b/x/bank/legacy/v036/types.go index 1de8ffb9b..ada368404 100644 --- a/x/bank/legacy/v036/types.go +++ b/x/bank/legacy/v036/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v036 import ( diff --git a/x/bank/legacy/v038/types.go b/x/bank/legacy/v038/types.go index e67640bf2..eba8d3518 100644 --- a/x/bank/legacy/v038/types.go +++ b/x/bank/legacy/v038/types.go @@ -1,7 +1,6 @@ package v038 // DONTCOVER -// nolint const ( ModuleName = "bank" diff --git a/x/bank/legacy/v040/keys.go b/x/bank/legacy/v040/keys.go index bd1035599..043c208b6 100644 --- a/x/bank/legacy/v040/keys.go +++ b/x/bank/legacy/v040/keys.go @@ -1,3 +1,5 @@ +// Package v040 is copy-pasted from: +// https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/bank/types/key.go package v040 import ( diff --git a/x/bank/legacy/v042/keys.go b/x/bank/legacy/v042/keys.go deleted file mode 100644 index 8d718b1c5..000000000 --- a/x/bank/legacy/v042/keys.go +++ /dev/null @@ -1,50 +0,0 @@ -package v042 - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/address" -) - -const ( - // ModuleName defines the module name - ModuleName = "bank" - - // StoreKey defines the primary module store key - StoreKey = ModuleName - - // RouterKey defines the module's message routing key - RouterKey = ModuleName - - // QuerierRoute defines the module's query routing key - QuerierRoute = ModuleName -) - -// KVStore keys -var ( - // BalancesPrefix is the for the account balances store. We use a byte - // (instead of say `[]]byte("balances")` to save some disk space). - BalancesPrefix = []byte{0x02} - SupplyKey = []byte{0x00} - DenomMetadataPrefix = []byte{0x1} -) - -// DenomMetadataKey returns the denomination metadata key. -func DenomMetadataKey(denom string) []byte { - d := []byte(denom) - return append(DenomMetadataPrefix, d...) -} - -// AddressFromBalancesStore returns an account address from a balances prefix -// store. The key must not contain the perfix BalancesPrefix as the prefix store -// iterator discards the actual prefix. -func AddressFromBalancesStore(key []byte) sdk.AccAddress { - addrLen := key[0] - addr := key[1 : addrLen+1] - - return sdk.AccAddress(addr) -} - -// CreateAccountBalancesPrefix creates the prefix for an account's balances. -func CreateAccountBalancesPrefix(addr []byte) []byte { - return append(BalancesPrefix, address.MustLengthPrefix(addr)...) -} diff --git a/x/bank/legacy/v042/store.go b/x/bank/legacy/v042/store.go index 9295d8d2f..894305b6c 100644 --- a/x/bank/legacy/v042/store.go +++ b/x/bank/legacy/v042/store.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" v040bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v040" + "github.com/cosmos/cosmos-sdk/x/bank/types" ) // MigrateStore performs in-place store migrations from v0.40 to v0.42. The @@ -27,7 +28,7 @@ func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error { for ; oldStoreIter.Valid(); oldStoreIter.Next() { addr := v040bank.AddressFromBalancesStore(oldStoreIter.Key()) denom := oldStoreIter.Key()[v040auth.AddrLen:] - newStoreKey := append(CreateAccountBalancesPrefix(addr), denom...) + newStoreKey := append(types.CreateAccountBalancesPrefix(addr), denom...) // Set new key on store. Values don't change. store.Set(newStoreKey, oldStoreIter.Value()) diff --git a/x/bank/legacy/v042/store_test.go b/x/bank/legacy/v042/store_test.go index e1b6a7987..7481232f5 100644 --- a/x/bank/legacy/v042/store_test.go +++ b/x/bank/legacy/v042/store_test.go @@ -10,6 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" v040bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v040" v042bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v042" + "github.com/cosmos/cosmos-sdk/x/bank/types" ) func TestStoreMigration(t *testing.T) { @@ -27,7 +28,7 @@ func TestStoreMigration(t *testing.T) { err := v042bank.MigrateStore(ctx, bankKey) require.NoError(t, err) - newKey := append(v042bank.CreateAccountBalancesPrefix(addr), denom...) + newKey := append(types.CreateAccountBalancesPrefix(addr), denom...) // -7 because we replaced "balances" with 0x02, // +1 because we added length-prefix to address. require.Equal(t, len(oldKey)-7+1, len(newKey)) diff --git a/x/bank/module.go b/x/bank/module.go index 77bff6b73..3e728e603 100644 --- a/x/bank/module.go +++ b/x/bank/module.go @@ -86,8 +86,6 @@ func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) types.RegisterInterfaces(registry) } -//____________________________________________________________________________ - // AppModule implements an application module for the bank module. type AppModule struct { AppModuleBasic @@ -100,9 +98,9 @@ type AppModule struct { func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) types.RegisterQueryServer(cfg.QueryServer(), am.keeper) - cfg.RegisterMigration(types.ModuleName, 0, func(ctx sdk.Context) error { - return am.keeper.(keeper.MigrationKeeper).Migrate1(ctx) - }) + + m := keeper.NewMigrator(am.keeper.(keeper.BaseKeeper)) + cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2) } // NewAppModule creates a new AppModule object @@ -166,8 +164,6 @@ func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.Validato return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the bank module. diff --git a/x/bank/simulation/operations.go b/x/bank/simulation/operations.go index 40be2feeb..78feff814 100644 --- a/x/bank/simulation/operations.go +++ b/x/bank/simulation/operations.go @@ -81,7 +81,6 @@ func SimulateMsgSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Operatio } // sendMsgSend sends a transaction with a MsgSend from a provided random account. -// nolint: interfacer func sendMsgSend( r *rand.Rand, app *baseapp.BaseApp, bk keeper.Keeper, ak types.AccountKeeper, msg *types.MsgSend, ctx sdk.Context, chainID string, privkeys []cryptotypes.PrivKey, @@ -223,7 +222,6 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Ope // sendMsgMultiSend sends a transaction with a MsgMultiSend from a provided random // account. -// nolint: interfacer func sendMsgMultiSend( r *rand.Rand, app *baseapp.BaseApp, bk keeper.Keeper, ak types.AccountKeeper, msg *types.MsgMultiSend, ctx sdk.Context, chainID string, privkeys []cryptotypes.PrivKey, @@ -289,7 +287,6 @@ func sendMsgMultiSend( // randomSendFields returns the sender and recipient simulation accounts as well // as the transferred amount. -// nolint: interfacer func randomSendFields( r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, bk keeper.Keeper, ak types.AccountKeeper, ) (simtypes.Account, simtypes.Account, sdk.Coins, bool) { diff --git a/x/bank/simulation/operations_test.go b/x/bank/simulation/operations_test.go index 144dc5dd3..efe6de624 100644 --- a/x/bank/simulation/operations_test.go +++ b/x/bank/simulation/operations_test.go @@ -131,8 +131,7 @@ func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Ac for _, account := range accounts { acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, account.Address) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - err := suite.app.BankKeeper.SetBalances(suite.ctx, account.Address, initCoins) - suite.Require().NoError(err) + suite.Require().NoError(simapp.FundAccount(suite.app, suite.ctx, account.Address, initCoins)) } return accounts diff --git a/x/bank/spec/02_keepers.md b/x/bank/spec/02_keepers.md index 0032961b1..e7e3caba5 100644 --- a/x/bank/spec/02_keepers.md +++ b/x/bank/spec/02_keepers.md @@ -84,12 +84,6 @@ type SendKeeper interface { InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error - SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error - AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error - - SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error - SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error - GetParams(ctx sdk.Context) types.Params SetParams(ctx sdk.Context, params types.Params) diff --git a/x/bank/spec/04_events.md b/x/bank/spec/04_events.md index 55c93b805..71c068b08 100644 --- a/x/bank/spec/04_events.md +++ b/x/bank/spec/04_events.md @@ -27,3 +27,123 @@ The bank module emits the following events: | message | module | bank | | message | action | multisend | | message | sender | {senderAddress} | + +## Keeper events + +In addition to handlers events, the bank keeper will produce events when the following methods are called (or any method which ends up calling them) + +### MintCoins + +```json +{ + "type": "coinbase", + "attributes": [ + { + "key": "minter", + "value": "{{sdk.AccAddress of the module minting coins}}", + "index": true + }, + { + "key": "amount", + "value": "{{sdk.Coins being minted}}", + "index": true + } + ] +} +``` + +```json +{ + "type": "coin_received", + "attributes": [ + { + "key": "receiver", + "value": "{{sdk.AccAddress of the module minting coins}}", + "index": true + }, + { + "key": "amount", + "value": "{{sdk.Coins being received}}", + "index": true + } + ] +} +``` + +### BurnCoins + +```json +{ + "type": "burn", + "attributes": [ + { + "key": "burner", + "value": "{{sdk.AccAddress of the module burning coins}}", + "index": true + }, + { + "key": "amount", + "value": "{{sdk.Coins being burned}}", + "index": true + } + ] +} +``` + +```json +{ + "type": "coin_spent", + "attributes": [ + { + "key": "spender", + "value": "{{sdk.AccAddress of the module burning coins}}", + "index": true + }, + { + "key": "amount", + "value": "{{sdk.Coins being burned}}", + "index": true + } + ] +} +``` + +### addCoins + +```json +{ + "type": "coin_received", + "attributes": [ + { + "key": "receiver", + "value": "{{sdk.AccAddress of the address beneficiary of the coins}}", + "index": true + }, + { + "key": "amount", + "value": "{{sdk.Coins being received}}", + "index": true + } + ] +} +``` + +### subUnlockedCoins/DelegateCoins + +```json +{ + "type": "coin_spent", + "attributes": [ + { + "key": "spender", + "value": "{{sdk.AccAddress of the address which is spending coins}}", + "index": true + }, + { + "key": "amount", + "value": "{{sdk.Coins being spent}}", + "index": true + } + ] +} +``` \ No newline at end of file diff --git a/x/bank/types/authz.pb.go b/x/bank/types/authz.pb.go new file mode 100644 index 000000000..92444cb98 --- /dev/null +++ b/x/bank/types/authz.pb.go @@ -0,0 +1,338 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/bank/v1beta1/authz.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "github.com/regen-network/cosmos-proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// SendAuthorization allows the grantee to spend up to spend_limit coins from +// the granter's account. +type SendAuthorization struct { + SpendLimit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=spend_limit,json=spendLimit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"spend_limit"` +} + +func (m *SendAuthorization) Reset() { *m = SendAuthorization{} } +func (m *SendAuthorization) String() string { return proto.CompactTextString(m) } +func (*SendAuthorization) ProtoMessage() {} +func (*SendAuthorization) Descriptor() ([]byte, []int) { + return fileDescriptor_a4d2a37888ea779f, []int{0} +} +func (m *SendAuthorization) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SendAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SendAuthorization.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SendAuthorization) XXX_Merge(src proto.Message) { + xxx_messageInfo_SendAuthorization.Merge(m, src) +} +func (m *SendAuthorization) XXX_Size() int { + return m.Size() +} +func (m *SendAuthorization) XXX_DiscardUnknown() { + xxx_messageInfo_SendAuthorization.DiscardUnknown(m) +} + +var xxx_messageInfo_SendAuthorization proto.InternalMessageInfo + +func (m *SendAuthorization) GetSpendLimit() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.SpendLimit + } + return nil +} + +func init() { + proto.RegisterType((*SendAuthorization)(nil), "cosmos.bank.v1beta1.SendAuthorization") +} + +func init() { proto.RegisterFile("cosmos/bank/v1beta1/authz.proto", fileDescriptor_a4d2a37888ea779f) } + +var fileDescriptor_a4d2a37888ea779f = []byte{ + // 257 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x4a, 0xcc, 0xcb, 0xd6, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, + 0x4f, 0x2c, 0x2d, 0xc9, 0xa8, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, 0x28, 0xd0, + 0x03, 0x29, 0xd0, 0x83, 0x2a, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xeb, 0x83, 0x58, + 0x10, 0xa5, 0x52, 0x92, 0x10, 0xa5, 0xf1, 0x10, 0x09, 0xa8, 0x3e, 0x88, 0x94, 0x1c, 0xdc, 0x9a, + 0xe2, 0x54, 0xb8, 0x35, 0xc9, 0xf9, 0x99, 0x79, 0x10, 0x79, 0xa5, 0x29, 0x8c, 0x5c, 0x82, 0xc1, + 0xa9, 0x79, 0x29, 0x8e, 0xa5, 0x25, 0x19, 0xf9, 0x45, 0x99, 0x55, 0x89, 0x25, 0x99, 0xf9, 0x79, + 0x42, 0x39, 0x5c, 0xdc, 0xc5, 0x05, 0xa9, 0x79, 0x29, 0xf1, 0x39, 0x99, 0xb9, 0x99, 0x25, 0x12, + 0x8c, 0x0a, 0xcc, 0x1a, 0xdc, 0x46, 0x92, 0x7a, 0x70, 0x17, 0x15, 0xa7, 0xc2, 0x5c, 0xa4, 0xe7, + 0x9c, 0x9f, 0x99, 0xe7, 0x64, 0x70, 0xe2, 0x9e, 0x3c, 0xc3, 0xaa, 0xfb, 0xf2, 0x1a, 0xe9, 0x99, + 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0x50, 0x67, 0x40, 0x29, 0xdd, 0xe2, 0x94, 0x6c, + 0xfd, 0x92, 0xca, 0x82, 0xd4, 0x62, 0xb0, 0x86, 0xe2, 0x20, 0x2e, 0xb0, 0xf9, 0x3e, 0x20, 0xe3, + 0xad, 0x04, 0x2f, 0x6d, 0xd1, 0xe5, 0x45, 0x71, 0x80, 0x93, 0xf3, 0x89, 0x47, 0x72, 0x8c, 0x17, + 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, + 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x69, 0xe2, 0xb5, 0xa2, 0x02, 0x12, 0x9e, 0x60, 0x9b, 0x92, 0xd8, + 0xc0, 0x5e, 0x34, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x3b, 0xce, 0xe1, 0x4c, 0x6b, 0x01, 0x00, + 0x00, +} + +func (m *SendAuthorization) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SendAuthorization) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SendAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.SpendLimit) > 0 { + for iNdEx := len(m.SpendLimit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SpendLimit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthz(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintAuthz(dAtA []byte, offset int, v uint64) int { + offset -= sovAuthz(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *SendAuthorization) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.SpendLimit) > 0 { + for _, e := range m.SpendLimit { + l = e.Size() + n += 1 + l + sovAuthz(uint64(l)) + } + } + return n +} + +func sovAuthz(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAuthz(x uint64) (n int) { + return sovAuthz(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *SendAuthorization) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SendAuthorization: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SendAuthorization: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SpendLimit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SpendLimit = append(m.SpendLimit, types.Coin{}) + if err := m.SpendLimit[len(m.SpendLimit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuthz(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthz + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAuthz(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAuthz + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAuthz + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAuthz + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAuthz = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAuthz = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAuthz = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/bank/types/bank.pb.go b/x/bank/types/bank.pb.go index ef1c52c1c..645e0aef5 100644 --- a/x/bank/types/bank.pb.go +++ b/x/bank/types/bank.pb.go @@ -327,6 +327,11 @@ type Metadata struct { // display indicates the suggested denom that should be // displayed in clients. Display string `protobuf:"bytes,4,opt,name=display,proto3" json:"display,omitempty"` + // name defines the name of the token (eg: Cosmos Atom) + Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"` + // symbol is the token symbol usually shown on exchanges (eg: ATOM). This can + // be the same as the display. + Symbol string `protobuf:"bytes,6,opt,name=symbol,proto3" json:"symbol,omitempty"` } func (m *Metadata) Reset() { *m = Metadata{} } @@ -390,6 +395,20 @@ func (m *Metadata) GetDisplay() string { return "" } +func (m *Metadata) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Metadata) GetSymbol() string { + if m != nil { + return m.Symbol + } + return "" +} + func init() { proto.RegisterType((*Params)(nil), "cosmos.bank.v1beta1.Params") proto.RegisterType((*SendEnabled)(nil), "cosmos.bank.v1beta1.SendEnabled") @@ -403,43 +422,44 @@ func init() { func init() { proto.RegisterFile("cosmos/bank/v1beta1/bank.proto", fileDescriptor_dd052eee12edf988) } var fileDescriptor_dd052eee12edf988 = []byte{ - // 566 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xbf, 0x6f, 0x13, 0x31, - 0x14, 0x3e, 0x37, 0x4d, 0x48, 0x1d, 0x58, 0x4c, 0x86, 0x6b, 0x24, 0xee, 0x8e, 0x93, 0x90, 0x52, - 0x44, 0x2f, 0x14, 0xc4, 0x92, 0x05, 0x29, 0xa5, 0x42, 0x1d, 0x10, 0xe8, 0x2a, 0x84, 0x04, 0x43, - 0xe4, 0xc4, 0x6e, 0xb0, 0x7a, 0x67, 0x9f, 0x62, 0x1f, 0x6a, 0xfe, 0x03, 0x26, 0x60, 0x44, 0x62, - 0xe9, 0xcc, 0x88, 0xf8, 0x23, 0x3a, 0x56, 0xb0, 0x30, 0x05, 0x94, 0x2c, 0xcc, 0xfd, 0x0b, 0x90, - 0xed, 0xcb, 0x8f, 0x4a, 0x01, 0x31, 0x30, 0x30, 0x9d, 0x9f, 0xdf, 0xf7, 0xbe, 0xf7, 0xe9, 0x7b, - 0xcf, 0x07, 0xbd, 0xbe, 0x90, 0xa9, 0x90, 0xad, 0x1e, 0xe6, 0x47, 0xad, 0x57, 0x3b, 0x3d, 0xaa, - 0xf0, 0x8e, 0x09, 0xa2, 0x6c, 0x28, 0x94, 0x40, 0x57, 0x6d, 0x3e, 0x32, 0x57, 0x45, 0xbe, 0x51, - 0x1f, 0x88, 0x81, 0x30, 0xf9, 0x96, 0x3e, 0x59, 0x68, 0x63, 0xd3, 0x42, 0xbb, 0x36, 0x51, 0xd4, - 0xd9, 0xd4, 0xa2, 0x8b, 0xa4, 0xf3, 0x2e, 0x7d, 0xc1, 0xb8, 0xcd, 0x87, 0x5f, 0x01, 0xac, 0x3c, - 0xc1, 0x43, 0x9c, 0x4a, 0x74, 0x08, 0x2f, 0x4b, 0xca, 0x49, 0x97, 0x72, 0xdc, 0x4b, 0x28, 0x71, - 0x41, 0x50, 0x6a, 0xd6, 0xee, 0x04, 0xd1, 0x0a, 0x1d, 0xd1, 0x01, 0xe5, 0x64, 0xcf, 0xe2, 0x3a, - 0xd7, 0xcf, 0xc7, 0xfe, 0xb5, 0x11, 0x4e, 0x93, 0x76, 0xb8, 0x5c, 0x7f, 0x4b, 0xa4, 0x4c, 0xd1, - 0x34, 0x53, 0xa3, 0x30, 0xae, 0xc9, 0x05, 0x1e, 0xbd, 0x80, 0x75, 0x42, 0x0f, 0x71, 0x9e, 0xa8, - 0xee, 0x85, 0x7e, 0x6b, 0x01, 0x68, 0x56, 0x3b, 0x5b, 0xe7, 0x63, 0xff, 0x86, 0x65, 0x5b, 0x85, - 0x5a, 0x66, 0x45, 0x05, 0x60, 0x49, 0x4c, 0x7b, 0xfd, 0xfd, 0x89, 0xef, 0x84, 0x0f, 0x61, 0x6d, - 0xe9, 0x12, 0xd5, 0x61, 0x99, 0x50, 0x2e, 0x52, 0x17, 0x04, 0xa0, 0xb9, 0x11, 0xdb, 0x00, 0xb9, - 0xf0, 0xd2, 0x85, 0xd6, 0xf1, 0x2c, 0x6c, 0x57, 0x35, 0xc9, 0xcf, 0x13, 0x1f, 0x84, 0x6f, 0x00, - 0x2c, 0xef, 0xf3, 0x2c, 0x57, 0x1a, 0x8d, 0x09, 0x19, 0x52, 0x29, 0x0b, 0x96, 0x59, 0x88, 0x30, - 0x2c, 0x6b, 0x43, 0xa5, 0xbb, 0x66, 0x0c, 0xdb, 0x5c, 0x18, 0x26, 0xe9, 0xdc, 0xb0, 0x5d, 0xc1, - 0x78, 0xe7, 0xf6, 0xe9, 0xd8, 0x77, 0x3e, 0x7e, 0xf7, 0x9b, 0x03, 0xa6, 0x5e, 0xe6, 0xbd, 0xa8, - 0x2f, 0xd2, 0x62, 0x5a, 0xc5, 0x67, 0x5b, 0x92, 0xa3, 0x96, 0x1a, 0x65, 0x54, 0x9a, 0x02, 0x19, - 0x5b, 0xe6, 0x76, 0xf5, 0xb5, 0x15, 0xe4, 0x84, 0x6f, 0x01, 0xac, 0x3c, 0xce, 0xd5, 0x7f, 0xa4, - 0xe8, 0x13, 0x80, 0x95, 0x83, 0x3c, 0xcb, 0x92, 0x91, 0xee, 0xab, 0x84, 0xc2, 0x49, 0xb1, 0x3a, - 0xff, 0xb6, 0xaf, 0x61, 0x6e, 0xef, 0xe9, 0xbe, 0xb3, 0xf1, 0x7c, 0xf9, 0xbc, 0x7d, 0xef, 0xe6, - 0x1f, 0x19, 0x8e, 0xed, 0xf3, 0xa2, 0xc7, 0x99, 0x18, 0x2a, 0x4a, 0x22, 0x2b, 0x74, 0x3f, 0x7c, - 0x06, 0x37, 0x1e, 0xe8, 0x25, 0x78, 0xca, 0x99, 0xfa, 0xcd, 0x7a, 0x34, 0x60, 0x55, 0x97, 0x71, - 0xca, 0x95, 0xd9, 0x8f, 0x2b, 0xf1, 0x3c, 0x36, 0xd6, 0x27, 0x0c, 0x4b, 0x2a, 0xdd, 0x52, 0x50, - 0x32, 0xd6, 0xdb, 0x30, 0xfc, 0x00, 0x60, 0xf5, 0x11, 0x55, 0x98, 0x60, 0x85, 0x51, 0x00, 0x6b, - 0x84, 0xca, 0xfe, 0x90, 0x65, 0x8a, 0x09, 0x5e, 0xd0, 0x2f, 0x5f, 0xa1, 0xfb, 0x1a, 0xc1, 0x45, - 0xda, 0xcd, 0x39, 0x53, 0xb3, 0x79, 0x79, 0x2b, 0x9f, 0xdc, 0x5c, 0x6f, 0x0c, 0xc9, 0xec, 0x28, - 0x11, 0x82, 0xeb, 0xda, 0x5d, 0xb7, 0x64, 0xb8, 0xcd, 0x59, 0xab, 0x23, 0x4c, 0x66, 0x09, 0x1e, - 0xb9, 0xeb, 0x76, 0x31, 0x8a, 0xb0, 0xb3, 0x7b, 0x3a, 0xf1, 0xc0, 0xd9, 0xc4, 0x03, 0x3f, 0x26, - 0x1e, 0x78, 0x37, 0xf5, 0x9c, 0xb3, 0xa9, 0xe7, 0x7c, 0x9b, 0x7a, 0xce, 0xf3, 0xad, 0xbf, 0xb1, - 0xd1, 0xcc, 0xa3, 0x57, 0x31, 0x7f, 0x8e, 0xbb, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0xe2, 0x72, - 0xf6, 0xc6, 0xc1, 0x04, 0x00, 0x00, + // 588 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xbf, 0x6f, 0xd3, 0x4e, + 0x14, 0xf7, 0x35, 0x8d, 0xbf, 0xe9, 0xe5, 0xcb, 0x72, 0x54, 0xc8, 0xad, 0x84, 0x6d, 0x2c, 0x21, + 0xa5, 0x88, 0x3a, 0x14, 0xc4, 0x92, 0x05, 0x29, 0xa5, 0x42, 0x1d, 0x10, 0xc8, 0x15, 0x42, 0x82, + 0x21, 0x3a, 0xe7, 0xae, 0xc1, 0xaa, 0x7d, 0x67, 0xe5, 0xce, 0xa8, 0xfe, 0x0f, 0x98, 0x80, 0x91, + 0xb1, 0x33, 0x23, 0xe2, 0x7f, 0xa0, 0x63, 0x05, 0x0b, 0x53, 0x40, 0xc9, 0xc2, 0xdc, 0xbf, 0x00, + 0xdd, 0x9d, 0xf3, 0xa3, 0x52, 0x40, 0x0c, 0x0c, 0x4c, 0xb9, 0xcf, 0x7b, 0x9f, 0xf7, 0x79, 0x4f, + 0x9f, 0xf7, 0x1c, 0xe8, 0xf6, 0xb9, 0xc8, 0xb8, 0x68, 0xc7, 0x98, 0x1d, 0xb5, 0x5f, 0xee, 0xc4, + 0x54, 0xe2, 0x1d, 0x0d, 0xc2, 0x7c, 0xc8, 0x25, 0x47, 0x97, 0x4d, 0x3e, 0xd4, 0xa1, 0x2a, 0xbf, + 0xb9, 0x3e, 0xe0, 0x03, 0xae, 0xf3, 0x6d, 0xf5, 0x32, 0xd4, 0xcd, 0x0d, 0x43, 0xed, 0x99, 0x44, + 0x55, 0x67, 0x52, 0xf3, 0x2e, 0x82, 0xce, 0xba, 0xf4, 0x79, 0xc2, 0x4c, 0x3e, 0xf8, 0x02, 0xa0, + 0xfd, 0x18, 0x0f, 0x71, 0x26, 0xd0, 0x21, 0xfc, 0x5f, 0x50, 0x46, 0x7a, 0x94, 0xe1, 0x38, 0xa5, + 0xc4, 0x01, 0x7e, 0xad, 0xd5, 0xbc, 0xed, 0x87, 0x4b, 0xe6, 0x08, 0x0f, 0x28, 0x23, 0x7b, 0x86, + 0xd7, 0xbd, 0x76, 0x3e, 0xf2, 0xae, 0x96, 0x38, 0x4b, 0x3b, 0xc1, 0x62, 0xfd, 0x4d, 0x9e, 0x25, + 0x92, 0x66, 0xb9, 0x2c, 0x83, 0xa8, 0x29, 0xe6, 0x7c, 0xf4, 0x1c, 0xae, 0x13, 0x7a, 0x88, 0x8b, + 0x54, 0xf6, 0x2e, 0xf4, 0x5b, 0xf1, 0x41, 0xab, 0xd1, 0xdd, 0x3a, 0x1f, 0x79, 0xd7, 0x8d, 0xda, + 0x32, 0xd6, 0xa2, 0x2a, 0xaa, 0x08, 0x0b, 0xc3, 0x74, 0x56, 0xdf, 0x9d, 0x78, 0x56, 0xf0, 0x00, + 0x36, 0x17, 0x82, 0x68, 0x1d, 0xd6, 0x09, 0x65, 0x3c, 0x73, 0x80, 0x0f, 0x5a, 0x6b, 0x91, 0x01, + 0xc8, 0x81, 0xff, 0x5d, 0x68, 0x1d, 0x4d, 0x61, 0xa7, 0xa1, 0x44, 0x7e, 0x9c, 0x78, 0x20, 0x78, + 0x0d, 0x60, 0x7d, 0x9f, 0xe5, 0x85, 0x54, 0x6c, 0x4c, 0xc8, 0x90, 0x0a, 0x51, 0xa9, 0x4c, 0x21, + 0xc2, 0xb0, 0xae, 0x0c, 0x15, 0xce, 0x8a, 0x36, 0x6c, 0x63, 0x6e, 0x98, 0xa0, 0x33, 0xc3, 0x76, + 0x79, 0xc2, 0xba, 0xb7, 0x4e, 0x47, 0x9e, 0xf5, 0xfe, 0x9b, 0xd7, 0x1a, 0x24, 0xf2, 0x45, 0x11, + 0x87, 0x7d, 0x9e, 0x55, 0xdb, 0xaa, 0x7e, 0xb6, 0x05, 0x39, 0x6a, 0xcb, 0x32, 0xa7, 0x42, 0x17, + 0x88, 0xc8, 0x28, 0x77, 0x1a, 0xaf, 0xcc, 0x40, 0x56, 0xf0, 0x06, 0x40, 0xfb, 0x51, 0x21, 0xff, + 0xa1, 0x89, 0x3e, 0x00, 0x68, 0x1f, 0x14, 0x79, 0x9e, 0x96, 0xaa, 0xaf, 0xe4, 0x12, 0xa7, 0xd5, + 0xe9, 0xfc, 0xdd, 0xbe, 0x5a, 0xb9, 0xb3, 0xa7, 0xfa, 0x4e, 0xd7, 0xf3, 0xf9, 0xe3, 0xf6, 0xdd, + 0x1b, 0xbf, 0x55, 0x38, 0x36, 0x9f, 0x17, 0x3d, 0xce, 0xf9, 0x50, 0x52, 0x12, 0x9a, 0x41, 0xf7, + 0x83, 0xa7, 0x70, 0xed, 0xbe, 0x3a, 0x82, 0x27, 0x2c, 0x91, 0xbf, 0x38, 0x8f, 0x4d, 0xd8, 0x50, + 0x65, 0x8c, 0x32, 0xa9, 0xef, 0xe3, 0x52, 0x34, 0xc3, 0xda, 0xfa, 0x34, 0xc1, 0x82, 0x0a, 0xa7, + 0xe6, 0xd7, 0xb4, 0xf5, 0x06, 0x06, 0x9f, 0x00, 0x6c, 0x3c, 0xa4, 0x12, 0x13, 0x2c, 0x31, 0xf2, + 0x61, 0x93, 0x50, 0xd1, 0x1f, 0x26, 0xb9, 0x4c, 0x38, 0xab, 0xe4, 0x17, 0x43, 0xe8, 0x9e, 0x62, + 0x30, 0x9e, 0xf5, 0x0a, 0x96, 0xc8, 0xe9, 0xbe, 0xdc, 0xa5, 0x9f, 0xdc, 0x6c, 0xde, 0x08, 0x92, + 0xe9, 0x53, 0x20, 0x04, 0x57, 0x95, 0xbb, 0x4e, 0x4d, 0x6b, 0xeb, 0xb7, 0x9a, 0x8e, 0x24, 0x22, + 0x4f, 0x71, 0xe9, 0xac, 0x9a, 0xc3, 0xa8, 0xa0, 0x62, 0x33, 0x9c, 0x51, 0xa7, 0x6e, 0xd8, 0xea, + 0x8d, 0xae, 0x40, 0x5b, 0x94, 0x59, 0xcc, 0x53, 0xc7, 0xd6, 0xd1, 0x0a, 0x75, 0x77, 0x4f, 0xc7, + 0x2e, 0x38, 0x1b, 0xbb, 0xe0, 0xfb, 0xd8, 0x05, 0x6f, 0x27, 0xae, 0x75, 0x36, 0x71, 0xad, 0xaf, + 0x13, 0xd7, 0x7a, 0xb6, 0xf5, 0x27, 0x96, 0xeb, 0xdd, 0xc5, 0xb6, 0xfe, 0x97, 0xb9, 0xf3, 0x33, + 0x00, 0x00, 0xff, 0xff, 0xa8, 0xf5, 0xda, 0x82, 0xed, 0x04, 0x00, 0x00, } func (this *SendEnabled) Equal(that interface{}) bool { @@ -774,6 +794,20 @@ func (m *Metadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Symbol) > 0 { + i -= len(m.Symbol) + copy(dAtA[i:], m.Symbol) + i = encodeVarintBank(dAtA, i, uint64(len(m.Symbol))) + i-- + dAtA[i] = 0x32 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintBank(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x2a + } if len(m.Display) > 0 { i -= len(m.Display) copy(dAtA[i:], m.Display) @@ -956,6 +990,14 @@ func (m *Metadata) Size() (n int) { if l > 0 { n += 1 + l + sovBank(uint64(l)) } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovBank(uint64(l)) + } + l = len(m.Symbol) + if l > 0 { + n += 1 + l + sovBank(uint64(l)) + } return n } @@ -1779,6 +1821,70 @@ func (m *Metadata) Unmarshal(dAtA []byte) error { } m.Display = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Symbol", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Symbol = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipBank(dAtA[iNdEx:]) diff --git a/x/bank/types/events.go b/x/bank/types/events.go index c03d142f0..9f06b85e4 100644 --- a/x/bank/types/events.go +++ b/x/bank/types/events.go @@ -1,5 +1,9 @@ package types +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + // bank module event types const ( EventTypeTransfer = "transfer" @@ -8,4 +12,55 @@ const ( AttributeKeySender = "sender" AttributeValueCategory = ModuleName + + // supply and balance tracking events name and attributes + EventTypeCoinSpent = "coin_spent" + EventTypeCoinReceived = "coin_received" + EventTypeCoinMint = "coinbase" // NOTE(fdymylja): using mint clashes with mint module event + EventTypeCoinBurn = "burn" + + AttributeKeySpender = "spender" + AttributeKeyReceiver = "receiver" + AttributeKeyMinter = "minter" + AttributeKeyBurner = "burner" ) + +// NewCoinSpentEvent constructs a new coin spent sdk.Event +// nolint: interfacer +func NewCoinSpentEvent(spender sdk.AccAddress, amount sdk.Coins) sdk.Event { + return sdk.NewEvent( + EventTypeCoinSpent, + sdk.NewAttribute(AttributeKeySpender, spender.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, amount.String()), + ) +} + +// NewCoinReceivedEvent constructs a new coin received sdk.Event +// nolint: interfacer +func NewCoinReceivedEvent(receiver sdk.AccAddress, amount sdk.Coins) sdk.Event { + return sdk.NewEvent( + EventTypeCoinReceived, + sdk.NewAttribute(AttributeKeyReceiver, receiver.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, amount.String()), + ) +} + +// NewCoinMintEvent construct a new coin minted sdk.Event +// nolint: interfacer +func NewCoinMintEvent(minter sdk.AccAddress, amount sdk.Coins) sdk.Event { + return sdk.NewEvent( + EventTypeCoinMint, + sdk.NewAttribute(AttributeKeyMinter, minter.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, amount.String()), + ) +} + +// NewCoinBurnEvent constructs a new coin burned sdk.Event +// nolint: interfacer +func NewCoinBurnEvent(burner sdk.AccAddress, amount sdk.Coins) sdk.Event { + return sdk.NewEvent( + EventTypeCoinBurn, + sdk.NewAttribute(AttributeKeyBurner, burner.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, amount.String()), + ) +} diff --git a/x/bank/types/genesis_test.go b/x/bank/types/genesis_test.go index 634b247e0..a177ddcda 100644 --- a/x/bank/types/genesis_test.go +++ b/x/bank/types/genesis_test.go @@ -28,6 +28,8 @@ func TestGenesisStateValidate(t *testing.T) { Supply: sdk.Coins{sdk.NewInt64Coin("uatom", 1)}, DenomMetadata: []Metadata{ { + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*DenomUnit{ {"uatom", uint32(0), []string{"microatom"}}, @@ -85,6 +87,8 @@ func TestGenesisStateValidate(t *testing.T) { GenesisState{ DenomMetadata: []Metadata{ { + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*DenomUnit{ {"uatom", uint32(0), []string{"microatom"}}, @@ -95,6 +99,8 @@ func TestGenesisStateValidate(t *testing.T) { Display: "atom", }, { + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*DenomUnit{ {"uatom", uint32(0), []string{"microatom"}}, @@ -113,6 +119,8 @@ func TestGenesisStateValidate(t *testing.T) { GenesisState{ DenomMetadata: []Metadata{ { + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*DenomUnit{ {"uatom", uint32(0), []string{"microatom"}}, diff --git a/x/bank/types/metadata.go b/x/bank/types/metadata.go index ce7794053..9583b85ce 100644 --- a/x/bank/types/metadata.go +++ b/x/bank/types/metadata.go @@ -9,12 +9,21 @@ import ( ) // Validate performs a basic validation of the coin metadata fields. It checks: +// - Name and Symbol are not blank // - Base and Display denominations are valid coin denominations // - Base and Display denominations are present in the DenomUnit slice // - Base denomination has exponent 0 // - Denomination units are sorted in ascending order // - Denomination units not duplicated func (m Metadata) Validate() error { + if strings.TrimSpace(m.Name) == "" { + return errors.New("name field cannot be blank") + } + + if strings.TrimSpace(m.Symbol) == "" { + return errors.New("symbol field cannot be blank") + } + if err := sdk.ValidateDenom(m.Base); err != nil { return fmt.Errorf("invalid metadata base denom: %w", err) } diff --git a/x/bank/types/metadata_test.go b/x/bank/types/metadata_test.go index 64241e609..3d4fe0a0e 100644 --- a/x/bank/types/metadata_test.go +++ b/x/bank/types/metadata_test.go @@ -18,6 +18,8 @@ func TestMetadataValidate(t *testing.T) { { "non-empty coins", types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ {"uatom", uint32(0), []string{"microatom"}}, @@ -29,17 +31,50 @@ func TestMetadataValidate(t *testing.T) { }, false, }, + { + "base coin is display coin", + types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"atom", uint32(0), []string{"ATOM"}}, + }, + Base: "atom", + Display: "atom", + }, + false, + }, {"empty metadata", types.Metadata{}, true}, + { + "blank name", + types.Metadata{ + Name: "", + }, + true, + }, + { + "blank symbol", + types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "", + }, + true, + }, { "invalid base denom", types.Metadata{ - Base: "", + Name: "Cosmos Hub Atom", + Symbol: "ATOM", + Base: "", }, true, }, { "invalid display denom", types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Base: "uatom", Display: "", }, @@ -48,6 +83,8 @@ func TestMetadataValidate(t *testing.T) { { "duplicate denom unit", types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ {"uatom", uint32(0), []string{"microatom"}}, @@ -61,6 +98,8 @@ func TestMetadataValidate(t *testing.T) { { "invalid denom unit", types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ {"", uint32(0), []string{"microatom"}}, @@ -73,6 +112,8 @@ func TestMetadataValidate(t *testing.T) { { "invalid denom unit alias", types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ {"uatom", uint32(0), []string{""}}, @@ -85,6 +126,8 @@ func TestMetadataValidate(t *testing.T) { { "duplicate denom unit alias", types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ {"uatom", uint32(0), []string{"microatom", "microatom"}}, @@ -97,6 +140,8 @@ func TestMetadataValidate(t *testing.T) { { "no base denom unit", types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ {"matom", uint32(3), []string{"milliatom"}}, @@ -110,6 +155,8 @@ func TestMetadataValidate(t *testing.T) { { "base denom exponent not zero", types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ {"uatom", uint32(1), []string{"microatom"}}, @@ -121,9 +168,26 @@ func TestMetadataValidate(t *testing.T) { }, true, }, + { + "invalid denom unit", + types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + {"", uint32(3), []string{"milliatom"}}, + }, + Base: "uatom", + Display: "uatom", + }, + true, + }, { "no display denom unit", types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ {"uatom", uint32(0), []string{"microatom"}}, @@ -136,6 +200,8 @@ func TestMetadataValidate(t *testing.T) { { "denom units not sorted", types.Metadata{ + Name: "Cosmos Hub Atom", + Symbol: "ATOM", Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ {"uatom", uint32(0), []string{"microatom"}}, diff --git a/x/bank/types/query.pb.gw.go b/x/bank/types/query.pb.gw.go index 06c7e2d04..3dec93b8b 100644 --- a/x/bank/types/query.pb.gw.go +++ b/x/bank/types/query.pb.gw.go @@ -690,19 +690,19 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Balance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "bank", "v1beta1", "balances", "address", "denom"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Balance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "bank", "v1beta1", "balances", "address", "denom"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_AllBalances_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "bank", "v1beta1", "balances", "address"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_AllBalances_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "bank", "v1beta1", "balances", "address"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_TotalSupply_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "bank", "v1beta1", "supply"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_TotalSupply_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "bank", "v1beta1", "supply"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_SupplyOf_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "bank", "v1beta1", "supply", "denom"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_SupplyOf_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "bank", "v1beta1", "supply", "denom"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "bank", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "bank", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DenomMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "bank", "v1beta1", "denoms_metadata", "denom"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DenomMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "bank", "v1beta1", "denoms_metadata", "denom"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DenomsMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "bank", "v1beta1", "denoms_metadata"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DenomsMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "bank", "v1beta1", "denoms_metadata"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/authz/types/send_authorization.go b/x/bank/types/send_authorization.go similarity index 55% rename from x/authz/types/send_authorization.go rename to x/bank/types/send_authorization.go index 2bb2a63a7..ccd755c1f 100644 --- a/x/authz/types/send_authorization.go +++ b/x/bank/types/send_authorization.go @@ -6,11 +6,12 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" - bank "github.com/cosmos/cosmos-sdk/x/bank/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authz "github.com/cosmos/cosmos-sdk/x/authz/exported" ) var ( - _ Authorization = &SendAuthorization{} + _ authz.Authorization = &SendAuthorization{} ) // NewSendAuthorization creates a new SendAuthorization object. @@ -26,20 +27,20 @@ func (authorization SendAuthorization) MethodName() string { } // Accept implements Authorization.Accept. -func (authorization SendAuthorization) Accept(msg sdk.ServiceMsg, block tmproto.Header) (allow bool, updated Authorization, delete bool) { - if reflect.TypeOf(msg.Request) == reflect.TypeOf(&bank.MsgSend{}) { - msg, ok := msg.Request.(*bank.MsgSend) +func (authorization SendAuthorization) Accept(msg sdk.ServiceMsg, block tmproto.Header) (updated authz.Authorization, delete bool, err error) { + if reflect.TypeOf(msg.Request) == reflect.TypeOf(&MsgSend{}) { + msg, ok := msg.Request.(*MsgSend) if ok { limitLeft, isNegative := authorization.SpendLimit.SafeSub(msg.Amount) if isNegative { - return false, nil, false + return nil, false, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "requested amount is more than spend limit") } if limitLeft.IsZero() { - return true, nil, true + return nil, true, nil } - return true, &SendAuthorization{SpendLimit: limitLeft}, false + return &SendAuthorization{SpendLimit: limitLeft}, false, nil } } - return false, nil, false + return nil, false, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "type mismatch") } diff --git a/x/crisis/client/cli/tx.go b/x/crisis/client/cli/tx.go index c18231724..c1288b95e 100644 --- a/x/crisis/client/cli/tx.go +++ b/x/crisis/client/cli/tx.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/x/crisis/types" ) @@ -49,11 +50,14 @@ func NewMsgVerifyInvariantTxCmd() *cobra.Command { senderAddr := clientCtx.GetFromAddress() msg := types.NewMsgVerifyInvariant(senderAddr, moduleName, route) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.VerifyInvariant(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } diff --git a/x/crisis/handler_test.go b/x/crisis/handler_test.go index dcde377f6..e981a1a78 100644 --- a/x/crisis/handler_test.go +++ b/x/crisis/handler_test.go @@ -5,14 +5,11 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/crisis" "github.com/cosmos/cosmos-sdk/x/crisis/types" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" @@ -26,9 +23,8 @@ var ( ) func createTestApp() (*simapp.SimApp, sdk.Context, []sdk.AccAddress) { - db := dbm.NewMemDB() - app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 1, simapp.MakeTestEncodingConfig(), simapp.EmptyAppOptions{}) - ctx := app.NewContext(true, tmproto.Header{}) + app := simapp.Setup(false) + ctx := app.NewContext(false, tmproto.Header{}) constantFee := sdk.NewInt64Coin(sdk.DefaultBondDenom, 10) app.CrisisKeeper.SetConstantFee(ctx, constantFee) @@ -40,15 +36,12 @@ func createTestApp() (*simapp.SimApp, sdk.Context, []sdk.AccAddress) { feePool := distrtypes.InitialFeePool() feePool.CommunityPool = sdk.NewDecCoinsFromCoins(sdk.NewCoins(constantFee)...) app.DistrKeeper.SetFeePool(ctx, feePool) - app.BankKeeper.SetSupply(ctx, banktypes.NewSupply(sdk.Coins{})) addrs := simapp.AddTestAddrs(app, ctx, 1, sdk.NewInt(10000)) return app, ctx, addrs } -//____________________________________________________________________________ - func TestHandleMsgVerifyInvariant(t *testing.T) { app, ctx, addrs := createTestApp() sender := addrs[0] diff --git a/x/crisis/keeper/msg_server.go b/x/crisis/keeper/msg_server.go index 304f8b172..a3eb07e6b 100644 --- a/x/crisis/keeper/msg_server.go +++ b/x/crisis/keeper/msg_server.go @@ -43,20 +43,8 @@ func (k Keeper) VerifyInvariant(goCtx context.Context, msg *types.MsgVerifyInvar } if stop { - // NOTE currently, because the chain halts here, this transaction will never be included - // in the blockchain thus the constant fee will have never been deducted. Thus no - // refund is required. - - // TODO uncomment the following code block with implementation of the circuit breaker - //// refund constant fee - //err := k.distrKeeper.DistributeFromFeePool(ctx, constantFee, msg.Sender) - //if err != nil { - //// if there are insufficient coins to refund, log the error, - //// but still halt the chain. - //logger := ctx.Logger().With("module", "x/crisis") - //logger.Error(fmt.Sprintf( - //"WARNING: insufficient funds to allocate to sender from fee pool, err: %s", err)) - //} + // Currently, because the chain halts here, this transaction will never be included in the + // blockchain thus the constant fee will have never been deducted. Thus no refund is required. // TODO replace with circuit breaker panic(res) diff --git a/x/crisis/module.go b/x/crisis/module.go index 66d829be8..ecea78f11 100644 --- a/x/crisis/module.go +++ b/x/crisis/module.go @@ -80,8 +80,6 @@ func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) types.RegisterInterfaces(registry) } -//____________________________________________________________________________ - // AppModule implements an application module for the crisis module. type AppModule struct { AppModuleBasic diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index dcf5a3b05..6237bf92e 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/distribution/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" @@ -214,11 +215,14 @@ $ %s tx distribution set-withdraw-addr %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p } msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.SetWithdrawAddress(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -253,11 +257,14 @@ $ %s tx distribution fund-community-pool 100uatom --from mykey } msg := types.NewMsgFundCommunityPool(amount, depositorAddr) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.FundCommunityPool(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -326,11 +333,14 @@ Where proposal.json contains: return err } - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := govtypes.NewMsgClient(svcMsgClientConn) + _, err = msgClient.SubmitProposal(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } diff --git a/x/distribution/keeper/allocation_test.go b/x/distribution/keeper/allocation_test.go index 9d8b0012a..f2a33a81b 100644 --- a/x/distribution/keeper/allocation_test.go +++ b/x/distribution/keeper/allocation_test.go @@ -82,8 +82,9 @@ func TestAllocateTokensToManyValidators(t *testing.T) { feeCollector := app.AccountKeeper.GetModuleAccount(ctx, types.FeeCollectorName) require.NotNil(t, feeCollector) - err := app.BankKeeper.SetBalances(ctx, feeCollector.GetAddress(), fees) - require.NoError(t, err) + // fund fee collector + require.NoError(t, simapp.FundAccount(app, ctx, feeCollector.GetAddress(), fees)) + app.AccountKeeper.SetAccount(ctx, feeCollector) votes := []abci.VoteInfo{ @@ -162,8 +163,7 @@ func TestAllocateTokensTruncation(t *testing.T) { feeCollector := app.AccountKeeper.GetModuleAccount(ctx, types.FeeCollectorName) require.NotNil(t, feeCollector) - err := app.BankKeeper.SetBalances(ctx, feeCollector.GetAddress(), fees) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, feeCollector.GetAddress(), fees)) app.AccountKeeper.SetAccount(ctx, feeCollector) diff --git a/x/distribution/keeper/delegation_test.go b/x/distribution/keeper/delegation_test.go index ac8ac4a33..51def9783 100644 --- a/x/distribution/keeper/delegation_test.go +++ b/x/distribution/keeper/delegation_test.go @@ -280,7 +280,7 @@ func TestWithdrawDelegationRewardsBasic(t *testing.T) { // set module account coins distrAcc := app.DistrKeeper.GetDistributionAccount(ctx) - require.NoError(t, app.BankKeeper.SetBalances(ctx, distrAcc.GetAddress(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, balanceTokens)))) + require.NoError(t, simapp.FundAccount(app, ctx, distrAcc.GetAddress(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, balanceTokens)))) app.AccountKeeper.SetModuleAccount(ctx, distrAcc) // create validator with 50% commission @@ -492,8 +492,7 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { // set module account coins distrAcc := app.DistrKeeper.GetDistributionAccount(ctx) - err := app.BankKeeper.SetBalances(ctx, distrAcc.GetAddress(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, distrAcc.GetAddress(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000))))) app.AccountKeeper.SetModuleAccount(ctx, distrAcc) tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDec(initial))} @@ -538,7 +537,7 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // first delegator withdraws - _, err = app.DistrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + _, err := app.DistrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) require.NoError(t, err) // second delegator withdraws diff --git a/x/distribution/keeper/genesis.go b/x/distribution/keeper/genesis.go index 4aa25671a..5cb65d760 100644 --- a/x/distribution/keeper/genesis.go +++ b/x/distribution/keeper/genesis.go @@ -97,12 +97,11 @@ func (k Keeper) InitGenesis(ctx sdk.Context, data types.GenesisState) { balances := k.bankKeeper.GetAllBalances(ctx, moduleAcc.GetAddress()) if balances.IsZero() { - if err := k.bankKeeper.SetBalances(ctx, moduleAcc.GetAddress(), moduleHoldingsInt); err != nil { - panic(err) - } - k.authKeeper.SetModuleAccount(ctx, moduleAcc) } + if !balances.IsEqual(moduleHoldingsInt) { + panic(fmt.Sprintf("distribution module balance does not match the module holdings: %s <-> %s", balances, moduleHoldingsInt)) + } } // ExportGenesis returns a GenesisState for a given context and keeper. diff --git a/x/distribution/keeper/grpc_query_test.go b/x/distribution/keeper/grpc_query_test.go index 412144094..8beb3c715 100644 --- a/x/distribution/keeper/grpc_query_test.go +++ b/x/distribution/keeper/grpc_query_test.go @@ -623,7 +623,7 @@ func (suite *KeeperTestSuite) TestGRPCCommunityPool() { "valid request", func() { amount := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) - suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addrs[0], amount)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addrs[0], amount)) err := app.DistrKeeper.FundCommunityPool(ctx, amount, addrs[0]) suite.Require().Nil(err) diff --git a/x/distribution/keeper/keeper_test.go b/x/distribution/keeper/keeper_test.go index a8205dfdc..ad1890225 100644 --- a/x/distribution/keeper/keeper_test.go +++ b/x/distribution/keeper/keeper_test.go @@ -48,11 +48,9 @@ func TestWithdrawValidatorCommission(t *testing.T) { // set module account coins distrAcc := app.DistrKeeper.GetDistributionAccount(ctx) - err := app.BankKeeper.SetBalances(ctx, distrAcc.GetAddress(), sdk.NewCoins( - sdk.NewCoin("mytoken", sdk.NewInt(2)), - sdk.NewCoin("stake", sdk.NewInt(2)), - )) - require.NoError(t, err) + coins := sdk.NewCoins(sdk.NewCoin("mytoken", sdk.NewInt(2)), sdk.NewCoin("stake", sdk.NewInt(2))) + require.NoError(t, simapp.FundAccount(app, ctx, distrAcc.GetAddress(), coins)) + app.AccountKeeper.SetModuleAccount(ctx, distrAcc) // check initial balance @@ -68,7 +66,7 @@ func TestWithdrawValidatorCommission(t *testing.T) { app.DistrKeeper.SetValidatorAccumulatedCommission(ctx, valAddrs[0], types.ValidatorAccumulatedCommission{Commission: valCommission}) // withdraw commission - _, err = app.DistrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + _, err := app.DistrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) require.NoError(t, err) // check balance increase @@ -113,10 +111,10 @@ func TestFundCommunityPool(t *testing.T) { app := simapp.Setup(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - addr := simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(1000000000)) + addr := simapp.AddTestAddrs(app, ctx, 2, sdk.ZeroInt()) amount := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) - require.NoError(t, app.BankKeeper.SetBalances(ctx, addr[0], amount)) + require.NoError(t, simapp.FundAccount(app, ctx, addr[0], amount)) initPool := app.DistrKeeper.GetFeePool(ctx) assert.Empty(t, initPool.CommunityPool) diff --git a/x/distribution/keeper/migrations.go b/x/distribution/keeper/migrations.go new file mode 100644 index 000000000..7c5d40dba --- /dev/null +++ b/x/distribution/keeper/migrations.go @@ -0,0 +1,21 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + v042 "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v042" +) + +// Migrator is a struct for handling in-place store migrations. +type Migrator struct { + keeper Keeper +} + +// NewMigrator returns a new Migrator. +func NewMigrator(keeper Keeper) Migrator { + return Migrator{keeper: keeper} +} + +// Migrate1to2 migrates from version 1 to 2. +func (m Migrator) Migrate1to2(ctx sdk.Context) error { + return v042.MigrateStore(ctx, m.keeper.storeKey) +} diff --git a/x/distribution/legacy/v034/types.go b/x/distribution/legacy/v034/types.go index 3ef672f4a..4e8b209ad 100644 --- a/x/distribution/legacy/v034/types.go +++ b/x/distribution/legacy/v034/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v034 import ( diff --git a/x/distribution/legacy/v036/types.go b/x/distribution/legacy/v036/types.go index 1c43eb5ac..2a11518b4 100644 --- a/x/distribution/legacy/v036/types.go +++ b/x/distribution/legacy/v036/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v036 import ( diff --git a/x/distribution/legacy/v038/migrate.go b/x/distribution/legacy/v038/migrate.go index cd00668b1..73e933da4 100644 --- a/x/distribution/legacy/v038/migrate.go +++ b/x/distribution/legacy/v038/migrate.go @@ -1,7 +1,6 @@ package v038 // DONTCOVER -// nolint import ( v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" diff --git a/x/distribution/legacy/v038/types.go b/x/distribution/legacy/v038/types.go index af7146418..335fbc124 100644 --- a/x/distribution/legacy/v038/types.go +++ b/x/distribution/legacy/v038/types.go @@ -7,7 +7,6 @@ import ( ) // DONTCOVER -// nolint const ( ModuleName = "distribution" diff --git a/x/distribution/legacy/v040/keys.go b/x/distribution/legacy/v040/keys.go new file mode 100644 index 000000000..88a3c7807 --- /dev/null +++ b/x/distribution/legacy/v040/keys.go @@ -0,0 +1,196 @@ +// Package v040 is copy-pasted from: +// https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/distribution/types/keys.go +package v040 + +import ( + "encoding/binary" + + sdk "github.com/cosmos/cosmos-sdk/types" + v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" +) + +const ( + // ModuleName is the module name constant used in many places + ModuleName = "distribution" + + // StoreKey is the store key string for distribution + StoreKey = ModuleName + + // RouterKey is the message route for distribution + RouterKey = ModuleName + + // QuerierRoute is the querier route for distribution + QuerierRoute = ModuleName +) + +// Keys for distribution store +// Items are stored with the following key: values +// +// - 0x00: FeePol +// +// - 0x01: sdk.ConsAddress +// +// - 0x02: ValidatorOutstandingRewards +// +// - 0x03: sdk.AccAddress +// +// - 0x04: DelegatorStartingInfo +// +// - 0x05: ValidatorHistoricalRewards +// +// - 0x06: ValidatorCurrentRewards +// +// - 0x07: ValidatorCurrentRewards +// +// - 0x08: ValidatorSlashEvent +var ( + FeePoolKey = []byte{0x00} // key for global distribution state + ProposerKey = []byte{0x01} // key for the proposer operator address + ValidatorOutstandingRewardsPrefix = []byte{0x02} // key for outstanding rewards + + DelegatorWithdrawAddrPrefix = []byte{0x03} // key for delegator withdraw address + DelegatorStartingInfoPrefix = []byte{0x04} // key for delegator starting info + ValidatorHistoricalRewardsPrefix = []byte{0x05} // key for historical validators rewards / stake + ValidatorCurrentRewardsPrefix = []byte{0x06} // key for current validator rewards + ValidatorAccumulatedCommissionPrefix = []byte{0x07} // key for accumulated validator commission + ValidatorSlashEventPrefix = []byte{0x08} // key for validator slash fraction +) + +// gets an address from a validator's outstanding rewards key +func GetValidatorOutstandingRewardsAddress(key []byte) (valAddr sdk.ValAddress) { + addr := key[1:] + if len(addr) != v040auth.AddrLen { + panic("unexpected key length") + } + return sdk.ValAddress(addr) +} + +// gets an address from a delegator's withdraw info key +func GetDelegatorWithdrawInfoAddress(key []byte) (delAddr sdk.AccAddress) { + addr := key[1:] + if len(addr) != v040auth.AddrLen { + panic("unexpected key length") + } + return sdk.AccAddress(addr) +} + +// gets the addresses from a delegator starting info key +func GetDelegatorStartingInfoAddresses(key []byte) (valAddr sdk.ValAddress, delAddr sdk.AccAddress) { + addr := key[1 : 1+v040auth.AddrLen] + if len(addr) != v040auth.AddrLen { + panic("unexpected key length") + } + valAddr = sdk.ValAddress(addr) + addr = key[1+v040auth.AddrLen:] + if len(addr) != v040auth.AddrLen { + panic("unexpected key length") + } + delAddr = sdk.AccAddress(addr) + return +} + +// gets the address & period from a validator's historical rewards key +func GetValidatorHistoricalRewardsAddressPeriod(key []byte) (valAddr sdk.ValAddress, period uint64) { + addr := key[1 : 1+v040auth.AddrLen] + if len(addr) != v040auth.AddrLen { + panic("unexpected key length") + } + valAddr = sdk.ValAddress(addr) + b := key[1+v040auth.AddrLen:] + if len(b) != 8 { + panic("unexpected key length") + } + period = binary.LittleEndian.Uint64(b) + return +} + +// gets the address from a validator's current rewards key +func GetValidatorCurrentRewardsAddress(key []byte) (valAddr sdk.ValAddress) { + addr := key[1:] + if len(addr) != v040auth.AddrLen { + panic("unexpected key length") + } + return sdk.ValAddress(addr) +} + +// gets the address from a validator's accumulated commission key +func GetValidatorAccumulatedCommissionAddress(key []byte) (valAddr sdk.ValAddress) { + addr := key[1:] + if len(addr) != v040auth.AddrLen { + panic("unexpected key length") + } + return sdk.ValAddress(addr) +} + +// gets the height from a validator's slash event key +func GetValidatorSlashEventAddressHeight(key []byte) (valAddr sdk.ValAddress, height uint64) { + addr := key[1 : 1+v040auth.AddrLen] + if len(addr) != v040auth.AddrLen { + panic("unexpected key length") + } + valAddr = sdk.ValAddress(addr) + startB := 1 + v040auth.AddrLen + b := key[startB : startB+8] // the next 8 bytes represent the height + height = binary.BigEndian.Uint64(b) + return +} + +// gets the outstanding rewards key for a validator +func GetValidatorOutstandingRewardsKey(valAddr sdk.ValAddress) []byte { + return append(ValidatorOutstandingRewardsPrefix, valAddr.Bytes()...) +} + +// gets the key for a delegator's withdraw addr +func GetDelegatorWithdrawAddrKey(delAddr sdk.AccAddress) []byte { + return append(DelegatorWithdrawAddrPrefix, delAddr.Bytes()...) +} + +// gets the key for a delegator's starting info +func GetDelegatorStartingInfoKey(v sdk.ValAddress, d sdk.AccAddress) []byte { + return append(append(DelegatorStartingInfoPrefix, v.Bytes()...), d.Bytes()...) +} + +// gets the prefix key for a validator's historical rewards +func GetValidatorHistoricalRewardsPrefix(v sdk.ValAddress) []byte { + return append(ValidatorHistoricalRewardsPrefix, v.Bytes()...) +} + +// gets the key for a validator's historical rewards +func GetValidatorHistoricalRewardsKey(v sdk.ValAddress, k uint64) []byte { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, k) + return append(append(ValidatorHistoricalRewardsPrefix, v.Bytes()...), b...) +} + +// gets the key for a validator's current rewards +func GetValidatorCurrentRewardsKey(v sdk.ValAddress) []byte { + return append(ValidatorCurrentRewardsPrefix, v.Bytes()...) +} + +// gets the key for a validator's current commission +func GetValidatorAccumulatedCommissionKey(v sdk.ValAddress) []byte { + return append(ValidatorAccumulatedCommissionPrefix, v.Bytes()...) +} + +// gets the prefix key for a validator's slash fractions +func GetValidatorSlashEventPrefix(v sdk.ValAddress) []byte { + return append(ValidatorSlashEventPrefix, v.Bytes()...) +} + +// gets the prefix key for a validator's slash fraction (ValidatorSlashEventPrefix + height) +func GetValidatorSlashEventKeyPrefix(v sdk.ValAddress, height uint64) []byte { + heightBz := make([]byte, 8) + binary.BigEndian.PutUint64(heightBz, height) + return append( + ValidatorSlashEventPrefix, + append(v.Bytes(), heightBz...)..., + ) +} + +// gets the key for a validator's slash fraction +func GetValidatorSlashEventKey(v sdk.ValAddress, height, period uint64) []byte { + periodBz := make([]byte, 8) + binary.BigEndian.PutUint64(periodBz, period) + prefix := GetValidatorSlashEventKeyPrefix(v, height) + return append(prefix, periodBz...) +} diff --git a/x/distribution/legacy/v040/types.go b/x/distribution/legacy/v040/types.go deleted file mode 100644 index aa5023038..000000000 --- a/x/distribution/legacy/v040/types.go +++ /dev/null @@ -1,6 +0,0 @@ -package v040 - -// Default parameter values -const ( - ModuleName = "distribution" -) diff --git a/x/distribution/legacy/v042/helpers.go b/x/distribution/legacy/v042/helpers.go new file mode 100644 index 000000000..afc99cf70 --- /dev/null +++ b/x/distribution/legacy/v042/helpers.go @@ -0,0 +1,70 @@ +package v042 + +import ( + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" + v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" +) + +// MigratePrefixAddress is a helper function that migrates all keys of format: +// prefix_bytes | address_bytes +// into format: +// prefix_bytes | address_len (1 byte) | address_bytes +func MigratePrefixAddress(store sdk.KVStore, prefixBz []byte) { + oldStore := prefix.NewStore(store, prefixBz) + + oldStoreIter := oldStore.Iterator(nil, nil) + defer oldStoreIter.Close() + + for ; oldStoreIter.Valid(); oldStoreIter.Next() { + addr := oldStoreIter.Key() + newStoreKey := append(prefixBz, address.MustLengthPrefix(addr)...) + + // Set new key on store. Values don't change. + store.Set(newStoreKey, oldStoreIter.Value()) + oldStore.Delete(oldStoreIter.Key()) + } +} + +// MigratePrefixAddressBytes is a helper function that migrates all keys of format: +// prefix_bytes | address_bytes | arbitrary_bytes +// into format: +// prefix_bytes | address_len (1 byte) | address_bytes | arbitrary_bytes +func MigratePrefixAddressBytes(store sdk.KVStore, prefixBz []byte) { + oldStore := prefix.NewStore(store, prefixBz) + + oldStoreIter := oldStore.Iterator(nil, nil) + defer oldStoreIter.Close() + + for ; oldStoreIter.Valid(); oldStoreIter.Next() { + addr := oldStoreIter.Key()[:v040auth.AddrLen] + endBz := oldStoreIter.Key()[v040auth.AddrLen:] + newStoreKey := append(append(prefixBz, address.MustLengthPrefix(addr)...), endBz...) + + // Set new key on store. Values don't change. + store.Set(newStoreKey, oldStoreIter.Value()) + oldStore.Delete(oldStoreIter.Key()) + } +} + +// MigratePrefixAddressAddress is a helper function that migrates all keys of format: +// prefix_bytes | address_1_bytes | address_2_bytes +// into format: +// prefix_bytes | address_1_len (1 byte) | address_1_bytes | address_2_len (1 byte) | address_2_bytes +func MigratePrefixAddressAddress(store sdk.KVStore, prefixBz []byte) { + oldStore := prefix.NewStore(store, prefixBz) + + oldStoreIter := oldStore.Iterator(nil, nil) + defer oldStoreIter.Close() + + for ; oldStoreIter.Valid(); oldStoreIter.Next() { + addr1 := oldStoreIter.Key()[:v040auth.AddrLen] + addr2 := oldStoreIter.Key()[v040auth.AddrLen:] + newStoreKey := append(append(prefixBz, address.MustLengthPrefix(addr1)...), address.MustLengthPrefix(addr2)...) + + // Set new key on store. Values don't change. + store.Set(newStoreKey, oldStoreIter.Value()) + oldStore.Delete(oldStoreIter.Key()) + } +} diff --git a/x/distribution/legacy/v042/store.go b/x/distribution/legacy/v042/store.go new file mode 100644 index 000000000..31c575b4a --- /dev/null +++ b/x/distribution/legacy/v042/store.go @@ -0,0 +1,23 @@ +package v042 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + v040distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v040" +) + +// MigrateStore performs in-place store migrations from v0.40 to v0.42. The +// migration includes: +// +// - Change addresses to be length-prefixed. +func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error { + store := ctx.KVStore(storeKey) + MigratePrefixAddress(store, v040distribution.ValidatorOutstandingRewardsPrefix) + MigratePrefixAddress(store, v040distribution.DelegatorWithdrawAddrPrefix) + MigratePrefixAddressAddress(store, v040distribution.DelegatorStartingInfoPrefix) + MigratePrefixAddressBytes(store, v040distribution.ValidatorHistoricalRewardsPrefix) + MigratePrefixAddress(store, v040distribution.ValidatorCurrentRewardsPrefix) + MigratePrefixAddress(store, v040distribution.ValidatorAccumulatedCommissionPrefix) + MigratePrefixAddressBytes(store, v040distribution.ValidatorSlashEventPrefix) + + return nil +} diff --git a/x/distribution/legacy/v042/store_test.go b/x/distribution/legacy/v042/store_test.go new file mode 100644 index 000000000..2a4a5df75 --- /dev/null +++ b/x/distribution/legacy/v042/store_test.go @@ -0,0 +1,99 @@ +package v042_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + v040distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v040" + v042distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v042" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +func TestStoreMigration(t *testing.T) { + distributionKey := sdk.NewKVStoreKey("distribution") + ctx := testutil.DefaultContext(distributionKey, sdk.NewTransientStoreKey("transient_test")) + store := ctx.KVStore(distributionKey) + + _, _, addr1 := testdata.KeyTestPubAddr() + valAddr := sdk.ValAddress(addr1) + _, _, addr2 := testdata.KeyTestPubAddr() + // Use dummy value for all keys. + value := []byte("foo") + + testCases := []struct { + name string + oldKey []byte + newKey []byte + }{ + { + "FeePoolKey", + v040distribution.FeePoolKey, + types.FeePoolKey, + }, + { + "ProposerKey", + v040distribution.ProposerKey, + types.ProposerKey, + }, + { + "ValidatorOutstandingRewards", + v040distribution.GetValidatorOutstandingRewardsKey(valAddr), + types.GetValidatorOutstandingRewardsKey(valAddr), + }, + { + "DelegatorWithdrawAddr", + v040distribution.GetDelegatorWithdrawAddrKey(addr2), + types.GetDelegatorWithdrawAddrKey(addr2), + }, + { + "DelegatorStartingInfo", + v040distribution.GetDelegatorStartingInfoKey(valAddr, addr2), + types.GetDelegatorStartingInfoKey(valAddr, addr2), + }, + { + "ValidatorHistoricalRewards", + v040distribution.GetValidatorHistoricalRewardsKey(valAddr, 6), + types.GetValidatorHistoricalRewardsKey(valAddr, 6), + }, + { + "ValidatorCurrentRewards", + v040distribution.GetValidatorCurrentRewardsKey(valAddr), + types.GetValidatorCurrentRewardsKey(valAddr), + }, + { + "ValidatorAccumulatedCommission", + v040distribution.GetValidatorAccumulatedCommissionKey(valAddr), + types.GetValidatorAccumulatedCommissionKey(valAddr), + }, + { + "ValidatorSlashEvent", + v040distribution.GetValidatorSlashEventKey(valAddr, 6, 8), + types.GetValidatorSlashEventKey(valAddr, 6, 8), + }, + } + + // Set all the old keys to the store + for _, tc := range testCases { + store.Set(tc.oldKey, value) + } + + // Run migrations. + err := v042distribution.MigrateStore(ctx, distributionKey) + require.NoError(t, err) + + // Make sure the new keys are set and old keys are deleted. + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + if bytes.Compare(tc.oldKey, tc.newKey) != 0 { + require.Nil(t, store.Get(tc.oldKey)) + } + require.Equal(t, value, store.Get(tc.newKey)) + }) + } +} diff --git a/x/distribution/module.go b/x/distribution/module.go index 0f0c0d358..d6261d3cd 100644 --- a/x/distribution/module.go +++ b/x/distribution/module.go @@ -88,8 +88,6 @@ func (b AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) types.RegisterInterfaces(registry) } -//____________________________________________________________________________ - // AppModule implements an application module for the distribution module. type AppModule struct { AppModuleBasic @@ -143,6 +141,9 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) types.RegisterQueryServer(cfg.QueryServer(), am.keeper) + + m := keeper.NewMigrator(am.keeper) + cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2) } // InitGenesis performs genesis initialization for the distribution module. It returns @@ -162,7 +163,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 1 } +func (AppModule) ConsensusVersion() uint64 { return 2 } // BeginBlock returns the begin blocker for the distribution module. func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { @@ -175,8 +176,6 @@ func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.Validato return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the distribution module. diff --git a/x/distribution/proposal_handler_test.go b/x/distribution/proposal_handler_test.go index 575dbb199..c4d9bdb5c 100644 --- a/x/distribution/proposal_handler_test.go +++ b/x/distribution/proposal_handler_test.go @@ -33,8 +33,7 @@ func TestProposalHandlerPassed(t *testing.T) { // add coins to the module account macc := app.DistrKeeper.GetDistributionAccount(ctx) balances := app.BankKeeper.GetAllBalances(ctx, macc.GetAddress()) - err := app.BankKeeper.SetBalances(ctx, macc.GetAddress(), balances.Add(amount...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, macc.GetAddress(), amount)) app.AccountKeeper.SetModuleAccount(ctx, macc) diff --git a/x/distribution/simulation/operations_test.go b/x/distribution/simulation/operations_test.go index 4a3dff308..8ac75201b 100644 --- a/x/distribution/simulation/operations_test.go +++ b/x/distribution/simulation/operations_test.go @@ -143,11 +143,10 @@ func (suite *SimTestSuite) testSimulateMsgWithdrawValidatorCommission(tokenName // set module account coins distrAcc := suite.app.DistrKeeper.GetDistributionAccount(suite.ctx) - err := suite.app.BankKeeper.SetBalances(suite.ctx, distrAcc.GetAddress(), sdk.NewCoins( + suite.Require().NoError(simapp.FundAccount(suite.app, suite.ctx, distrAcc.GetAddress(), sdk.NewCoins( sdk.NewCoin(tokenName, sdk.NewInt(10)), sdk.NewCoin("stake", sdk.NewInt(5)), - )) - suite.Require().NoError(err) + ))) suite.app.AccountKeeper.SetModuleAccount(suite.ctx, distrAcc) // set outstanding rewards @@ -230,8 +229,7 @@ func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Ac for _, account := range accounts { acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, account.Address) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - err := suite.app.BankKeeper.SetBalances(suite.ctx, account.Address, initCoins) - suite.Require().NoError(err) + suite.Require().NoError(simapp.FundAccount(suite.app, suite.ctx, account.Address, initCoins)) } return accounts diff --git a/x/distribution/spec/02_state.md b/x/distribution/spec/02_state.md index b8dbde4a9..4111089cc 100644 --- a/x/distribution/spec/02_state.md +++ b/x/distribution/spec/02_state.md @@ -15,7 +15,7 @@ for fractions of coins to be received from operations like inflation. When coins are distributed from the pool they are truncated back to `sdk.Coins` which are non-decimal. -- FeePool: `0x00 -> ProtocolBuffer(FeePool)` +- FeePool: `0x00 -> ProtocolBuffer(FeePool)` ```go // coins with decimal @@ -33,12 +33,12 @@ type DecCoin struct { Validator distribution information for the relevant validator is updated each time: - 1. delegation amount to a validator is updated, - 2. a validator successfully proposes a block and receives a reward, - 3. any delegator withdraws from a validator, or - 4. the validator withdraws it's commission. +1. delegation amount to a validator is updated, +2. a validator successfully proposes a block and receives a reward, +3. any delegator withdraws from a validator, or +4. the validator withdraws it's commission. -- ValidatorDistInfo: `0x02 | ValOperatorAddr -> ProtocolBuffer(validatorDistribution)` +- ValidatorDistInfo: `0x02 | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(validatorDistribution)` ```go type ValidatorDistInfo struct { @@ -56,7 +56,7 @@ properties change (aka bonded tokens etc.) its properties will remain constant and the delegator's _accumulation_ factor can be calculated passively knowing only the height of the last withdrawal and its current properties. -- DelegationDistInfo: `0x02 | DelegatorAddr | ValOperatorAddr -> ProtocolBuffer(delegatorDist)` +- DelegationDistInfo: `0x02 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(delegatorDist)` ```go type DelegationDistInfo struct { diff --git a/x/distribution/types/expected_keepers.go b/x/distribution/types/expected_keepers.go index 29346d390..ac9ebc3a4 100644 --- a/x/distribution/types/expected_keepers.go +++ b/x/distribution/types/expected_keepers.go @@ -21,7 +21,6 @@ type AccountKeeper interface { type BankKeeper interface { GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin - SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins diff --git a/x/distribution/types/query.pb.gw.go b/x/distribution/types/query.pb.gw.go index 7bbe7b7a2..2e8f1cde3 100644 --- a/x/distribution/types/query.pb.gw.go +++ b/x/distribution/types/query.pb.gw.go @@ -896,23 +896,23 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "distribution", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "distribution", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ValidatorOutstandingRewards_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "validators", "validator_address", "outstanding_rewards"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ValidatorOutstandingRewards_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "validators", "validator_address", "outstanding_rewards"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ValidatorCommission_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "validators", "validator_address", "commission"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ValidatorCommission_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "validators", "validator_address", "commission"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ValidatorSlashes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "validators", "validator_address", "slashes"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ValidatorSlashes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "validators", "validator_address", "slashes"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DelegationRewards_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "rewards", "validator_address"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DelegationRewards_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "rewards", "validator_address"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DelegationTotalRewards_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "rewards"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DelegationTotalRewards_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "rewards"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DelegatorValidators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "validators"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DelegatorValidators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "validators"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DelegatorWithdrawAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "withdraw_address"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DelegatorWithdrawAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "withdraw_address"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_CommunityPool_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "distribution", "v1beta1", "community_pool"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_CommunityPool_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "distribution", "v1beta1", "community_pool"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/evidence/atlas/atlas-v0.41.x.md b/x/evidence/atlas/atlas-v0.41.x.md new file mode 100644 index 000000000..e3df33751 --- /dev/null +++ b/x/evidence/atlas/atlas-v0.41.x.md @@ -0,0 +1,167 @@ +# x/evidence + +The `x/evidence` module is responsible for handling multi-asset coin transfers between +accounts and tracking special-case pseudo-transfers which must work differently +with particular kinds of accounts. + +## Usage + +1. Import the module. + + ```go + import ( + "github.com/cosmos/cosmos-sdk/x/evidence" + evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + ) + ``` + +2. Add `AppModuleBasic` to your `ModuleBasics`. + + ```go + var ( + ModuleBasics = module.NewBasicManager( + // ... + evidence.AppModuleBasic{}, + } + ) + ``` + +3. Add the evidence keeper to your apps struct. + + ```go + type app struct { + // ... + EvidenceKeeper evidencekeeper.Keeper + // ... + } + ``` + +4. Add the evidence store key to the group of store keys. + + ```go + func NewApp(...) *App { + // ... + keys := sdk.NewKVStoreKeys( + evidencetypes.StoreKey, + ) + // ... + } + ``` + +5. Create the keeper. Note, the `x/evidence` module depends on the `x/staking` and `x/slashing` modules. Evidence has expected interfaces, these interfaces are linked to slashing and staking. You can find these interfaces [here](https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/evidence/types/expected_keepers.go) + + ```go + func NewApp(...) *App { + // ... + // create evidence keeper with router + evidenceKeeper := evidencekeeper.NewKeeper( + appCodec, keys[evidencetypes.StoreKey], &app.StakingKeeper, app.SlashingKeeper, + ) + } + ``` + +6. Add the `x/evidence` module to the app's `ModuleManager`. + + ```go + func NewApp(...) *App { + // ... + app.mm = module.NewManager( + // ... + evidence.NewAppModule(app.EvidenceKeeper), + // ... + ) + } + ``` + +7. Set the `x/evidence` module begin blocker order. + + ```go + func NewApp(...) *App { + // ... + app.mm.SetOrderBeginBlockers( + // ... + evidencetypes.ModuleName, + // ... + ) + } + ``` + +8. Set the `x/evidence` module genesis order. + + ```go + func NewApp(...) *App { + // ... + app.mm.SetOrderInitGenesis(..., evidencetypes.ModuleName, ...) + } + ``` + +9. Add the `x/evidence` module to the simulation manager (if you have one set). + + ```go + func NewApp(...) *App { + // ... + app.sm = module.NewSimulationManager( + // ... + evidence.NewAppModule(app.EvidenceKeeper), + // ... + ) + } + +## Genesis + +The `x/evidence` module defines its genesis state as follows: + +```proto +type GenesisState struct { + // evidence defines all the evidence at genesis. + Evidence []*types.Any `protobuf:"bytes,1,rep,name=evidence,proto3" json:"evidence,omitempty"` +} +``` + +## Messages + + +View supported messages at [docs.cosmos.network/v0.40/modules/evidence](https://docs.cosmos.network/v0.40/modules/evidence/03_messages.html) + +## Client + +Evidence supports querying of old evidence and submission of new evidence. There are two queries. One for all the evidence, and one for a specific piece of evidence. + +### CLI + +The evidence module supports the blow command to query evidence. + +```sh +Usage: + app query evidence [flags] + +Flags: + --count-total count total number of records in evidence to query for + --height int Use a specific height to query state at (this can error if the node is pruning state) + -h, --help help for evidence + --limit uint pagination limit of evidence to query for (default 100) + --node string : to Tendermint RPC interface for this chain (default "tcp://localhost:26657") + --offset uint pagination offset of evidence to query for + -o, --output string Output format (text|json) (default "text") + --page uint pagination page of evidence to query for. This sets offset to a multiple of limit (default 1) + --page-key string pagination page-key of evidence to query for +``` + +### REST + +Evidence REST API supports only queries of evidence. To submit evidence please use gRPC or the cli. + +### gRPC + +Evidence supports both querying and submitting transactions via gRPC + +#### Query + +[gRPC query](https://docs.cosmos.network/master/core/proto-docs.html#cosmos/evidence/v1beta1/query.proto) + +#### Tx + +[gRPC Tx](https://docs.cosmos.network/master/core/proto-docs.html#cosmos-evidence-v1beta1-tx-proto) + +View supported messages at [docs.cosmos.network/v0.40/modules/evidence](https://docs.cosmos.network/v0.40/modules/evidence/03_messages.html) diff --git a/x/evidence/atlas/manifest.toml b/x/evidence/atlas/manifest.toml new file mode 100644 index 000000000..e277c6802 --- /dev/null +++ b/x/evidence/atlas/manifest.toml @@ -0,0 +1,48 @@ +[module] +# Name of the module. (Required) +name = "x/evidence" + +# Description of the module. (Optional) +description = "The evidence module is responsible for storing and handling evidence of misbeaviour." + +# Link to where the module is located, it can also be a link to your project. (Optional) +homepage = "https://github.com/cosmos/cosmos-sdk" + +#List of key words describing your module (Optional) +keywords = [ + "evidence", + "misbeaviour", + "accountability" +] + + +[bug_tracker] +# A URL to a site that provides information or guidance on how to submit or deal +# with security vulnerabilities and bug reports. +url = "https://github.com/cosmos/cosmos-sdk/issues" + +# To list multiple authors, multiple [[authors]] need to be created +[[authors]] +# Name of one of the authors. Typically their Github name. (Required) +name = "alexanderbez" + +[[authors]] +name = "fedekunze" + +[[authors]] +name = "aaronc" + +[version] +# The repository field should be a URL to the source repository for your module. +# Typically, this will point to the specific GitHub repository release/tag for the +# module, although this is not enforced or required. (Required) +repo = "https://github.com/cosmos/cosmos-sdk" + +# The documentation field specifies a URL to a website hosting the module's documentation. (Optional) +documentation = "https://raw.githubusercontent.com/cosmos/cosmos-sdk/master/x/evidence/atlas/atlas-v0.41.x.md" + +# The module version to be published. (Required) +version = "v0.41" + +# An optional Cosmos SDK version compatibility may be provided. (Optional) +sdk_compat = "v0.41" diff --git a/x/evidence/keeper/keeper_test.go b/x/evidence/keeper/keeper_test.go index 916573046..6f2b071be 100644 --- a/x/evidence/keeper/keeper_test.go +++ b/x/evidence/keeper/keeper_test.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -14,7 +16,6 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/keeper" "github.com/cosmos/cosmos-sdk/x/evidence/types" @@ -130,11 +131,10 @@ func (suite *KeeperTestSuite) populateValidators(ctx sdk.Context) { // add accounts and set total supply totalSupplyAmt := initAmt.MulRaw(int64(len(valAddresses))) totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, totalSupplyAmt)) - suite.app.BankKeeper.SetSupply(ctx, banktypes.NewSupply(totalSupply)) + suite.NoError(suite.app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, totalSupply)) for _, addr := range valAddresses { - err := suite.app.BankKeeper.AddCoins(ctx, sdk.AccAddress(addr), initCoins) - suite.NoError(err) + suite.NoError(suite.app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, (sdk.AccAddress)(addr), initCoins)) } } diff --git a/x/evidence/module.go b/x/evidence/module.go index 9143e208e..204cb2635 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -189,8 +189,6 @@ func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.Val return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the evidence module. diff --git a/x/evidence/types/query.pb.gw.go b/x/evidence/types/query.pb.gw.go index e15d16352..6f85fd6f1 100644 --- a/x/evidence/types/query.pb.gw.go +++ b/x/evidence/types/query.pb.gw.go @@ -252,9 +252,9 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Evidence_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 1, 1, 0, 4, 1, 5, 3}, []string{"cosmos", "evidence", "v1beta1", "evidence_hash"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Evidence_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 1, 1, 0, 4, 1, 5, 3}, []string{"cosmos", "evidence", "v1beta1", "evidence_hash"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_AllEvidence_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 1}, []string{"cosmos", "evidence", "v1beta1"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_AllEvidence_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 1}, []string{"cosmos", "evidence", "v1beta1"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/feegrant/ante/fee_test.go b/x/feegrant/ante/fee_test.go index fe7bc4208..1a924db6a 100644 --- a/x/feegrant/ante/fee_test.go +++ b/x/feegrant/ante/fee_test.go @@ -56,7 +56,7 @@ func (suite *AnteTestSuite) SetupTest(isCheckTx bool) { } func (suite *AnteTestSuite) TestDeductFeesNoDelegation() { - suite.SetupTest(true) + suite.SetupTest(false) // setup app, ctx := suite.app, suite.ctx @@ -77,17 +77,15 @@ func (suite *AnteTestSuite) TestDeductFeesNoDelegation() { priv5, _, addr5 := testdata.KeyTestPubAddr() // Set addr1 with insufficient funds - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - app.AccountKeeper.SetAccount(ctx, acc1) - app.BankKeeper.SetBalances(ctx, addr1, []sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(10))}) + err := simapp.FundAccount(suite.app, suite.ctx, addr1, []sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(10))}) + suite.Require().NoError(err) // Set addr2 with more funds - acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - app.AccountKeeper.SetAccount(ctx, acc2) - app.BankKeeper.SetBalances(ctx, addr2, []sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(99999))}) + err = simapp.FundAccount(suite.app, suite.ctx, addr2, []sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(99999))}) + suite.Require().NoError(err) // grant fee allowance from `addr2` to `addr3` (plenty to pay) - err := app.FeeGrantKeeper.GrantFeeAllowance(ctx, addr2, addr3, &types.BasicFeeAllowance{ + err = app.FeeGrantKeeper.GrantFeeAllowance(ctx, addr2, addr3, &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 500)), }) suite.Require().NoError(err) diff --git a/x/feegrant/client/cli/cli_test.go b/x/feegrant/client/cli/cli_test.go index df4ac286f..66827e614 100644 --- a/x/feegrant/client/cli/cli_test.go +++ b/x/feegrant/client/cli/cli_test.go @@ -573,7 +573,7 @@ func (s *IntegrationTestSuite) TestTxWithFeeGrant() { granter := val.Address // creating an account manually (This account won't be exist in state) - info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) s.Require().NoError(err) grantee := sdk.AccAddress(info.GetPubKey().Address()) diff --git a/x/feegrant/client/cli/tx.go b/x/feegrant/client/cli/tx.go index a28ddb976..49702e0c7 100644 --- a/x/feegrant/client/cli/tx.go +++ b/x/feegrant/client/cli/tx.go @@ -149,8 +149,8 @@ Examples: } svcMsgClientConn := &msgservice.ServiceMsgClientConn{} - feeGrantMsgClient := types.NewMsgClient(svcMsgClientConn) - _, err = feeGrantMsgClient.GrantFeeAllowance(cmd.Context(), msg) + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.GrantFeeAllowance(cmd.Context(), msg) if err != nil { return err } @@ -196,8 +196,8 @@ Example: msg := types.NewMsgRevokeFeeAllowance(clientCtx.GetFromAddress(), grantee) svcMsgClientConn := &msgservice.ServiceMsgClientConn{} - feeGrantMsgClient := types.NewMsgClient(svcMsgClientConn) - _, err = feeGrantMsgClient.RevokeFeeAllowance(cmd.Context(), &msg) + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.RevokeFeeAllowance(cmd.Context(), &msg) if err != nil { return err } diff --git a/x/feegrant/client/rest/grpc_query_test.go b/x/feegrant/client/rest/grpc_query_test.go index 7b665ee91..5dac57a8f 100644 --- a/x/feegrant/client/rest/grpc_query_test.go +++ b/x/feegrant/client/rest/grpc_query_test.go @@ -36,7 +36,7 @@ func (s *IntegrationTestSuite) SetupSuite() { val := s.network.Validators[0] // Create new account in the keyring. - info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) s.Require().NoError(err) newAddr := sdk.AccAddress(info.GetPubKey().Address()) diff --git a/x/feegrant/module.go b/x/feegrant/module.go index 31b08e6ba..a6f1f4f62 100644 --- a/x/feegrant/module.go +++ b/x/feegrant/module.go @@ -179,8 +179,6 @@ func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.Validato return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the feegrant module. diff --git a/x/feegrant/simulation/operations_test.go b/x/feegrant/simulation/operations_test.go index 7c2593a53..85db81f2c 100644 --- a/x/feegrant/simulation/operations_test.go +++ b/x/feegrant/simulation/operations_test.go @@ -37,19 +37,15 @@ func (suite *SimTestSuite) SetupTest() { } func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Account { - app, ctx := suite.app, suite.ctx accounts := simtypes.RandomAccounts(r, n) - require := suite.Require() initAmt := sdk.TokensFromConsensusPower(200) initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt)) // add coins to the accounts for _, account := range accounts { - acc := app.AccountKeeper.NewAccountWithAddress(ctx, account.Address) - app.AccountKeeper.SetAccount(ctx, acc) - err := app.BankKeeper.SetBalances(ctx, account.Address, initCoins) - require.NoError(err) + err := simapp.FundAccount(suite.app, suite.ctx, account.Address, initCoins) + suite.Require().NoError(err) } return accounts diff --git a/x/feegrant/types/query.pb.gw.go b/x/feegrant/types/query.pb.gw.go index 499e4a441..ef81bab40 100644 --- a/x/feegrant/types/query.pb.gw.go +++ b/x/feegrant/types/query.pb.gw.go @@ -310,9 +310,9 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_FeeAllowance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "feegrant", "v1beta1", "fee_allowance", "granter", "grantee"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_FeeAllowance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "feegrant", "v1beta1", "fee_allowance", "granter", "grantee"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_FeeAllowances_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "feegrant", "v1beta1", "fee_allowances", "grantee"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_FeeAllowances_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "feegrant", "v1beta1", "fee_allowances", "grantee"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/genaccounts/legacy/v034/types.go b/x/genaccounts/legacy/v034/types.go index 080b534d8..f4bb73fa3 100644 --- a/x/genaccounts/legacy/v034/types.go +++ b/x/genaccounts/legacy/v034/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v034 import ( diff --git a/x/genaccounts/legacy/v036/types.go b/x/genaccounts/legacy/v036/types.go index 8d143e69d..e91db341d 100644 --- a/x/genaccounts/legacy/v036/types.go +++ b/x/genaccounts/legacy/v036/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v036 import ( diff --git a/x/genutil/gentx_test.go b/x/genutil/gentx_test.go index b4b843bc9..79c5bad39 100644 --- a/x/genutil/gentx_test.go +++ b/x/genutil/gentx_test.go @@ -64,9 +64,7 @@ func (suite *GenTxTestSuite) setAccountBalance(addr sdk.AccAddress, amount int64 acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - err := suite.app.BankKeeper.SetBalances( - suite.ctx, addr, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 25)}, - ) + err := simapp.FundAccount(suite.app, suite.ctx, addr, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 25)}) suite.Require().NoError(err) bankGenesisState := suite.app.BankKeeper.ExportGenesis(suite.ctx) diff --git a/x/genutil/module.go b/x/genutil/module.go index 7a65531e4..ebfb70c4a 100644 --- a/x/genutil/module.go +++ b/x/genutil/module.go @@ -66,8 +66,6 @@ func (AppModuleBasic) GetTxCmd() *cobra.Command { return nil } // GetQueryCmd returns no root query command for the genutil module. func (AppModuleBasic) GetQueryCmd() *cobra.Command { return nil } -//____________________________________________________________________________ - // AppModule implements an application module for the genutil module. type AppModule struct { AppModuleBasic diff --git a/x/gov/abci_test.go b/x/gov/abci_test.go index bfda1b359..cefd12eed 100644 --- a/x/gov/abci_test.go +++ b/x/gov/abci_test.go @@ -307,7 +307,7 @@ func TestProposalPassedEndblocker(t *testing.T) { deposits := initialModuleAccCoins.Add(proposal.TotalDeposit...).Add(proposalCoins...) require.True(t, moduleAccCoins.IsEqual(deposits)) - err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.OptionYes) + err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)) require.NoError(t, err) newHeader := ctx.BlockHeader() @@ -348,7 +348,7 @@ func TestEndBlockerProposalHandlerFailed(t *testing.T) { handleAndCheck(t, gov.NewHandler(app.GovKeeper), ctx, newDepositMsg) - err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.OptionYes) + err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)) require.NoError(t, err) newHeader := ctx.BlockHeader() diff --git a/x/gov/client/cli/cli_test.go b/x/gov/client/cli/cli_test.go index 981b13024..54bcdde07 100644 --- a/x/gov/client/cli/cli_test.go +++ b/x/gov/client/cli/cli_test.go @@ -61,6 +61,18 @@ func (s *IntegrationTestSuite) SetupSuite() { s.Require().NoError(err) _, err = s.network.WaitForHeight(1) s.Require().NoError(err) + + // create a proposal3 with deposit + _, err = govtestutil.MsgSubmitProposal(val.ClientCtx, val.Address.String(), + "Text Proposal 3", "Where is the title!?", types.ProposalTypeText, + fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens).String())) + s.Require().NoError(err) + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) + + // vote for proposal3 as val + _, err = govtestutil.MsgVote(val.ClientCtx, val.Address.String(), "3", "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05") + s.Require().NoError(err) } func (s *IntegrationTestSuite) TearDownSuite() { @@ -436,7 +448,7 @@ func (s *IntegrationTestSuite) TestCmdGetProposals() { var proposals types.QueryProposalsResponse s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &proposals), out.String()) - s.Require().Len(proposals.Proposals, 2) + s.Require().Len(proposals.Proposals, 3) } }) } @@ -677,9 +689,10 @@ func (s *IntegrationTestSuite) TestCmdQueryVote() { val := s.network.Validators[0] testCases := []struct { - name string - args []string - expectErr bool + name string + args []string + expectErr bool + expVoteOptions types.WeightedVoteOptions }{ { "get vote of non existing proposal", @@ -688,6 +701,7 @@ func (s *IntegrationTestSuite) TestCmdQueryVote() { val.Address.String(), }, true, + types.NewNonSplitVoteOption(types.OptionYes), }, { "get vote by wrong voter", @@ -696,6 +710,7 @@ func (s *IntegrationTestSuite) TestCmdQueryVote() { "wrong address", }, true, + types.NewNonSplitVoteOption(types.OptionYes), }, { "vote for valid proposal", @@ -705,6 +720,22 @@ func (s *IntegrationTestSuite) TestCmdQueryVote() { fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, false, + types.NewNonSplitVoteOption(types.OptionYes), + }, + { + "split vote for valid proposal", + []string{ + "3", + val.Address.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + types.WeightedVoteOptions{ + types.WeightedVoteOption{Option: types.OptionYes, Weight: sdk.NewDecWithPrec(60, 2)}, + types.WeightedVoteOption{Option: types.OptionNo, Weight: sdk.NewDecWithPrec(30, 2)}, + types.WeightedVoteOption{Option: types.OptionAbstain, Weight: sdk.NewDecWithPrec(5, 2)}, + types.WeightedVoteOption{Option: types.OptionNoWithVeto, Weight: sdk.NewDecWithPrec(5, 2)}, + }, }, } @@ -723,7 +754,11 @@ func (s *IntegrationTestSuite) TestCmdQueryVote() { var vote types.Vote s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &vote), out.String()) - s.Require().Equal(types.OptionYes, vote.Option) + s.Require().Equal(len(vote.Options), len(tc.expVoteOptions)) + for i, option := range tc.expVoteOptions { + s.Require().Equal(option.Option, vote.Options[i].Option) + s.Require().True(option.Weight.Equal(vote.Options[i].Weight)) + } } }) } @@ -789,6 +824,90 @@ func (s *IntegrationTestSuite) TestNewCmdVote() { } } +func (s *IntegrationTestSuite) TestNewCmdWeightedVote() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + expectedCode uint32 + }{ + { + "invalid vote", + []string{}, + true, 0, + }, + { + "vote for invalid proposal", + []string{ + "10", + fmt.Sprintf("%s", "yes"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + false, 2, + }, + { + "valid vote", + []string{ + "1", + fmt.Sprintf("%s", "yes"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + false, 0, + }, + { + "invalid valid split vote string", + []string{ + "1", + fmt.Sprintf("%s", "yes/0.6,no/0.3,abstain/0.05,no_with_veto/0.05"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + true, 0, + }, + { + "valid split vote", + []string{ + "1", + fmt.Sprintf("%s", "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + false, 0, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.NewCmdWeightedVote() + clientCtx := val.ClientCtx + var txResp sdk.TxResponse + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txResp), out.String()) + s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) + } + }) + } +} + func TestIntegrationTestSuite(t *testing.T) { suite.Run(t, new(IntegrationTestSuite)) } diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 16ca7f7c6..8ea8f0023 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/version" govutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/cosmos/cosmos-sdk/x/gov/types" @@ -68,6 +69,7 @@ func NewTxCmd(propCmds []*cobra.Command) *cobra.Command { govTxCmd.AddCommand( NewCmdDeposit(), NewCmdVote(), + NewCmdWeightedVote(), cmdSubmitProp, ) @@ -125,11 +127,14 @@ $ %s tx gov submit-proposal --title="Test Proposal" --description="My awesome pr return fmt.Errorf("invalid message: %w", err) } - if err = msg.ValidateBasic(); err != nil { - return fmt.Errorf("message validation failed: %w", err) + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.SubmitProposal(cmd.Context(), msg) + if err != nil { + return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -181,12 +186,14 @@ $ %s tx gov deposit 1 10stake --from mykey } msg := types.NewMsgDeposit(from, proposalID, amount) - err = msg.ValidateBasic() + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.Deposit(cmd.Context(), msg) if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -205,7 +212,6 @@ func NewCmdVote() *cobra.Command { fmt.Sprintf(`Submit a vote for an active proposal. You can find the proposal-id by running "%s query gov proposals". - Example: $ %s tx gov vote 1 yes --from mykey `, @@ -234,6 +240,61 @@ $ %s tx gov vote 1 yes --from mykey // Build vote message and run basic validation msg := types.NewMsgVote(from, proposalID, byteVoteOption) + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.Vote(cmd.Context(), msg) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// NewCmdWeightedVote implements creating a new weighted vote command. +func NewCmdWeightedVote() *cobra.Command { + cmd := &cobra.Command{ + Use: "weighted-vote [proposal-id] [weighted-options]", + Args: cobra.ExactArgs(2), + Short: "Vote for an active proposal, options: yes/no/no_with_veto/abstain", + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a vote for an active proposal. You can +find the proposal-id by running "%s query gov proposals". + +Example: +$ %s tx gov vote 1 yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05 --from mykey +`, + version.AppName, version.AppName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + // Get voter address + from := clientCtx.GetFromAddress() + + // validate that the proposal id is a uint + proposalID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0]) + } + + // Figure out which vote options user chose + options, err := types.WeightedVoteOptionsFromString(govutils.NormalizeWeightedVoteOptions(args[1])) + if err != nil { + return err + } + + // Build vote message and run basic validation + msg := types.NewMsgVoteWeighted(from, proposalID, options) err = msg.ValidateBasic() if err != nil { return err diff --git a/x/gov/client/rest/grpc_query_test.go b/x/gov/client/rest/grpc_query_test.go index 8e2d4efa9..9e4984a9a 100644 --- a/x/gov/client/rest/grpc_query_test.go +++ b/x/gov/client/rest/grpc_query_test.go @@ -57,6 +57,18 @@ func (s *IntegrationTestSuite) SetupSuite() { s.Require().NoError(err) _, err = s.network.WaitForHeight(1) s.Require().NoError(err) + + // create a proposal3 with deposit + _, err = govtestutil.MsgSubmitProposal(val.ClientCtx, val.Address.String(), + "Text Proposal 3", "Where is the title!?", types.ProposalTypeText, + fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens).String())) + s.Require().NoError(err) + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) + + // vote for proposal3 as val + _, err = govtestutil.MsgVote(val.ClientCtx, val.Address.String(), "3", "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05") + s.Require().NoError(err) } func (s *IntegrationTestSuite) TestGetProposalGRPC() { @@ -141,7 +153,7 @@ func (s *IntegrationTestSuite) TestGetProposalsGRPC() { s.Require().Empty(proposals.Proposals) } else { s.Require().NoError(err) - s.Require().Len(proposals.Proposals, 2) + s.Require().Len(proposals.Proposals, 3) } }) } @@ -153,29 +165,45 @@ func (s *IntegrationTestSuite) TestGetProposalVoteGRPC() { voterAddressBech32 := val.Address.String() testCases := []struct { - name string - url string - expErr bool + name string + url string + expErr bool + expVoteOptions types.WeightedVoteOptions }{ { "empty proposal", fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "", voterAddressBech32), true, + types.NewNonSplitVoteOption(types.OptionYes), }, { "get non existing proposal", fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "10", voterAddressBech32), true, + types.NewNonSplitVoteOption(types.OptionYes), }, { "get proposal with wrong voter address", fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "1", "wrongVoterAddress"), true, + types.NewNonSplitVoteOption(types.OptionYes), }, { "get proposal with id", fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "1", voterAddressBech32), false, + types.NewNonSplitVoteOption(types.OptionYes), + }, + { + "get proposal with id for split vote", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "3", voterAddressBech32), + false, + types.WeightedVoteOptions{ + types.WeightedVoteOption{Option: types.OptionYes, Weight: sdk.NewDecWithPrec(60, 2)}, + types.WeightedVoteOption{Option: types.OptionNo, Weight: sdk.NewDecWithPrec(30, 2)}, + types.WeightedVoteOption{Option: types.OptionAbstain, Weight: sdk.NewDecWithPrec(5, 2)}, + types.WeightedVoteOption{Option: types.OptionNoWithVeto, Weight: sdk.NewDecWithPrec(5, 2)}, + }, }, } @@ -193,7 +221,11 @@ func (s *IntegrationTestSuite) TestGetProposalVoteGRPC() { } else { s.Require().NoError(err) s.Require().NotEmpty(vote.Vote) - s.Require().Equal(types.OptionYes, vote.Vote.Option) + s.Require().Equal(len(vote.Vote.Options), len(tc.expVoteOptions)) + for i, option := range tc.expVoteOptions { + s.Require().Equal(option.Option, vote.Vote.Options[i].Option) + s.Require().True(option.Weight.Equal(vote.Vote.Options[i].Weight)) + } } }) } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index f11798e96..46d66c240 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -12,7 +12,6 @@ import ( ) // REST Variable names -// nolint const ( RestParamsType = "type" RestProposalID = "proposal-id" diff --git a/x/gov/client/rest/rest_test.go b/x/gov/client/rest/rest_test.go index 0b1a27402..adca107ad 100644 --- a/x/gov/client/rest/rest_test.go +++ b/x/gov/client/rest/rest_test.go @@ -5,6 +5,7 @@ package rest_test import ( "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/gov/types" ) @@ -57,7 +58,7 @@ func (s *IntegrationTestSuite) TestLegacyGetVote() { s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(resp.Result, &vote)) s.Require().Equal(val.Address.String(), vote.Voter) // Note that option is now an int. - s.Require().Equal(types.VoteOption(1), vote.Option) + s.Require().Equal([]types.WeightedVoteOption{{types.VoteOption(1), sdk.NewDec(1)}}, vote.Options) } }) } diff --git a/x/gov/client/testutil/helpers.go b/x/gov/client/testutil/helpers.go index 535cb8d09..ce9d5de72 100644 --- a/x/gov/client/testutil/helpers.go +++ b/x/gov/client/testutil/helpers.go @@ -41,5 +41,5 @@ func MsgVote(clientCtx client.Context, from, id, vote string, extraArgs ...strin args = append(args, extraArgs...) - return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdVote(), args) + return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdWeightedVote(), args) } diff --git a/x/gov/client/utils/query.go b/x/gov/client/utils/query.go index e543f526b..de5f5f7c5 100644 --- a/x/gov/client/utils/query.go +++ b/x/gov/client/utils/query.go @@ -37,14 +37,19 @@ func (p Proposer) String() string { // NOTE: SearchTxs is used to facilitate the txs query which does not currently // support configurable pagination. func QueryDepositsByTxQuery(clientCtx client.Context, params types.QueryProposalParams) ([]byte, error) { - events := []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgDeposit), - fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalDeposit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), - } - - // NOTE: SearchTxs is used to facilitate the txs query which does not currently - // support configurable pagination. - searchResult, err := authclient.QueryTxsByEvents(clientCtx, events, defaultPage, defaultLimit, "") + searchResult, err := combineEvents( + clientCtx, defaultPage, + // Query old Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgDeposit), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalDeposit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + }, + // Query service Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgDeposit), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalDeposit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + }, + ) if err != nil { return nil, err } @@ -53,9 +58,14 @@ func QueryDepositsByTxQuery(clientCtx client.Context, params types.QueryProposal for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { - if msg.Type() == types.TypeMsgDeposit { - depMsg := msg.(*types.MsgDeposit) + var depMsg *types.MsgDeposit + if msg.Type() == types.TypeSvcMsgDeposit { + depMsg = msg.(sdk.ServiceMsg).Request.(*types.MsgDeposit) + } else if protoDepMsg, ok := msg.(*types.MsgDeposit); ok { + depMsg = protoDepMsg + } + if depMsg != nil { deposits = append(deposits, types.Deposit{ Depositor: depMsg.Depositor, ProposalId: params.ProposalID, @@ -78,30 +88,70 @@ func QueryDepositsByTxQuery(clientCtx client.Context, params types.QueryProposal // marshalled result or any error that occurred. func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVotesParams) ([]byte, error) { var ( - events = []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote), - fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), - } votes []types.Vote nextTxPage = defaultPage totalLimit = params.Limit * params.Page ) + // query interrupted either if we collected enough votes or tx indexer run out of relevant txs for len(votes) < totalLimit { - searchResult, err := authclient.QueryTxsByEvents(clientCtx, events, nextTxPage, defaultLimit, "") + // Search for both (old) votes and weighted votes. + searchResult, err := combineEvents( + clientCtx, nextTxPage, + // Query old Vote Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + }, + // Query Vote service Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgVote), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + }, + // Query old VoteWeighted Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVoteWeighted), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + }, + // Query VoteWeighted service Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgVoteWeighted), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + }, + ) if err != nil { return nil, err } - nextTxPage++ + for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { - if msg.Type() == types.TypeMsgVote { - voteMsg := msg.(*types.MsgVote) + var voteMsg *types.MsgVote + if msg.Type() == types.TypeSvcMsgVote { + voteMsg = msg.(sdk.ServiceMsg).Request.(*types.MsgVote) + } else if protoVoteMsg, ok := msg.(*types.MsgVote); ok { + voteMsg = protoVoteMsg + } + if voteMsg != nil { votes = append(votes, types.Vote{ Voter: voteMsg.Voter, ProposalId: params.ProposalID, - Option: voteMsg.Option, + Options: types.NewNonSplitVoteOption(voteMsg.Option), + }) + } + + var voteWeightedMsg *types.MsgVoteWeighted + if msg.Type() == types.TypeSvcMsgVoteWeighted { + voteWeightedMsg = msg.(sdk.ServiceMsg).Request.(*types.MsgVoteWeighted) + } else if protoVoteWeightedMsg, ok := msg.(*types.MsgVoteWeighted); ok { + voteWeightedMsg = protoVoteWeightedMsg + } + + if voteWeightedMsg != nil { + votes = append(votes, types.Vote{ + Voter: voteWeightedMsg.Voter, + ProposalId: params.ProposalID, + Options: voteWeightedMsg.Options, }) } } @@ -109,6 +159,8 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot if len(searchResult.Txs) != defaultLimit { break } + + nextTxPage++ } start, end := client.Paginate(len(votes), params.Page, params.Limit, 100) if start < 0 || end < 0 { @@ -127,28 +179,67 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot // QueryVoteByTxQuery will query for a single vote via a direct txs tags query. func QueryVoteByTxQuery(clientCtx client.Context, params types.QueryVoteParams) ([]byte, error) { - events := []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote), - fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Voter.String())), - } - - // NOTE: SearchTxs is used to facilitate the txs query which does not currently - // support configurable pagination. - searchResult, err := authclient.QueryTxsByEvents(clientCtx, events, defaultPage, defaultLimit, "") + searchResult, err := combineEvents( + clientCtx, defaultPage, + // Query old Vote Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Voter.String())), + }, + // Query Vote service Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgVote), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Voter.String())), + }, + // Query old VoteWeighted Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVoteWeighted), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Voter.String())), + }, + // Query VoteWeighted service Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgVoteWeighted), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Voter.String())), + }, + ) if err != nil { return nil, err } + for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { + var voteMsg *types.MsgVote // there should only be a single vote under the given conditions - if msg.Type() == types.TypeMsgVote { - voteMsg := msg.(*types.MsgVote) + if msg.Type() == types.TypeSvcMsgVote { + voteMsg = msg.(sdk.ServiceMsg).Request.(*types.MsgVote) + } else if protoVoteMsg, ok := msg.(*types.MsgVote); ok { + voteMsg = protoVoteMsg + } + + if voteMsg != nil { + vote := types.Vote{ + Voter: voteMsg.Voter, + ProposalId: params.ProposalID, + Options: types.NewNonSplitVoteOption(voteMsg.Option), + } + + bz, err := clientCtx.JSONMarshaler.MarshalJSON(&vote) + if err != nil { + return nil, err + } + + return bz, nil + } else if msg.Type() == types.TypeMsgVoteWeighted { + voteMsg := msg.(*types.MsgVoteWeighted) vote := types.Vote{ Voter: voteMsg.Voter, ProposalId: params.ProposalID, - Option: voteMsg.Option, + Options: voteMsg.Options, } bz, err := clientCtx.JSONMarshaler.MarshalJSON(&vote) @@ -167,25 +258,36 @@ func QueryVoteByTxQuery(clientCtx client.Context, params types.QueryVoteParams) // QueryDepositByTxQuery will query for a single deposit via a direct txs tags // query. func QueryDepositByTxQuery(clientCtx client.Context, params types.QueryDepositParams) ([]byte, error) { - events := []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgDeposit), - fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalDeposit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Depositor.String())), - } - - // NOTE: SearchTxs is used to facilitate the txs query which does not currently - // support configurable pagination. - searchResult, err := authclient.QueryTxsByEvents(clientCtx, events, defaultPage, defaultLimit, "") + searchResult, err := combineEvents( + clientCtx, defaultPage, + // Query old Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgDeposit), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalDeposit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Depositor.String())), + }, + // Query service Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgDeposit), + fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalDeposit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Depositor.String())), + }, + ) if err != nil { return nil, err } for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { + var depMsg *types.MsgDeposit // there should only be a single deposit under the given conditions - if msg.Type() == types.TypeMsgDeposit { - depMsg := msg.(*types.MsgDeposit) + if msg.Type() == types.TypeSvcMsgDeposit { + depMsg = msg.(sdk.ServiceMsg).Request.(*types.MsgDeposit) + } else if protoDepMsg, ok := msg.(*types.MsgDeposit); ok { + depMsg = protoDepMsg + } + if depMsg != nil { deposit := types.Deposit{ Depositor: depMsg.Depositor, ProposalId: params.ProposalID, @@ -208,14 +310,20 @@ func QueryDepositByTxQuery(clientCtx client.Context, params types.QueryDepositPa // QueryProposerByTxQuery will query for a proposer of a governance proposal by // ID. func QueryProposerByTxQuery(clientCtx client.Context, proposalID uint64) (Proposer, error) { - events := []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgSubmitProposal), - fmt.Sprintf("%s.%s='%s'", types.EventTypeSubmitProposal, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", proposalID))), - } - - // NOTE: SearchTxs is used to facilitate the txs query which does not currently - // support configurable pagination. - searchResult, err := authclient.QueryTxsByEvents(clientCtx, events, defaultPage, defaultLimit, "") + searchResult, err := combineEvents( + clientCtx, + defaultPage, + // Query old Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgSubmitProposal), + fmt.Sprintf("%s.%s='%s'", types.EventTypeSubmitProposal, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", proposalID))), + }, + // Query service Msgs + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgSubmitProposal), + fmt.Sprintf("%s.%s='%s'", types.EventTypeSubmitProposal, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", proposalID))), + }, + ) if err != nil { return Proposer{}, err } @@ -223,8 +331,13 @@ func QueryProposerByTxQuery(clientCtx client.Context, proposalID uint64) (Propos for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { // there should only be a single proposal under the given conditions - if msg.Type() == types.TypeMsgSubmitProposal { - subMsg := msg.(*types.MsgSubmitProposal) + if msg.Type() == types.TypeSvcMsgSubmitProposal { + subMsg := msg.(sdk.ServiceMsg).Request.(*types.MsgSubmitProposal) + + return NewProposer(proposalID, subMsg.Proposer), nil + } else if protoSubMsg, ok := msg.(*types.MsgSubmitProposal); ok { + subMsg := protoSubMsg + return NewProposer(proposalID, subMsg.Proposer), nil } } @@ -248,3 +361,25 @@ func QueryProposalByID(proposalID uint64, clientCtx client.Context, queryRoute s return res, err } + +// combineEvents queries txs by events with all events from each event group, +// and combines all those events together. +// +// Tx are indexed in tendermint via their Msgs `Type()`, which can be: +// - via legacy Msgs (amino or proto), their `Type()` is a custom string, +// - via ADR-031 service msgs, their `Type()` is the protobuf FQ method name. +// In searching for events, we search for both `Type()`s, and we use the +// `combineEvents` function here to merge events. +func combineEvents(clientCtx client.Context, page int, eventGroups ...[]string) (*sdk.SearchTxsResult, error) { + // Only the Txs field will be populated in the final SearchTxsResult. + allTxs := []*sdk.TxResponse{} + for _, events := range eventGroups { + res, err := authclient.QueryTxsByEvents(clientCtx, events, page, defaultLimit, "") + if err != nil { + return nil, err + } + allTxs = append(allTxs, res.Txs...) + } + + return &sdk.SearchTxsResult{Txs: allTxs}, nil +} diff --git a/x/gov/client/utils/query_test.go b/x/gov/client/utils/query_test.go index 81b15a457..fb7d002c8 100644 --- a/x/gov/client/utils/query_test.go +++ b/x/gov/client/utils/query_test.go @@ -2,6 +2,7 @@ package utils_test import ( "context" + "regexp" "testing" "github.com/stretchr/testify/require" @@ -10,15 +11,14 @@ import ( tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/cosmos/cosmos-sdk/x/gov/types" ) type TxSearchMock struct { + txConfig client.TxConfig mock.Client txs []tmtypes.Tx } @@ -32,12 +32,35 @@ func (mock TxSearchMock) TxSearch(ctx context.Context, query string, prove bool, *perPage = 0 } + // Get the `message.action` value from the query. + messageAction := regexp.MustCompile(`message\.action='(.*)' .*$`) + msgType := messageAction.FindStringSubmatch(query)[1] + + // Filter only the txs that match the query + matchingTxs := make([]tmtypes.Tx, 0) + for _, tx := range mock.txs { + sdkTx, err := mock.txConfig.TxDecoder()(tx) + if err != nil { + return nil, err + } + for _, msg := range sdkTx.GetMsgs() { + if msg.Type() == msgType { + matchingTxs = append(matchingTxs, tx) + break + } + } + } + start, end := client.Paginate(len(mock.txs), *page, *perPage, 100) if start < 0 || end < 0 { // nil result with nil error crashes utils.QueryTxsByEvents return &ctypes.ResultTxSearch{}, nil } - txs := mock.txs[start:end] + if len(matchingTxs) < end { + return &ctypes.ResultTxSearch{}, nil + } + + txs := matchingTxs[start:end] rst := &ctypes.ResultTxSearch{Txs: make([]*ctypes.ResultTx, len(txs)), TotalCount: len(txs)} for i := range txs { rst.Txs[i] = &ctypes.ResultTx{Tx: txs[i]} @@ -50,15 +73,9 @@ func (mock TxSearchMock) Block(ctx context.Context, height *int64) (*ctypes.Resu return &ctypes.ResultBlock{Block: &tmtypes.Block{}}, nil } -func newTestCodec() *codec.LegacyAmino { - cdc := codec.NewLegacyAmino() - sdk.RegisterLegacyAminoCodec(cdc) - types.RegisterLegacyAminoCodec(cdc) - authtypes.RegisterLegacyAminoCodec(cdc) - return cdc -} - func TestGetPaginatedVotes(t *testing.T) { + encCfg := simapp.MakeTestEncodingConfig() + type testCase struct { description string page, limit int @@ -75,7 +92,7 @@ func TestGetPaginatedVotes(t *testing.T) { } acc2Msgs := []sdk.Msg{ types.NewMsgVote(acc2, 0, types.OptionYes), - types.NewMsgVote(acc2, 0, types.OptionYes), + types.NewMsgVoteWeighted(acc2, 0, types.NewNonSplitVoteOption(types.OptionYes)), } for _, tc := range []testCase{ { @@ -87,8 +104,8 @@ func TestGetPaginatedVotes(t *testing.T) { acc2Msgs[:1], }, votes: []types.Vote{ - types.NewVote(0, acc1, types.OptionYes), - types.NewVote(0, acc2, types.OptionYes)}, + types.NewVote(0, acc1, types.NewNonSplitVoteOption(types.OptionYes)), + types.NewVote(0, acc2, types.NewNonSplitVoteOption(types.OptionYes))}, }, { description: "2MsgPerTx1Chunk", @@ -99,8 +116,9 @@ func TestGetPaginatedVotes(t *testing.T) { acc2Msgs, }, votes: []types.Vote{ - types.NewVote(0, acc1, types.OptionYes), - types.NewVote(0, acc1, types.OptionYes)}, + types.NewVote(0, acc1, types.NewNonSplitVoteOption(types.OptionYes)), + types.NewVote(0, acc1, types.NewNonSplitVoteOption(types.OptionYes)), + }, }, { description: "2MsgPerTx2Chunk", @@ -111,8 +129,9 @@ func TestGetPaginatedVotes(t *testing.T) { acc2Msgs, }, votes: []types.Vote{ - types.NewVote(0, acc2, types.OptionYes), - types.NewVote(0, acc2, types.OptionYes)}, + types.NewVote(0, acc2, types.NewNonSplitVoteOption(types.OptionYes)), + types.NewVote(0, acc2, types.NewNonSplitVoteOption(types.OptionYes)), + }, }, { description: "IncompleteSearchTx", @@ -121,7 +140,7 @@ func TestGetPaginatedVotes(t *testing.T) { msgs: [][]sdk.Msg{ acc1Msgs[:1], }, - votes: []types.Vote{types.NewVote(0, acc1, types.OptionYes)}, + votes: []types.Vote{types.NewVote(0, acc1, types.NewNonSplitVoteOption(types.OptionYes))}, }, { description: "InvalidPage", @@ -142,17 +161,12 @@ func TestGetPaginatedVotes(t *testing.T) { tc := tc t.Run(tc.description, func(t *testing.T) { - var ( - marshalled = make([]tmtypes.Tx, len(tc.msgs)) - cdc = newTestCodec() - ) - - encodingConfig := simapp.MakeTestEncodingConfig() - cli := TxSearchMock{txs: marshalled} + var marshalled = make([]tmtypes.Tx, len(tc.msgs)) + cli := TxSearchMock{txs: marshalled, txConfig: encCfg.TxConfig} clientCtx := client.Context{}. - WithLegacyAmino(cdc). + WithLegacyAmino(encCfg.Amino). WithClient(cli). - WithTxConfig(encodingConfig.TxConfig) + WithTxConfig(encCfg.TxConfig) for i := range tc.msgs { txBuilder := clientCtx.TxConfig.NewTxBuilder() diff --git a/x/gov/client/utils/utils.go b/x/gov/client/utils/utils.go index 84c2884d1..c88be3df3 100644 --- a/x/gov/client/utils/utils.go +++ b/x/gov/client/utils/utils.go @@ -1,6 +1,10 @@ package utils -import "github.com/cosmos/cosmos-sdk/x/gov/types" +import ( + "strings" + + "github.com/cosmos/cosmos-sdk/x/gov/types" +) // NormalizeVoteOption - normalize user specified vote option func NormalizeVoteOption(option string) string { @@ -22,7 +26,21 @@ func NormalizeVoteOption(option string) string { } } -//NormalizeProposalType - normalize user specified proposal type +// NormalizeWeightedVoteOptions - normalize vote options param string +func NormalizeWeightedVoteOptions(options string) string { + newOptions := []string{} + for _, option := range strings.Split(options, ",") { + fields := strings.Split(option, "=") + fields[0] = NormalizeVoteOption(fields[0]) + if len(fields) < 2 { + fields = append(fields, "1") + } + newOptions = append(newOptions, strings.Join(fields, "=")) + } + return strings.Join(newOptions, ",") +} + +// NormalizeProposalType - normalize user specified proposal type. func NormalizeProposalType(proposalType string) string { switch proposalType { case "Text", "text": @@ -33,7 +51,7 @@ func NormalizeProposalType(proposalType string) string { } } -//NormalizeProposalStatus - normalize user specified proposal status +// NormalizeProposalStatus - normalize user specified proposal status. func NormalizeProposalStatus(status string) string { switch status { case "DepositPeriod", "deposit_period": diff --git a/x/gov/client/utils/utils_test.go b/x/gov/client/utils/utils_test.go new file mode 100644 index 000000000..0da66b3c2 --- /dev/null +++ b/x/gov/client/utils/utils_test.go @@ -0,0 +1,56 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNormalizeWeightedVoteOptions(t *testing.T) { + cases := map[string]struct { + options string + normalized string + }{ + "simple Yes": { + options: "Yes", + normalized: "VOTE_OPTION_YES=1", + }, + "simple yes": { + options: "yes", + normalized: "VOTE_OPTION_YES=1", + }, + "formal yes": { + options: "yes=1", + normalized: "VOTE_OPTION_YES=1", + }, + "half yes half no": { + options: "yes=0.5,no=0.5", + normalized: "VOTE_OPTION_YES=0.5,VOTE_OPTION_NO=0.5", + }, + "3 options": { + options: "Yes=0.5,No=0.4,NoWithVeto=0.1", + normalized: "VOTE_OPTION_YES=0.5,VOTE_OPTION_NO=0.4,VOTE_OPTION_NO_WITH_VETO=0.1", + }, + "zero weight option": { + options: "Yes=0.5,No=0.5,NoWithVeto=0", + normalized: "VOTE_OPTION_YES=0.5,VOTE_OPTION_NO=0.5,VOTE_OPTION_NO_WITH_VETO=0", + }, + "minus weight option": { + options: "Yes=0.5,No=0.6,NoWithVeto=-0.1", + normalized: "VOTE_OPTION_YES=0.5,VOTE_OPTION_NO=0.6,VOTE_OPTION_NO_WITH_VETO=-0.1", + }, + "empty options": { + options: "", + normalized: "=1", + }, + "not available option": { + options: "Yessss=1", + normalized: "Yessss=1", + }, + } + + for _, tc := range cases { + normalized := NormalizeWeightedVoteOptions(tc.options) + require.Equal(t, normalized, tc.normalized) + } +} diff --git a/x/gov/genesis.go b/x/gov/genesis.go index 553edbaa0..62a194d9c 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -41,14 +41,16 @@ func InitGenesis(ctx sdk.Context, ak types.AccountKeeper, bk types.BankKeeper, k k.SetProposal(ctx, proposal) } - // add coins if not provided on genesis - if bk.GetAllBalances(ctx, moduleAcc.GetAddress()).IsZero() { - if err := bk.SetBalances(ctx, moduleAcc.GetAddress(), totalDeposits); err != nil { - panic(err) - } - + // if account has zero balance it probably means it's not set, so we set it + balance := bk.GetAllBalances(ctx, moduleAcc.GetAddress()) + if balance.IsZero() { ak.SetModuleAccount(ctx, moduleAcc) } + + // check if total deposits equals balance, if it doesn't panic because there were export/import errors + if !balance.IsEqual(totalDeposits) { + panic(fmt.Sprintf("expected module account was %s but we got %s", balance.String(), totalDeposits.String())) + } } // ExportGenesis - output genesis parameters diff --git a/x/gov/genesis_test.go b/x/gov/genesis_test.go index 2fb3aea94..f504ef5a1 100644 --- a/x/gov/genesis_test.go +++ b/x/gov/genesis_test.go @@ -11,6 +11,7 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -111,6 +112,27 @@ func TestImportExportQueues(t *testing.T) { require.True(t, proposal2.Status == types.StatusRejected) } +func TestImportExportQueues_ErrorUnconsistentState(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + require.Panics(t, func() { + gov.InitGenesis(ctx, app.AccountKeeper, app.BankKeeper, app.GovKeeper, &types.GenesisState{ + Deposits: types.Deposits{ + { + 1234, + "me", + sdk.Coins{ + { + "stake", + sdk.NewInt(1234), + }, + }, + }, + }, + }) + }) +} + func TestEqualProposals(t *testing.T) { app := simapp.Setup(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) diff --git a/x/gov/handler.go b/x/gov/handler.go index 25d97bfab..6af451d3d 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -27,6 +27,10 @@ func NewHandler(k keeper.Keeper) sdk.Handler { res, err := msgServer.Vote(sdk.WrapSDKContext(ctx), msg) return sdk.WrapServiceResult(ctx, res, err) + case *types.MsgVoteWeighted: + res, err := msgServer.VoteWeighted(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + default: return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) } diff --git a/x/gov/keeper/grpc_query_test.go b/x/gov/keeper/grpc_query_test.go index dc330d734..4789f954e 100644 --- a/x/gov/keeper/grpc_query_test.go +++ b/x/gov/keeper/grpc_query_test.go @@ -183,7 +183,7 @@ func (suite *KeeperTestSuite) TestGRPCQueryProposals() { func() { testProposals[1].Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, testProposals[1]) - suite.Require().NoError(app.GovKeeper.AddVote(ctx, testProposals[1].ProposalId, addrs[0], types.OptionAbstain)) + suite.Require().NoError(app.GovKeeper.AddVote(ctx, testProposals[1].ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))) req = &types.QueryProposalsRequest{ Voter: addrs[0].String(), @@ -291,14 +291,14 @@ func (suite *KeeperTestSuite) TestGRPCQueryVote() { func() { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.OptionAbstain)) + suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))) req = &types.QueryVoteRequest{ ProposalId: proposal.ProposalId, Voter: addrs[0].String(), } - expRes = &types.QueryVoteResponse{Vote: types.NewVote(proposal.ProposalId, addrs[0], types.OptionAbstain)} + expRes = &types.QueryVoteResponse{Vote: types.NewVote(proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))} }, true, }, @@ -395,15 +395,15 @@ func (suite *KeeperTestSuite) TestGRPCQueryVotes() { app.GovKeeper.SetProposal(ctx, proposal) votes = []types.Vote{ - {proposal.ProposalId, addrs[0].String(), types.OptionAbstain}, - {proposal.ProposalId, addrs[1].String(), types.OptionYes}, + {proposal.ProposalId, addrs[0].String(), types.NewNonSplitVoteOption(types.OptionAbstain)}, + {proposal.ProposalId, addrs[1].String(), types.NewNonSplitVoteOption(types.OptionYes)}, } accAddr1, err1 := sdk.AccAddressFromBech32(votes[0].Voter) accAddr2, err2 := sdk.AccAddressFromBech32(votes[1].Voter) suite.Require().NoError(err1) suite.Require().NoError(err2) - suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, accAddr1, votes[0].Option)) - suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, accAddr2, votes[1].Option)) + suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, accAddr1, votes[0].Options)) + suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, accAddr2, votes[1].Options)) req = &types.QueryVotesRequest{ ProposalId: proposal.ProposalId, @@ -769,9 +769,9 @@ func (suite *KeeperTestSuite) TestGRPCQueryTally() { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.OptionYes)) - suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[1], types.OptionYes)) - suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[2], types.OptionYes)) + suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))) + suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[1], types.NewNonSplitVoteOption(types.OptionYes))) + suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[2], types.NewNonSplitVoteOption(types.OptionYes))) req = &types.QueryTallyResultRequest{ProposalId: proposal.ProposalId} diff --git a/x/gov/keeper/migrations.go b/x/gov/keeper/migrations.go new file mode 100644 index 000000000..c643c6dd7 --- /dev/null +++ b/x/gov/keeper/migrations.go @@ -0,0 +1,21 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + v042 "github.com/cosmos/cosmos-sdk/x/gov/legacy/v042" +) + +// Migrator is a struct for handling in-place store migrations. +type Migrator struct { + keeper Keeper +} + +// NewMigrator returns a new Migrator. +func NewMigrator(keeper Keeper) Migrator { + return Migrator{keeper: keeper} +} + +// Migrate1to2 migrates from version 1 to 2. +func (m Migrator) Migrate1to2(ctx sdk.Context) error { + return v042.MigrateStore(ctx, m.keeper.storeKey) +} diff --git a/x/gov/keeper/msg_server.go b/x/gov/keeper/msg_server.go index 39ddf3ab4..86e6e9326 100644 --- a/x/gov/keeper/msg_server.go +++ b/x/gov/keeper/msg_server.go @@ -65,7 +65,7 @@ func (k msgServer) Vote(goCtx context.Context, msg *types.MsgVote) (*types.MsgVo if accErr != nil { return nil, accErr } - err := k.Keeper.AddVote(ctx, msg.ProposalId, accAddr, msg.Option) + err := k.Keeper.AddVote(ctx, msg.ProposalId, accAddr, types.NewNonSplitVoteOption(msg.Option)) if err != nil { return nil, err } @@ -89,6 +89,36 @@ func (k msgServer) Vote(goCtx context.Context, msg *types.MsgVote) (*types.MsgVo return &types.MsgVoteResponse{}, nil } +func (k msgServer) VoteWeighted(goCtx context.Context, msg *types.MsgVoteWeighted) (*types.MsgVoteWeightedResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + accAddr, accErr := sdk.AccAddressFromBech32(msg.Voter) + if accErr != nil { + return nil, accErr + } + err := k.Keeper.AddVote(ctx, msg.ProposalId, accAddr, msg.Options) + if err != nil { + return nil, err + } + + defer telemetry.IncrCounterWithLabels( + []string{types.ModuleName, "vote"}, + 1, + []metrics.Label{ + telemetry.NewLabel("proposal_id", strconv.Itoa(int(msg.ProposalId))), + }, + ) + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Voter), + ), + ) + + return &types.MsgVoteWeightedResponse{}, nil +} + func (k msgServer) Deposit(goCtx context.Context, msg *types.MsgDeposit) (*types.MsgDepositResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) accAddr, err := sdk.AccAddressFromBech32(msg.Depositor) diff --git a/x/gov/keeper/proposal_test.go b/x/gov/keeper/proposal_test.go index 1a833737d..7f62d5a56 100644 --- a/x/gov/keeper/proposal_test.go +++ b/x/gov/keeper/proposal_test.go @@ -101,7 +101,7 @@ func TestGetProposalsFiltered(t *testing.T) { if i%2 == 0 { d := types.NewDeposit(proposalID, addr1, nil) - v := types.NewVote(proposalID, addr1, types.OptionYes) + v := types.NewVote(proposalID, addr1, types.NewNonSplitVoteOption(types.OptionYes)) app.GovKeeper.SetDeposit(ctx, d) app.GovKeeper.SetVote(ctx, v) } diff --git a/x/gov/keeper/querier_test.go b/x/gov/keeper/querier_test.go index 9960fb116..16f2694cd 100644 --- a/x/gov/keeper/querier_test.go +++ b/x/gov/keeper/querier_test.go @@ -251,13 +251,13 @@ func TestQueries(t *testing.T) { require.Equal(t, proposal3, proposals[1]) // Addrs[0] votes on proposals #2 & #3 - vote1 := types.NewVote(proposal2.ProposalId, TestAddrs[0], types.OptionYes) - vote2 := types.NewVote(proposal3.ProposalId, TestAddrs[0], types.OptionYes) + vote1 := types.NewVote(proposal2.ProposalId, TestAddrs[0], types.NewNonSplitVoteOption(types.OptionYes)) + vote2 := types.NewVote(proposal3.ProposalId, TestAddrs[0], types.NewNonSplitVoteOption(types.OptionYes)) app.GovKeeper.SetVote(ctx, vote1) app.GovKeeper.SetVote(ctx, vote2) // Addrs[1] votes on proposal #3 - vote3 := types.NewVote(proposal3.ProposalId, TestAddrs[1], types.OptionYes) + vote3 := types.NewVote(proposal3.ProposalId, TestAddrs[1], types.NewNonSplitVoteOption(types.OptionYes)) app.GovKeeper.SetVote(ctx, vote3) // Test query voted by TestAddrs[0] @@ -323,7 +323,7 @@ func TestPaginatedVotesQuery(t *testing.T) { vote := types.Vote{ ProposalId: proposal.ProposalId, Voter: addr.String(), - Option: types.OptionYes, + Options: types.NewNonSplitVoteOption(types.OptionYes), } votes[i] = vote app.GovKeeper.SetVote(ctx, vote) diff --git a/x/gov/keeper/tally.go b/x/gov/keeper/tally.go index cddfd5902..f1f1f3254 100644 --- a/x/gov/keeper/tally.go +++ b/x/gov/keeper/tally.go @@ -27,7 +27,7 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo validator.GetBondedTokens(), validator.GetDelegatorShares(), sdk.ZeroDec(), - types.OptionEmpty, + types.WeightedVoteOptions{}, ) return false @@ -43,7 +43,7 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo valAddrStr := sdk.ValAddress(voter.Bytes()).String() if val, ok := currValidators[valAddrStr]; ok { - val.Vote = vote.Option + val.Vote = vote.Options currValidators[valAddrStr] = val } @@ -60,7 +60,10 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo // delegation shares * bonded / total shares votingPower := delegation.GetShares().MulInt(val.BondedTokens).Quo(val.DelegatorShares) - results[vote.Option] = results[vote.Option].Add(votingPower) + for _, option := range vote.Options { + subPower := votingPower.Mul(option.Weight) + results[option.Option] = results[option.Option].Add(subPower) + } totalVotingPower = totalVotingPower.Add(votingPower) } @@ -73,14 +76,17 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo // iterate over the validators again to tally their voting power for _, val := range currValidators { - if val.Vote == types.OptionEmpty { + if len(val.Vote) == 0 { continue } sharesAfterDeductions := val.DelegatorShares.Sub(val.DelegatorDeductions) votingPower := sharesAfterDeductions.MulInt(val.BondedTokens).Quo(val.DelegatorShares) - results[val.Vote] = results[val.Vote].Add(votingPower) + for _, option := range val.Vote { + subPower := votingPower.Mul(option.Weight) + results[option.Option] = results[option.Option].Add(subPower) + } totalVotingPower = totalVotingPower.Add(votingPower) } diff --git a/x/gov/keeper/tally_test.go b/x/gov/keeper/tally_test.go index 77f468cd2..ef9cbd027 100644 --- a/x/gov/keeper/tally_test.go +++ b/x/gov/keeper/tally_test.go @@ -50,7 +50,7 @@ func TestTallyNoQuorum(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - err = app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes) + err = app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)) require.Nil(t, err) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) @@ -73,9 +73,9 @@ func TestTallyOnlyValidatorsAllYes(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionYes)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionYes))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -99,8 +99,8 @@ func TestTallyOnlyValidators51No(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionNo)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionNo))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -123,8 +123,8 @@ func TestTallyOnlyValidators51Yes(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionNo)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionYes)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionYes))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -148,9 +148,9 @@ func TestTallyOnlyValidatorsVetoed(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.OptionNoWithVeto)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.NewNonSplitVoteOption(types.OptionNoWithVeto))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -174,9 +174,9 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionAbstain)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionNo)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.OptionYes)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.NewNonSplitVoteOption(types.OptionYes))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -200,9 +200,9 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionAbstain)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.OptionNo)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.NewNonSplitVoteOption(types.OptionNo))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -227,8 +227,8 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr1, types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr2, types.NewNonSplitVoteOption(types.OptionNo))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -261,10 +261,10 @@ func TestTallyDelgatorOverride(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[3], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[4], types.OptionNo)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[3], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[4], types.NewNonSplitVoteOption(types.OptionNo))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -297,9 +297,9 @@ func TestTallyDelgatorInherit(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionNo)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionNo)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionYes)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionYes))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -336,10 +336,10 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[3], types.OptionNo)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[3], types.NewNonSplitVoteOption(types.OptionNo))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -378,9 +378,9 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionNo)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionNo)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionNo))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -421,9 +421,9 @@ func TestTallyJailedValidator(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionNo)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionNo)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionNo))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -454,9 +454,9 @@ func TestTallyValidatorMultipleDelegations(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionNo)) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.OptionYes)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionYes))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) diff --git a/x/gov/keeper/vote.go b/x/gov/keeper/vote.go index a35569bc3..b554826ac 100644 --- a/x/gov/keeper/vote.go +++ b/x/gov/keeper/vote.go @@ -9,7 +9,7 @@ import ( ) // AddVote adds a vote on a specific proposal -func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, option types.VoteOption) error { +func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, options types.WeightedVoteOptions) error { proposal, ok := keeper.GetProposal(ctx, proposalID) if !ok { return sdkerrors.Wrapf(types.ErrUnknownProposal, "%d", proposalID) @@ -18,17 +18,19 @@ func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.A return sdkerrors.Wrapf(types.ErrInactiveProposal, "%d", proposalID) } - if !types.ValidVoteOption(option) { - return sdkerrors.Wrap(types.ErrInvalidVote, option.String()) + for _, option := range options { + if !types.ValidWeightedVoteOption(option) { + return sdkerrors.Wrap(types.ErrInvalidVote, option.String()) + } } - vote := types.NewVote(proposalID, voterAddr, option) + vote := types.NewVote(proposalID, voterAddr, options) keeper.SetVote(ctx, vote) ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeProposalVote, - sdk.NewAttribute(types.AttributeKeyOption, option.String()), + sdk.NewAttribute(types.AttributeKeyOption, options.String()), sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposalID)), ), ) diff --git a/x/gov/keeper/vote_test.go b/x/gov/keeper/vote_test.go index 5f5bf0cc7..ae6ebc4a5 100644 --- a/x/gov/keeper/vote_test.go +++ b/x/gov/keeper/vote_test.go @@ -24,37 +24,52 @@ func TestVotes(t *testing.T) { var invalidOption types.VoteOption = 0x10 - require.Error(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes), "proposal not on voting period") - require.Error(t, app.GovKeeper.AddVote(ctx, 10, addrs[0], types.OptionYes), "invalid proposal ID") + require.Error(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)), "proposal not on voting period") + require.Error(t, app.GovKeeper.AddVote(ctx, 10, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)), "invalid proposal ID") proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.Error(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], invalidOption), "invalid option") + require.Error(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(invalidOption)), "invalid option") // Test first vote - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionAbstain)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))) vote, found := app.GovKeeper.GetVote(ctx, proposalID, addrs[0]) require.True(t, found) require.Equal(t, addrs[0].String(), vote.Voter) require.Equal(t, proposalID, vote.ProposalId) - require.Equal(t, types.OptionAbstain, vote.Option) + require.True(t, len(vote.Options) == 1) + require.Equal(t, types.OptionAbstain, vote.Options[0].Option) // Test change of vote - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))) vote, found = app.GovKeeper.GetVote(ctx, proposalID, addrs[0]) require.True(t, found) require.Equal(t, addrs[0].String(), vote.Voter) require.Equal(t, proposalID, vote.ProposalId) - require.Equal(t, types.OptionYes, vote.Option) + require.True(t, len(vote.Options) == 1) + require.Equal(t, types.OptionYes, vote.Options[0].Option) // Test second vote - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionNoWithVeto)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.WeightedVoteOptions{ + types.WeightedVoteOption{Option: types.OptionYes, Weight: sdk.NewDecWithPrec(60, 2)}, + types.WeightedVoteOption{Option: types.OptionNo, Weight: sdk.NewDecWithPrec(30, 2)}, + types.WeightedVoteOption{Option: types.OptionAbstain, Weight: sdk.NewDecWithPrec(5, 2)}, + types.WeightedVoteOption{Option: types.OptionNoWithVeto, Weight: sdk.NewDecWithPrec(5, 2)}, + })) vote, found = app.GovKeeper.GetVote(ctx, proposalID, addrs[1]) require.True(t, found) require.Equal(t, addrs[1].String(), vote.Voter) require.Equal(t, proposalID, vote.ProposalId) - require.Equal(t, types.OptionNoWithVeto, vote.Option) + require.True(t, len(vote.Options) == 4) + require.Equal(t, types.OptionYes, vote.Options[0].Option) + require.Equal(t, types.OptionNo, vote.Options[1].Option) + require.Equal(t, types.OptionAbstain, vote.Options[2].Option) + require.Equal(t, types.OptionNoWithVeto, vote.Options[3].Option) + require.True(t, vote.Options[0].Weight.Equal(sdk.NewDecWithPrec(60, 2))) + require.True(t, vote.Options[1].Weight.Equal(sdk.NewDecWithPrec(30, 2))) + require.True(t, vote.Options[2].Weight.Equal(sdk.NewDecWithPrec(5, 2))) + require.True(t, vote.Options[3].Weight.Equal(sdk.NewDecWithPrec(5, 2))) // Test vote iterator // NOTE order of deposits is determined by the addresses @@ -63,8 +78,13 @@ func TestVotes(t *testing.T) { require.Equal(t, votes, app.GovKeeper.GetVotes(ctx, proposalID)) require.Equal(t, addrs[0].String(), votes[0].Voter) require.Equal(t, proposalID, votes[0].ProposalId) - require.Equal(t, types.OptionYes, votes[0].Option) + require.True(t, len(votes[0].Options) == 1) + require.Equal(t, types.OptionYes, votes[0].Options[0].Option) require.Equal(t, addrs[1].String(), votes[1].Voter) require.Equal(t, proposalID, votes[1].ProposalId) - require.Equal(t, types.OptionNoWithVeto, votes[1].Option) + require.True(t, len(votes[1].Options) == 4) + require.True(t, votes[1].Options[0].Weight.Equal(sdk.NewDecWithPrec(60, 2))) + require.True(t, votes[1].Options[1].Weight.Equal(sdk.NewDecWithPrec(30, 2))) + require.True(t, votes[1].Options[2].Weight.Equal(sdk.NewDecWithPrec(5, 2))) + require.True(t, votes[1].Options[3].Weight.Equal(sdk.NewDecWithPrec(5, 2))) } diff --git a/x/gov/legacy/v034/types.go b/x/gov/legacy/v034/types.go index dd98cca21..14e0b7a26 100644 --- a/x/gov/legacy/v034/types.go +++ b/x/gov/legacy/v034/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v034 import ( diff --git a/x/gov/legacy/v036/types.go b/x/gov/legacy/v036/types.go index d0bc4620b..6632c6da2 100644 --- a/x/gov/legacy/v036/types.go +++ b/x/gov/legacy/v036/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v036 import ( diff --git a/x/gov/legacy/v040/keys.go b/x/gov/legacy/v040/keys.go new file mode 100644 index 000000000..b1559875b --- /dev/null +++ b/x/gov/legacy/v040/keys.go @@ -0,0 +1,167 @@ +// Package v040 is copy-pasted from: +// https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/gov/types/keys.go +package v040 + +import ( + "encoding/binary" + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" +) + +const ( + // ModuleName is the name of the module + ModuleName = "gov" + + // StoreKey is the store key string for gov + StoreKey = ModuleName + + // RouterKey is the message route for gov + RouterKey = ModuleName + + // QuerierRoute is the querier route for gov + QuerierRoute = ModuleName +) + +// Keys for governance store +// Items are stored with the following key: values +// +// - 0x00: Proposal +// +// - 0x01: activeProposalID +// +// - 0x02: inactiveProposalID +// +// - 0x03: nextProposalID +// +// - 0x10: Deposit +// +// - 0x20: Voter +var ( + ProposalsKeyPrefix = []byte{0x00} + ActiveProposalQueuePrefix = []byte{0x01} + InactiveProposalQueuePrefix = []byte{0x02} + ProposalIDKey = []byte{0x03} + + DepositsKeyPrefix = []byte{0x10} + + VotesKeyPrefix = []byte{0x20} +) + +var lenTime = len(sdk.FormatTimeBytes(time.Now())) + +// GetProposalIDBytes returns the byte representation of the proposalID +func GetProposalIDBytes(proposalID uint64) (proposalIDBz []byte) { + proposalIDBz = make([]byte, 8) + binary.BigEndian.PutUint64(proposalIDBz, proposalID) + return +} + +// GetProposalIDFromBytes returns proposalID in uint64 format from a byte array +func GetProposalIDFromBytes(bz []byte) (proposalID uint64) { + return binary.BigEndian.Uint64(bz) +} + +// ProposalKey gets a specific proposal from the store +func ProposalKey(proposalID uint64) []byte { + return append(ProposalsKeyPrefix, GetProposalIDBytes(proposalID)...) +} + +// ActiveProposalByTimeKey gets the active proposal queue key by endTime +func ActiveProposalByTimeKey(endTime time.Time) []byte { + return append(ActiveProposalQueuePrefix, sdk.FormatTimeBytes(endTime)...) +} + +// ActiveProposalQueueKey returns the key for a proposalID in the activeProposalQueue +func ActiveProposalQueueKey(proposalID uint64, endTime time.Time) []byte { + return append(ActiveProposalByTimeKey(endTime), GetProposalIDBytes(proposalID)...) +} + +// InactiveProposalByTimeKey gets the inactive proposal queue key by endTime +func InactiveProposalByTimeKey(endTime time.Time) []byte { + return append(InactiveProposalQueuePrefix, sdk.FormatTimeBytes(endTime)...) +} + +// InactiveProposalQueueKey returns the key for a proposalID in the inactiveProposalQueue +func InactiveProposalQueueKey(proposalID uint64, endTime time.Time) []byte { + return append(InactiveProposalByTimeKey(endTime), GetProposalIDBytes(proposalID)...) +} + +// DepositsKey gets the first part of the deposits key based on the proposalID +func DepositsKey(proposalID uint64) []byte { + return append(DepositsKeyPrefix, GetProposalIDBytes(proposalID)...) +} + +// DepositKey key of a specific deposit from the store +func DepositKey(proposalID uint64, depositorAddr sdk.AccAddress) []byte { + return append(DepositsKey(proposalID), depositorAddr.Bytes()...) +} + +// VotesKey gets the first part of the votes key based on the proposalID +func VotesKey(proposalID uint64) []byte { + return append(VotesKeyPrefix, GetProposalIDBytes(proposalID)...) +} + +// VoteKey key of a specific vote from the store +func VoteKey(proposalID uint64, voterAddr sdk.AccAddress) []byte { + return append(VotesKey(proposalID), voterAddr.Bytes()...) +} + +// Split keys function; used for iterators + +// SplitProposalKey split the proposal key and returns the proposal id +func SplitProposalKey(key []byte) (proposalID uint64) { + if len(key[1:]) != 8 { + panic(fmt.Sprintf("unexpected key length (%d ≠ 8)", len(key[1:]))) + } + + return GetProposalIDFromBytes(key[1:]) +} + +// SplitActiveProposalQueueKey split the active proposal key and returns the proposal id and endTime +func SplitActiveProposalQueueKey(key []byte) (proposalID uint64, endTime time.Time) { + return splitKeyWithTime(key) +} + +// SplitInactiveProposalQueueKey split the inactive proposal key and returns the proposal id and endTime +func SplitInactiveProposalQueueKey(key []byte) (proposalID uint64, endTime time.Time) { + return splitKeyWithTime(key) +} + +// SplitKeyDeposit split the deposits key and returns the proposal id and depositor address +func SplitKeyDeposit(key []byte) (proposalID uint64, depositorAddr sdk.AccAddress) { + return splitKeyWithAddress(key) +} + +// SplitKeyVote split the votes key and returns the proposal id and voter address +func SplitKeyVote(key []byte) (proposalID uint64, voterAddr sdk.AccAddress) { + return splitKeyWithAddress(key) +} + +// private functions + +func splitKeyWithTime(key []byte) (proposalID uint64, endTime time.Time) { + if len(key[1:]) != 8+lenTime { + panic(fmt.Sprintf("unexpected key length (%d ≠ %d)", len(key[1:]), lenTime+8)) + } + + endTime, err := sdk.ParseTimeBytes(key[1 : 1+lenTime]) + if err != nil { + panic(err) + } + + proposalID = GetProposalIDFromBytes(key[1+lenTime:]) + return +} + +func splitKeyWithAddress(key []byte) (proposalID uint64, addr sdk.AccAddress) { + if len(key[1:]) != 8+v040auth.AddrLen { + panic(fmt.Sprintf("unexpected key length (%d ≠ %d)", len(key), 8+v040auth.AddrLen)) + } + + proposalID = GetProposalIDFromBytes(key[1:9]) + addr = sdk.AccAddress(key[9:]) + return +} diff --git a/x/gov/legacy/v040/migrate.go b/x/gov/legacy/v040/migrate.go index 2accb7428..69b32a621 100644 --- a/x/gov/legacy/v040/migrate.go +++ b/x/gov/legacy/v040/migrate.go @@ -164,7 +164,7 @@ func Migrate(oldGovState v036gov.GenesisState) *v040gov.GenesisState { newVotes[i] = v040gov.Vote{ ProposalId: oldVote.ProposalID, Voter: oldVote.Voter.String(), - Option: migrateVoteOption(oldVote.Option), + Options: v040gov.NewNonSplitVoteOption(migrateVoteOption(oldVote.Option)), } } diff --git a/x/gov/legacy/v040/types.go b/x/gov/legacy/v040/types.go deleted file mode 100644 index 27f668b2e..000000000 --- a/x/gov/legacy/v040/types.go +++ /dev/null @@ -1,6 +0,0 @@ -package v040 - -// Default parameter values -const ( - ModuleName = "gov" -) diff --git a/x/gov/legacy/v042/store.go b/x/gov/legacy/v042/store.go new file mode 100644 index 000000000..acf824a16 --- /dev/null +++ b/x/gov/legacy/v042/store.go @@ -0,0 +1,43 @@ +package v042 + +import ( + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" + v040gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v040" +) + +const proposalIDLen = 8 + +// migratePrefixProposalAddress is a helper function that migrates all keys of format: +// +// into format: +// +func migratePrefixProposalAddress(store sdk.KVStore, prefixBz []byte) { + oldStore := prefix.NewStore(store, prefixBz) + + oldStoreIter := oldStore.Iterator(nil, nil) + defer oldStoreIter.Close() + + for ; oldStoreIter.Valid(); oldStoreIter.Next() { + proposalID := oldStoreIter.Key()[:proposalIDLen] + addr := oldStoreIter.Key()[proposalIDLen:] + newStoreKey := append(append(prefixBz, proposalID...), address.MustLengthPrefix(addr)...) + + // Set new key on store. Values don't change. + store.Set(newStoreKey, oldStoreIter.Value()) + oldStore.Delete(oldStoreIter.Key()) + } +} + +// MigrateStore performs in-place store migrations from v0.40 to v0.42. The +// migration includes: +// +// - Change addresses to be length-prefixed. +func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error { + store := ctx.KVStore(storeKey) + migratePrefixProposalAddress(store, v040gov.DepositsKeyPrefix) + migratePrefixProposalAddress(store, v040gov.VotesKeyPrefix) + + return nil +} diff --git a/x/gov/legacy/v042/store_test.go b/x/gov/legacy/v042/store_test.go new file mode 100644 index 000000000..6bcb056a9 --- /dev/null +++ b/x/gov/legacy/v042/store_test.go @@ -0,0 +1,85 @@ +package v042_test + +import ( + "bytes" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + v040gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v040" + v042gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v042" + "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +func TestStoreMigration(t *testing.T) { + govKey := sdk.NewKVStoreKey("gov") + ctx := testutil.DefaultContext(govKey, sdk.NewTransientStoreKey("transient_test")) + store := ctx.KVStore(govKey) + + _, _, addr1 := testdata.KeyTestPubAddr() + proposalID := uint64(6) + now := time.Now() + // Use dummy value for all keys. + value := []byte("foo") + + testCases := []struct { + name string + oldKey []byte + newKey []byte + }{ + { + "ProposalKey", + v040gov.ProposalKey(proposalID), + types.ProposalKey(proposalID), + }, + { + "ActiveProposalQueue", + v040gov.ActiveProposalQueueKey(proposalID, now), + types.ActiveProposalQueueKey(proposalID, now), + }, + { + "InactiveProposalQueue", + v040gov.InactiveProposalQueueKey(proposalID, now), + types.InactiveProposalQueueKey(proposalID, now), + }, + { + "ProposalIDKey", + v040gov.ProposalIDKey, + types.ProposalIDKey, + }, + { + "DepositKey", + v040gov.DepositKey(proposalID, addr1), + types.DepositKey(proposalID, addr1), + }, + { + "VotesKeyPrefix", + v040gov.VoteKey(proposalID, addr1), + types.VoteKey(proposalID, addr1), + }, + } + + // Set all the old keys to the store + for _, tc := range testCases { + store.Set(tc.oldKey, value) + } + + // Run migrations. + err := v042gov.MigrateStore(ctx, govKey) + require.NoError(t, err) + + // Make sure the new keys are set and old keys are deleted. + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + if bytes.Compare(tc.oldKey, tc.newKey) != 0 { + require.Nil(t, store.Get(tc.oldKey)) + } + require.Equal(t, value, store.Get(tc.newKey)) + }) + } +} diff --git a/x/gov/module.go b/x/gov/module.go index b72a4bfe5..5e24cda3a 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -109,8 +109,6 @@ func (a AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry types.RegisterInterfaces(registry) } -//____________________________________________________________________________ - // AppModule implements an application module for the gov module. type AppModule struct { AppModuleBasic @@ -159,6 +157,9 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) types.RegisterQueryServer(cfg.QueryServer(), am.keeper) + + m := keeper.NewMigrator(am.keeper) + cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2) } // InitGenesis performs genesis initialization for the gov module. It returns @@ -178,7 +179,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 1 } +func (AppModule) ConsensusVersion() uint64 { return 2 } // BeginBlock performs a no-op. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} @@ -190,8 +191,6 @@ func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.Val return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the gov module. diff --git a/x/gov/simulation/decoder.go b/x/gov/simulation/decoder.go index 75cb4a5fc..180754dde 100644 --- a/x/gov/simulation/decoder.go +++ b/x/gov/simulation/decoder.go @@ -22,7 +22,7 @@ func NewDecodeStore(cdc codec.Marshaler) func(kvA, kvB kv.Pair) string { panic(err) } var proposalB types.Proposal - err = cdc.UnmarshalBinaryBare(kvA.Value, &proposalB) + err = cdc.UnmarshalBinaryBare(kvB.Value, &proposalB) if err != nil { panic(err) } diff --git a/x/gov/simulation/decoder_test.go b/x/gov/simulation/decoder_test.go index 6160cdfa9..4f6273a17 100644 --- a/x/gov/simulation/decoder_test.go +++ b/x/gov/simulation/decoder_test.go @@ -27,46 +27,66 @@ func TestDecodeStore(t *testing.T) { endTime := time.Now().UTC() content := types.ContentFromProposalType("test", "test", types.ProposalTypeText) - proposal, err := types.NewProposal(content, 1, endTime, endTime.Add(24*time.Hour)) + proposalA, err := types.NewProposal(content, 1, endTime, endTime.Add(24*time.Hour)) + require.NoError(t, err) + proposalB, err := types.NewProposal(content, 2, endTime, endTime.Add(24*time.Hour)) require.NoError(t, err) proposalIDBz := make([]byte, 8) binary.LittleEndian.PutUint64(proposalIDBz, 1) deposit := types.NewDeposit(1, delAddr1, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt()))) - vote := types.NewVote(1, delAddr1, types.OptionYes) + vote := types.NewVote(1, delAddr1, types.NewNonSplitVoteOption(types.OptionYes)) - proposalBz, err := cdc.MarshalBinaryBare(&proposal) + proposalBzA, err := cdc.MarshalBinaryBare(&proposalA) + require.NoError(t, err) + proposalBzB, err := cdc.MarshalBinaryBare(&proposalB) require.NoError(t, err) - - kvPairs := kv.Pairs{ - Pairs: []kv.Pair{ - {Key: types.ProposalKey(1), Value: proposalBz}, - {Key: types.InactiveProposalQueueKey(1, endTime), Value: proposalIDBz}, - {Key: types.DepositKey(1, delAddr1), Value: cdc.MustMarshalBinaryBare(&deposit)}, - {Key: types.VoteKey(1, delAddr1), Value: cdc.MustMarshalBinaryBare(&vote)}, - {Key: []byte{0x99}, Value: []byte{0x99}}, - }, - } tests := []struct { name string + kvA, kvB kv.Pair expectedLog string + wantPanic bool }{ - {"proposals", fmt.Sprintf("%v\n%v", proposal, proposal)}, - {"proposal IDs", "proposalIDA: 1\nProposalIDB: 1"}, - {"deposits", fmt.Sprintf("%v\n%v", deposit, deposit)}, - {"votes", fmt.Sprintf("%v\n%v", vote, vote)}, - {"other", ""}, + { + "proposals", + kv.Pair{Key: types.ProposalKey(1), Value: proposalBzA}, + kv.Pair{Key: types.ProposalKey(2), Value: proposalBzB}, + fmt.Sprintf("%v\n%v", proposalA, proposalB), false, + }, + { + "proposal IDs", + kv.Pair{Key: types.InactiveProposalQueueKey(1, endTime), Value: proposalIDBz}, + kv.Pair{Key: types.InactiveProposalQueueKey(1, endTime), Value: proposalIDBz}, + "proposalIDA: 1\nProposalIDB: 1", false, + }, + { + "deposits", + kv.Pair{Key: types.DepositKey(1, delAddr1), Value: cdc.MustMarshalBinaryBare(&deposit)}, + kv.Pair{Key: types.DepositKey(1, delAddr1), Value: cdc.MustMarshalBinaryBare(&deposit)}, + fmt.Sprintf("%v\n%v", deposit, deposit), false, + }, + { + "votes", + kv.Pair{Key: types.VoteKey(1, delAddr1), Value: cdc.MustMarshalBinaryBare(&vote)}, + kv.Pair{Key: types.VoteKey(1, delAddr1), Value: cdc.MustMarshalBinaryBare(&vote)}, + fmt.Sprintf("%v\n%v", vote, vote), false, + }, + { + "other", + kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, + kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, + "", true, + }, } - for i, tt := range tests { - i, tt := i, tt + for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { - switch i { - case len(tests) - 1: - require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) - default: - require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) + if tt.wantPanic { + require.Panics(t, func() { dec(tt.kvA, tt.kvB) }, tt.name) + } else { + require.Equal(t, tt.expectedLog, dec(tt.kvA, tt.kvB), tt.name) } }) } diff --git a/x/gov/simulation/operations.go b/x/gov/simulation/operations.go index d90ca07d5..8ce709da8 100644 --- a/x/gov/simulation/operations.go +++ b/x/gov/simulation/operations.go @@ -20,8 +20,9 @@ var initialProposalID = uint64(100000000000000) // Simulation operation weights constants const ( - OpWeightMsgDeposit = "op_weight_msg_deposit" - OpWeightMsgVote = "op_weight_msg_vote" + OpWeightMsgDeposit = "op_weight_msg_deposit" + OpWeightMsgVote = "op_weight_msg_vote" + OpWeightMsgVoteWeighted = "op_weight_msg_weighted_vote" ) // WeightedOperations returns all the operations from the module with their respective weights @@ -31,8 +32,9 @@ func WeightedOperations( ) simulation.WeightedOperations { var ( - weightMsgDeposit int - weightMsgVote int + weightMsgDeposit int + weightMsgVote int + weightMsgVoteWeighted int ) appParams.GetOrGenerate(cdc, OpWeightMsgDeposit, &weightMsgDeposit, nil, @@ -47,6 +49,12 @@ func WeightedOperations( }, ) + appParams.GetOrGenerate(cdc, OpWeightMsgVoteWeighted, &weightMsgVoteWeighted, nil, + func(_ *rand.Rand) { + weightMsgVoteWeighted = simappparams.DefaultWeightMsgVoteWeighted + }, + ) + // generate the weighted operations for the proposal contents var wProposalOps simulation.WeightedOperations @@ -74,12 +82,16 @@ func WeightedOperations( weightMsgVote, SimulateMsgVote(ak, bk, k), ), + simulation.NewWeightedOperation( + weightMsgVoteWeighted, + SimulateMsgVoteWeighted(ak, bk, k), + ), } return append(wProposalOps, wGovOps...) } -// SimulateSubmitProposal simulates creating a msg Submit Proposal +// SimulateMsgSubmitProposal simulates creating a msg Submit Proposal // voting on the proposal, and subsequently slashing the proposal. It is implemented using // future operations. func SimulateMsgSubmitProposal( @@ -315,6 +327,69 @@ func operationSimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, k kee } } +// SimulateMsgVoteWeighted generates a MsgVoteWeighted with random values. +func SimulateMsgVoteWeighted(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { + return operationSimulateMsgVoteWeighted(ak, bk, k, simtypes.Account{}, -1) +} + +func operationSimulateMsgVoteWeighted(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, + simAccount simtypes.Account, proposalIDInt int64) simtypes.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + if simAccount.Equals(simtypes.Account{}) { + simAccount, _ = simtypes.RandomAcc(r, accs) + } + + var proposalID uint64 + + switch { + case proposalIDInt < 0: + var ok bool + proposalID, ok = randomProposalID(r, k, ctx, types.StatusVotingPeriod) + if !ok { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgVoteWeighted, "unable to generate proposalID"), nil, nil + } + default: + proposalID = uint64(proposalIDInt) + } + + options := randomWeightedVotingOptions(r) + msg := types.NewMsgVoteWeighted(simAccount.Address, proposalID, options) + + account := ak.GetAccount(ctx, simAccount.Address) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) + + fees, err := simtypes.RandomFees(r, ctx, spendable) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate fees"), nil, err + } + + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, + []sdk.Msg{msg}, + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{account.GetAccountNumber()}, + []uint64{account.GetSequence()}, + simAccount.PrivKey, + ) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err + } + + _, _, err = app.Deliver(txGen.TxEncoder(), tx) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err + } + + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil + } +} + // Pick a random deposit with a random denomination with a // deposit amount between (0, min(balance, minDepositAmount)) // This is to simulate multiple users depositing to get the @@ -393,3 +468,37 @@ func randomVotingOption(r *rand.Rand) types.VoteOption { panic("invalid vote option") } } + +// Pick a random weighted voting options +func randomWeightedVotingOptions(r *rand.Rand) types.WeightedVoteOptions { + w1 := r.Intn(100 + 1) + w2 := r.Intn(100 - w1 + 1) + w3 := r.Intn(100 - w1 - w2 + 1) + w4 := 100 - w1 - w2 - w3 + weightedVoteOptions := types.WeightedVoteOptions{} + if w1 > 0 { + weightedVoteOptions = append(weightedVoteOptions, types.WeightedVoteOption{ + Option: types.OptionYes, + Weight: sdk.NewDecWithPrec(int64(w1), 2), + }) + } + if w2 > 0 { + weightedVoteOptions = append(weightedVoteOptions, types.WeightedVoteOption{ + Option: types.OptionAbstain, + Weight: sdk.NewDecWithPrec(int64(w2), 2), + }) + } + if w3 > 0 { + weightedVoteOptions = append(weightedVoteOptions, types.WeightedVoteOption{ + Option: types.OptionNo, + Weight: sdk.NewDecWithPrec(int64(w3), 2), + }) + } + if w4 > 0 { + weightedVoteOptions = append(weightedVoteOptions, types.WeightedVoteOption{ + Option: types.OptionNoWithVeto, + Weight: sdk.NewDecWithPrec(int64(w4), 2), + }) + } + return weightedVoteOptions +} diff --git a/x/gov/simulation/operations_test.go b/x/gov/simulation/operations_test.go index 9c6e8306f..2d3bbe22f 100644 --- a/x/gov/simulation/operations_test.go +++ b/x/gov/simulation/operations_test.go @@ -80,6 +80,7 @@ func TestWeightedOperations(t *testing.T) { {2, types.ModuleName, "submit_proposal"}, {simappparams.DefaultWeightMsgDeposit, types.ModuleName, types.TypeMsgDeposit}, {simappparams.DefaultWeightMsgVote, types.ModuleName, types.TypeMsgVote}, + {simappparams.DefaultWeightMsgVoteWeighted, types.ModuleName, types.TypeMsgVoteWeighted}, } for i, w := range weightesOps { @@ -205,7 +206,48 @@ func TestSimulateMsgVote(t *testing.T) { require.Equal(t, types.OptionYes, msg.Option) require.Equal(t, "gov", msg.Route()) require.Equal(t, types.TypeMsgVote, msg.Type()) +} +// TestSimulateMsgVoteWeighted tests the normal scenario of a valid message of type TypeMsgVoteWeighted. +// Abonormal scenarios, where the message is created by an errors are not tested here. +func TestSimulateMsgVoteWeighted(t *testing.T) { + app, ctx := createTestApp(false) + blockTime := time.Now().UTC() + ctx = ctx.WithBlockTime(blockTime) + + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accounts := getTestingAccounts(t, r, app, ctx, 3) + + // setup a proposal + content := types.NewTextProposal("Test", "description") + + submitTime := ctx.BlockHeader().Time + depositPeriod := app.GovKeeper.GetDepositParams(ctx).MaxDepositPeriod + + proposal, err := types.NewProposal(content, 1, submitTime, submitTime.Add(depositPeriod)) + require.NoError(t, err) + + app.GovKeeper.ActivateVotingPeriod(ctx, proposal) + + // begin a new block + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash, Time: blockTime}}) + + // execute operation + op := simulation.SimulateMsgVoteWeighted(app.AccountKeeper, app.BankKeeper, app.GovKeeper) + operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(t, err) + + var msg types.MsgVoteWeighted + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + require.True(t, operationMsg.OK) + require.Equal(t, uint64(1), msg.ProposalId) + require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Voter) + require.True(t, len(msg.Options) >= 1) + require.Equal(t, "gov", msg.Route()) + require.Equal(t, types.TypeMsgVoteWeighted, msg.Type()) } // returns context and an app with updated mint keeper @@ -229,8 +271,7 @@ func getTestingAccounts(t *testing.T, r *rand.Rand, app *simapp.SimApp, ctx sdk. for _, account := range accounts { acc := app.AccountKeeper.NewAccountWithAddress(ctx, account.Address) app.AccountKeeper.SetAccount(ctx, acc) - err := app.BankKeeper.SetBalances(ctx, account.Address, initCoins) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, account.Address, initCoins)) } return accounts diff --git a/x/gov/spec/04_events.md b/x/gov/spec/04_events.md index dc3b1bf3a..300d4d187 100644 --- a/x/gov/spec/04_events.md +++ b/x/gov/spec/04_events.md @@ -41,6 +41,16 @@ The governance module emits the following events: | message | action | vote | | message | sender | {senderAddress} | +### MsgVoteWeighted + +| Type | Attribute Key | Attribute Value | +| ------------- | ------------- | ------------------------ | +| proposal_vote | option | {weightedVoteOptions} | +| proposal_vote | proposal_id | {proposalID} | +| message | module | governance | +| message | action | vote | +| message | sender | {senderAddress} | + ### MsgDeposit | Type | Attribute Key | Attribute Value | diff --git a/x/gov/types/codec.go b/x/gov/types/codec.go index 46deb5c59..e59e0ef49 100644 --- a/x/gov/types/codec.go +++ b/x/gov/types/codec.go @@ -15,6 +15,7 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgSubmitProposal{}, "cosmos-sdk/MsgSubmitProposal", nil) cdc.RegisterConcrete(&MsgDeposit{}, "cosmos-sdk/MsgDeposit", nil) cdc.RegisterConcrete(&MsgVote{}, "cosmos-sdk/MsgVote", nil) + cdc.RegisterConcrete(&MsgVoteWeighted{}, "cosmos-sdk/MsgVoteWeighted", nil) cdc.RegisterConcrete(&TextProposal{}, "cosmos-sdk/TextProposal", nil) } @@ -22,6 +23,7 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSubmitProposal{}, &MsgVote{}, + &MsgVoteWeighted{}, &MsgDeposit{}, ) registry.RegisterInterface( diff --git a/x/gov/types/expected_keepers.go b/x/gov/types/expected_keepers.go index f862a9bce..1ad39abca 100644 --- a/x/gov/types/expected_keepers.go +++ b/x/gov/types/expected_keepers.go @@ -41,7 +41,6 @@ type AccountKeeper interface { type BankKeeper interface { GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin - SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins diff --git a/x/gov/types/gov.pb.go b/x/gov/types/gov.pb.go index 57730bcb9..caa52a67a 100644 --- a/x/gov/types/gov.pb.go +++ b/x/gov/types/gov.pb.go @@ -121,6 +121,44 @@ func (ProposalStatus) EnumDescriptor() ([]byte, []int) { return fileDescriptor_6e82113c1a9a4b7c, []int{1} } +// WeightedVoteOption defines a unit of vote for vote split. +type WeightedVoteOption struct { + Option VoteOption `protobuf:"varint,1,opt,name=option,proto3,enum=cosmos.gov.v1beta1.VoteOption" json:"option,omitempty"` + Weight github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=weight,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"weight" yaml:"weight"` +} + +func (m *WeightedVoteOption) Reset() { *m = WeightedVoteOption{} } +func (*WeightedVoteOption) ProtoMessage() {} +func (*WeightedVoteOption) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82113c1a9a4b7c, []int{0} +} +func (m *WeightedVoteOption) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WeightedVoteOption) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WeightedVoteOption.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WeightedVoteOption) XXX_Merge(src proto.Message) { + xxx_messageInfo_WeightedVoteOption.Merge(m, src) +} +func (m *WeightedVoteOption) XXX_Size() int { + return m.Size() +} +func (m *WeightedVoteOption) XXX_DiscardUnknown() { + xxx_messageInfo_WeightedVoteOption.DiscardUnknown(m) +} + +var xxx_messageInfo_WeightedVoteOption proto.InternalMessageInfo + // TextProposal defines a standard text proposal whose changes need to be // manually updated in case of approval. type TextProposal struct { @@ -131,7 +169,7 @@ type TextProposal struct { func (m *TextProposal) Reset() { *m = TextProposal{} } func (*TextProposal) ProtoMessage() {} func (*TextProposal) Descriptor() ([]byte, []int) { - return fileDescriptor_6e82113c1a9a4b7c, []int{0} + return fileDescriptor_6e82113c1a9a4b7c, []int{1} } func (m *TextProposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -171,7 +209,7 @@ type Deposit struct { func (m *Deposit) Reset() { *m = Deposit{} } func (*Deposit) ProtoMessage() {} func (*Deposit) Descriptor() ([]byte, []int) { - return fileDescriptor_6e82113c1a9a4b7c, []int{1} + return fileDescriptor_6e82113c1a9a4b7c, []int{2} } func (m *Deposit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -216,7 +254,7 @@ type Proposal struct { func (m *Proposal) Reset() { *m = Proposal{} } func (*Proposal) ProtoMessage() {} func (*Proposal) Descriptor() ([]byte, []int) { - return fileDescriptor_6e82113c1a9a4b7c, []int{2} + return fileDescriptor_6e82113c1a9a4b7c, []int{3} } func (m *Proposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -256,7 +294,7 @@ type TallyResult struct { func (m *TallyResult) Reset() { *m = TallyResult{} } func (*TallyResult) ProtoMessage() {} func (*TallyResult) Descriptor() ([]byte, []int) { - return fileDescriptor_6e82113c1a9a4b7c, []int{3} + return fileDescriptor_6e82113c1a9a4b7c, []int{4} } func (m *TallyResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -288,15 +326,15 @@ var xxx_messageInfo_TallyResult proto.InternalMessageInfo // Vote defines a vote on a governance proposal. // A Vote consists of a proposal ID, the voter, and the vote option. type Vote struct { - ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty" yaml:"proposal_id"` - Voter string `protobuf:"bytes,2,opt,name=voter,proto3" json:"voter,omitempty"` - Option VoteOption `protobuf:"varint,3,opt,name=option,proto3,enum=cosmos.gov.v1beta1.VoteOption" json:"option,omitempty"` + ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty" yaml:"proposal_id"` + Voter string `protobuf:"bytes,2,opt,name=voter,proto3" json:"voter,omitempty"` + Options []WeightedVoteOption `protobuf:"bytes,4,rep,name=options,proto3" json:"options"` } func (m *Vote) Reset() { *m = Vote{} } func (*Vote) ProtoMessage() {} func (*Vote) Descriptor() ([]byte, []int) { - return fileDescriptor_6e82113c1a9a4b7c, []int{4} + return fileDescriptor_6e82113c1a9a4b7c, []int{5} } func (m *Vote) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -337,7 +375,7 @@ type DepositParams struct { func (m *DepositParams) Reset() { *m = DepositParams{} } func (*DepositParams) ProtoMessage() {} func (*DepositParams) Descriptor() ([]byte, []int) { - return fileDescriptor_6e82113c1a9a4b7c, []int{5} + return fileDescriptor_6e82113c1a9a4b7c, []int{6} } func (m *DepositParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -375,7 +413,7 @@ type VotingParams struct { func (m *VotingParams) Reset() { *m = VotingParams{} } func (*VotingParams) ProtoMessage() {} func (*VotingParams) Descriptor() ([]byte, []int) { - return fileDescriptor_6e82113c1a9a4b7c, []int{6} + return fileDescriptor_6e82113c1a9a4b7c, []int{7} } func (m *VotingParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -419,7 +457,7 @@ type TallyParams struct { func (m *TallyParams) Reset() { *m = TallyParams{} } func (*TallyParams) ProtoMessage() {} func (*TallyParams) Descriptor() ([]byte, []int) { - return fileDescriptor_6e82113c1a9a4b7c, []int{7} + return fileDescriptor_6e82113c1a9a4b7c, []int{8} } func (m *TallyParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -451,6 +489,7 @@ var xxx_messageInfo_TallyParams proto.InternalMessageInfo func init() { proto.RegisterEnum("cosmos.gov.v1beta1.VoteOption", VoteOption_name, VoteOption_value) proto.RegisterEnum("cosmos.gov.v1beta1.ProposalStatus", ProposalStatus_name, ProposalStatus_value) + proto.RegisterType((*WeightedVoteOption)(nil), "cosmos.gov.v1beta1.WeightedVoteOption") proto.RegisterType((*TextProposal)(nil), "cosmos.gov.v1beta1.TextProposal") proto.RegisterType((*Deposit)(nil), "cosmos.gov.v1beta1.Deposit") proto.RegisterType((*Proposal)(nil), "cosmos.gov.v1beta1.Proposal") @@ -464,94 +503,98 @@ func init() { func init() { proto.RegisterFile("cosmos/gov/v1beta1/gov.proto", fileDescriptor_6e82113c1a9a4b7c) } var fileDescriptor_6e82113c1a9a4b7c = []byte{ - // 1377 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x5f, 0x6c, 0xdb, 0xd4, - 0x17, 0x8e, 0xd3, 0xbf, 0xb9, 0x49, 0x5b, 0xef, 0x36, 0x6b, 0x53, 0xff, 0xf6, 0xb3, 0x8d, 0x41, - 0xa8, 0x9a, 0xb6, 0x74, 0x2b, 0x08, 0x44, 0x27, 0x21, 0x92, 0xc6, 0x63, 0x41, 0x53, 0x12, 0x39, - 0x5e, 0xa6, 0x8d, 0x07, 0xcb, 0x49, 0xee, 0x52, 0x43, 0xec, 0x1b, 0xe2, 0x9b, 0xd2, 0x88, 0x17, - 0x1e, 0xa7, 0x20, 0xa1, 0xbd, 0x31, 0x09, 0x45, 0x9a, 0xc4, 0x1b, 0xcf, 0x3c, 0xf3, 0x5c, 0x21, - 0x24, 0x26, 0x9e, 0x26, 0x90, 0x32, 0xd6, 0x49, 0x68, 0xea, 0x63, 0x1f, 0x78, 0x46, 0xf6, 0xbd, - 0x6e, 0x9c, 0xa4, 0xa2, 0x84, 0xa7, 0xd9, 0xe7, 0x9e, 0xef, 0xfb, 0xce, 0xfd, 0x7c, 0xce, 0xc9, - 0x0a, 0x2e, 0xd5, 0xb0, 0x6b, 0x63, 0x77, 0xab, 0x81, 0xf7, 0xb7, 0xf6, 0xaf, 0x57, 0x11, 0x31, - 0xaf, 0x7b, 0xcf, 0xe9, 0x56, 0x1b, 0x13, 0x0c, 0x21, 0x3d, 0x4d, 0x7b, 0x11, 0x76, 0x2a, 0x88, - 0x0c, 0x51, 0x35, 0x5d, 0x74, 0x0a, 0xa9, 0x61, 0xcb, 0xa1, 0x18, 0x21, 0xd9, 0xc0, 0x0d, 0xec, - 0x3f, 0x6e, 0x79, 0x4f, 0x2c, 0xba, 0x41, 0x51, 0x06, 0x3d, 0x60, 0xb4, 0xf4, 0x48, 0x6a, 0x60, - 0xdc, 0x68, 0xa2, 0x2d, 0xff, 0xad, 0xda, 0x79, 0xb0, 0x45, 0x2c, 0x1b, 0xb9, 0xc4, 0xb4, 0x5b, - 0x01, 0x76, 0x3c, 0xc1, 0x74, 0xba, 0xec, 0x48, 0x1c, 0x3f, 0xaa, 0x77, 0xda, 0x26, 0xb1, 0x30, - 0x2b, 0x46, 0xb9, 0x0b, 0x12, 0x3a, 0x3a, 0x20, 0xa5, 0x36, 0x6e, 0x61, 0xd7, 0x6c, 0xc2, 0x24, - 0x98, 0x23, 0x16, 0x69, 0xa2, 0x14, 0x27, 0x73, 0x9b, 0x31, 0x8d, 0xbe, 0x40, 0x19, 0xc4, 0xeb, - 0xc8, 0xad, 0xb5, 0xad, 0x96, 0x07, 0x4d, 0x45, 0xfd, 0xb3, 0x70, 0x68, 0x67, 0xe5, 0xd5, 0x13, - 0x89, 0xfb, 0xf5, 0x87, 0xab, 0x0b, 0xbb, 0xd8, 0x21, 0xc8, 0x21, 0xca, 0x2f, 0x1c, 0x58, 0xc8, - 0xa1, 0x16, 0x76, 0x2d, 0x02, 0xdf, 0x05, 0xf1, 0x16, 0x13, 0x30, 0xac, 0xba, 0x4f, 0x3d, 0x9b, - 0x5d, 0x3b, 0x19, 0x48, 0xb0, 0x6b, 0xda, 0xcd, 0x1d, 0x25, 0x74, 0xa8, 0x68, 0x20, 0x78, 0xcb, - 0xd7, 0xe1, 0x25, 0x10, 0xab, 0x53, 0x0e, 0xdc, 0x66, 0xaa, 0xc3, 0x00, 0xac, 0x81, 0x79, 0xd3, - 0xc6, 0x1d, 0x87, 0xa4, 0x66, 0xe4, 0x99, 0xcd, 0xf8, 0xf6, 0x46, 0x9a, 0xd9, 0xe6, 0x39, 0x1f, - 0x7c, 0x8e, 0xf4, 0x2e, 0xb6, 0x9c, 0xec, 0xb5, 0xc3, 0x81, 0x14, 0xf9, 0xfe, 0xb9, 0xb4, 0xd9, - 0xb0, 0xc8, 0x5e, 0xa7, 0x9a, 0xae, 0x61, 0x9b, 0x79, 0xcc, 0xfe, 0xb9, 0xea, 0xd6, 0x3f, 0xdd, - 0x22, 0xdd, 0x16, 0x72, 0x7d, 0x80, 0xab, 0x31, 0xea, 0x9d, 0xc5, 0x87, 0x4f, 0xa4, 0xc8, 0xab, - 0x27, 0x52, 0x44, 0xf9, 0x6b, 0x1e, 0x2c, 0x9e, 0xfa, 0xf4, 0xf6, 0x59, 0x57, 0x5a, 0x3d, 0x1e, - 0x48, 0x51, 0xab, 0x7e, 0x32, 0x90, 0x62, 0xf4, 0x62, 0xe3, 0xf7, 0xb9, 0x01, 0x16, 0x6a, 0xd4, - 0x1f, 0xff, 0x36, 0xf1, 0xed, 0x64, 0x9a, 0x7e, 0x9f, 0x74, 0xf0, 0x7d, 0xd2, 0x19, 0xa7, 0x9b, - 0x8d, 0xff, 0x34, 0x34, 0x52, 0x0b, 0x10, 0xb0, 0x02, 0xe6, 0x5d, 0x62, 0x92, 0x8e, 0x9b, 0x9a, - 0x91, 0xb9, 0xcd, 0xe5, 0x6d, 0x25, 0x3d, 0xd9, 0x7c, 0xe9, 0xa0, 0xc0, 0xb2, 0x9f, 0x99, 0x15, - 0x4e, 0x06, 0xd2, 0xda, 0x98, 0xc9, 0x94, 0x44, 0xd1, 0x18, 0x1b, 0x6c, 0x01, 0xf8, 0xc0, 0x72, - 0xcc, 0xa6, 0x41, 0xcc, 0x66, 0xb3, 0x6b, 0xb4, 0x91, 0xdb, 0x69, 0x92, 0xd4, 0xac, 0x5f, 0x9f, - 0x74, 0x96, 0x86, 0xee, 0xe5, 0x69, 0x7e, 0x5a, 0xf6, 0x35, 0xcf, 0xd8, 0x93, 0x81, 0xb4, 0x41, - 0x45, 0x26, 0x89, 0x14, 0x8d, 0xf7, 0x83, 0x21, 0x10, 0xfc, 0x18, 0xc4, 0xdd, 0x4e, 0xd5, 0xb6, - 0x88, 0xe1, 0x75, 0x72, 0x6a, 0xce, 0x97, 0x12, 0x26, 0xac, 0xd0, 0x83, 0x36, 0xcf, 0x8a, 0x4c, - 0x85, 0xf5, 0x4b, 0x08, 0xac, 0x3c, 0x7a, 0x2e, 0x71, 0x1a, 0xa0, 0x11, 0x0f, 0x00, 0x2d, 0xc0, - 0xb3, 0x16, 0x31, 0x90, 0x53, 0xa7, 0x0a, 0xf3, 0xe7, 0x2a, 0xbc, 0xce, 0x14, 0xd6, 0xa9, 0xc2, - 0x38, 0x03, 0x95, 0x59, 0x66, 0x61, 0xd5, 0xa9, 0xfb, 0x52, 0x0f, 0x39, 0xb0, 0x44, 0x30, 0x31, - 0x9b, 0x06, 0x3b, 0x48, 0x2d, 0x9c, 0xd7, 0x88, 0xb7, 0x98, 0x4e, 0x92, 0xea, 0x8c, 0xa0, 0x95, - 0xa9, 0x1a, 0x34, 0xe1, 0x63, 0x83, 0x11, 0x6b, 0x82, 0x0b, 0xfb, 0x98, 0x58, 0x4e, 0xc3, 0xfb, - 0xbc, 0x6d, 0x66, 0xec, 0xe2, 0xb9, 0xd7, 0x7e, 0x83, 0x95, 0x93, 0xa2, 0xe5, 0x4c, 0x50, 0xd0, - 0x7b, 0xaf, 0xd0, 0x78, 0xd9, 0x0b, 0xfb, 0x17, 0x7f, 0x00, 0x58, 0x68, 0x68, 0x71, 0xec, 0x5c, - 0x2d, 0x85, 0x69, 0xad, 0x8d, 0x68, 0x8d, 0x3a, 0xbc, 0x44, 0xa3, 0xcc, 0xe0, 0x9d, 0x59, 0x6f, - 0xab, 0x28, 0x87, 0x51, 0x10, 0x0f, 0xb7, 0xcf, 0x07, 0x60, 0xa6, 0x8b, 0x5c, 0xba, 0xa1, 0xb2, - 0x69, 0x8f, 0xf5, 0xb7, 0x81, 0xf4, 0xe6, 0xbf, 0x30, 0x2e, 0xef, 0x10, 0xcd, 0x83, 0xc2, 0x5b, - 0x60, 0xc1, 0xac, 0xba, 0xc4, 0xb4, 0xd8, 0x2e, 0x9b, 0x9a, 0x25, 0x80, 0xc3, 0xf7, 0x41, 0xd4, - 0xc1, 0xfe, 0x40, 0x4e, 0x4f, 0x12, 0x75, 0x30, 0x6c, 0x80, 0x84, 0x83, 0x8d, 0xcf, 0x2d, 0xb2, - 0x67, 0xec, 0x23, 0x82, 0xfd, 0xb1, 0x8b, 0x65, 0xd5, 0xe9, 0x98, 0x4e, 0x06, 0xd2, 0x2a, 0x35, - 0x35, 0xcc, 0xa5, 0x68, 0xc0, 0xc1, 0x77, 0x2d, 0xb2, 0x57, 0x41, 0x04, 0x33, 0x2b, 0xbf, 0xe1, - 0xc0, 0x6c, 0x05, 0x13, 0xf4, 0xdf, 0x57, 0x72, 0x12, 0xcc, 0xed, 0x63, 0x82, 0x82, 0x75, 0x4c, - 0x5f, 0xe0, 0x3b, 0x60, 0x1e, 0xd3, 0xdf, 0x06, 0xba, 0x9b, 0xc4, 0xb3, 0xf6, 0x86, 0x27, 0x5c, - 0xf4, 0xb3, 0x34, 0x96, 0xbd, 0xb3, 0xf8, 0x38, 0xd8, 0xae, 0x3f, 0x46, 0xc1, 0x12, 0x6b, 0xe6, - 0x92, 0xd9, 0x36, 0x6d, 0x17, 0x7e, 0xcb, 0x81, 0xb8, 0x6d, 0x39, 0xa7, 0xb3, 0xc5, 0x9d, 0x37, - 0x5b, 0x86, 0xe7, 0xda, 0xf1, 0x40, 0xba, 0x18, 0x42, 0x5d, 0xc1, 0xb6, 0x45, 0x90, 0xdd, 0x22, - 0xdd, 0xe1, 0xdd, 0x42, 0xc7, 0xd3, 0x8d, 0x1c, 0xb0, 0x2d, 0x27, 0x18, 0xb8, 0xaf, 0x39, 0x00, - 0x6d, 0xf3, 0x20, 0x20, 0x32, 0x5a, 0xa8, 0x6d, 0xe1, 0x3a, 0x5b, 0xeb, 0x1b, 0x13, 0x63, 0x90, - 0x63, 0x3f, 0xbb, 0xf4, 0xd3, 0x1e, 0x0f, 0xa4, 0x4b, 0x93, 0xe0, 0x91, 0x5a, 0xd9, 0x42, 0x9d, - 0xcc, 0x52, 0x1e, 0x7b, 0x83, 0xc2, 0xdb, 0xe6, 0x41, 0x60, 0x17, 0x0d, 0x7f, 0xc5, 0x81, 0x44, - 0xc5, 0x9f, 0x1e, 0xe6, 0xdf, 0x17, 0x80, 0x4d, 0x53, 0x50, 0x1b, 0x77, 0x5e, 0x6d, 0x37, 0x58, - 0x6d, 0xeb, 0x23, 0xb8, 0x91, 0xb2, 0x92, 0x23, 0xc3, 0x1b, 0xae, 0x28, 0x41, 0x63, 0xac, 0x9a, - 0xdf, 0x83, 0x99, 0x65, 0xc5, 0xdc, 0x07, 0xf3, 0x9f, 0x75, 0x70, 0xbb, 0x63, 0xfb, 0x55, 0x24, - 0xb2, 0xd9, 0x29, 0x3a, 0x3c, 0x87, 0x6a, 0xc7, 0x03, 0x89, 0xa7, 0xf8, 0x61, 0x35, 0x1a, 0x63, - 0x84, 0x35, 0x10, 0x23, 0x7b, 0x6d, 0xe4, 0xee, 0xe1, 0x26, 0xfd, 0x00, 0x89, 0xa9, 0x06, 0x88, - 0xd2, 0xaf, 0x9e, 0x52, 0x84, 0x14, 0x86, 0xbc, 0xb0, 0xc7, 0x81, 0x65, 0x6f, 0xaa, 0x8c, 0xa1, - 0xd4, 0x8c, 0x2f, 0x55, 0x9b, 0x5a, 0x2a, 0x35, 0xca, 0x33, 0xe2, 0xef, 0x45, 0xe6, 0xef, 0x48, - 0x86, 0xa2, 0x2d, 0x79, 0x01, 0x3d, 0x78, 0xbf, 0xfc, 0x27, 0x07, 0xc0, 0x70, 0x9a, 0xe0, 0x15, - 0xb0, 0x5e, 0x29, 0xea, 0xaa, 0x51, 0x2c, 0xe9, 0xf9, 0x62, 0xc1, 0xb8, 0x53, 0x28, 0x97, 0xd4, - 0xdd, 0xfc, 0xcd, 0xbc, 0x9a, 0xe3, 0x23, 0xc2, 0x4a, 0xaf, 0x2f, 0xc7, 0x69, 0xa2, 0xea, 0x89, - 0x40, 0x05, 0xac, 0x84, 0xb3, 0xef, 0xa9, 0x65, 0x9e, 0x13, 0x96, 0x7a, 0x7d, 0x39, 0x46, 0xb3, - 0xee, 0x21, 0x17, 0x5e, 0x06, 0xab, 0xe1, 0x9c, 0x4c, 0xb6, 0xac, 0x67, 0xf2, 0x05, 0x3e, 0x2a, - 0x5c, 0xe8, 0xf5, 0xe5, 0x25, 0x9a, 0x97, 0x61, 0x2b, 0x50, 0x06, 0xcb, 0xe1, 0xdc, 0x42, 0x91, - 0x9f, 0x11, 0x12, 0xbd, 0xbe, 0xbc, 0x48, 0xd3, 0x0a, 0x18, 0x6e, 0x83, 0xd4, 0x68, 0x86, 0x71, - 0x37, 0xaf, 0xdf, 0x32, 0x2a, 0xaa, 0x5e, 0xe4, 0x67, 0x85, 0x64, 0xaf, 0x2f, 0xf3, 0x41, 0x6e, - 0xb0, 0xaf, 0x84, 0xd9, 0x87, 0xdf, 0x89, 0x91, 0xcb, 0x3f, 0x47, 0xc1, 0xf2, 0xe8, 0x7f, 0x69, - 0x60, 0x1a, 0xfc, 0xaf, 0xa4, 0x15, 0x4b, 0xc5, 0x72, 0xe6, 0xb6, 0x51, 0xd6, 0x33, 0xfa, 0x9d, - 0xf2, 0xd8, 0x85, 0xfd, 0xab, 0xd0, 0xe4, 0x82, 0xd5, 0x84, 0x37, 0x80, 0x38, 0x9e, 0x9f, 0x53, - 0x4b, 0xc5, 0x72, 0x5e, 0x37, 0x4a, 0xaa, 0x96, 0x2f, 0xe6, 0x78, 0x4e, 0x58, 0xef, 0xf5, 0xe5, - 0x55, 0x0a, 0x19, 0x19, 0x2a, 0xf8, 0x1e, 0xf8, 0xff, 0x38, 0xb8, 0x52, 0xd4, 0xf3, 0x85, 0x0f, - 0x03, 0x6c, 0x54, 0x58, 0xeb, 0xf5, 0x65, 0x48, 0xb1, 0x95, 0xd0, 0x04, 0xc0, 0x2b, 0x60, 0x6d, - 0x1c, 0x5a, 0xca, 0x94, 0xcb, 0x6a, 0x8e, 0x9f, 0x11, 0xf8, 0x5e, 0x5f, 0x4e, 0x50, 0x4c, 0xc9, - 0x74, 0x5d, 0x54, 0x87, 0xd7, 0x40, 0x6a, 0x3c, 0x5b, 0x53, 0x3f, 0x52, 0x77, 0x75, 0x35, 0xc7, - 0xcf, 0x0a, 0xb0, 0xd7, 0x97, 0x97, 0x69, 0xbe, 0x86, 0x3e, 0x41, 0x35, 0x82, 0xce, 0xe4, 0xbf, - 0x99, 0xc9, 0xdf, 0x56, 0x73, 0xfc, 0x5c, 0x98, 0xff, 0xa6, 0x69, 0x35, 0x51, 0x9d, 0xda, 0x99, - 0x2d, 0x1c, 0xbe, 0x10, 0x23, 0xcf, 0x5e, 0x88, 0x91, 0x2f, 0x8f, 0xc4, 0xc8, 0xe1, 0x91, 0xc8, - 0x3d, 0x3d, 0x12, 0xb9, 0x3f, 0x8e, 0x44, 0xee, 0xd1, 0x4b, 0x31, 0xf2, 0xf4, 0xa5, 0x18, 0x79, - 0xf6, 0x52, 0x8c, 0xdc, 0xff, 0xe7, 0x85, 0x78, 0xe0, 0xff, 0x29, 0xe4, 0xf7, 0x73, 0x75, 0xde, - 0xdf, 0x21, 0x6f, 0xfd, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xef, 0x91, 0x01, 0x67, 0x25, 0x0d, 0x00, - 0x00, + // 1450 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x51, 0x68, 0xdb, 0x56, + 0x17, 0xb6, 0x6c, 0xc7, 0x89, 0xaf, 0x9d, 0x44, 0xbd, 0x49, 0x13, 0xc7, 0x7f, 0x7f, 0xc9, 0xbf, + 0xfe, 0x51, 0x42, 0x69, 0x9d, 0x36, 0x1b, 0x1b, 0x4b, 0x61, 0x9b, 0x15, 0x2b, 0xab, 0x4b, 0xb1, + 0x8d, 0xac, 0x3a, 0xb4, 0x7b, 0x10, 0x8a, 0x7d, 0xeb, 0x68, 0xb3, 0x74, 0x3d, 0xeb, 0x3a, 0x4d, + 0xd8, 0xcb, 0x1e, 0x8b, 0x07, 0xa3, 0x8f, 0x85, 0x61, 0x28, 0x8c, 0xbd, 0xec, 0x79, 0x7b, 0xdd, + 0x73, 0x18, 0x83, 0x95, 0x3d, 0x95, 0x0d, 0xdc, 0x35, 0x85, 0x51, 0xf2, 0x98, 0x87, 0x3d, 0x0f, + 0xe9, 0x5e, 0xc5, 0xb2, 0x1d, 0x96, 0x79, 0x4f, 0xd1, 0x3d, 0xf7, 0x7c, 0xdf, 0x77, 0xce, 0xf1, + 0x39, 0x47, 0x0a, 0xb8, 0x54, 0xc3, 0x8e, 0x85, 0x9d, 0xb5, 0x06, 0xde, 0x5b, 0xdb, 0xbb, 0xb1, + 0x83, 0x88, 0x71, 0xc3, 0x7d, 0xce, 0xb6, 0xda, 0x98, 0x60, 0x08, 0xe9, 0x6d, 0xd6, 0xb5, 0xb0, + 0xdb, 0xb4, 0xc0, 0x10, 0x3b, 0x86, 0x83, 0x4e, 0x21, 0x35, 0x6c, 0xda, 0x14, 0x93, 0x5e, 0x6c, + 0xe0, 0x06, 0xf6, 0x1e, 0xd7, 0xdc, 0x27, 0x66, 0x5d, 0xa1, 0x28, 0x9d, 0x5e, 0x30, 0x5a, 0x7a, + 0x25, 0x36, 0x30, 0x6e, 0x34, 0xd1, 0x9a, 0x77, 0xda, 0xe9, 0x3c, 0x58, 0x23, 0xa6, 0x85, 0x1c, + 0x62, 0x58, 0x2d, 0x1f, 0x3b, 0xea, 0x60, 0xd8, 0x07, 0xec, 0x4a, 0x18, 0xbd, 0xaa, 0x77, 0xda, + 0x06, 0x31, 0x31, 0x0b, 0x46, 0xfa, 0x86, 0x03, 0x70, 0x1b, 0x99, 0x8d, 0x5d, 0x82, 0xea, 0x55, + 0x4c, 0x50, 0xa9, 0xe5, 0x5e, 0xc2, 0xb7, 0x41, 0x0c, 0x7b, 0x4f, 0x29, 0x2e, 0xc3, 0xad, 0xce, + 0xad, 0x0b, 0xd9, 0xf1, 0x44, 0xb3, 0x03, 0x7f, 0x95, 0x79, 0xc3, 0x6d, 0x10, 0x7b, 0xe8, 0xb1, + 0xa5, 0xc2, 0x19, 0x6e, 0x35, 0x2e, 0xbf, 0x7f, 0xd8, 0x17, 0x43, 0xbf, 0xf6, 0xc5, 0xcb, 0x0d, + 0x93, 0xec, 0x76, 0x76, 0xb2, 0x35, 0x6c, 0xb1, 0xdc, 0xd8, 0x9f, 0x6b, 0x4e, 0xfd, 0x93, 0x35, + 0x72, 0xd0, 0x42, 0x4e, 0x36, 0x8f, 0x6a, 0x27, 0x7d, 0x71, 0xf6, 0xc0, 0xb0, 0x9a, 0x1b, 0x12, + 0x65, 0x91, 0x54, 0x46, 0x27, 0x6d, 0x83, 0xa4, 0x86, 0xf6, 0x49, 0xb9, 0x8d, 0x5b, 0xd8, 0x31, + 0x9a, 0x70, 0x11, 0x4c, 0x11, 0x93, 0x34, 0x91, 0x17, 0x5f, 0x5c, 0xa5, 0x07, 0x98, 0x01, 0x89, + 0x3a, 0x72, 0x6a, 0x6d, 0x93, 0xc6, 0xee, 0xc5, 0xa0, 0x06, 0x4d, 0x1b, 0xf3, 0xaf, 0x9f, 0x8a, + 0xdc, 0x2f, 0xdf, 0x5d, 0x9b, 0xde, 0xc4, 0x36, 0x41, 0x36, 0x91, 0x7e, 0xe6, 0xc0, 0x74, 0x1e, + 0xb5, 0xb0, 0x63, 0x12, 0xf8, 0x0e, 0x48, 0xb4, 0x98, 0x80, 0x6e, 0xd6, 0x3d, 0xea, 0xa8, 0xbc, + 0x74, 0xd2, 0x17, 0x21, 0x0d, 0x2a, 0x70, 0x29, 0xa9, 0xc0, 0x3f, 0x15, 0xea, 0xf0, 0x12, 0x88, + 0xd7, 0x29, 0x07, 0x6e, 0x33, 0xd5, 0x81, 0x01, 0xd6, 0x40, 0xcc, 0xb0, 0x70, 0xc7, 0x26, 0xa9, + 0x48, 0x26, 0xb2, 0x9a, 0x58, 0x5f, 0xf1, 0x8b, 0xe9, 0x76, 0xc8, 0x69, 0x35, 0x37, 0xb1, 0x69, + 0xcb, 0xd7, 0xdd, 0x7a, 0x7d, 0xfb, 0x42, 0x5c, 0xfd, 0x07, 0xf5, 0x72, 0x01, 0x8e, 0xca, 0xa8, + 0x37, 0x66, 0x1e, 0x3d, 0x15, 0x43, 0xaf, 0x9f, 0x8a, 0x21, 0xe9, 0xcf, 0x18, 0x98, 0x39, 0xad, + 0xd3, 0x5b, 0x67, 0xa5, 0xb4, 0x70, 0xdc, 0x17, 0xc3, 0x66, 0xfd, 0xa4, 0x2f, 0xc6, 0x69, 0x62, + 0xa3, 0xf9, 0xdc, 0x04, 0xd3, 0x35, 0x5a, 0x1f, 0x2f, 0x9b, 0xc4, 0xfa, 0x62, 0x96, 0xf6, 0x51, + 0xd6, 0xef, 0xa3, 0x6c, 0xce, 0x3e, 0x90, 0x13, 0x3f, 0x0e, 0x0a, 0xa9, 0xfa, 0x08, 0x58, 0x05, + 0x31, 0x87, 0x18, 0xa4, 0xe3, 0xa4, 0x22, 0x5e, 0xef, 0x48, 0x67, 0xf5, 0x8e, 0x1f, 0x60, 0xc5, + 0xf3, 0x94, 0xd3, 0x27, 0x7d, 0x71, 0x69, 0xa4, 0xc8, 0x94, 0x44, 0x52, 0x19, 0x1b, 0x6c, 0x01, + 0xf8, 0xc0, 0xb4, 0x8d, 0xa6, 0x4e, 0x8c, 0x66, 0xf3, 0x40, 0x6f, 0x23, 0xa7, 0xd3, 0x24, 0xa9, + 0xa8, 0x17, 0x9f, 0x78, 0x96, 0x86, 0xe6, 0xfa, 0xa9, 0x9e, 0x9b, 0xfc, 0x3f, 0xb7, 0xb0, 0x27, + 0x7d, 0x71, 0x85, 0x8a, 0x8c, 0x13, 0x49, 0x2a, 0xef, 0x19, 0x03, 0x20, 0xf8, 0x11, 0x48, 0x38, + 0x9d, 0x1d, 0xcb, 0x24, 0xba, 0x3b, 0x71, 0xa9, 0x29, 0x4f, 0x2a, 0x3d, 0x56, 0x0a, 0xcd, 0x1f, + 0x47, 0x59, 0x60, 0x2a, 0xac, 0x5f, 0x02, 0x60, 0xe9, 0xf1, 0x0b, 0x91, 0x53, 0x01, 0xb5, 0xb8, + 0x00, 0x68, 0x02, 0x9e, 0xb5, 0x88, 0x8e, 0xec, 0x3a, 0x55, 0x88, 0x9d, 0xab, 0xf0, 0x7f, 0xa6, + 0xb0, 0x4c, 0x15, 0x46, 0x19, 0xa8, 0xcc, 0x1c, 0x33, 0x2b, 0x76, 0xdd, 0x93, 0x7a, 0xc4, 0x81, + 0x59, 0x82, 0x89, 0xd1, 0xd4, 0xd9, 0x45, 0x6a, 0xfa, 0xbc, 0x46, 0xbc, 0xc5, 0x74, 0x16, 0xa9, + 0xce, 0x10, 0x5a, 0x9a, 0xa8, 0x41, 0x93, 0x1e, 0xd6, 0x1f, 0xb1, 0x26, 0xb8, 0xb0, 0x87, 0x89, + 0x69, 0x37, 0xdc, 0x9f, 0xb7, 0xcd, 0x0a, 0x3b, 0x73, 0x6e, 0xda, 0x6f, 0xb0, 0x70, 0x52, 0x34, + 0x9c, 0x31, 0x0a, 0x9a, 0xf7, 0x3c, 0xb5, 0x57, 0x5c, 0xb3, 0x97, 0xf8, 0x03, 0xc0, 0x4c, 0x83, + 0x12, 0xc7, 0xcf, 0xd5, 0x92, 0x98, 0xd6, 0xd2, 0x90, 0xd6, 0x70, 0x85, 0x67, 0xa9, 0x95, 0x15, + 0x78, 0x23, 0xea, 0x6e, 0x15, 0xe9, 0x30, 0x0c, 0x12, 0xc1, 0xf6, 0xf9, 0x00, 0x44, 0x0e, 0x90, + 0x43, 0x37, 0x94, 0x9c, 0x9d, 0x60, 0x13, 0x16, 0x6c, 0xa2, 0xba, 0x50, 0x78, 0x0b, 0x4c, 0x1b, + 0x3b, 0x0e, 0x31, 0x4c, 0xb6, 0xcb, 0x26, 0x66, 0xf1, 0xe1, 0xf0, 0x3d, 0x10, 0xb6, 0xb1, 0x37, + 0x90, 0x93, 0x93, 0x84, 0x6d, 0x0c, 0x1b, 0x20, 0x69, 0x63, 0xfd, 0xa1, 0x49, 0x76, 0xf5, 0x3d, + 0x44, 0xb0, 0x37, 0x76, 0x71, 0x59, 0x99, 0x8c, 0xe9, 0xa4, 0x2f, 0x2e, 0xd0, 0xa2, 0x06, 0xb9, + 0x24, 0x15, 0xd8, 0x78, 0xdb, 0x24, 0xbb, 0x55, 0x44, 0x30, 0x2b, 0xe5, 0xf7, 0x1c, 0x88, 0xba, + 0xaf, 0x97, 0x7f, 0xbf, 0x92, 0x17, 0xc1, 0xd4, 0x1e, 0x26, 0xc8, 0x5f, 0xc7, 0xf4, 0x00, 0xb7, + 0xc0, 0x34, 0x7d, 0x53, 0x39, 0xa9, 0xa8, 0x37, 0x02, 0x97, 0xcf, 0x5a, 0x1c, 0xe3, 0x2f, 0x44, + 0x39, 0xea, 0x66, 0xaa, 0xfa, 0xe0, 0x8d, 0x99, 0x27, 0x6c, 0xdb, 0xde, 0x8e, 0xce, 0x44, 0xf8, + 0xa8, 0xff, 0xfe, 0x93, 0x7e, 0x08, 0x83, 0x59, 0xd6, 0xea, 0x65, 0xa3, 0x6d, 0x58, 0x0e, 0xfc, + 0x8a, 0x03, 0x09, 0xcb, 0xb4, 0x4f, 0x27, 0x8f, 0x3b, 0x6f, 0xf2, 0x74, 0x57, 0xe9, 0xb8, 0x2f, + 0x5e, 0x0c, 0xa0, 0xae, 0x62, 0xcb, 0x24, 0xc8, 0x6a, 0x91, 0x83, 0x41, 0xe6, 0x81, 0xeb, 0xc9, + 0x06, 0x12, 0x58, 0xa6, 0xed, 0x8f, 0xe3, 0x97, 0x1c, 0x80, 0x96, 0xb1, 0xef, 0x13, 0xe9, 0x2d, + 0xd4, 0x36, 0x71, 0x9d, 0x2d, 0xfd, 0x95, 0xb1, 0x21, 0xc9, 0xb3, 0x8f, 0x07, 0xfa, 0xc3, 0x1f, + 0xf7, 0xc5, 0x4b, 0xe3, 0xe0, 0xa1, 0x58, 0xd9, 0xba, 0x1d, 0xf7, 0x92, 0x9e, 0xb8, 0x63, 0xc4, + 0x5b, 0xc6, 0xbe, 0x5f, 0x2e, 0x6a, 0xfe, 0x82, 0x03, 0xc9, 0xaa, 0x37, 0x5b, 0xac, 0x7e, 0x9f, + 0x01, 0x36, 0x6b, 0x7e, 0x6c, 0xdc, 0x79, 0xb1, 0xdd, 0x64, 0xb1, 0x2d, 0x0f, 0xe1, 0x86, 0xc2, + 0x5a, 0x1c, 0x1a, 0xed, 0x60, 0x44, 0x49, 0x6a, 0x63, 0xd1, 0xfc, 0xe6, 0x4f, 0x34, 0x0b, 0xe6, + 0x3e, 0x88, 0x7d, 0xda, 0xc1, 0xed, 0x8e, 0xe5, 0x45, 0x91, 0x94, 0xe5, 0xc9, 0x3e, 0x6f, 0x8e, + 0xfb, 0x22, 0x4f, 0xf1, 0x83, 0x68, 0x54, 0xc6, 0x08, 0x6b, 0x20, 0x4e, 0x76, 0xdb, 0xc8, 0xd9, + 0xc5, 0x4d, 0xfa, 0x03, 0x24, 0x27, 0x1a, 0x2f, 0x4a, 0xbf, 0x70, 0x4a, 0x11, 0x50, 0x18, 0xf0, + 0xc2, 0x2e, 0x07, 0xe6, 0xdc, 0x99, 0xd3, 0x07, 0x52, 0x11, 0x4f, 0xaa, 0x36, 0xb1, 0x54, 0x6a, + 0x98, 0x67, 0xa8, 0xbe, 0x17, 0x59, 0x7d, 0x87, 0x3c, 0x24, 0x75, 0xd6, 0x35, 0x68, 0xfe, 0xf9, + 0xca, 0x1f, 0x1c, 0x00, 0x81, 0x6f, 0xce, 0xab, 0x60, 0xb9, 0x5a, 0xd2, 0x14, 0xbd, 0x54, 0xd6, + 0x0a, 0xa5, 0xa2, 0x7e, 0xb7, 0x58, 0x29, 0x2b, 0x9b, 0x85, 0xad, 0x82, 0x92, 0xe7, 0x43, 0xe9, + 0xf9, 0x6e, 0x2f, 0x93, 0xa0, 0x8e, 0x8a, 0x2b, 0x02, 0x25, 0x30, 0x1f, 0xf4, 0xbe, 0xa7, 0x54, + 0x78, 0x2e, 0x3d, 0xdb, 0xed, 0x65, 0xe2, 0xd4, 0xeb, 0x1e, 0x72, 0xe0, 0x15, 0xb0, 0x10, 0xf4, + 0xc9, 0xc9, 0x15, 0x2d, 0x57, 0x28, 0xf2, 0xe1, 0xf4, 0x85, 0x6e, 0x2f, 0x33, 0x4b, 0xfd, 0x72, + 0x6c, 0x41, 0x66, 0xc0, 0x5c, 0xd0, 0xb7, 0x58, 0xe2, 0x23, 0xe9, 0x64, 0xb7, 0x97, 0x99, 0xa1, + 0x6e, 0x45, 0x0c, 0xd7, 0x41, 0x6a, 0xd8, 0x43, 0xdf, 0x2e, 0x68, 0xb7, 0xf4, 0xaa, 0xa2, 0x95, + 0xf8, 0x68, 0x7a, 0xb1, 0xdb, 0xcb, 0xf0, 0xbe, 0xaf, 0xbf, 0xcd, 0xd2, 0xd1, 0x47, 0x5f, 0x0b, + 0xa1, 0x2b, 0x3f, 0x85, 0xc1, 0xdc, 0xf0, 0x07, 0x0f, 0xcc, 0x82, 0xff, 0x94, 0xd5, 0x52, 0xb9, + 0x54, 0xc9, 0xdd, 0xd1, 0x2b, 0x5a, 0x4e, 0xbb, 0x5b, 0x19, 0x49, 0xd8, 0x4b, 0x85, 0x3a, 0x17, + 0xcd, 0x26, 0xbc, 0x09, 0x84, 0x51, 0xff, 0xbc, 0x52, 0x2e, 0x55, 0x0a, 0x9a, 0x5e, 0x56, 0xd4, + 0x42, 0x29, 0xcf, 0x73, 0xe9, 0xe5, 0x6e, 0x2f, 0xb3, 0x40, 0x21, 0x43, 0x43, 0x05, 0xdf, 0x05, + 0xff, 0x1d, 0x05, 0x57, 0x4b, 0x5a, 0xa1, 0xf8, 0xa1, 0x8f, 0x0d, 0xa7, 0x97, 0xba, 0xbd, 0x0c, + 0xa4, 0xd8, 0x6a, 0x60, 0x02, 0xe0, 0x55, 0xb0, 0x34, 0x0a, 0x2d, 0xe7, 0x2a, 0x15, 0x25, 0xcf, + 0x47, 0xd2, 0x7c, 0xb7, 0x97, 0x49, 0x52, 0x4c, 0xd9, 0x70, 0x1c, 0x54, 0x87, 0xd7, 0x41, 0x6a, + 0xd4, 0x5b, 0x55, 0x6e, 0x2b, 0x9b, 0x9a, 0x92, 0xe7, 0xa3, 0x69, 0xd8, 0xed, 0x65, 0xe6, 0xa8, + 0xbf, 0x8a, 0x3e, 0x46, 0x35, 0x82, 0xce, 0xe4, 0xdf, 0xca, 0x15, 0xee, 0x28, 0x79, 0x7e, 0x2a, + 0xc8, 0xbf, 0x65, 0x98, 0x4d, 0x54, 0xa7, 0xe5, 0x94, 0x8b, 0x87, 0x2f, 0x85, 0xd0, 0xf3, 0x97, + 0x42, 0xe8, 0xf3, 0x23, 0x21, 0x74, 0x78, 0x24, 0x70, 0xcf, 0x8e, 0x04, 0xee, 0xf7, 0x23, 0x81, + 0x7b, 0xfc, 0x4a, 0x08, 0x3d, 0x7b, 0x25, 0x84, 0x9e, 0xbf, 0x12, 0x42, 0xf7, 0xff, 0x7e, 0x21, + 0xee, 0x7b, 0xff, 0xd0, 0x79, 0xfd, 0xbc, 0x13, 0xf3, 0x76, 0xc8, 0x9b, 0x7f, 0x05, 0x00, 0x00, + 0xff, 0xff, 0x51, 0x93, 0x54, 0x90, 0xeb, 0x0d, 0x00, 0x00, } func (this *TextProposal) Equal(that interface{}) bool { @@ -667,6 +710,44 @@ func (this *TallyResult) Equal(that interface{}) bool { } return true } +func (m *WeightedVoteOption) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WeightedVoteOption) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WeightedVoteOption) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Weight.Size() + i -= size + if _, err := m.Weight.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if m.Option != 0 { + i = encodeVarintGov(dAtA, i, uint64(m.Option)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *TextProposal) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -937,10 +1018,19 @@ func (m *Vote) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.Option != 0 { - i = encodeVarintGov(dAtA, i, uint64(m.Option)) - i-- - dAtA[i] = 0x18 + if len(m.Options) > 0 { + for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Options[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } } if len(m.Voter) > 0 { i -= len(m.Voter) @@ -1097,6 +1187,20 @@ func encodeVarintGov(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } +func (m *WeightedVoteOption) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Option != 0 { + n += 1 + sovGov(uint64(m.Option)) + } + l = m.Weight.Size() + n += 1 + l + sovGov(uint64(l)) + return n +} + func (m *TextProposal) Size() (n int) { if m == nil { return 0 @@ -1201,8 +1305,11 @@ func (m *Vote) Size() (n int) { if l > 0 { n += 1 + l + sovGov(uint64(l)) } - if m.Option != 0 { - n += 1 + sovGov(uint64(m.Option)) + if len(m.Options) > 0 { + for _, e := range m.Options { + l = e.Size() + n += 1 + l + sovGov(uint64(l)) + } } return n } @@ -1256,6 +1363,109 @@ func sovGov(x uint64) (n int) { func sozGov(x uint64) (n int) { return sovGov(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (m *WeightedVoteOption) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WeightedVoteOption: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WeightedVoteOption: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Option", wireType) + } + m.Option = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Option |= VoteOption(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Weight", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Weight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGov(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGov + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *TextProposal) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2094,11 +2304,11 @@ func (m *Vote) Unmarshal(dAtA []byte) error { } m.Voter = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Option", wireType) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) } - m.Option = 0 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGov @@ -2108,11 +2318,26 @@ func (m *Vote) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Option |= VoteOption(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } + if msglen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Options = append(m.Options, WeightedVoteOption{}) + if err := m.Options[len(m.Options)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGov(dAtA[iNdEx:]) diff --git a/x/gov/types/msgs.go b/x/gov/types/msgs.go index e97c25ce8..098aad437 100644 --- a/x/gov/types/msgs.go +++ b/x/gov/types/msgs.go @@ -16,12 +16,19 @@ import ( const ( TypeMsgDeposit = "deposit" TypeMsgVote = "vote" + TypeMsgVoteWeighted = "weighted_vote" TypeMsgSubmitProposal = "submit_proposal" + + // These are used for querying events by action. + TypeSvcMsgDeposit = "/cosmos.gov.v1beta1.Msg/Deposit" + TypeSvcMsgVote = "/cosmos.gov.v1beta1.Msg/Vote" + TypeSvcMsgVoteWeighted = "/cosmos.gov.v1beta1.Msg/VoteWeighted" + TypeSvcMsgSubmitProposal = "/cosmos.gov.v1beta1.Msg/SubmitProposal" ) var ( - _, _, _ sdk.Msg = &MsgSubmitProposal{}, &MsgDeposit{}, &MsgVote{} - _ types.UnpackInterfacesMessage = &MsgSubmitProposal{} + _, _, _, _ sdk.Msg = &MsgSubmitProposal{}, &MsgDeposit{}, &MsgVote{}, &MsgVoteWeighted{} + _ types.UnpackInterfacesMessage = &MsgSubmitProposal{} ) // NewMsgSubmitProposal creates a new MsgSubmitProposal. @@ -192,6 +199,7 @@ func (msg MsgVote) ValidateBasic() error { if msg.Voter == "" { return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Voter) } + if !ValidVoteOption(msg.Option) { return sdkerrors.Wrap(ErrInvalidVote, msg.Option.String()) } @@ -216,3 +224,67 @@ func (msg MsgVote) GetSigners() []sdk.AccAddress { voter, _ := sdk.AccAddressFromBech32(msg.Voter) return []sdk.AccAddress{voter} } + +// NewMsgVoteWeighted creates a message to cast a vote on an active proposal +//nolint:interfacer +func NewMsgVoteWeighted(voter sdk.AccAddress, proposalID uint64, options WeightedVoteOptions) *MsgVoteWeighted { + return &MsgVoteWeighted{proposalID, voter.String(), options} +} + +// Route implements Msg +func (msg MsgVoteWeighted) Route() string { return RouterKey } + +// Type implements Msg +func (msg MsgVoteWeighted) Type() string { return TypeMsgVoteWeighted } + +// ValidateBasic implements Msg +func (msg MsgVoteWeighted) ValidateBasic() error { + if msg.Voter == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Voter) + } + + if len(msg.Options) == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, WeightedVoteOptions(msg.Options).String()) + } + + totalWeight := sdk.NewDec(0) + usedOptions := make(map[VoteOption]bool) + for _, option := range msg.Options { + if !ValidWeightedVoteOption(option) { + return sdkerrors.Wrap(ErrInvalidVote, option.String()) + } + totalWeight = totalWeight.Add(option.Weight) + if usedOptions[option.Option] { + return sdkerrors.Wrap(ErrInvalidVote, "Duplicated vote option") + } + usedOptions[option.Option] = true + } + + if totalWeight.GT(sdk.NewDec(1)) { + return sdkerrors.Wrap(ErrInvalidVote, "Total weight overflow 1.00") + } + + if totalWeight.LT(sdk.NewDec(1)) { + return sdkerrors.Wrap(ErrInvalidVote, "Total weight lower than 1.00") + } + + return nil +} + +// String implements the Stringer interface +func (msg MsgVoteWeighted) String() string { + out, _ := yaml.Marshal(msg) + return string(out) +} + +// GetSignBytes implements Msg +func (msg MsgVoteWeighted) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(&msg) + return sdk.MustSortJSON(bz) +} + +// GetSigners implements Msg +func (msg MsgVoteWeighted) GetSigners() []sdk.AccAddress { + voter, _ := sdk.AccAddressFromBech32(msg.Voter) + return []sdk.AccAddress{voter} +} diff --git a/x/gov/types/msgs_test.go b/x/gov/types/msgs_test.go index 9d7d6a368..b942b8986 100644 --- a/x/gov/types/msgs_test.go +++ b/x/gov/types/msgs_test.go @@ -92,7 +92,7 @@ func TestMsgDeposit(t *testing.T) { } } -// test ValidateBasic for MsgDeposit +// test ValidateBasic for MsgVote func TestMsgVote(t *testing.T) { tests := []struct { proposalID uint64 @@ -118,6 +118,47 @@ func TestMsgVote(t *testing.T) { } } +// test ValidateBasic for MsgVoteWeighted +func TestMsgVoteWeighted(t *testing.T) { + tests := []struct { + proposalID uint64 + voterAddr sdk.AccAddress + options WeightedVoteOptions + expectPass bool + }{ + {0, addrs[0], NewNonSplitVoteOption(OptionYes), true}, + {0, sdk.AccAddress{}, NewNonSplitVoteOption(OptionYes), false}, + {0, addrs[0], NewNonSplitVoteOption(OptionNo), true}, + {0, addrs[0], NewNonSplitVoteOption(OptionNoWithVeto), true}, + {0, addrs[0], NewNonSplitVoteOption(OptionAbstain), true}, + {0, addrs[0], WeightedVoteOptions{ // weight sum > 1 + WeightedVoteOption{Option: OptionYes, Weight: sdk.NewDec(1)}, + WeightedVoteOption{Option: OptionAbstain, Weight: sdk.NewDec(1)}, + }, false}, + {0, addrs[0], WeightedVoteOptions{ // duplicate option + WeightedVoteOption{Option: OptionYes, Weight: sdk.NewDecWithPrec(5, 1)}, + WeightedVoteOption{Option: OptionYes, Weight: sdk.NewDecWithPrec(5, 1)}, + }, false}, + {0, addrs[0], WeightedVoteOptions{ // zero weight + WeightedVoteOption{Option: OptionYes, Weight: sdk.NewDec(0)}, + }, false}, + {0, addrs[0], WeightedVoteOptions{ // negative weight + WeightedVoteOption{Option: OptionYes, Weight: sdk.NewDec(-1)}, + }, false}, + {0, addrs[0], WeightedVoteOptions{}, false}, + {0, addrs[0], NewNonSplitVoteOption(VoteOption(0x13)), false}, + } + + for i, tc := range tests { + msg := NewMsgVoteWeighted(tc.voterAddr, tc.proposalID, tc.options) + if tc.expectPass { + require.Nil(t, msg.ValidateBasic(), "test: %v", i) + } else { + require.NotNil(t, msg.ValidateBasic(), "test: %v", i) + } + } +} + // this tests that Amino JSON MsgSubmitProposal.GetSignBytes() still works with Content as Any using the ModuleCdc func TestMsgSubmitProposal_GetSignBytes(t *testing.T) { msg, err := NewMsgSubmitProposal(NewTextProposal("test", "abcd"), sdk.NewCoins(), sdk.AccAddress{}) diff --git a/x/gov/types/proposal.go b/x/gov/types/proposal.go index 0c856647f..f9b59cbfc 100644 --- a/x/gov/types/proposal.go +++ b/x/gov/types/proposal.go @@ -18,15 +18,6 @@ const DefaultStartingProposalID uint64 = 1 // NewProposal creates a new Proposal instance func NewProposal(content Content, id uint64, submitTime, depositEndTime time.Time) (Proposal, error) { - p := Proposal{ - ProposalId: id, - Status: StatusDepositPeriod, - FinalTallyResult: EmptyTallyResult(), - TotalDeposit: sdk.NewCoins(), - SubmitTime: submitTime, - DepositEndTime: depositEndTime, - } - msg, ok := content.(proto.Message) if !ok { return Proposal{}, fmt.Errorf("%T does not implement proto.Message", content) @@ -37,6 +28,15 @@ func NewProposal(content Content, id uint64, submitTime, depositEndTime time.Tim return Proposal{}, err } + p := Proposal{ + ProposalId: id, + Status: StatusDepositPeriod, + FinalTallyResult: EmptyTallyResult(), + TotalDeposit: sdk.NewCoins(), + SubmitTime: submitTime, + DepositEndTime: depositEndTime, + } + p.Content = any return p, nil diff --git a/x/gov/types/query.pb.gw.go b/x/gov/types/query.pb.gw.go index 52814511b..4b6832d41 100644 --- a/x/gov/types/query.pb.gw.go +++ b/x/gov/types/query.pb.gw.go @@ -896,21 +896,21 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Proposal_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Proposal_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Proposals_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "gov", "v1beta1", "proposals"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Proposals_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "gov", "v1beta1", "proposals"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Vote_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "votes", "voter"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Vote_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "votes", "voter"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Votes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "votes"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Votes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "votes"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "gov", "v1beta1", "params", "params_type"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "gov", "v1beta1", "params", "params_type"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Deposit_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "deposits", "depositor"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Deposit_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "deposits", "depositor"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Deposits_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "deposits"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Deposits_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "deposits"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_TallyResult_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "tally"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_TallyResult_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "tally"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/gov/types/tally.go b/x/gov/types/tally.go index a4e9ee908..3d5efe51c 100644 --- a/x/gov/types/tally.go +++ b/x/gov/types/tally.go @@ -8,23 +8,23 @@ import ( // ValidatorGovInfo used for tallying type ValidatorGovInfo struct { - Address sdk.ValAddress // address of the validator operator - BondedTokens sdk.Int // Power of a Validator - DelegatorShares sdk.Dec // Total outstanding delegator shares - DelegatorDeductions sdk.Dec // Delegator deductions from validator's delegators voting independently - Vote VoteOption // Vote of the validator + Address sdk.ValAddress // address of the validator operator + BondedTokens sdk.Int // Power of a Validator + DelegatorShares sdk.Dec // Total outstanding delegator shares + DelegatorDeductions sdk.Dec // Delegator deductions from validator's delegators voting independently + Vote WeightedVoteOptions // Vote of the validator } // NewValidatorGovInfo creates a ValidatorGovInfo instance func NewValidatorGovInfo(address sdk.ValAddress, bondedTokens sdk.Int, delegatorShares, - delegatorDeductions sdk.Dec, vote VoteOption) ValidatorGovInfo { + delegatorDeductions sdk.Dec, options WeightedVoteOptions) ValidatorGovInfo { return ValidatorGovInfo{ Address: address, BondedTokens: bondedTokens, DelegatorShares: delegatorShares, DelegatorDeductions: delegatorDeductions, - Vote: vote, + Vote: options, } } diff --git a/x/gov/types/tx.pb.go b/x/gov/types/tx.pb.go index 0261e1c13..76bbd1c19 100644 --- a/x/gov/types/tx.pb.go +++ b/x/gov/types/tx.pb.go @@ -156,6 +156,45 @@ func (m *MsgVote) XXX_DiscardUnknown() { var xxx_messageInfo_MsgVote proto.InternalMessageInfo +// MsgVote defines a message to cast a vote. +type MsgVoteWeighted struct { + ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id" yaml:"proposal_id"` + Voter string `protobuf:"bytes,2,opt,name=voter,proto3" json:"voter,omitempty"` + Options []WeightedVoteOption `protobuf:"bytes,3,rep,name=options,proto3" json:"options"` +} + +func (m *MsgVoteWeighted) Reset() { *m = MsgVoteWeighted{} } +func (*MsgVoteWeighted) ProtoMessage() {} +func (*MsgVoteWeighted) Descriptor() ([]byte, []int) { + return fileDescriptor_3c053992595e3dce, []int{3} +} +func (m *MsgVoteWeighted) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVoteWeighted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVoteWeighted.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgVoteWeighted) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVoteWeighted.Merge(m, src) +} +func (m *MsgVoteWeighted) XXX_Size() int { + return m.Size() +} +func (m *MsgVoteWeighted) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVoteWeighted.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVoteWeighted proto.InternalMessageInfo + // MsgVoteResponse defines the Msg/Vote response type. type MsgVoteResponse struct { } @@ -164,7 +203,7 @@ func (m *MsgVoteResponse) Reset() { *m = MsgVoteResponse{} } func (m *MsgVoteResponse) String() string { return proto.CompactTextString(m) } func (*MsgVoteResponse) ProtoMessage() {} func (*MsgVoteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3c053992595e3dce, []int{3} + return fileDescriptor_3c053992595e3dce, []int{4} } func (m *MsgVoteResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -193,6 +232,43 @@ func (m *MsgVoteResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgVoteResponse proto.InternalMessageInfo +// MsgVoteWeightedResponse defines the Msg/VoteWeighted response type. +type MsgVoteWeightedResponse struct { +} + +func (m *MsgVoteWeightedResponse) Reset() { *m = MsgVoteWeightedResponse{} } +func (m *MsgVoteWeightedResponse) String() string { return proto.CompactTextString(m) } +func (*MsgVoteWeightedResponse) ProtoMessage() {} +func (*MsgVoteWeightedResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3c053992595e3dce, []int{5} +} +func (m *MsgVoteWeightedResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVoteWeightedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVoteWeightedResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgVoteWeightedResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVoteWeightedResponse.Merge(m, src) +} +func (m *MsgVoteWeightedResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgVoteWeightedResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVoteWeightedResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVoteWeightedResponse proto.InternalMessageInfo + // MsgDeposit defines a message to submit a deposit to an existing proposal. type MsgDeposit struct { ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id" yaml:"proposal_id"` @@ -203,7 +279,7 @@ type MsgDeposit struct { func (m *MsgDeposit) Reset() { *m = MsgDeposit{} } func (*MsgDeposit) ProtoMessage() {} func (*MsgDeposit) Descriptor() ([]byte, []int) { - return fileDescriptor_3c053992595e3dce, []int{4} + return fileDescriptor_3c053992595e3dce, []int{6} } func (m *MsgDeposit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -240,7 +316,7 @@ func (m *MsgDepositResponse) Reset() { *m = MsgDepositResponse{} } func (m *MsgDepositResponse) String() string { return proto.CompactTextString(m) } func (*MsgDepositResponse) ProtoMessage() {} func (*MsgDepositResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_3c053992595e3dce, []int{5} + return fileDescriptor_3c053992595e3dce, []int{7} } func (m *MsgDepositResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -273,7 +349,9 @@ func init() { proto.RegisterType((*MsgSubmitProposal)(nil), "cosmos.gov.v1beta1.MsgSubmitProposal") proto.RegisterType((*MsgSubmitProposalResponse)(nil), "cosmos.gov.v1beta1.MsgSubmitProposalResponse") proto.RegisterType((*MsgVote)(nil), "cosmos.gov.v1beta1.MsgVote") + proto.RegisterType((*MsgVoteWeighted)(nil), "cosmos.gov.v1beta1.MsgVoteWeighted") proto.RegisterType((*MsgVoteResponse)(nil), "cosmos.gov.v1beta1.MsgVoteResponse") + proto.RegisterType((*MsgVoteWeightedResponse)(nil), "cosmos.gov.v1beta1.MsgVoteWeightedResponse") proto.RegisterType((*MsgDeposit)(nil), "cosmos.gov.v1beta1.MsgDeposit") proto.RegisterType((*MsgDepositResponse)(nil), "cosmos.gov.v1beta1.MsgDepositResponse") } @@ -281,44 +359,48 @@ func init() { func init() { proto.RegisterFile("cosmos/gov/v1beta1/tx.proto", fileDescriptor_3c053992595e3dce) } var fileDescriptor_3c053992595e3dce = []byte{ - // 586 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xbf, 0x6f, 0xd3, 0x40, - 0x14, 0xb6, 0x93, 0xd2, 0xd0, 0x8b, 0x94, 0xd2, 0x53, 0x84, 0x12, 0xb7, 0xb2, 0x23, 0xa3, 0xa2, - 0x2c, 0xb1, 0x69, 0x90, 0x18, 0xca, 0x44, 0x8a, 0x10, 0x20, 0x45, 0x80, 0x91, 0x18, 0x58, 0x22, - 0xdb, 0x71, 0x8d, 0x45, 0xe2, 0x67, 0xe5, 0x2e, 0x51, 0xb3, 0x31, 0x22, 0x06, 0x60, 0x64, 0xcc, - 0xcc, 0x86, 0xc4, 0x1f, 0x51, 0x31, 0x75, 0x64, 0x40, 0x01, 0x25, 0x0b, 0x30, 0xf6, 0x2f, 0x40, - 0xbe, 0x1f, 0x69, 0xd5, 0xa4, 0x01, 0xa4, 0x4e, 0xc9, 0x7b, 0xdf, 0xfb, 0x3e, 0xdd, 0xf7, 0xdd, - 0xf3, 0xa1, 0x4d, 0x1f, 0x48, 0x17, 0x88, 0x1d, 0xc2, 0xc0, 0x1e, 0xec, 0x78, 0x01, 0x75, 0x77, - 0x6c, 0x7a, 0x60, 0x25, 0x3d, 0xa0, 0x80, 0x31, 0x07, 0xad, 0x10, 0x06, 0x96, 0x00, 0x35, 0x5d, - 0x10, 0x3c, 0x97, 0x04, 0x33, 0x86, 0x0f, 0x51, 0xcc, 0x39, 0xda, 0xd6, 0x02, 0xc1, 0x94, 0xcf, - 0xd1, 0x32, 0x47, 0x5b, 0xac, 0xb2, 0x85, 0x3c, 0x87, 0x8a, 0x21, 0x84, 0xc0, 0xfb, 0xe9, 0x3f, - 0x49, 0x08, 0x01, 0xc2, 0x4e, 0x60, 0xb3, 0xca, 0xeb, 0xef, 0xdb, 0x6e, 0x3c, 0xe4, 0x90, 0xf9, - 0x2e, 0x83, 0x36, 0x9a, 0x24, 0x7c, 0xda, 0xf7, 0xba, 0x11, 0x7d, 0xdc, 0x83, 0x04, 0x88, 0xdb, - 0xc1, 0xb7, 0x51, 0xce, 0x87, 0x98, 0x06, 0x31, 0x2d, 0xa9, 0x15, 0xb5, 0x9a, 0xaf, 0x17, 0x2d, - 0x2e, 0x61, 0x49, 0x09, 0xeb, 0x4e, 0x3c, 0x6c, 0xe4, 0xbf, 0x7c, 0xae, 0xe5, 0xf6, 0xf8, 0xa0, - 0x23, 0x19, 0xf8, 0xad, 0x8a, 0xd6, 0xa3, 0x38, 0xa2, 0x91, 0xdb, 0x69, 0xb5, 0x83, 0x04, 0x48, - 0x44, 0x4b, 0x99, 0x4a, 0xb6, 0x9a, 0xaf, 0x97, 0x2d, 0x71, 0xd8, 0xd4, 0xb7, 0x0c, 0xc3, 0xda, - 0x83, 0x28, 0x6e, 0x3c, 0x3c, 0x1c, 0x1b, 0xca, 0xf1, 0xd8, 0xb8, 0x3a, 0x74, 0xbb, 0x9d, 0x5d, - 0xf3, 0x0c, 0xdf, 0xfc, 0xf8, 0xdd, 0xa8, 0x86, 0x11, 0x7d, 0xd1, 0xf7, 0x2c, 0x1f, 0xba, 0xc2, - 0xb3, 0xf8, 0xa9, 0x91, 0xf6, 0x4b, 0x9b, 0x0e, 0x93, 0x80, 0x30, 0x29, 0xe2, 0x14, 0x04, 0xfb, - 0x2e, 0x27, 0x63, 0x0d, 0x5d, 0x4e, 0x98, 0xb3, 0xa0, 0x57, 0xca, 0x56, 0xd4, 0xea, 0x9a, 0x33, - 0xab, 0x77, 0xaf, 0xbc, 0x1e, 0x19, 0xca, 0x87, 0x91, 0xa1, 0xfc, 0x1c, 0x19, 0xca, 0xab, 0x6f, - 0x15, 0xc5, 0xf4, 0x51, 0x79, 0x2e, 0x10, 0x27, 0x20, 0x09, 0xc4, 0x24, 0xc0, 0xf7, 0x50, 0x3e, - 0x11, 0xbd, 0x56, 0xd4, 0x66, 0xe1, 0xac, 0x34, 0xb6, 0x7f, 0x8f, 0x8d, 0xd3, 0xed, 0xe3, 0xb1, - 0x81, 0xb9, 0x8d, 0x53, 0x4d, 0xd3, 0x41, 0xb2, 0x7a, 0xd0, 0x36, 0x3f, 0xa9, 0x28, 0xd7, 0x24, - 0xe1, 0x33, 0xa0, 0x17, 0xa6, 0x89, 0x8b, 0xe8, 0xd2, 0x00, 0x68, 0xd0, 0x2b, 0x65, 0x98, 0x47, - 0x5e, 0xe0, 0x5b, 0x68, 0x15, 0x12, 0x1a, 0x41, 0xcc, 0xac, 0x17, 0xea, 0xba, 0x35, 0xbf, 0x8f, - 0x56, 0x7a, 0x8e, 0x47, 0x6c, 0xca, 0x11, 0xd3, 0x0b, 0x82, 0xd9, 0x40, 0xeb, 0xe2, 0xc8, 0x32, - 0x0e, 0xf3, 0x97, 0x8a, 0x50, 0x93, 0x84, 0x32, 0xe8, 0x8b, 0x72, 0xb2, 0x85, 0xd6, 0xc4, 0xc5, - 0x83, 0x74, 0x73, 0xd2, 0xc0, 0x3e, 0x5a, 0x75, 0xbb, 0xd0, 0x8f, 0x69, 0x29, 0xfb, 0xb7, 0xad, - 0xba, 0x91, 0x6e, 0xd5, 0x7f, 0xed, 0x8e, 0x90, 0x5e, 0x60, 0xbf, 0x88, 0xf0, 0x89, 0x55, 0x99, - 0x40, 0xfd, 0x4d, 0x06, 0x65, 0x9b, 0x24, 0xc4, 0xfb, 0xa8, 0x70, 0xe6, 0x1b, 0xda, 0x5e, 0x14, - 0xf4, 0xdc, 0x66, 0x69, 0xb5, 0x7f, 0x1a, 0x9b, 0x2d, 0xe0, 0x7d, 0xb4, 0xc2, 0x96, 0x66, 0xf3, - 0x1c, 0x5a, 0x0a, 0x6a, 0xd7, 0x96, 0x80, 0x33, 0xa5, 0x27, 0x28, 0x27, 0xef, 0x4d, 0x3f, 0x67, - 0x5e, 0xe0, 0xda, 0xf5, 0xe5, 0xb8, 0x94, 0x6c, 0x34, 0x0e, 0x27, 0xba, 0x7a, 0x34, 0xd1, 0xd5, - 0x1f, 0x13, 0x5d, 0x7d, 0x3f, 0xd5, 0x95, 0xa3, 0xa9, 0xae, 0x7c, 0x9d, 0xea, 0xca, 0xf3, 0xe5, - 0x17, 0x70, 0xc0, 0x1e, 0x3a, 0x76, 0x0d, 0xde, 0x2a, 0x7b, 0x61, 0x6e, 0xfe, 0x09, 0x00, 0x00, - 0xff, 0xff, 0x7f, 0x09, 0x3f, 0x57, 0x54, 0x05, 0x00, 0x00, + // 653 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x3f, 0x6f, 0xd3, 0x5e, + 0x14, 0xb5, 0x93, 0xfe, 0x9a, 0x5f, 0x6f, 0x50, 0x4b, 0xad, 0x08, 0x12, 0xb7, 0xb2, 0x23, 0xa3, + 0x56, 0x91, 0x50, 0x6d, 0x1a, 0x24, 0x86, 0x32, 0x91, 0xa2, 0x0a, 0x90, 0x22, 0xc0, 0x48, 0x20, + 0xb1, 0x14, 0x27, 0x71, 0x5d, 0x8b, 0xc4, 0xd7, 0xca, 0x7b, 0x89, 0x9a, 0x8d, 0x91, 0x09, 0x18, + 0x19, 0x3b, 0xb3, 0x21, 0xf1, 0x21, 0x0a, 0x53, 0x47, 0x06, 0x14, 0x50, 0xbb, 0x00, 0x62, 0xea, + 0x27, 0x40, 0x7e, 0x7f, 0xdc, 0xd2, 0xa6, 0x51, 0x91, 0xca, 0x94, 0xbc, 0x7b, 0xee, 0x39, 0xb9, + 0xe7, 0xbe, 0x7b, 0x5f, 0x60, 0xae, 0x89, 0xa4, 0x83, 0xc4, 0x09, 0xb0, 0xef, 0xf4, 0x97, 0x1b, + 0x3e, 0xf5, 0x96, 0x1d, 0xba, 0x65, 0xc7, 0x5d, 0xa4, 0xa8, 0x69, 0x1c, 0xb4, 0x03, 0xec, 0xdb, + 0x02, 0xd4, 0x0d, 0x41, 0x68, 0x78, 0xc4, 0x4f, 0x19, 0x4d, 0x0c, 0x23, 0xce, 0xd1, 0xe7, 0x47, + 0x08, 0x26, 0x7c, 0x8e, 0x96, 0x38, 0xba, 0xce, 0x4e, 0x8e, 0x90, 0xe7, 0x50, 0x21, 0xc0, 0x00, + 0x79, 0x3c, 0xf9, 0x26, 0x09, 0x01, 0x62, 0xd0, 0xf6, 0x1d, 0x76, 0x6a, 0xf4, 0x36, 0x1c, 0x2f, + 0x1a, 0x70, 0xc8, 0x7a, 0x9d, 0x81, 0xd9, 0x3a, 0x09, 0x1e, 0xf5, 0x1a, 0x9d, 0x90, 0x3e, 0xe8, + 0x62, 0x8c, 0xc4, 0x6b, 0x6b, 0x37, 0x21, 0xd7, 0xc4, 0x88, 0xfa, 0x11, 0x2d, 0xaa, 0x65, 0xb5, + 0x92, 0xaf, 0x16, 0x6c, 0x2e, 0x61, 0x4b, 0x09, 0xfb, 0x56, 0x34, 0xa8, 0xe5, 0x3f, 0x7d, 0x58, + 0xca, 0xad, 0xf2, 0x44, 0x57, 0x32, 0xb4, 0x57, 0x2a, 0xcc, 0x84, 0x51, 0x48, 0x43, 0xaf, 0xbd, + 0xde, 0xf2, 0x63, 0x24, 0x21, 0x2d, 0x66, 0xca, 0xd9, 0x4a, 0xbe, 0x5a, 0xb2, 0x45, 0xb1, 0x89, + 0x6f, 0xd9, 0x0c, 0x7b, 0x15, 0xc3, 0xa8, 0x76, 0x6f, 0x67, 0x68, 0x2a, 0x07, 0x43, 0xf3, 0xd2, + 0xc0, 0xeb, 0xb4, 0x57, 0xac, 0x63, 0x7c, 0xeb, 0xdd, 0x57, 0xb3, 0x12, 0x84, 0x74, 0xb3, 0xd7, + 0xb0, 0x9b, 0xd8, 0x11, 0x9e, 0xc5, 0xc7, 0x12, 0x69, 0x3d, 0x77, 0xe8, 0x20, 0xf6, 0x09, 0x93, + 0x22, 0xee, 0xb4, 0x60, 0xdf, 0xe6, 0x64, 0x4d, 0x87, 0xff, 0x63, 0xe6, 0xcc, 0xef, 0x16, 0xb3, + 0x65, 0xb5, 0x32, 0xe5, 0xa6, 0xe7, 0x95, 0x8b, 0x2f, 0xb7, 0x4d, 0xe5, 0xed, 0xb6, 0xa9, 0x7c, + 0xdf, 0x36, 0x95, 0x17, 0x5f, 0xca, 0x8a, 0xd5, 0x84, 0xd2, 0x89, 0x86, 0xb8, 0x3e, 0x89, 0x31, + 0x22, 0xbe, 0xb6, 0x06, 0xf9, 0x58, 0xc4, 0xd6, 0xc3, 0x16, 0x6b, 0xce, 0x44, 0x6d, 0xe1, 0xe7, + 0xd0, 0x3c, 0x1a, 0x3e, 0x18, 0x9a, 0x1a, 0xb7, 0x71, 0x24, 0x68, 0xb9, 0x20, 0x4f, 0x77, 0x5b, + 0xd6, 0x7b, 0x15, 0x72, 0x75, 0x12, 0x3c, 0x46, 0x7a, 0x6e, 0x9a, 0x5a, 0x01, 0xfe, 0xeb, 0x23, + 0xf5, 0xbb, 0xc5, 0x0c, 0xf3, 0xc8, 0x0f, 0xda, 0x0d, 0x98, 0xc4, 0x98, 0x86, 0x18, 0x31, 0xeb, + 0xd3, 0x55, 0xc3, 0x3e, 0x39, 0x8f, 0x76, 0x52, 0xc7, 0x7d, 0x96, 0xe5, 0x8a, 0xec, 0x11, 0x8d, + 0xf9, 0xa8, 0xc2, 0x8c, 0xa8, 0xf9, 0x89, 0x1f, 0x06, 0x9b, 0xd4, 0x6f, 0xfd, 0xe3, 0xda, 0xd7, + 0x20, 0xc7, 0xab, 0x21, 0xc5, 0x2c, 0x1b, 0xa0, 0xc5, 0x51, 0xc5, 0xcb, 0x62, 0x0e, 0x4d, 0xd4, + 0x26, 0x92, 0x69, 0x72, 0x25, 0x79, 0x84, 0x97, 0xd9, 0xd4, 0x8a, 0xbc, 0x5a, 0xab, 0x04, 0x97, + 0x8f, 0xb9, 0x4b, 0xa1, 0x1f, 0x2a, 0x40, 0x9d, 0x04, 0x72, 0x9e, 0xce, 0xcb, 0xf4, 0x3c, 0x4c, + 0x89, 0xf9, 0x46, 0x69, 0xfc, 0x30, 0xa0, 0x35, 0x61, 0xd2, 0xeb, 0x60, 0x2f, 0xa2, 0xc2, 0xfb, + 0x98, 0xe5, 0xb9, 0x96, 0xd8, 0xfd, 0xab, 0x15, 0x11, 0xd2, 0x23, 0x3a, 0x53, 0x00, 0xed, 0xd0, + 0xaa, 0xec, 0x40, 0xf5, 0x57, 0x06, 0xb2, 0x75, 0x12, 0x68, 0x1b, 0x30, 0x7d, 0xec, 0xa9, 0x58, + 0x18, 0x75, 0x25, 0x27, 0x16, 0x48, 0x5f, 0x3a, 0x53, 0x5a, 0xba, 0x67, 0x77, 0x60, 0x82, 0xed, + 0xc6, 0xdc, 0x29, 0xb4, 0x04, 0xd4, 0xaf, 0x8c, 0x01, 0x53, 0xa5, 0x67, 0x70, 0xe1, 0x8f, 0x89, + 0x1d, 0x47, 0x92, 0x49, 0xfa, 0xd5, 0x33, 0x24, 0xa5, 0xbf, 0xf0, 0x10, 0x72, 0x72, 0x32, 0x8c, + 0x53, 0x78, 0x02, 0xd7, 0x17, 0xc7, 0xe3, 0x52, 0xb2, 0x56, 0xdb, 0xd9, 0x33, 0xd4, 0xdd, 0x3d, + 0x43, 0xfd, 0xb6, 0x67, 0xa8, 0x6f, 0xf6, 0x0d, 0x65, 0x77, 0xdf, 0x50, 0x3e, 0xef, 0x1b, 0xca, + 0xd3, 0xf1, 0x57, 0xbc, 0xc5, 0xfe, 0x31, 0xd8, 0x45, 0x37, 0x26, 0xd9, 0x53, 0x7d, 0xfd, 0x77, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x6c, 0x75, 0x57, 0x9d, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -337,6 +419,8 @@ type MsgClient interface { SubmitProposal(ctx context.Context, in *MsgSubmitProposal, opts ...grpc.CallOption) (*MsgSubmitProposalResponse, error) // Vote defines a method to add a vote on a specific proposal. Vote(ctx context.Context, in *MsgVote, opts ...grpc.CallOption) (*MsgVoteResponse, error) + // WeightedVote defines a method to add a weighted vote on a specific proposal. + VoteWeighted(ctx context.Context, in *MsgVoteWeighted, opts ...grpc.CallOption) (*MsgVoteWeightedResponse, error) // Deposit defines a method to add deposit on a specific proposal. Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error) } @@ -367,6 +451,15 @@ func (c *msgClient) Vote(ctx context.Context, in *MsgVote, opts ...grpc.CallOpti return out, nil } +func (c *msgClient) VoteWeighted(ctx context.Context, in *MsgVoteWeighted, opts ...grpc.CallOption) (*MsgVoteWeightedResponse, error) { + out := new(MsgVoteWeightedResponse) + err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Msg/VoteWeighted", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *msgClient) Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error) { out := new(MsgDepositResponse) err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Msg/Deposit", in, out, opts...) @@ -382,6 +475,8 @@ type MsgServer interface { SubmitProposal(context.Context, *MsgSubmitProposal) (*MsgSubmitProposalResponse, error) // Vote defines a method to add a vote on a specific proposal. Vote(context.Context, *MsgVote) (*MsgVoteResponse, error) + // WeightedVote defines a method to add a weighted vote on a specific proposal. + VoteWeighted(context.Context, *MsgVoteWeighted) (*MsgVoteWeightedResponse, error) // Deposit defines a method to add deposit on a specific proposal. Deposit(context.Context, *MsgDeposit) (*MsgDepositResponse, error) } @@ -396,6 +491,9 @@ func (*UnimplementedMsgServer) SubmitProposal(ctx context.Context, req *MsgSubmi func (*UnimplementedMsgServer) Vote(ctx context.Context, req *MsgVote) (*MsgVoteResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Vote not implemented") } +func (*UnimplementedMsgServer) VoteWeighted(ctx context.Context, req *MsgVoteWeighted) (*MsgVoteWeightedResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VoteWeighted not implemented") +} func (*UnimplementedMsgServer) Deposit(ctx context.Context, req *MsgDeposit) (*MsgDepositResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Deposit not implemented") } @@ -440,6 +538,24 @@ func _Msg_Vote_Handler(srv interface{}, ctx context.Context, dec func(interface{ return interceptor(ctx, in, info, handler) } +func _Msg_VoteWeighted_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgVoteWeighted) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).VoteWeighted(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Msg/VoteWeighted", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).VoteWeighted(ctx, req.(*MsgVoteWeighted)) + } + return interceptor(ctx, in, info, handler) +} + func _Msg_Deposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MsgDeposit) if err := dec(in); err != nil { @@ -470,6 +586,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "Vote", Handler: _Msg_Vote_Handler, }, + { + MethodName: "VoteWeighted", + Handler: _Msg_VoteWeighted_Handler, + }, { MethodName: "Deposit", Handler: _Msg_Deposit_Handler, @@ -603,6 +723,55 @@ func (m *MsgVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *MsgVoteWeighted) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgVoteWeighted) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVoteWeighted) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Options) > 0 { + for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Options[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Voter) > 0 { + i -= len(m.Voter) + copy(dAtA[i:], m.Voter) + i = encodeVarintTx(dAtA, i, uint64(len(m.Voter))) + i-- + dAtA[i] = 0x12 + } + if m.ProposalId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *MsgVoteResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -626,6 +795,29 @@ func (m *MsgVoteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *MsgVoteWeightedResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgVoteWeightedResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVoteWeightedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func (m *MsgDeposit) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -763,6 +955,28 @@ func (m *MsgVote) Size() (n int) { return n } +func (m *MsgVoteWeighted) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovTx(uint64(m.ProposalId)) + } + l = len(m.Voter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Options) > 0 { + for _, e := range m.Options { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + func (m *MsgVoteResponse) Size() (n int) { if m == nil { return 0 @@ -772,6 +986,15 @@ func (m *MsgVoteResponse) Size() (n int) { return n } +func (m *MsgVoteWeightedResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func (m *MsgDeposit) Size() (n int) { if m == nil { return 0 @@ -1150,6 +1373,141 @@ func (m *MsgVote) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgVoteWeighted) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgVoteWeighted: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVoteWeighted: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProposalId", wireType) + } + m.ProposalId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProposalId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Voter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Voter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Options = append(m.Options, WeightedVoteOption{}) + if err := m.Options[len(m.Options)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *MsgVoteResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -1200,6 +1558,56 @@ func (m *MsgVoteResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgVoteWeightedResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgVoteWeightedResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVoteWeightedResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *MsgDeposit) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/gov/types/vote.go b/x/gov/types/vote.go index 363887a28..6b7c1660d 100644 --- a/x/gov/types/vote.go +++ b/x/gov/types/vote.go @@ -1,7 +1,9 @@ package types import ( + "encoding/json" "fmt" + "strings" yaml "gopkg.in/yaml.v2" @@ -10,8 +12,8 @@ import ( // NewVote creates a new Vote instance //nolint:interfacer -func NewVote(proposalID uint64, voter sdk.AccAddress, option VoteOption) Vote { - return Vote{proposalID, voter.String(), option} +func NewVote(proposalID uint64, voter sdk.AccAddress, options WeightedVoteOptions) Vote { + return Vote{proposalID, voter.String(), options} } func (v Vote) String() string { @@ -43,7 +45,7 @@ func (v Votes) String() string { } out := fmt.Sprintf("Votes for Proposal %d:", v[0].ProposalId) for _, vot := range v { - out += fmt.Sprintf("\n %s: %s", vot.Voter, vot.Option) + out += fmt.Sprintf("\n %s: %s", vot.Voter, vot.Options) } return out } @@ -53,6 +55,35 @@ func (v Vote) Empty() bool { return v.String() == Vote{}.String() } +// NewNonSplitVoteOption creates a single option vote with weight 1 +func NewNonSplitVoteOption(option VoteOption) WeightedVoteOptions { + return WeightedVoteOptions{{option, sdk.NewDec(1)}} +} + +func (v WeightedVoteOption) String() string { + out, _ := json.Marshal(v) + return string(out) +} + +// WeightedVoteOptions describes array of WeightedVoteOptions +type WeightedVoteOptions []WeightedVoteOption + +func (v WeightedVoteOptions) String() (out string) { + for _, opt := range v { + out += opt.String() + "\n" + } + + return strings.TrimSpace(out) +} + +// ValidWeightedVoteOption returns true if the sub vote is valid and false otherwise. +func ValidWeightedVoteOption(option WeightedVoteOption) bool { + if !option.Weight.IsPositive() || option.Weight.GT(sdk.NewDec(1)) { + return false + } + return ValidVoteOption(option.Option) +} + // VoteOptionFromString returns a VoteOption from a string. It returns an error // if the string is invalid. func VoteOptionFromString(str string) (VoteOption, error) { @@ -63,6 +94,28 @@ func VoteOptionFromString(str string) (VoteOption, error) { return VoteOption(option), nil } +// WeightedVoteOptionsFromString returns weighted vote options from string. It returns an error +// if the string is invalid. +func WeightedVoteOptionsFromString(str string) (WeightedVoteOptions, error) { + options := WeightedVoteOptions{} + for _, option := range strings.Split(str, ",") { + fields := strings.Split(option, "=") + option, err := VoteOptionFromString(fields[0]) + if err != nil { + return options, err + } + if len(fields) < 2 { + return options, fmt.Errorf("weight field does not exist for %s option", fields[0]) + } + weight, err := sdk.NewDecFromStr(fields[1]) + if err != nil { + return options, err + } + options = append(options, WeightedVoteOption{option, weight}) + } + return options, nil +} + // ValidVoteOption returns true if the vote option is valid and false otherwise. func ValidVoteOption(option VoteOption) bool { if option == OptionYes || diff --git a/x/ibc/applications/transfer/client/cli/tx.go b/x/ibc/applications/transfer/client/cli/tx.go index c9c6fde51..1f9e92f63 100644 --- a/x/ibc/applications/transfer/client/cli/tx.go +++ b/x/ibc/applications/transfer/client/cli/tx.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" @@ -96,11 +97,14 @@ to the counterparty channel. Any timeout set to 0 is disabled.`), msg := types.NewMsgTransfer( srcPort, srcChannel, coin, sender, receiver, timeoutHeight, timeoutTimestamp, ) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.Transfer(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } diff --git a/x/ibc/applications/transfer/keeper/mbt_relay_test.go b/x/ibc/applications/transfer/keeper/mbt_relay_test.go index defcbbbc8..cd64fbabc 100644 --- a/x/ibc/applications/transfer/keeper/mbt_relay_test.go +++ b/x/ibc/applications/transfer/keeper/mbt_relay_test.go @@ -262,22 +262,6 @@ func BankOfChain(chain *ibctesting.TestChain) Bank { return bank } -// Set balances of the chain bank for balances present in the bank -func (suite *KeeperTestSuite) SetChainBankBalances(chain *ibctesting.TestChain, bank *Bank) error { - for coin, amount := range bank.balances { - address, err := sdk.AccAddressFromBech32(coin.Address) - if err != nil { - return err - } - trace := types.ParseDenomTrace(coin.Denom) - err = chain.App.BankKeeper.SetBalance(chain.GetContext(), address, sdk.NewCoin(trace.IBCDenom(), amount)) - if err != nil { - return err - } - } - return nil -} - // Check that the state of the bank is the bankBefore + expectedBankChange func (suite *KeeperTestSuite) CheckBankBalances(chain *ibctesting.TestChain, bankBefore *Bank, expectedBankChange *Bank) error { bankAfter := BankOfChain(chain) diff --git a/x/ibc/applications/transfer/keeper/relay_test.go b/x/ibc/applications/transfer/keeper/relay_test.go index 9f3031751..89058ac29 100644 --- a/x/ibc/applications/transfer/keeper/relay_test.go +++ b/x/ibc/applications/transfer/keeper/relay_test.go @@ -3,6 +3,8 @@ package keeper_test import ( "fmt" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" @@ -257,8 +259,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { trace = types.ParseDenomTrace(sdk.DefaultBondDenom) coin := sdk.NewCoin(sdk.DefaultBondDenom, amount) - err := suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), escrow, sdk.NewCoins(coin)) - suite.Require().NoError(err) + suite.Require().NoError(simapp.FundAccount(suite.chainA.App, suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) }, false, true}, {"unsuccessful refund from source", failedAck, func() { @@ -270,8 +271,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { trace = types.ParseDenomTrace(types.GetPrefixedDenom(channelA.PortID, channelA.ID, sdk.DefaultBondDenom)) coin := sdk.NewCoin(trace.IBCDenom(), amount) - err := suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), escrow, sdk.NewCoins(coin)) - suite.Require().NoError(err) + suite.Require().NoError(simapp.FundAccount(suite.chainA.App, suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) }, false, true}, } @@ -332,8 +332,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { trace = types.ParseDenomTrace(sdk.DefaultBondDenom) coin := sdk.NewCoin(trace.IBCDenom(), amount) - err := suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), escrow, sdk.NewCoins(coin)) - suite.Require().NoError(err) + suite.Require().NoError(simapp.FundAccount(suite.chainA.App, suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) }, true}, {"successful timeout from external chain", func() { @@ -341,8 +340,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { trace = types.ParseDenomTrace(types.GetPrefixedDenom(channelA.PortID, channelA.ID, sdk.DefaultBondDenom)) coin := sdk.NewCoin(trace.IBCDenom(), amount) - err := suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), escrow, sdk.NewCoins(coin)) - suite.Require().NoError(err) + suite.Require().NoError(simapp.FundAccount(suite.chainA.App, suite.chainA.GetContext(), escrow, sdk.NewCoins(coin))) }, true}, {"no balance for coin denom", func() { diff --git a/x/ibc/applications/transfer/module.go b/x/ibc/applications/transfer/module.go index 25290d69a..b93253ad7 100644 --- a/x/ibc/applications/transfer/module.go +++ b/x/ibc/applications/transfer/module.go @@ -157,8 +157,6 @@ func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.V return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the transfer module. @@ -186,8 +184,6 @@ func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.Weig return nil } -//____________________________________________________________________________ - // ValidateTransferChannelParams does validation of a newly created transfer channel. A transfer // channel must be UNORDERED, use the correct port (by default 'transfer'), and use the current // supported version. Only 2^32 channels are allowed to be created. diff --git a/x/ibc/applications/transfer/types/query.pb.gw.go b/x/ibc/applications/transfer/types/query.pb.gw.go index 99a7f1906..007ed6682 100644 --- a/x/ibc/applications/transfer/types/query.pb.gw.go +++ b/x/ibc/applications/transfer/types/query.pb.gw.go @@ -310,11 +310,11 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_DenomTrace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "applications", "transfer", "v1beta1", "denom_traces", "hash"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DenomTrace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "applications", "transfer", "v1beta1", "denom_traces", "hash"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DenomTraces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "applications", "transfer", "v1beta1", "denom_traces"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DenomTraces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "applications", "transfer", "v1beta1", "denom_traces"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "applications", "transfer", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "applications", "transfer", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/ibc/core/02-client/client/cli/tx.go b/x/ibc/core/02-client/client/cli/tx.go index d036a5766..bdaa53a8a 100644 --- a/x/ibc/core/02-client/client/cli/tx.go +++ b/x/ibc/core/02-client/client/cli/tx.go @@ -11,7 +11,11 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/version" + govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) @@ -70,11 +74,14 @@ func NewCreateClientCmd() *cobra.Command { return err } - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.CreateClient(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -120,11 +127,14 @@ func NewUpdateClientCmd() *cobra.Command { return err } - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.UpdateClient(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } } @@ -165,11 +175,14 @@ func NewSubmitMisbehaviourCmd() *cobra.Command { return err } - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.SubmitMisbehaviour(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } } @@ -232,11 +245,14 @@ func NewUpgradeClientCmd() *cobra.Command { return err } - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.UpgradeClient(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -244,3 +260,69 @@ func NewUpgradeClientCmd() *cobra.Command { return cmd } + +// NewCmdSubmitUpdateClientProposal implements a command handler for submitting an update IBC client proposal transaction. +func NewCmdSubmitUpdateClientProposal() *cobra.Command { + cmd := &cobra.Command{ + Use: "update-client [subject-client-id] [substitute-client-id] [initial-height] [flags]", + Args: cobra.ExactArgs(3), + Short: "Submit an update IBC client proposal", + Long: "Submit an update IBC client proposal along with an initial deposit.\n" + + "Please specify a subject client identifier you want to update..\n" + + "Please specify the substitute client the subject client will use and the initial height to reference the substitute client's state.", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + title, err := cmd.Flags().GetString(govcli.FlagTitle) + if err != nil { + return err + } + + description, err := cmd.Flags().GetString(govcli.FlagDescription) + if err != nil { + return err + } + + subjectClientID := args[0] + substituteClientID := args[1] + + initialHeight, err := types.ParseHeight(args[2]) + if err != nil { + return err + } + + content := types.NewClientUpdateProposal(title, description, subjectClientID, substituteClientID, initialHeight) + + from := clientCtx.GetFromAddress() + + depositStr, err := cmd.Flags().GetString(govcli.FlagDeposit) + if err != nil { + return err + } + deposit, err := sdk.ParseCoinsNormalized(depositStr) + if err != nil { + return err + } + + msg, err := govtypes.NewMsgSubmitProposal(content, deposit, from) + if err != nil { + return err + } + + if err = msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().String(govcli.FlagTitle, "", "title of proposal") + cmd.Flags().String(govcli.FlagDescription, "", "description of proposal") + cmd.Flags().String(govcli.FlagDeposit, "", "deposit of proposal") + + return cmd +} diff --git a/x/ibc/core/02-client/client/proposal_handler.go b/x/ibc/core/02-client/client/proposal_handler.go new file mode 100644 index 000000000..63585cbe5 --- /dev/null +++ b/x/ibc/core/02-client/client/proposal_handler.go @@ -0,0 +1,8 @@ +package client + +import ( + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/client/cli" +) + +var ProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitUpdateClientProposal, nil) diff --git a/x/ibc/core/02-client/keeper/grpc_query.go b/x/ibc/core/02-client/keeper/grpc_query.go index 6328e5de3..213442772 100644 --- a/x/ibc/core/02-client/keeper/grpc_query.go +++ b/x/ibc/core/02-client/keeper/grpc_query.go @@ -1,6 +1,7 @@ package keeper import ( + "bytes" "context" "fmt" "sort" @@ -159,7 +160,7 @@ func (q Keeper) ConsensusStates(c context.Context, req *types.QueryConsensusStat pageRes, err := query.FilteredPaginate(store, req.Pagination, func(key, value []byte, accumulate bool) (bool, error) { // filter any metadata stored under consensus state key - if strings.Contains(string(key), "/") { + if bytes.Contains(key, []byte("/")) { return false, nil } diff --git a/x/ibc/core/02-client/keeper/proposal.go b/x/ibc/core/02-client/keeper/proposal.go index 6b17278e0..6d4ff350d 100644 --- a/x/ibc/core/02-client/keeper/proposal.go +++ b/x/ibc/core/02-client/keeper/proposal.go @@ -10,32 +10,41 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) -// ClientUpdateProposal will try to update the client with the new header if and only if -// the proposal passes. The localhost client is not allowed to be modified with a proposal. +// ClientUpdateProposal will retrieve the subject and substitute client. +// The initial height must be greater than the latest height of the subject +// client. A callback will occur to the subject client state with the client +// prefixed store being provided for both the subject and the substitute client. +// The localhost client is not allowed to be modified with a proposal. The IBC +// client implementations are responsible for validating the parameters of the +// subtitute (enusring they match the subject's parameters) as well as copying +// the necessary consensus states from the subtitute to the subject client +// store. func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdateProposal) error { - if p.ClientId == exported.Localhost { + if p.SubjectClientId == exported.Localhost || p.SubstituteClientId == exported.Localhost { return sdkerrors.Wrap(types.ErrInvalidUpdateClientProposal, "cannot update localhost client with proposal") } - clientState, found := k.GetClientState(ctx, p.ClientId) + subjectClientState, found := k.GetClientState(ctx, p.SubjectClientId) if !found { - return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", p.ClientId) + return sdkerrors.Wrapf(types.ErrClientNotFound, "subject client with ID %s", p.SubjectClientId) } - header, err := types.UnpackHeader(p.Header) + if subjectClientState.GetLatestHeight().GTE(p.InitialHeight) { + return sdkerrors.Wrapf(types.ErrInvalidHeight, "subject client state latest height is greater or equal to initial height (%s >= %s)", subjectClientState.GetLatestHeight(), p.InitialHeight) + } + + substituteClientState, found := k.GetClientState(ctx, p.SubstituteClientId) + if !found { + return sdkerrors.Wrapf(types.ErrClientNotFound, "substitute client with ID %s", p.SubstituteClientId) + } + + clientState, err := subjectClientState.CheckSubstituteAndUpdateState(ctx, k.cdc, k.ClientStore(ctx, p.SubjectClientId), k.ClientStore(ctx, p.SubstituteClientId), substituteClientState, p.InitialHeight) if err != nil { return err } + k.SetClientState(ctx, p.SubjectClientId, clientState) - clientState, consensusState, err := clientState.CheckProposedHeaderAndUpdateState(ctx, k.cdc, k.ClientStore(ctx, p.ClientId), header) - if err != nil { - return err - } - - k.SetClientState(ctx, p.ClientId, clientState) - k.SetClientConsensusState(ctx, p.ClientId, header.GetHeight(), consensusState) - - k.Logger(ctx).Info("client updated after governance proposal passed", "client-id", p.ClientId, "height", clientState.GetLatestHeight().String()) + k.Logger(ctx).Info("client updated after governance proposal passed", "client-id", p.SubjectClientId, "height", clientState.GetLatestHeight().String()) defer func() { telemetry.IncrCounterWithLabels( @@ -43,7 +52,7 @@ func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdatePropo 1, []metrics.Label{ telemetry.NewLabel("client-type", clientState.ClientType()), - telemetry.NewLabel("client-id", p.ClientId), + telemetry.NewLabel("client-id", p.SubjectClientId), telemetry.NewLabel("update-type", "proposal"), }, ) @@ -53,9 +62,9 @@ func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdatePropo ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeUpdateClientProposal, - sdk.NewAttribute(types.AttributeKeyClientID, p.ClientId), + sdk.NewAttribute(types.AttributeKeySubjectClientID, p.SubjectClientId), sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), - sdk.NewAttribute(types.AttributeKeyConsensusHeight, header.GetHeight().String()), + sdk.NewAttribute(types.AttributeKeyConsensusHeight, clientState.GetLatestHeight().String()), ), ) diff --git a/x/ibc/core/02-client/keeper/proposal_test.go b/x/ibc/core/02-client/keeper/proposal_test.go index ada205402..8dbe43f7d 100644 --- a/x/ibc/core/02-client/keeper/proposal_test.go +++ b/x/ibc/core/02-client/keeper/proposal_test.go @@ -10,8 +10,11 @@ import ( func (suite *KeeperTestSuite) TestClientUpdateProposal() { var ( - content *types.ClientUpdateProposal - err error + subject, substitute string + subjectClientState, substituteClientState exported.ClientState + initialHeight clienttypes.Height + content *types.ClientUpdateProposal + err error ) testCases := []struct { @@ -21,53 +24,63 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { }{ { "valid update client proposal", func() { - clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) - clientState := suite.chainA.GetClientState(clientA) - - tmClientState, ok := clientState.(*ibctmtypes.ClientState) - suite.Require().True(ok) - tmClientState.AllowUpdateAfterMisbehaviour = true - tmClientState.FrozenHeight = tmClientState.LatestHeight - suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), clientA, tmClientState) - - // use next header for chainB to update the client on chainA - header, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, clientA) - suite.Require().NoError(err) - - content, err = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, clientA, header) - suite.Require().NoError(err) + content = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, substitute, initialHeight) }, true, }, { - "client type does not exist", func() { - content, err = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, ibctesting.InvalidID, &ibctmtypes.Header{}) - suite.Require().NoError(err) + "subject and substitute use different revision numbers", func() { + tmClientState, ok := substituteClientState.(*ibctmtypes.ClientState) + suite.Require().True(ok) + consState, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientConsensusState(suite.chainA.GetContext(), substitute, tmClientState.LatestHeight) + suite.Require().True(found) + newRevisionNumber := tmClientState.GetLatestHeight().GetRevisionNumber() + 1 + + tmClientState.LatestHeight = clienttypes.NewHeight(newRevisionNumber, tmClientState.GetLatestHeight().GetRevisionHeight()) + initialHeight = clienttypes.NewHeight(newRevisionNumber, initialHeight.GetRevisionHeight()) + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), substitute, tmClientState.LatestHeight, consState) + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), substitute, tmClientState) + + content = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, substitute, initialHeight) + }, true, + }, + { + "cannot use localhost as subject", func() { + content = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, exported.Localhost, substitute, initialHeight) }, false, }, { - "cannot update localhost", func() { - content, err = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, exported.Localhost, &ibctmtypes.Header{}) - suite.Require().NoError(err) + "cannot use localhost as substitute", func() { + content = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, exported.Localhost, initialHeight) }, false, }, { - "client does not exist", func() { - content, err = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, ibctesting.InvalidID, &ibctmtypes.Header{}) - suite.Require().NoError(err) + "subject client does not exist", func() { + content = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, ibctesting.InvalidID, substitute, initialHeight) }, false, }, { - "cannot unpack header, header is nil", func() { - clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) - content = &clienttypes.ClientUpdateProposal{ibctesting.Title, ibctesting.Description, clientA, nil} + "substitute client does not exist", func() { + content = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, ibctesting.InvalidID, initialHeight) }, false, }, { - "update fails", func() { - header := &ibctmtypes.Header{} - clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) - content, err = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, clientA, header) - suite.Require().NoError(err) + "subject and substitute have equal latest height", func() { + tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClientState.LatestHeight = substituteClientState.GetLatestHeight().(clienttypes.Height) + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), subject, tmClientState) + + content = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, substitute, initialHeight) + }, false, + }, + { + "update fails, client is not frozen or expired", func() { + tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClientState.FrozenHeight = clienttypes.ZeroHeight() + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), subject, tmClientState) + + content = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, substitute, initialHeight) }, false, }, } @@ -78,6 +91,30 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { suite.Run(tc.name, func() { suite.SetupTest() // reset + subject, _ = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + subjectClientState = suite.chainA.GetClientState(subject) + substitute, _ = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + initialHeight = clienttypes.NewHeight(subjectClientState.GetLatestHeight().GetRevisionNumber(), subjectClientState.GetLatestHeight().GetRevisionHeight()+1) + + // update substitute twice + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, substitute, exported.Tendermint) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, substitute, exported.Tendermint) + substituteClientState = suite.chainA.GetClientState(substitute) + + tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClientState.AllowUpdateAfterMisbehaviour = true + tmClientState.AllowUpdateAfterExpiry = true + tmClientState.FrozenHeight = tmClientState.LatestHeight + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), subject, tmClientState) + + tmClientState, ok = substituteClientState.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClientState.AllowUpdateAfterMisbehaviour = true + tmClientState.AllowUpdateAfterExpiry = true + tmClientState.FrozenHeight = tmClientState.LatestHeight + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), substitute, tmClientState) + tc.malleate() err = suite.chainA.App.IBCKeeper.ClientKeeper.ClientUpdateProposal(suite.chainA.GetContext(), content) diff --git a/x/ibc/core/02-client/proposal_handler_test.go b/x/ibc/core/02-client/proposal_handler_test.go index 91c1451b7..41b893186 100644 --- a/x/ibc/core/02-client/proposal_handler_test.go +++ b/x/ibc/core/02-client/proposal_handler_test.go @@ -24,21 +24,29 @@ func (suite *ClientTestSuite) TestNewClientUpdateProposalHandler() { }{ { "valid update client proposal", func() { - clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) - clientState := suite.chainA.GetClientState(clientA) + subject, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + subjectClientState := suite.chainA.GetClientState(subject) + substitute, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + initialHeight := clienttypes.NewHeight(subjectClientState.GetLatestHeight().GetRevisionNumber(), subjectClientState.GetLatestHeight().GetRevisionHeight()+1) - tmClientState, ok := clientState.(*ibctmtypes.ClientState) + // update substitute twice + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, substitute, exported.Tendermint) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, substitute, exported.Tendermint) + substituteClientState := suite.chainA.GetClientState(substitute) + + tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) suite.Require().True(ok) tmClientState.AllowUpdateAfterMisbehaviour = true tmClientState.FrozenHeight = tmClientState.LatestHeight - suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), clientA, tmClientState) + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), subject, tmClientState) - // use next header for chainB to update the client on chainA - header, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, clientA) - suite.Require().NoError(err) + // replicate changes to substitute (they must match) + tmClientState, ok = substituteClientState.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClientState.AllowUpdateAfterMisbehaviour = true + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), substitute, tmClientState) - content, err = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, clientA, header) - suite.Require().NoError(err) + content = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, substitute, initialHeight) }, true, }, { diff --git a/x/ibc/core/02-client/types/client.pb.go b/x/ibc/core/02-client/types/client.pb.go index a42ddef4c..61bdd9bf8 100644 --- a/x/ibc/core/02-client/types/client.pb.go +++ b/x/ibc/core/02-client/types/client.pb.go @@ -191,18 +191,22 @@ func (m *ClientConsensusStates) GetConsensusStates() []ConsensusStateWithHeight return nil } -// ClientUpdateProposal is a governance proposal. If it passes, the client is -// updated with the provided header. The update may fail if the header is not -// valid given certain conditions specified by the client implementation. +// ClientUpdateProposal is a governance proposal. If it passes, the substitute client's +// consensus states starting from the 'initial height' are copied over to the subjects +// client state. The proposal handler may fail if the subject and the substitute do not +// match in client and chain parameters (with exception to latest height, frozen height, and chain-id). +// The updated client must also be valid (cannot be expired). type ClientUpdateProposal struct { // the title of the update proposal Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` // the description of the proposal Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` // the client identifier for the client to be updated if the proposal passes - ClientId string `protobuf:"bytes,3,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` - // the header used to update the client if the proposal passes - Header *types.Any `protobuf:"bytes,4,opt,name=header,proto3" json:"header,omitempty"` + SubjectClientId string `protobuf:"bytes,3,opt,name=subject_client_id,json=subjectClientId,proto3" json:"subject_client_id,omitempty" yaml:"subject_client_id"` + // the substitute client identifier for the client standing in for the subject client + SubstituteClientId string `protobuf:"bytes,4,opt,name=substitute_client_id,json=substituteClientId,proto3" json:"substitute_client_id,omitempty" yaml:"susbtitute_client_id"` + // the intital height to copy consensus states from the substitute to the subject + InitialHeight Height `protobuf:"bytes,5,opt,name=initial_height,json=initialHeight,proto3" json:"initial_height" yaml:"initial_height"` } func (m *ClientUpdateProposal) Reset() { *m = ClientUpdateProposal{} } @@ -344,43 +348,48 @@ func init() { func init() { proto.RegisterFile("ibc/core/client/v1/client.proto", fileDescriptor_b6bc4c8185546947) } var fileDescriptor_b6bc4c8185546947 = []byte{ - // 574 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xbd, 0x8e, 0xd3, 0x4c, - 0x14, 0x8d, 0x93, 0x7c, 0xd1, 0x66, 0xf2, 0x29, 0x59, 0x99, 0x84, 0xf5, 0xa6, 0xb0, 0xa3, 0xa9, - 0x52, 0xec, 0xda, 0x24, 0x14, 0xa0, 0x74, 0x38, 0x0d, 0x5b, 0x80, 0x82, 0x11, 0x02, 0xd1, 0x44, - 0xfe, 0x99, 0x75, 0x46, 0x38, 0x9e, 0xc8, 0x33, 0x09, 0x9b, 0x37, 0xa0, 0xa4, 0xa4, 0xa0, 0xe0, - 0x09, 0xe8, 0x78, 0x03, 0x8a, 0x2d, 0xb7, 0xa4, 0xb2, 0x50, 0xf2, 0x06, 0x79, 0x02, 0xe4, 0x99, - 0xc9, 0x12, 0x07, 0x22, 0xad, 0xa8, 0x7c, 0x7d, 0xe6, 0xcc, 0xb9, 0xe7, 0xdc, 0x19, 0x0d, 0x30, - 0xb0, 0xe7, 0x5b, 0x3e, 0x49, 0x90, 0xe5, 0x47, 0x18, 0xc5, 0xcc, 0x5a, 0xf4, 0x64, 0x65, 0xce, - 0x12, 0xc2, 0x88, 0xaa, 0x62, 0xcf, 0x37, 0x33, 0x82, 0x29, 0xe1, 0x45, 0xaf, 0xdd, 0x0c, 0x49, - 0x48, 0xf8, 0xb2, 0x95, 0x55, 0x82, 0xd9, 0x3e, 0x0d, 0x09, 0x09, 0x23, 0x64, 0xf1, 0x3f, 0x6f, - 0x7e, 0x69, 0xb9, 0xf1, 0x52, 0x2c, 0xc1, 0xcf, 0x0a, 0x68, 0x5d, 0x04, 0x28, 0x66, 0xf8, 0x12, - 0xa3, 0x60, 0xc8, 0x85, 0x5e, 0x32, 0x97, 0x21, 0xb5, 0x07, 0xaa, 0x42, 0x77, 0x8c, 0x03, 0x4d, - 0xe9, 0x28, 0xdd, 0xaa, 0xdd, 0xdc, 0xa4, 0xc6, 0xf1, 0xd2, 0x9d, 0x46, 0x03, 0x78, 0xbb, 0x04, - 0x9d, 0x23, 0x51, 0x5f, 0x04, 0xea, 0x08, 0xfc, 0x2f, 0x71, 0x9a, 0x49, 0x68, 0xc5, 0x8e, 0xd2, - 0xad, 0xf5, 0x9b, 0xa6, 0x68, 0x6f, 0x6e, 0xdb, 0x9b, 0x4f, 0xe2, 0xa5, 0x7d, 0xb2, 0x49, 0x8d, - 0x7b, 0x39, 0x2d, 0xbe, 0x07, 0x3a, 0x35, 0xff, 0xb7, 0x09, 0xf8, 0x55, 0x01, 0xda, 0x90, 0xc4, - 0x14, 0xc5, 0x74, 0x4e, 0x39, 0xf4, 0x1a, 0xb3, 0xc9, 0x53, 0x84, 0xc3, 0x09, 0x53, 0x1f, 0x83, - 0xca, 0x84, 0x57, 0xdc, 0x5e, 0xad, 0xdf, 0x36, 0xff, 0x9c, 0x88, 0x29, 0xb8, 0x76, 0xf9, 0x3a, - 0x35, 0x0a, 0x8e, 0xe4, 0xab, 0x6f, 0x40, 0xc3, 0xdf, 0xaa, 0xde, 0xc1, 0xeb, 0xe9, 0x26, 0x35, - 0x5a, 0x99, 0x57, 0xb8, 0xb7, 0x0b, 0x3a, 0x75, 0x3f, 0xe7, 0x0e, 0x7e, 0x57, 0x40, 0x4b, 0x4c, - 0x31, 0x6f, 0x9b, 0xfe, 0xcb, 0x3c, 0xaf, 0xc0, 0xf1, 0x5e, 0x43, 0xaa, 0x15, 0x3b, 0xa5, 0x6e, - 0xad, 0x7f, 0xf6, 0xb7, 0xa8, 0x87, 0x06, 0x65, 0x1b, 0x59, 0xf8, 0x4d, 0x6a, 0x9c, 0xc8, 0x5e, - 0x7b, 0x9a, 0xd0, 0x69, 0xe4, 0x53, 0x50, 0xf8, 0x4d, 0x01, 0x4d, 0x11, 0xe3, 0xd5, 0x2c, 0x70, - 0x19, 0x1a, 0x25, 0x64, 0x46, 0xa8, 0x1b, 0xa9, 0x4d, 0xf0, 0x1f, 0xc3, 0x2c, 0x42, 0x22, 0x81, - 0x23, 0x7e, 0xd4, 0x0e, 0xa8, 0x05, 0x88, 0xfa, 0x09, 0x9e, 0x31, 0x4c, 0x62, 0x3e, 0xcb, 0xaa, - 0xb3, 0x0b, 0xe5, 0xd3, 0x97, 0xee, 0x94, 0xfe, 0x2c, 0x3b, 0x5e, 0x37, 0x40, 0x89, 0x56, 0x3e, - 0x7c, 0x36, 0x8e, 0xe4, 0x0c, 0xca, 0x1f, 0xbe, 0x18, 0x85, 0xec, 0x3a, 0x57, 0xe4, 0xed, 0x18, - 0x82, 0x46, 0x82, 0x16, 0x98, 0x62, 0x12, 0x8f, 0xe3, 0xf9, 0xd4, 0x43, 0x09, 0xf7, 0x5c, 0xb6, - 0xdb, 0x9b, 0xd4, 0xb8, 0x2f, 0xfa, 0xee, 0x11, 0xa0, 0x53, 0xdf, 0x22, 0xcf, 0x39, 0x90, 0x13, - 0x91, 0x77, 0xad, 0x78, 0x50, 0x44, 0x10, 0x76, 0x44, 0x84, 0x93, 0xc1, 0x51, 0x66, 0xed, 0x53, - 0x66, 0xef, 0x19, 0xa8, 0x8c, 0xdc, 0xc4, 0x9d, 0xd2, 0x4c, 0xd8, 0x8d, 0x22, 0xf2, 0x1e, 0x05, - 0x63, 0x11, 0x98, 0x6a, 0x4a, 0xa7, 0xd4, 0xad, 0xee, 0x0a, 0xef, 0x11, 0xa0, 0x53, 0x97, 0x88, - 0x38, 0x19, 0x6a, 0xbf, 0xb8, 0x5e, 0xe9, 0xca, 0xcd, 0x4a, 0x57, 0x7e, 0xae, 0x74, 0xe5, 0xe3, - 0x5a, 0x2f, 0xdc, 0xac, 0xf5, 0xc2, 0x8f, 0xb5, 0x5e, 0x78, 0xfb, 0x28, 0xc4, 0x6c, 0x32, 0xf7, - 0x4c, 0x9f, 0x4c, 0x2d, 0x9f, 0xd0, 0x29, 0xa1, 0xf2, 0x73, 0x4e, 0x83, 0x77, 0xd6, 0x95, 0x75, - 0xfb, 0xb6, 0x3c, 0xe8, 0x9f, 0xcb, 0xe7, 0x85, 0x2d, 0x67, 0x88, 0x7a, 0x15, 0x3e, 0xdc, 0x87, - 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x39, 0xbe, 0xfd, 0x04, 0x7e, 0x04, 0x00, 0x00, + // 641 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xbf, 0x6e, 0xd3, 0x40, + 0x1c, 0x8e, 0xd3, 0x34, 0x6a, 0x2e, 0x90, 0x14, 0x93, 0xd0, 0x34, 0x40, 0x2e, 0xba, 0x29, 0x03, + 0xb5, 0x69, 0x18, 0x40, 0xdd, 0x48, 0x96, 0x76, 0x00, 0xb5, 0x87, 0x10, 0x88, 0x25, 0xf8, 0xcf, + 0x35, 0x39, 0x70, 0x7c, 0x91, 0xef, 0x5c, 0x9a, 0x37, 0x60, 0x64, 0x64, 0x60, 0xe0, 0x09, 0x78, + 0x0a, 0x86, 0x8e, 0x95, 0x58, 0x98, 0x2c, 0xd4, 0xbe, 0x81, 0x9f, 0x00, 0xf9, 0xee, 0x92, 0x36, + 0x29, 0x15, 0x88, 0xc9, 0x3f, 0x7f, 0xf7, 0xfd, 0xbe, 0xdf, 0xf7, 0xfb, 0x74, 0x36, 0x80, 0xd4, + 0xf5, 0x6c, 0x8f, 0x45, 0xc4, 0xf6, 0x02, 0x4a, 0x42, 0x61, 0x1f, 0x6d, 0xeb, 0xca, 0x9a, 0x44, + 0x4c, 0x30, 0xd3, 0xa4, 0xae, 0x67, 0x65, 0x04, 0x4b, 0xc3, 0x47, 0xdb, 0xcd, 0xda, 0x90, 0x0d, + 0x99, 0x3c, 0xb6, 0xb3, 0x4a, 0x31, 0x9b, 0x9b, 0x43, 0xc6, 0x86, 0x01, 0xb1, 0xe5, 0x9b, 0x1b, + 0x1f, 0xda, 0x4e, 0x38, 0x55, 0x47, 0xe8, 0x8b, 0x01, 0xea, 0x7b, 0x3e, 0x09, 0x05, 0x3d, 0xa4, + 0xc4, 0xef, 0x4b, 0xa1, 0x17, 0xc2, 0x11, 0xc4, 0xdc, 0x06, 0x25, 0xa5, 0x3b, 0xa0, 0x7e, 0xc3, + 0x68, 0x1b, 0x9d, 0x52, 0xaf, 0x96, 0x26, 0x70, 0x7d, 0xea, 0x8c, 0x83, 0x1d, 0x34, 0x3f, 0x42, + 0x78, 0x4d, 0xd5, 0x7b, 0xbe, 0xb9, 0x0f, 0x6e, 0x68, 0x9c, 0x67, 0x12, 0x8d, 0x7c, 0xdb, 0xe8, + 0x94, 0xbb, 0x35, 0x4b, 0x8d, 0xb7, 0x66, 0xe3, 0xad, 0xa7, 0xe1, 0xb4, 0xb7, 0x91, 0x26, 0xf0, + 0xf6, 0x82, 0x96, 0xec, 0x41, 0xb8, 0xec, 0x5d, 0x98, 0x40, 0xdf, 0x0c, 0xd0, 0xe8, 0xb3, 0x90, + 0x93, 0x90, 0xc7, 0x5c, 0x42, 0xaf, 0xa8, 0x18, 0xed, 0x12, 0x3a, 0x1c, 0x09, 0xf3, 0x09, 0x28, + 0x8e, 0x64, 0x25, 0xed, 0x95, 0xbb, 0x4d, 0xeb, 0x6a, 0x22, 0x96, 0xe2, 0xf6, 0x0a, 0x27, 0x09, + 0xcc, 0x61, 0xcd, 0x37, 0x5f, 0x83, 0xaa, 0x37, 0x53, 0xfd, 0x07, 0xaf, 0x9b, 0x69, 0x02, 0xeb, + 0x99, 0x57, 0xb4, 0xd4, 0x85, 0x70, 0xc5, 0x5b, 0x70, 0x87, 0xbe, 0x1b, 0xa0, 0xae, 0x52, 0x5c, + 0xb4, 0xcd, 0xff, 0x27, 0xcf, 0x63, 0xb0, 0xbe, 0x34, 0x90, 0x37, 0xf2, 0xed, 0x95, 0x4e, 0xb9, + 0xfb, 0xe0, 0x4f, 0xab, 0x5e, 0x17, 0x54, 0x0f, 0x66, 0xcb, 0xa7, 0x09, 0xdc, 0xd0, 0xb3, 0x96, + 0x34, 0x11, 0xae, 0x2e, 0x6e, 0xc1, 0xd1, 0x8f, 0x3c, 0xa8, 0xa9, 0x35, 0x5e, 0x4e, 0x7c, 0x47, + 0x90, 0xfd, 0x88, 0x4d, 0x18, 0x77, 0x02, 0xb3, 0x06, 0x56, 0x05, 0x15, 0x01, 0x51, 0x1b, 0x60, + 0xf5, 0x62, 0xb6, 0x41, 0xd9, 0x27, 0xdc, 0x8b, 0xe8, 0x44, 0x50, 0x16, 0xca, 0x2c, 0x4b, 0xf8, + 0x32, 0x64, 0xee, 0x82, 0x5b, 0x3c, 0x76, 0xdf, 0x11, 0x4f, 0x0c, 0x2e, 0x52, 0x58, 0x91, 0x29, + 0xdc, 0x4b, 0x13, 0xd8, 0x50, 0xce, 0xae, 0x50, 0x10, 0xae, 0x6a, 0xac, 0x3f, 0x0b, 0xe5, 0x00, + 0xd4, 0x78, 0xec, 0x72, 0x41, 0x45, 0x2c, 0xc8, 0x25, 0xb1, 0x82, 0x14, 0x83, 0x69, 0x02, 0xef, + 0xce, 0xc4, 0xb8, 0xbb, 0xcc, 0x42, 0xd8, 0xbc, 0x68, 0x9e, 0x4b, 0xbe, 0x05, 0x15, 0x1a, 0x52, + 0x41, 0x9d, 0x60, 0xa0, 0x2f, 0xd4, 0xea, 0x5f, 0x2f, 0xd4, 0x7d, 0x9d, 0x69, 0x5d, 0x0d, 0x5b, + 0xec, 0x47, 0xf8, 0xa6, 0x06, 0x14, 0x7b, 0xa7, 0xf0, 0xf1, 0x2b, 0xcc, 0x65, 0x1f, 0x5b, 0x51, + 0xdf, 0xdd, 0x3e, 0xa8, 0x46, 0xe4, 0x88, 0x72, 0xca, 0xc2, 0x41, 0x18, 0x8f, 0x5d, 0x12, 0xc9, + 0x44, 0x0b, 0xbd, 0x66, 0x9a, 0xc0, 0x3b, 0x4a, 0x73, 0x89, 0x80, 0x70, 0x65, 0x86, 0x3c, 0x97, + 0xc0, 0x82, 0x88, 0x36, 0x9e, 0xbf, 0x56, 0x64, 0xe6, 0x6c, 0x2e, 0xa2, 0xad, 0xad, 0x65, 0xd6, + 0x3e, 0x67, 0xf6, 0x9e, 0x81, 0xe2, 0xbe, 0x13, 0x39, 0x63, 0x9e, 0x09, 0x3b, 0x41, 0xc0, 0x3e, + 0x10, 0x5f, 0x47, 0xc7, 0x1b, 0x46, 0x7b, 0xa5, 0x53, 0xba, 0x2c, 0xbc, 0x44, 0x40, 0xb8, 0xa2, + 0x11, 0x15, 0x2b, 0xef, 0x1d, 0x9c, 0x9c, 0xb5, 0x8c, 0xd3, 0xb3, 0x96, 0xf1, 0xeb, 0xac, 0x65, + 0x7c, 0x3a, 0x6f, 0xe5, 0x4e, 0xcf, 0x5b, 0xb9, 0x9f, 0xe7, 0xad, 0xdc, 0x9b, 0xc7, 0x43, 0x2a, + 0x46, 0xb1, 0x6b, 0x79, 0x6c, 0x6c, 0x7b, 0x8c, 0x8f, 0x19, 0xd7, 0x8f, 0x2d, 0xee, 0xbf, 0xb7, + 0x8f, 0xed, 0xf9, 0x9f, 0xef, 0x61, 0x77, 0x4b, 0xff, 0xfc, 0xc4, 0x74, 0x42, 0xb8, 0x5b, 0x94, + 0x9f, 0xe5, 0xa3, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x95, 0xe2, 0x8e, 0x47, 0x1c, 0x05, 0x00, + 0x00, } func (m *IdentifiedClientState) Marshal() (dAtA []byte, err error) { @@ -534,22 +543,27 @@ func (m *ClientUpdateProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.Header != nil { - { - size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintClient(dAtA, i, uint64(size)) + { + size, err := m.InitialHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = encodeVarintClient(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.SubstituteClientId) > 0 { + i -= len(m.SubstituteClientId) + copy(dAtA[i:], m.SubstituteClientId) + i = encodeVarintClient(dAtA, i, uint64(len(m.SubstituteClientId))) i-- dAtA[i] = 0x22 } - if len(m.ClientId) > 0 { - i -= len(m.ClientId) - copy(dAtA[i:], m.ClientId) - i = encodeVarintClient(dAtA, i, uint64(len(m.ClientId))) + if len(m.SubjectClientId) > 0 { + i -= len(m.SubjectClientId) + copy(dAtA[i:], m.SubjectClientId) + i = encodeVarintClient(dAtA, i, uint64(len(m.SubjectClientId))) i-- dAtA[i] = 0x1a } @@ -711,14 +725,16 @@ func (m *ClientUpdateProposal) Size() (n int) { if l > 0 { n += 1 + l + sovClient(uint64(l)) } - l = len(m.ClientId) + l = len(m.SubjectClientId) if l > 0 { n += 1 + l + sovClient(uint64(l)) } - if m.Header != nil { - l = m.Header.Size() + l = len(m.SubstituteClientId) + if l > 0 { n += 1 + l + sovClient(uint64(l)) } + l = m.InitialHeight.Size() + n += 1 + l + sovClient(uint64(l)) return n } @@ -1206,7 +1222,7 @@ func (m *ClientUpdateProposal) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SubjectClientId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1234,11 +1250,43 @@ func (m *ClientUpdateProposal) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ClientId = string(dAtA[iNdEx:postIndex]) + m.SubjectClientId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SubstituteClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowClient + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthClient + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthClient + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SubstituteClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InitialHeight", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1265,10 +1313,7 @@ func (m *ClientUpdateProposal) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Header == nil { - m.Header = &types.Any{} - } - if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.InitialHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/ibc/core/02-client/types/codec.go b/x/ibc/core/02-client/types/codec.go index 8d79dcdaa..59a15832b 100644 --- a/x/ibc/core/02-client/types/codec.go +++ b/x/ibc/core/02-client/types/codec.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/msgservice" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) @@ -33,6 +34,10 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { "ibc.core.client.v1.Misbehaviour", (*exported.Misbehaviour)(nil), ) + registry.RegisterImplementations( + (*govtypes.Content)(nil), + &ClientUpdateProposal{}, + ) registry.RegisterImplementations( (*sdk.Msg)(nil), &MsgCreateClient{}, diff --git a/x/ibc/core/02-client/types/errors.go b/x/ibc/core/02-client/types/errors.go index 09dc92959..5b44cd522 100644 --- a/x/ibc/core/02-client/types/errors.go +++ b/x/ibc/core/02-client/types/errors.go @@ -30,4 +30,6 @@ var ( ErrUpdateClientFailed = sdkerrors.Register(SubModuleName, 23, "unable to update light client") ErrInvalidUpdateClientProposal = sdkerrors.Register(SubModuleName, 24, "invalid update client proposal") ErrInvalidUpgradeClient = sdkerrors.Register(SubModuleName, 25, "invalid client upgrade") + ErrInvalidHeight = sdkerrors.Register(SubModuleName, 26, "invalid height") + ErrInvalidSubstitute = sdkerrors.Register(SubModuleName, 27, "invalid client state substitute") ) diff --git a/x/ibc/core/02-client/types/events.go b/x/ibc/core/02-client/types/events.go index 779e87cee..d0760ba89 100644 --- a/x/ibc/core/02-client/types/events.go +++ b/x/ibc/core/02-client/types/events.go @@ -9,6 +9,7 @@ import ( // IBC client events const ( AttributeKeyClientID = "client_id" + AttributeKeySubjectClientID = "subject_client_id" AttributeKeyClientType = "client_type" AttributeKeyConsensusHeight = "consensus_height" ) diff --git a/x/ibc/core/02-client/types/proposal.go b/x/ibc/core/02-client/types/proposal.go index 334a9d459..95b10aaf4 100644 --- a/x/ibc/core/02-client/types/proposal.go +++ b/x/ibc/core/02-client/types/proposal.go @@ -1,10 +1,8 @@ package types import ( - codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" - "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) const ( @@ -12,24 +10,21 @@ const ( ProposalTypeClientUpdate = "ClientUpdate" ) -var ( - _ govtypes.Content = &ClientUpdateProposal{} - _ codectypes.UnpackInterfacesMessage = ClientUpdateProposal{} -) +var _ govtypes.Content = &ClientUpdateProposal{} + +func init() { + govtypes.RegisterProposalType(ProposalTypeClientUpdate) +} // NewClientUpdateProposal creates a new client update proposal. -func NewClientUpdateProposal(title, description, clientID string, header exported.Header) (*ClientUpdateProposal, error) { - any, err := PackHeader(header) - if err != nil { - return nil, err - } - +func NewClientUpdateProposal(title, description, subjectClientID, substituteClientID string, initialHeight Height) *ClientUpdateProposal { return &ClientUpdateProposal{ - Title: title, - Description: description, - ClientId: clientID, - Header: any, - }, nil + Title: title, + Description: description, + SubjectClientId: subjectClientID, + SubstituteClientId: substituteClientID, + InitialHeight: initialHeight, + } } // GetTitle returns the title of a client update proposal. @@ -51,20 +46,19 @@ func (cup *ClientUpdateProposal) ValidateBasic() error { return err } - if err := host.ClientIdentifierValidator(cup.ClientId); err != nil { + if cup.SubjectClientId == cup.SubstituteClientId { + return sdkerrors.Wrap(ErrInvalidSubstitute, "subject and substitute client identifiers are equal") + } + if _, _, err := ParseClientIdentifier(cup.SubjectClientId); err != nil { + return err + } + if _, _, err := ParseClientIdentifier(cup.SubstituteClientId); err != nil { return err } - header, err := UnpackHeader(cup.Header) - if err != nil { - return err + if cup.InitialHeight.IsZero() { + return sdkerrors.Wrap(ErrInvalidHeight, "initial height cannot be zero height") } - return header.ValidateBasic() -} - -// UnpackInterfaces implements the UnpackInterfacesMessage interface. -func (cup ClientUpdateProposal) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - var header exported.Header - return unpacker.UnpackAny(cup.Header, &header) + return nil } diff --git a/x/ibc/core/02-client/types/proposal_test.go b/x/ibc/core/02-client/types/proposal_test.go index 5a47cf2f0..597e5cf8f 100644 --- a/x/ibc/core/02-client/types/proposal_test.go +++ b/x/ibc/core/02-client/types/proposal_test.go @@ -5,35 +5,15 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" - ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ) -func (suite *TypesTestSuite) TestNewUpdateClientProposal() { - p, err := types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, clientID, &ibctmtypes.Header{}) - suite.Require().NoError(err) - suite.Require().NotNil(p) - - p, err = types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, clientID, nil) - suite.Require().Error(err) - suite.Require().Nil(p) -} - func (suite *TypesTestSuite) TestValidateBasic() { - // use solo machine header for testing - solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, clientID, "", 2) - smHeader := solomachine.CreateHeader() - header, err := types.PackHeader(smHeader) - suite.Require().NoError(err) - - // use a different pointer so we don't modify 'header' - smInvalidHeader := solomachine.CreateHeader() - - // a sequence of 0 will fail basic validation - smInvalidHeader.Sequence = 0 - - invalidHeader, err := types.PackHeader(smInvalidHeader) - suite.Require().NoError(err) + subject, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + subjectClientState := suite.chainA.GetClientState(subject) + substitute, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + initialHeight := types.NewHeight(subjectClientState.GetLatestHeight().GetRevisionNumber(), subjectClientState.GetLatestHeight().GetRevisionHeight()+1) testCases := []struct { name string @@ -42,22 +22,32 @@ func (suite *TypesTestSuite) TestValidateBasic() { }{ { "success", - &types.ClientUpdateProposal{ibctesting.Title, ibctesting.Description, clientID, header}, + types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, substitute, initialHeight), true, }, { "fails validate abstract - empty title", - &types.ClientUpdateProposal{"", ibctesting.Description, clientID, header}, + types.NewClientUpdateProposal("", ibctesting.Description, subject, substitute, initialHeight), false, }, { - "fails to unpack header", - &types.ClientUpdateProposal{ibctesting.Title, ibctesting.Description, clientID, nil}, + "subject and substitute use the same identifier", + types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, subject, initialHeight), false, }, { - "fails header validate basic", - &types.ClientUpdateProposal{ibctesting.Title, ibctesting.Description, clientID, invalidHeader}, + "invalid subject clientID", + types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, ibctesting.InvalidID, substitute, initialHeight), + false, + }, + { + "invalid substitute clientID", + types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, ibctesting.InvalidID, initialHeight), + false, + }, + { + "initial height is zero", + types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, substitute, types.ZeroHeight()), false, }, } @@ -74,22 +64,15 @@ func (suite *TypesTestSuite) TestValidateBasic() { } } -// tests a client update proposal can be marshaled and unmarshaled, and the -// client state can be unpacked +// tests a client update proposal can be marshaled and unmarshaled func (suite *TypesTestSuite) TestMarshalClientUpdateProposalProposal() { - _, err := types.PackHeader(&ibctmtypes.Header{}) - suite.Require().NoError(err) - // create proposal - header := suite.chainA.CurrentTMClientHeader() - proposal, err := types.NewClientUpdateProposal("update IBC client", "description", "client-id", header) - suite.Require().NoError(err) + proposal := types.NewClientUpdateProposal("update IBC client", "description", "subject", "substitute", types.NewHeight(1, 0)) // create codec ir := codectypes.NewInterfaceRegistry() types.RegisterInterfaces(ir) govtypes.RegisterInterfaces(ir) - ibctmtypes.RegisterInterfaces(ir) cdc := codec.NewProtoCodec(ir) // marshal message @@ -100,8 +83,4 @@ func (suite *TypesTestSuite) TestMarshalClientUpdateProposalProposal() { newProposal := &types.ClientUpdateProposal{} err = cdc.UnmarshalJSON(bz, newProposal) suite.Require().NoError(err) - - // unpack client state - _, err = types.UnpackHeader(newProposal.Header) - suite.Require().NoError(err) } diff --git a/x/ibc/core/02-client/types/query.pb.gw.go b/x/ibc/core/02-client/types/query.pb.gw.go index d4414d5ea..40bc6a85f 100644 --- a/x/ibc/core/02-client/types/query.pb.gw.go +++ b/x/ibc/core/02-client/types/query.pb.gw.go @@ -578,15 +578,15 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_ClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1beta1", "client_states", "client_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1beta1", "client_states", "client_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ClientStates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "client", "v1beta1", "client_states"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ClientStates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "client", "v1beta1", "client_states"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "client", "v1beta1", "consensus_states", "client_id", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "client", "v1beta1", "consensus_states", "client_id", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ConsensusStates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1beta1", "consensus_states", "client_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConsensusStates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1beta1", "consensus_states", "client_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ClientParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"ibc", "client", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ClientParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"ibc", "client", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/ibc/core/03-connection/client/cli/tx.go b/x/ibc/core/03-connection/client/cli/tx.go index 339e2e977..68b1a6208 100644 --- a/x/ibc/core/03-connection/client/cli/tx.go +++ b/x/ibc/core/03-connection/client/cli/tx.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/version" clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/client/utils" @@ -76,11 +77,14 @@ func NewConnectionOpenInitCmd() *cobra.Command { counterpartyPrefix, version, delayPeriod, clientCtx.GetFromAddress(), ) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ConnectionOpenInit(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -189,11 +193,14 @@ func NewConnectionOpenTryCmd() *cobra.Command { consensusHeight, clientCtx.GetFromAddress(), ) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ConnectionOpenTry(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -276,11 +283,14 @@ func NewConnectionOpenAckCmd() *cobra.Command { consensusHeight, version, clientCtx.GetFromAddress(), ) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ConnectionOpenAck(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -321,11 +331,14 @@ func NewConnectionOpenConfirmCmd() *cobra.Command { connectionID, proofAck, proofHeight, clientCtx.GetFromAddress(), ) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ConnectionOpenConfirm(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } diff --git a/x/ibc/core/03-connection/types/query.pb.gw.go b/x/ibc/core/03-connection/types/query.pb.gw.go index 3e6de4e06..f9824f5bd 100644 --- a/x/ibc/core/03-connection/types/query.pb.gw.go +++ b/x/ibc/core/03-connection/types/query.pb.gw.go @@ -578,15 +578,15 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Connection_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "connection", "v1beta1", "connections", "connection_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Connection_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "connection", "v1beta1", "connections", "connection_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Connections_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "connection", "v1beta1", "connections"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Connections_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "connection", "v1beta1", "connections"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ClientConnections_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "connection", "v1beta1", "client_connections", "client_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ClientConnections_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "connection", "v1beta1", "client_connections", "client_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ConnectionClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "connection", "v1beta1", "connections", "connection_id", "client_state"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConnectionClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "connection", "v1beta1", "connections", "connection_id", "client_state"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ConnectionConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 2, 7, 1, 0, 4, 1, 5, 8, 2, 9, 1, 0, 4, 1, 5, 10}, []string{"ibc", "core", "connection", "v1beta1", "connections", "connection_id", "consensus_state", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConnectionConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 2, 7, 1, 0, 4, 1, 5, 8, 2, 9, 1, 0, 4, 1, 5, 10}, []string{"ibc", "core", "connection", "v1beta1", "connections", "connection_id", "consensus_state", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/ibc/core/04-channel/client/cli/tx.go b/x/ibc/core/04-channel/client/cli/tx.go index 21aafba19..20afe6226 100644 --- a/x/ibc/core/04-channel/client/cli/tx.go +++ b/x/ibc/core/04-channel/client/cli/tx.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/types/msgservice" ibctransfertypes "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" connectionutils "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/client/utils" @@ -42,11 +43,14 @@ func NewChannelOpenInitCmd() *cobra.Command { portID, version, order, hops, counterpartyPortID, clientCtx.GetFromAddress(), ) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ChannelOpenInit(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -93,11 +97,14 @@ func NewChannelOpenTryCmd() *cobra.Command { counterpartyPortID, counterpartyChannelID, version, proofInit, proofHeight, clientCtx.GetFromAddress(), ) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ChannelOpenTry(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -139,11 +146,14 @@ func NewChannelOpenAckCmd() *cobra.Command { msg := types.NewMsgChannelOpenAck( portID, channelID, counterpartyChannelID, version, proofTry, proofHeight, clientCtx.GetFromAddress(), ) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ChannelOpenAck(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } cmd.Flags().String(FlagIBCVersion, ibctransfertypes.Version, "IBC application version") @@ -179,11 +189,14 @@ func NewChannelOpenConfirmCmd() *cobra.Command { msg := types.NewMsgChannelOpenConfirm( portID, channelID, proofAck, proofHeight, clientCtx.GetFromAddress(), ) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ChannelOpenConfirm(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -207,11 +220,14 @@ func NewChannelCloseInitCmd() *cobra.Command { channelID := args[1] msg := types.NewMsgChannelCloseInit(portID, channelID, clientCtx.GetFromAddress()) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ChannelCloseInit(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -247,11 +263,14 @@ func NewChannelCloseConfirmCmd() *cobra.Command { msg := types.NewMsgChannelCloseConfirm( portID, channelID, proofInit, proofHeight, clientCtx.GetFromAddress(), ) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ChannelCloseConfirm(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } diff --git a/x/ibc/core/04-channel/types/query.pb.gw.go b/x/ibc/core/04-channel/types/query.pb.gw.go index bb5a94aba..f38c05956 100644 --- a/x/ibc/core/04-channel/types/query.pb.gw.go +++ b/x/ibc/core/04-channel/types/query.pb.gw.go @@ -1736,31 +1736,31 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Channel_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Channel_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Channels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "channel", "v1beta1", "channels"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Channels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "channel", "v1beta1", "channels"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ConnectionChannels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "channel", "v1beta1", "connections", "connection", "channels"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ConnectionChannels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "channel", "v1beta1", "connections", "connection", "channels"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ChannelClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "client_state"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ChannelClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "client_state"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ChannelConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 2, 9, 1, 0, 4, 1, 5, 10, 2, 11, 1, 0, 4, 1, 5, 12}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "consensus_state", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ChannelConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 2, 9, 1, 0, 4, 1, 5, 10, 2, 11, 1, 0, 4, 1, 5, 12}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "consensus_state", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_PacketCommitment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "sequence"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_PacketCommitment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "sequence"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_PacketCommitments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_PacketCommitments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_PacketReceipt_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_receipts", "sequence"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_PacketReceipt_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_receipts", "sequence"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_PacketAcknowledgement_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_acks", "sequence"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_PacketAcknowledgement_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_acks", "sequence"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_PacketAcknowledgements_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_acknowledgements"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_PacketAcknowledgements_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_acknowledgements"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_UnreceivedPackets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "packet_commitment_sequences", "unreceived_packets"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_UnreceivedPackets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "packet_commitment_sequences", "unreceived_packets"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_UnreceivedAcks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "packet_ack_sequences", "unreceived_acks"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_UnreceivedAcks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "packet_ack_sequences", "unreceived_acks"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_NextSequenceReceive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "next_sequence"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_NextSequenceReceive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "next_sequence"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/ibc/core/exported/client.go b/x/ibc/core/exported/client.go index 656a233b3..3d552b077 100644 --- a/x/ibc/core/exported/client.go +++ b/x/ibc/core/exported/client.go @@ -46,7 +46,7 @@ type ClientState interface { CheckHeaderAndUpdateState(sdk.Context, codec.BinaryMarshaler, sdk.KVStore, Header) (ClientState, ConsensusState, error) CheckMisbehaviourAndUpdateState(sdk.Context, codec.BinaryMarshaler, sdk.KVStore, Misbehaviour) (ClientState, error) - CheckProposedHeaderAndUpdateState(sdk.Context, codec.BinaryMarshaler, sdk.KVStore, Header) (ClientState, ConsensusState, error) + CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryMarshaler, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState, height Height) (ClientState, error) // Upgrade functions // NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last diff --git a/x/ibc/core/module.go b/x/ibc/core/module.go index 6527ab71e..9d5d4e863 100644 --- a/x/ibc/core/module.go +++ b/x/ibc/core/module.go @@ -170,8 +170,6 @@ func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.V return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the ibc module. diff --git a/x/ibc/core/spec/01_concepts.md b/x/ibc/core/spec/01_concepts.md index 045508999..4347fb674 100644 --- a/x/ibc/core/spec/01_concepts.md +++ b/x/ibc/core/spec/01_concepts.md @@ -53,11 +53,11 @@ submission. ## ClientUpdateProposal -A governance proposal may be passed to update a specified client with a provided -header. This is useful in unfreezing clients or updating expired clients. Each -client is expected to implement this functionality. A client may choose to disallow -an update by a governance proposal by returning an error in the client state function -'CheckProposedHeaderAndUpdateState'. +A governance proposal may be passed to update a specified client using another client +known as the "substitute client". This is useful in unfreezing clients or updating +expired clients, thereby making the effected channels active again. Each client is +expected to implement this functionality. A client may choose to disallow an update +by a governance proposal by returning an error in the client state function 'CheckSubstituteAndUpdateState'. The localhost client cannot be updated by a governance proposal. @@ -68,9 +68,21 @@ be updated later. The tendermint client has two flags update flags, 'AllowUpdateAfterExpiry' and 'AllowUpdateAfterMisbehaviour'. The former flag can only be used to unexpire clients. The latter flag can be used to unfreeze a client and if necessary it will also unexpire the client. -It is advised to let a client expire if it has become frozen before proposing a new header. -This is to avoid the client from becoming refrozen if the misbehaviour evidence has not -expired. These boolean flags are set upon client creation and cannot be updated later. +It is best practice to initialize a new substitute client instead of using an existing one +This avoids potential issues of the substitute becoming frozen due to misbehaviour or the +subject client becoming refrozen due to misbehaviour not being expired at the time the +proposal passes. These boolean flags are set upon client creation and cannot be updated later. + +The `CheckSubstituteAndUpdateState` function provides the light client with its own client +store, the client store of the substitute, the substitute client state, and the intitial +height that should be used when referring to the substitute client. Most light client +implementations should copy consensus states from the substitute to the subject, but +are not required to do so. Light clients may copy informationa as they deem necessary. + +It is not recommended to use a substitute client in normal operations since the subject +light client will be given unrestricted access to the substitute client store. Governance +should not pass votes which enable byzantine light client modules from modifying the state +of the substitute. ## IBC Client Heights diff --git a/x/ibc/core/spec/03_state_transitions.md b/x/ibc/core/spec/03_state_transitions.md index be3b508b7..518ff9247 100644 --- a/x/ibc/core/spec/03_state_transitions.md +++ b/x/ibc/core/spec/03_state_transitions.md @@ -27,8 +27,9 @@ parameters and if applicable will update to the new light client implementation. ## Client Update Proposal -An Update Client Proposal will unfreeze a client and set an updated `ClientState` and a new -`ConsensusState`. +An Update Client Proposal will unfreeze a client (if necessary) and set an updated `ClientState`. +The light client may make optional modifications to the client prefixed store of the subject client +including copying `ConsensusStates` from the substitute to the subject. ## Connection Open Init diff --git a/x/ibc/light-clients/06-solomachine/spec/01_concepts.md b/x/ibc/light-clients/06-solomachine/spec/01_concepts.md index fd8a4f71b..de486b71b 100644 --- a/x/ibc/light-clients/06-solomachine/spec/01_concepts.md +++ b/x/ibc/light-clients/06-solomachine/spec/01_concepts.md @@ -129,17 +129,14 @@ If the update is successful: An update by a governance proposal will only succeed if: -- the header provided is parseable to solo machine header +- the substitute provided is parseable to solo machine client state - the `AllowUpdateAfterProposal` client parameter is set to `true` -- the new header public key does not equal the consensus state public key +- the new consensus state public key does not equal the current consensus state public key If the update is successful: -- the public key is updated -- the diversifier is updated -- the timestamp is updated -- the sequence is updated -- the new consensus state is set in the client state +- the subject client state is updated to the substitute client state +- the subject consensus state is updated to the substitute consensus state - the client is unfrozen (if it was previously frozen) ## Misbehaviour diff --git a/x/ibc/light-clients/06-solomachine/spec/03_state_transitions.md b/x/ibc/light-clients/06-solomachine/spec/03_state_transitions.md index 3ca4b7017..48a1e18f1 100644 --- a/x/ibc/light-clients/06-solomachine/spec/03_state_transitions.md +++ b/x/ibc/light-clients/06-solomachine/spec/03_state_transitions.md @@ -24,11 +24,8 @@ A successful update of a solo machine light client by a header will result in: A successful update of a solo machine light client by a governance proposal will result in: -- the public key being updated to the new public key provided by the header. -- the diversifier being updated to the new diviersifier provided by the header. -- the timestamp being updated to the new timestamp provided by the header. -- the sequence being set to the new sequence provided by the header. -- the consensus state being updated (consensus state stores the public key, diversifier, and timestamp) +- the client state being updated to the substitute client state +- the consensus state being updated to the substitute consensus state (consensus state stores the public key, diversifier, and timestamp) - the frozen sequence being set to zero (client is unfrozen if it was previously frozen). ## Upgrade diff --git a/x/ibc/light-clients/06-solomachine/types/proposal_handle.go b/x/ibc/light-clients/06-solomachine/types/proposal_handle.go index b55e612b3..e38155b23 100644 --- a/x/ibc/light-clients/06-solomachine/types/proposal_handle.go +++ b/x/ibc/light-clients/06-solomachine/types/proposal_handle.go @@ -10,56 +10,55 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) -// CheckProposedHeaderAndUpdateState updates the consensus state to the header's sequence and -// public key. An error is returned if the client has been disallowed to be updated by a -// governance proposal, the header cannot be casted to a solo machine header, or the current -// public key equals the new public key. -func (cs ClientState) CheckProposedHeaderAndUpdateState( - ctx sdk.Context, cdc codec.BinaryMarshaler, clientStore sdk.KVStore, - header exported.Header, -) (exported.ClientState, exported.ConsensusState, error) { +// CheckSubstituteAndUpdateState verifies that the subject is allowed to be updated by +// a governance proposal and that the substitute client is a solo machine. +// It will update the consensus state to the substitute's consensus state and +// the sequence to the substitute's current sequence. An error is returned if +// the client has been disallowed to be updated by a governance proposal, +// the substitute is not a solo machine, or the current public key equals +// the new public key. +func (cs ClientState) CheckSubstituteAndUpdateState( + ctx sdk.Context, cdc codec.BinaryMarshaler, subjectClientStore, + _ sdk.KVStore, substituteClient exported.ClientState, + _ exported.Height, +) (exported.ClientState, error) { if !cs.AllowUpdateAfterProposal { - return nil, nil, sdkerrors.Wrapf( + return nil, sdkerrors.Wrapf( clienttypes.ErrUpdateClientFailed, "solo machine client is not allowed to updated with a proposal", ) } - smHeader, ok := header.(*Header) + substituteClientState, ok := substituteClient.(*ClientState) if !ok { - return nil, nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, "header type %T, expected %T", header, &Header{}, + return nil, sdkerrors.Wrapf( + clienttypes.ErrInvalidClientType, "substitute client state type %T, expected %T", substituteClient, &ClientState{}, ) } - consensusPublicKey, err := cs.ConsensusState.GetPubKey() + subjectPublicKey, err := cs.ConsensusState.GetPubKey() if err != nil { - return nil, nil, sdkerrors.Wrap(err, "failed to get consensus public key") + return nil, sdkerrors.Wrap(err, "failed to get consensus public key") } - headerPublicKey, err := smHeader.GetPubKey() + substitutePublicKey, err := substituteClientState.ConsensusState.GetPubKey() if err != nil { - return nil, nil, sdkerrors.Wrap(err, "failed to get header public key") + return nil, sdkerrors.Wrap(err, "failed to get substitute client public key") } - if reflect.DeepEqual(consensusPublicKey, headerPublicKey) { - return nil, nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, "new public key in header equals current public key", + if reflect.DeepEqual(subjectPublicKey, substitutePublicKey) { + return nil, sdkerrors.Wrapf( + clienttypes.ErrInvalidHeader, "subject and substitute have the same public key", ) } clientState := &cs - consensusState := &ConsensusState{ - PublicKey: smHeader.NewPublicKey, - Diversifier: smHeader.NewDiversifier, - Timestamp: smHeader.Timestamp, - } - - clientState.Sequence = smHeader.Sequence - clientState.ConsensusState = consensusState + // update to substitute parameters + clientState.Sequence = substituteClientState.Sequence + clientState.ConsensusState = substituteClientState.ConsensusState clientState.FrozenSequence = 0 - return clientState, consensusState, nil + return clientState, nil } diff --git a/x/ibc/light-clients/06-solomachine/types/proposal_handle_test.go b/x/ibc/light-clients/06-solomachine/types/proposal_handle_test.go index da5c815ec..0113da104 100644 --- a/x/ibc/light-clients/06-solomachine/types/proposal_handle_test.go +++ b/x/ibc/light-clients/06-solomachine/types/proposal_handle_test.go @@ -7,8 +7,11 @@ import ( ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ) -func (suite *SoloMachineTestSuite) TestCheckProposedHeaderAndUpdateState() { - var header exported.Header +func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { + var ( + subjectClientState *types.ClientState + substituteClientState exported.ClientState + ) // test singlesig and multisig public keys for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { @@ -19,21 +22,34 @@ func (suite *SoloMachineTestSuite) TestCheckProposedHeaderAndUpdateState() { expPass bool }{ { - "valid header", func() { - header = solomachine.CreateHeader() + "valid substitute", func() { + subjectClientState.AllowUpdateAfterProposal = true }, true, }, { - "nil header", func() { - header = &ibctmtypes.Header{} + "subject not allowed to be updated", func() { + subjectClientState.AllowUpdateAfterProposal = false }, false, }, { - "header does not update public key", func() { - header = &types.Header{ - Sequence: 1, - NewPublicKey: solomachine.ConsensusState().PublicKey, - } + "substitute is not the solo machine", func() { + substituteClientState = &ibctmtypes.ClientState{} + }, false, + }, + { + "subject public key is nil", func() { + subjectClientState.ConsensusState.PublicKey = nil + }, false, + }, + + { + "substitute public key is nil", func() { + substituteClientState.(*types.ClientState).ConsensusState.PublicKey = nil + }, false, + }, + { + "subject and substitute use the same public key", func() { + substituteClientState.(*types.ClientState).ConsensusState.PublicKey = subjectClientState.ConsensusState.PublicKey }, false, }, } @@ -44,46 +60,27 @@ func (suite *SoloMachineTestSuite) TestCheckProposedHeaderAndUpdateState() { suite.Run(tc.name, func() { suite.SetupTest() - clientState := solomachine.ClientState() + subjectClientState = solomachine.ClientState() + subjectClientState.AllowUpdateAfterProposal = true + substitute := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "substitute", "testing", 5) + substituteClientState = substitute.ClientState() tc.malleate() - clientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), solomachine.ClientID) + subjectClientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), solomachine.ClientID) + substituteClientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), substitute.ClientID) - // all cases should always fail if the client has 'AllowUpdateAfterProposal' set to false - clientState.AllowUpdateAfterProposal = false - cs, consState, err := clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header) - suite.Require().Error(err) - suite.Require().Nil(cs) - suite.Require().Nil(consState) - - clientState.AllowUpdateAfterProposal = true - cs, consState, err = clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header) + updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState, nil) if tc.expPass { suite.Require().NoError(err) - smConsState, ok := consState.(*types.ConsensusState) - suite.Require().True(ok) - smHeader, ok := header.(*types.Header) - suite.Require().True(ok) - - suite.Require().Equal(cs.(*types.ClientState).ConsensusState, consState) - - headerPubKey, err := smHeader.GetPubKey() - suite.Require().NoError(err) - - consStatePubKey, err := smConsState.GetPubKey() - suite.Require().NoError(err) - - suite.Require().Equal(headerPubKey, consStatePubKey) - suite.Require().Equal(smHeader.NewDiversifier, smConsState.Diversifier) - suite.Require().Equal(smHeader.Timestamp, smConsState.Timestamp) - suite.Require().Equal(smHeader.GetHeight().GetRevisionHeight(), cs.(*types.ClientState).Sequence) + suite.Require().Equal(substituteClientState.(*types.ClientState).ConsensusState, updatedClient.(*types.ClientState).ConsensusState) + suite.Require().Equal(substituteClientState.(*types.ClientState).Sequence, updatedClient.(*types.ClientState).Sequence) + suite.Require().Equal(uint64(0), updatedClient.(*types.ClientState).FrozenSequence) } else { suite.Require().Error(err) - suite.Require().Nil(cs) - suite.Require().Nil(consState) + suite.Require().Nil(updatedClient) } }) } diff --git a/x/ibc/light-clients/07-tendermint/types/proposal_handle.go b/x/ibc/light-clients/07-tendermint/types/proposal_handle.go index 4cd3eb376..c64c52b3f 100644 --- a/x/ibc/light-clients/07-tendermint/types/proposal_handle.go +++ b/x/ibc/light-clients/07-tendermint/types/proposal_handle.go @@ -1,9 +1,7 @@ package types import ( - "time" - - tmtypes "github.com/tendermint/tendermint/types" + "reflect" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -12,31 +10,52 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) -// CheckProposedHeaderAndUpdateState will try to update the client with the new header if and -// only if the proposal passes and one of the following two conditions is satisfied: -// 1) AllowUpdateAfterExpiry=true and Expire(ctx.BlockTime) = true -// 2) AllowUpdateAfterMisbehaviour and IsFrozen() = true -// In case 2) before trying to update the client, the client will be unfrozen by resetting -// the FrozenHeight to the zero Height. If AllowUpdateAfterMisbehaviour is set to true, -// expired clients will also be updated even if AllowUpdateAfterExpiry is set to false. -// Note, that even if the update happens, it may not be successful. The header may fail -// validation checks and an error will be returned in that case. -func (cs ClientState) CheckProposedHeaderAndUpdateState( - ctx sdk.Context, cdc codec.BinaryMarshaler, clientStore sdk.KVStore, - header exported.Header, -) (exported.ClientState, exported.ConsensusState, error) { - tmHeader, ok := header.(*Header) +// CheckSubstituteAndUpdateState will try to update the client with the state of the +// substitute if and only if the proposal passes and one of the following conditions are +// satisfied: +// 1) AllowUpdateAfterMisbehaviour and IsFrozen() = true +// 2) AllowUpdateAfterExpiry=true and Expire(ctx.BlockTime) = true +// +// The following must always be true: +// - The substitute client is the same type as the subject client +// - The subject and substitute client states match in all parameters (expect frozen height, latest height, and chain-id) +// +// In case 1) before updating the client, the client will be unfrozen by resetting +// the FrozenHeight to the zero Height. If a client is frozen and AllowUpdateAfterMisbehaviour +// is set to true, the client will be unexpired even if AllowUpdateAfterExpiry is set to false. +// Note, that even if the subject is updated to the state of the substitute, an error may be +// returned if the updated client state is invalid or the client is expired. +func (cs ClientState) CheckSubstituteAndUpdateState( + ctx sdk.Context, cdc codec.BinaryMarshaler, subjectClientStore, + substituteClientStore sdk.KVStore, substituteClient exported.ClientState, + initialHeight exported.Height, +) (exported.ClientState, error) { + substituteClientState, ok := substituteClient.(*ClientState) if !ok { - return nil, nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, "expected type %T, got %T", &Header{}, header, + return nil, sdkerrors.Wrapf( + clienttypes.ErrInvalidClient, "expected type %T, got %T", &ClientState{}, substituteClient, ) } + // substitute clients are not allowed to be upgraded during the voting period + // If an upgrade passes before the subject client has been updated, a new proposal must be created + // with an initial height that contains the new revision number. + if substituteClientState.GetLatestHeight().GetRevisionNumber() != initialHeight.GetRevisionNumber() { + return nil, sdkerrors.Wrapf( + clienttypes.ErrInvalidHeight, "substitute client revision number must equal initial height revision number (%d != %d)", + substituteClientState.GetLatestHeight().GetRevisionNumber(), initialHeight.GetRevisionNumber(), + ) + } + + if !IsMatchingClientState(cs, *substituteClientState) { + return nil, sdkerrors.Wrap(clienttypes.ErrInvalidSubstitute, "subject client state does not match substitute client state") + } + // get consensus state corresponding to client state to check if the client is expired - consensusState, err := GetConsensusState(clientStore, cdc, cs.GetLatestHeight()) + consensusState, err := GetConsensusState(subjectClientStore, cdc, cs.GetLatestHeight()) if err != nil { - return nil, nil, sdkerrors.Wrapf( - err, "could not get consensus state from clientstore at height: %d", cs.GetLatestHeight(), + return nil, sdkerrors.Wrapf( + err, "unexpected error: could not get consensus state from clientstore at height: %d", cs.GetLatestHeight(), ) } @@ -44,84 +63,72 @@ func (cs ClientState) CheckProposedHeaderAndUpdateState( case cs.IsFrozen(): if !cs.AllowUpdateAfterMisbehaviour { - return nil, nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "client is not allowed to be unfrozen") + return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "client is not allowed to be unfrozen") } // unfreeze the client cs.FrozenHeight = clienttypes.ZeroHeight() - // if the client is expired we unexpire the client using softer validation, otherwise - // full validation on the header is performed. - if cs.IsExpired(consensusState.Timestamp, ctx.BlockTime()) { - return cs.unexpireClient(ctx, clientStore, consensusState, tmHeader, ctx.BlockTime()) + case cs.IsExpired(consensusState.Timestamp, ctx.BlockTime()): + if !cs.AllowUpdateAfterExpiry { + return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "client is not allowed to be unexpired") } - // NOTE: the client may be frozen again since the misbehaviour evidence may - // not be expired yet - return cs.CheckHeaderAndUpdateState(ctx, cdc, clientStore, header) - - case cs.AllowUpdateAfterExpiry && cs.IsExpired(consensusState.Timestamp, ctx.BlockTime()): - return cs.unexpireClient(ctx, clientStore, consensusState, tmHeader, ctx.BlockTime()) - default: - return nil, nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "client cannot be updated with proposal") + return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "client cannot be updated with proposal") } -} + // copy consensus states and processed time from substitute to subject + // starting from initial height and ending on the latest height (inclusive) + for i := initialHeight.GetRevisionHeight(); i <= substituteClientState.GetLatestHeight().GetRevisionHeight(); i++ { + height := clienttypes.NewHeight(substituteClientState.GetLatestHeight().GetRevisionNumber(), i) -// unexpireClient checks if the proposed header is sufficient to update an expired client. -// The client is updated if no error occurs. -func (cs ClientState) unexpireClient( - ctx sdk.Context, clientStore sdk.KVStore, consensusState *ConsensusState, header *Header, currentTimestamp time.Time, -) (exported.ClientState, exported.ConsensusState, error) { + consensusState, err := GetConsensusState(substituteClientStore, cdc, height) + if err != nil { + // not all consensus states will be filled in + continue + } + SetConsensusState(subjectClientStore, cdc, consensusState, height) + + processedTime, found := GetProcessedTime(substituteClientStore, height) + if !found { + continue + } + SetProcessedTime(subjectClientStore, height, processedTime) - // the client is expired and either AllowUpdateAfterMisbehaviour or AllowUpdateAfterExpiry - // is set to true so light validation of the header is executed - if err := cs.checkProposedHeader(consensusState, header, currentTimestamp); err != nil { - return nil, nil, err } - newClientState, consensusState := update(ctx, clientStore, &cs, header) - return newClientState, consensusState, nil -} + cs.LatestHeight = substituteClientState.LatestHeight -// checkProposedHeader checks if the Tendermint header is valid for updating a client after -// a passed proposal. -// It returns an error if: -// - the header provided is not parseable to tendermint types -// - header height is less than or equal to the latest client state height -// - signed tendermint header is invalid -// - header timestamp is less than or equal to the latest consensus state timestamp -// - header timestamp is expired -// NOTE: header.ValidateBasic is called in the 02-client proposal handler. Additional checks -// on the validator set and the validator set hash are done in header.ValidateBasic. -func (cs ClientState) checkProposedHeader(consensusState *ConsensusState, header *Header, currentTimestamp time.Time) error { - tmSignedHeader, err := tmtypes.SignedHeaderFromProto(header.SignedHeader) + // validate the updated client and ensure it isn't expired + if err := cs.Validate(); err != nil { + return nil, sdkerrors.Wrap(err, "unexpected error: updated subject client state is invalid") + } + + latestConsensusState, err := GetConsensusState(subjectClientStore, cdc, cs.GetLatestHeight()) if err != nil { - return sdkerrors.Wrap(err, "signed header in not tendermint signed header type") - } - - if !header.GetTime().After(consensusState.Timestamp) { - return sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, - "header timestamp is less than or equal to latest consensus state timestamp (%s ≤ %s)", header.GetTime(), consensusState.Timestamp) - } - - // assert header height is newer than latest client state - if header.GetHeight().LTE(cs.GetLatestHeight()) { - return sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, - "header height ≤ consensus state height (%s ≤ %s)", header.GetHeight(), cs.GetLatestHeight(), + return nil, sdkerrors.Wrapf( + err, "unexpected error: could not get consensus state for updated subject client from clientstore at height: %d", cs.GetLatestHeight(), ) } - if err := tmSignedHeader.ValidateBasic(cs.GetChainID()); err != nil { - return sdkerrors.Wrap(err, "signed header failed basic validation") + if cs.IsExpired(latestConsensusState.Timestamp, ctx.BlockTime()) { + return nil, sdkerrors.Wrap(clienttypes.ErrInvalidClient, "updated subject client is expired") } - if cs.IsExpired(header.GetTime(), currentTimestamp) { - return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "header timestamp is already expired") - } - - return nil + return &cs, nil +} + +// IsMatchingClientState returns true if all the client state parameters match +// except for frozen height, latest height, and chain-id. +func IsMatchingClientState(subject, substitute ClientState) bool { + // zero out parameters which do not need to match + subject.LatestHeight = clienttypes.ZeroHeight() + subject.FrozenHeight = clienttypes.ZeroHeight() + substitute.LatestHeight = clienttypes.ZeroHeight() + substitute.FrozenHeight = clienttypes.ZeroHeight() + subject.ChainId = "" + substitute.ChainId = "" + + return reflect.DeepEqual(subject, substitute) } diff --git a/x/ibc/light-clients/07-tendermint/types/proposal_handle_test.go b/x/ibc/light-clients/07-tendermint/types/proposal_handle_test.go index 6863ad1d1..66a512030 100644 --- a/x/ibc/light-clients/07-tendermint/types/proposal_handle_test.go +++ b/x/ibc/light-clients/07-tendermint/types/proposal_handle_test.go @@ -1,6 +1,8 @@ package types_test import ( + "time" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" @@ -11,38 +13,120 @@ var ( frozenHeight = clienttypes.NewHeight(0, 1) ) -// sanity checks -func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateStateBasic() { - clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) - clientState := suite.chainA.GetClientState(clientA).(*types.ClientState) - clientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) +func (suite *TendermintTestSuite) TestCheckSubstituteUpdateStateBasic() { + var ( + substitute string + substituteClientState exported.ClientState + initialHeight clienttypes.Height + ) + testCases := []struct { + name string + malleate func() + }{ + { + "solo machine used for substitute", func() { + substituteClientState = ibctesting.NewSolomachine(suite.T(), suite.cdc, "solo machine", "", 1).ClientState() + }, + }, + { + "initial height and substitute revision numbers do not match", func() { + initialHeight = clienttypes.NewHeight(substituteClientState.GetLatestHeight().GetRevisionNumber()+1, 1) + }, + }, + { + "non-matching substitute", func() { + substitute, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + substituteClientState = suite.chainA.GetClientState(substitute).(*types.ClientState) + tmClientState, ok := substituteClientState.(*types.ClientState) + suite.Require().True(ok) - // use nil header - cs, consState, err := clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, nil) - suite.Require().Error(err) - suite.Require().Nil(cs) - suite.Require().Nil(consState) + tmClientState.ChainId = tmClientState.ChainId + "different chain" + }, + }, + { + "updated client is invalid - revision height is zero", func() { + substitute, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + substituteClientState = suite.chainA.GetClientState(substitute).(*types.ClientState) + tmClientState, ok := substituteClientState.(*types.ClientState) + suite.Require().True(ok) + // match subject + tmClientState.AllowUpdateAfterMisbehaviour = true + tmClientState.AllowUpdateAfterExpiry = true - clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) + // will occur. This case should never occur (caught by upstream checks) + initialHeight = clienttypes.NewHeight(5, 0) + tmClientState.LatestHeight = clienttypes.NewHeight(5, 0) + }, + }, + { + "updated client is expired", func() { + substitute, _ = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + substituteClientState = suite.chainA.GetClientState(substitute).(*types.ClientState) + tmClientState, ok := substituteClientState.(*types.ClientState) + suite.Require().True(ok) + initialHeight = tmClientState.LatestHeight - // consensus state for latest height does not exist - cs, consState, err = clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, suite.chainA.LastHeader) - suite.Require().Error(err) - suite.Require().Nil(cs) - suite.Require().Nil(consState) + // match subject + tmClientState.AllowUpdateAfterMisbehaviour = true + tmClientState.AllowUpdateAfterExpiry = true + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), substitute, tmClientState) + + // update substitute a few times + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, substitute, exported.Tendermint) + suite.Require().NoError(err) + substituteClientState = suite.chainA.GetClientState(substitute) + + err = suite.coordinator.UpdateClient(suite.chainA, suite.chainB, substitute, exported.Tendermint) + suite.Require().NoError(err) + + suite.chainA.ExpireClient(tmClientState.TrustingPeriod) + suite.chainB.ExpireClient(tmClientState.TrustingPeriod) + suite.coordinator.CommitBlock(suite.chainA, suite.chainB) + + substituteClientState = suite.chainA.GetClientState(substitute) + }, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + + suite.SetupTest() // reset + + subject, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + subjectClientState := suite.chainA.GetClientState(subject).(*types.ClientState) + subjectClientState.AllowUpdateAfterMisbehaviour = true + subjectClientState.AllowUpdateAfterExpiry = true + + // expire subject + suite.chainA.ExpireClient(subjectClientState.TrustingPeriod) + suite.chainB.ExpireClient(subjectClientState.TrustingPeriod) + suite.coordinator.CommitBlock(suite.chainA, suite.chainB) + + tc.malleate() + + subjectClientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), subject) + substituteClientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), substitute) + + updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState, initialHeight) + suite.Require().Error(err) + suite.Require().Nil(updatedClient) + }) + } } // to expire clients, time needs to be fast forwarded on both chainA and chainB. // this is to prevent headers from failing when attempting to update later. -func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { +func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { testCases := []struct { name string AllowUpdateAfterExpiry bool AllowUpdateAfterMisbehaviour bool FreezeClient bool ExpireClient bool - expPassUnfreeze bool // expected result using a header that passes stronger validation - expPassUnexpire bool // expected result using a header that passes weaker validation + expPass bool }{ { name: "not allowed to be updated, not frozen or expired", @@ -50,8 +134,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: false, FreezeClient: false, ExpireClient: false, - expPassUnfreeze: false, - expPassUnexpire: false, + expPass: false, }, { name: "not allowed to be updated, client is frozen", @@ -59,8 +142,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: false, FreezeClient: true, ExpireClient: false, - expPassUnfreeze: false, - expPassUnexpire: false, + expPass: false, }, { name: "not allowed to be updated, client is expired", @@ -68,8 +150,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: false, FreezeClient: false, ExpireClient: true, - expPassUnfreeze: false, - expPassUnexpire: false, + expPass: false, }, { name: "not allowed to be updated, client is frozen and expired", @@ -77,8 +158,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: false, FreezeClient: true, ExpireClient: true, - expPassUnfreeze: false, - expPassUnexpire: false, + expPass: false, }, { name: "allowed to be updated only after misbehaviour, not frozen or expired", @@ -86,17 +166,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: true, FreezeClient: false, ExpireClient: false, - expPassUnfreeze: false, - expPassUnexpire: false, - }, - { - name: "PASS: allowed to be updated only after misbehaviour, client is frozen", - AllowUpdateAfterExpiry: false, - AllowUpdateAfterMisbehaviour: true, - FreezeClient: true, - ExpireClient: false, - expPassUnfreeze: true, - expPassUnexpire: false, + expPass: false, }, { name: "allowed to be updated only after misbehaviour, client is expired", @@ -104,17 +174,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: true, FreezeClient: false, ExpireClient: true, - expPassUnfreeze: false, - expPassUnexpire: false, - }, - { - name: "allowed to be updated only after misbehaviour, client is frozen and expired", - AllowUpdateAfterExpiry: false, - AllowUpdateAfterMisbehaviour: true, - FreezeClient: true, - ExpireClient: true, - expPassUnfreeze: true, - expPassUnexpire: true, + expPass: false, }, { name: "allowed to be updated only after expiry, not frozen or expired", @@ -122,8 +182,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: false, FreezeClient: false, ExpireClient: false, - expPassUnfreeze: false, - expPassUnexpire: false, + expPass: false, }, { name: "allowed to be updated only after expiry, client is frozen", @@ -131,8 +190,23 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: false, FreezeClient: true, ExpireClient: false, - expPassUnfreeze: false, - expPassUnexpire: false, + expPass: false, + }, + { + name: "PASS: allowed to be updated only after misbehaviour, client is frozen", + AllowUpdateAfterExpiry: false, + AllowUpdateAfterMisbehaviour: true, + FreezeClient: true, + ExpireClient: false, + expPass: true, + }, + { + name: "PASS: allowed to be updated only after misbehaviour, client is frozen and expired", + AllowUpdateAfterExpiry: false, + AllowUpdateAfterMisbehaviour: true, + FreezeClient: true, + ExpireClient: true, + expPass: true, }, { name: "PASS: allowed to be updated only after expiry, client is expired", @@ -140,8 +214,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: false, FreezeClient: false, ExpireClient: true, - expPassUnfreeze: true, - expPassUnexpire: true, + expPass: true, }, { name: "allowed to be updated only after expiry, client is frozen and expired", @@ -149,8 +222,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: false, FreezeClient: true, ExpireClient: true, - expPassUnfreeze: false, - expPassUnexpire: false, + expPass: false, }, { name: "allowed to be updated after expiry and misbehaviour, not frozen or expired", @@ -158,8 +230,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: true, FreezeClient: false, ExpireClient: false, - expPassUnfreeze: false, - expPassUnexpire: false, + expPass: false, }, { name: "PASS: allowed to be updated after expiry and misbehaviour, client is frozen", @@ -167,8 +238,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: true, FreezeClient: true, ExpireClient: false, - expPassUnfreeze: true, - expPassUnexpire: false, + expPass: true, }, { name: "PASS: allowed to be updated after expiry and misbehaviour, client is expired", @@ -176,8 +246,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: true, FreezeClient: false, ExpireClient: true, - expPassUnfreeze: true, - expPassUnexpire: true, + expPass: true, }, { name: "PASS: allowed to be updated after expiry and misbehaviour, client is frozen and expired", @@ -185,8 +254,7 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { AllowUpdateAfterMisbehaviour: true, FreezeClient: true, ExpireClient: true, - expPassUnfreeze: true, - expPassUnexpire: true, + expPass: true, }, } @@ -201,67 +269,67 @@ func (suite *TendermintTestSuite) TestCheckProposedHeaderAndUpdateState() { // start by testing unexpiring the client suite.SetupTest() // reset - // construct client state based on test case parameters - clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) - clientState := suite.chainA.GetClientState(clientA).(*types.ClientState) - clientState.AllowUpdateAfterExpiry = tc.AllowUpdateAfterExpiry - clientState.AllowUpdateAfterMisbehaviour = tc.AllowUpdateAfterMisbehaviour + // construct subject using test case parameters + subject, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + subjectClientState := suite.chainA.GetClientState(subject).(*types.ClientState) + subjectClientState.AllowUpdateAfterExpiry = tc.AllowUpdateAfterExpiry + subjectClientState.AllowUpdateAfterMisbehaviour = tc.AllowUpdateAfterMisbehaviour + + // apply freezing or expiry as determined by the test case if tc.FreezeClient { - clientState.FrozenHeight = frozenHeight + subjectClientState.FrozenHeight = frozenHeight } if tc.ExpireClient { - suite.chainA.ExpireClient(clientState.TrustingPeriod) - suite.chainB.ExpireClient(clientState.TrustingPeriod) + suite.chainA.ExpireClient(subjectClientState.TrustingPeriod) + suite.chainB.ExpireClient(subjectClientState.TrustingPeriod) suite.coordinator.CommitBlock(suite.chainA, suite.chainB) } - // use next header for chainB to unfreeze client on chainA - unfreezeClientHeader, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, clientA) - suite.Require().NoError(err) + // construct the substitute to match the subject client + // NOTE: the substitute is explicitly created after the freezing or expiry occurs, + // primarily to prevent the substitute from becoming frozen. It also should be + // the natural flow of events in practice. The subject will become frozen/expired + // and a substitute will be created along with a governance proposal as a response - clientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) - cs, consState, err := clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, unfreezeClientHeader) + substitute, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + substituteClientState := suite.chainA.GetClientState(substitute).(*types.ClientState) + substituteClientState.AllowUpdateAfterExpiry = tc.AllowUpdateAfterExpiry + substituteClientState.AllowUpdateAfterMisbehaviour = tc.AllowUpdateAfterMisbehaviour + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), substitute, substituteClientState) - if tc.expPassUnfreeze { + initialHeight := substituteClientState.GetLatestHeight() + + // update substitute a few times + for i := 0; i < 3; i++ { + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, substitute, exported.Tendermint) suite.Require().NoError(err) - suite.Require().Equal(clienttypes.ZeroHeight(), cs.GetFrozenHeight()) - suite.Require().NotNil(consState) - } else { - suite.Require().Error(err) - suite.Require().Nil(cs) - suite.Require().Nil(consState) + // skip a block + suite.coordinator.CommitBlock(suite.chainA, suite.chainB) } - // use next header for chainB to unexpire clients but with empty trusted heights - // and validators. Update chainB time so header won't be expired. - unexpireClientHeader, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, clientA) - suite.Require().NoError(err) - unexpireClientHeader.TrustedHeight = clienttypes.ZeroHeight() - unexpireClientHeader.TrustedValidators = nil + // get updated substitute + substituteClientState = suite.chainA.GetClientState(substitute).(*types.ClientState) - clientStore = suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) - cs, consState, err = clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, unexpireClientHeader) + subjectClientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), subject) + substituteClientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), substitute) + updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState, initialHeight) - if tc.expPassUnexpire { + if tc.expPass { suite.Require().NoError(err) - suite.Require().NotNil(cs) - suite.Require().NotNil(consState) + suite.Require().Equal(clienttypes.ZeroHeight(), updatedClient.GetFrozenHeight()) } else { suite.Require().Error(err) - suite.Require().Nil(cs) - suite.Require().Nil(consState) + suite.Require().Nil(updatedClient) } + }) } } -// test softer validation on headers used for unexpiring clients -func (suite *TendermintTestSuite) TestCheckProposedHeader() { +func (suite *TendermintTestSuite) TestIsMatchingClientState() { var ( - header *types.Header - clientState *types.ClientState - clientA string - err error + subject, substitute string + subjectClientState, substituteClientState *types.ClientState ) testCases := []struct { @@ -270,46 +338,33 @@ func (suite *TendermintTestSuite) TestCheckProposedHeader() { expPass bool }{ { - "success", func() {}, true, + "matching clients", func() { + subjectClientState = suite.chainA.GetClientState(subject).(*types.ClientState) + substituteClientState = suite.chainA.GetClientState(substitute).(*types.ClientState) + }, true, }, { - "invalid signed header", func() { - header.SignedHeader = nil - }, false, + "matching, frozen height is not used in check for equality", func() { + subjectClientState.FrozenHeight = frozenHeight + substituteClientState.FrozenHeight = clienttypes.ZeroHeight() + }, true, }, { - "header time is less than or equal to consensus state timestamp", func() { - consensusState, found := suite.chainA.GetConsensusState(clientA, clientState.GetLatestHeight()) - suite.Require().True(found) - consensusState.(*types.ConsensusState).Timestamp = header.GetTime() - suite.chainA.App.IBCKeeper.ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientA, clientState.GetLatestHeight(), consensusState) - - // update block time so client is expired - suite.chainA.ExpireClient(clientState.TrustingPeriod) - suite.chainB.ExpireClient(clientState.TrustingPeriod) - suite.coordinator.CommitBlock(suite.chainA, suite.chainB) - }, false, + "matching, latest height is not used in check for equality", func() { + subjectClientState.LatestHeight = clienttypes.NewHeight(0, 10) + substituteClientState.FrozenHeight = clienttypes.ZeroHeight() + }, true, }, { - "header height is not newer than client state", func() { - consensusState, found := suite.chainA.GetConsensusState(clientA, clientState.GetLatestHeight()) - suite.Require().True(found) - clientState.LatestHeight = header.GetHeight().(clienttypes.Height) - suite.chainA.App.IBCKeeper.ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientA, clientState.GetLatestHeight(), consensusState) - - }, false, + "matching, chain id is different", func() { + subjectClientState.ChainId = "bitcoin" + substituteClientState.ChainId = "ethereum" + }, true, }, { - "signed header failed validate basic - wrong chain ID", func() { - clientState.ChainId = ibctesting.InvalidID - }, false, - }, - { - "header is already expired", func() { - // expire client - suite.chainA.ExpireClient(clientState.TrustingPeriod) - suite.chainB.ExpireClient(clientState.TrustingPeriod) - suite.coordinator.CommitBlock(suite.chainA, suite.chainB) + "not matching, trusting period is different", func() { + subjectClientState.TrustingPeriod = time.Duration(time.Hour * 10) + substituteClientState.TrustingPeriod = time.Duration(time.Hour * 1) }, false, }, } @@ -320,37 +375,13 @@ func (suite *TendermintTestSuite) TestCheckProposedHeader() { suite.Run(tc.name, func() { suite.SetupTest() // reset - clientA, _ = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) - clientState = suite.chainA.GetClientState(clientA).(*types.ClientState) - clientState.AllowUpdateAfterExpiry = true - clientState.AllowUpdateAfterMisbehaviour = false - - // expire client - suite.chainA.ExpireClient(clientState.TrustingPeriod) - suite.chainB.ExpireClient(clientState.TrustingPeriod) - suite.coordinator.CommitBlock(suite.chainA, suite.chainB) - - // use next header for chainB to unexpire clients but with empty trusted heights - // and validators. - header, err = suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, clientA) - suite.Require().NoError(err) - header.TrustedHeight = clienttypes.ZeroHeight() - header.TrustedValidators = nil + subject, _ = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + substitute, _ = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) tc.malleate() - clientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) - cs, consState, err := clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header) + suite.Require().Equal(tc.expPass, types.IsMatchingClientState(*subjectClientState, *substituteClientState)) - if tc.expPass { - suite.Require().NoError(err) - suite.Require().NotNil(cs) - suite.Require().NotNil(consState) - } else { - suite.Require().Error(err) - suite.Require().Nil(cs) - suite.Require().Nil(consState) - } }) } } diff --git a/x/ibc/light-clients/07-tendermint/types/store.go b/x/ibc/light-clients/07-tendermint/types/store.go index 8b2720c5a..7d6a841b8 100644 --- a/x/ibc/light-clients/07-tendermint/types/store.go +++ b/x/ibc/light-clients/07-tendermint/types/store.go @@ -14,6 +14,13 @@ import ( // KeyProcessedTime is appended to consensus state key to store the processed time var KeyProcessedTime = []byte("/processedTime") +// SetConsensusState stores the consensus state at the given height. +func SetConsensusState(clientStore sdk.KVStore, cdc codec.BinaryMarshaler, consensusState *ConsensusState, height exported.Height) { + key := host.ConsensusStateKey(height) + val := clienttypes.MustMarshalConsensusState(cdc, consensusState) + clientStore.Set(key, val) +} + // GetConsensusState retrieves the consensus state from the client prefixed // store. An error is returned if the consensus state does not exist. func GetConsensusState(store sdk.KVStore, cdc codec.BinaryMarshaler, height exported.Height) (*ConsensusState, error) { diff --git a/x/ibc/light-clients/09-localhost/types/client_state.go b/x/ibc/light-clients/09-localhost/types/client_state.go index e0ba7a2f0..5a4a41a17 100644 --- a/x/ibc/light-clients/09-localhost/types/client_state.go +++ b/x/ibc/light-clients/09-localhost/types/client_state.go @@ -107,12 +107,13 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( return nil, sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "cannot submit misbehaviour to localhost client") } -// CheckProposedHeaderAndUpdateState returns an error. The localhost cannot be modified by +// CheckSubstituteAndUpdateState returns an error. The localhost cannot be modified by // proposals. -func (cs ClientState) CheckProposedHeaderAndUpdateState( - ctx sdk.Context, _ codec.BinaryMarshaler, _ sdk.KVStore, _ exported.Header, -) (exported.ClientState, exported.ConsensusState, error) { - return nil, nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "cannot update localhost client with a proposal") +func (cs ClientState) CheckSubstituteAndUpdateState( + ctx sdk.Context, _ codec.BinaryMarshaler, _, _ sdk.KVStore, + _ exported.ClientState, _ exported.Height, +) (exported.ClientState, error) { + return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "cannot update localhost client with a proposal") } // VerifyUpgradeAndUpdateState returns an error since localhost cannot be upgraded diff --git a/x/ibc/light-clients/09-localhost/types/client_state_test.go b/x/ibc/light-clients/09-localhost/types/client_state_test.go index 13a1367d5..bc58f6253 100644 --- a/x/ibc/light-clients/09-localhost/types/client_state_test.go +++ b/x/ibc/light-clients/09-localhost/types/client_state_test.go @@ -170,10 +170,9 @@ func (suite *LocalhostTestSuite) TestMisbehaviourAndUpdateState() { func (suite *LocalhostTestSuite) TestProposedHeaderAndUpdateState() { clientState := types.NewClientState("chainID", clientHeight) - cs, consState, err := clientState.CheckProposedHeaderAndUpdateState(suite.ctx, nil, nil, nil) + cs, err := clientState.CheckSubstituteAndUpdateState(suite.ctx, nil, nil, nil, nil, nil) suite.Require().Error(err) suite.Require().Nil(cs) - suite.Require().Nil(consState) } func (suite *LocalhostTestSuite) TestVerifyConnectionState() { diff --git a/x/ibc/testing/mock/mock.go b/x/ibc/testing/mock/mock.go index 663497aa0..16e2ad345 100644 --- a/x/ibc/testing/mock/mock.go +++ b/x/ibc/testing/mock/mock.go @@ -124,8 +124,6 @@ func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.V return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // OnChanOpenInit implements the IBCModule interface. func (am AppModule) OnChanOpenInit( ctx sdk.Context, _ channeltypes.Order, _ []string, portID string, diff --git a/x/mint/keeper/keeper.go b/x/mint/keeper/keeper.go index de0a31222..46eb6ba53 100644 --- a/x/mint/keeper/keeper.go +++ b/x/mint/keeper/keeper.go @@ -45,8 +45,6 @@ func NewKeeper( } } -//______________________________________________________________________ - // Logger returns a module-specific logger. func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", "x/"+types.ModuleName) @@ -71,8 +69,6 @@ func (k Keeper) SetMinter(ctx sdk.Context, minter types.Minter) { store.Set(types.MinterKey, b) } -//______________________________________________________________________ - // GetParams returns the total set of minting parameters. func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { k.paramSpace.GetParamSet(ctx, ¶ms) @@ -84,8 +80,6 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { k.paramSpace.SetParamSet(ctx, ¶ms) } -//______________________________________________________________________ - // StakingTokenSupply implements an alias call to the underlying staking keeper's // StakingTokenSupply to be used in BeginBlocker. func (k Keeper) StakingTokenSupply(ctx sdk.Context) sdk.Int { diff --git a/x/mint/module.go b/x/mint/module.go index d33043b36..a4d1f113f 100644 --- a/x/mint/module.go +++ b/x/mint/module.go @@ -83,8 +83,6 @@ func (AppModuleBasic) GetQueryCmd() *cobra.Command { return cli.GetQueryCmd() } -//____________________________________________________________________________ - // AppModule implements an application module for the mint module. type AppModule struct { AppModuleBasic @@ -160,8 +158,6 @@ func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.Validato return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the mint module. diff --git a/x/mint/types/query.pb.gw.go b/x/mint/types/query.pb.gw.go index c70c3e60b..576b206d4 100644 --- a/x/mint/types/query.pb.gw.go +++ b/x/mint/types/query.pb.gw.go @@ -256,11 +256,11 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "mint", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "mint", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Inflation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "mint", "v1beta1", "inflation"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Inflation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "mint", "v1beta1", "inflation"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_AnnualProvisions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "mint", "v1beta1", "annual_provisions"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_AnnualProvisions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "mint", "v1beta1", "annual_provisions"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/params/client/cli/tx.go b/x/params/client/cli/tx.go index 39b024252..f83793a14 100644 --- a/x/params/client/cli/tx.go +++ b/x/params/client/cli/tx.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/version" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" paramscutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" @@ -80,11 +81,14 @@ Where proposal.json contains: if err != nil { return err } - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := govtypes.NewMsgClient(svcMsgClientConn) + _, err = msgClient.SubmitProposal(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } } diff --git a/x/params/module.go b/x/params/module.go index c1a115b21..624ff2d25 100644 --- a/x/params/module.go +++ b/x/params/module.go @@ -72,8 +72,6 @@ func (am AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistr proposal.RegisterInterfaces(registry) } -//____________________________________________________________________________ - // AppModule implements an application module for the distribution module. type AppModule struct { AppModuleBasic diff --git a/x/params/types/proposal/query.pb.gw.go b/x/params/types/proposal/query.pb.gw.go index c7fab13fc..f5b9f9d5b 100644 --- a/x/params/types/proposal/query.pb.gw.go +++ b/x/params/types/proposal/query.pb.gw.go @@ -158,7 +158,7 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 1}, []string{"cosmos", "params", "v1beta1"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 1}, []string{"cosmos", "params", "v1beta1"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/simulation/log.go b/x/simulation/log.go index 236a72440..ba22b2af4 100644 --- a/x/simulation/log.go +++ b/x/simulation/log.go @@ -68,7 +68,6 @@ func createLogFile() *os.File { return f } -//_____________________ // dummy log writter type DummyLogWriter struct{} diff --git a/x/simulation/mock_tendermint.go b/x/simulation/mock_tendermint.go index ad84e8213..55c2ca544 100644 --- a/x/simulation/mock_tendermint.go +++ b/x/simulation/mock_tendermint.go @@ -59,8 +59,6 @@ func (vals mockValidators) getKeys() []string { return keys } -//_________________________________________________________________________________ - // randomProposer picks a random proposer from the current validator set func (vals mockValidators) randomProposer(r *rand.Rand) tmbytes.HexBytes { keys := vals.getKeys() diff --git a/x/simulation/operation.go b/x/simulation/operation.go index 914daafc8..7aab48e9f 100644 --- a/x/simulation/operation.go +++ b/x/simulation/operation.go @@ -64,8 +64,6 @@ func (oe OperationEntry) MustMarshal() json.RawMessage { return out } -//_____________________________________________________________________ - // OperationQueue defines an object for a queue of operations type OperationQueue map[int][]simulation.Operation @@ -107,8 +105,6 @@ func queueOperations(queuedOps OperationQueue, queuedTimeOps []simulation.Future } } -//________________________________________________________________________ - // WeightedOperation is an operation with associated weight. // This is used to bias the selection operation within the simulator. type WeightedOperation struct { diff --git a/x/simulation/params.go b/x/simulation/params.go index 12e8e0972..b0569e948 100644 --- a/x/simulation/params.go +++ b/x/simulation/params.go @@ -87,7 +87,6 @@ func RandomParams(r *rand.Rand) Params { } } -//----------------------------------------------------------------------------- // Param change proposals // ParamChange defines the object used for simulating parameter change proposals @@ -123,7 +122,6 @@ func (spc ParamChange) ComposedKey() string { return fmt.Sprintf("%s/%s", spc.Subspace(), spc.Key()) } -//----------------------------------------------------------------------------- // Proposal Contents // WeightedProposalContent defines a common struct for proposal contents defined by @@ -150,7 +148,6 @@ func (w WeightedProposalContent) ContentSimulatorFn() simulation.ContentSimulato return w.contentSimulatorFn } -//----------------------------------------------------------------------------- // Param change proposals // randomConsensusParams returns random simulation consensus parameters, it extracts the Evidence from the Staking genesis state. diff --git a/x/simulation/simulate.go b/x/simulation/simulate.go index fdcf22461..6e391aa2e 100644 --- a/x/simulation/simulate.go +++ b/x/simulation/simulate.go @@ -240,8 +240,6 @@ func SimulateFromSeed( return false, exportedParams, nil } -//______________________________________________________________________________ - type blockSimFn func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []simulation.Account, header tmproto.Header) (opCount int) diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index d896c8570..67a83e6a6 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/x/slashing/types" ) @@ -41,11 +42,14 @@ $ tx slashing unjail --from mykey valAddr := clientCtx.GetFromAddress() msg := types.NewMsgUnjail(sdk.ValAddress(valAddr)) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.Unjail(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } diff --git a/x/slashing/keeper/hooks.go b/x/slashing/keeper/hooks.go index d59601425..e3a00e9f5 100644 --- a/x/slashing/keeper/hooks.go +++ b/x/slashing/keeper/hooks.go @@ -42,8 +42,6 @@ func (k Keeper) AfterValidatorRemoved(ctx sdk.Context, address sdk.ConsAddress) k.deleteAddrPubkeyRelation(ctx, crypto.Address(address)) } -//_________________________________________________________________________________________ - // Hooks wrapper struct for slashing keeper type Hooks struct { k Keeper diff --git a/x/slashing/keeper/migrations.go b/x/slashing/keeper/migrations.go new file mode 100644 index 000000000..b1bda4ab6 --- /dev/null +++ b/x/slashing/keeper/migrations.go @@ -0,0 +1,21 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + v042 "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v042" +) + +// Migrator is a struct for handling in-place store migrations. +type Migrator struct { + keeper Keeper +} + +// NewMigrator returns a new Migrator. +func NewMigrator(keeper Keeper) Migrator { + return Migrator{keeper: keeper} +} + +// Migrate1to2 migrates from version 1 to 2. +func (m Migrator) Migrate1to2(ctx sdk.Context) error { + return v042.MigrateStore(ctx, m.keeper.storeKey) +} diff --git a/x/slashing/legacy/v040/keys.go b/x/slashing/legacy/v040/keys.go new file mode 100644 index 000000000..02cefbc45 --- /dev/null +++ b/x/slashing/legacy/v040/keys.go @@ -0,0 +1,69 @@ +// Package v040 is copy-pasted from: +// https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/slashing/types/keys.go +package v040 + +import ( + "encoding/binary" + + sdk "github.com/cosmos/cosmos-sdk/types" + v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" +) + +const ( + // ModuleName is the name of the module + ModuleName = "slashing" + + // StoreKey is the store key string for slashing + StoreKey = ModuleName + + // RouterKey is the message route for slashing + RouterKey = ModuleName + + // QuerierRoute is the querier route for slashing + QuerierRoute = ModuleName +) + +// Keys for slashing store +// Items are stored with the following key: values +// +// - 0x01: ValidatorSigningInfo +// +// - 0x02: bool +// +// - 0x03: crypto.PubKey +var ( + ValidatorSigningInfoKeyPrefix = []byte{0x01} // Prefix for signing info + ValidatorMissedBlockBitArrayKeyPrefix = []byte{0x02} // Prefix for missed block bit array + AddrPubkeyRelationKeyPrefix = []byte{0x03} // Prefix for address-pubkey relation +) + +// ValidatorSigningInfoKey - stored by *Consensus* address (not operator address) +func ValidatorSigningInfoKey(v sdk.ConsAddress) []byte { + return append(ValidatorSigningInfoKeyPrefix, v.Bytes()...) +} + +// ValidatorSigningInfoAddress - extract the address from a validator signing info key +func ValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) { + addr := key[1:] + if len(addr) != v040auth.AddrLen { + panic("unexpected key length") + } + return sdk.ConsAddress(addr) +} + +// ValidatorMissedBlockBitArrayPrefixKey - stored by *Consensus* address (not operator address) +func ValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte { + return append(ValidatorMissedBlockBitArrayKeyPrefix, v.Bytes()...) +} + +// ValidatorMissedBlockBitArrayKey - stored by *Consensus* address (not operator address) +func ValidatorMissedBlockBitArrayKey(v sdk.ConsAddress, i int64) []byte { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, uint64(i)) + return append(ValidatorMissedBlockBitArrayPrefixKey(v), b...) +} + +// AddrPubkeyRelationKey gets pubkey relation key used to get the pubkey from the address +func AddrPubkeyRelationKey(address []byte) []byte { + return append(AddrPubkeyRelationKeyPrefix, address...) +} diff --git a/x/slashing/legacy/v040/types.go b/x/slashing/legacy/v040/types.go deleted file mode 100644 index b95fa6fe2..000000000 --- a/x/slashing/legacy/v040/types.go +++ /dev/null @@ -1,5 +0,0 @@ -package v040 - -const ( - ModuleName = "slashing" -) diff --git a/x/slashing/legacy/v042/store.go b/x/slashing/legacy/v042/store.go new file mode 100644 index 000000000..7864087b1 --- /dev/null +++ b/x/slashing/legacy/v042/store.go @@ -0,0 +1,20 @@ +package v042 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + v042distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v042" + v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040" +) + +// MigrateStore performs in-place store migrations from v0.40 to v0.42. The +// migration includes: +// +// - Change addresses to be length-prefixed. +func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error { + store := ctx.KVStore(storeKey) + v042distribution.MigratePrefixAddress(store, v040slashing.ValidatorSigningInfoKeyPrefix) + v042distribution.MigratePrefixAddressBytes(store, v040slashing.ValidatorMissedBlockBitArrayKeyPrefix) + v042distribution.MigratePrefixAddress(store, v040slashing.AddrPubkeyRelationKeyPrefix) + + return nil +} diff --git a/x/slashing/legacy/v042/store_test.go b/x/slashing/legacy/v042/store_test.go new file mode 100644 index 000000000..60bf43a27 --- /dev/null +++ b/x/slashing/legacy/v042/store_test.go @@ -0,0 +1,68 @@ +package v042_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040" + v042slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v042" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +func TestStoreMigration(t *testing.T) { + slashingKey := sdk.NewKVStoreKey("slashing") + ctx := testutil.DefaultContext(slashingKey, sdk.NewTransientStoreKey("transient_test")) + store := ctx.KVStore(slashingKey) + + _, _, addr1 := testdata.KeyTestPubAddr() + consAddr := sdk.ConsAddress(addr1) + // Use dummy value for all keys. + value := []byte("foo") + + testCases := []struct { + name string + oldKey []byte + newKey []byte + }{ + { + "ValidatorSigningInfoKey", + v040slashing.ValidatorSigningInfoKey(consAddr), + types.ValidatorSigningInfoKey(consAddr), + }, + { + "ValidatorMissedBlockBitArrayKey", + v040slashing.ValidatorMissedBlockBitArrayKey(consAddr, 2), + types.ValidatorMissedBlockBitArrayKey(consAddr, 2), + }, + { + "AddrPubkeyRelationKey", + v040slashing.AddrPubkeyRelationKey(consAddr), + types.AddrPubkeyRelationKey(consAddr), + }, + } + + // Set all the old keys to the store + for _, tc := range testCases { + store.Set(tc.oldKey, value) + } + + // Run migrations. + err := v042slashing.MigrateStore(ctx, slashingKey) + require.NoError(t, err) + + // Make sure the new keys are set and old keys are deleted. + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + if bytes.Compare(tc.oldKey, tc.newKey) != 0 { + require.Nil(t, store.Get(tc.oldKey)) + } + require.Equal(t, value, store.Get(tc.newKey)) + }) + } +} diff --git a/x/slashing/module.go b/x/slashing/module.go index ba5cfb60a..69e96b49d 100644 --- a/x/slashing/module.go +++ b/x/slashing/module.go @@ -91,8 +91,6 @@ func (AppModuleBasic) GetQueryCmd() *cobra.Command { return cli.GetQueryCmd() } -//____________________________________________________________________________ - // AppModule implements an application module for the slashing module. type AppModule struct { AppModuleBasic @@ -141,6 +139,9 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) types.RegisterQueryServer(cfg.QueryServer(), am.keeper) + + m := keeper.NewMigrator(am.keeper) + cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2) } // InitGenesis performs genesis initialization for the slashing module. It returns @@ -173,8 +174,6 @@ func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.Validato return []abci.ValidatorUpdate{} } -//____________________________________________________________________________ - // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the slashing module. diff --git a/x/slashing/simulation/operations.go b/x/slashing/simulation/operations.go index 1c49775cc..19f106ddb 100644 --- a/x/slashing/simulation/operations.go +++ b/x/slashing/simulation/operations.go @@ -43,7 +43,6 @@ func WeightedOperations( } // SimulateMsgUnjail generates a MsgUnjail with random values -// nolint: interfacer func SimulateMsgUnjail(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, diff --git a/x/slashing/simulation/operations_test.go b/x/slashing/simulation/operations_test.go index 15764c631..a224d460a 100644 --- a/x/slashing/simulation/operations_test.go +++ b/x/slashing/simulation/operations_test.go @@ -123,8 +123,7 @@ func getTestingAccounts(t *testing.T, r *rand.Rand, app *simapp.SimApp, ctx sdk. for _, account := range accounts { acc := app.AccountKeeper.NewAccountWithAddress(ctx, account.Address) app.AccountKeeper.SetAccount(ctx, acc) - err := app.BankKeeper.SetBalances(ctx, account.Address, initCoins) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, account.Address, initCoins)) } return accounts diff --git a/x/slashing/spec/02_state.md b/x/slashing/spec/02_state.md index 3644ff965..8b2755a18 100644 --- a/x/slashing/spec/02_state.md +++ b/x/slashing/spec/02_state.md @@ -27,8 +27,8 @@ number of blocks by being automatically jailed, potentially slashed, and unbonde Information about validator's liveness activity is tracked through `ValidatorSigningInfo`. It is indexed in the store as follows: -- ValidatorSigningInfo: ` 0x01 | ConsAddress -> ProtocolBuffer(ValSigningInfo)` -- MissedBlocksBitArray: ` 0x02 | ConsAddress | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)` (varint is a number encoding format) +- ValidatorSigningInfo: ` 0x01 | ConsAddrLen (1 byte) | ConsAddress -> ProtocolBuffer(ValSigningInfo)` +- MissedBlocksBitArray: ` 0x02 | ConsAddrLen (1 byte) | ConsAddress | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)` (varint is a number encoding format) The first mapping allows us to easily lookup the recent signing info for a validator based on the validator's consensus address. diff --git a/x/slashing/types/events.go b/x/slashing/types/events.go index 62b6493c3..e9fb25454 100644 --- a/x/slashing/types/events.go +++ b/x/slashing/types/events.go @@ -1,4 +1,3 @@ -//noalias package types // Slashing module event types diff --git a/x/slashing/types/expected_keepers.go b/x/slashing/types/expected_keepers.go index 538a030ba..9710ad178 100644 --- a/x/slashing/types/expected_keepers.go +++ b/x/slashing/types/expected_keepers.go @@ -19,7 +19,6 @@ type AccountKeeper interface { type BankKeeper interface { GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin - SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins } diff --git a/x/slashing/types/query.pb.gw.go b/x/slashing/types/query.pb.gw.go index 083fe73aa..95446797b 100644 --- a/x/slashing/types/query.pb.gw.go +++ b/x/slashing/types/query.pb.gw.go @@ -310,11 +310,11 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "slashing", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "slashing", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_SigningInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "slashing", "v1beta1", "signing_infos", "cons_address"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_SigningInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "slashing", "v1beta1", "signing_infos", "cons_address"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_SigningInfos_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "slashing", "v1beta1", "signing_infos"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_SigningInfos_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "slashing", "v1beta1", "signing_infos"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/staking/client/cli/cli_test.go b/x/staking/client/cli/cli_test.go index f1ef9edc6..adaa825f9 100644 --- a/x/staking/client/cli/cli_test.go +++ b/x/staking/client/cli/cli_test.go @@ -58,11 +58,17 @@ func (s *IntegrationTestSuite) SetupSuite() { val2 := s.network.Validators[1] // redelegate - _, err = stakingtestutil.MsgRedelegateExec(val.ClientCtx, val.Address, val.ValAddress, val2.ValAddress, unbond) + _, err = stakingtestutil.MsgRedelegateExec( + val.ClientCtx, + val.Address, + val.ValAddress, + val2.ValAddress, + unbond, + fmt.Sprintf("--%s=%d", flags.FlagGas, 202954), // 202954 is the required + ) s.Require().NoError(err) _, err = s.network.WaitForHeight(1) s.Require().NoError(err) - // unbonding _, err = stakingtestutil.MsgUnbondExec(val.ClientCtx, val.Address, val.ValAddress, unbond) s.Require().NoError(err) @@ -82,7 +88,7 @@ func (s *IntegrationTestSuite) TestNewCreateValidatorCmd() { consPubKey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, consPrivKey.PubKey()) s.Require().NoError(err) - info, _, err := val.ClientCtx.Keyring.NewMnemonic("NewValidator", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := val.ClientCtx.Keyring.NewMnemonic("NewValidator", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) s.Require().NoError(err) newAddr := sdk.AccAddress(info.GetPubKey().Address()) @@ -1037,7 +1043,7 @@ func (s *IntegrationTestSuite) TestNewCmdEditValidator() { func (s *IntegrationTestSuite) TestNewCmdDelegate() { val := s.network.Validators[0] - info, _, err := val.ClientCtx.Keyring.NewMnemonic("NewAccount", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := val.ClientCtx.Keyring.NewMnemonic("NewAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) s.Require().NoError(err) newAddr := sdk.AccAddress(info.GetPubKey().Address()) @@ -1277,7 +1283,7 @@ func (s *IntegrationTestSuite) TestBlockResults() { val := s.network.Validators[0] // Create new account in the keyring. - info, _, err := val.ClientCtx.Keyring.NewMnemonic("NewDelegator", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + info, _, err := val.ClientCtx.Keyring.NewMnemonic("NewDelegator", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(err) newAddr := sdk.AccAddress(info.GetPubKey().Address()) diff --git a/x/staking/client/cli/tx.go b/x/staking/client/cli/tx.go index 10ceba6a4..b7cd500cf 100644 --- a/x/staking/client/cli/tx.go +++ b/x/staking/client/cli/tx.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -59,12 +60,19 @@ func NewCreateValidatorCmd() *cobra.Command { } txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) - txf, msg, err := NewBuildCreateValidatorMsg(clientCtx, txf, cmd.Flags()) + txf, msg, err := newBuildCreateValidatorMsg(clientCtx, txf, cmd.Flags()) if err != nil { return err } - return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.CreateValidator(cmd.Context(), msg) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, svcMsgClientConn.GetMsgs()...) }, } @@ -128,11 +136,14 @@ func NewEditValidatorCmd() *cobra.Command { } msg := types.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate, newMinSelfDelegation) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.EditValidator(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -177,11 +188,14 @@ $ %s tx staking delegate %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 1000stake --f } msg := types.NewMsgDelegate(delAddr, valAddr, amount) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.Delegate(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -228,11 +242,14 @@ $ %s tx staking redelegate %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj %s1l2rsakp3 } msg := types.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, amount) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.BeginRedelegate(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -274,11 +291,14 @@ $ %s tx staking unbond %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from } msg := types.NewMsgUndelegate(delAddr, valAddr, amount) - if err := msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.Undelegate(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -287,7 +307,7 @@ $ %s tx staking unbond %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from return cmd } -func NewBuildCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet) (tx.Factory, sdk.Msg, error) { +func newBuildCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet) (tx.Factory, *types.MsgCreateValidator, error) { fAmount, _ := fs.GetString(FlagAmount) amount, err := sdk.ParseCoinNormalized(fAmount) if err != nil { diff --git a/x/staking/client/rest/grpc_query_test.go b/x/staking/client/rest/grpc_query_test.go index 696a55ff9..69e9f34ad 100644 --- a/x/staking/client/rest/grpc_query_test.go +++ b/x/staking/client/rest/grpc_query_test.go @@ -6,6 +6,8 @@ import ( "fmt" "testing" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/suite" @@ -46,7 +48,15 @@ func (s *IntegrationTestSuite) SetupSuite() { val2 := s.network.Validators[1] // redelegate - _, err = stakingtestutil.MsgRedelegateExec(val.ClientCtx, val.Address, val.ValAddress, val2.ValAddress, unbond) + _, err = stakingtestutil.MsgRedelegateExec( + val.ClientCtx, + val.Address, + val.ValAddress, + val2.ValAddress, + unbond, + fmt.Sprintf("--%s=%d", flags.FlagGas, 254000), + ) // expected gas is 202987 + s.Require().NoError(err) _, err = s.network.WaitForHeight(1) s.Require().NoError(err) @@ -330,7 +340,7 @@ func (s *IntegrationTestSuite) TestQueryDelegationGRPC() { s.Run(tc.name, func() { resp, err := rest.GetRequest(tc.url) s.Require().NoError(err) - + s.T().Logf("%s", resp) err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType) if tc.error { diff --git a/x/staking/client/rest/query.go b/x/staking/client/rest/query.go index d515d5dfe..7fae344d6 100644 --- a/x/staking/client/rest/query.go +++ b/x/staking/client/rest/query.go @@ -151,20 +151,30 @@ func delegatorTxsHandlerFn(clientCtx client.Context) http.HandlerFunc { actions []string ) + // For each case, we search txs for both: + // - legacy messages: their Type() is a custom string, e.g. "delegate" + // - service Msgs: their Type() is their FQ method name, e.g. "/cosmos.staking.v1beta1.Msg/Deledate" + // and we combine the results. switch { case isBondTx: - actions = append(actions, types.MsgDelegate{}.Type()) + actions = append(actions, types.TypeMsgDelegate) + actions = append(actions, types.TypeSvcMsgDelegate) case isUnbondTx: - actions = append(actions, types.MsgUndelegate{}.Type()) + actions = append(actions, types.TypeMsgUndelegate) + actions = append(actions, types.TypeSvcMsgUndelegate) case isRedTx: - actions = append(actions, types.MsgBeginRedelegate{}.Type()) + actions = append(actions, types.TypeMsgBeginRedelegate) + actions = append(actions, types.TypeSvcMsgBeginRedelegate) case noQuery: - actions = append(actions, types.MsgDelegate{}.Type()) - actions = append(actions, types.MsgUndelegate{}.Type()) - actions = append(actions, types.MsgBeginRedelegate{}.Type()) + actions = append(actions, types.TypeMsgDelegate) + actions = append(actions, types.TypeSvcMsgDelegate) + actions = append(actions, types.TypeMsgUndelegate) + actions = append(actions, types.TypeSvcMsgUndelegate) + actions = append(actions, types.TypeMsgBeginRedelegate) + actions = append(actions, types.TypeSvcMsgBeginRedelegate) default: w.WriteHeader(http.StatusNoContent) diff --git a/x/staking/client/testutil/test_helpers.go b/x/staking/client/testutil/test_helpers.go index 1a2a4b84b..ad60b96a4 100644 --- a/x/staking/client/testutil/test_helpers.go +++ b/x/staking/client/testutil/test_helpers.go @@ -27,6 +27,7 @@ func MsgRedelegateExec(clientCtx client.Context, from, src, dst, amount fmt.Stri amount.String(), fmt.Sprintf("--%s=%s", flags.FlagFrom, from.String()), } + args = append(args, extraArgs...) args = append(args, commonArgs...) return clitestutil.ExecTestCLICmd(clientCtx, stakingcli.NewRedelegateCmd(), args) @@ -43,5 +44,6 @@ func MsgUnbondExec(clientCtx client.Context, from fmt.Stringer, valAddress, } args = append(args, commonArgs...) + args = append(args, extraArgs...) return clitestutil.ExecTestCLICmd(clientCtx, stakingcli.NewUnbondCmd(), args) } diff --git a/x/staking/genesis.go b/x/staking/genesis.go index 02f6bc317..96c7be8e4 100644 --- a/x/staking/genesis.go +++ b/x/staking/genesis.go @@ -105,30 +105,28 @@ func InitGenesis( if bondedPool == nil { panic(fmt.Sprintf("%s module account has not been set", types.BondedPoolName)) } - // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 - // add coins if not provided on genesis - if bankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()).IsZero() { - if err := bankKeeper.SetBalances(ctx, bondedPool.GetAddress(), bondedCoins); err != nil { - panic(err) - } - + bondedBalance := bankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + if bondedBalance.IsZero() { accountKeeper.SetModuleAccount(ctx, bondedPool) } - + // if balance is different from bonded coins panic because genesis is most likely malformed + if !bondedBalance.IsEqual(bondedCoins) { + panic(fmt.Sprintf("bonded pool balance is different from bonded coins: %s <-> %s", bondedBalance, bondedCoins)) + } notBondedPool := keeper.GetNotBondedPool(ctx) if notBondedPool == nil { panic(fmt.Sprintf("%s module account has not been set", types.NotBondedPoolName)) } - if bankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()).IsZero() { - if err := bankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), notBondedCoins); err != nil { - panic(err) - } - + notBondedBalance := bankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) + if notBondedBalance.IsZero() { accountKeeper.SetModuleAccount(ctx, notBondedPool) } - + // if balance is different from non bonded coins panic because genesis is most likely malformed + if !notBondedBalance.IsEqual(notBondedCoins) { + panic(fmt.Sprintf("not bonded pool balance is different from not bonded coins: %s <-> %s", notBondedBalance, notBondedCoins)) + } // don't need to run Tendermint updates if we exported if data.Exported { for _, lv := range data.LastValidatorPowers { diff --git a/x/staking/genesis_test.go b/x/staking/genesis_test.go index 28e3dec8a..67f6c7a87 100644 --- a/x/staking/genesis_test.go +++ b/x/staking/genesis_test.go @@ -2,47 +2,39 @@ package staking_test import ( "fmt" + "log" "testing" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" "github.com/cosmos/cosmos-sdk/x/staking/types" ) -func bootstrapGenesisTest(t *testing.T, power int64, numAddrs int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress) { +func bootstrapGenesisTest(numAddrs int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress) { _, app, ctx := getBaseSimappWithCustomKeeper() addrDels, _ := generateAddresses(app, ctx, numAddrs, sdk.NewInt(10000)) - - amt := sdk.TokensFromConsensusPower(power) - totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels))))) - - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), totalSupply) - require.NoError(t, err) - - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - app.BankKeeper.SetSupply(ctx, banktypes.NewSupply(totalSupply)) - return app, ctx, addrDels } func TestInitGenesis(t *testing.T) { - app, ctx, addrs := bootstrapGenesisTest(t, 1000, 10) + app, ctx, addrs := bootstrapGenesisTest(10) valTokens := sdk.TokensFromConsensusPower(1) params := app.StakingKeeper.GetParams(ctx) - validators := make([]types.Validator, 2) + validators := app.StakingKeeper.GetAllValidators(ctx) var delegations []types.Delegation pk0, err := codectypes.NewAnyWithValue(PKs[0]) @@ -52,19 +44,40 @@ func TestInitGenesis(t *testing.T) { require.NoError(t, err) // initialize the validators - validators[0].OperatorAddress = sdk.ValAddress(addrs[0]).String() - validators[0].ConsensusPubkey = pk0 - validators[0].Description = types.NewDescription("hoop", "", "", "", "") - validators[0].Status = types.Bonded - validators[0].Tokens = valTokens - validators[0].DelegatorShares = valTokens.ToDec() - validators[1].OperatorAddress = sdk.ValAddress(addrs[1]).String() - validators[1].ConsensusPubkey = pk1 - validators[1].Description = types.NewDescription("bloop", "", "", "", "") - validators[1].Status = types.Bonded - validators[1].Tokens = valTokens - validators[1].DelegatorShares = valTokens.ToDec() + bondedVal1 := types.Validator{ + OperatorAddress: sdk.ValAddress(addrs[0]).String(), + ConsensusPubkey: pk0, + Status: types.Bonded, + Tokens: valTokens, + DelegatorShares: valTokens.ToDec(), + Description: types.NewDescription("hoop", "", "", "", ""), + } + bondedVal2 := types.Validator{ + OperatorAddress: sdk.ValAddress(addrs[1]).String(), + ConsensusPubkey: pk1, + Status: types.Bonded, + Tokens: valTokens, + DelegatorShares: valTokens.ToDec(), + Description: types.NewDescription("bloop", "", "", "", ""), + } + // append new bonded validators to the list + validators = append(validators, bondedVal1, bondedVal2) + log.Printf("%#v", len(validators)) + // mint coins in the bonded pool representing the validators coins + require.NoError(t, + simapp.FundAccount( + app, + ctx, + auth.NewModuleAddress(types.BondedPoolName), + sdk.NewCoins( + sdk.NewCoin( + params.BondDenom, + valTokens.MulRaw((int64)(len(validators))), + ), + ), + ), + ) genesisState := types.NewGenesisState(params, validators, delegations) vals := staking.InitGenesis(ctx, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, genesisState) @@ -97,16 +110,63 @@ func TestInitGenesis(t *testing.T) { require.Equal(t, abcivals, vals) } +func TestInitGenesis_PoolsBalanceMismatch(t *testing.T) { + app := simapp.Setup(false) + ctx := app.NewContext(false, tmproto.Header{}) + + consPub, err := codectypes.NewAnyWithValue(PKs[0]) + require.NoError(t, err) + + // create mock validator + validator := types.Validator{ + OperatorAddress: sdk.ValAddress("12345678901234567890").String(), + ConsensusPubkey: consPub, + Jailed: false, + Tokens: sdk.NewInt(10), + DelegatorShares: sdk.NewInt(10).ToDec(), + Description: types.NewDescription("bloop", "", "", "", ""), + } + // valid params + params := types.Params{ + UnbondingTime: 10000, + MaxValidators: 1, + MaxEntries: 10, + BondDenom: "stake", + } + + // test + + require.Panics(t, func() { + // setting validator status to bonded so the balance counts towards bonded pool + validator.Status = types.Bonded + staking.InitGenesis(ctx, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, &types.GenesisState{ + Params: params, + Validators: []types.Validator{validator}, + }) + }, "should panic because bonded pool balance is different from bonded pool coins") + + require.Panics(t, func() { + // setting validator status to unbonded so the balance counts towards not bonded pool + validator.Status = types.Unbonded + staking.InitGenesis(ctx, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, &types.GenesisState{ + Params: params, + Validators: []types.Validator{validator}, + }) + }, "should panic because not bonded pool balance is different from not bonded pool coins") +} + func TestInitGenesisLargeValidatorSet(t *testing.T) { size := 200 require.True(t, size > 100) - app, ctx, addrs := bootstrapGenesisTest(t, 1000, 200) + app, ctx, addrs := bootstrapGenesisTest(200) params := app.StakingKeeper.GetParams(ctx) delegations := []types.Delegation{} validators := make([]types.Validator, size) var err error + + bondedPoolAmt := sdk.ZeroInt() for i := range validators { validators[i], err = types.NewValidator(sdk.ValAddress(addrs[i]), PKs[i], types.NewDescription(fmt.Sprintf("#%d", i), "", "", "", "")) @@ -119,9 +179,22 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) { } validators[i].Tokens = tokens validators[i].DelegatorShares = tokens.ToDec() + // add bonded coins + bondedPoolAmt = bondedPoolAmt.Add(tokens) } - genesisState := types.NewGenesisState(params, validators, delegations) + // mint coins in the bonded pool representing the validators coins + require.NoError(t, + simapp.FundAccount( + app, + ctx, + auth.NewModuleAddress(types.BondedPoolName), + sdk.NewCoins( + sdk.NewCoin(params.BondDenom, bondedPoolAmt), + ), + ), + ) + vals := staking.InitGenesis(ctx, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, genesisState) abcivals := make([]abci.ValidatorUpdate, 100) diff --git a/x/staking/handler_test.go b/x/staking/handler_test.go index 4b48f0942..bceef6b3f 100644 --- a/x/staking/handler_test.go +++ b/x/staking/handler_test.go @@ -20,7 +20,6 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" @@ -36,12 +35,10 @@ func bootstrapHandlerGenesisTest(t *testing.T, power int64, numAddrs int, accAmo totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels))))) notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), totalSupply) - require.NoError(t, err) + // set non bonded pool balance app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - app.BankKeeper.SetSupply(ctx, banktypes.NewSupply(totalSupply)) - + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), totalSupply)) return app, ctx, addrDels, addrVals } diff --git a/x/staking/keeper/alias_functions.go b/x/staking/keeper/alias_functions.go index 1d563c690..36c2d2366 100644 --- a/x/staking/keeper/alias_functions.go +++ b/x/staking/keeper/alias_functions.go @@ -7,7 +7,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking/types" ) -//_______________________________________________________________________ // Validator Set // iterate through the validator set and perform the provided function @@ -96,7 +95,6 @@ func (k Keeper) ValidatorByConsAddr(ctx sdk.Context, addr sdk.ConsAddress) types return val } -//_______________________________________________________________________ // Delegation Set // Returns self as it is both a validatorset and delegationset diff --git a/x/staking/keeper/delegation_test.go b/x/staking/keeper/delegation_test.go index 77f4cb8e2..6458c35d3 100644 --- a/x/staking/keeper/delegation_test.go +++ b/x/staking/keeper/delegation_test.go @@ -186,13 +186,7 @@ func TestUnbondDelegation(t *testing.T) { startTokens := sdk.TokensFromConsensusPower(10) notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - require.NoError(t, - app.BankKeeper.SetBalances( - ctx, - notBondedPool.GetAddress(), - sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)), - ), - ) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)))) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // create a validator and a delegator to that validator @@ -233,8 +227,7 @@ func TestUnbondingDelegationsMaxEntries(t *testing.T) { bondDenom := app.StakingKeeper.BondDenom(ctx) notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(bondDenom, startTokens))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(bondDenom, startTokens)))) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // create a validator and a delegator to that validator @@ -272,7 +265,7 @@ func TestUnbondingDelegationsMaxEntries(t *testing.T) { oldNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount // an additional unbond should fail due to max entries - _, err = app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) + _, err := app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) require.Error(t, err) newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount @@ -322,9 +315,7 @@ func TestUndelegateSelfDelegationBelowMinSelfDelegation(t *testing.T) { // add bonded tokens to pool for delegations notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - oldNotBonded := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), oldNotBonded.Add(delCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) @@ -336,9 +327,7 @@ func TestUndelegateSelfDelegationBelowMinSelfDelegation(t *testing.T) { // add bonded tokens to pool for delegations bondedPool := app.StakingKeeper.GetBondedPool(ctx) - oldBonded := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), oldBonded.Add(delCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) // create a second delegation to this validator @@ -348,9 +337,7 @@ func TestUndelegateSelfDelegationBelowMinSelfDelegation(t *testing.T) { require.Equal(t, delTokens, issuedShares.RoundInt()) // add bonded tokens to pool for delegations - oldBonded = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), oldBonded.Add(delCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) @@ -358,7 +345,7 @@ func TestUndelegateSelfDelegationBelowMinSelfDelegation(t *testing.T) { app.StakingKeeper.SetDelegation(ctx, delegation) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - _, err = app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.TokensFromConsensusPower(6).ToDec()) + _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.TokensFromConsensusPower(6).ToDec()) require.NoError(t, err) // end block @@ -388,9 +375,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { // add bonded tokens to pool for delegations notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - oldNotBonded := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), oldNotBonded.Add(delCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) @@ -400,9 +385,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { app.StakingKeeper.SetDelegation(ctx, selfDelegation) bondedPool := app.StakingKeeper.GetBondedPool(ctx) - oldBonded := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), oldBonded.Add(delCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) // create a second delegation to this validator @@ -411,18 +394,14 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { validator, issuedShares = validator.AddTokensFromDel(delTokens) require.Equal(t, delTokens, issuedShares.RoundInt()) - oldBonded = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), oldBonded.Add(delCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) delegation := types.NewDelegation(addrDels[1], addrVals[0], issuedShares) app.StakingKeeper.SetDelegation(ctx, delegation) - oldBonded = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), oldBonded.Add(delCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) header := ctx.BlockHeader() @@ -434,7 +413,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { // unbond the all self-delegation to put validator in unbonding state val0AccAddr := sdk.AccAddress(addrVals[0]) - _, err = app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) + _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) require.NoError(t, err) // end block @@ -474,9 +453,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { // add bonded tokens to pool for delegations notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - oldNotBonded := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), oldNotBonded.Add(delCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // create a validator with a self-delegation @@ -494,9 +471,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { app.StakingKeeper.SetDelegation(ctx, selfDelegation) bondedPool := app.StakingKeeper.GetBondedPool(ctx) - oldBonded := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), oldBonded.Add(delCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) // create a second delegation to this validator @@ -512,7 +487,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err = app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec()) + _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec()) require.NoError(t, err) // end block @@ -558,9 +533,7 @@ func TestUnbondingAllDelegationFromValidator(t *testing.T) { // add bonded tokens to pool for delegations notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - oldNotBonded := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), oldNotBonded.Add(delCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) //create a validator with a self-delegation @@ -584,9 +557,7 @@ func TestUnbondingAllDelegationFromValidator(t *testing.T) { require.Equal(t, delTokens, issuedShares.RoundInt()) bondedPool := app.StakingKeeper.GetBondedPool(ctx) - oldBonded := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), oldBonded.Add(delCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) @@ -599,7 +570,7 @@ func TestUnbondingAllDelegationFromValidator(t *testing.T) { ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err = app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec()) + _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec()) require.NoError(t, err) // end block @@ -725,9 +696,7 @@ func TestRedelegateToSameValidator(t *testing.T) { // add bonded tokens to pool for delegations notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - oldNotBonded := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), oldNotBonded.Add(startCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), startCoins)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // create a validator with a self-delegation @@ -741,7 +710,7 @@ func TestRedelegateToSameValidator(t *testing.T) { selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) app.StakingKeeper.SetDelegation(ctx, selfDelegation) - _, err = app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[0], sdk.NewDec(5)) + _, err := app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[0], sdk.NewDec(5)) require.Error(t, err) } @@ -756,9 +725,7 @@ func TestRedelegationMaxEntries(t *testing.T) { // add bonded tokens to pool for delegations notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - oldNotBonded := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), oldNotBonded.Add(startCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), startCoins)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // create a validator with a self-delegation @@ -790,7 +757,7 @@ func TestRedelegationMaxEntries(t *testing.T) { } // an additional redelegation should fail due to max entries - _, err = app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) + _, err := app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) require.Error(t, err) // mature redelegations @@ -814,9 +781,7 @@ func TestRedelegateSelfDelegation(t *testing.T) { // add bonded tokens to pool for delegations notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - oldNotBonded := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), oldNotBonded.Add(startCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), startCoins)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) //create a validator with a self-delegation @@ -849,7 +814,7 @@ func TestRedelegateSelfDelegation(t *testing.T) { delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) app.StakingKeeper.SetDelegation(ctx, delegation) - _, err = app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], delTokens.ToDec()) + _, err := app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], delTokens.ToDec()) require.NoError(t, err) // end block @@ -872,9 +837,7 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { // add bonded tokens to pool for delegations notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - oldNotBonded := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), oldNotBonded.Add(startCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), startCoins)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) //create a validator with a self-delegation @@ -912,7 +875,7 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { ctx = ctx.WithBlockHeader(header) // unbond the all self-delegation to put validator in unbonding state - _, err = app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) + _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) require.NoError(t, err) // end block @@ -956,9 +919,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) { // add bonded tokens to pool for delegations notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - oldNotBonded := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), oldNotBonded.Add(startCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), startCoins)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) //create a validator with a self-delegation @@ -993,7 +954,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) { ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err = app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) + _, err := app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) require.NoError(t, err) // end block diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go index 74d85a645..81613d92b 100644 --- a/x/staking/keeper/keeper.go +++ b/x/staking/keeper/keeper.go @@ -12,8 +12,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking/types" ) -const aminoCacheSize = 500 - // Implements ValidatorSet interface var _ types.ValidatorSet = Keeper{} @@ -28,7 +26,6 @@ type Keeper struct { bankKeeper types.BankKeeper hooks types.StakingHooks paramstore paramtypes.Subspace - validatorCache map[string]cachedValidator validatorCacheList *list.List } @@ -58,7 +55,6 @@ func NewKeeper( bankKeeper: bk, paramstore: ps, hooks: nil, - validatorCache: make(map[string]cachedValidator, aminoCacheSize), validatorCacheList: list.New(), } } diff --git a/x/staking/keeper/migrations.go b/x/staking/keeper/migrations.go new file mode 100644 index 000000000..948f93ed6 --- /dev/null +++ b/x/staking/keeper/migrations.go @@ -0,0 +1,21 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + v042 "github.com/cosmos/cosmos-sdk/x/staking/legacy/v042" +) + +// Migrator is a struct for handling in-place store migrations. +type Migrator struct { + keeper Keeper +} + +// NewMigrator returns a new Migrator. +func NewMigrator(keeper Keeper) Migrator { + return Migrator{keeper: keeper} +} + +// Migrate1to2 migrates from version 1 to 2. +func (m Migrator) Migrate1to2(ctx sdk.Context) error { + return v042.MigrateStore(ctx, m.keeper.storeKey) +} diff --git a/x/staking/keeper/querier.go b/x/staking/keeper/querier.go index 48272e245..06bab7704 100644 --- a/x/staking/keeper/querier.go +++ b/x/staking/keeper/querier.go @@ -444,7 +444,6 @@ func queryParameters(ctx sdk.Context, k Keeper, legacyQuerierCdc *codec.LegacyAm return res, nil } -//______________________________________________________ // util func DelegationToDelegationResponse(ctx sdk.Context, k Keeper, del types.Delegation) (types.DelegationResponse, error) { diff --git a/x/staking/keeper/query_utils.go b/x/staking/keeper/query_utils.go index a6f323092..d757522f6 100644 --- a/x/staking/keeper/query_utils.go +++ b/x/staking/keeper/query_utils.go @@ -50,8 +50,6 @@ func (k Keeper) GetDelegatorValidator( return validator, nil } -//_____________________________________________________________________________________ - // return all delegations for a delegator func (k Keeper) GetAllDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress) []types.Delegation { delegations := make([]types.Delegation, 0) @@ -59,7 +57,7 @@ func (k Keeper) GetAllDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAdd store := ctx.KVStore(k.storeKey) delegatorPrefixKey := types.GetDelegationsKey(delegator) - iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) // smallest to largest defer iterator.Close() i := 0 diff --git a/x/staking/keeper/slash_test.go b/x/staking/keeper/slash_test.go index 356c022e4..3f72e857f 100644 --- a/x/staking/keeper/slash_test.go +++ b/x/staking/keeper/slash_test.go @@ -10,7 +10,6 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -26,8 +25,7 @@ func bootstrapSlashTest(t *testing.T, power int64) (*simapp.SimApp, sdk.Context, totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels))))) notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), totalSupply) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), totalSupply)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) @@ -35,11 +33,9 @@ func bootstrapSlashTest(t *testing.T, power int64) (*simapp.SimApp, sdk.Context, bondedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(numVals))) bondedPool := app.StakingKeeper.GetBondedPool(ctx) - err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), bondedCoins) - require.NoError(t, err) - + // set bonded pool balance app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - app.BankKeeper.SetSupply(ctx, banktypes.NewSupply(totalSupply)) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), bondedCoins)) for i := int64(0); i < numVals; i++ { validator := teststaking.NewValidator(t, addrVals[i], PKs[i]) @@ -129,7 +125,7 @@ func TestSlashRedelegation(t *testing.T) { bondedPool := app.StakingKeeper.GetBondedPool(ctx) balances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - require.NoError(t, app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), balances.Add(startCoins...))) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), startCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) // set a redelegation with an expiration timestamp beyond which the @@ -385,7 +381,6 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { require.Equal(t, validator.GetStatus(), types.Unbonding) } -//_________________________________________________________________________________ // tests Slash at a previous height with a redelegation func TestSlashWithRedelegation(t *testing.T) { app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) @@ -408,10 +403,7 @@ func TestSlashWithRedelegation(t *testing.T) { notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) rdCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdTokens.MulRaw(2))) - balances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - - err := app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), balances.Add(rdCoins...)) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), rdCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) @@ -574,11 +566,9 @@ func TestSlashBoth(t *testing.T) { bondedPool := app.StakingKeeper.GetBondedPool(ctx) notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - bondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - require.NoError(t, app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), bondedPoolBalances.Add(bondedCoins...))) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), bondedCoins)) - notBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - require.NoError(t, app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), notBondedPoolBalances.Add(notBondedCoins...))) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), notBondedCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) diff --git a/x/staking/keeper/validator.go b/x/staking/keeper/validator.go index 9fa6a0ca0..46585c4ff 100644 --- a/x/staking/keeper/validator.go +++ b/x/staking/keeper/validator.go @@ -10,22 +10,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// Cache the amino decoding of validators, as it can be the case that repeated slashing calls -// cause many calls to GetValidator, which were shown to throttle the state machine in our -// simulation. Note this is quite biased though, as the simulator does more slashes than a -// live chain should, however we require the slashing to be fast as noone pays gas for it. -type cachedValidator struct { - val types.Validator - marshalled string // marshalled amino bytes for the validator object (not operator address) -} - -func newCachedValidator(val types.Validator, marshalled string) cachedValidator { - return cachedValidator{ - val: val, - marshalled: marshalled, - } -} - // get a single validator func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator types.Validator, found bool) { store := ctx.KVStore(k.storeKey) @@ -35,30 +19,7 @@ func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator ty return validator, false } - // If these amino encoded bytes are in the cache, return the cached validator - strValue := string(value) - if val, ok := k.validatorCache[strValue]; ok { - valToReturn := val.val - // Doesn't mutate the cache's value - valToReturn.OperatorAddress = addr.String() - - return valToReturn, true - } - - // amino bytes weren't found in cache, so amino unmarshal and add it to the cache validator = types.MustUnmarshalValidator(k.cdc, value) - cachedVal := newCachedValidator(validator, strValue) - k.validatorCache[strValue] = newCachedValidator(validator, strValue) - k.validatorCacheList.PushBack(cachedVal) - - // if the cache is too big, pop off the last element from it - if k.validatorCacheList.Len() > aminoCacheSize { - valToRemove := k.validatorCacheList.Remove(k.validatorCacheList.Front()).(cachedValidator) - delete(k.validatorCache, valToRemove.marshalled) - } - - validator = types.MustUnmarshalValidator(k.cdc, value) - return validator, true } @@ -279,7 +240,6 @@ func (k Keeper) ValidatorsPowerStoreIterator(ctx sdk.Context) sdk.Iterator { return sdk.KVStoreReversePrefixIterator(store, types.ValidatorsByPowerIndexKey) } -//_______________________________________________________________________ // Last Validator Index // Load the last validator power. diff --git a/x/staking/keeper/validator_bench_test.go b/x/staking/keeper/validator_bench_test.go new file mode 100644 index 000000000..54a616c90 --- /dev/null +++ b/x/staking/keeper/validator_bench_test.go @@ -0,0 +1,29 @@ +package keeper_test + +import "testing" + +func BenchmarkGetValidator(b *testing.B) { + // 900 is the max number we are allowed to use in order to avoid simapp.CreateTestPubKeys + // panic: encoding/hex: odd length hex string + var powersNumber = 900 + + var totalPower int64 = 0 + var powers = make([]int64, powersNumber) + for i := range powers { + powers[i] = int64(i) + totalPower += int64(i) + } + + app, ctx, _, valAddrs, vals := initValidators(b, totalPower, len(powers), powers) + + for _, validator := range vals { + app.StakingKeeper.SetValidator(ctx, validator) + } + + b.ResetTimer() + for n := 0; n < b.N; n++ { + for _, addr := range valAddrs { + _, _ = app.StakingKeeper.GetValidator(ctx, addr) + } + } +} diff --git a/x/staking/keeper/validator_test.go b/x/staking/keeper/validator_test.go index ae98c177e..3a1842397 100644 --- a/x/staking/keeper/validator_test.go +++ b/x/staking/keeper/validator_test.go @@ -13,19 +13,18 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" "github.com/cosmos/cosmos-sdk/x/staking/types" ) -func newMonikerValidator(t *testing.T, operator sdk.ValAddress, pubKey cryptotypes.PubKey, moniker string) types.Validator { +func newMonikerValidator(t testing.TB, operator sdk.ValAddress, pubKey cryptotypes.PubKey, moniker string) types.Validator { v, err := types.NewValidator(operator, pubKey, types.Description{Moniker: moniker}) require.NoError(t, err) return v } -func bootstrapValidatorTest(t *testing.T, power int64, numAddrs int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) { +func bootstrapValidatorTest(t testing.TB, power int64, numAddrs int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) { _, app, ctx := createTestInput() addrDels, addrVals := generateAddresses(app, ctx, numAddrs) @@ -34,21 +33,21 @@ func bootstrapValidatorTest(t *testing.T, power int64, numAddrs int) (*simapp.Si totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels))))) notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - err := app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), totalSupply) - require.NoError(t, err) - + // set bonded pool supply app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - app.BankKeeper.SetSupply(ctx, banktypes.NewSupply(totalSupply)) + + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), totalSupply)) return app, ctx, addrDels, addrVals } -func initValidators(t *testing.T, power int64, numAddrs int, powers []int64) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress, []types.Validator) { - app, ctx, addrs, valAddrs := bootstrapValidatorTest(t, 1000, 20) +func initValidators(t testing.TB, power int64, numAddrs int, powers []int64) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress, []types.Validator) { + app, ctx, addrs, valAddrs := bootstrapValidatorTest(t, power, numAddrs) + pks := simapp.CreateTestPubKeys(numAddrs) vs := make([]types.Validator, len(powers)) for i, power := range powers { - vs[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) + vs[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), pks[i]) tokens := sdk.TokensFromConsensusPower(power) vs[i], _ = vs[i].AddTokensFromDel(tokens) } @@ -114,11 +113,9 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) { bondedPool := app.StakingKeeper.GetBondedPool(ctx) notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - err := app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(1234)))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(1234))))) - err = app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(10000)))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(10000))))) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) @@ -166,11 +163,9 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { app.StakingKeeper.SetParams(ctx, params) // create a random pool - err := app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(1234)))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(1234))))) - err = app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(10000)))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(10000))))) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) @@ -224,8 +219,7 @@ func TestSlashToZeroPowerRemoved(t *testing.T) { bondedPool := app.StakingKeeper.GetBondedPool(ctx) - err := app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), valTokens))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), valTokens)))) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) @@ -430,11 +424,9 @@ func TestGetValidatorSortingMixed(t *testing.T) { bondedPool := app.StakingKeeper.GetBondedPool(ctx) notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - err := app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(501)))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(501))))) - err = app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(0)))) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(0))))) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) @@ -508,9 +500,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) { validators[i], _ = validators[i].AddTokensFromDel(tokens) notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - balances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - require.NoError(t, app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), balances.Add(sdk.NewCoin(params.BondDenom, tokens)))) - + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, tokens)))) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) validators[i] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) } @@ -528,9 +518,8 @@ func TestGetValidatorsEdgeCases(t *testing.T) { notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) newTokens := sdk.NewCoins() - balances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - require.NoError(t, app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), balances.Add(newTokens...))) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), newTokens)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // test that the two largest validators are @@ -562,8 +551,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) { notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) newTokens = sdk.NewCoins(sdk.NewCoin(params.BondDenom, sdk.TokensFromConsensusPower(1))) - balances = app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - require.NoError(t, app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), balances.Add(newTokens...))) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), newTokens)) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) @@ -578,8 +566,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) { validators[3], _ = validators[3].RemoveDelShares(sdk.NewDec(201)) bondedPool := app.StakingKeeper.GetBondedPool(ctx) - balances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) - require.NoError(t, app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), balances.Add(sdk.NewCoin(params.BondDenom, rmTokens)))) + require.NoError(t, simapp.FundAccount(app, ctx, bondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, rmTokens)))) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) @@ -593,8 +580,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) { validators[3], _ = validators[3].AddTokensFromDel(sdk.NewInt(200)) notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) - balances = app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) - require.NoError(t, app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), balances.Add(sdk.NewCoin(params.BondDenom, sdk.NewInt(200))))) + require.NoError(t, simapp.FundAccount(app, ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, sdk.NewInt(200))))) app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) diff --git a/x/staking/legacy/v034/types.go b/x/staking/legacy/v034/types.go index 9f8622d4b..539e6534b 100644 --- a/x/staking/legacy/v034/types.go +++ b/x/staking/legacy/v034/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v034 import ( diff --git a/x/staking/legacy/v036/migrate.go b/x/staking/legacy/v036/migrate.go index cc4d7a408..bb1f78bc4 100644 --- a/x/staking/legacy/v036/migrate.go +++ b/x/staking/legacy/v036/migrate.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v036 import ( diff --git a/x/staking/legacy/v036/types.go b/x/staking/legacy/v036/types.go index b433b0070..44cf3746d 100644 --- a/x/staking/legacy/v036/types.go +++ b/x/staking/legacy/v036/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v036 import ( diff --git a/x/staking/legacy/v038/migrate.go b/x/staking/legacy/v038/migrate.go index 21029881a..d2f65edad 100644 --- a/x/staking/legacy/v038/migrate.go +++ b/x/staking/legacy/v038/migrate.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v038 import ( diff --git a/x/staking/legacy/v038/types.go b/x/staking/legacy/v038/types.go index 58ffa1351..e11157d0e 100644 --- a/x/staking/legacy/v038/types.go +++ b/x/staking/legacy/v038/types.go @@ -1,5 +1,4 @@ // DONTCOVER -// nolint package v038 import ( diff --git a/x/staking/legacy/v040/keys.go b/x/staking/legacy/v040/keys.go new file mode 100644 index 000000000..8c074a1ed --- /dev/null +++ b/x/staking/legacy/v040/keys.go @@ -0,0 +1,330 @@ +// Package v040 is copy-pasted from: +// https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/staking/types/keys.go +package v040 + +import ( + "bytes" + "encoding/binary" + "fmt" + "strconv" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +const ( + // ModuleName is the name of the staking module + ModuleName = "staking" + + // StoreKey is the string store representation + StoreKey = ModuleName + + // QuerierRoute is the querier route for the staking module + QuerierRoute = ModuleName + + // RouterKey is the msg router key for the staking module + RouterKey = ModuleName +) + +var ( + // Keys for store prefixes + // Last* values are constant during a block. + LastValidatorPowerKey = []byte{0x11} // prefix for each key to a validator index, for bonded validators + LastTotalPowerKey = []byte{0x12} // prefix for the total power + + ValidatorsKey = []byte{0x21} // prefix for each key to a validator + ValidatorsByConsAddrKey = []byte{0x22} // prefix for each key to a validator index, by pubkey + ValidatorsByPowerIndexKey = []byte{0x23} // prefix for each key to a validator index, sorted by power + + DelegationKey = []byte{0x31} // key for a delegation + UnbondingDelegationKey = []byte{0x32} // key for an unbonding-delegation + UnbondingDelegationByValIndexKey = []byte{0x33} // prefix for each key for an unbonding-delegation, by validator operator + RedelegationKey = []byte{0x34} // key for a redelegation + RedelegationByValSrcIndexKey = []byte{0x35} // prefix for each key for an redelegation, by source validator operator + RedelegationByValDstIndexKey = []byte{0x36} // prefix for each key for an redelegation, by destination validator operator + + UnbondingQueueKey = []byte{0x41} // prefix for the timestamps in unbonding queue + RedelegationQueueKey = []byte{0x42} // prefix for the timestamps in redelegations queue + ValidatorQueueKey = []byte{0x43} // prefix for the timestamps in validator queue + + HistoricalInfoKey = []byte{0x50} // prefix for the historical info +) + +// gets the key for the validator with address +// VALUE: staking/Validator +func GetValidatorKey(operatorAddr sdk.ValAddress) []byte { + return append(ValidatorsKey, operatorAddr.Bytes()...) +} + +// gets the key for the validator with pubkey +// VALUE: validator operator address ([]byte) +func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte { + return append(ValidatorsByConsAddrKey, addr.Bytes()...) +} + +// Get the validator operator address from LastValidatorPowerKey +func AddressFromLastValidatorPowerKey(key []byte) []byte { + return key[1:] // remove prefix bytes +} + +// get the validator by power index. +// Power index is the key used in the power-store, and represents the relative +// power ranking of the validator. +// VALUE: validator operator address ([]byte) +func GetValidatorsByPowerIndexKey(validator types.Validator) []byte { + // NOTE the address doesn't need to be stored because counter bytes must always be different + // NOTE the larger values are of higher value + + consensusPower := sdk.TokensToConsensusPower(validator.Tokens) + consensusPowerBytes := make([]byte, 8) + binary.BigEndian.PutUint64(consensusPowerBytes, uint64(consensusPower)) + + powerBytes := consensusPowerBytes + powerBytesLen := len(powerBytes) // 8 + + // key is of format prefix || powerbytes || addrBytes + key := make([]byte, 1+powerBytesLen+v040auth.AddrLen) + + key[0] = ValidatorsByPowerIndexKey[0] + copy(key[1:powerBytesLen+1], powerBytes) + addr, err := sdk.ValAddressFromBech32(validator.OperatorAddress) + if err != nil { + panic(err) + } + operAddrInvr := sdk.CopyBytes(addr) + + for i, b := range operAddrInvr { + operAddrInvr[i] = ^b + } + + copy(key[powerBytesLen+1:], operAddrInvr) + + return key +} + +// get the bonded validator index key for an operator address +func GetLastValidatorPowerKey(operator sdk.ValAddress) []byte { + return append(LastValidatorPowerKey, operator...) +} + +// parse the validators operator address from power rank key +func ParseValidatorPowerRankKey(key []byte) (operAddr []byte) { + powerBytesLen := 8 + if len(key) != 1+powerBytesLen+v040auth.AddrLen { + panic("Invalid validator power rank key length") + } + + operAddr = sdk.CopyBytes(key[powerBytesLen+1:]) + + for i, b := range operAddr { + operAddr[i] = ^b + } + + return operAddr +} + +// GetValidatorQueueKey returns the prefix key used for getting a set of unbonding +// validators whose unbonding completion occurs at the given time and height. +func GetValidatorQueueKey(timestamp time.Time, height int64) []byte { + heightBz := sdk.Uint64ToBigEndian(uint64(height)) + timeBz := sdk.FormatTimeBytes(timestamp) + timeBzL := len(timeBz) + prefixL := len(ValidatorQueueKey) + + bz := make([]byte, prefixL+8+timeBzL+8) + + // copy the prefix + copy(bz[:prefixL], ValidatorQueueKey) + + // copy the encoded time bytes length + copy(bz[prefixL:prefixL+8], sdk.Uint64ToBigEndian(uint64(timeBzL))) + + // copy the encoded time bytes + copy(bz[prefixL+8:prefixL+8+timeBzL], timeBz) + + // copy the encoded height + copy(bz[prefixL+8+timeBzL:], heightBz) + + return bz +} + +// ParseValidatorQueueKey returns the encoded time and height from a key created +// from GetValidatorQueueKey. +func ParseValidatorQueueKey(bz []byte) (time.Time, int64, error) { + prefixL := len(ValidatorQueueKey) + if prefix := bz[:prefixL]; !bytes.Equal(prefix, ValidatorQueueKey) { + return time.Time{}, 0, fmt.Errorf("invalid prefix; expected: %X, got: %X", ValidatorQueueKey, prefix) + } + + timeBzL := sdk.BigEndianToUint64(bz[prefixL : prefixL+8]) + ts, err := sdk.ParseTimeBytes(bz[prefixL+8 : prefixL+8+int(timeBzL)]) + if err != nil { + return time.Time{}, 0, err + } + + height := sdk.BigEndianToUint64(bz[prefixL+8+int(timeBzL):]) + + return ts, int64(height), nil +} + +// gets the key for delegator bond with validator +// VALUE: staking/Delegation +func GetDelegationKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { + return append(GetDelegationsKey(delAddr), valAddr.Bytes()...) +} + +// gets the prefix for a delegator for all validators +func GetDelegationsKey(delAddr sdk.AccAddress) []byte { + return append(DelegationKey, delAddr.Bytes()...) +} + +// gets the key for an unbonding delegation by delegator and validator addr +// VALUE: staking/UnbondingDelegation +func GetUBDKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { + return append( + GetUBDsKey(delAddr.Bytes()), + valAddr.Bytes()...) +} + +// gets the index-key for an unbonding delegation, stored by validator-index +// VALUE: none (key rearrangement used) +func GetUBDByValIndexKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { + return append(GetUBDsByValIndexKey(valAddr), delAddr.Bytes()...) +} + +// rearranges the ValIndexKey to get the UBDKey +func GetUBDKeyFromValIndexKey(indexKey []byte) []byte { + addrs := indexKey[1:] // remove prefix bytes + if len(addrs) != 2*v040auth.AddrLen { + panic("unexpected key length") + } + + valAddr := addrs[:v040auth.AddrLen] + delAddr := addrs[v040auth.AddrLen:] + + return GetUBDKey(delAddr, valAddr) +} + +// gets the prefix for all unbonding delegations from a delegator +func GetUBDsKey(delAddr sdk.AccAddress) []byte { + return append(UnbondingDelegationKey, delAddr.Bytes()...) +} + +// gets the prefix keyspace for the indexes of unbonding delegations for a validator +func GetUBDsByValIndexKey(valAddr sdk.ValAddress) []byte { + return append(UnbondingDelegationByValIndexKey, valAddr.Bytes()...) +} + +// gets the prefix for all unbonding delegations from a delegator +func GetUnbondingDelegationTimeKey(timestamp time.Time) []byte { + bz := sdk.FormatTimeBytes(timestamp) + return append(UnbondingQueueKey, bz...) +} + +// GetREDKey returns a key prefix for indexing a redelegation from a delegator +// and source validator to a destination validator. +func GetREDKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte { + key := make([]byte, 1+v040auth.AddrLen*3) + + copy(key[0:v040auth.AddrLen+1], GetREDsKey(delAddr.Bytes())) + copy(key[v040auth.AddrLen+1:2*v040auth.AddrLen+1], valSrcAddr.Bytes()) + copy(key[2*v040auth.AddrLen+1:3*v040auth.AddrLen+1], valDstAddr.Bytes()) + + return key +} + +// gets the index-key for a redelegation, stored by source-validator-index +// VALUE: none (key rearrangement used) +func GetREDByValSrcIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte { + REDSFromValsSrcKey := GetREDsFromValSrcIndexKey(valSrcAddr) + offset := len(REDSFromValsSrcKey) + + // key is of the form REDSFromValsSrcKey || delAddr || valDstAddr + key := make([]byte, len(REDSFromValsSrcKey)+2*v040auth.AddrLen) + copy(key[0:offset], REDSFromValsSrcKey) + copy(key[offset:offset+v040auth.AddrLen], delAddr.Bytes()) + copy(key[offset+v040auth.AddrLen:offset+2*v040auth.AddrLen], valDstAddr.Bytes()) + + return key +} + +// gets the index-key for a redelegation, stored by destination-validator-index +// VALUE: none (key rearrangement used) +func GetREDByValDstIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte { + REDSToValsDstKey := GetREDsToValDstIndexKey(valDstAddr) + offset := len(REDSToValsDstKey) + + // key is of the form REDSToValsDstKey || delAddr || valSrcAddr + key := make([]byte, len(REDSToValsDstKey)+2*v040auth.AddrLen) + copy(key[0:offset], REDSToValsDstKey) + copy(key[offset:offset+v040auth.AddrLen], delAddr.Bytes()) + copy(key[offset+v040auth.AddrLen:offset+2*v040auth.AddrLen], valSrcAddr.Bytes()) + + return key +} + +// GetREDKeyFromValSrcIndexKey rearranges the ValSrcIndexKey to get the REDKey +func GetREDKeyFromValSrcIndexKey(indexKey []byte) []byte { + // note that first byte is prefix byte + if len(indexKey) != 3*v040auth.AddrLen+1 { + panic("unexpected key length") + } + + valSrcAddr := indexKey[1 : v040auth.AddrLen+1] + delAddr := indexKey[v040auth.AddrLen+1 : 2*v040auth.AddrLen+1] + valDstAddr := indexKey[2*v040auth.AddrLen+1 : 3*v040auth.AddrLen+1] + + return GetREDKey(delAddr, valSrcAddr, valDstAddr) +} + +// GetREDKeyFromValDstIndexKey rearranges the ValDstIndexKey to get the REDKey +func GetREDKeyFromValDstIndexKey(indexKey []byte) []byte { + // note that first byte is prefix byte + if len(indexKey) != 3*v040auth.AddrLen+1 { + panic("unexpected key length") + } + + valDstAddr := indexKey[1 : v040auth.AddrLen+1] + delAddr := indexKey[v040auth.AddrLen+1 : 2*v040auth.AddrLen+1] + valSrcAddr := indexKey[2*v040auth.AddrLen+1 : 3*v040auth.AddrLen+1] + + return GetREDKey(delAddr, valSrcAddr, valDstAddr) +} + +// GetRedelegationTimeKey returns a key prefix for indexing an unbonding +// redelegation based on a completion time. +func GetRedelegationTimeKey(timestamp time.Time) []byte { + bz := sdk.FormatTimeBytes(timestamp) + return append(RedelegationQueueKey, bz...) +} + +// GetREDsKey returns a key prefix for indexing a redelegation from a delegator +// address. +func GetREDsKey(delAddr sdk.AccAddress) []byte { + return append(RedelegationKey, delAddr.Bytes()...) +} + +// GetREDsFromValSrcIndexKey returns a key prefix for indexing a redelegation to +// a source validator. +func GetREDsFromValSrcIndexKey(valSrcAddr sdk.ValAddress) []byte { + return append(RedelegationByValSrcIndexKey, valSrcAddr.Bytes()...) +} + +// GetREDsToValDstIndexKey returns a key prefix for indexing a redelegation to a +// destination (target) validator. +func GetREDsToValDstIndexKey(valDstAddr sdk.ValAddress) []byte { + return append(RedelegationByValDstIndexKey, valDstAddr.Bytes()...) +} + +// GetREDsByDelToValDstIndexKey returns a key prefix for indexing a redelegation +// from an address to a source validator. +func GetREDsByDelToValDstIndexKey(delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) []byte { + return append(GetREDsToValDstIndexKey(valDstAddr), delAddr.Bytes()...) +} + +// GetHistoricalInfoKey returns a key prefix for indexing HistoricalInfo objects. +func GetHistoricalInfoKey(height int64) []byte { + return append(HistoricalInfoKey, []byte(strconv.FormatInt(height, 10))...) +} diff --git a/x/staking/legacy/v040/types.go b/x/staking/legacy/v040/types.go deleted file mode 100644 index 6ad280e27..000000000 --- a/x/staking/legacy/v040/types.go +++ /dev/null @@ -1,5 +0,0 @@ -package v040 - -const ( - ModuleName = "staking" -) diff --git a/x/staking/legacy/v042/store.go b/x/staking/legacy/v042/store.go new file mode 100644 index 000000000..2be604e66 --- /dev/null +++ b/x/staking/legacy/v042/store.go @@ -0,0 +1,78 @@ +package v042 + +import ( + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" + v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" + v042distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v042" + v040staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v040" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// migratePrefixAddressAddressAddress is a helper function that migrates all keys of format: +// prefix_bytes | address_1_bytes | address_2_bytes | address_3_bytes +// into format: +// prefix_bytes | address_1_len (1 byte) | address_1_bytes | address_2_len (1 byte) | address_2_bytes | address_3_len (1 byte) | address_3_bytes +func migratePrefixAddressAddressAddress(store sdk.KVStore, prefixBz []byte) { + oldStore := prefix.NewStore(store, prefixBz) + + oldStoreIter := oldStore.Iterator(nil, nil) + defer oldStoreIter.Close() + + for ; oldStoreIter.Valid(); oldStoreIter.Next() { + addr1 := oldStoreIter.Key()[:v040auth.AddrLen] + addr2 := oldStoreIter.Key()[v040auth.AddrLen : 2*v040auth.AddrLen] + addr3 := oldStoreIter.Key()[2*v040auth.AddrLen:] + newStoreKey := append(append(append( + prefixBz, + address.MustLengthPrefix(addr1)...), address.MustLengthPrefix(addr2)...), address.MustLengthPrefix(addr3)..., + ) + + // Set new key on store. Values don't change. + store.Set(newStoreKey, oldStoreIter.Value()) + oldStore.Delete(oldStoreIter.Key()) + } +} + +const powerBytesLen = 8 + +func migrateValidatorsByPowerIndexKey(store sdk.KVStore) { + oldStore := prefix.NewStore(store, v040staking.ValidatorsByPowerIndexKey) + + oldStoreIter := oldStore.Iterator(nil, nil) + defer oldStoreIter.Close() + + for ; oldStoreIter.Valid(); oldStoreIter.Next() { + powerBytes := oldStoreIter.Key()[:powerBytesLen] + valAddr := oldStoreIter.Key()[powerBytesLen:] + newStoreKey := append(append(types.ValidatorsByPowerIndexKey, powerBytes...), address.MustLengthPrefix(valAddr)...) + + // Set new key on store. Values don't change. + store.Set(newStoreKey, oldStoreIter.Value()) + oldStore.Delete(oldStoreIter.Key()) + } +} + +// MigrateStore performs in-place store migrations from v0.40 to v0.42. The +// migration includes: +// +// - Change addresses to be length-prefixed. +func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error { + store := ctx.KVStore(storeKey) + + v042distribution.MigratePrefixAddress(store, v040staking.LastValidatorPowerKey) + + v042distribution.MigratePrefixAddress(store, v040staking.ValidatorsKey) + v042distribution.MigratePrefixAddress(store, v040staking.ValidatorsByConsAddrKey) + migrateValidatorsByPowerIndexKey(store) + + v042distribution.MigratePrefixAddressAddress(store, v040staking.DelegationKey) + v042distribution.MigratePrefixAddressAddress(store, v040staking.UnbondingDelegationKey) + v042distribution.MigratePrefixAddressAddress(store, v040staking.UnbondingDelegationByValIndexKey) + migratePrefixAddressAddressAddress(store, v040staking.RedelegationKey) + migratePrefixAddressAddressAddress(store, v040staking.RedelegationByValSrcIndexKey) + migratePrefixAddressAddressAddress(store, v040staking.RedelegationByValDstIndexKey) + + return nil +} diff --git a/x/staking/legacy/v042/store_test.go b/x/staking/legacy/v042/store_test.go new file mode 100644 index 000000000..963ef02ed --- /dev/null +++ b/x/staking/legacy/v042/store_test.go @@ -0,0 +1,137 @@ +package v042_test + +import ( + "bytes" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + v040staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v040" + v042staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v042" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func TestStoreMigration(t *testing.T) { + stakingKey := sdk.NewKVStoreKey("staking") + ctx := testutil.DefaultContext(stakingKey, sdk.NewTransientStoreKey("transient_test")) + store := ctx.KVStore(stakingKey) + + _, pk1, addr1 := testdata.KeyTestPubAddr() + valAddr1 := sdk.ValAddress(addr1) + val := teststaking.NewValidator(t, valAddr1, pk1) + _, pk1, addr2 := testdata.KeyTestPubAddr() + valAddr2 := sdk.ValAddress(addr2) + _, _, addr3 := testdata.KeyTestPubAddr() + consAddr := sdk.ConsAddress(addr3.String()) + _, _, addr4 := testdata.KeyTestPubAddr() + now := time.Now() + // Use dummy value for all keys. + value := []byte("foo") + + testCases := []struct { + name string + oldKey []byte + newKey []byte + }{ + { + "LastValidatorPowerKey", + v040staking.GetLastValidatorPowerKey(valAddr1), + types.GetLastValidatorPowerKey(valAddr1), + }, + { + "LastTotalPowerKey", + v040staking.LastTotalPowerKey, + types.LastTotalPowerKey, + }, + { + "ValidatorsKey", + v040staking.GetValidatorKey(valAddr1), + types.GetValidatorKey(valAddr1), + }, + { + "ValidatorsByConsAddrKey", + v040staking.GetValidatorByConsAddrKey(consAddr), + types.GetValidatorByConsAddrKey(consAddr), + }, + { + "ValidatorsByPowerIndexKey", + v040staking.GetValidatorsByPowerIndexKey(val), + types.GetValidatorsByPowerIndexKey(val), + }, + { + "DelegationKey", + v040staking.GetDelegationKey(addr4, valAddr1), + types.GetDelegationKey(addr4, valAddr1), + }, + { + "UnbondingDelegationKey", + v040staking.GetUBDKey(addr4, valAddr1), + types.GetUBDKey(addr4, valAddr1), + }, + { + "UnbondingDelegationByValIndexKey", + v040staking.GetUBDByValIndexKey(addr4, valAddr1), + types.GetUBDByValIndexKey(addr4, valAddr1), + }, + { + "RedelegationKey", + v040staking.GetREDKey(addr4, valAddr1, valAddr2), + types.GetREDKey(addr4, valAddr1, valAddr2), + }, + { + "RedelegationByValSrcIndexKey", + v040staking.GetREDByValSrcIndexKey(addr4, valAddr1, valAddr2), + types.GetREDByValSrcIndexKey(addr4, valAddr1, valAddr2), + }, + { + "RedelegationByValDstIndexKey", + v040staking.GetREDByValDstIndexKey(addr4, valAddr1, valAddr2), + types.GetREDByValDstIndexKey(addr4, valAddr1, valAddr2), + }, + { + "UnbondingQueueKey", + v040staking.GetUnbondingDelegationTimeKey(now), + types.GetUnbondingDelegationTimeKey(now), + }, + { + "RedelegationQueueKey", + v040staking.GetRedelegationTimeKey(now), + types.GetRedelegationTimeKey(now), + }, + { + "ValidatorQueueKey", + v040staking.GetValidatorQueueKey(now, 4), + types.GetValidatorQueueKey(now, 4), + }, + { + "HistoricalInfoKey", + v040staking.GetHistoricalInfoKey(4), + types.GetHistoricalInfoKey(4), + }, + } + + // Set all the old keys to the store + for _, tc := range testCases { + store.Set(tc.oldKey, value) + } + + // Run migrations. + err := v042staking.MigrateStore(ctx, stakingKey) + require.NoError(t, err) + + // Make sure the new keys are set and old keys are deleted. + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + if bytes.Compare(tc.oldKey, tc.newKey) != 0 { + require.Nil(t, store.Get(tc.oldKey)) + } + require.Equal(t, value, store.Get(tc.newKey)) + }) + } +} diff --git a/x/staking/module.go b/x/staking/module.go index 48c67e6f5..067620cc6 100644 --- a/x/staking/module.go +++ b/x/staking/module.go @@ -138,6 +138,9 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) querier := keeper.Querier{Keeper: am.keeper} types.RegisterQueryServer(cfg.QueryServer(), querier) + + m := keeper.NewMigrator(am.keeper) + cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2) } // InitGenesis performs genesis initialization for the staking module. It returns @@ -158,7 +161,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 1 } +func (AppModule) ConsensusVersion() uint64 { return 2 } // BeginBlock returns the begin blocker for the staking module. func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { @@ -171,8 +174,6 @@ func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.Val return EndBlocker(ctx, am.keeper) } -//____________________________________________________________________________ - // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the staking module. diff --git a/x/staking/simulation/operations.go b/x/staking/simulation/operations.go index d9621bc73..cf99446b9 100644 --- a/x/staking/simulation/operations.go +++ b/x/staking/simulation/operations.go @@ -92,7 +92,6 @@ func WeightedOperations( } // SimulateMsgCreateValidator generates a MsgCreateValidator with random values -// nolint: interfacer func SimulateMsgCreateValidator(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, @@ -178,7 +177,6 @@ func SimulateMsgCreateValidator(ak types.AccountKeeper, bk types.BankKeeper, k k } // SimulateMsgEditValidator generates a MsgEditValidator with random values -// nolint: interfacer func SimulateMsgEditValidator(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, @@ -249,7 +247,6 @@ func SimulateMsgEditValidator(ak types.AccountKeeper, bk types.BankKeeper, k kee } // SimulateMsgDelegate generates a MsgDelegate with random values -// nolint: interfacer func SimulateMsgDelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, @@ -322,7 +319,6 @@ func SimulateMsgDelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.K } // SimulateMsgUndelegate generates a MsgUndelegate with random values -// nolint: interfacer func SimulateMsgUndelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, @@ -412,7 +408,6 @@ func SimulateMsgUndelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper } // SimulateMsgBeginRedelegate generates a MsgBeginRedelegate with random values -// nolint: interfacer func SimulateMsgBeginRedelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, diff --git a/x/staking/simulation/operations_test.go b/x/staking/simulation/operations_test.go index fca3b7812..8930e2921 100644 --- a/x/staking/simulation/operations_test.go +++ b/x/staking/simulation/operations_test.go @@ -283,8 +283,7 @@ func getTestingAccounts(t *testing.T, r *rand.Rand, app *simapp.SimApp, ctx sdk. for _, account := range accounts { acc := app.AccountKeeper.NewAccountWithAddress(ctx, account.Address) app.AccountKeeper.SetAccount(ctx, acc) - err := app.BankKeeper.SetBalances(ctx, account.Address, initCoins) - require.NoError(t, err) + require.NoError(t, simapp.FundAccount(app, ctx, account.Address, initCoins)) } return accounts diff --git a/x/staking/spec/01_state.md b/x/staking/spec/01_state.md index e090acb01..66c6c0c5d 100644 --- a/x/staking/spec/01_state.md +++ b/x/staking/spec/01_state.md @@ -34,7 +34,7 @@ Validators can have one of three statuses during the period of time that the tokens were bonded. - `Unbonding`: When a validator leaves the active set, either by choice or due to slashing or tombstoning, an unbonding of all their delegations begins. All delegations must then wait the UnbondingTime - before moving receiving their tokens to their accounts from the `BondedPool`. + before moving their tokens to their accounts from the `BondedPool`. Validators objects should be primarily stored and accessed by the `OperatorAddr`, an SDK validator address for the operator of the validator. Two @@ -44,10 +44,10 @@ required lookups for slashing and validator-set updates. A third special index throughout each block, unlike the first two indices which mirror the validator records within a block. -- Validators: `0x21 | OperatorAddr -> ProtocolBuffer(validator)` -- ValidatorsByConsAddr: `0x22 | ConsAddr -> OperatorAddr` -- ValidatorsByPower: `0x23 | BigEndian(ConsensusPower) | OperatorAddr -> OperatorAddr` -- LastValidatorsPower: `0x11 OperatorAddr -> ProtocolBuffer(ConsensusPower)` +- Validators: `0x21 | OperatorAddrLen (1 byte) | OperatorAddr -> ProtocolBuffer(validator)` +- ValidatorsByConsAddr: `0x22 | ConsAddrLen (1 byte) | ConsAddr -> OperatorAddr` +- ValidatorsByPower: `0x23 | BigEndian(ConsensusPower) | OperatorAddrLen (1 byte) | OperatorAddr -> OperatorAddr` +- LastValidatorsPower: `0x11 | OperatorAddrLen (1 byte) | OperatorAddr -> ProtocolBuffer(ConsensusPower)` `Validators` is the primary index - it ensures that each operator can have only one associated validator, where the public key of that validator can change in the @@ -79,7 +79,7 @@ Each validator's state is stored in a `Validator` struct: Delegations are identified by combining `DelegatorAddr` (the address of the delegator) with the `ValidatorAddr` Delegators are indexed in the store as follows: -- Delegation: `0x31 | DelegatorAddr | ValidatorAddr -> ProtocolBuffer(delegation)` +- Delegation: `0x31 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValidatorAddrLen (1 byte) | ValidatorAddr -> ProtocolBuffer(delegation)` Stake holders may delegate coins to validators; under this circumstance their funds are held in a `Delegation` data structure. It is owned by one @@ -115,8 +115,8 @@ detected. `UnbondingDelegation` are indexed in the store as: -- UnbondingDelegation: `0x32 | DelegatorAddr | ValidatorAddr -> ProtocolBuffer(unbondingDelegation)` -- UnbondingDelegationsFromValidator: `0x33 | ValidatorAddr | DelegatorAddr -> nil` +- UnbondingDelegation: `0x32 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValidatorAddrLen (1 byte) | ValidatorAddr -> ProtocolBuffer(unbondingDelegation)` +- UnbondingDelegationsFromValidator: `0x33 | ValidatorAddrLen (1 byte) | ValidatorAddr | DelegatorAddrLen (1 byte) | DelegatorAddr -> nil` The first map here is used in queries, to lookup all unbonding delegations for a given delegator, while the second map is used in slashing, to lookup all @@ -137,9 +137,9 @@ committed by the source validator. `Redelegation` are indexed in the store as: -- Redelegations: `0x34 | DelegatorAddr | ValidatorSrcAddr | ValidatorDstAddr -> ProtocolBuffer(redelegation)` -- RedelegationsBySrc: `0x35 | ValidatorSrcAddr | ValidatorDstAddr | DelegatorAddr -> nil` -- RedelegationsByDst: `0x36 | ValidatorDstAddr | ValidatorSrcAddr | DelegatorAddr -> nil` +- Redelegations: `0x34 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValidatorAddrLen (1 byte) | ValidatorSrcAddr | ValidatorDstAddr -> ProtocolBuffer(redelegation)` +- RedelegationsBySrc: `0x35 | ValidatorSrcAddrLen (1 byte) | ValidatorSrcAddr | ValidatorDstAddrLen (1 byte) | ValidatorDstAddr | DelegatorAddrLen (1 byte) | DelegatorAddr -> nil` +- RedelegationsByDst: `0x36 | ValidatorDstAddrLen (1 byte) | ValidatorDstAddr | ValidatorSrcAddrLen (1 byte) | ValidatorSrcAddr | DelegatorAddrLen (1 byte) | DelegatorAddr -> nil` The first map here is used for queries, to lookup all redelegations for a given delegator. The second map is used for slashing based on the `ValidatorSrcAddr`, diff --git a/x/staking/spec/02_state_transitions.md b/x/staking/spec/02_state_transitions.md index 9e245719c..4a69769b9 100644 --- a/x/staking/spec/02_state_transitions.md +++ b/x/staking/spec/02_state_transitions.md @@ -105,6 +105,9 @@ Redelegations affect the delegation, source and destination validators. is `Bonded`, transfer the newly delegated tokens from the `NotBondedPool` to the `BondedPool` `ModuleAccount` - record the token amount in an new entry in the relevant `Redelegation` +From when a redelegation begins until it completes, the delegator is in a state of "pseudo-unbonding", and can still be +slashed for infractions that occured before the redelegation began. + ### Complete Redelegation When a redelegations complete the following occurs: @@ -119,13 +122,17 @@ When a Validator is slashed, the following occurs: - The total `slashAmount` is calculated as the `slashFactor` (a chain parameter) \* `TokensFromConsensusPower`, the total number of tokens bonded to the validator at the time of the infraction. -- Every unbonding delegation and redelegation from the validator are slashed by the `slashFactor` - percentage of the initialBalance. +- Every unbonding delegation and pseudo-unbonding redelegation such that the infraction occured before the unbonding or + redelegation began from the validator are slashed by the `slashFactor` percentage of the initialBalance. - Each amount slashed from redelegations and unbonding delegations is subtracted from the total slash amount. - The `remaingSlashAmount` is then slashed from the validator's tokens in the `BondedPool` or `NonBondedPool` depending on the validator's status. This reduces the total supply of tokens. +In the case of a slash due to any infraction that requires evidence to submitted (for example double-sign), the slash +occurs at the block where the evidence is included, not at the block where the infraction occured. +Put otherwise, validators are not slashed retroactively, only when they are caught. + ### Slash Unbonding Delegation When a validator is slashed, so are those unbonding delegations from the validator that began unbonding @@ -137,5 +144,25 @@ delegation and is capped to prevent a resulting negative balance. Completed (or When a validator is slashed, so are all redelegations from the validator that began after the infraction. Redelegations are slashed by `slashFactor`. +Redelegations that began before the infraction are not slashed. The amount slashed is calculated from the `InitialBalance` of the delegation and is capped to -prevent a resulting negative balance. Mature redelegations are not slashed. +prevent a resulting negative balance. +Mature redelegations (that have completed pseudo-unbonding) are not slashed. + +## How Shares are calculated + +At any given point in time, each validator has a number of tokens, `T`, and has a number of shares issued, `S`. +Each delegator, `i`, holds a number of shares, `S_i`. +The number of tokens is the sum of all tokens delegated to the validator, plus the rewards, minus the slashes. + +The delegator is entitled to a portion of the underlying tokens proportional to their proportion of shares. +So delegator `i` is entitled to `T * S_i / S` of the validator's tokens. + +When a delegator delegates new tokens to the validator, they receive a number of shares proportional to their contribution. +So when delegator `j` delegates `T_j` tokens, they receive `S_j = S * T_j / T` shares. +The total number of tokens is now `T + T_j`, and the total number of shares is `S + S_j`. +`j`s proportion of the shares is the same as their proportion of the total tokens contributed: `(S + S_j) / S = (T + T_j) / T`. + +A special case is the initial delegation, when `T = 0` and `S = 0`, so `T_j / T` is undefined. +For the initial delegation, delegator `j` who delegates `T_j` tokens receive `S_j = T_j` shares. +So a validator that hasn't received any rewards and has not been slashed will have `T = S`. diff --git a/x/staking/spec/03_messages.md b/x/staking/spec/03_messages.md index 1155da93d..d0f059ecf 100644 --- a/x/staking/spec/03_messages.md +++ b/x/staking/spec/03_messages.md @@ -9,6 +9,7 @@ In this section we describe the processing of the staking messages and the corre ## Msg/CreateValidator A validator is created using the `Msg/CreateValidator` service message. +The validator must be created with an initial delegation from the operator. +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L16-L17 @@ -63,11 +64,17 @@ This service message is expected to fail if: - the validator is does not exist - the validator is jailed - the `Amount` `Coin` has a denomination different than one defined by `params.BondDenom` +- the exchange rate is invalid, meaning the validator has no tokens (due to slashing) but there are outstanding shares +- the amount delegated is less than the minimum allowed delegation If an existing `Delegation` object for provided addresses does not already -exist than it is created as part of this service message otherwise the existing +exist then it is created as part of this message otherwise the existing `Delegation` is updated to include the newly received shares. +The delegator receives newly minted shares at the current exchange rate. +The exchange rate is the number of existing shares in the validator divided by +the number of currently delegated tokens. + ## Msg/Undelegate The `Msg/Undelegate` service message allows delegators to undelegate their tokens from diff --git a/x/staking/spec/08_params.md b/x/staking/spec/08_params.md index 3d87879e1..8d78efa5c 100644 --- a/x/staking/spec/08_params.md +++ b/x/staking/spec/08_params.md @@ -12,4 +12,4 @@ The staking module contains the following parameters: | MaxValidators | uint16 | 100 | | KeyMaxEntries | uint16 | 7 | | HistoricalEntries | uint16 | 3 | -| BondDenom | string | "uatom" | +| BondDenom | string | "stake" | diff --git a/x/staking/teststaking/validator.go b/x/staking/teststaking/validator.go index 901395d76..71459581f 100644 --- a/x/staking/teststaking/validator.go +++ b/x/staking/teststaking/validator.go @@ -11,7 +11,7 @@ import ( ) // NewValidator is a testing helper method to create validators in tests -func NewValidator(t *testing.T, operator sdk.ValAddress, pubKey cryptotypes.PubKey) types.Validator { +func NewValidator(t testing.TB, operator sdk.ValAddress, pubKey cryptotypes.PubKey) types.Validator { v, err := types.NewValidator(operator, pubKey, types.Description{}) require.NoError(t, err) return v diff --git a/x/staking/types/authz.go b/x/staking/types/authz.go new file mode 100644 index 000000000..e88288da4 --- /dev/null +++ b/x/staking/types/authz.go @@ -0,0 +1,136 @@ +package types + +import ( + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authz "github.com/cosmos/cosmos-sdk/x/authz/exported" +) + +var ( + _ authz.Authorization = &StakeAuthorization{} + TypeDelegate = "/cosmos.staking.v1beta1.Msg/Delegate" + TypeUndelegate = "/cosmos.staking.v1beta1.Msg/Undelegate" + TypeBeginRedelegate = "/cosmos.staking.v1beta1.Msg/BeginRedelegate" +) + +// NewStakeAuthorization creates a new StakeAuthorization object. +func NewStakeAuthorization(allowed []sdk.ValAddress, denied []sdk.ValAddress, authzType AuthorizationType, amount *sdk.Coin) (*StakeAuthorization, error) { + allowedValidators, deniedValidators, err := validateAndBech32fy(allowed, denied) + if err != nil { + return nil, err + } + + authorization := StakeAuthorization{} + if allowedValidators != nil { + authorization.Validators = &StakeAuthorization_AllowList{AllowList: &StakeAuthorization_Validators{Address: allowedValidators}} + } else { + authorization.Validators = &StakeAuthorization_DenyList{DenyList: &StakeAuthorization_Validators{Address: deniedValidators}} + } + + if amount != nil { + authorization.MaxTokens = amount + } + authorization.AuthorizationType = authzType + + return &authorization, nil +} + +// MethodName implements Authorization.MethodName. +func (authorization StakeAuthorization) MethodName() string { + authzType, err := normalizeAuthzType(authorization.AuthorizationType) + if err != nil { + panic(err) + } + return authzType +} + +// Accept implements Authorization.Accept. +func (authorization StakeAuthorization) Accept(msg sdk.ServiceMsg, block tmproto.Header) (updated authz.Authorization, delete bool, err error) { + var validatorAddress string + var amount sdk.Coin + + switch msg := msg.Request.(type) { + case *MsgDelegate: + validatorAddress = msg.ValidatorAddress + amount = msg.Amount + case *MsgUndelegate: + validatorAddress = msg.ValidatorAddress + amount = msg.Amount + case *MsgBeginRedelegate: + validatorAddress = msg.ValidatorDstAddress + amount = msg.Amount + default: + return nil, false, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "unknown msg type") + } + + isValidatorExists := false + allowedList := authorization.GetAllowList().GetAddress() + for _, validator := range allowedList { + if validator == validatorAddress { + isValidatorExists = true + break + } + } + denyList := authorization.GetDenyList().GetAddress() + for _, validator := range denyList { + if validator == validatorAddress { + return nil, false, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, " cannot delegate/undelegate to %s validator", validator) + } + } + + if !isValidatorExists { + return nil, false, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "cannot delegate/undelegate to %s validator", validatorAddress) + } + + if authorization.MaxTokens == nil { + return &StakeAuthorization{Validators: authorization.GetValidators(), AuthorizationType: authorization.GetAuthorizationType()}, false, nil + } + + limitLeft := authorization.MaxTokens.Sub(amount) + if limitLeft.IsZero() { + return nil, true, nil + } + + return &StakeAuthorization{Validators: authorization.GetValidators(), MaxTokens: &limitLeft, AuthorizationType: authorization.GetAuthorizationType()}, false, nil + +} + +func validateAndBech32fy(allowed []sdk.ValAddress, denied []sdk.ValAddress) ([]string, []string, error) { + if len(allowed) == 0 && len(denied) == 0 { + return nil, nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "both allowed & deny list cannot be empty") + } + + if len(allowed) > 0 && len(denied) > 0 { + return nil, nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "cannot set both allowed & deny list") + } + + allowedValidators := make([]string, len(allowed)) + if len(allowed) > 0 { + for i, validator := range allowed { + allowedValidators[i] = validator.String() + } + return allowedValidators, nil, nil + } + + deniedValidators := make([]string, len(denied)) + for i, validator := range denied { + deniedValidators[i] = validator.String() + } + + return nil, deniedValidators, nil +} + +func normalizeAuthzType(authzType AuthorizationType) (string, error) { + switch authzType { + case AuthorizationType_AUTHORIZATION_TYPE_DELEGATE: + return TypeDelegate, nil + case AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE: + return TypeUndelegate, nil + case AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE: + return TypeBeginRedelegate, nil + default: + return "", sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "unknown authorization type %T", authzType) + } +} diff --git a/x/staking/types/authz.pb.go b/x/staking/types/authz.pb.go new file mode 100644 index 000000000..d556e0155 --- /dev/null +++ b/x/staking/types/authz.pb.go @@ -0,0 +1,797 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/staking/v1beta1/authz.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "github.com/regen-network/cosmos-proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// AuthorizationType defines the type of staking module authorization type +type AuthorizationType int32 + +const ( + // AUTHORIZATION_TYPE_UNSPECIFIED specifies an unknown authorization type + AuthorizationType_AUTHORIZATION_TYPE_UNSPECIFIED AuthorizationType = 0 + // AUTHORIZATION_TYPE_DELEGATE defines an authorization type for Msg/Delegate + AuthorizationType_AUTHORIZATION_TYPE_DELEGATE AuthorizationType = 1 + // AUTHORIZATION_TYPE_UNDELEGATE defines an authorization type for Msg/Undelegate + AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE AuthorizationType = 2 + // AUTHORIZATION_TYPE_REDELEGATE defines an authorization type for Msg/BeginRedelegate + AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE AuthorizationType = 3 +) + +var AuthorizationType_name = map[int32]string{ + 0: "AUTHORIZATION_TYPE_UNSPECIFIED", + 1: "AUTHORIZATION_TYPE_DELEGATE", + 2: "AUTHORIZATION_TYPE_UNDELEGATE", + 3: "AUTHORIZATION_TYPE_REDELEGATE", +} + +var AuthorizationType_value = map[string]int32{ + "AUTHORIZATION_TYPE_UNSPECIFIED": 0, + "AUTHORIZATION_TYPE_DELEGATE": 1, + "AUTHORIZATION_TYPE_UNDELEGATE": 2, + "AUTHORIZATION_TYPE_REDELEGATE": 3, +} + +func (x AuthorizationType) String() string { + return proto.EnumName(AuthorizationType_name, int32(x)) +} + +func (AuthorizationType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_d6d8cdbc6f4432f0, []int{0} +} + +// StakeAuthorization defines authorization for delegate/undelegate/redelegate. +type StakeAuthorization struct { + // max_tokens specifies the maximum amount of tokens can be delegate to a validator. If it is + // empty, there is no spend limit and any amount of coins can be delegated. + MaxTokens *types.Coin `protobuf:"bytes,1,opt,name=max_tokens,json=maxTokens,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coin" json:"max_tokens,omitempty"` + // validators is the oneof that represents either allow_list or deny_list + // + // Types that are valid to be assigned to Validators: + // *StakeAuthorization_AllowList + // *StakeAuthorization_DenyList + Validators isStakeAuthorization_Validators `protobuf_oneof:"validators"` + // authorization_type defines one of AuthorizationType. + AuthorizationType AuthorizationType `protobuf:"varint,4,opt,name=authorization_type,json=authorizationType,proto3,enum=cosmos.staking.v1beta1.AuthorizationType" json:"authorization_type,omitempty"` +} + +func (m *StakeAuthorization) Reset() { *m = StakeAuthorization{} } +func (m *StakeAuthorization) String() string { return proto.CompactTextString(m) } +func (*StakeAuthorization) ProtoMessage() {} +func (*StakeAuthorization) Descriptor() ([]byte, []int) { + return fileDescriptor_d6d8cdbc6f4432f0, []int{0} +} +func (m *StakeAuthorization) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StakeAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StakeAuthorization.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StakeAuthorization) XXX_Merge(src proto.Message) { + xxx_messageInfo_StakeAuthorization.Merge(m, src) +} +func (m *StakeAuthorization) XXX_Size() int { + return m.Size() +} +func (m *StakeAuthorization) XXX_DiscardUnknown() { + xxx_messageInfo_StakeAuthorization.DiscardUnknown(m) +} + +var xxx_messageInfo_StakeAuthorization proto.InternalMessageInfo + +type isStakeAuthorization_Validators interface { + isStakeAuthorization_Validators() + MarshalTo([]byte) (int, error) + Size() int +} + +type StakeAuthorization_AllowList struct { + AllowList *StakeAuthorization_Validators `protobuf:"bytes,2,opt,name=allow_list,json=allowList,proto3,oneof" json:"allow_list,omitempty"` +} +type StakeAuthorization_DenyList struct { + DenyList *StakeAuthorization_Validators `protobuf:"bytes,3,opt,name=deny_list,json=denyList,proto3,oneof" json:"deny_list,omitempty"` +} + +func (*StakeAuthorization_AllowList) isStakeAuthorization_Validators() {} +func (*StakeAuthorization_DenyList) isStakeAuthorization_Validators() {} + +func (m *StakeAuthorization) GetValidators() isStakeAuthorization_Validators { + if m != nil { + return m.Validators + } + return nil +} + +func (m *StakeAuthorization) GetMaxTokens() *types.Coin { + if m != nil { + return m.MaxTokens + } + return nil +} + +func (m *StakeAuthorization) GetAllowList() *StakeAuthorization_Validators { + if x, ok := m.GetValidators().(*StakeAuthorization_AllowList); ok { + return x.AllowList + } + return nil +} + +func (m *StakeAuthorization) GetDenyList() *StakeAuthorization_Validators { + if x, ok := m.GetValidators().(*StakeAuthorization_DenyList); ok { + return x.DenyList + } + return nil +} + +func (m *StakeAuthorization) GetAuthorizationType() AuthorizationType { + if m != nil { + return m.AuthorizationType + } + return AuthorizationType_AUTHORIZATION_TYPE_UNSPECIFIED +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*StakeAuthorization) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*StakeAuthorization_AllowList)(nil), + (*StakeAuthorization_DenyList)(nil), + } +} + +// Validators defines list of validator addresses. +type StakeAuthorization_Validators struct { + Address []string `protobuf:"bytes,1,rep,name=address,proto3" json:"address,omitempty"` +} + +func (m *StakeAuthorization_Validators) Reset() { *m = StakeAuthorization_Validators{} } +func (m *StakeAuthorization_Validators) String() string { return proto.CompactTextString(m) } +func (*StakeAuthorization_Validators) ProtoMessage() {} +func (*StakeAuthorization_Validators) Descriptor() ([]byte, []int) { + return fileDescriptor_d6d8cdbc6f4432f0, []int{0, 0} +} +func (m *StakeAuthorization_Validators) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StakeAuthorization_Validators) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StakeAuthorization_Validators.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StakeAuthorization_Validators) XXX_Merge(src proto.Message) { + xxx_messageInfo_StakeAuthorization_Validators.Merge(m, src) +} +func (m *StakeAuthorization_Validators) XXX_Size() int { + return m.Size() +} +func (m *StakeAuthorization_Validators) XXX_DiscardUnknown() { + xxx_messageInfo_StakeAuthorization_Validators.DiscardUnknown(m) +} + +var xxx_messageInfo_StakeAuthorization_Validators proto.InternalMessageInfo + +func (m *StakeAuthorization_Validators) GetAddress() []string { + if m != nil { + return m.Address + } + return nil +} + +func init() { + proto.RegisterEnum("cosmos.staking.v1beta1.AuthorizationType", AuthorizationType_name, AuthorizationType_value) + proto.RegisterType((*StakeAuthorization)(nil), "cosmos.staking.v1beta1.StakeAuthorization") + proto.RegisterType((*StakeAuthorization_Validators)(nil), "cosmos.staking.v1beta1.StakeAuthorization.Validators") +} + +func init() { + proto.RegisterFile("cosmos/staking/v1beta1/authz.proto", fileDescriptor_d6d8cdbc6f4432f0) +} + +var fileDescriptor_d6d8cdbc6f4432f0 = []byte{ + // 462 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x93, 0xcb, 0x6e, 0xd3, 0x40, + 0x14, 0x86, 0x3d, 0x0d, 0x02, 0x72, 0xb8, 0xa8, 0x19, 0x21, 0x94, 0x06, 0x31, 0x2d, 0x59, 0x40, + 0xb8, 0x74, 0xac, 0x16, 0xb1, 0x61, 0x97, 0xb4, 0x2e, 0x8d, 0x54, 0xb5, 0x95, 0xeb, 0x56, 0xd0, + 0x8d, 0x35, 0x89, 0x47, 0xc9, 0x28, 0xb6, 0x27, 0xf2, 0x4c, 0x4a, 0xd2, 0xa7, 0xe0, 0x09, 0x78, + 0x00, 0xd6, 0x3c, 0x04, 0x0b, 0x16, 0x15, 0x2b, 0x76, 0xa0, 0xe4, 0x45, 0x90, 0xc7, 0xae, 0xa1, + 0x34, 0x65, 0xc3, 0xca, 0x97, 0xf3, 0xcd, 0xf7, 0x9f, 0x63, 0x1d, 0x43, 0xbd, 0x2b, 0x55, 0x24, + 0x95, 0xad, 0x34, 0x1b, 0x88, 0xb8, 0x67, 0x9f, 0xac, 0x75, 0xb8, 0x66, 0x6b, 0x36, 0x1b, 0xe9, + 0xfe, 0x29, 0x1d, 0x26, 0x52, 0x4b, 0x7c, 0x3f, 0x63, 0x68, 0xce, 0xd0, 0x9c, 0xa9, 0xdd, 0xeb, + 0xc9, 0x9e, 0x34, 0x88, 0x9d, 0xde, 0x65, 0x74, 0x6d, 0x29, 0xa3, 0xfd, 0xac, 0x90, 0x1f, 0xcd, + 0x4a, 0x24, 0x0f, 0xeb, 0x30, 0xc5, 0x8b, 0xa4, 0xae, 0x14, 0x71, 0x56, 0xaf, 0x7f, 0x2d, 0x01, + 0x3e, 0xd0, 0x6c, 0xc0, 0x9b, 0x23, 0xdd, 0x97, 0x89, 0x38, 0x65, 0x5a, 0xc8, 0x18, 0x73, 0x80, + 0x88, 0x8d, 0x7d, 0x2d, 0x07, 0x3c, 0x56, 0x55, 0xb4, 0x82, 0x1a, 0xb7, 0xd6, 0x97, 0x68, 0x6e, + 0x4e, 0x5d, 0xe7, 0x1d, 0xd1, 0x0d, 0x29, 0xe2, 0xd6, 0xf3, 0x4f, 0x3f, 0x96, 0x9f, 0xf4, 0x84, + 0xee, 0x8f, 0x3a, 0xb4, 0x2b, 0xa3, 0xbc, 0x85, 0xfc, 0xb2, 0xaa, 0x82, 0x81, 0xad, 0x27, 0x43, + 0xae, 0x0c, 0xec, 0x96, 0x23, 0x36, 0xf6, 0x8c, 0x18, 0x1f, 0x01, 0xb0, 0x30, 0x94, 0xef, 0xfd, + 0x50, 0x28, 0x5d, 0x5d, 0x30, 0x31, 0xaf, 0xe8, 0xfc, 0xd9, 0xe9, 0xe5, 0x36, 0xe9, 0x11, 0x0b, + 0x45, 0xc0, 0xb4, 0x4c, 0xd4, 0xb6, 0xe5, 0x96, 0x8d, 0x6a, 0x47, 0x28, 0x8d, 0x3d, 0x28, 0x07, + 0x3c, 0x9e, 0x64, 0xda, 0xd2, 0xff, 0x69, 0x6f, 0xa6, 0x26, 0x63, 0x7d, 0x0b, 0x98, 0xfd, 0xc9, + 0xf9, 0xe9, 0x50, 0xd5, 0x6b, 0x2b, 0xa8, 0x71, 0x77, 0xfd, 0xe9, 0x55, 0xfa, 0x0b, 0x66, 0x6f, + 0x32, 0xe4, 0x6e, 0x85, 0xfd, 0xfd, 0xaa, 0xf6, 0x18, 0xe0, 0x77, 0x26, 0xae, 0xc2, 0x0d, 0x16, + 0x04, 0x09, 0x57, 0xe9, 0x97, 0x2f, 0x35, 0xca, 0xee, 0xf9, 0xe3, 0xeb, 0xca, 0xb7, 0xcf, 0xab, + 0x77, 0x2e, 0x18, 0x5b, 0xb7, 0x01, 0x4e, 0x8a, 0xa3, 0xcf, 0x3e, 0x22, 0xa8, 0x5c, 0x4a, 0xc4, + 0x75, 0x20, 0xcd, 0x43, 0x6f, 0x7b, 0xcf, 0x6d, 0x1f, 0x37, 0xbd, 0xf6, 0xde, 0xae, 0xef, 0xbd, + 0xdb, 0x77, 0xfc, 0xc3, 0xdd, 0x83, 0x7d, 0x67, 0xa3, 0xbd, 0xd5, 0x76, 0x36, 0x17, 0x2d, 0xbc, + 0x0c, 0x0f, 0xe6, 0x30, 0x9b, 0xce, 0x8e, 0xf3, 0xa6, 0xe9, 0x39, 0x8b, 0x08, 0x3f, 0x82, 0x87, + 0x73, 0x25, 0x05, 0xb2, 0x70, 0x05, 0xe2, 0x3a, 0x05, 0x52, 0x6a, 0x6d, 0x7d, 0x99, 0x12, 0x74, + 0x36, 0x25, 0xe8, 0xe7, 0x94, 0xa0, 0x0f, 0x33, 0x62, 0x9d, 0xcd, 0x88, 0xf5, 0x7d, 0x46, 0xac, + 0xe3, 0x17, 0xff, 0xdc, 0x9f, 0x71, 0xf1, 0xbb, 0x98, 0x4d, 0xea, 0x5c, 0x37, 0xeb, 0xfb, 0xf2, + 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x54, 0x04, 0xbf, 0x8c, 0x4d, 0x03, 0x00, 0x00, +} + +func (m *StakeAuthorization) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StakeAuthorization) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StakeAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AuthorizationType != 0 { + i = encodeVarintAuthz(dAtA, i, uint64(m.AuthorizationType)) + i-- + dAtA[i] = 0x20 + } + if m.Validators != nil { + { + size := m.Validators.Size() + i -= size + if _, err := m.Validators.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if m.MaxTokens != nil { + { + size, err := m.MaxTokens.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthz(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *StakeAuthorization_AllowList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StakeAuthorization_AllowList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.AllowList != nil { + { + size, err := m.AllowList.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthz(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *StakeAuthorization_DenyList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StakeAuthorization_DenyList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.DenyList != nil { + { + size, err := m.DenyList.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthz(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + return len(dAtA) - i, nil +} +func (m *StakeAuthorization_Validators) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StakeAuthorization_Validators) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StakeAuthorization_Validators) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + for iNdEx := len(m.Address) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Address[iNdEx]) + copy(dAtA[i:], m.Address[iNdEx]) + i = encodeVarintAuthz(dAtA, i, uint64(len(m.Address[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintAuthz(dAtA []byte, offset int, v uint64) int { + offset -= sovAuthz(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *StakeAuthorization) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MaxTokens != nil { + l = m.MaxTokens.Size() + n += 1 + l + sovAuthz(uint64(l)) + } + if m.Validators != nil { + n += m.Validators.Size() + } + if m.AuthorizationType != 0 { + n += 1 + sovAuthz(uint64(m.AuthorizationType)) + } + return n +} + +func (m *StakeAuthorization_AllowList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AllowList != nil { + l = m.AllowList.Size() + n += 1 + l + sovAuthz(uint64(l)) + } + return n +} +func (m *StakeAuthorization_DenyList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DenyList != nil { + l = m.DenyList.Size() + n += 1 + l + sovAuthz(uint64(l)) + } + return n +} +func (m *StakeAuthorization_Validators) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Address) > 0 { + for _, s := range m.Address { + l = len(s) + n += 1 + l + sovAuthz(uint64(l)) + } + } + return n +} + +func sovAuthz(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAuthz(x uint64) (n int) { + return sovAuthz(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *StakeAuthorization) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StakeAuthorization: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StakeAuthorization: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxTokens", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.MaxTokens == nil { + m.MaxTokens = &types.Coin{} + } + if err := m.MaxTokens.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowList", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &StakeAuthorization_Validators{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Validators = &StakeAuthorization_AllowList{v} + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DenyList", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &StakeAuthorization_Validators{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Validators = &StakeAuthorization_DenyList{v} + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationType", wireType) + } + m.AuthorizationType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AuthorizationType |= AuthorizationType(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAuthz(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthz + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StakeAuthorization_Validators) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Validators: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Validators: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = append(m.Address, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuthz(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthz + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAuthz(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAuthz + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAuthz + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAuthz + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAuthz = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAuthz = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAuthz = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/staking/types/authz_test.go b/x/staking/types/authz_test.go new file mode 100644 index 000000000..18709d9ec --- /dev/null +++ b/x/staking/types/authz_test.go @@ -0,0 +1,283 @@ +package types_test + +import ( + "testing" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +var ( + coin100 = sdk.NewInt64Coin("steak", 100) + coin50 = sdk.NewInt64Coin("steak", 50) + delAddr = sdk.AccAddress("_____delegator _____") + val1 = sdk.ValAddress("_____validator1_____") + val2 = sdk.ValAddress("_____validator2_____") + val3 = sdk.ValAddress("_____validator3_____") +) + +func TestAuthzAuthorizations(t *testing.T) { + + // verify MethodName + delAuth, _ := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &coin100) + require.Equal(t, delAuth.MethodName(), stakingtypes.TypeDelegate) + + // error both allow & deny list + _, err := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{val1}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &coin100) + require.Error(t, err) + + // verify MethodName + undelAuth, _ := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, &coin100) + require.Equal(t, undelAuth.MethodName(), stakingtypes.TypeUndelegate) + + // verify MethodName + beginRedelAuth, _ := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, &coin100) + require.Equal(t, beginRedelAuth.MethodName(), stakingtypes.TypeBeginRedelegate) + + validators1_2 := []string{val1.String(), val2.String()} + + testCases := []struct { + msg string + allowed []sdk.ValAddress + denied []sdk.ValAddress + msgType stakingtypes.AuthorizationType + limit *sdk.Coin + srvMsg sdk.ServiceMsg + expectErr bool + isDelete bool + updatedAuthorization *stakingtypes.StakeAuthorization + }{ + { + "delegate: expect 0 remaining coins", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, + &coin100, + createSrvMsgDelegate(delAuth.MethodName(), delAddr, val1, coin100), + false, + true, + nil, + }, + { + "delegate: verify remaining coins", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, + &coin100, + createSrvMsgDelegate(delAuth.MethodName(), delAddr, val1, coin50), + false, + false, + &stakingtypes.StakeAuthorization{ + Validators: &stakingtypes.StakeAuthorization_AllowList{ + AllowList: &stakingtypes.StakeAuthorization_Validators{Address: validators1_2}, + }, MaxTokens: &coin50, AuthorizationType: stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE}, + }, + { + "delegate: testing with invalid validator", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, + &coin100, + createSrvMsgDelegate(delAuth.MethodName(), delAddr, val3, coin100), + true, + false, + nil, + }, + { + "delegate: testing delegate without spent limit", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, + nil, + createSrvMsgDelegate(delAuth.MethodName(), delAddr, val2, coin100), + false, + false, + &stakingtypes.StakeAuthorization{ + Validators: &stakingtypes.StakeAuthorization_AllowList{ + AllowList: &stakingtypes.StakeAuthorization_Validators{Address: validators1_2}, + }, MaxTokens: nil, AuthorizationType: stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE}, + }, + { + "delegate: fail validator denied", + []sdk.ValAddress{}, + []sdk.ValAddress{val1}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, + nil, + createSrvMsgDelegate(delAuth.MethodName(), delAddr, val1, coin100), + true, + false, + nil, + }, + + { + "undelegate: expect 0 remaining coins", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, + &coin100, + createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val1, coin100), + false, + true, + nil, + }, + { + "undelegate: verify remaining coins", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, + &coin100, + createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val1, coin50), + false, + false, + &stakingtypes.StakeAuthorization{ + Validators: &stakingtypes.StakeAuthorization_AllowList{ + AllowList: &stakingtypes.StakeAuthorization_Validators{Address: validators1_2}, + }, MaxTokens: &coin50, AuthorizationType: stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE}, + }, + { + "undelegate: testing with invalid validator", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, + &coin100, + createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val3, coin100), + true, + false, + nil, + }, + { + "undelegate: testing delegate without spent limit", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, + nil, + createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val2, coin100), + false, + false, + &stakingtypes.StakeAuthorization{ + Validators: &stakingtypes.StakeAuthorization_AllowList{ + AllowList: &stakingtypes.StakeAuthorization_Validators{Address: validators1_2}, + }, MaxTokens: nil, AuthorizationType: stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE}, + }, + { + "undelegate: fail cannot undelegate, permission denied", + []sdk.ValAddress{}, + []sdk.ValAddress{val1}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, + &coin100, + createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val1, coin100), + true, + false, + nil, + }, + + { + "redelegate: expect 0 remaining coins", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, + &coin100, + createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val1, coin100), + false, + true, + nil, + }, + { + "redelegate: verify remaining coins", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, + &coin100, + createSrvMsgReDelegate(undelAuth.MethodName(), delAddr, val1, coin50), + false, + false, + &stakingtypes.StakeAuthorization{ + Validators: &stakingtypes.StakeAuthorization_AllowList{ + AllowList: &stakingtypes.StakeAuthorization_Validators{Address: validators1_2}, + }, MaxTokens: &coin50, AuthorizationType: stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE}, + }, + { + "redelegate: testing with invalid validator", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, + &coin100, + createSrvMsgReDelegate(undelAuth.MethodName(), delAddr, val3, coin100), + true, + false, + nil, + }, + { + "redelegate: testing delegate without spent limit", + []sdk.ValAddress{val1, val2}, + []sdk.ValAddress{}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, + nil, + createSrvMsgReDelegate(undelAuth.MethodName(), delAddr, val2, coin100), + false, + false, + &stakingtypes.StakeAuthorization{ + Validators: &stakingtypes.StakeAuthorization_AllowList{ + AllowList: &stakingtypes.StakeAuthorization_Validators{Address: validators1_2}, + }, MaxTokens: nil, AuthorizationType: stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE}, + }, + { + "redelegate: fail cannot undelegate, permission denied", + []sdk.ValAddress{}, + []sdk.ValAddress{val1}, + stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, + &coin100, + createSrvMsgReDelegate(undelAuth.MethodName(), delAddr, val1, coin100), + true, + false, + nil, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.msg, func(t *testing.T) { + delAuth, err := stakingtypes.NewStakeAuthorization(tc.allowed, tc.denied, tc.msgType, tc.limit) + require.NoError(t, err) + updated, del, err := delAuth.Accept(tc.srvMsg, tmproto.Header{}) + if tc.expectErr { + require.Error(t, err) + require.Equal(t, tc.isDelete, del) + } else { + require.NoError(t, err) + require.Equal(t, tc.isDelete, del) + if tc.updatedAuthorization != nil { + require.Equal(t, tc.updatedAuthorization.String(), updated.String()) + } + } + }) + } +} + +func createSrvMsgUndelegate(methodName string, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) sdk.ServiceMsg { + msg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, amount) + return sdk.ServiceMsg{ + MethodName: methodName, + Request: msg, + } +} + +func createSrvMsgReDelegate(methodName string, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) sdk.ServiceMsg { + msg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr, valAddr, amount) + return sdk.ServiceMsg{ + MethodName: methodName, + Request: msg, + } +} + +func createSrvMsgDelegate(methodName string, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) sdk.ServiceMsg { + msg := stakingtypes.NewMsgDelegate(delAddr, valAddr, amount) + return sdk.ServiceMsg{ + MethodName: methodName, + Request: msg, + } +} diff --git a/x/staking/types/expected_keepers.go b/x/staking/types/expected_keepers.go index 1c1d7f7de..2b7326f99 100644 --- a/x/staking/types/expected_keepers.go +++ b/x/staking/types/expected_keepers.go @@ -28,7 +28,6 @@ type AccountKeeper interface { type BankKeeper interface { GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin - SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins @@ -83,7 +82,6 @@ type DelegationSet interface { fn func(index int64, delegation DelegationI) (stop bool)) } -//_______________________________________________________________________________ // Event Hooks // These can be utilized to communicate between a staking keeper and another // keeper which must take particular actions when validators/delegators change diff --git a/x/staking/types/msg.go b/x/staking/types/msg.go index 52ab1750b..eda27b768 100644 --- a/x/staking/types/msg.go +++ b/x/staking/types/msg.go @@ -24,6 +24,13 @@ const ( TypeMsgCreateValidator = "create_validator" TypeMsgDelegate = "delegate" TypeMsgBeginRedelegate = "begin_redelegate" + + // These are used for querying events by action. + TypeSvcMsgUndelegate = "/cosmos.staking.v1beta1.Msg/Undelegate" + TypeSvcMsgEditValidator = "/cosmos.staking.v1beta1.Msg/EditValidator" + TypeSvcMsgCreateValidator = "/cosmos.staking.v1beta1.Msg/CreateValidator" + TypeSvcMsgDelegate = "/cosmos.staking.v1beta1.Msg/Deledate" + TypeSvcMsgBeginRedelegate = "/cosmos.staking.v1beta1.Msg/BeginRedelegate" ) var ( diff --git a/x/staking/types/query.pb.gw.go b/x/staking/types/query.pb.gw.go index eecdb3f31..0982dfc3d 100644 --- a/x/staking/types/query.pb.gw.go +++ b/x/staking/types/query.pb.gw.go @@ -1482,33 +1482,33 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Validators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "staking", "v1beta1", "validators"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Validators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "staking", "v1beta1", "validators"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Validator_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Validator_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ValidatorDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "delegations"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ValidatorDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "delegations"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ValidatorUnbondingDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "unbonding_delegations"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_ValidatorUnbondingDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "unbonding_delegations"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Delegation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "delegations", "delegator_addr"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Delegation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "delegations", "delegator_addr"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_UnbondingDelegation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "delegations", "delegator_addr", "unbonding_delegation"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_UnbondingDelegation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "delegations", "delegator_addr", "unbonding_delegation"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DelegatorDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "staking", "v1beta1", "delegations", "delegator_addr"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DelegatorDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "staking", "v1beta1", "delegations", "delegator_addr"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DelegatorUnbondingDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "unbonding_delegations"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DelegatorUnbondingDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "unbonding_delegations"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Redelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "redelegations"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Redelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "redelegations"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DelegatorValidators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "validators"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DelegatorValidators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "validators"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_DelegatorValidator_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "validators", "validator_addr"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_DelegatorValidator_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "validators", "validator_addr"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_HistoricalInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "staking", "v1beta1", "historical_info", "height"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_HistoricalInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "staking", "v1beta1", "historical_info", "height"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Pool_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "staking", "v1beta1", "pool"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Pool_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "staking", "v1beta1", "pool"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "staking", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "staking", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/staking/types/staking.pb.go b/x/staking/types/staking.pb.go index 45b30b7a0..36b5a0b66 100644 --- a/x/staking/types/staking.pb.go +++ b/x/staking/types/staking.pb.go @@ -1260,608 +1260,621 @@ func (this *Pool) Description() (desc *github_com_gogo_protobuf_protoc_gen_gogo_ func StakingDescription() (desc *github_com_gogo_protobuf_protoc_gen_gogo_descriptor.FileDescriptorSet) { d := &github_com_gogo_protobuf_protoc_gen_gogo_descriptor.FileDescriptorSet{} var gzipped = []byte{ - // 9603 bytes of a gzipped FileDescriptorSet - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x6d, 0x70, 0x24, 0xd7, - 0x71, 0xd8, 0xcd, 0xee, 0x02, 0xd8, 0x6d, 0x2c, 0x80, 0xc5, 0x03, 0xee, 0x6e, 0x6f, 0x79, 0x04, - 0xc0, 0xe1, 0xd7, 0xf1, 0x48, 0x02, 0xe4, 0x91, 0x77, 0x24, 0xf7, 0x24, 0xd2, 0x58, 0x60, 0x0f, - 0x07, 0x1e, 0xbe, 0x38, 0x00, 0x8e, 0xd4, 0x87, 0xb3, 0x35, 0x98, 0x7d, 0x58, 0x0c, 0xb1, 0x3b, - 0x33, 0x9c, 0x99, 0xbd, 0x3b, 0x50, 0x52, 0x15, 0x2d, 0x29, 0x8a, 0x44, 0xc7, 0x91, 0x14, 0xb9, - 0x1c, 0x89, 0xd6, 0x29, 0x92, 0xe5, 0x44, 0x8e, 0xac, 0xc4, 0x1f, 0x52, 0x94, 0x38, 0x49, 0x55, - 0xa4, 0x24, 0x8e, 0x25, 0xa5, 0xe2, 0x92, 0x2a, 0xae, 0xc4, 0x71, 0x25, 0x67, 0x87, 0x52, 0x39, - 0x8c, 0xa2, 0xc4, 0xf2, 0x59, 0x4e, 0x9c, 0x52, 0xa5, 0x92, 0x7a, 0x5f, 0xf3, 0xb5, 0x1f, 0xb3, - 0x0b, 0xdd, 0x49, 0x72, 0x9c, 0x5f, 0xd8, 0xd7, 0xaf, 0xbb, 0x5f, 0xbf, 0x7e, 0xfd, 0xba, 0xfb, - 0x7d, 0x0d, 0xe0, 0x9f, 0x9f, 0x87, 0x99, 0x9a, 0x69, 0xd6, 0xea, 0x78, 0xce, 0xb2, 0x4d, 0xd7, - 0xdc, 0x69, 0xee, 0xce, 0x55, 0xb1, 0xa3, 0xd9, 0xba, 0xe5, 0x9a, 0xf6, 0x2c, 0x85, 0xa1, 0x31, - 0x86, 0x31, 0x2b, 0x30, 0xe4, 0x55, 0x18, 0xbf, 0xa0, 0xd7, 0xf1, 0xa2, 0x87, 0xb8, 0x89, 0x5d, - 0xf4, 0x24, 0xa4, 0x76, 0xf5, 0x3a, 0xce, 0x4b, 0x33, 0xc9, 0x53, 0xc3, 0x67, 0xee, 0x99, 0x8d, - 0x10, 0xcd, 0x86, 0x29, 0x36, 0x08, 0x58, 0xa1, 0x14, 0xf2, 0xb7, 0x52, 0x30, 0xd1, 0xa6, 0x16, - 0x21, 0x48, 0x19, 0x6a, 0x83, 0x70, 0x94, 0x4e, 0x65, 0x14, 0xfa, 0x1b, 0xe5, 0x61, 0xc8, 0x52, - 0xb5, 0x7d, 0xb5, 0x86, 0xf3, 0x09, 0x0a, 0x16, 0x45, 0x34, 0x05, 0x50, 0xc5, 0x16, 0x36, 0xaa, - 0xd8, 0xd0, 0x0e, 0xf2, 0xc9, 0x99, 0xe4, 0xa9, 0x8c, 0x12, 0x80, 0xa0, 0x07, 0x61, 0xdc, 0x6a, - 0xee, 0xd4, 0x75, 0xad, 0x12, 0x40, 0x83, 0x99, 0xe4, 0xa9, 0x01, 0x25, 0xc7, 0x2a, 0x16, 0x7d, - 0xe4, 0xfb, 0x61, 0xec, 0x2a, 0x56, 0xf7, 0x83, 0xa8, 0xc3, 0x14, 0x75, 0x94, 0x80, 0x03, 0x88, - 0x0b, 0x90, 0x6d, 0x60, 0xc7, 0x51, 0x6b, 0xb8, 0xe2, 0x1e, 0x58, 0x38, 0x9f, 0xa2, 0xbd, 0x9f, - 0x69, 0xe9, 0x7d, 0xb4, 0xe7, 0xc3, 0x9c, 0x6a, 0xeb, 0xc0, 0xc2, 0x68, 0x1e, 0x32, 0xd8, 0x68, - 0x36, 0x18, 0x87, 0x81, 0x0e, 0xfa, 0x2b, 0x1b, 0xcd, 0x46, 0x94, 0x4b, 0x9a, 0x90, 0x71, 0x16, - 0x43, 0x0e, 0xb6, 0xaf, 0xe8, 0x1a, 0xce, 0x0f, 0x52, 0x06, 0xf7, 0xb7, 0x30, 0xd8, 0x64, 0xf5, - 0x51, 0x1e, 0x82, 0x0e, 0x2d, 0x40, 0x06, 0x5f, 0x73, 0xb1, 0xe1, 0xe8, 0xa6, 0x91, 0x1f, 0xa2, - 0x4c, 0xee, 0x6d, 0x33, 0x8a, 0xb8, 0x5e, 0x8d, 0xb2, 0xf0, 0xe9, 0xd0, 0x39, 0x18, 0x32, 0x2d, - 0x57, 0x37, 0x0d, 0x27, 0x9f, 0x9e, 0x91, 0x4e, 0x0d, 0x9f, 0x39, 0xd9, 0xd6, 0x10, 0xd6, 0x19, - 0x8e, 0x22, 0x90, 0xd1, 0x32, 0xe4, 0x1c, 0xb3, 0x69, 0x6b, 0xb8, 0xa2, 0x99, 0x55, 0x5c, 0xd1, - 0x8d, 0x5d, 0x33, 0x9f, 0xa1, 0x0c, 0xa6, 0x5b, 0x3b, 0x42, 0x11, 0x17, 0xcc, 0x2a, 0x5e, 0x36, - 0x76, 0x4d, 0x65, 0xd4, 0x09, 0x95, 0xd1, 0x31, 0x18, 0x74, 0x0e, 0x0c, 0x57, 0xbd, 0x96, 0xcf, - 0x52, 0x0b, 0xe1, 0x25, 0xf9, 0x37, 0x06, 0x61, 0xac, 0x17, 0x13, 0x3b, 0x0f, 0x03, 0xbb, 0xa4, - 0x97, 0xf9, 0x44, 0x3f, 0x3a, 0x60, 0x34, 0x61, 0x25, 0x0e, 0x1e, 0x52, 0x89, 0xf3, 0x30, 0x6c, - 0x60, 0xc7, 0xc5, 0x55, 0x66, 0x11, 0xc9, 0x1e, 0x6d, 0x0a, 0x18, 0x51, 0xab, 0x49, 0xa5, 0x0e, - 0x65, 0x52, 0x2f, 0xc0, 0x98, 0x27, 0x52, 0xc5, 0x56, 0x8d, 0x9a, 0xb0, 0xcd, 0xb9, 0x38, 0x49, - 0x66, 0xcb, 0x82, 0x4e, 0x21, 0x64, 0xca, 0x28, 0x0e, 0x95, 0xd1, 0x22, 0x80, 0x69, 0x60, 0x73, - 0xb7, 0x52, 0xc5, 0x5a, 0x3d, 0x9f, 0xee, 0xa0, 0xa5, 0x75, 0x82, 0xd2, 0xa2, 0x25, 0x93, 0x41, - 0xb5, 0x3a, 0x7a, 0xca, 0x37, 0xb5, 0xa1, 0x0e, 0x96, 0xb2, 0xca, 0x26, 0x59, 0x8b, 0xb5, 0x6d, - 0xc3, 0xa8, 0x8d, 0x89, 0xdd, 0xe3, 0x2a, 0xef, 0x59, 0x86, 0x0a, 0x31, 0x1b, 0xdb, 0x33, 0x85, - 0x93, 0xb1, 0x8e, 0x8d, 0xd8, 0xc1, 0x22, 0xba, 0x1b, 0x3c, 0x40, 0x85, 0x9a, 0x15, 0x50, 0x2f, - 0x94, 0x15, 0xc0, 0x35, 0xb5, 0x81, 0x0b, 0x2f, 0xc3, 0x68, 0x58, 0x3d, 0x68, 0x12, 0x06, 0x1c, - 0x57, 0xb5, 0x5d, 0x6a, 0x85, 0x03, 0x0a, 0x2b, 0xa0, 0x1c, 0x24, 0xb1, 0x51, 0xa5, 0x5e, 0x6e, - 0x40, 0x21, 0x3f, 0xd1, 0x4f, 0xf8, 0x1d, 0x4e, 0xd2, 0x0e, 0xdf, 0xd7, 0x3a, 0xa2, 0x21, 0xce, - 0xd1, 0x7e, 0x17, 0x9e, 0x80, 0x91, 0x50, 0x07, 0x7a, 0x6d, 0x5a, 0x7e, 0x27, 0x1c, 0x6d, 0xcb, - 0x1a, 0xbd, 0x00, 0x93, 0x4d, 0x43, 0x37, 0x5c, 0x6c, 0x5b, 0x36, 0x26, 0x16, 0xcb, 0x9a, 0xca, - 0xff, 0xe7, 0xa1, 0x0e, 0x36, 0xb7, 0x1d, 0xc4, 0x66, 0x5c, 0x94, 0x89, 0x66, 0x2b, 0xf0, 0x74, - 0x26, 0xfd, 0xc6, 0x50, 0xee, 0x95, 0x57, 0x5e, 0x79, 0x25, 0x21, 0x7f, 0x79, 0x10, 0x26, 0xdb, - 0xcd, 0x99, 0xb6, 0xd3, 0xf7, 0x18, 0x0c, 0x1a, 0xcd, 0xc6, 0x0e, 0xb6, 0xa9, 0x92, 0x06, 0x14, - 0x5e, 0x42, 0xf3, 0x30, 0x50, 0x57, 0x77, 0x70, 0x3d, 0x9f, 0x9a, 0x91, 0x4e, 0x8d, 0x9e, 0x79, - 0xb0, 0xa7, 0x59, 0x39, 0xbb, 0x42, 0x48, 0x14, 0x46, 0x89, 0x9e, 0x86, 0x14, 0x77, 0xd1, 0x84, - 0xc3, 0xe9, 0xde, 0x38, 0x90, 0xb9, 0xa4, 0x50, 0x3a, 0x74, 0x07, 0x64, 0xc8, 0x5f, 0x66, 0x1b, - 0x83, 0x54, 0xe6, 0x34, 0x01, 0x10, 0xbb, 0x40, 0x05, 0x48, 0xd3, 0x69, 0x52, 0xc5, 0x22, 0xb4, - 0x79, 0x65, 0x62, 0x58, 0x55, 0xbc, 0xab, 0x36, 0xeb, 0x6e, 0xe5, 0x8a, 0x5a, 0x6f, 0x62, 0x6a, - 0xf0, 0x19, 0x25, 0xcb, 0x81, 0x97, 0x09, 0x0c, 0x4d, 0xc3, 0x30, 0x9b, 0x55, 0xba, 0x51, 0xc5, - 0xd7, 0xa8, 0xf7, 0x1c, 0x50, 0xd8, 0x44, 0x5b, 0x26, 0x10, 0xd2, 0xfc, 0x8b, 0x8e, 0x69, 0x08, - 0xd3, 0xa4, 0x4d, 0x10, 0x00, 0x6d, 0xfe, 0x89, 0xa8, 0xe3, 0xbe, 0xb3, 0x7d, 0xf7, 0x5a, 0xe6, - 0xd2, 0xfd, 0x30, 0x46, 0x31, 0x1e, 0xe3, 0x43, 0xaf, 0xd6, 0xf3, 0xe3, 0x33, 0xd2, 0xa9, 0xb4, - 0x32, 0xca, 0xc0, 0xeb, 0x1c, 0x2a, 0x7f, 0x31, 0x01, 0x29, 0xea, 0x58, 0xc6, 0x60, 0x78, 0xeb, - 0x2d, 0x1b, 0xe5, 0xca, 0xe2, 0xfa, 0x76, 0x69, 0xa5, 0x9c, 0x93, 0xd0, 0x28, 0x00, 0x05, 0x5c, - 0x58, 0x59, 0x9f, 0xdf, 0xca, 0x25, 0xbc, 0xf2, 0xf2, 0xda, 0xd6, 0xb9, 0xc7, 0x73, 0x49, 0x8f, - 0x60, 0x9b, 0x01, 0x52, 0x41, 0x84, 0xc7, 0xce, 0xe4, 0x06, 0x50, 0x0e, 0xb2, 0x8c, 0xc1, 0xf2, - 0x0b, 0xe5, 0xc5, 0x73, 0x8f, 0xe7, 0x06, 0xc3, 0x90, 0xc7, 0xce, 0xe4, 0x86, 0xd0, 0x08, 0x64, - 0x28, 0xa4, 0xb4, 0xbe, 0xbe, 0x92, 0x4b, 0x7b, 0x3c, 0x37, 0xb7, 0x94, 0xe5, 0xb5, 0xa5, 0x5c, - 0xc6, 0xe3, 0xb9, 0xa4, 0xac, 0x6f, 0x6f, 0xe4, 0xc0, 0xe3, 0xb0, 0x5a, 0xde, 0xdc, 0x9c, 0x5f, - 0x2a, 0xe7, 0x86, 0x3d, 0x8c, 0xd2, 0x5b, 0xb6, 0xca, 0x9b, 0xb9, 0x6c, 0x48, 0xac, 0xc7, 0xce, - 0xe4, 0x46, 0xbc, 0x26, 0xca, 0x6b, 0xdb, 0xab, 0xb9, 0x51, 0x34, 0x0e, 0x23, 0xac, 0x09, 0x21, - 0xc4, 0x58, 0x04, 0x74, 0xee, 0xf1, 0x5c, 0xce, 0x17, 0x84, 0x71, 0x19, 0x0f, 0x01, 0xce, 0x3d, - 0x9e, 0x43, 0xf2, 0x02, 0x0c, 0x50, 0x33, 0x44, 0x08, 0x46, 0x57, 0xe6, 0x4b, 0xe5, 0x95, 0xca, - 0xfa, 0xc6, 0xd6, 0xf2, 0xfa, 0xda, 0xfc, 0x4a, 0x4e, 0xf2, 0x61, 0x4a, 0xf9, 0xb9, 0xed, 0x65, - 0xa5, 0xbc, 0x98, 0x4b, 0x04, 0x61, 0x1b, 0xe5, 0xf9, 0xad, 0xf2, 0x62, 0x2e, 0x29, 0x6b, 0x30, - 0xd9, 0xce, 0xa1, 0xb6, 0x9d, 0x42, 0x01, 0x5b, 0x48, 0x74, 0xb0, 0x05, 0xca, 0x2b, 0x6a, 0x0b, - 0xf2, 0x37, 0x13, 0x30, 0xd1, 0x26, 0xa8, 0xb4, 0x6d, 0xe4, 0x19, 0x18, 0x60, 0xb6, 0xcc, 0xc2, - 0xec, 0x03, 0x6d, 0xa3, 0x13, 0xb5, 0xec, 0x96, 0x50, 0x4b, 0xe9, 0x82, 0xa9, 0x46, 0xb2, 0x43, - 0xaa, 0x41, 0x58, 0xb4, 0x18, 0xec, 0x4f, 0xb6, 0x38, 0x7f, 0x16, 0x1f, 0xcf, 0xf5, 0x12, 0x1f, - 0x29, 0xac, 0xbf, 0x20, 0x30, 0xd0, 0x26, 0x08, 0x9c, 0x87, 0xf1, 0x16, 0x46, 0x3d, 0x3b, 0xe3, - 0xf7, 0x48, 0x90, 0xef, 0xa4, 0x9c, 0x18, 0x97, 0x98, 0x08, 0xb9, 0xc4, 0xf3, 0x51, 0x0d, 0xde, - 0xd5, 0x79, 0x10, 0x5a, 0xc6, 0xfa, 0x33, 0x12, 0x1c, 0x6b, 0x9f, 0x52, 0xb6, 0x95, 0xe1, 0x69, - 0x18, 0x6c, 0x60, 0x77, 0xcf, 0x14, 0x69, 0xd5, 0x7d, 0x6d, 0x82, 0x35, 0xa9, 0x8e, 0x0e, 0x36, - 0xa7, 0x0a, 0x46, 0xfb, 0x64, 0xa7, 0xbc, 0x90, 0x49, 0xd3, 0x22, 0xe9, 0x07, 0x12, 0x70, 0xb4, - 0x2d, 0xf3, 0xb6, 0x82, 0xde, 0x09, 0xa0, 0x1b, 0x56, 0xd3, 0x65, 0xa9, 0x13, 0xf3, 0xc4, 0x19, - 0x0a, 0xa1, 0xce, 0x8b, 0x78, 0xd9, 0xa6, 0xeb, 0xd5, 0x27, 0x69, 0x3d, 0x30, 0x10, 0x45, 0x78, - 0xd2, 0x17, 0x34, 0x45, 0x05, 0x9d, 0xea, 0xd0, 0xd3, 0x16, 0xc3, 0x7c, 0x04, 0x72, 0x5a, 0x5d, - 0xc7, 0x86, 0x5b, 0x71, 0x5c, 0x1b, 0xab, 0x0d, 0xdd, 0xa8, 0xd1, 0x50, 0x93, 0x2e, 0x0e, 0xec, - 0xaa, 0x75, 0x07, 0x2b, 0x63, 0xac, 0x7a, 0x53, 0xd4, 0x12, 0x0a, 0x6a, 0x40, 0x76, 0x80, 0x62, - 0x30, 0x44, 0xc1, 0xaa, 0x3d, 0x0a, 0xf9, 0xc3, 0x19, 0x18, 0x0e, 0x24, 0xe0, 0xe8, 0x2e, 0xc8, - 0xbe, 0xa8, 0x5e, 0x51, 0x2b, 0x62, 0x51, 0xc5, 0x34, 0x31, 0x4c, 0x60, 0x1b, 0x7c, 0x61, 0xf5, - 0x08, 0x4c, 0x52, 0x14, 0xb3, 0xe9, 0x62, 0xbb, 0xa2, 0xd5, 0x55, 0xc7, 0xa1, 0x4a, 0x4b, 0x53, - 0x54, 0x44, 0xea, 0xd6, 0x49, 0xd5, 0x82, 0xa8, 0x41, 0x67, 0x61, 0x82, 0x52, 0x34, 0x9a, 0x75, - 0x57, 0xb7, 0xea, 0xb8, 0x42, 0x96, 0x79, 0x0e, 0x0d, 0x39, 0x9e, 0x64, 0xe3, 0x04, 0x63, 0x95, - 0x23, 0x10, 0x89, 0x1c, 0xb4, 0x08, 0x77, 0x52, 0xb2, 0x1a, 0x36, 0xb0, 0xad, 0xba, 0xb8, 0x82, - 0x5f, 0x6a, 0xaa, 0x75, 0xa7, 0xa2, 0x1a, 0xd5, 0xca, 0x9e, 0xea, 0xec, 0xe5, 0x27, 0x09, 0x83, - 0x52, 0x22, 0x2f, 0x29, 0x27, 0x08, 0xe2, 0x12, 0xc7, 0x2b, 0x53, 0xb4, 0x79, 0xa3, 0x7a, 0x51, - 0x75, 0xf6, 0x50, 0x11, 0x8e, 0x51, 0x2e, 0x8e, 0x6b, 0xeb, 0x46, 0xad, 0xa2, 0xed, 0x61, 0x6d, - 0xbf, 0xd2, 0x74, 0x77, 0x9f, 0xcc, 0xdf, 0x11, 0x6c, 0x9f, 0x4a, 0xb8, 0x49, 0x71, 0x16, 0x08, - 0xca, 0xb6, 0xbb, 0xfb, 0x24, 0xda, 0x84, 0x2c, 0x19, 0x8c, 0x86, 0xfe, 0x32, 0xae, 0xec, 0x9a, - 0x36, 0x8d, 0xa1, 0xa3, 0x6d, 0x5c, 0x53, 0x40, 0x83, 0xb3, 0xeb, 0x9c, 0x60, 0xd5, 0xac, 0xe2, - 0xe2, 0xc0, 0xe6, 0x46, 0xb9, 0xbc, 0xa8, 0x0c, 0x0b, 0x2e, 0x17, 0x4c, 0x9b, 0x18, 0x54, 0xcd, - 0xf4, 0x14, 0x3c, 0xcc, 0x0c, 0xaa, 0x66, 0x0a, 0xf5, 0x9e, 0x85, 0x09, 0x4d, 0x63, 0x7d, 0xd6, - 0xb5, 0x0a, 0x5f, 0x8c, 0x39, 0xf9, 0x5c, 0x48, 0x59, 0x9a, 0xb6, 0xc4, 0x10, 0xb8, 0x8d, 0x3b, - 0xe8, 0x29, 0x38, 0xea, 0x2b, 0x2b, 0x48, 0x38, 0xde, 0xd2, 0xcb, 0x28, 0xe9, 0x59, 0x98, 0xb0, - 0x0e, 0x5a, 0x09, 0x51, 0xa8, 0x45, 0xeb, 0x20, 0x4a, 0xf6, 0x04, 0x4c, 0x5a, 0x7b, 0x56, 0x2b, - 0xdd, 0xe9, 0x20, 0x1d, 0xb2, 0xf6, 0xac, 0x28, 0xe1, 0xbd, 0x74, 0x65, 0x6e, 0x63, 0x4d, 0x75, - 0x71, 0x35, 0x7f, 0x3c, 0x88, 0x1e, 0xa8, 0x40, 0xb3, 0x90, 0xd3, 0xb4, 0x0a, 0x36, 0xd4, 0x9d, - 0x3a, 0xae, 0xa8, 0x36, 0x36, 0x54, 0x27, 0x3f, 0x4d, 0x91, 0x53, 0xae, 0xdd, 0xc4, 0xca, 0xa8, - 0xa6, 0x95, 0x69, 0xe5, 0x3c, 0xad, 0x43, 0xa7, 0x61, 0xdc, 0xdc, 0x79, 0x51, 0x63, 0x16, 0x59, - 0xb1, 0x6c, 0xbc, 0xab, 0x5f, 0xcb, 0xdf, 0x43, 0xd5, 0x3b, 0x46, 0x2a, 0xa8, 0x3d, 0x6e, 0x50, - 0x30, 0x7a, 0x00, 0x72, 0x9a, 0xb3, 0xa7, 0xda, 0x16, 0x75, 0xc9, 0x8e, 0xa5, 0x6a, 0x38, 0x7f, - 0x2f, 0x43, 0x65, 0xf0, 0x35, 0x01, 0x26, 0x33, 0xc2, 0xb9, 0xaa, 0xef, 0xba, 0x82, 0xe3, 0xfd, - 0x6c, 0x46, 0x50, 0x18, 0xe7, 0x76, 0x0a, 0x72, 0x44, 0x13, 0xa1, 0x86, 0x4f, 0x51, 0xb4, 0x51, - 0x6b, 0xcf, 0x0a, 0xb6, 0x7b, 0x37, 0x8c, 0x10, 0x4c, 0xbf, 0xd1, 0x07, 0x58, 0xe2, 0x66, 0xed, - 0x05, 0x5a, 0x7c, 0x1c, 0x8e, 0x11, 0xa4, 0x06, 0x76, 0xd5, 0xaa, 0xea, 0xaa, 0x01, 0xec, 0x87, - 0x28, 0x36, 0x51, 0xfb, 0x2a, 0xaf, 0x0c, 0xc9, 0x69, 0x37, 0x77, 0x0e, 0x3c, 0xc3, 0x7a, 0x98, - 0xc9, 0x49, 0x60, 0xc2, 0xb4, 0x6e, 0x5b, 0x72, 0x2e, 0x17, 0x21, 0x1b, 0xb4, 0x7b, 0x94, 0x01, - 0x66, 0xf9, 0x39, 0x89, 0x24, 0x41, 0x0b, 0xeb, 0x8b, 0x24, 0x7d, 0x79, 0x6b, 0x39, 0x97, 0x20, - 0x69, 0xd4, 0xca, 0xf2, 0x56, 0xb9, 0xa2, 0x6c, 0xaf, 0x6d, 0x2d, 0xaf, 0x96, 0x73, 0xc9, 0x40, - 0x62, 0xff, 0x6c, 0x2a, 0x7d, 0x5f, 0xee, 0x7e, 0xf9, 0x1b, 0x09, 0x18, 0x0d, 0xaf, 0xd4, 0xd0, - 0x9b, 0xe0, 0xb8, 0xd8, 0x56, 0x71, 0xb0, 0x5b, 0xb9, 0xaa, 0xdb, 0x74, 0x42, 0x36, 0x54, 0x16, - 0x1c, 0x3d, 0xfb, 0x99, 0xe4, 0x58, 0x9b, 0xd8, 0x7d, 0x5e, 0xb7, 0xc9, 0x74, 0x6b, 0xa8, 0x2e, - 0x5a, 0x81, 0x69, 0xc3, 0xac, 0x38, 0xae, 0x6a, 0x54, 0x55, 0xbb, 0x5a, 0xf1, 0x37, 0xb4, 0x2a, - 0xaa, 0xa6, 0x61, 0xc7, 0x31, 0x59, 0x20, 0xf4, 0xb8, 0x9c, 0x34, 0xcc, 0x4d, 0x8e, 0xec, 0x47, - 0x88, 0x79, 0x8e, 0x1a, 0x31, 0xdf, 0x64, 0x27, 0xf3, 0xbd, 0x03, 0x32, 0x0d, 0xd5, 0xaa, 0x60, - 0xc3, 0xb5, 0x0f, 0x68, 0x7e, 0x9e, 0x56, 0xd2, 0x0d, 0xd5, 0x2a, 0x93, 0xf2, 0x0f, 0x65, 0x99, - 0xf4, 0x6c, 0x2a, 0x9d, 0xce, 0x65, 0x9e, 0x4d, 0xa5, 0x33, 0x39, 0x90, 0x5f, 0x4f, 0x42, 0x36, - 0x98, 0xaf, 0x93, 0xe5, 0x8f, 0x46, 0x23, 0x96, 0x44, 0x7d, 0xda, 0xdd, 0x5d, 0xb3, 0xfb, 0xd9, - 0x05, 0x12, 0xca, 0x8a, 0x83, 0x2c, 0x39, 0x56, 0x18, 0x25, 0x49, 0x23, 0x88, 0xb1, 0x61, 0x96, - 0x8c, 0xa4, 0x15, 0x5e, 0x42, 0x4b, 0x30, 0xf8, 0xa2, 0x43, 0x79, 0x0f, 0x52, 0xde, 0xf7, 0x74, - 0xe7, 0xfd, 0xec, 0x26, 0x65, 0x9e, 0x79, 0x76, 0xb3, 0xb2, 0xb6, 0xae, 0xac, 0xce, 0xaf, 0x28, - 0x9c, 0x1c, 0x9d, 0x80, 0x54, 0x5d, 0x7d, 0xf9, 0x20, 0x1c, 0xf4, 0x28, 0xa8, 0xd7, 0x41, 0x38, - 0x01, 0xa9, 0xab, 0x58, 0xdd, 0x0f, 0x87, 0x1a, 0x0a, 0xba, 0x8d, 0x93, 0x61, 0x0e, 0x06, 0xa8, - 0xbe, 0x10, 0x00, 0xd7, 0x58, 0xee, 0x08, 0x4a, 0x43, 0x6a, 0x61, 0x5d, 0x21, 0x13, 0x22, 0x07, - 0x59, 0x06, 0xad, 0x6c, 0x2c, 0x97, 0x17, 0xca, 0xb9, 0x84, 0x7c, 0x16, 0x06, 0x99, 0x12, 0xc8, - 0x64, 0xf1, 0xd4, 0x90, 0x3b, 0xc2, 0x8b, 0x9c, 0x87, 0x24, 0x6a, 0xb7, 0x57, 0x4b, 0x65, 0x25, - 0x97, 0x08, 0x0f, 0x75, 0x2a, 0x37, 0x20, 0x3b, 0x90, 0x0d, 0xe6, 0xe1, 0x3f, 0x9c, 0xc5, 0xf8, - 0x97, 0x24, 0x18, 0x0e, 0xe4, 0xd5, 0x24, 0x21, 0x52, 0xeb, 0x75, 0xf3, 0x6a, 0x45, 0xad, 0xeb, - 0xaa, 0xc3, 0x4d, 0x03, 0x28, 0x68, 0x9e, 0x40, 0x7a, 0x1d, 0xba, 0x1f, 0xd2, 0x14, 0x19, 0xc8, - 0x0d, 0xca, 0x9f, 0x90, 0x20, 0x17, 0x4d, 0x6c, 0x23, 0x62, 0x4a, 0x3f, 0x4a, 0x31, 0xe5, 0x8f, - 0x4b, 0x30, 0x1a, 0xce, 0x66, 0x23, 0xe2, 0xdd, 0xf5, 0x23, 0x15, 0xef, 0x0f, 0x12, 0x30, 0x12, - 0xca, 0x61, 0x7b, 0x95, 0xee, 0x25, 0x18, 0xd7, 0xab, 0xb8, 0x61, 0x99, 0x2e, 0x36, 0xb4, 0x83, - 0x4a, 0x1d, 0x5f, 0xc1, 0xf5, 0xbc, 0x4c, 0x9d, 0xc6, 0x5c, 0xf7, 0x2c, 0x79, 0x76, 0xd9, 0xa7, - 0x5b, 0x21, 0x64, 0xc5, 0x89, 0xe5, 0xc5, 0xf2, 0xea, 0xc6, 0xfa, 0x56, 0x79, 0x6d, 0xe1, 0x2d, - 0x95, 0xed, 0xb5, 0x4b, 0x6b, 0xeb, 0xcf, 0xaf, 0x29, 0x39, 0x3d, 0x82, 0x76, 0x1b, 0xa7, 0xfd, - 0x06, 0xe4, 0xa2, 0x42, 0xa1, 0xe3, 0xd0, 0x4e, 0xac, 0xdc, 0x11, 0x34, 0x01, 0x63, 0x6b, 0xeb, - 0x95, 0xcd, 0xe5, 0xc5, 0x72, 0xa5, 0x7c, 0xe1, 0x42, 0x79, 0x61, 0x6b, 0x93, 0xed, 0x7b, 0x78, - 0xd8, 0x5b, 0xa1, 0x09, 0x2e, 0xbf, 0x96, 0x84, 0x89, 0x36, 0x92, 0xa0, 0x79, 0xbe, 0x62, 0x61, - 0x8b, 0xa8, 0x87, 0x7b, 0x91, 0x7e, 0x96, 0xe4, 0x0c, 0x1b, 0xaa, 0xed, 0xf2, 0x05, 0xce, 0x03, - 0x40, 0xb4, 0x64, 0xb8, 0xfa, 0xae, 0x8e, 0x6d, 0xbe, 0x9f, 0xc4, 0x96, 0x31, 0x63, 0x3e, 0x9c, - 0x6d, 0x29, 0x3d, 0x04, 0xc8, 0x32, 0x1d, 0xdd, 0xd5, 0xaf, 0xe0, 0x8a, 0x6e, 0x88, 0xcd, 0x27, - 0xb2, 0xac, 0x49, 0x29, 0x39, 0x51, 0xb3, 0x6c, 0xb8, 0x1e, 0xb6, 0x81, 0x6b, 0x6a, 0x04, 0x9b, - 0x38, 0xf3, 0xa4, 0x92, 0x13, 0x35, 0x1e, 0xf6, 0x5d, 0x90, 0xad, 0x9a, 0x4d, 0x92, 0xeb, 0x31, - 0x3c, 0x12, 0x3b, 0x24, 0x65, 0x98, 0xc1, 0x3c, 0x14, 0x9e, 0xc5, 0xfb, 0xbb, 0x5e, 0x59, 0x65, - 0x98, 0xc1, 0x18, 0xca, 0xfd, 0x30, 0xa6, 0xd6, 0x6a, 0x36, 0x61, 0x2e, 0x18, 0xb1, 0x75, 0xc9, - 0xa8, 0x07, 0xa6, 0x88, 0x85, 0x67, 0x21, 0x2d, 0xf4, 0x40, 0x42, 0x35, 0xd1, 0x44, 0xc5, 0x62, - 0x8b, 0xed, 0xc4, 0xa9, 0x8c, 0x92, 0x36, 0x44, 0xe5, 0x5d, 0x90, 0xd5, 0x9d, 0x8a, 0xbf, 0x89, - 0x9f, 0x98, 0x49, 0x9c, 0x4a, 0x2b, 0xc3, 0xba, 0xe3, 0x6d, 0x80, 0xca, 0x9f, 0x49, 0xc0, 0x68, - 0xf8, 0x10, 0x02, 0x2d, 0x42, 0xba, 0x6e, 0x6a, 0x2a, 0x35, 0x2d, 0x76, 0x02, 0x76, 0x2a, 0xe6, - 0xdc, 0x62, 0x76, 0x85, 0xe3, 0x2b, 0x1e, 0x65, 0xe1, 0xb7, 0x25, 0x48, 0x0b, 0x30, 0x3a, 0x06, - 0x29, 0x4b, 0x75, 0xf7, 0x28, 0xbb, 0x81, 0x52, 0x22, 0x27, 0x29, 0xb4, 0x4c, 0xe0, 0x8e, 0xa5, - 0x1a, 0xd4, 0x04, 0x38, 0x9c, 0x94, 0xc9, 0xb8, 0xd6, 0xb1, 0x5a, 0xa5, 0x8b, 0x1e, 0xb3, 0xd1, - 0xc0, 0x86, 0xeb, 0x88, 0x71, 0xe5, 0xf0, 0x05, 0x0e, 0x46, 0x0f, 0xc2, 0xb8, 0x6b, 0xab, 0x7a, - 0x3d, 0x84, 0x9b, 0xa2, 0xb8, 0x39, 0x51, 0xe1, 0x21, 0x17, 0xe1, 0x84, 0xe0, 0x5b, 0xc5, 0xae, - 0xaa, 0xed, 0xe1, 0xaa, 0x4f, 0x34, 0x48, 0x37, 0x37, 0x8e, 0x73, 0x84, 0x45, 0x5e, 0x2f, 0x68, - 0xe5, 0x6f, 0x48, 0x30, 0x2e, 0x96, 0x69, 0x55, 0x4f, 0x59, 0xab, 0x00, 0xaa, 0x61, 0x98, 0x6e, - 0x50, 0x5d, 0xad, 0xa6, 0xdc, 0x42, 0x37, 0x3b, 0xef, 0x11, 0x29, 0x01, 0x06, 0x85, 0x06, 0x80, - 0x5f, 0xd3, 0x51, 0x6d, 0xd3, 0x30, 0xcc, 0x4f, 0x98, 0xe8, 0x31, 0x25, 0x5b, 0xd8, 0x03, 0x03, - 0x91, 0xf5, 0x1c, 0x9a, 0x84, 0x81, 0x1d, 0x5c, 0xd3, 0x0d, 0xbe, 0x6f, 0xcc, 0x0a, 0x62, 0xfb, - 0x25, 0xe5, 0x6d, 0xbf, 0x94, 0x3e, 0x28, 0xc1, 0x84, 0x66, 0x36, 0xa2, 0xf2, 0x96, 0x72, 0x91, - 0xdd, 0x05, 0xe7, 0xa2, 0xf4, 0xd6, 0xa7, 0x6b, 0xba, 0xbb, 0xd7, 0xdc, 0x99, 0xd5, 0xcc, 0xc6, - 0x5c, 0xcd, 0xac, 0xab, 0x46, 0xcd, 0x3f, 0x67, 0xa5, 0x3f, 0xb4, 0x87, 0x6b, 0xd8, 0x78, 0xb8, - 0x66, 0x06, 0x4e, 0x5d, 0xcf, 0xfb, 0x3f, 0xff, 0x4c, 0x92, 0x7e, 0x21, 0x91, 0x5c, 0xda, 0x28, - 0x7d, 0x36, 0x51, 0x58, 0x62, 0xcd, 0x6d, 0x08, 0xf5, 0x28, 0x78, 0xb7, 0x8e, 0x35, 0xd2, 0x65, - 0xf8, 0xf6, 0x83, 0x30, 0x59, 0x33, 0x6b, 0x26, 0xe5, 0x38, 0x47, 0x7e, 0xf1, 0x93, 0xdb, 0x8c, - 0x07, 0x2d, 0xc4, 0x1e, 0xf3, 0x16, 0xd7, 0x60, 0x82, 0x23, 0x57, 0xe8, 0xd1, 0x11, 0x5b, 0xd8, - 0xa0, 0xae, 0xbb, 0x6a, 0xf9, 0x5f, 0xfb, 0x16, 0x0d, 0xe8, 0xca, 0x38, 0x27, 0x25, 0x75, 0x6c, - 0xed, 0x53, 0x54, 0xe0, 0x68, 0x88, 0x1f, 0x9b, 0xb6, 0xd8, 0x8e, 0xe1, 0xf8, 0x9b, 0x9c, 0xe3, - 0x44, 0x80, 0xe3, 0x26, 0x27, 0x2d, 0x2e, 0xc0, 0x48, 0x3f, 0xbc, 0xfe, 0x25, 0xe7, 0x95, 0xc5, - 0x41, 0x26, 0x4b, 0x30, 0x46, 0x99, 0x68, 0x4d, 0xc7, 0x35, 0x1b, 0xd4, 0x27, 0x76, 0x67, 0xf3, - 0x5b, 0xdf, 0x62, 0xf3, 0x68, 0x94, 0x90, 0x2d, 0x78, 0x54, 0xc5, 0x22, 0xd0, 0xd3, 0xb2, 0x2a, - 0xd6, 0xea, 0x31, 0x1c, 0xbe, 0xc2, 0x05, 0xf1, 0xf0, 0x8b, 0x97, 0x61, 0x92, 0xfc, 0xa6, 0x2e, - 0x2b, 0x28, 0x49, 0xfc, 0x16, 0x5c, 0xfe, 0x1b, 0xef, 0x61, 0x53, 0x75, 0xc2, 0x63, 0x10, 0x90, - 0x29, 0x30, 0x8a, 0x35, 0xec, 0xba, 0xd8, 0x76, 0x2a, 0x6a, 0xbd, 0x9d, 0x78, 0x81, 0x3d, 0x8c, - 0xfc, 0xc7, 0xbe, 0x13, 0x1e, 0xc5, 0x25, 0x46, 0x39, 0x5f, 0xaf, 0x17, 0xb7, 0xe1, 0x78, 0x1b, - 0xab, 0xe8, 0x81, 0xe7, 0x6b, 0x9c, 0xe7, 0x64, 0x8b, 0x65, 0x10, 0xb6, 0x1b, 0x20, 0xe0, 0xde, - 0x58, 0xf6, 0xc0, 0xf3, 0xe7, 0x39, 0x4f, 0xc4, 0x69, 0xc5, 0x90, 0x12, 0x8e, 0xcf, 0xc2, 0xf8, - 0x15, 0x6c, 0xef, 0x98, 0x0e, 0xdf, 0x37, 0xea, 0x81, 0xdd, 0xc7, 0x39, 0xbb, 0x31, 0x4e, 0x48, - 0x37, 0x92, 0x08, 0xaf, 0xa7, 0x20, 0xbd, 0xab, 0x6a, 0xb8, 0x07, 0x16, 0xd7, 0x39, 0x8b, 0x21, - 0x82, 0x4f, 0x48, 0xe7, 0x21, 0x5b, 0x33, 0x79, 0xd4, 0x8a, 0x27, 0xff, 0x04, 0x27, 0x1f, 0x16, - 0x34, 0x9c, 0x85, 0x65, 0x5a, 0xcd, 0x3a, 0x09, 0x69, 0xf1, 0x2c, 0xfe, 0xa6, 0x60, 0x21, 0x68, - 0x38, 0x8b, 0x3e, 0xd4, 0xfa, 0x49, 0xc1, 0xc2, 0x09, 0xe8, 0xf3, 0x19, 0x18, 0x36, 0x8d, 0xfa, - 0x81, 0x69, 0xf4, 0x22, 0xc4, 0xa7, 0x38, 0x07, 0xe0, 0x24, 0x84, 0xc1, 0x79, 0xc8, 0xf4, 0x3a, - 0x10, 0x7f, 0xeb, 0x3b, 0x62, 0x7a, 0x88, 0x11, 0x58, 0x82, 0x31, 0xe1, 0xa0, 0x74, 0xd3, 0xe8, - 0x81, 0xc5, 0xdf, 0xe6, 0x2c, 0x46, 0x03, 0x64, 0xbc, 0x1b, 0x2e, 0x76, 0xdc, 0x1a, 0xee, 0x85, - 0xc9, 0x67, 0x44, 0x37, 0x38, 0x09, 0x57, 0xe5, 0x0e, 0x36, 0xb4, 0xbd, 0xde, 0x38, 0xfc, 0x92, - 0x50, 0xa5, 0xa0, 0x21, 0x2c, 0x16, 0x60, 0xa4, 0xa1, 0xda, 0xce, 0x9e, 0x5a, 0xef, 0x69, 0x38, - 0xfe, 0x0e, 0xe7, 0x91, 0xf5, 0x88, 0xb8, 0x46, 0x9a, 0x46, 0x3f, 0x6c, 0x3e, 0x2b, 0x34, 0x12, - 0x20, 0xe3, 0x53, 0xcf, 0x71, 0xe9, 0x26, 0x5b, 0x3f, 0xdc, 0x7e, 0x59, 0x4c, 0x3d, 0x46, 0xbb, - 0x1a, 0xe4, 0x78, 0x1e, 0x32, 0x8e, 0xfe, 0x72, 0x4f, 0x6c, 0x3e, 0x27, 0x46, 0x9a, 0x12, 0x10, - 0xe2, 0xb7, 0xc0, 0x89, 0xb6, 0x61, 0xa2, 0x07, 0x66, 0x7f, 0x97, 0x33, 0x3b, 0xd6, 0x26, 0x54, - 0x70, 0x97, 0xd0, 0x2f, 0xcb, 0xbf, 0x27, 0x5c, 0x02, 0x8e, 0xf0, 0xda, 0x20, 0xeb, 0x08, 0x47, - 0xdd, 0xed, 0x4f, 0x6b, 0xbf, 0x22, 0xb4, 0xc6, 0x68, 0x43, 0x5a, 0xdb, 0x82, 0x63, 0x9c, 0x63, - 0x7f, 0xe3, 0xfa, 0xab, 0xc2, 0xb1, 0x32, 0xea, 0xed, 0xf0, 0xe8, 0xbe, 0x0d, 0x0a, 0x9e, 0x3a, - 0x45, 0xc2, 0xea, 0x54, 0x1a, 0xaa, 0xd5, 0x03, 0xe7, 0x5f, 0xe3, 0x9c, 0x85, 0xc7, 0xf7, 0x32, - 0x5e, 0x67, 0x55, 0xb5, 0x08, 0xf3, 0x17, 0x20, 0x2f, 0x98, 0x37, 0x0d, 0x1b, 0x6b, 0x66, 0xcd, - 0xd0, 0x5f, 0xc6, 0xd5, 0x1e, 0x58, 0xff, 0x7a, 0x64, 0xa8, 0xb6, 0x03, 0xe4, 0x84, 0xf3, 0x32, - 0xe4, 0xbc, 0x5c, 0xa5, 0xa2, 0x37, 0x2c, 0xd3, 0x76, 0x63, 0x38, 0x7e, 0x5e, 0x8c, 0x94, 0x47, - 0xb7, 0x4c, 0xc9, 0x8a, 0x65, 0x60, 0x27, 0xcf, 0xbd, 0x9a, 0xe4, 0x17, 0x38, 0xa3, 0x11, 0x9f, - 0x8a, 0x3b, 0x0e, 0xcd, 0x6c, 0x58, 0xaa, 0xdd, 0x8b, 0xff, 0xfb, 0xfb, 0xc2, 0x71, 0x70, 0x12, - 0xee, 0x38, 0xdc, 0x03, 0x0b, 0x93, 0x68, 0xdf, 0x03, 0x87, 0x2f, 0x0a, 0xc7, 0x21, 0x68, 0x38, - 0x0b, 0x91, 0x30, 0xf4, 0xc0, 0xe2, 0x1f, 0x08, 0x16, 0x82, 0x86, 0xb0, 0x78, 0xce, 0x0f, 0xb4, - 0x36, 0xae, 0xe9, 0x8e, 0x6b, 0xb3, 0x34, 0xb9, 0x3b, 0xab, 0x7f, 0xf8, 0x9d, 0x70, 0x12, 0xa6, - 0x04, 0x48, 0x89, 0x27, 0xe2, 0xdb, 0xae, 0x74, 0x15, 0x15, 0x2f, 0xd8, 0x6f, 0x08, 0x4f, 0x14, - 0x20, 0x23, 0xb2, 0x05, 0x32, 0x44, 0xa2, 0x76, 0x8d, 0xac, 0x1d, 0x7a, 0x60, 0xf7, 0x8f, 0x22, - 0xc2, 0x6d, 0x0a, 0x5a, 0xc2, 0x33, 0x90, 0xff, 0x34, 0x8d, 0x7d, 0x7c, 0xd0, 0x93, 0x75, 0xfe, - 0xe3, 0x48, 0xfe, 0xb3, 0xcd, 0x28, 0x99, 0x0f, 0x19, 0x8b, 0xe4, 0x53, 0x28, 0xee, 0x9e, 0x51, - 0xfe, 0xa7, 0xbe, 0xc7, 0xfb, 0x1b, 0x4e, 0xa7, 0x8a, 0x2b, 0xc4, 0xc8, 0xc3, 0x49, 0x4f, 0x3c, - 0xb3, 0xf7, 0x7c, 0xcf, 0xb3, 0xf3, 0x50, 0xce, 0x53, 0xbc, 0x00, 0x23, 0xa1, 0x84, 0x27, 0x9e, - 0xd5, 0x7b, 0x39, 0xab, 0x6c, 0x30, 0xdf, 0x29, 0x9e, 0x85, 0x14, 0x49, 0x5e, 0xe2, 0xc9, 0xff, - 0x32, 0x27, 0xa7, 0xe8, 0xc5, 0x37, 0x43, 0x5a, 0x24, 0x2d, 0xf1, 0xa4, 0xef, 0xe3, 0xa4, 0x1e, - 0x09, 0x21, 0x17, 0x09, 0x4b, 0x3c, 0xf9, 0x5f, 0x11, 0xe4, 0x82, 0x84, 0x90, 0xf7, 0xae, 0xc2, - 0x2f, 0xfd, 0x74, 0x8a, 0x07, 0x1d, 0xa1, 0xbb, 0xf3, 0x30, 0xc4, 0x33, 0x95, 0x78, 0xea, 0x0f, - 0xf0, 0xc6, 0x05, 0x45, 0xf1, 0x09, 0x18, 0xe8, 0x51, 0xe1, 0x3f, 0xc3, 0x49, 0x19, 0x7e, 0x71, - 0x01, 0x86, 0x03, 0xd9, 0x49, 0x3c, 0xf9, 0x5f, 0xe3, 0xe4, 0x41, 0x2a, 0x22, 0x3a, 0xcf, 0x4e, - 0xe2, 0x19, 0x7c, 0x50, 0x88, 0xce, 0x29, 0x88, 0xda, 0x44, 0x62, 0x12, 0x4f, 0xfd, 0x21, 0xa1, - 0x75, 0x41, 0x52, 0x7c, 0x06, 0x32, 0x5e, 0xb0, 0x89, 0xa7, 0xff, 0x30, 0xa7, 0xf7, 0x69, 0x88, - 0x06, 0x02, 0xc1, 0x2e, 0x9e, 0xc5, 0x5f, 0x17, 0x1a, 0x08, 0x50, 0x91, 0x69, 0x14, 0x4d, 0x60, - 0xe2, 0x39, 0x7d, 0x44, 0x4c, 0xa3, 0x48, 0xfe, 0x42, 0x46, 0x93, 0xfa, 0xfc, 0x78, 0x16, 0x3f, - 0x2b, 0x46, 0x93, 0xe2, 0x13, 0x31, 0xa2, 0x19, 0x41, 0x3c, 0x8f, 0xbf, 0x21, 0xc4, 0x88, 0x24, - 0x04, 0xc5, 0x0d, 0x40, 0xad, 0xd9, 0x40, 0x3c, 0xbf, 0x8f, 0x72, 0x7e, 0xe3, 0x2d, 0xc9, 0x40, - 0xf1, 0x79, 0x38, 0xd6, 0x3e, 0x13, 0x88, 0xe7, 0xfa, 0xb1, 0xef, 0x45, 0xd6, 0x6e, 0xc1, 0x44, - 0xa0, 0xb8, 0xe5, 0x87, 0x94, 0x60, 0x16, 0x10, 0xcf, 0xf6, 0xb5, 0xef, 0x85, 0x1d, 0x77, 0x30, - 0x09, 0x28, 0xce, 0x03, 0xf8, 0x01, 0x38, 0x9e, 0xd7, 0xc7, 0x39, 0xaf, 0x00, 0x11, 0x99, 0x1a, - 0x3c, 0xfe, 0xc6, 0xd3, 0x5f, 0x17, 0x53, 0x83, 0x53, 0x90, 0xa9, 0x21, 0x42, 0x6f, 0x3c, 0xf5, - 0x27, 0xc4, 0xd4, 0x10, 0x24, 0xc4, 0xb2, 0x03, 0xd1, 0x2d, 0x9e, 0xc3, 0xa7, 0x84, 0x65, 0x07, - 0xa8, 0x8a, 0x6b, 0x30, 0xde, 0x12, 0x10, 0xe3, 0x59, 0xfd, 0x02, 0x67, 0x95, 0x8b, 0xc6, 0xc3, - 0x60, 0xf0, 0xe2, 0xc1, 0x30, 0x9e, 0xdb, 0xa7, 0x23, 0xc1, 0x8b, 0xc7, 0xc2, 0xe2, 0x79, 0x48, - 0x1b, 0xcd, 0x7a, 0x9d, 0x4c, 0x1e, 0xd4, 0xfd, 0x6e, 0x60, 0xfe, 0xbf, 0x7c, 0x9f, 0x6b, 0x47, - 0x10, 0x14, 0xcf, 0xc2, 0x00, 0x6e, 0xec, 0xe0, 0x6a, 0x1c, 0xe5, 0xb7, 0xbf, 0x2f, 0x1c, 0x26, - 0xc1, 0x2e, 0x3e, 0x03, 0xc0, 0xb6, 0x46, 0xe8, 0xf1, 0x60, 0x0c, 0xed, 0x7f, 0xfd, 0x3e, 0xbf, - 0x8c, 0xe3, 0x93, 0xf8, 0x0c, 0xd8, 0xd5, 0x9e, 0xee, 0x0c, 0xbe, 0x13, 0x66, 0x40, 0x47, 0xe4, - 0x29, 0x18, 0x7a, 0xd1, 0x31, 0x0d, 0x57, 0xad, 0xc5, 0x51, 0xff, 0x37, 0x4e, 0x2d, 0xf0, 0x89, - 0xc2, 0x1a, 0xa6, 0x8d, 0x5d, 0xb5, 0xe6, 0xc4, 0xd1, 0xfe, 0x77, 0x4e, 0xeb, 0x11, 0x10, 0x62, - 0x4d, 0x75, 0xdc, 0x5e, 0xfa, 0xfd, 0x47, 0x82, 0x58, 0x10, 0x10, 0xa1, 0xc9, 0xef, 0x7d, 0x7c, - 0x10, 0x47, 0xfb, 0x5d, 0x21, 0x34, 0xc7, 0x2f, 0xbe, 0x19, 0x32, 0xe4, 0x27, 0xbb, 0x61, 0x17, - 0x43, 0xfc, 0xc7, 0x9c, 0xd8, 0xa7, 0x20, 0x2d, 0x3b, 0x6e, 0xd5, 0xd5, 0xe3, 0x95, 0x7d, 0x93, - 0x8f, 0xb4, 0xc0, 0x2f, 0xce, 0xc3, 0xb0, 0xe3, 0x56, 0xab, 0x4d, 0x9e, 0x9f, 0xc6, 0x90, 0xff, - 0xc9, 0xf7, 0xbd, 0x2d, 0x0b, 0x8f, 0x86, 0x8c, 0xf6, 0xd5, 0x7d, 0xd7, 0x32, 0xe9, 0x11, 0x48, - 0x1c, 0x87, 0xef, 0x71, 0x0e, 0x01, 0x92, 0xe2, 0x02, 0x64, 0x49, 0x5f, 0x6c, 0x6c, 0x61, 0x7a, - 0x5e, 0x15, 0xc3, 0xe2, 0x4f, 0xb9, 0x02, 0x42, 0x44, 0xa5, 0x9f, 0xfc, 0xca, 0xeb, 0x53, 0xd2, - 0xd7, 0x5f, 0x9f, 0x92, 0xfe, 0xe0, 0xf5, 0x29, 0xe9, 0x43, 0xdf, 0x9c, 0x3a, 0xf2, 0xf5, 0x6f, - 0x4e, 0x1d, 0xf9, 0xdd, 0x6f, 0x4e, 0x1d, 0x69, 0xbf, 0x6d, 0x0c, 0x4b, 0xe6, 0x92, 0xc9, 0x36, - 0x8c, 0xdf, 0x2a, 0x87, 0xb6, 0x8b, 0x6b, 0xa6, 0xbf, 0x5b, 0xeb, 0x2d, 0x72, 0xe0, 0x4f, 0x25, - 0xb2, 0x60, 0x0e, 0xef, 0xe5, 0xaa, 0xc6, 0x41, 0x87, 0xb7, 0x3a, 0x85, 0xb6, 0x1b, 0xc3, 0xf2, - 0x9b, 0x20, 0x39, 0x6f, 0x1c, 0xa0, 0x13, 0xcc, 0xe7, 0x55, 0x9a, 0x76, 0x9d, 0xdf, 0xfc, 0x1a, - 0x22, 0xe5, 0x6d, 0xbb, 0x8e, 0x26, 0xfd, 0xeb, 0x99, 0xd2, 0xa9, 0x2c, 0xbf, 0x73, 0x59, 0x4c, - 0x7d, 0xf7, 0x53, 0xd3, 0x47, 0x4a, 0xfb, 0xd1, 0x1e, 0x7e, 0x29, 0xb6, 0x97, 0xe9, 0x79, 0xe3, - 0x80, 0x76, 0x72, 0x43, 0x7a, 0xeb, 0x00, 0x69, 0xc3, 0x11, 0x1b, 0xdb, 0x53, 0xd1, 0x8d, 0xed, - 0xe7, 0x71, 0xbd, 0x7e, 0xc9, 0x30, 0xaf, 0x1a, 0x5b, 0x04, 0x6d, 0x67, 0x90, 0x5d, 0x23, 0x86, - 0xbf, 0x9a, 0x80, 0xa9, 0x96, 0x3d, 0x6c, 0x3e, 0xf2, 0x9d, 0x1e, 0x2a, 0x15, 0x21, 0xbd, 0x28, - 0x0c, 0x2a, 0x0f, 0x43, 0x0e, 0xd6, 0x4c, 0xa3, 0xea, 0xd0, 0xae, 0x26, 0x15, 0x51, 0x24, 0x5d, - 0x35, 0x54, 0xc3, 0x74, 0xf8, 0xed, 0x48, 0x56, 0x28, 0xfd, 0xac, 0xd4, 0xdf, 0x38, 0x8e, 0x88, - 0x96, 0x44, 0x37, 0x4f, 0x77, 0xdb, 0xfb, 0xa7, 0x2a, 0xf0, 0xe4, 0x0f, 0xec, 0xf3, 0xf7, 0xaa, - 0x8e, 0x0f, 0x25, 0x60, 0x3a, 0xaa, 0x0e, 0x32, 0x8f, 0x1c, 0x57, 0x6d, 0x58, 0x9d, 0xf4, 0x71, - 0x1e, 0x32, 0x5b, 0x02, 0xa7, 0x6f, 0x85, 0xfc, 0x5c, 0x9f, 0x0a, 0x19, 0xf5, 0x9a, 0x12, 0x1a, - 0x79, 0x30, 0x5e, 0x23, 0x5e, 0x17, 0x0e, 0xa1, 0x92, 0x77, 0x27, 0xe1, 0x84, 0x66, 0x3a, 0x0d, - 0xd3, 0xa9, 0x30, 0x83, 0x67, 0x05, 0xae, 0x8c, 0x6c, 0xb0, 0xaa, 0x87, 0xe3, 0x90, 0x8b, 0x30, - 0x4a, 0x9d, 0x02, 0xdd, 0x08, 0xa6, 0x7e, 0x38, 0x36, 0x74, 0x7e, 0xf5, 0xdf, 0x0e, 0xd0, 0x49, - 0x34, 0xe2, 0x11, 0xd2, 0x9b, 0x2e, 0x5b, 0x30, 0xa9, 0x37, 0xac, 0x3a, 0xa6, 0x47, 0x62, 0x15, - 0xaf, 0x2e, 0x9e, 0xdf, 0xd7, 0x38, 0xbf, 0x09, 0x9f, 0x7c, 0x59, 0x50, 0x17, 0x57, 0x60, 0x5c, - 0xd5, 0x34, 0x6c, 0x85, 0x58, 0xc6, 0x38, 0x2c, 0x21, 0x60, 0x8e, 0x53, 0x7a, 0xdc, 0x4a, 0xcf, - 0x74, 0x1a, 0xdb, 0xb7, 0xde, 0x1b, 0x18, 0x34, 0x1b, 0xd7, 0xb0, 0xf1, 0xb0, 0x81, 0xdd, 0xab, - 0xa6, 0xbd, 0xcf, 0xd5, 0xfb, 0x30, 0x6b, 0x4a, 0x0c, 0xc2, 0x7b, 0x93, 0x30, 0xc5, 0x2a, 0xe6, - 0x76, 0x54, 0x07, 0xcf, 0x5d, 0x79, 0x74, 0x07, 0xbb, 0xea, 0xa3, 0x73, 0x9a, 0xa9, 0x8b, 0x69, - 0x3a, 0xc1, 0xc7, 0x85, 0xd4, 0xcf, 0xf2, 0xfa, 0x0e, 0x7e, 0x6a, 0x09, 0x52, 0x0b, 0xa6, 0x6e, - 0x10, 0x8b, 0xac, 0x62, 0xc3, 0x6c, 0x70, 0x2f, 0xc5, 0x0a, 0xe8, 0x6e, 0x18, 0x54, 0x1b, 0x66, - 0xd3, 0x70, 0xd9, 0x69, 0x5e, 0x69, 0xf8, 0x2b, 0x37, 0xa6, 0x8f, 0xfc, 0xde, 0x8d, 0xe9, 0xe4, - 0xb2, 0xe1, 0x2a, 0xbc, 0xaa, 0x98, 0x7a, 0xe3, 0x93, 0xd3, 0x92, 0xfc, 0x2c, 0x0c, 0x2d, 0x62, - 0xed, 0x30, 0xbc, 0x16, 0xb1, 0x16, 0xe1, 0xf5, 0x00, 0xa4, 0x97, 0x0d, 0x97, 0xdd, 0x20, 0xbe, - 0x13, 0x92, 0xba, 0xc1, 0x2e, 0xa5, 0x45, 0xda, 0x27, 0x70, 0x82, 0xba, 0x88, 0x35, 0x0f, 0xb5, - 0x8a, 0xb5, 0x28, 0x2a, 0x61, 0x4f, 0xe0, 0xa5, 0xc5, 0xdf, 0xfd, 0x4f, 0x53, 0x47, 0x5e, 0x79, - 0x7d, 0xea, 0x48, 0xc7, 0x91, 0x08, 0x46, 0x07, 0xae, 0x62, 0x3e, 0x04, 0x4e, 0x75, 0x7f, 0xce, - 0x0d, 0xcd, 0x85, 0xcf, 0xa6, 0xe0, 0x4e, 0xfa, 0x78, 0xc4, 0x6e, 0xe8, 0x86, 0x3b, 0xa7, 0xd9, - 0x07, 0x96, 0x4b, 0xc3, 0x89, 0xb9, 0xcb, 0x47, 0x61, 0xdc, 0xaf, 0x9e, 0x65, 0xd5, 0x1d, 0xc6, - 0x60, 0x17, 0x06, 0x36, 0x08, 0x1d, 0x51, 0x9c, 0x6b, 0xba, 0x6a, 0x9d, 0xbb, 0x0b, 0x56, 0x20, - 0x50, 0xf6, 0xe0, 0x24, 0xc1, 0xa0, 0xba, 0x78, 0x6b, 0x52, 0xc7, 0xea, 0x2e, 0xbb, 0xb7, 0x9b, - 0xa4, 0x21, 0x24, 0x4d, 0x00, 0xf4, 0x8a, 0xee, 0x24, 0x0c, 0xa8, 0x4d, 0x76, 0xe4, 0x9c, 0x24, - 0xb1, 0x85, 0x16, 0xe4, 0x4b, 0x30, 0xc4, 0x8f, 0xb9, 0x50, 0x0e, 0x92, 0xfb, 0xf8, 0x80, 0xb6, - 0x93, 0x55, 0xc8, 0x4f, 0x34, 0x0b, 0x03, 0x54, 0x78, 0xfe, 0x20, 0x21, 0x3f, 0xdb, 0x22, 0xfd, - 0x2c, 0x15, 0x52, 0x61, 0x68, 0xf2, 0xb3, 0x90, 0x5e, 0x34, 0x1b, 0xba, 0x61, 0x86, 0xb9, 0x65, - 0x18, 0x37, 0x2a, 0xb3, 0xd5, 0xe4, 0x63, 0xad, 0xb0, 0x02, 0x3a, 0x06, 0x83, 0xec, 0x1e, 0x37, - 0x3f, 0x36, 0xe7, 0x25, 0x79, 0x01, 0x86, 0x28, 0xef, 0x75, 0x0b, 0x21, 0xfe, 0x02, 0x88, 0x5f, - 0x18, 0xa7, 0x6e, 0x81, 0xb3, 0x4f, 0xf8, 0xc2, 0x22, 0x48, 0x55, 0x55, 0x57, 0xe5, 0xfd, 0xa6, - 0xbf, 0xe5, 0xa7, 0x21, 0xcd, 0x99, 0x38, 0xe8, 0x0c, 0x24, 0x4d, 0xcb, 0xe1, 0x07, 0xdf, 0x85, - 0x4e, 0x5d, 0x59, 0xb7, 0x4a, 0x29, 0x62, 0x25, 0x0a, 0x41, 0x2e, 0x29, 0x1d, 0xcd, 0xe2, 0xc9, - 0x80, 0x59, 0x04, 0x86, 0x3c, 0xf0, 0x93, 0x0d, 0x69, 0x8b, 0x39, 0x78, 0xc6, 0xf2, 0xa9, 0x04, - 0x4c, 0x05, 0x6a, 0xaf, 0x60, 0x9b, 0xac, 0xf5, 0x98, 0x45, 0x71, 0x6b, 0x41, 0x01, 0x21, 0x79, - 0x7d, 0x07, 0x73, 0x79, 0x33, 0x24, 0xe7, 0x2d, 0x0b, 0x15, 0x20, 0xcd, 0x0e, 0xb8, 0x4d, 0x66, - 0x2f, 0x29, 0xc5, 0x2b, 0x93, 0x3a, 0xc7, 0xdc, 0x75, 0xaf, 0xaa, 0xb6, 0xf7, 0xd4, 0x49, 0x94, - 0xe5, 0xa7, 0x20, 0xb3, 0x60, 0x1a, 0x0e, 0x36, 0x9c, 0x26, 0x0d, 0x44, 0x3b, 0x75, 0x53, 0xdb, - 0xe7, 0x1c, 0x58, 0x81, 0x28, 0x5c, 0xb5, 0x2c, 0x4a, 0x99, 0x52, 0xc8, 0x4f, 0x36, 0x2f, 0x4b, - 0x9b, 0x1d, 0x55, 0xf4, 0x54, 0xff, 0x2a, 0xe2, 0x9d, 0xf4, 0x74, 0xf4, 0xbf, 0x25, 0x38, 0xd9, - 0x3a, 0xa1, 0xf6, 0xf1, 0x81, 0xd3, 0xef, 0x7c, 0x7a, 0x01, 0x32, 0x1b, 0xf4, 0xbd, 0xf1, 0x25, - 0x7c, 0x80, 0x0a, 0x30, 0x84, 0xab, 0x67, 0xce, 0x9e, 0x7d, 0xf4, 0x29, 0x66, 0xed, 0x17, 0x8f, - 0x28, 0x02, 0x80, 0xa6, 0x20, 0xe3, 0x60, 0xcd, 0x3a, 0x73, 0xf6, 0xdc, 0xfe, 0xa3, 0xcc, 0xbc, - 0x2e, 0x1e, 0x51, 0x7c, 0x50, 0x31, 0x4d, 0x7a, 0xfd, 0xc6, 0xa7, 0xa6, 0xa5, 0xd2, 0x00, 0x24, - 0x9d, 0x66, 0xe3, 0xb6, 0xda, 0xc8, 0x6b, 0x03, 0x30, 0x13, 0xa4, 0xa4, 0xd1, 0xfa, 0x8a, 0x5a, - 0xd7, 0xab, 0xaa, 0xff, 0x52, 0x3c, 0x17, 0xd0, 0x01, 0xc5, 0x68, 0xaf, 0x82, 0x42, 0x57, 0x4d, - 0xca, 0xbf, 0x2e, 0x41, 0xf6, 0xb2, 0xe0, 0xbc, 0x89, 0x5d, 0x74, 0x1e, 0xc0, 0x6b, 0x49, 0x4c, - 0x9b, 0x3b, 0x66, 0xa3, 0x6d, 0xcd, 0x7a, 0x34, 0x4a, 0x00, 0x1d, 0x3d, 0x41, 0x0d, 0xd1, 0x32, - 0x1d, 0xfe, 0xfc, 0x25, 0x86, 0xd4, 0x43, 0x46, 0x0f, 0x01, 0xa2, 0x1e, 0xae, 0x72, 0xc5, 0x74, - 0x75, 0xa3, 0x56, 0xb1, 0xcc, 0xab, 0xfc, 0x51, 0x61, 0x52, 0xc9, 0xd1, 0x9a, 0xcb, 0xb4, 0x62, - 0x83, 0xc0, 0x89, 0xd0, 0x19, 0x8f, 0x0b, 0xc9, 0xad, 0xd4, 0x6a, 0xd5, 0xc6, 0x8e, 0xc3, 0x9d, - 0x98, 0x28, 0xa2, 0xf3, 0x30, 0x64, 0x35, 0x77, 0x2a, 0xc2, 0x63, 0x0c, 0x9f, 0x39, 0xd9, 0x6e, - 0xfe, 0x0b, 0xfb, 0xe0, 0x1e, 0x60, 0xd0, 0x6a, 0xee, 0x10, 0x6b, 0xb9, 0x0b, 0xb2, 0x6d, 0x84, - 0x19, 0xbe, 0xe2, 0xcb, 0x41, 0x9f, 0xb9, 0xf3, 0x1e, 0x54, 0x2c, 0x5b, 0x37, 0x6d, 0xdd, 0x3d, - 0xa0, 0xb7, 0x57, 0x92, 0x4a, 0x4e, 0x54, 0x6c, 0x70, 0xb8, 0xbc, 0x0f, 0x63, 0x9b, 0x34, 0xb7, - 0xf0, 0x25, 0x3f, 0xeb, 0xcb, 0x27, 0xc5, 0xcb, 0xd7, 0x51, 0xb2, 0x44, 0x8b, 0x64, 0xa5, 0xe7, - 0x3a, 0x5a, 0xe7, 0x13, 0xfd, 0x5b, 0x67, 0x38, 0xda, 0xfd, 0xd1, 0x89, 0xd0, 0xe4, 0xe4, 0xa9, - 0x64, 0xc0, 0x7d, 0xf5, 0x6a, 0x98, 0x71, 0x29, 0x75, 0xa1, 0x7b, 0x50, 0x2d, 0xc4, 0xb8, 0xd1, - 0x42, 0xec, 0x14, 0x92, 0x9f, 0x82, 0x91, 0x0d, 0xd5, 0x76, 0x37, 0xb1, 0x7b, 0x11, 0xab, 0x55, - 0x6c, 0x87, 0xa3, 0xee, 0x88, 0x88, 0xba, 0x08, 0x52, 0x34, 0xb4, 0xb2, 0xa8, 0x43, 0x7f, 0xcb, - 0x7b, 0x90, 0xa2, 0x37, 0xd8, 0xbc, 0x88, 0xcc, 0x29, 0x58, 0x44, 0x26, 0xbe, 0xf4, 0xc0, 0xc5, - 0x8e, 0x58, 0xd0, 0xd1, 0x02, 0x7a, 0x5c, 0xc4, 0xd5, 0x64, 0xf7, 0xb8, 0xca, 0x0d, 0x91, 0x47, - 0xd7, 0x3a, 0x0c, 0x95, 0x88, 0x2b, 0x5e, 0x5e, 0xf4, 0x04, 0x91, 0x7c, 0x41, 0xd0, 0x2a, 0x8c, - 0x59, 0xaa, 0xed, 0xd2, 0xab, 0xfb, 0x7b, 0xb4, 0x17, 0xdc, 0xd6, 0xa7, 0x5b, 0x67, 0x5e, 0xa8, - 0xb3, 0xbc, 0x95, 0x11, 0x2b, 0x08, 0x94, 0xff, 0x30, 0x05, 0x83, 0x5c, 0x19, 0x6f, 0x86, 0x21, - 0xae, 0x56, 0x6e, 0x9d, 0x77, 0xce, 0xb6, 0x06, 0xa6, 0x59, 0x2f, 0x80, 0x70, 0x7e, 0x82, 0x06, - 0xdd, 0x07, 0x69, 0x6d, 0x4f, 0xd5, 0x8d, 0x8a, 0x5e, 0x15, 0x69, 0xde, 0xeb, 0x37, 0xa6, 0x87, - 0x16, 0x08, 0x6c, 0x79, 0x51, 0x19, 0xa2, 0x95, 0xcb, 0x55, 0x92, 0x09, 0xec, 0x61, 0xbd, 0xb6, - 0xe7, 0xf2, 0x19, 0xc6, 0x4b, 0xe8, 0x49, 0x48, 0x11, 0x83, 0xe0, 0x0f, 0xbb, 0x0a, 0x2d, 0xc9, - 0xb6, 0xb7, 0xe2, 0x29, 0xa5, 0x49, 0xc3, 0x1f, 0xfa, 0xfd, 0x69, 0x49, 0xa1, 0x14, 0x68, 0x01, - 0x46, 0xea, 0xaa, 0xe3, 0x56, 0x68, 0x04, 0x23, 0xcd, 0x0f, 0x50, 0x16, 0x27, 0x5a, 0x15, 0xc2, - 0x15, 0xcb, 0x45, 0x1f, 0x26, 0x54, 0x0c, 0x54, 0x45, 0xa7, 0x20, 0x47, 0x99, 0x68, 0x66, 0xa3, - 0xa1, 0xbb, 0x2c, 0xb7, 0x1a, 0xa4, 0x7a, 0x1f, 0x25, 0xf0, 0x05, 0x0a, 0xa6, 0x19, 0xd6, 0x1d, - 0x90, 0xa1, 0x4f, 0x49, 0x28, 0x0a, 0xbb, 0x36, 0x99, 0x26, 0x00, 0x5a, 0x79, 0x3f, 0x8c, 0xf9, - 0xfe, 0x91, 0xa1, 0xa4, 0x19, 0x17, 0x1f, 0x4c, 0x11, 0x1f, 0x81, 0x49, 0x03, 0x5f, 0xa3, 0x17, - 0x39, 0x43, 0xd8, 0x19, 0x8a, 0x8d, 0x48, 0xdd, 0xe5, 0x30, 0xc5, 0xbd, 0x30, 0xaa, 0x09, 0xe5, - 0x33, 0x5c, 0xa0, 0xb8, 0x23, 0x1e, 0x94, 0xa2, 0x9d, 0x80, 0xb4, 0x6a, 0x59, 0x0c, 0x61, 0x98, - 0xfb, 0x47, 0xcb, 0xa2, 0x55, 0xa7, 0x61, 0x9c, 0xf6, 0xd1, 0xc6, 0x4e, 0xb3, 0xee, 0x72, 0x26, - 0x59, 0x8a, 0x33, 0x46, 0x2a, 0x14, 0x06, 0xa7, 0xb8, 0x77, 0xc3, 0x08, 0xbe, 0xa2, 0x57, 0xb1, - 0xa1, 0x61, 0x86, 0x37, 0x42, 0xf1, 0xb2, 0x02, 0x48, 0x91, 0x1e, 0x00, 0xcf, 0xef, 0x55, 0x84, - 0x4f, 0x1e, 0x65, 0xfc, 0x04, 0x7c, 0x9e, 0x81, 0xe5, 0x3c, 0xa4, 0x16, 0x55, 0x57, 0x25, 0x09, - 0x86, 0x7b, 0x8d, 0x05, 0x9a, 0xac, 0x42, 0x7e, 0xca, 0x6f, 0x24, 0x20, 0x75, 0xd9, 0x74, 0x31, - 0x7a, 0x2c, 0x90, 0x00, 0x8e, 0xb6, 0xb3, 0xe7, 0x4d, 0xbd, 0x66, 0xe0, 0xea, 0xaa, 0x53, 0x0b, - 0xbc, 0xfb, 0xf6, 0xcd, 0x29, 0x11, 0x32, 0xa7, 0x49, 0x18, 0xb0, 0xcd, 0xa6, 0x51, 0x15, 0x37, - 0x0e, 0x69, 0x01, 0x95, 0x21, 0xed, 0x59, 0x49, 0x2a, 0xce, 0x4a, 0xc6, 0x88, 0x95, 0x10, 0x1b, - 0xe6, 0x00, 0x65, 0x68, 0x87, 0x1b, 0x4b, 0x09, 0x32, 0x9e, 0xf3, 0xe2, 0xd6, 0xd6, 0x9b, 0xc1, - 0xfa, 0x64, 0x24, 0x98, 0x78, 0x63, 0xef, 0x29, 0x8f, 0x59, 0x5c, 0xce, 0xab, 0xe0, 0xda, 0x0b, - 0x99, 0x15, 0x7f, 0x83, 0x3e, 0x44, 0xfb, 0xe5, 0x9b, 0x15, 0x7b, 0x87, 0x7e, 0x12, 0x32, 0x8e, - 0x5e, 0x33, 0x54, 0xb7, 0x69, 0x63, 0x6e, 0x79, 0x3e, 0x40, 0xfe, 0x92, 0x04, 0x83, 0xcc, 0x92, - 0x03, 0x7a, 0x93, 0xda, 0xeb, 0x2d, 0xd1, 0x49, 0x6f, 0xc9, 0xc3, 0xeb, 0x6d, 0x1e, 0xc0, 0x13, - 0xc6, 0xe1, 0x4f, 0x83, 0xdb, 0x64, 0x0c, 0x4c, 0xc4, 0x4d, 0xbd, 0xc6, 0x27, 0x6a, 0x80, 0x48, - 0xfe, 0x8f, 0x12, 0x49, 0x62, 0x79, 0x3d, 0x9a, 0x87, 0x11, 0x21, 0x57, 0x65, 0xb7, 0xae, 0xd6, - 0xb8, 0xed, 0xdc, 0xd9, 0x51, 0xb8, 0x0b, 0x75, 0xb5, 0xa6, 0x0c, 0x73, 0x79, 0x48, 0xa1, 0xfd, - 0x38, 0x24, 0x3a, 0x8c, 0x43, 0x68, 0xe0, 0x93, 0x87, 0x1b, 0xf8, 0xd0, 0x10, 0xa5, 0xa2, 0x43, - 0xf4, 0xf9, 0x04, 0x5d, 0xcc, 0x58, 0xa6, 0xa3, 0xd6, 0x7f, 0x18, 0x33, 0xe2, 0x0e, 0xc8, 0x58, - 0x66, 0xbd, 0xc2, 0x6a, 0xd8, 0x4d, 0xdc, 0xb4, 0x65, 0xd6, 0x95, 0x96, 0x61, 0x1f, 0xb8, 0x45, - 0xd3, 0x65, 0xf0, 0x16, 0x68, 0x6d, 0x28, 0xaa, 0x35, 0x1b, 0xb2, 0x4c, 0x15, 0x3c, 0x96, 0x3d, - 0x42, 0x74, 0x40, 0x83, 0xa3, 0xd4, 0x1a, 0x7b, 0x99, 0xd8, 0x0c, 0x53, 0xe1, 0x78, 0x84, 0x82, - 0xb9, 0xfe, 0x76, 0xab, 0xe0, 0xa0, 0x59, 0x2a, 0x1c, 0x4f, 0xfe, 0x39, 0x09, 0x60, 0x85, 0x68, - 0x96, 0xf6, 0x97, 0x44, 0x21, 0x87, 0x8a, 0x50, 0x09, 0xb5, 0x3c, 0xd5, 0x69, 0xd0, 0x78, 0xfb, - 0x59, 0x27, 0x28, 0xf7, 0x02, 0x8c, 0xf8, 0xc6, 0xe8, 0x60, 0x21, 0xcc, 0x54, 0x97, 0xac, 0x7a, - 0x13, 0xbb, 0x4a, 0xf6, 0x4a, 0xa0, 0x24, 0xff, 0x33, 0x09, 0x32, 0x54, 0xa6, 0x55, 0xec, 0xaa, - 0xa1, 0x31, 0x94, 0x0e, 0x3f, 0x86, 0x77, 0x02, 0x30, 0x36, 0x8e, 0xfe, 0x32, 0xe6, 0x96, 0x95, - 0xa1, 0x90, 0x4d, 0xfd, 0x65, 0x8c, 0xce, 0x79, 0x0a, 0x4f, 0x76, 0x57, 0xb8, 0xc8, 0xba, 0xb9, - 0xda, 0x8f, 0xc3, 0x10, 0xfd, 0x94, 0xce, 0x35, 0x87, 0x27, 0xd2, 0x83, 0x46, 0xb3, 0xb1, 0x75, - 0xcd, 0x91, 0x5f, 0x84, 0xa1, 0xad, 0x6b, 0x6c, 0x6f, 0xe4, 0x0e, 0xc8, 0xd8, 0xa6, 0xc9, 0x63, - 0x32, 0xcb, 0x85, 0xd2, 0x04, 0x40, 0x43, 0x90, 0xd8, 0x0f, 0x48, 0xf8, 0xfb, 0x01, 0xfe, 0x86, - 0x46, 0xb2, 0xa7, 0x0d, 0x8d, 0xd3, 0xff, 0x4e, 0x82, 0xe1, 0x80, 0x7f, 0x40, 0x8f, 0xc2, 0xd1, - 0xd2, 0xca, 0xfa, 0xc2, 0xa5, 0xca, 0xf2, 0x62, 0xe5, 0xc2, 0xca, 0xfc, 0x92, 0xff, 0xd6, 0xa4, - 0x70, 0xec, 0xd5, 0xeb, 0x33, 0x28, 0x80, 0xbb, 0x6d, 0xec, 0x1b, 0xe6, 0x55, 0x03, 0xcd, 0xc1, - 0x64, 0x98, 0x64, 0xbe, 0xb4, 0x59, 0x5e, 0xdb, 0xca, 0x49, 0x85, 0xa3, 0xaf, 0x5e, 0x9f, 0x19, - 0x0f, 0x50, 0xcc, 0xef, 0x38, 0xd8, 0x70, 0x5b, 0x09, 0x16, 0xd6, 0x57, 0x57, 0x97, 0xb7, 0x72, - 0x89, 0x16, 0x02, 0xee, 0xb0, 0x1f, 0x80, 0xf1, 0x30, 0xc1, 0xda, 0xf2, 0x4a, 0x2e, 0x59, 0x40, - 0xaf, 0x5e, 0x9f, 0x19, 0x0d, 0x60, 0xaf, 0xe9, 0xf5, 0x42, 0xfa, 0xfd, 0x9f, 0x9e, 0x3a, 0xf2, - 0x4b, 0xbf, 0x38, 0x25, 0x91, 0x9e, 0x8d, 0x84, 0x7c, 0x04, 0x7a, 0x08, 0x8e, 0x6f, 0x2e, 0x2f, - 0xad, 0x95, 0x17, 0x2b, 0xab, 0x9b, 0x4b, 0x15, 0xf6, 0x8d, 0x0d, 0xaf, 0x77, 0x63, 0xaf, 0x5e, - 0x9f, 0x19, 0xe6, 0x5d, 0xea, 0x84, 0xbd, 0xa1, 0x94, 0x2f, 0xaf, 0x6f, 0x95, 0x73, 0x12, 0xc3, - 0xde, 0xb0, 0xf1, 0x15, 0xd3, 0x65, 0xdf, 0xda, 0x7a, 0x04, 0x4e, 0xb4, 0xc1, 0xf6, 0x3a, 0x36, - 0xfe, 0xea, 0xf5, 0x99, 0x91, 0x0d, 0x1b, 0xb3, 0xf9, 0x43, 0x29, 0x66, 0x21, 0xdf, 0x4a, 0xb1, - 0xbe, 0xb1, 0xbe, 0x39, 0xbf, 0x92, 0x9b, 0x29, 0xe4, 0x5e, 0xbd, 0x3e, 0x93, 0x15, 0xce, 0x90, - 0xe0, 0xfb, 0x3d, 0xbb, 0x9d, 0x2b, 0x9e, 0x3f, 0x79, 0x18, 0xee, 0xe1, 0x7b, 0x80, 0x8e, 0xab, - 0xee, 0xeb, 0x46, 0xcd, 0xdb, 0x69, 0xe5, 0x65, 0xbe, 0xf2, 0x39, 0xc6, 0x37, 0x5b, 0x05, 0xb4, - 0xeb, 0x7e, 0x6b, 0xa1, 0xf3, 0xc9, 0x52, 0x21, 0xe6, 0xf0, 0x25, 0x7e, 0xe9, 0xd4, 0x79, 0x6f, - 0xbe, 0x10, 0xb3, 0x63, 0x5c, 0xe8, 0xba, 0xb8, 0x93, 0x3f, 0x20, 0xc1, 0xe8, 0x45, 0xdd, 0x71, - 0x4d, 0x5b, 0xd7, 0xd4, 0x3a, 0x7d, 0x61, 0x72, 0xae, 0x57, 0xdf, 0x1a, 0x99, 0xea, 0xcf, 0xc0, - 0xe0, 0x15, 0xb5, 0xce, 0x9c, 0x5a, 0x92, 0x7e, 0x10, 0xa3, 0xbd, 0xfa, 0x7c, 0xd7, 0x26, 0x18, - 0x30, 0x32, 0xf9, 0x57, 0x12, 0x30, 0x46, 0x27, 0x83, 0xc3, 0x3e, 0x95, 0x44, 0xd6, 0x58, 0x25, - 0x48, 0xd9, 0xaa, 0xcb, 0x37, 0x0d, 0x4b, 0xb3, 0x7c, 0xe7, 0xf7, 0xbe, 0xf8, 0xdd, 0xdc, 0xd9, - 0x45, 0xac, 0x29, 0x94, 0x16, 0xbd, 0x1d, 0xd2, 0x0d, 0xf5, 0x5a, 0x85, 0xf2, 0x61, 0x2b, 0x97, - 0xf9, 0xfe, 0xf8, 0xdc, 0xbc, 0x31, 0x3d, 0x76, 0xa0, 0x36, 0xea, 0x45, 0x59, 0xf0, 0x91, 0x95, - 0xa1, 0x86, 0x7a, 0x8d, 0x88, 0x88, 0x2c, 0x18, 0x23, 0x50, 0x6d, 0x4f, 0x35, 0x6a, 0x98, 0x35, - 0x42, 0xb7, 0x40, 0x4b, 0x17, 0xfb, 0x6e, 0xe4, 0x98, 0xdf, 0x48, 0x80, 0x9d, 0xac, 0x8c, 0x34, - 0xd4, 0x6b, 0x0b, 0x14, 0x40, 0x5a, 0x2c, 0xa6, 0x3f, 0xfa, 0xc9, 0xe9, 0x23, 0x74, 0x37, 0xfd, - 0x1b, 0x12, 0x80, 0xaf, 0x31, 0xf4, 0x76, 0xc8, 0x69, 0x5e, 0x89, 0xd2, 0x3a, 0x7c, 0x0c, 0xef, - 0xef, 0x34, 0x16, 0x11, 0x7d, 0xb3, 0xd8, 0xfc, 0xf5, 0x1b, 0xd3, 0x92, 0x32, 0xa6, 0x45, 0x86, - 0xe2, 0x6d, 0x30, 0xdc, 0xb4, 0xaa, 0xaa, 0x8b, 0x2b, 0x74, 0x1d, 0x97, 0x88, 0x8d, 0xf3, 0x53, - 0x84, 0xd7, 0xcd, 0x1b, 0xd3, 0x88, 0x75, 0x2b, 0x40, 0x2c, 0xd3, 0xe8, 0x0f, 0x0c, 0x42, 0x08, - 0x02, 0x7d, 0xfa, 0xaa, 0x04, 0xc3, 0x8b, 0x81, 0x9b, 0x5e, 0x79, 0x18, 0x6a, 0x98, 0x86, 0xbe, - 0xcf, 0xed, 0x31, 0xa3, 0x88, 0x22, 0x2a, 0x40, 0x9a, 0x3d, 0xba, 0x73, 0x0f, 0xc4, 0x56, 0xa8, - 0x28, 0x13, 0xaa, 0xab, 0x78, 0xc7, 0xd1, 0xc5, 0x68, 0x28, 0xa2, 0x88, 0x2e, 0x40, 0xce, 0xc1, - 0x5a, 0xd3, 0xd6, 0xdd, 0x83, 0x8a, 0x66, 0x1a, 0xae, 0xaa, 0xb9, 0xec, 0xf9, 0x56, 0xe9, 0x8e, - 0x9b, 0x37, 0xa6, 0x8f, 0x33, 0x59, 0xa3, 0x18, 0xb2, 0x32, 0x26, 0x40, 0x0b, 0x0c, 0x42, 0x5a, - 0xa8, 0x62, 0x57, 0xd5, 0xeb, 0x4e, 0x9e, 0x1d, 0x0c, 0x89, 0x62, 0xa0, 0x2f, 0x9f, 0x1b, 0x0a, - 0x6e, 0x6c, 0x5d, 0x80, 0x9c, 0x69, 0x61, 0x3b, 0x94, 0x88, 0x4a, 0xd1, 0x96, 0xa3, 0x18, 0xb2, - 0x32, 0x26, 0x40, 0x22, 0x49, 0x75, 0xc9, 0x30, 0x8b, 0x85, 0xa2, 0xd5, 0xdc, 0xf1, 0xf7, 0xc3, - 0x26, 0x5b, 0x46, 0x63, 0xde, 0x38, 0x28, 0x3d, 0xe6, 0x73, 0x8f, 0xd2, 0xc9, 0x5f, 0xfb, 0xc2, - 0xc3, 0x93, 0xdc, 0x34, 0xfc, 0xfd, 0xa9, 0x4b, 0xf8, 0x80, 0x0c, 0x3f, 0x47, 0xdd, 0xa0, 0x98, - 0x24, 0xed, 0x7c, 0x51, 0xd5, 0xeb, 0xe2, 0x19, 0xb2, 0xc2, 0x4b, 0xa8, 0x08, 0x83, 0x8e, 0xab, - 0xba, 0x4d, 0x87, 0x7f, 0x1c, 0x4c, 0xee, 0x64, 0x6a, 0x25, 0xd3, 0xa8, 0x6e, 0x52, 0x4c, 0x85, - 0x53, 0xa0, 0x0b, 0x30, 0xe8, 0x9a, 0xfb, 0xd8, 0xe0, 0x2a, 0xec, 0x6b, 0x7e, 0xd3, 0x73, 0x2a, - 0x46, 0x4d, 0x34, 0x52, 0xc5, 0x75, 0x5c, 0x63, 0x69, 0xd5, 0x9e, 0x4a, 0x56, 0x1f, 0xf4, 0x1b, - 0x61, 0xa5, 0xe5, 0xbe, 0x27, 0x21, 0xd7, 0x54, 0x94, 0x9f, 0xac, 0x8c, 0x79, 0xa0, 0x4d, 0x0a, - 0x41, 0x97, 0x42, 0x57, 0x12, 0xf9, 0x87, 0xf4, 0xee, 0xee, 0xd4, 0xfd, 0x80, 0x4d, 0x8b, 0xfd, - 0x89, 0xe0, 0x85, 0xc6, 0x0b, 0x90, 0x6b, 0x1a, 0x3b, 0xa6, 0x41, 0xdf, 0x0a, 0xf2, 0xfc, 0x9e, - 0xac, 0xef, 0x92, 0x41, 0xe3, 0x88, 0x62, 0xc8, 0xca, 0x98, 0x07, 0xba, 0xc8, 0x56, 0x01, 0x55, - 0x18, 0xf5, 0xb1, 0xe8, 0x44, 0xcd, 0xc4, 0x4e, 0xd4, 0xbb, 0xf8, 0x44, 0x3d, 0x1a, 0x6d, 0xc5, - 0x9f, 0xab, 0x23, 0x1e, 0x90, 0x90, 0xa1, 0x8b, 0x00, 0xbe, 0x7b, 0xa0, 0xfb, 0x14, 0xc3, 0x9d, - 0x07, 0xde, 0xf7, 0x31, 0x62, 0xbd, 0xe7, 0xd3, 0xa2, 0x77, 0xc2, 0x44, 0x43, 0x37, 0x2a, 0x0e, - 0xae, 0xef, 0x56, 0xb8, 0x82, 0x09, 0x4b, 0xfa, 0xa9, 0x97, 0xd2, 0x4a, 0x7f, 0xf6, 0x70, 0xf3, - 0xc6, 0x74, 0x81, 0xbb, 0xd0, 0x56, 0x96, 0xb2, 0x32, 0xde, 0xd0, 0x8d, 0x4d, 0x5c, 0xdf, 0x5d, - 0xf4, 0x60, 0xc5, 0xec, 0xfb, 0x3f, 0x39, 0x7d, 0x84, 0x4f, 0xd7, 0x23, 0xf2, 0x39, 0xba, 0x77, - 0xce, 0xa7, 0x19, 0x76, 0xc8, 0x9a, 0x44, 0x15, 0x05, 0xba, 0xa3, 0x91, 0x51, 0x7c, 0x00, 0x9b, - 0xe6, 0xaf, 0xfc, 0x87, 0x19, 0x49, 0xfe, 0x9c, 0x04, 0x83, 0x8b, 0x97, 0x37, 0x54, 0xdd, 0x46, - 0xcb, 0x30, 0xee, 0x5b, 0x4e, 0x78, 0x92, 0x9f, 0xbc, 0x79, 0x63, 0x3a, 0x1f, 0x35, 0x2e, 0x6f, - 0x96, 0xfb, 0x06, 0x2c, 0xa6, 0xf9, 0x72, 0xa7, 0x85, 0x6b, 0x88, 0x55, 0x0b, 0x8a, 0xdc, 0xba, - 0xac, 0x8d, 0x74, 0xb3, 0x0c, 0x43, 0x4c, 0x5a, 0x07, 0x15, 0x61, 0xc0, 0x22, 0x3f, 0xf8, 0xc1, - 0xc0, 0x54, 0x47, 0xe3, 0xa5, 0xf8, 0xde, 0x46, 0x26, 0x21, 0x91, 0x3f, 0x9c, 0x00, 0x58, 0xbc, - 0x7c, 0x79, 0xcb, 0xd6, 0xad, 0x3a, 0x76, 0x6f, 0x65, 0xcf, 0xb7, 0xe0, 0x68, 0x60, 0x95, 0x64, - 0x6b, 0x91, 0xde, 0xcf, 0xdc, 0xbc, 0x31, 0x7d, 0x32, 0xda, 0xfb, 0x00, 0x9a, 0xac, 0x4c, 0xf8, - 0xeb, 0x25, 0x5b, 0x6b, 0xcb, 0xb5, 0xea, 0xb8, 0x1e, 0xd7, 0x64, 0x67, 0xae, 0x01, 0xb4, 0x20, - 0xd7, 0x45, 0xc7, 0x6d, 0xaf, 0xda, 0x4d, 0x18, 0xf6, 0x55, 0xe2, 0xa0, 0x45, 0x48, 0xbb, 0xfc, - 0x37, 0xd7, 0xb0, 0xdc, 0x59, 0xc3, 0x82, 0x8c, 0x6b, 0xd9, 0xa3, 0x94, 0xff, 0x4c, 0x02, 0xf0, - 0x6d, 0xf6, 0xc7, 0xd3, 0xc4, 0x88, 0x2b, 0xe7, 0x8e, 0x37, 0x79, 0xa8, 0x54, 0x8d, 0x53, 0x47, - 0xf4, 0xf9, 0xd3, 0x09, 0x98, 0xd8, 0x16, 0x9e, 0xe7, 0xc7, 0x5e, 0x07, 0x1b, 0x30, 0x84, 0x0d, - 0xd7, 0xd6, 0xa9, 0x12, 0xc8, 0x68, 0x3f, 0xd2, 0x69, 0xb4, 0xdb, 0xf4, 0x89, 0x7e, 0xec, 0x46, - 0x6c, 0xba, 0x73, 0x36, 0x11, 0x6d, 0x7c, 0x30, 0x09, 0xf9, 0x4e, 0x94, 0x68, 0x01, 0xc6, 0x34, - 0x1b, 0x53, 0x40, 0x25, 0xb8, 0xf3, 0x57, 0x2a, 0xf8, 0x99, 0x65, 0x04, 0x41, 0x56, 0x46, 0x05, - 0x84, 0x47, 0x8f, 0x1a, 0x90, 0xb4, 0x8f, 0x98, 0x1d, 0xc1, 0xea, 0x31, 0xcf, 0x93, 0x79, 0xf8, - 0x10, 0x8d, 0x84, 0x19, 0xb0, 0xf8, 0x31, 0xea, 0x43, 0x69, 0x00, 0x79, 0x09, 0xc6, 0x74, 0x43, - 0x77, 0x75, 0xb5, 0x5e, 0xd9, 0x51, 0xeb, 0xaa, 0xa1, 0x1d, 0x26, 0x6b, 0x66, 0x2e, 0x9f, 0x37, - 0x1b, 0x61, 0x27, 0x2b, 0xa3, 0x1c, 0x52, 0x62, 0x00, 0x74, 0x11, 0x86, 0x44, 0x53, 0xa9, 0x43, - 0x65, 0x1b, 0x82, 0x3c, 0x90, 0xe0, 0xfd, 0x4c, 0x12, 0xc6, 0x15, 0x5c, 0xfd, 0xff, 0x43, 0xd1, - 0xdf, 0x50, 0xac, 0x02, 0xb0, 0xe9, 0x4e, 0x1c, 0xec, 0x21, 0x46, 0x83, 0x38, 0x8c, 0x0c, 0xe3, - 0xb0, 0xe8, 0xb8, 0x81, 0xf1, 0xb8, 0x91, 0x80, 0x6c, 0x70, 0x3c, 0xfe, 0x82, 0x46, 0x25, 0xb4, - 0xec, 0x7b, 0xa2, 0x14, 0xff, 0x44, 0x68, 0x07, 0x4f, 0xd4, 0x62, 0xbd, 0xdd, 0x5d, 0xd0, 0xff, - 0x48, 0xc0, 0xe0, 0x86, 0x6a, 0xab, 0x0d, 0x07, 0x69, 0x2d, 0x99, 0xa6, 0xd8, 0x7e, 0x6c, 0xf9, - 0x10, 0x34, 0xdf, 0xed, 0x88, 0x49, 0x34, 0x3f, 0xda, 0x26, 0xd1, 0xfc, 0x09, 0x18, 0x25, 0xcb, - 0xe1, 0xc0, 0x15, 0x06, 0xa2, 0xed, 0x91, 0xd2, 0x09, 0x9f, 0x4b, 0xb8, 0x9e, 0xad, 0x96, 0x2f, - 0x07, 0xef, 0x30, 0x0c, 0x13, 0x0c, 0xdf, 0x31, 0x13, 0xf2, 0x63, 0xfe, 0xb2, 0x34, 0x50, 0x29, - 0x2b, 0xd0, 0x50, 0xaf, 0x95, 0x59, 0x01, 0xad, 0x00, 0xda, 0xf3, 0x76, 0x46, 0x2a, 0xbe, 0x3a, - 0x09, 0xfd, 0x9d, 0x37, 0x6f, 0x4c, 0x9f, 0x60, 0xf4, 0xad, 0x38, 0xb2, 0x32, 0xee, 0x03, 0x05, - 0xb7, 0xc7, 0x01, 0x48, 0xbf, 0x2a, 0xec, 0xfa, 0x1c, 0x5b, 0xee, 0x1c, 0xbd, 0x79, 0x63, 0x7a, - 0x9c, 0x71, 0xf1, 0xeb, 0x64, 0x25, 0x43, 0x0a, 0x8b, 0xe4, 0x77, 0xc0, 0xb2, 0x3f, 0x2d, 0x01, - 0xf2, 0x5d, 0xbe, 0x82, 0x1d, 0x8b, 0xac, 0xcf, 0x48, 0x22, 0x1e, 0xc8, 0x9a, 0xa5, 0xee, 0x89, - 0xb8, 0x4f, 0x2f, 0x12, 0xf1, 0xc0, 0x4c, 0x79, 0xca, 0x77, 0x8f, 0x09, 0x3e, 0x8e, 0x6d, 0xee, - 0x1a, 0xce, 0x2e, 0x98, 0xba, 0xa0, 0x6e, 0xf1, 0x87, 0x47, 0xe4, 0x7f, 0x25, 0xc1, 0x89, 0x16, - 0x8b, 0xf2, 0x84, 0xfd, 0x4b, 0x80, 0xec, 0x40, 0x25, 0xff, 0xde, 0x1b, 0x13, 0xba, 0x6f, 0x03, - 0x1d, 0xb7, 0x5b, 0xfc, 0xee, 0xad, 0xf3, 0xf0, 0xec, 0xb2, 0xe2, 0x3f, 0x95, 0x60, 0x32, 0xd8, - 0xbc, 0xd7, 0x91, 0x35, 0xc8, 0x06, 0x5b, 0xe7, 0x5d, 0xb8, 0xa7, 0x97, 0x2e, 0x70, 0xe9, 0x43, - 0xf4, 0xe8, 0x39, 0x7f, 0xba, 0xb2, 0xbd, 0xb3, 0x47, 0x7b, 0xd6, 0x86, 0x90, 0x29, 0x3a, 0x6d, - 0x53, 0x74, 0x3c, 0xfe, 0x8f, 0x04, 0xa9, 0x0d, 0xd3, 0xac, 0x23, 0x13, 0xc6, 0x0d, 0xd3, 0xad, - 0x10, 0xcb, 0xc2, 0xd5, 0x0a, 0x5f, 0x74, 0x33, 0x3f, 0xb8, 0xd0, 0x9f, 0x92, 0xbe, 0x7d, 0x63, - 0xba, 0x95, 0x95, 0x32, 0x66, 0x98, 0x6e, 0x89, 0x42, 0xb6, 0xd8, 0x92, 0xfc, 0x9d, 0x30, 0x12, - 0x6e, 0x8c, 0x79, 0xc9, 0xe7, 0xfb, 0x6e, 0x2c, 0xcc, 0xe6, 0xe6, 0x8d, 0xe9, 0x49, 0x7f, 0xc6, - 0x78, 0x60, 0x59, 0xc9, 0xee, 0x04, 0x5a, 0x67, 0xd7, 0xbb, 0xbe, 0xfb, 0xc9, 0x69, 0xe9, 0xf4, - 0x17, 0x25, 0x00, 0x7f, 0xe7, 0x01, 0x3d, 0x04, 0xc7, 0x4b, 0xeb, 0x6b, 0x8b, 0x95, 0xcd, 0xad, - 0xf9, 0xad, 0xed, 0xcd, 0xca, 0xf6, 0xda, 0xe6, 0x46, 0x79, 0x61, 0xf9, 0xc2, 0x72, 0x79, 0xd1, - 0xdf, 0x1e, 0x77, 0x2c, 0xac, 0xe9, 0xbb, 0x3a, 0xae, 0xa2, 0xfb, 0x60, 0x32, 0x8c, 0x4d, 0x4a, - 0xe5, 0xc5, 0x9c, 0x54, 0xc8, 0xbe, 0x7a, 0x7d, 0x26, 0xcd, 0x72, 0x31, 0x5c, 0x45, 0xa7, 0xe0, - 0x68, 0x2b, 0xde, 0xf2, 0xda, 0x52, 0x2e, 0x51, 0x18, 0x79, 0xf5, 0xfa, 0x4c, 0xc6, 0x4b, 0xda, - 0x90, 0x0c, 0x28, 0x88, 0xc9, 0xf9, 0x25, 0x0b, 0xf0, 0xea, 0xf5, 0x99, 0x41, 0xa6, 0xc0, 0x42, - 0xea, 0xfd, 0x9f, 0x9e, 0x3a, 0x52, 0xba, 0xd0, 0x71, 0x03, 0xfc, 0xa1, 0xae, 0xba, 0xbb, 0xe6, - 0x6d, 0x6a, 0x87, 0x77, 0xbd, 0xff, 0x78, 0xa8, 0xe3, 0xae, 0x77, 0x0d, 0x1b, 0xd8, 0xd1, 0x9d, - 0x43, 0xed, 0x7a, 0xf7, 0xb4, 0x93, 0x2e, 0xff, 0xce, 0x00, 0x64, 0x97, 0x58, 0x2b, 0x64, 0x20, - 0x30, 0x7a, 0x13, 0x0c, 0x5a, 0x34, 0x8c, 0x78, 0xc7, 0x68, 0x1d, 0x0c, 0x9e, 0x05, 0x1b, 0xef, - 0x2e, 0x17, 0x0b, 0x3d, 0x0e, 0xbf, 0xcc, 0xc1, 0xee, 0x98, 0xf9, 0xb7, 0xa6, 0xb2, 0x7d, 0xed, - 0xf7, 0xb0, 0x9c, 0x85, 0x6f, 0xad, 0x44, 0xf9, 0xc9, 0xec, 0x5e, 0xc8, 0x16, 0x81, 0xb0, 0xdb, - 0x61, 0xef, 0x95, 0xe0, 0x28, 0xc5, 0xf2, 0x03, 0x31, 0xc5, 0x14, 0xc9, 0xfe, 0xe9, 0x4e, 0x5d, - 0x58, 0x51, 0x1d, 0xff, 0xae, 0x07, 0xbb, 0xcf, 0x75, 0x0f, 0x0f, 0x84, 0x27, 0x03, 0x8d, 0x47, - 0xd9, 0xca, 0xca, 0x44, 0xbd, 0x85, 0xd2, 0x41, 0x4b, 0xa1, 0x0b, 0x7d, 0xa9, 0xfe, 0xb6, 0xda, - 0x83, 0x97, 0xfb, 0x9e, 0x85, 0x61, 0xdf, 0x97, 0x38, 0xfc, 0xff, 0x53, 0xf4, 0x1e, 0x3b, 0x82, - 0xc4, 0xe8, 0x7d, 0x12, 0x1c, 0xf5, 0xa3, 0x79, 0x90, 0x2d, 0xfb, 0x3f, 0x1e, 0x0f, 0xf6, 0xb1, - 0x10, 0x8a, 0x2a, 0xa7, 0x2d, 0x5f, 0x59, 0x99, 0x6c, 0xb6, 0x92, 0x92, 0x25, 0xd8, 0x48, 0xd0, - 0xb3, 0x3a, 0x79, 0xf1, 0xa9, 0xba, 0xde, 0x5d, 0x73, 0x98, 0x01, 0xfb, 0xdf, 0x02, 0x96, 0x69, - 0xbb, 0xb8, 0x4a, 0x37, 0xe4, 0xd2, 0x8a, 0x57, 0x96, 0xd7, 0x00, 0xb5, 0x0e, 0x6e, 0xf4, 0x02, - 0x63, 0xc6, 0xbf, 0xc0, 0x38, 0x09, 0x03, 0xc1, 0x2b, 0x7e, 0xac, 0x50, 0x4c, 0xbf, 0x9f, 0x87, - 0xcf, 0x5b, 0x3e, 0xe7, 0xff, 0x45, 0x02, 0x4e, 0x07, 0x8f, 0x87, 0x5e, 0x6a, 0x62, 0xfb, 0xc0, - 0x9b, 0xa2, 0x96, 0x5a, 0xd3, 0x8d, 0xe0, 0x1b, 0xa0, 0x13, 0xc1, 0x80, 0x4f, 0x71, 0x85, 0x9e, - 0x64, 0x03, 0x86, 0x37, 0xd4, 0x1a, 0x56, 0xf0, 0x4b, 0x4d, 0xec, 0xb8, 0x6d, 0x2e, 0x99, 0x1f, - 0x83, 0x41, 0x73, 0x77, 0x57, 0x1c, 0x69, 0xa7, 0x14, 0x5e, 0x22, 0x5d, 0xae, 0xeb, 0x0d, 0x9d, - 0xdd, 0x06, 0x4b, 0x29, 0xac, 0x80, 0xa6, 0x61, 0x58, 0x33, 0x9b, 0x06, 0x9f, 0x71, 0xf9, 0x94, - 0xf8, 0x00, 0x44, 0xd3, 0x60, 0x33, 0x4e, 0x7e, 0x06, 0xb2, 0xac, 0x3d, 0x1e, 0x71, 0x4f, 0x40, - 0x9a, 0x5e, 0xa7, 0xf2, 0x5b, 0x1d, 0x22, 0xe5, 0x4b, 0xec, 0x42, 0x3a, 0xe3, 0xc2, 0x1a, 0x66, - 0x85, 0x52, 0xa9, 0xa3, 0x2a, 0x4f, 0xc5, 0xbb, 0x06, 0xa6, 0x28, 0x4f, 0x8d, 0xbf, 0x39, 0x00, - 0x47, 0xf9, 0x09, 0x9d, 0x6a, 0xe9, 0x73, 0x7b, 0xae, 0x2b, 0x5e, 0x09, 0x01, 0x4f, 0x75, 0x55, - 0x4b, 0x97, 0x0f, 0x20, 0x75, 0xd1, 0x75, 0x2d, 0x74, 0x1a, 0x06, 0xec, 0x66, 0x1d, 0x8b, 0x1d, - 0x1f, 0x6f, 0x4f, 0x5e, 0xb5, 0xf4, 0x59, 0x82, 0xa0, 0x34, 0xeb, 0x58, 0x61, 0x28, 0xa8, 0x0c, - 0xd3, 0xbb, 0xcd, 0x7a, 0xfd, 0xa0, 0x52, 0xc5, 0xf4, 0x7f, 0xf7, 0x78, 0x5f, 0xbf, 0xc7, 0xd7, - 0x2c, 0x55, 0x7c, 0x43, 0x8f, 0xe8, 0xe6, 0x24, 0x45, 0x5b, 0xa4, 0x58, 0xe2, 0xcb, 0xf7, 0x65, - 0x81, 0x23, 0xff, 0x5e, 0x02, 0xd2, 0x82, 0x35, 0xbd, 0x21, 0x8e, 0xeb, 0x58, 0x73, 0x4d, 0x71, - 0x62, 0xe2, 0x95, 0x11, 0x82, 0x64, 0x8d, 0x0f, 0x51, 0xe6, 0xe2, 0x11, 0x85, 0x14, 0x08, 0xcc, - 0xbb, 0xb7, 0x4f, 0x60, 0x56, 0x93, 0x8c, 0x5a, 0xca, 0x32, 0xc5, 0xd2, 0xec, 0xe2, 0x11, 0x85, - 0x96, 0x50, 0x1e, 0x06, 0xc9, 0xcc, 0x70, 0xd9, 0x87, 0x09, 0x09, 0x9c, 0x97, 0xd1, 0x31, 0x18, - 0xb0, 0x54, 0x57, 0x63, 0x57, 0xea, 0x48, 0x05, 0x2b, 0xa2, 0x27, 0x60, 0x90, 0x3d, 0x08, 0x8d, - 0xfe, 0x63, 0x0c, 0xa2, 0x0c, 0xf6, 0xe5, 0x2d, 0x22, 0xf7, 0x86, 0xea, 0xba, 0xd8, 0x36, 0x08, - 0x43, 0x86, 0x8e, 0x10, 0xa4, 0x76, 0xcc, 0xea, 0x01, 0xff, 0x67, 0x1d, 0xf4, 0x37, 0xff, 0xef, - 0x00, 0xd4, 0x1e, 0x2a, 0xb4, 0x92, 0xfd, 0x8f, 0xa2, 0xac, 0x00, 0x96, 0x08, 0x52, 0x19, 0x26, - 0xd4, 0x6a, 0x55, 0x67, 0xff, 0x37, 0xa3, 0xb2, 0xa3, 0x53, 0x0f, 0xe1, 0xd0, 0xff, 0x40, 0xd5, - 0x69, 0x2c, 0x90, 0x4f, 0x50, 0xe2, 0xf8, 0xa5, 0x0c, 0x0c, 0x59, 0x4c, 0x28, 0xf9, 0x3c, 0x8c, - 0xb7, 0x48, 0x4a, 0xe4, 0xdb, 0xd7, 0x8d, 0xaa, 0x78, 0xcc, 0x40, 0x7e, 0x13, 0x18, 0xfd, 0x7a, - 0x1e, 0x3b, 0x8b, 0xa2, 0xbf, 0x4b, 0xef, 0xee, 0xfc, 0xf0, 0x6b, 0x34, 0xf0, 0xf0, 0x4b, 0xb5, - 0xf4, 0x52, 0x86, 0xf2, 0xe7, 0xcf, 0xbd, 0xe6, 0x79, 0x05, 0x7b, 0xea, 0x35, 0x6b, 0xda, 0x35, - 0x12, 0xa5, 0x45, 0xf4, 0x25, 0x55, 0xaa, 0xa5, 0x3b, 0xd4, 0x1c, 0xfd, 0xaf, 0xf9, 0x39, 0xe7, - 0x03, 0xbf, 0xe9, 0x23, 0xb0, 0xd4, 0xd2, 0xfc, 0xc6, 0xb2, 0x67, 0xc7, 0x5f, 0x4e, 0xc0, 0xc9, - 0x80, 0x1d, 0x07, 0x90, 0x5b, 0xcd, 0xb9, 0xd0, 0xde, 0xe2, 0x7b, 0x78, 0xfc, 0x75, 0x09, 0x52, - 0x04, 0x1f, 0xc5, 0x7c, 0xbb, 0x3f, 0xff, 0xab, 0x5f, 0xfb, 0x27, 0x72, 0xf8, 0xd4, 0x2a, 0x34, - 0x2a, 0x94, 0x49, 0xe9, 0x7d, 0xbd, 0xeb, 0x2f, 0xe7, 0x7f, 0xc8, 0xd0, 0xb9, 0x75, 0x6a, 0x8c, - 0xea, 0xf0, 0x5b, 0x67, 0x41, 0xee, 0x90, 0xf2, 0x30, 0x8f, 0xd9, 0x3d, 0x89, 0xea, 0xc3, 0x1d, - 0x77, 0xba, 0xff, 0xdf, 0x6d, 0x04, 0x7b, 0x4c, 0xc7, 0xae, 0xc1, 0xb1, 0xe7, 0x48, 0xdb, 0xfe, - 0x32, 0x59, 0x38, 0xf6, 0x63, 0xde, 0x69, 0x9e, 0xc4, 0xff, 0x01, 0x98, 0x38, 0xa9, 0x03, 0x5f, - 0x3e, 0xbe, 0x40, 0xbc, 0x6f, 0xb6, 0x63, 0xbc, 0x98, 0x0d, 0x04, 0x0b, 0x25, 0x40, 0x29, 0xff, - 0xb2, 0x04, 0xc7, 0x5b, 0x9a, 0xe6, 0x3e, 0x7e, 0xa9, 0xcd, 0x53, 0x85, 0x43, 0x65, 0x36, 0x4b, - 0x6d, 0x84, 0xbd, 0x3f, 0x56, 0x58, 0x26, 0x45, 0x48, 0xda, 0xa7, 0xe1, 0x68, 0x58, 0x58, 0xa1, - 0xa6, 0x7b, 0x61, 0x34, 0xbc, 0x23, 0xcc, 0xd5, 0x35, 0x12, 0xda, 0x13, 0x96, 0x2b, 0x51, 0x3d, - 0x7b, 0x7d, 0x2d, 0x43, 0xc6, 0x43, 0xe5, 0x29, 0x70, 0xcf, 0x5d, 0xf5, 0x29, 0xe5, 0x0f, 0x4b, - 0x30, 0x13, 0x6e, 0x21, 0x90, 0x0c, 0xf5, 0x27, 0xec, 0x2d, 0x1b, 0xe2, 0x37, 0x24, 0xb8, 0xab, - 0x8b, 0x4c, 0x5c, 0x01, 0x2f, 0xc3, 0x64, 0x60, 0x27, 0x40, 0xb8, 0x70, 0x31, 0xec, 0xa7, 0xe3, - 0xd3, 0x50, 0x6f, 0xe1, 0x7b, 0x07, 0x51, 0xca, 0x67, 0x7f, 0x7f, 0x7a, 0xa2, 0xb5, 0xce, 0x51, - 0x26, 0x5a, 0x57, 0xef, 0xb7, 0xd0, 0x3e, 0x5e, 0x93, 0xe0, 0x81, 0x70, 0x57, 0xdb, 0xe4, 0xb3, - 0x3f, 0xaa, 0x71, 0xf8, 0xf7, 0x12, 0x9c, 0xee, 0x45, 0x38, 0x3e, 0x20, 0x3b, 0x30, 0xe1, 0x67, - 0xda, 0xd1, 0xf1, 0xe8, 0x2b, 0x7f, 0x67, 0x56, 0x8a, 0x3c, 0x6e, 0xb7, 0x41, 0xf1, 0x16, 0x9f, - 0x58, 0xc1, 0x21, 0xf7, 0x94, 0x1c, 0xde, 0xcd, 0x15, 0x4a, 0x0e, 0xed, 0xe7, 0xb6, 0x19, 0x8b, - 0x44, 0x9b, 0xb1, 0xf0, 0x53, 0x73, 0xf9, 0x0a, 0xf7, 0x5b, 0x6d, 0xf6, 0xe0, 0xde, 0x06, 0x13, - 0x6d, 0x4c, 0x99, 0xcf, 0xea, 0x3e, 0x2c, 0x59, 0x41, 0xad, 0xc6, 0x2a, 0x1f, 0xc0, 0x34, 0x6d, - 0xb7, 0x8d, 0xa2, 0x6f, 0x77, 0x97, 0x1b, 0xdc, 0xb7, 0xb4, 0x6d, 0x9a, 0xf7, 0x7d, 0x19, 0x06, - 0xd9, 0x38, 0xf3, 0xee, 0x1e, 0xc2, 0x50, 0x38, 0x03, 0xf9, 0xe7, 0x85, 0x2f, 0x5b, 0x14, 0x62, - 0xb7, 0x9f, 0x43, 0xbd, 0xf4, 0xf5, 0x16, 0xcd, 0xa1, 0x80, 0x32, 0xbe, 0x21, 0xbc, 0x5a, 0x7b, - 0xe9, 0xb8, 0x3a, 0xb4, 0x5b, 0xe6, 0xd5, 0x98, 0x6e, 0x6e, 0xaf, 0xfb, 0xfa, 0x45, 0xe1, 0xbe, - 0xbc, 0x3e, 0xc5, 0xb8, 0xaf, 0x1f, 0x8d, 0xea, 0x3d, 0x47, 0x16, 0x23, 0xe6, 0x9f, 0x47, 0x47, - 0xf6, 0x5d, 0x09, 0x4e, 0xd0, 0xbe, 0x05, 0x37, 0x22, 0xfa, 0x55, 0xf9, 0x43, 0x80, 0x1c, 0x5b, - 0xab, 0xb4, 0x9d, 0xdd, 0x39, 0xc7, 0xd6, 0x2e, 0x87, 0xe2, 0xcb, 0x43, 0x80, 0xaa, 0xa1, 0xed, - 0x26, 0x8a, 0xcd, 0x6e, 0xc9, 0xe5, 0xaa, 0x81, 0xdd, 0x8c, 0x36, 0xc3, 0x99, 0xba, 0x05, 0xc3, - 0xf9, 0x75, 0x09, 0x0a, 0xed, 0xba, 0xcc, 0x87, 0x4f, 0x87, 0x63, 0xa1, 0x43, 0x82, 0xe8, 0x08, - 0x3e, 0xd4, 0xcb, 0x56, 0x4e, 0x64, 0x1a, 0x1d, 0xb5, 0xf1, 0xed, 0xce, 0x03, 0xa6, 0xc3, 0x16, - 0xda, 0x9a, 0x59, 0xff, 0xc8, 0xa6, 0xcf, 0x17, 0x5a, 0xfc, 0xea, 0x9f, 0x8b, 0xdc, 0xfb, 0x1a, - 0x4c, 0x75, 0x90, 0xfa, 0x76, 0xc7, 0xbd, 0xbd, 0x8e, 0x83, 0x79, 0xab, 0xd3, 0xf7, 0xc7, 0xf9, - 0x4c, 0x08, 0xdf, 0xc0, 0x0e, 0xac, 0xc5, 0xda, 0x3d, 0xe1, 0x92, 0xdf, 0x02, 0x77, 0xb4, 0xa5, - 0xe2, 0xb2, 0x15, 0x21, 0xb5, 0xa7, 0x3b, 0x2e, 0x17, 0xeb, 0xbe, 0x4e, 0x62, 0x45, 0xa8, 0x29, - 0x8d, 0x8c, 0x20, 0x47, 0x59, 0x6f, 0x98, 0x66, 0x9d, 0x8b, 0x21, 0x5f, 0x82, 0xf1, 0x00, 0x8c, - 0x37, 0x72, 0x0e, 0x52, 0x96, 0xc9, 0x3f, 0x4f, 0x30, 0x7c, 0xe6, 0x64, 0xc7, 0xdd, 0x7b, 0xd3, - 0xac, 0xf3, 0x6e, 0x53, 0x7c, 0x79, 0x12, 0x10, 0x63, 0x46, 0x37, 0xf2, 0x45, 0x13, 0x9b, 0x30, - 0x11, 0x82, 0xf2, 0x46, 0x7e, 0xa0, 0x43, 0x82, 0x33, 0xdf, 0x3e, 0x0a, 0x03, 0x94, 0x2b, 0xfa, - 0x98, 0x04, 0x10, 0x38, 0x11, 0x9e, 0xed, 0xc4, 0xa6, 0xfd, 0x9a, 0xb8, 0x30, 0xd7, 0x33, 0x3e, - 0xcf, 0xd9, 0x4e, 0xbf, 0xfb, 0xdf, 0x7c, 0xeb, 0x23, 0x89, 0x7b, 0x90, 0x3c, 0xd7, 0x61, 0x35, - 0x1e, 0x98, 0x2f, 0x9f, 0x09, 0xbd, 0x7d, 0x7f, 0xb8, 0xb7, 0xa6, 0x84, 0x64, 0xb3, 0xbd, 0xa2, - 0x73, 0xc1, 0xce, 0x53, 0xc1, 0xce, 0xa2, 0xc7, 0xe2, 0x05, 0x9b, 0x7b, 0x47, 0x78, 0xd2, 0xbc, - 0x0b, 0xfd, 0x8e, 0x04, 0x93, 0xed, 0x96, 0x74, 0xe8, 0xc9, 0xde, 0xa4, 0x68, 0x4d, 0x29, 0x0a, - 0x4f, 0x1d, 0x82, 0x92, 0x77, 0x65, 0x89, 0x76, 0x65, 0x1e, 0x3d, 0x73, 0x88, 0xae, 0xcc, 0x05, - 0xf7, 0xf7, 0xff, 0x97, 0x04, 0x77, 0x76, 0x5d, 0x21, 0xa1, 0xf9, 0xde, 0xa4, 0xec, 0x92, 0x3b, - 0x15, 0x4a, 0x3f, 0x08, 0x0b, 0xde, 0xe3, 0xe7, 0x68, 0x8f, 0x2f, 0xa1, 0xe5, 0xc3, 0xf4, 0xb8, - 0xed, 0x21, 0x0a, 0xfa, 0xad, 0xf0, 0xcd, 0xc2, 0xee, 0xe6, 0xd4, 0xb2, 0xf0, 0x88, 0x99, 0x18, - 0xad, 0x49, 0xad, 0xfc, 0x02, 0xed, 0x82, 0x82, 0x36, 0x7e, 0xc0, 0x41, 0x9b, 0x7b, 0x47, 0xd8, - 0xf1, 0xbf, 0x0b, 0xfd, 0x4f, 0xa9, 0xfd, 0x45, 0xc1, 0x27, 0xba, 0x8a, 0xd8, 0x79, 0x51, 0x55, - 0x78, 0xb2, 0x7f, 0x42, 0xde, 0xc9, 0x06, 0xed, 0x64, 0x0d, 0xe1, 0x5b, 0xdd, 0xc9, 0xb6, 0x83, - 0x88, 0xbe, 0x2a, 0xc1, 0x64, 0xbb, 0x35, 0x49, 0xcc, 0xb4, 0xec, 0xb2, 0xc8, 0x8a, 0x99, 0x96, - 0xdd, 0x16, 0x40, 0xf2, 0x9b, 0x68, 0xe7, 0xcf, 0xa1, 0xc7, 0x3b, 0x75, 0xbe, 0xeb, 0x28, 0x92, - 0xb9, 0xd8, 0x35, 0xc9, 0x8f, 0x99, 0x8b, 0xbd, 0xac, 0x63, 0x62, 0xe6, 0x62, 0x4f, 0x6b, 0x8c, - 0xf8, 0xb9, 0xe8, 0xf5, 0xac, 0xc7, 0x61, 0x74, 0xd0, 0x97, 0x25, 0x18, 0x09, 0x65, 0xc4, 0xe8, - 0xd1, 0xae, 0x82, 0xb6, 0x5b, 0x30, 0x14, 0xce, 0xf4, 0x43, 0xc2, 0xfb, 0xb2, 0x4c, 0xfb, 0xb2, - 0x80, 0xe6, 0x0f, 0xd3, 0x97, 0xf0, 0x59, 0xe9, 0xd7, 0x25, 0x98, 0x68, 0x93, 0x65, 0xc6, 0xcc, - 0xc2, 0xce, 0x49, 0x73, 0xe1, 0xc9, 0xfe, 0x09, 0x79, 0xaf, 0x2e, 0xd0, 0x5e, 0xfd, 0x04, 0x7a, - 0xfa, 0x30, 0xbd, 0x0a, 0xc4, 0xe7, 0x1b, 0xfe, 0xbd, 0xab, 0x40, 0x3b, 0xe8, 0x5c, 0x9f, 0x82, - 0x89, 0x0e, 0x3d, 0xd1, 0x37, 0x1d, 0xef, 0xcf, 0xf3, 0xb4, 0x3f, 0xcf, 0xa1, 0xf5, 0x1f, 0xac, - 0x3f, 0xad, 0x61, 0xfd, 0xf3, 0xad, 0x2f, 0x00, 0xbb, 0x5b, 0x51, 0xdb, 0x64, 0xb5, 0xf0, 0x58, - 0x5f, 0x34, 0xbc, 0x53, 0x4f, 0xd2, 0x4e, 0x9d, 0x41, 0x8f, 0x74, 0xea, 0x54, 0xe0, 0x72, 0x9d, - 0x6e, 0xec, 0x9a, 0x73, 0xef, 0x60, 0x29, 0xf0, 0xbb, 0xd0, 0x4f, 0x89, 0x8b, 0x4d, 0xa7, 0xba, - 0xb6, 0x1b, 0xc8, 0x63, 0x0b, 0x0f, 0xf4, 0x80, 0xc9, 0xe5, 0xba, 0x87, 0xca, 0x35, 0x85, 0x4e, - 0x76, 0x92, 0x8b, 0xe4, 0xb2, 0xe8, 0x03, 0x92, 0x77, 0x17, 0xf2, 0x74, 0x77, 0xde, 0xc1, 0x64, - 0xb7, 0xf0, 0x60, 0x4f, 0xb8, 0x5c, 0x92, 0xfb, 0xa8, 0x24, 0x33, 0x68, 0xaa, 0xa3, 0x24, 0x2c, - 0xf5, 0xbd, 0xd5, 0x37, 0x07, 0x5e, 0x3d, 0x0e, 0xd3, 0x1d, 0x5a, 0x74, 0xaf, 0xc5, 0x9c, 0x71, - 0x75, 0x79, 0x08, 0x1b, 0xfb, 0xd0, 0xb5, 0xc3, 0xd3, 0xda, 0xc3, 0x3f, 0x7f, 0xed, 0xed, 0x40, - 0xec, 0x5f, 0xa7, 0x00, 0xad, 0x3a, 0xb5, 0x05, 0x1b, 0xb3, 0x7f, 0x7a, 0xc7, 0x67, 0x79, 0xe4, - 0x85, 0x97, 0xf4, 0x03, 0xbd, 0xf0, 0x5a, 0x0d, 0xbd, 0x99, 0x4a, 0xf4, 0xf7, 0x2e, 0xb3, 0xe7, - 0x87, 0x53, 0xc9, 0x1f, 0xca, 0xc3, 0xa9, 0xf6, 0xf7, 0xaa, 0x53, 0xb7, 0xee, 0x01, 0xc6, 0xc0, - 0x61, 0x1f, 0xa1, 0xf0, 0xf7, 0x90, 0x83, 0x5d, 0xde, 0x43, 0xe6, 0x3b, 0x3e, 0x7a, 0xe4, 0xd4, - 0xe8, 0xac, 0xf8, 0x80, 0xef, 0x50, 0x6f, 0x37, 0x61, 0xf9, 0x17, 0x7e, 0xfd, 0x2d, 0x84, 0x93, - 0x50, 0x68, 0x35, 0x27, 0x6f, 0x52, 0x7f, 0x24, 0x09, 0xb9, 0x55, 0xa7, 0x56, 0xae, 0xea, 0xee, - 0x6d, 0xb2, 0xb5, 0x67, 0x3a, 0x3f, 0x6a, 0x41, 0x37, 0x6f, 0x4c, 0x8f, 0x32, 0x9d, 0x76, 0xd1, - 0x64, 0x03, 0xc6, 0x22, 0x4f, 0x89, 0xb9, 0x65, 0x2d, 0x1e, 0xe6, 0x45, 0x73, 0x84, 0x95, 0x4c, - 0xdf, 0x20, 0x04, 0xec, 0x1b, 0x5d, 0x6b, 0x6f, 0xcc, 0xcc, 0xa0, 0x2e, 0xde, 0xce, 0x17, 0x80, - 0xfe, 0x98, 0x15, 0x20, 0x1f, 0x1d, 0x14, 0x6f, 0xc4, 0xfe, 0x50, 0x82, 0xe1, 0x55, 0x47, 0xa4, - 0x82, 0xf8, 0xc7, 0xf4, 0xfd, 0xd1, 0x13, 0xde, 0x77, 0x58, 0x93, 0xbd, 0xd9, 0xad, 0xf8, 0x36, - 0xab, 0xaf, 0x84, 0xa3, 0x30, 0x11, 0xe8, 0xa7, 0xd7, 0xff, 0xdf, 0x4e, 0x50, 0xff, 0x58, 0xc2, - 0x35, 0xdd, 0xf0, 0xb2, 0x48, 0xfc, 0x17, 0xf5, 0x75, 0x85, 0xaf, 0xe7, 0xd4, 0x61, 0xf5, 0xbc, - 0x4f, 0x1d, 0x44, 0x44, 0x9f, 0xde, 0xc6, 0xd7, 0x6a, 0xeb, 0xdb, 0x1f, 0xa9, 0x8f, 0xcf, 0xea, - 0x44, 0x5e, 0xf8, 0xc8, 0x6f, 0x48, 0x30, 0xb2, 0xea, 0xd4, 0xb6, 0x8d, 0xea, 0xff, 0xf3, 0xf6, - 0xbb, 0x0b, 0x47, 0x43, 0x3d, 0xbd, 0x4d, 0x2a, 0x3d, 0xf3, 0x5a, 0x0a, 0x92, 0xab, 0x4e, 0x0d, - 0xbd, 0x04, 0x63, 0xd1, 0xa4, 0xa1, 0x63, 0x2e, 0xd8, 0x1a, 0x11, 0x3a, 0xaf, 0xd7, 0x3a, 0x47, - 0x0f, 0xb4, 0x0f, 0x23, 0xe1, 0xc8, 0x71, 0xaa, 0x0b, 0x93, 0x10, 0x66, 0xe1, 0x91, 0x5e, 0x31, - 0xbd, 0xc6, 0xde, 0x0e, 0x69, 0xcf, 0xe9, 0xdd, 0xdd, 0x85, 0x5a, 0x20, 0x75, 0xce, 0x6e, 0xdb, - 0xb8, 0x15, 0xa2, 0xbd, 0xa8, 0x4b, 0xe9, 0xa6, 0xbd, 0x08, 0x6e, 0x57, 0xed, 0x75, 0x9a, 0x5a, - 0x3b, 0x00, 0x81, 0x79, 0x70, 0x6f, 0x17, 0x0e, 0x3e, 0x5a, 0xe1, 0xe1, 0x9e, 0xd0, 0xbc, 0x43, - 0xa7, 0x5b, 0x9c, 0x8c, 0xff, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd5, 0xca, 0x34, 0x9e, 0x6a, - 0x94, 0x00, 0x00, + // 9822 bytes of a gzipped FileDescriptorSet + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x6d, 0x70, 0x5c, 0xd7, + 0x75, 0x18, 0xde, 0x7e, 0x00, 0xbb, 0x07, 0x0b, 0x60, 0x71, 0x01, 0x92, 0xcb, 0x25, 0x09, 0x40, + 0x4f, 0x5f, 0x14, 0x25, 0x82, 0x12, 0x25, 0x52, 0xd2, 0xd2, 0x96, 0xbc, 0x8b, 0x5d, 0x82, 0x10, + 0xf1, 0xa5, 0x07, 0x80, 0x92, 0x65, 0xa7, 0x3b, 0x0f, 0xbb, 0x17, 0x8b, 0x27, 0xec, 0xbe, 0xf7, + 0xf4, 0xde, 0x5b, 0x12, 0x90, 0xed, 0x19, 0xc5, 0x76, 0x5d, 0x9b, 0x69, 0x6a, 0xbb, 0xce, 0xa4, + 0xb6, 0x62, 0xba, 0x76, 0x9c, 0xd6, 0xa9, 0xe3, 0x36, 0x1f, 0x76, 0xdd, 0xa6, 0xed, 0x4c, 0xed, + 0xb6, 0x69, 0x6c, 0xb7, 0xc9, 0xd8, 0x6d, 0xa6, 0x4d, 0x33, 0x2d, 0x9d, 0xca, 0x9e, 0x54, 0x75, + 0xdd, 0xc6, 0x61, 0x9c, 0x36, 0x1d, 0x4f, 0xa7, 0x9d, 0xfb, 0xf5, 0xbe, 0xf6, 0x13, 0x10, 0x69, + 0x3b, 0x4d, 0x7e, 0x61, 0xef, 0xb9, 0xe7, 0x9c, 0x7b, 0xee, 0xb9, 0xe7, 0x9c, 0x7b, 0xee, 0xd7, + 0x03, 0xfc, 0xf3, 0x0b, 0x30, 0x53, 0x33, 0x8c, 0x5a, 0x1d, 0x9f, 0x31, 0x2d, 0xc3, 0x31, 0x36, + 0x9b, 0x5b, 0x67, 0xaa, 0xd8, 0xae, 0x58, 0x9a, 0xe9, 0x18, 0xd6, 0x2c, 0x85, 0xa1, 0x31, 0x86, + 0x31, 0x2b, 0x30, 0xe4, 0x25, 0x18, 0xbf, 0xa8, 0xd5, 0x71, 0xd1, 0x45, 0x5c, 0xc3, 0x0e, 0x7a, + 0x02, 0x62, 0x5b, 0x5a, 0x1d, 0x67, 0xa4, 0x99, 0xe8, 0xc9, 0xe1, 0xb3, 0xf7, 0xcc, 0x86, 0x88, + 0x66, 0x83, 0x14, 0xab, 0x04, 0xac, 0x50, 0x0a, 0xf9, 0xdb, 0x31, 0x98, 0x68, 0x53, 0x8b, 0x10, + 0xc4, 0x74, 0xb5, 0x41, 0x38, 0x4a, 0x27, 0x93, 0x0a, 0xfd, 0x8d, 0x32, 0x30, 0x64, 0xaa, 0x95, + 0x1d, 0xb5, 0x86, 0x33, 0x11, 0x0a, 0x16, 0x45, 0x34, 0x05, 0x50, 0xc5, 0x26, 0xd6, 0xab, 0x58, + 0xaf, 0xec, 0x65, 0xa2, 0x33, 0xd1, 0x93, 0x49, 0xc5, 0x07, 0x41, 0x0f, 0xc2, 0xb8, 0xd9, 0xdc, + 0xac, 0x6b, 0x95, 0xb2, 0x0f, 0x0d, 0x66, 0xa2, 0x27, 0xe3, 0x4a, 0x9a, 0x55, 0x14, 0x3d, 0xe4, + 0xfb, 0x61, 0xec, 0x1a, 0x56, 0x77, 0xfc, 0xa8, 0xc3, 0x14, 0x75, 0x94, 0x80, 0x7d, 0x88, 0x73, + 0x90, 0x6a, 0x60, 0xdb, 0x56, 0x6b, 0xb8, 0xec, 0xec, 0x99, 0x38, 0x13, 0xa3, 0xbd, 0x9f, 0x69, + 0xe9, 0x7d, 0xb8, 0xe7, 0xc3, 0x9c, 0x6a, 0x7d, 0xcf, 0xc4, 0x28, 0x0f, 0x49, 0xac, 0x37, 0x1b, + 0x8c, 0x43, 0xbc, 0x83, 0xfe, 0x4a, 0x7a, 0xb3, 0x11, 0xe6, 0x92, 0x20, 0x64, 0x9c, 0xc5, 0x90, + 0x8d, 0xad, 0xab, 0x5a, 0x05, 0x67, 0x06, 0x29, 0x83, 0xfb, 0x5b, 0x18, 0xac, 0xb1, 0xfa, 0x30, + 0x0f, 0x41, 0x87, 0xe6, 0x20, 0x89, 0x77, 0x1d, 0xac, 0xdb, 0x9a, 0xa1, 0x67, 0x86, 0x28, 0x93, + 0x7b, 0xdb, 0x8c, 0x22, 0xae, 0x57, 0xc3, 0x2c, 0x3c, 0x3a, 0x74, 0x1e, 0x86, 0x0c, 0xd3, 0xd1, + 0x0c, 0xdd, 0xce, 0x24, 0x66, 0xa4, 0x93, 0xc3, 0x67, 0x8f, 0xb7, 0x35, 0x84, 0x15, 0x86, 0xa3, + 0x08, 0x64, 0xb4, 0x00, 0x69, 0xdb, 0x68, 0x5a, 0x15, 0x5c, 0xae, 0x18, 0x55, 0x5c, 0xd6, 0xf4, + 0x2d, 0x23, 0x93, 0xa4, 0x0c, 0xa6, 0x5b, 0x3b, 0x42, 0x11, 0xe7, 0x8c, 0x2a, 0x5e, 0xd0, 0xb7, + 0x0c, 0x65, 0xd4, 0x0e, 0x94, 0xd1, 0x61, 0x18, 0xb4, 0xf7, 0x74, 0x47, 0xdd, 0xcd, 0xa4, 0xa8, + 0x85, 0xf0, 0x92, 0xfc, 0xeb, 0x83, 0x30, 0xd6, 0x8f, 0x89, 0x5d, 0x80, 0xf8, 0x16, 0xe9, 0x65, + 0x26, 0xb2, 0x1f, 0x1d, 0x30, 0x9a, 0xa0, 0x12, 0x07, 0x0f, 0xa8, 0xc4, 0x3c, 0x0c, 0xeb, 0xd8, + 0x76, 0x70, 0x95, 0x59, 0x44, 0xb4, 0x4f, 0x9b, 0x02, 0x46, 0xd4, 0x6a, 0x52, 0xb1, 0x03, 0x99, + 0xd4, 0xf3, 0x30, 0xe6, 0x8a, 0x54, 0xb6, 0x54, 0xbd, 0x26, 0x6c, 0xf3, 0x4c, 0x2f, 0x49, 0x66, + 0x4b, 0x82, 0x4e, 0x21, 0x64, 0xca, 0x28, 0x0e, 0x94, 0x51, 0x11, 0xc0, 0xd0, 0xb1, 0xb1, 0x55, + 0xae, 0xe2, 0x4a, 0x3d, 0x93, 0xe8, 0xa0, 0xa5, 0x15, 0x82, 0xd2, 0xa2, 0x25, 0x83, 0x41, 0x2b, + 0x75, 0xf4, 0xa4, 0x67, 0x6a, 0x43, 0x1d, 0x2c, 0x65, 0x89, 0x39, 0x59, 0x8b, 0xb5, 0x6d, 0xc0, + 0xa8, 0x85, 0x89, 0xdd, 0xe3, 0x2a, 0xef, 0x59, 0x92, 0x0a, 0x31, 0xdb, 0xb3, 0x67, 0x0a, 0x27, + 0x63, 0x1d, 0x1b, 0xb1, 0xfc, 0x45, 0x74, 0x37, 0xb8, 0x80, 0x32, 0x35, 0x2b, 0xa0, 0x51, 0x28, + 0x25, 0x80, 0xcb, 0x6a, 0x03, 0x67, 0x5f, 0x86, 0xd1, 0xa0, 0x7a, 0xd0, 0x24, 0xc4, 0x6d, 0x47, + 0xb5, 0x1c, 0x6a, 0x85, 0x71, 0x85, 0x15, 0x50, 0x1a, 0xa2, 0x58, 0xaf, 0xd2, 0x28, 0x17, 0x57, + 0xc8, 0x4f, 0xf4, 0x16, 0xaf, 0xc3, 0x51, 0xda, 0xe1, 0xfb, 0x5a, 0x47, 0x34, 0xc0, 0x39, 0xdc, + 0xef, 0xec, 0xe3, 0x30, 0x12, 0xe8, 0x40, 0xbf, 0x4d, 0xcb, 0xef, 0x84, 0x43, 0x6d, 0x59, 0xa3, + 0xe7, 0x61, 0xb2, 0xa9, 0x6b, 0xba, 0x83, 0x2d, 0xd3, 0xc2, 0xc4, 0x62, 0x59, 0x53, 0x99, 0xff, + 0x32, 0xd4, 0xc1, 0xe6, 0x36, 0xfc, 0xd8, 0x8c, 0x8b, 0x32, 0xd1, 0x6c, 0x05, 0x9e, 0x4a, 0x26, + 0x5e, 0x1f, 0x4a, 0xbf, 0xf2, 0xca, 0x2b, 0xaf, 0x44, 0xe4, 0x2f, 0x0f, 0xc2, 0x64, 0x3b, 0x9f, + 0x69, 0xeb, 0xbe, 0x87, 0x61, 0x50, 0x6f, 0x36, 0x36, 0xb1, 0x45, 0x95, 0x14, 0x57, 0x78, 0x09, + 0xe5, 0x21, 0x5e, 0x57, 0x37, 0x71, 0x3d, 0x13, 0x9b, 0x91, 0x4e, 0x8e, 0x9e, 0x7d, 0xb0, 0x2f, + 0xaf, 0x9c, 0x5d, 0x24, 0x24, 0x0a, 0xa3, 0x44, 0x4f, 0x41, 0x8c, 0x87, 0x68, 0xc2, 0xe1, 0x54, + 0x7f, 0x1c, 0x88, 0x2f, 0x29, 0x94, 0x0e, 0x1d, 0x83, 0x24, 0xf9, 0xcb, 0x6c, 0x63, 0x90, 0xca, + 0x9c, 0x20, 0x00, 0x62, 0x17, 0x28, 0x0b, 0x09, 0xea, 0x26, 0x55, 0x2c, 0xa6, 0x36, 0xb7, 0x4c, + 0x0c, 0xab, 0x8a, 0xb7, 0xd4, 0x66, 0xdd, 0x29, 0x5f, 0x55, 0xeb, 0x4d, 0x4c, 0x0d, 0x3e, 0xa9, + 0xa4, 0x38, 0xf0, 0x0a, 0x81, 0xa1, 0x69, 0x18, 0x66, 0x5e, 0xa5, 0xe9, 0x55, 0xbc, 0x4b, 0xa3, + 0x67, 0x5c, 0x61, 0x8e, 0xb6, 0x40, 0x20, 0xa4, 0xf9, 0x17, 0x6d, 0x43, 0x17, 0xa6, 0x49, 0x9b, + 0x20, 0x00, 0xda, 0xfc, 0xe3, 0xe1, 0xc0, 0x7d, 0xa2, 0x7d, 0xf7, 0x5a, 0x7c, 0xe9, 0x7e, 0x18, + 0xa3, 0x18, 0x8f, 0xf2, 0xa1, 0x57, 0xeb, 0x99, 0xf1, 0x19, 0xe9, 0x64, 0x42, 0x19, 0x65, 0xe0, + 0x15, 0x0e, 0x95, 0xbf, 0x18, 0x81, 0x18, 0x0d, 0x2c, 0x63, 0x30, 0xbc, 0xfe, 0xd6, 0xd5, 0x52, + 0xb9, 0xb8, 0xb2, 0x51, 0x58, 0x2c, 0xa5, 0x25, 0x34, 0x0a, 0x40, 0x01, 0x17, 0x17, 0x57, 0xf2, + 0xeb, 0xe9, 0x88, 0x5b, 0x5e, 0x58, 0x5e, 0x3f, 0xff, 0x58, 0x3a, 0xea, 0x12, 0x6c, 0x30, 0x40, + 0xcc, 0x8f, 0xf0, 0xe8, 0xd9, 0x74, 0x1c, 0xa5, 0x21, 0xc5, 0x18, 0x2c, 0x3c, 0x5f, 0x2a, 0x9e, + 0x7f, 0x2c, 0x3d, 0x18, 0x84, 0x3c, 0x7a, 0x36, 0x3d, 0x84, 0x46, 0x20, 0x49, 0x21, 0x85, 0x95, + 0x95, 0xc5, 0x74, 0xc2, 0xe5, 0xb9, 0xb6, 0xae, 0x2c, 0x2c, 0xcf, 0xa7, 0x93, 0x2e, 0xcf, 0x79, + 0x65, 0x65, 0x63, 0x35, 0x0d, 0x2e, 0x87, 0xa5, 0xd2, 0xda, 0x5a, 0x7e, 0xbe, 0x94, 0x1e, 0x76, + 0x31, 0x0a, 0x6f, 0x5d, 0x2f, 0xad, 0xa5, 0x53, 0x01, 0xb1, 0x1e, 0x3d, 0x9b, 0x1e, 0x71, 0x9b, + 0x28, 0x2d, 0x6f, 0x2c, 0xa5, 0x47, 0xd1, 0x38, 0x8c, 0xb0, 0x26, 0x84, 0x10, 0x63, 0x21, 0xd0, + 0xf9, 0xc7, 0xd2, 0x69, 0x4f, 0x10, 0xc6, 0x65, 0x3c, 0x00, 0x38, 0xff, 0x58, 0x1a, 0xc9, 0x73, + 0x10, 0xa7, 0x66, 0x88, 0x10, 0x8c, 0x2e, 0xe6, 0x0b, 0xa5, 0xc5, 0xf2, 0xca, 0xea, 0xfa, 0xc2, + 0xca, 0x72, 0x7e, 0x31, 0x2d, 0x79, 0x30, 0xa5, 0xf4, 0xec, 0xc6, 0x82, 0x52, 0x2a, 0xa6, 0x23, + 0x7e, 0xd8, 0x6a, 0x29, 0xbf, 0x5e, 0x2a, 0xa6, 0xa3, 0x72, 0x05, 0x26, 0xdb, 0x05, 0xd4, 0xb6, + 0x2e, 0xe4, 0xb3, 0x85, 0x48, 0x07, 0x5b, 0xa0, 0xbc, 0xc2, 0xb6, 0x20, 0x7f, 0x2b, 0x02, 0x13, + 0x6d, 0x26, 0x95, 0xb6, 0x8d, 0x3c, 0x0d, 0x71, 0x66, 0xcb, 0x6c, 0x9a, 0x7d, 0xa0, 0xed, 0xec, + 0x44, 0x2d, 0xbb, 0x65, 0xaa, 0xa5, 0x74, 0xfe, 0x54, 0x23, 0xda, 0x21, 0xd5, 0x20, 0x2c, 0x5a, + 0x0c, 0xf6, 0x27, 0x5a, 0x82, 0x3f, 0x9b, 0x1f, 0xcf, 0xf7, 0x33, 0x3f, 0x52, 0xd8, 0xfe, 0x26, + 0x81, 0x78, 0x9b, 0x49, 0xe0, 0x02, 0x8c, 0xb7, 0x30, 0xea, 0x3b, 0x18, 0xbf, 0x47, 0x82, 0x4c, + 0x27, 0xe5, 0xf4, 0x08, 0x89, 0x91, 0x40, 0x48, 0xbc, 0x10, 0xd6, 0xe0, 0x5d, 0x9d, 0x07, 0xa1, + 0x65, 0xac, 0x3f, 0x23, 0xc1, 0xe1, 0xf6, 0x29, 0x65, 0x5b, 0x19, 0x9e, 0x82, 0xc1, 0x06, 0x76, + 0xb6, 0x0d, 0x91, 0x56, 0xdd, 0xd7, 0x66, 0xb2, 0x26, 0xd5, 0xe1, 0xc1, 0xe6, 0x54, 0xfe, 0xd9, + 0x3e, 0xda, 0x29, 0x2f, 0x64, 0xd2, 0xb4, 0x48, 0xfa, 0x81, 0x08, 0x1c, 0x6a, 0xcb, 0xbc, 0xad, + 0xa0, 0x27, 0x00, 0x34, 0xdd, 0x6c, 0x3a, 0x2c, 0x75, 0x62, 0x91, 0x38, 0x49, 0x21, 0x34, 0x78, + 0x91, 0x28, 0xdb, 0x74, 0xdc, 0xfa, 0x28, 0xad, 0x07, 0x06, 0xa2, 0x08, 0x4f, 0x78, 0x82, 0xc6, + 0xa8, 0xa0, 0x53, 0x1d, 0x7a, 0xda, 0x62, 0x98, 0x0f, 0x43, 0xba, 0x52, 0xd7, 0xb0, 0xee, 0x94, + 0x6d, 0xc7, 0xc2, 0x6a, 0x43, 0xd3, 0x6b, 0x74, 0xaa, 0x49, 0xe4, 0xe2, 0x5b, 0x6a, 0xdd, 0xc6, + 0xca, 0x18, 0xab, 0x5e, 0x13, 0xb5, 0x84, 0x82, 0x1a, 0x90, 0xe5, 0xa3, 0x18, 0x0c, 0x50, 0xb0, + 0x6a, 0x97, 0x42, 0xfe, 0x70, 0x12, 0x86, 0x7d, 0x09, 0x38, 0xba, 0x0b, 0x52, 0x2f, 0xaa, 0x57, + 0xd5, 0xb2, 0x58, 0x54, 0x31, 0x4d, 0x0c, 0x13, 0xd8, 0x2a, 0x5f, 0x58, 0x3d, 0x0c, 0x93, 0x14, + 0xc5, 0x68, 0x3a, 0xd8, 0x2a, 0x57, 0xea, 0xaa, 0x6d, 0x53, 0xa5, 0x25, 0x28, 0x2a, 0x22, 0x75, + 0x2b, 0xa4, 0x6a, 0x4e, 0xd4, 0xa0, 0x73, 0x30, 0x41, 0x29, 0x1a, 0xcd, 0xba, 0xa3, 0x99, 0x75, + 0x5c, 0x26, 0xcb, 0x3c, 0x9b, 0x4e, 0x39, 0xae, 0x64, 0xe3, 0x04, 0x63, 0x89, 0x23, 0x10, 0x89, + 0x6c, 0x54, 0x84, 0x13, 0x94, 0xac, 0x86, 0x75, 0x6c, 0xa9, 0x0e, 0x2e, 0xe3, 0x97, 0x9a, 0x6a, + 0xdd, 0x2e, 0xab, 0x7a, 0xb5, 0xbc, 0xad, 0xda, 0xdb, 0x99, 0x49, 0xc2, 0xa0, 0x10, 0xc9, 0x48, + 0xca, 0x51, 0x82, 0x38, 0xcf, 0xf1, 0x4a, 0x14, 0x2d, 0xaf, 0x57, 0x2f, 0xa9, 0xf6, 0x36, 0xca, + 0xc1, 0x61, 0xca, 0xc5, 0x76, 0x2c, 0x4d, 0xaf, 0x95, 0x2b, 0xdb, 0xb8, 0xb2, 0x53, 0x6e, 0x3a, + 0x5b, 0x4f, 0x64, 0x8e, 0xf9, 0xdb, 0xa7, 0x12, 0xae, 0x51, 0x9c, 0x39, 0x82, 0xb2, 0xe1, 0x6c, + 0x3d, 0x81, 0xd6, 0x20, 0x45, 0x06, 0xa3, 0xa1, 0xbd, 0x8c, 0xcb, 0x5b, 0x86, 0x45, 0xe7, 0xd0, + 0xd1, 0x36, 0xa1, 0xc9, 0xa7, 0xc1, 0xd9, 0x15, 0x4e, 0xb0, 0x64, 0x54, 0x71, 0x2e, 0xbe, 0xb6, + 0x5a, 0x2a, 0x15, 0x95, 0x61, 0xc1, 0xe5, 0xa2, 0x61, 0x11, 0x83, 0xaa, 0x19, 0xae, 0x82, 0x87, + 0x99, 0x41, 0xd5, 0x0c, 0xa1, 0xde, 0x73, 0x30, 0x51, 0xa9, 0xb0, 0x3e, 0x6b, 0x95, 0x32, 0x5f, + 0x8c, 0xd9, 0x99, 0x74, 0x40, 0x59, 0x95, 0xca, 0x3c, 0x43, 0xe0, 0x36, 0x6e, 0xa3, 0x27, 0xe1, + 0x90, 0xa7, 0x2c, 0x3f, 0xe1, 0x78, 0x4b, 0x2f, 0xc3, 0xa4, 0xe7, 0x60, 0xc2, 0xdc, 0x6b, 0x25, + 0x44, 0x81, 0x16, 0xcd, 0xbd, 0x30, 0xd9, 0xe3, 0x30, 0x69, 0x6e, 0x9b, 0xad, 0x74, 0xa7, 0xfc, + 0x74, 0xc8, 0xdc, 0x36, 0xc3, 0x84, 0xf7, 0xd2, 0x95, 0xb9, 0x85, 0x2b, 0xaa, 0x83, 0xab, 0x99, + 0x23, 0x7e, 0x74, 0x5f, 0x05, 0x9a, 0x85, 0x74, 0xa5, 0x52, 0xc6, 0xba, 0xba, 0x59, 0xc7, 0x65, + 0xd5, 0xc2, 0xba, 0x6a, 0x67, 0xa6, 0x29, 0x72, 0xcc, 0xb1, 0x9a, 0x58, 0x19, 0xad, 0x54, 0x4a, + 0xb4, 0x32, 0x4f, 0xeb, 0xd0, 0x29, 0x18, 0x37, 0x36, 0x5f, 0xac, 0x30, 0x8b, 0x2c, 0x9b, 0x16, + 0xde, 0xd2, 0x76, 0x33, 0xf7, 0x50, 0xf5, 0x8e, 0x91, 0x0a, 0x6a, 0x8f, 0xab, 0x14, 0x8c, 0x1e, + 0x80, 0x74, 0xc5, 0xde, 0x56, 0x2d, 0x93, 0x86, 0x64, 0xdb, 0x54, 0x2b, 0x38, 0x73, 0x2f, 0x43, + 0x65, 0xf0, 0x65, 0x01, 0x26, 0x1e, 0x61, 0x5f, 0xd3, 0xb6, 0x1c, 0xc1, 0xf1, 0x7e, 0xe6, 0x11, + 0x14, 0xc6, 0xb9, 0x9d, 0x84, 0x34, 0xd1, 0x44, 0xa0, 0xe1, 0x93, 0x14, 0x6d, 0xd4, 0xdc, 0x36, + 0xfd, 0xed, 0xde, 0x0d, 0x23, 0x04, 0xd3, 0x6b, 0xf4, 0x01, 0x96, 0xb8, 0x99, 0xdb, 0xbe, 0x16, + 0x1f, 0x83, 0xc3, 0x04, 0xa9, 0x81, 0x1d, 0xb5, 0xaa, 0x3a, 0xaa, 0x0f, 0xfb, 0x21, 0x8a, 0x4d, + 0xd4, 0xbe, 0xc4, 0x2b, 0x03, 0x72, 0x5a, 0xcd, 0xcd, 0x3d, 0xd7, 0xb0, 0x4e, 0x33, 0x39, 0x09, + 0x4c, 0x98, 0xd6, 0x1d, 0x4b, 0xce, 0xe5, 0x1c, 0xa4, 0xfc, 0x76, 0x8f, 0x92, 0xc0, 0x2c, 0x3f, + 0x2d, 0x91, 0x24, 0x68, 0x6e, 0xa5, 0x48, 0xd2, 0x97, 0x17, 0x4a, 0xe9, 0x08, 0x49, 0xa3, 0x16, + 0x17, 0xd6, 0x4b, 0x65, 0x65, 0x63, 0x79, 0x7d, 0x61, 0xa9, 0x94, 0x8e, 0xfa, 0x12, 0xfb, 0x67, + 0x62, 0x89, 0xfb, 0xd2, 0xf7, 0xcb, 0xdf, 0x88, 0xc0, 0x68, 0x70, 0xa5, 0x86, 0xde, 0x04, 0x47, + 0xc4, 0xb6, 0x8a, 0x8d, 0x9d, 0xf2, 0x35, 0xcd, 0xa2, 0x0e, 0xd9, 0x50, 0xd9, 0xe4, 0xe8, 0xda, + 0xcf, 0x24, 0xc7, 0x5a, 0xc3, 0xce, 0x73, 0x9a, 0x45, 0xdc, 0xad, 0xa1, 0x3a, 0x68, 0x11, 0xa6, + 0x75, 0xa3, 0x6c, 0x3b, 0xaa, 0x5e, 0x55, 0xad, 0x6a, 0xd9, 0xdb, 0xd0, 0x2a, 0xab, 0x95, 0x0a, + 0xb6, 0x6d, 0x83, 0x4d, 0x84, 0x2e, 0x97, 0xe3, 0xba, 0xb1, 0xc6, 0x91, 0xbd, 0x19, 0x22, 0xcf, + 0x51, 0x43, 0xe6, 0x1b, 0xed, 0x64, 0xbe, 0xc7, 0x20, 0xd9, 0x50, 0xcd, 0x32, 0xd6, 0x1d, 0x6b, + 0x8f, 0xe6, 0xe7, 0x09, 0x25, 0xd1, 0x50, 0xcd, 0x12, 0x29, 0xff, 0x50, 0x96, 0x49, 0xcf, 0xc4, + 0x12, 0x89, 0x74, 0xf2, 0x99, 0x58, 0x22, 0x99, 0x06, 0xf9, 0xb5, 0x28, 0xa4, 0xfc, 0xf9, 0x3a, + 0x59, 0xfe, 0x54, 0xe8, 0x8c, 0x25, 0xd1, 0x98, 0x76, 0x77, 0xd7, 0xec, 0x7e, 0x76, 0x8e, 0x4c, + 0x65, 0xb9, 0x41, 0x96, 0x1c, 0x2b, 0x8c, 0x92, 0xa4, 0x11, 0xc4, 0xd8, 0x30, 0x4b, 0x46, 0x12, + 0x0a, 0x2f, 0xa1, 0x79, 0x18, 0x7c, 0xd1, 0xa6, 0xbc, 0x07, 0x29, 0xef, 0x7b, 0xba, 0xf3, 0x7e, + 0x66, 0x8d, 0x32, 0x4f, 0x3e, 0xb3, 0x56, 0x5e, 0x5e, 0x51, 0x96, 0xf2, 0x8b, 0x0a, 0x27, 0x47, + 0x47, 0x21, 0x56, 0x57, 0x5f, 0xde, 0x0b, 0x4e, 0x7a, 0x14, 0xd4, 0xef, 0x20, 0x1c, 0x85, 0xd8, + 0x35, 0xac, 0xee, 0x04, 0xa7, 0x1a, 0x0a, 0xba, 0x83, 0xce, 0x70, 0x06, 0xe2, 0x54, 0x5f, 0x08, + 0x80, 0x6b, 0x2c, 0x3d, 0x80, 0x12, 0x10, 0x9b, 0x5b, 0x51, 0x88, 0x43, 0xa4, 0x21, 0xc5, 0xa0, + 0xe5, 0xd5, 0x85, 0xd2, 0x5c, 0x29, 0x1d, 0x91, 0xcf, 0xc1, 0x20, 0x53, 0x02, 0x71, 0x16, 0x57, + 0x0d, 0xe9, 0x01, 0x5e, 0xe4, 0x3c, 0x24, 0x51, 0xbb, 0xb1, 0x54, 0x28, 0x29, 0xe9, 0x48, 0x70, + 0xa8, 0x63, 0xe9, 0xb8, 0x6c, 0x43, 0xca, 0x9f, 0x87, 0xff, 0x70, 0x16, 0xe3, 0x5f, 0x92, 0x60, + 0xd8, 0x97, 0x57, 0x93, 0x84, 0x48, 0xad, 0xd7, 0x8d, 0x6b, 0x65, 0xb5, 0xae, 0xa9, 0x36, 0x37, + 0x0d, 0xa0, 0xa0, 0x3c, 0x81, 0xf4, 0x3b, 0x74, 0x3f, 0x24, 0x17, 0x89, 0xa7, 0x07, 0xe5, 0x4f, + 0x48, 0x90, 0x0e, 0x27, 0xb6, 0x21, 0x31, 0xa5, 0x1f, 0xa5, 0x98, 0xf2, 0xc7, 0x25, 0x18, 0x0d, + 0x66, 0xb3, 0x21, 0xf1, 0xee, 0xfa, 0x91, 0x8a, 0xf7, 0xfb, 0x11, 0x18, 0x09, 0xe4, 0xb0, 0xfd, + 0x4a, 0xf7, 0x12, 0x8c, 0x6b, 0x55, 0xdc, 0x30, 0x0d, 0x07, 0xeb, 0x95, 0xbd, 0x72, 0x1d, 0x5f, + 0xc5, 0xf5, 0x8c, 0x4c, 0x83, 0xc6, 0x99, 0xee, 0x59, 0xf2, 0xec, 0x82, 0x47, 0xb7, 0x48, 0xc8, + 0x72, 0x13, 0x0b, 0xc5, 0xd2, 0xd2, 0xea, 0xca, 0x7a, 0x69, 0x79, 0xee, 0xad, 0xe5, 0x8d, 0xe5, + 0xcb, 0xcb, 0x2b, 0xcf, 0x2d, 0x2b, 0x69, 0x2d, 0x84, 0x76, 0x07, 0xdd, 0x7e, 0x15, 0xd2, 0x61, + 0xa1, 0xd0, 0x11, 0x68, 0x27, 0x56, 0x7a, 0x00, 0x4d, 0xc0, 0xd8, 0xf2, 0x4a, 0x79, 0x6d, 0xa1, + 0x58, 0x2a, 0x97, 0x2e, 0x5e, 0x2c, 0xcd, 0xad, 0xaf, 0xb1, 0x7d, 0x0f, 0x17, 0x7b, 0x3d, 0xe0, + 0xe0, 0xf2, 0xab, 0x51, 0x98, 0x68, 0x23, 0x09, 0xca, 0xf3, 0x15, 0x0b, 0x5b, 0x44, 0x9d, 0xee, + 0x47, 0xfa, 0x59, 0x92, 0x33, 0xac, 0xaa, 0x96, 0xc3, 0x17, 0x38, 0x0f, 0x00, 0xd1, 0x92, 0xee, + 0x68, 0x5b, 0x1a, 0xb6, 0xf8, 0x7e, 0x12, 0x5b, 0xc6, 0x8c, 0x79, 0x70, 0xb6, 0xa5, 0xf4, 0x10, + 0x20, 0xd3, 0xb0, 0x35, 0x47, 0xbb, 0x8a, 0xcb, 0x9a, 0x2e, 0x36, 0x9f, 0xc8, 0xb2, 0x26, 0xa6, + 0xa4, 0x45, 0xcd, 0x82, 0xee, 0xb8, 0xd8, 0x3a, 0xae, 0xa9, 0x21, 0x6c, 0x12, 0xcc, 0xa3, 0x4a, + 0x5a, 0xd4, 0xb8, 0xd8, 0x77, 0x41, 0xaa, 0x6a, 0x34, 0x49, 0xae, 0xc7, 0xf0, 0xc8, 0xdc, 0x21, + 0x29, 0xc3, 0x0c, 0xe6, 0xa2, 0xf0, 0x2c, 0xde, 0xdb, 0xf5, 0x4a, 0x29, 0xc3, 0x0c, 0xc6, 0x50, + 0xee, 0x87, 0x31, 0xb5, 0x56, 0xb3, 0x08, 0x73, 0xc1, 0x88, 0xad, 0x4b, 0x46, 0x5d, 0x30, 0x45, + 0xcc, 0x3e, 0x03, 0x09, 0xa1, 0x07, 0x32, 0x55, 0x13, 0x4d, 0x94, 0x4d, 0xb6, 0xd8, 0x8e, 0x9c, + 0x4c, 0x2a, 0x09, 0x5d, 0x54, 0xde, 0x05, 0x29, 0xcd, 0x2e, 0x7b, 0x9b, 0xf8, 0x91, 0x99, 0xc8, + 0xc9, 0x84, 0x32, 0xac, 0xd9, 0xee, 0x06, 0xa8, 0xfc, 0x99, 0x08, 0x8c, 0x06, 0x0f, 0x21, 0x50, + 0x11, 0x12, 0x75, 0xa3, 0xa2, 0x52, 0xd3, 0x62, 0x27, 0x60, 0x27, 0x7b, 0x9c, 0x5b, 0xcc, 0x2e, + 0x72, 0x7c, 0xc5, 0xa5, 0xcc, 0xfe, 0xb6, 0x04, 0x09, 0x01, 0x46, 0x87, 0x21, 0x66, 0xaa, 0xce, + 0x36, 0x65, 0x17, 0x2f, 0x44, 0xd2, 0x92, 0x42, 0xcb, 0x04, 0x6e, 0x9b, 0xaa, 0x4e, 0x4d, 0x80, + 0xc3, 0x49, 0x99, 0x8c, 0x6b, 0x1d, 0xab, 0x55, 0xba, 0xe8, 0x31, 0x1a, 0x0d, 0xac, 0x3b, 0xb6, + 0x18, 0x57, 0x0e, 0x9f, 0xe3, 0x60, 0xf4, 0x20, 0x8c, 0x3b, 0x96, 0xaa, 0xd5, 0x03, 0xb8, 0x31, + 0x8a, 0x9b, 0x16, 0x15, 0x2e, 0x72, 0x0e, 0x8e, 0x0a, 0xbe, 0x55, 0xec, 0xa8, 0x95, 0x6d, 0x5c, + 0xf5, 0x88, 0x06, 0xe9, 0xe6, 0xc6, 0x11, 0x8e, 0x50, 0xe4, 0xf5, 0x82, 0x56, 0xfe, 0x86, 0x04, + 0xe3, 0x62, 0x99, 0x56, 0x75, 0x95, 0xb5, 0x04, 0xa0, 0xea, 0xba, 0xe1, 0xf8, 0xd5, 0xd5, 0x6a, + 0xca, 0x2d, 0x74, 0xb3, 0x79, 0x97, 0x48, 0xf1, 0x31, 0xc8, 0x36, 0x00, 0xbc, 0x9a, 0x8e, 0x6a, + 0x9b, 0x86, 0x61, 0x7e, 0xc2, 0x44, 0x8f, 0x29, 0xd9, 0xc2, 0x1e, 0x18, 0x88, 0xac, 0xe7, 0xd0, + 0x24, 0xc4, 0x37, 0x71, 0x4d, 0xd3, 0xf9, 0xbe, 0x31, 0x2b, 0x88, 0xed, 0x97, 0x98, 0xbb, 0xfd, + 0x52, 0xf8, 0xa0, 0x04, 0x13, 0x15, 0xa3, 0x11, 0x96, 0xb7, 0x90, 0x0e, 0xed, 0x2e, 0xd8, 0x97, + 0xa4, 0x17, 0x9e, 0xaa, 0x69, 0xce, 0x76, 0x73, 0x73, 0xb6, 0x62, 0x34, 0xce, 0xd4, 0x8c, 0xba, + 0xaa, 0xd7, 0xbc, 0x73, 0x56, 0xfa, 0xa3, 0x72, 0xba, 0x86, 0xf5, 0xd3, 0x35, 0xc3, 0x77, 0xea, + 0x7a, 0xc1, 0xfb, 0xf9, 0xa7, 0x92, 0xf4, 0xf3, 0x91, 0xe8, 0xfc, 0x6a, 0xe1, 0xb3, 0x91, 0xec, + 0x3c, 0x6b, 0x6e, 0x55, 0xa8, 0x47, 0xc1, 0x5b, 0x75, 0x5c, 0x21, 0x5d, 0x86, 0xef, 0x3c, 0x08, + 0x93, 0x35, 0xa3, 0x66, 0x50, 0x8e, 0x67, 0xc8, 0x2f, 0x7e, 0x72, 0x9b, 0x74, 0xa1, 0xd9, 0x9e, + 0xc7, 0xbc, 0xb9, 0x65, 0x98, 0xe0, 0xc8, 0x65, 0x7a, 0x74, 0xc4, 0x16, 0x36, 0xa8, 0xeb, 0xae, + 0x5a, 0xe6, 0x57, 0xbf, 0x4d, 0x27, 0x74, 0x65, 0x9c, 0x93, 0x92, 0x3a, 0xb6, 0xf6, 0xc9, 0x29, + 0x70, 0x28, 0xc0, 0x8f, 0xb9, 0x2d, 0xb6, 0x7a, 0x70, 0xfc, 0x0d, 0xce, 0x71, 0xc2, 0xc7, 0x71, + 0x8d, 0x93, 0xe6, 0xe6, 0x60, 0x64, 0x3f, 0xbc, 0xfe, 0x25, 0xe7, 0x95, 0xc2, 0x7e, 0x26, 0xf3, + 0x30, 0x46, 0x99, 0x54, 0x9a, 0xb6, 0x63, 0x34, 0x68, 0x4c, 0xec, 0xce, 0xe6, 0x37, 0xbf, 0xcd, + 0xfc, 0x68, 0x94, 0x90, 0xcd, 0xb9, 0x54, 0xb9, 0x1c, 0xd0, 0xd3, 0xb2, 0x2a, 0xae, 0xd4, 0x7b, + 0x70, 0xf8, 0x0a, 0x17, 0xc4, 0xc5, 0xcf, 0x5d, 0x81, 0x49, 0xf2, 0x9b, 0x86, 0x2c, 0xbf, 0x24, + 0xbd, 0xb7, 0xe0, 0x32, 0xdf, 0x78, 0x0f, 0x73, 0xd5, 0x09, 0x97, 0x81, 0x4f, 0x26, 0xdf, 0x28, + 0xd6, 0xb0, 0xe3, 0x60, 0xcb, 0x2e, 0xab, 0xf5, 0x76, 0xe2, 0xf9, 0xf6, 0x30, 0x32, 0x1f, 0xfb, + 0x6e, 0x70, 0x14, 0xe7, 0x19, 0x65, 0xbe, 0x5e, 0xcf, 0x6d, 0xc0, 0x91, 0x36, 0x56, 0xd1, 0x07, + 0xcf, 0x57, 0x39, 0xcf, 0xc9, 0x16, 0xcb, 0x20, 0x6c, 0x57, 0x41, 0xc0, 0xdd, 0xb1, 0xec, 0x83, + 0xe7, 0xcf, 0x71, 0x9e, 0x88, 0xd3, 0x8a, 0x21, 0x25, 0x1c, 0x9f, 0x81, 0xf1, 0xab, 0xd8, 0xda, + 0x34, 0x6c, 0xbe, 0x6f, 0xd4, 0x07, 0xbb, 0x8f, 0x73, 0x76, 0x63, 0x9c, 0x90, 0x6e, 0x24, 0x11, + 0x5e, 0x4f, 0x42, 0x62, 0x4b, 0xad, 0xe0, 0x3e, 0x58, 0xdc, 0xe0, 0x2c, 0x86, 0x08, 0x3e, 0x21, + 0xcd, 0x43, 0xaa, 0x66, 0xf0, 0x59, 0xab, 0x37, 0xf9, 0x27, 0x38, 0xf9, 0xb0, 0xa0, 0xe1, 0x2c, + 0x4c, 0xc3, 0x6c, 0xd6, 0xc9, 0x94, 0xd6, 0x9b, 0xc5, 0xdf, 0x14, 0x2c, 0x04, 0x0d, 0x67, 0xb1, + 0x0f, 0xb5, 0x7e, 0x52, 0xb0, 0xb0, 0x7d, 0xfa, 0x7c, 0x1a, 0x86, 0x0d, 0xbd, 0xbe, 0x67, 0xe8, + 0xfd, 0x08, 0xf1, 0x29, 0xce, 0x01, 0x38, 0x09, 0x61, 0x70, 0x01, 0x92, 0xfd, 0x0e, 0xc4, 0xdf, + 0xfa, 0xae, 0x70, 0x0f, 0x31, 0x02, 0xf3, 0x30, 0x26, 0x02, 0x94, 0x66, 0xe8, 0x7d, 0xb0, 0xf8, + 0xdb, 0x9c, 0xc5, 0xa8, 0x8f, 0x8c, 0x77, 0xc3, 0xc1, 0xb6, 0x53, 0xc3, 0xfd, 0x30, 0xf9, 0x8c, + 0xe8, 0x06, 0x27, 0xe1, 0xaa, 0xdc, 0xc4, 0x7a, 0x65, 0xbb, 0x3f, 0x0e, 0xbf, 0x28, 0x54, 0x29, + 0x68, 0x08, 0x8b, 0x39, 0x18, 0x69, 0xa8, 0x96, 0xbd, 0xad, 0xd6, 0xfb, 0x1a, 0x8e, 0xbf, 0xc3, + 0x79, 0xa4, 0x5c, 0x22, 0xae, 0x91, 0xa6, 0xbe, 0x1f, 0x36, 0x9f, 0x15, 0x1a, 0xf1, 0x91, 0x71, + 0xd7, 0xb3, 0x1d, 0xba, 0xc9, 0xb6, 0x1f, 0x6e, 0xbf, 0x24, 0x5c, 0x8f, 0xd1, 0x2e, 0xf9, 0x39, + 0x5e, 0x80, 0xa4, 0xad, 0xbd, 0xdc, 0x17, 0x9b, 0xcf, 0x89, 0x91, 0xa6, 0x04, 0x84, 0xf8, 0xad, + 0x70, 0xb4, 0xed, 0x34, 0xd1, 0x07, 0xb3, 0xbf, 0xcb, 0x99, 0x1d, 0x6e, 0x33, 0x55, 0xf0, 0x90, + 0xb0, 0x5f, 0x96, 0x7f, 0x4f, 0x84, 0x04, 0x1c, 0xe2, 0xb5, 0x4a, 0xd6, 0x11, 0xb6, 0xba, 0xb5, + 0x3f, 0xad, 0xfd, 0xb2, 0xd0, 0x1a, 0xa3, 0x0d, 0x68, 0x6d, 0x1d, 0x0e, 0x73, 0x8e, 0xfb, 0x1b, + 0xd7, 0x5f, 0x11, 0x81, 0x95, 0x51, 0x6f, 0x04, 0x47, 0xf7, 0x6d, 0x90, 0x75, 0xd5, 0x29, 0x12, + 0x56, 0xbb, 0xdc, 0x50, 0xcd, 0x3e, 0x38, 0xff, 0x2a, 0xe7, 0x2c, 0x22, 0xbe, 0x9b, 0xf1, 0xda, + 0x4b, 0xaa, 0x49, 0x98, 0x3f, 0x0f, 0x19, 0xc1, 0xbc, 0xa9, 0x5b, 0xb8, 0x62, 0xd4, 0x74, 0xed, + 0x65, 0x5c, 0xed, 0x83, 0xf5, 0xaf, 0x85, 0x86, 0x6a, 0xc3, 0x47, 0x4e, 0x38, 0x2f, 0x40, 0xda, + 0xcd, 0x55, 0xca, 0x5a, 0xc3, 0x34, 0x2c, 0xa7, 0x07, 0xc7, 0xcf, 0x8b, 0x91, 0x72, 0xe9, 0x16, + 0x28, 0x59, 0xae, 0x04, 0xec, 0xe4, 0xb9, 0x5f, 0x93, 0xfc, 0x02, 0x67, 0x34, 0xe2, 0x51, 0xf1, + 0xc0, 0x51, 0x31, 0x1a, 0xa6, 0x6a, 0xf5, 0x13, 0xff, 0xfe, 0xbe, 0x08, 0x1c, 0x9c, 0x84, 0x07, + 0x0e, 0x67, 0xcf, 0xc4, 0x64, 0xb6, 0xef, 0x83, 0xc3, 0x17, 0x45, 0xe0, 0x10, 0x34, 0x9c, 0x85, + 0x48, 0x18, 0xfa, 0x60, 0xf1, 0x0f, 0x04, 0x0b, 0x41, 0x43, 0x58, 0x3c, 0xeb, 0x4d, 0xb4, 0x16, + 0xae, 0x69, 0xb6, 0x63, 0xb1, 0x34, 0xb9, 0x3b, 0xab, 0x7f, 0xf8, 0xdd, 0x60, 0x12, 0xa6, 0xf8, + 0x48, 0x49, 0x24, 0xe2, 0xdb, 0xae, 0x74, 0x15, 0xd5, 0x5b, 0xb0, 0x5f, 0x17, 0x91, 0xc8, 0x47, + 0x46, 0x64, 0xf3, 0x65, 0x88, 0x44, 0xed, 0x15, 0xb2, 0x76, 0xe8, 0x83, 0xdd, 0x3f, 0x0a, 0x09, + 0xb7, 0x26, 0x68, 0x09, 0x4f, 0x5f, 0xfe, 0xd3, 0xd4, 0x77, 0xf0, 0x5e, 0x5f, 0xd6, 0xf9, 0x8f, + 0x43, 0xf9, 0xcf, 0x06, 0xa3, 0x64, 0x31, 0x64, 0x2c, 0x94, 0x4f, 0xa1, 0x5e, 0xf7, 0x8c, 0x32, + 0x3f, 0xf9, 0x7d, 0xde, 0xdf, 0x60, 0x3a, 0x95, 0x5b, 0x24, 0x46, 0x1e, 0x4c, 0x7a, 0x7a, 0x33, + 0x7b, 0xcf, 0xf7, 0x5d, 0x3b, 0x0f, 0xe4, 0x3c, 0xb9, 0x8b, 0x30, 0x12, 0x48, 0x78, 0x7a, 0xb3, + 0x7a, 0x2f, 0x67, 0x95, 0xf2, 0xe7, 0x3b, 0xb9, 0x73, 0x10, 0x23, 0xc9, 0x4b, 0x6f, 0xf2, 0xbf, + 0xcc, 0xc9, 0x29, 0x7a, 0xee, 0xcd, 0x90, 0x10, 0x49, 0x4b, 0x6f, 0xd2, 0xf7, 0x71, 0x52, 0x97, + 0x84, 0x90, 0x8b, 0x84, 0xa5, 0x37, 0xf9, 0x5f, 0x11, 0xe4, 0x82, 0x84, 0x90, 0xf7, 0xaf, 0xc2, + 0x2f, 0xfd, 0x54, 0x8c, 0x4f, 0x3a, 0x42, 0x77, 0x17, 0x60, 0x88, 0x67, 0x2a, 0xbd, 0xa9, 0x3f, + 0xc0, 0x1b, 0x17, 0x14, 0xb9, 0xc7, 0x21, 0xde, 0xa7, 0xc2, 0x7f, 0x9a, 0x93, 0x32, 0xfc, 0xdc, + 0x1c, 0x0c, 0xfb, 0xb2, 0x93, 0xde, 0xe4, 0x7f, 0x8d, 0x93, 0xfb, 0xa9, 0x88, 0xe8, 0x3c, 0x3b, + 0xe9, 0xcd, 0xe0, 0x83, 0x42, 0x74, 0x4e, 0x41, 0xd4, 0x26, 0x12, 0x93, 0xde, 0xd4, 0x1f, 0x12, + 0x5a, 0x17, 0x24, 0xb9, 0xa7, 0x21, 0xe9, 0x4e, 0x36, 0xbd, 0xe9, 0x3f, 0xcc, 0xe9, 0x3d, 0x1a, + 0xa2, 0x01, 0xdf, 0x64, 0xd7, 0x9b, 0xc5, 0x5f, 0x17, 0x1a, 0xf0, 0x51, 0x11, 0x37, 0x0a, 0x27, + 0x30, 0xbd, 0x39, 0x7d, 0x44, 0xb8, 0x51, 0x28, 0x7f, 0x21, 0xa3, 0x49, 0x63, 0x7e, 0x6f, 0x16, + 0x3f, 0x23, 0x46, 0x93, 0xe2, 0x13, 0x31, 0xc2, 0x19, 0x41, 0x6f, 0x1e, 0x7f, 0x43, 0x88, 0x11, + 0x4a, 0x08, 0x72, 0xab, 0x80, 0x5a, 0xb3, 0x81, 0xde, 0xfc, 0x3e, 0xca, 0xf9, 0x8d, 0xb7, 0x24, + 0x03, 0xb9, 0xe7, 0xe0, 0x70, 0xfb, 0x4c, 0xa0, 0x37, 0xd7, 0x8f, 0x7d, 0x3f, 0xb4, 0x76, 0xf3, + 0x27, 0x02, 0xb9, 0x75, 0x6f, 0x4a, 0xf1, 0x67, 0x01, 0xbd, 0xd9, 0xbe, 0xfa, 0xfd, 0x60, 0xe0, + 0xf6, 0x27, 0x01, 0xb9, 0x3c, 0x80, 0x37, 0x01, 0xf7, 0xe6, 0xf5, 0x71, 0xce, 0xcb, 0x47, 0x44, + 0x5c, 0x83, 0xcf, 0xbf, 0xbd, 0xe9, 0x6f, 0x08, 0xd7, 0xe0, 0x14, 0xc4, 0x35, 0xc4, 0xd4, 0xdb, + 0x9b, 0xfa, 0x13, 0xc2, 0x35, 0x04, 0x09, 0xb1, 0x6c, 0xdf, 0xec, 0xd6, 0x9b, 0xc3, 0xa7, 0x84, + 0x65, 0xfb, 0xa8, 0x72, 0xcb, 0x30, 0xde, 0x32, 0x21, 0xf6, 0x66, 0xf5, 0xf3, 0x9c, 0x55, 0x3a, + 0x3c, 0x1f, 0xfa, 0x27, 0x2f, 0x3e, 0x19, 0xf6, 0xe6, 0xf6, 0xe9, 0xd0, 0xe4, 0xc5, 0xe7, 0xc2, + 0xdc, 0x05, 0x48, 0xe8, 0xcd, 0x7a, 0x9d, 0x38, 0x0f, 0xea, 0x7e, 0x37, 0x30, 0xf3, 0x5f, 0x7f, + 0xc0, 0xb5, 0x23, 0x08, 0x72, 0xe7, 0x20, 0x8e, 0x1b, 0x9b, 0xb8, 0xda, 0x8b, 0xf2, 0x3b, 0x3f, + 0x10, 0x01, 0x93, 0x60, 0xe7, 0x9e, 0x06, 0x60, 0x5b, 0x23, 0xf4, 0x78, 0xb0, 0x07, 0xed, 0x7f, + 0xfb, 0x01, 0xbf, 0x8c, 0xe3, 0x91, 0x78, 0x0c, 0xd8, 0xd5, 0x9e, 0xee, 0x0c, 0xbe, 0x1b, 0x64, + 0x40, 0x47, 0xe4, 0x49, 0x18, 0x7a, 0xd1, 0x36, 0x74, 0x47, 0xad, 0xf5, 0xa2, 0xfe, 0xef, 0x9c, + 0x5a, 0xe0, 0x13, 0x85, 0x35, 0x0c, 0x0b, 0x3b, 0x6a, 0xcd, 0xee, 0x45, 0xfb, 0x3f, 0x38, 0xad, + 0x4b, 0x40, 0x88, 0x2b, 0xaa, 0xed, 0xf4, 0xd3, 0xef, 0x3f, 0x14, 0xc4, 0x82, 0x80, 0x08, 0x4d, + 0x7e, 0xef, 0xe0, 0xbd, 0x5e, 0xb4, 0xdf, 0x13, 0x42, 0x73, 0xfc, 0xdc, 0x9b, 0x21, 0x49, 0x7e, + 0xb2, 0x1b, 0x76, 0x3d, 0x88, 0xff, 0x88, 0x13, 0x7b, 0x14, 0xa4, 0x65, 0xdb, 0xa9, 0x3a, 0x5a, + 0x6f, 0x65, 0xdf, 0xe2, 0x23, 0x2d, 0xf0, 0x73, 0x79, 0x18, 0xb6, 0x9d, 0x6a, 0xb5, 0xc9, 0xf3, + 0xd3, 0x1e, 0xe4, 0x7f, 0xfc, 0x03, 0x77, 0xcb, 0xc2, 0xa5, 0x21, 0xa3, 0x7d, 0x6d, 0xc7, 0x31, + 0x0d, 0x7a, 0x04, 0xd2, 0x8b, 0xc3, 0xf7, 0x39, 0x07, 0x1f, 0x49, 0x6e, 0x0e, 0x52, 0xa4, 0x2f, + 0x16, 0x36, 0x31, 0x3d, 0xaf, 0xea, 0xc1, 0xe2, 0x4f, 0xb8, 0x02, 0x02, 0x44, 0x85, 0x9f, 0xf8, + 0xca, 0x6b, 0x53, 0xd2, 0xd7, 0x5f, 0x9b, 0x92, 0x7e, 0xff, 0xb5, 0x29, 0xe9, 0x43, 0xdf, 0x9a, + 0x1a, 0xf8, 0xfa, 0xb7, 0xa6, 0x06, 0x7e, 0xf7, 0x5b, 0x53, 0x03, 0xed, 0xb7, 0x8d, 0x61, 0xde, + 0x98, 0x37, 0xd8, 0x86, 0xf1, 0x0b, 0x72, 0x60, 0xbb, 0xb8, 0x66, 0x78, 0xbb, 0xb5, 0xee, 0x22, + 0x07, 0xde, 0x1d, 0x85, 0xa3, 0x15, 0xc3, 0x6e, 0x18, 0x76, 0x99, 0xed, 0xf7, 0xb2, 0x02, 0xdf, + 0xf1, 0x4d, 0xf9, 0xab, 0xfa, 0xd8, 0xf4, 0xbd, 0x04, 0xa3, 0xb4, 0xeb, 0x74, 0xbb, 0x8b, 0x5a, + 0x5b, 0xcf, 0x00, 0xf1, 0xd5, 0x7f, 0x17, 0xa7, 0xbd, 0x1e, 0x71, 0x09, 0xe9, 0x79, 0xfe, 0x3a, + 0x4c, 0x6a, 0x0d, 0xb3, 0x8e, 0xe9, 0xc6, 0x7f, 0xd9, 0xad, 0xeb, 0xcd, 0xef, 0x6b, 0x9c, 0xdf, + 0x84, 0x47, 0xbe, 0x20, 0xa8, 0x73, 0x8b, 0x30, 0xae, 0x56, 0x2a, 0xd8, 0x0c, 0xb0, 0xec, 0x31, + 0x2c, 0x42, 0xc0, 0x34, 0xa7, 0x74, 0xb9, 0x15, 0x9e, 0xee, 0x34, 0x34, 0x2f, 0xdc, 0xeb, 0xd3, + 0xbc, 0x85, 0x6b, 0x58, 0x3f, 0xad, 0x63, 0xe7, 0x9a, 0x61, 0xed, 0x70, 0xf5, 0x9e, 0x66, 0x4d, + 0x0d, 0xb2, 0x3b, 0xcd, 0xf0, 0xde, 0x28, 0x4c, 0xb1, 0x8a, 0x33, 0x9b, 0xaa, 0x8d, 0xcf, 0x5c, + 0x7d, 0x64, 0x13, 0x3b, 0xea, 0x23, 0x67, 0x2a, 0x86, 0xa6, 0xf3, 0x91, 0x98, 0xe0, 0xe3, 0x42, + 0xea, 0x67, 0x79, 0x7d, 0xb6, 0xed, 0x36, 0xbd, 0x3c, 0x0f, 0xb1, 0x39, 0x43, 0xd3, 0xd1, 0x24, + 0xc4, 0xab, 0x58, 0x37, 0x1a, 0xfc, 0x16, 0x1e, 0x2b, 0xa0, 0xbb, 0x61, 0x50, 0x6d, 0x18, 0x4d, + 0xdd, 0x61, 0x67, 0x16, 0x85, 0xe1, 0xaf, 0xdc, 0x9c, 0x1e, 0xf8, 0xbd, 0x9b, 0xd3, 0xd1, 0x05, + 0xdd, 0x51, 0x78, 0x55, 0x2e, 0xf6, 0xfa, 0x27, 0xa7, 0x25, 0xf9, 0x19, 0x18, 0x2a, 0xe2, 0xca, + 0x41, 0x78, 0x15, 0x71, 0x25, 0xc4, 0xeb, 0x01, 0x48, 0x2c, 0xe8, 0x0e, 0xbb, 0x27, 0x79, 0x02, + 0xa2, 0x9a, 0xce, 0xae, 0xde, 0x84, 0xda, 0x27, 0x70, 0x82, 0x5a, 0xc4, 0x15, 0x17, 0xb5, 0x8a, + 0x2b, 0x61, 0x54, 0xc2, 0x9e, 0xc0, 0x0b, 0xc5, 0xdf, 0xfd, 0xcf, 0x53, 0x03, 0xaf, 0xbc, 0x36, + 0x35, 0xd0, 0x71, 0x24, 0xfc, 0x3e, 0xc0, 0x55, 0xcc, 0x87, 0xc0, 0xae, 0xee, 0x9c, 0x21, 0x26, + 0x6a, 0xbb, 0xc3, 0xf0, 0x5b, 0x83, 0x20, 0x73, 0x1c, 0xdb, 0x51, 0x77, 0x34, 0xbd, 0xe6, 0x8e, + 0x84, 0xda, 0x74, 0xb6, 0x5f, 0xe6, 0x43, 0x71, 0x98, 0x0f, 0x05, 0xc7, 0xe9, 0x3e, 0x1a, 0xd9, + 0xce, 0xde, 0x95, 0xed, 0x31, 0xe6, 0xf2, 0xbf, 0x8e, 0x02, 0x5a, 0x73, 0xd4, 0x1d, 0x9c, 0x6f, + 0x3a, 0xdb, 0x86, 0xa5, 0xbd, 0xcc, 0x62, 0x19, 0x06, 0x68, 0xa8, 0xbb, 0x65, 0xc7, 0xd8, 0xc1, + 0xba, 0x4d, 0x55, 0x33, 0x7c, 0xf6, 0xe8, 0x6c, 0x1b, 0xfb, 0x98, 0x25, 0x43, 0x57, 0x78, 0xf0, + 0xb3, 0xdf, 0x9c, 0xbe, 0xbf, 0xb7, 0x16, 0x28, 0x32, 0x49, 0xae, 0x77, 0xd7, 0x29, 0x63, 0x74, + 0x05, 0xd8, 0xb5, 0x8b, 0x72, 0x5d, 0xb3, 0x1d, 0x7e, 0x73, 0xfb, 0xdc, 0x6c, 0xfb, 0xbe, 0xcf, + 0xb6, 0x8a, 0x39, 0x7b, 0x45, 0xad, 0x6b, 0x55, 0xd5, 0x31, 0x2c, 0xfb, 0xd2, 0x80, 0x92, 0xa4, + 0xac, 0x16, 0x35, 0xdb, 0x41, 0xeb, 0x90, 0xac, 0x62, 0x7d, 0x8f, 0xb1, 0x8d, 0xbe, 0x31, 0xb6, + 0x09, 0xc2, 0x89, 0x72, 0x7d, 0x1e, 0x90, 0xea, 0xc7, 0x13, 0x4f, 0x95, 0xd8, 0x8d, 0xcb, 0x0e, + 0xec, 0x03, 0x9c, 0xe9, 0xcb, 0x8a, 0x71, 0x35, 0x0c, 0xca, 0xde, 0x07, 0xe0, 0xb5, 0x89, 0x32, + 0x30, 0xa4, 0x56, 0xab, 0x16, 0xb6, 0x6d, 0x7a, 0x24, 0x98, 0x54, 0x44, 0x31, 0x37, 0xfe, 0x6f, + 0xbe, 0x70, 0x7a, 0x24, 0xc0, 0xb1, 0x90, 0x02, 0xb8, 0xea, 0x92, 0x9e, 0xfa, 0x84, 0x04, 0xe3, + 0x2d, 0x2d, 0x22, 0x19, 0xa6, 0xf2, 0x1b, 0xeb, 0x97, 0x56, 0x94, 0x85, 0x17, 0xf2, 0xeb, 0x0b, + 0x2b, 0xcb, 0x65, 0xf6, 0x08, 0x60, 0x79, 0x6d, 0xb5, 0x34, 0xb7, 0x70, 0x71, 0xa1, 0x54, 0x4c, + 0x0f, 0xa0, 0x69, 0x38, 0xd6, 0x06, 0xa7, 0x58, 0x5a, 0x2c, 0xcd, 0xe7, 0xd7, 0x4b, 0x69, 0x09, + 0xdd, 0x05, 0x27, 0xda, 0x32, 0x71, 0x51, 0x22, 0x1d, 0x50, 0x94, 0x92, 0x8b, 0x12, 0x2d, 0x5c, + 0xec, 0xe8, 0x45, 0x0f, 0x75, 0xb5, 0x9f, 0x5d, 0xd7, 0x5d, 0x82, 0xfe, 0xf4, 0x27, 0x12, 0x1c, + 0x0d, 0x4f, 0x19, 0xaa, 0xbe, 0xd7, 0xe1, 0x1d, 0x68, 0x87, 0x68, 0xf6, 0x26, 0x88, 0xe6, 0xf5, + 0x3d, 0x74, 0x94, 0xe5, 0xd3, 0xe5, 0xa6, 0x55, 0xe7, 0x31, 0x68, 0x88, 0x94, 0x37, 0xac, 0x3a, + 0x89, 0x4d, 0xe2, 0xea, 0xbf, 0x74, 0x32, 0xc5, 0xef, 0xf3, 0xe7, 0x62, 0xdf, 0xfb, 0xd4, 0xf4, + 0x40, 0x61, 0x27, 0xdc, 0xa5, 0x2f, 0xf5, 0x9c, 0x41, 0x13, 0x79, 0x7d, 0x8f, 0x06, 0x9f, 0x55, + 0xe9, 0x85, 0x38, 0xed, 0x90, 0x38, 0x34, 0x9d, 0x0a, 0x1f, 0x9a, 0x3e, 0x87, 0xeb, 0xf5, 0xcb, + 0xba, 0x71, 0x8d, 0x8e, 0xa4, 0xd7, 0xef, 0xbf, 0x1a, 0x81, 0xa9, 0x96, 0xa9, 0x92, 0x67, 0x15, + 0x9d, 0x1e, 0xc1, 0xe6, 0x20, 0x51, 0x14, 0xc9, 0x4a, 0x06, 0x86, 0x6c, 0x5c, 0x31, 0xf4, 0x2a, + 0xf3, 0xee, 0xa8, 0x22, 0x8a, 0xa4, 0xab, 0xba, 0xaa, 0x1b, 0x36, 0xbf, 0x79, 0xcf, 0x0a, 0x85, + 0x9f, 0x91, 0xf6, 0x97, 0x23, 0x8c, 0x88, 0x96, 0x44, 0x37, 0x4f, 0x75, 0x3b, 0x57, 0xa6, 0x2a, + 0x70, 0xe5, 0xf7, 0x9d, 0x21, 0xf7, 0xab, 0x8e, 0x0f, 0x45, 0x60, 0x3a, 0xac, 0x0e, 0x92, 0xa3, + 0xd9, 0x8e, 0xda, 0x30, 0x3b, 0xe9, 0xe3, 0x02, 0x24, 0xd7, 0x05, 0xce, 0xbe, 0x15, 0xf2, 0xb3, + 0xfb, 0x54, 0xc8, 0xa8, 0xdb, 0x94, 0xd0, 0xc8, 0x83, 0xbd, 0x35, 0xe2, 0x76, 0xe1, 0x00, 0x2a, + 0xf9, 0x6c, 0x0c, 0x4e, 0xd0, 0xc7, 0x58, 0x56, 0x43, 0xd3, 0x9d, 0x33, 0x15, 0x6b, 0xcf, 0x74, + 0x68, 0x7a, 0x66, 0x6c, 0x71, 0x85, 0x8c, 0x7b, 0xd5, 0xb3, 0xac, 0xba, 0x83, 0x7f, 0x6c, 0x41, + 0x7c, 0x95, 0xd0, 0x11, 0x55, 0x38, 0x86, 0xa3, 0xd6, 0xb9, 0x8a, 0x58, 0x81, 0x40, 0xd9, 0x03, + 0xae, 0x08, 0x83, 0x6a, 0xe2, 0xed, 0x56, 0x1d, 0xab, 0x5b, 0xec, 0x1e, 0x7c, 0x94, 0xba, 0x4d, + 0x82, 0x00, 0xe8, 0x95, 0xf7, 0x49, 0x88, 0xab, 0x4d, 0x76, 0x85, 0x23, 0x4a, 0xfc, 0x89, 0x16, + 0xe4, 0xcb, 0x30, 0xc4, 0x8f, 0x8d, 0x51, 0x1a, 0xa2, 0x3b, 0x78, 0x8f, 0xb6, 0x93, 0x52, 0xc8, + 0x4f, 0x34, 0x0b, 0x71, 0x2a, 0x3c, 0x9f, 0x26, 0x32, 0xb3, 0x2d, 0xd2, 0xcf, 0x52, 0x21, 0x15, + 0x86, 0x26, 0x3f, 0x03, 0x89, 0xa2, 0xd1, 0xd0, 0x74, 0x23, 0xc8, 0x2d, 0xc9, 0xb8, 0x51, 0x99, + 0xcd, 0x26, 0xcf, 0x2a, 0x14, 0x56, 0x40, 0x87, 0x61, 0x90, 0xbd, 0x8b, 0xe0, 0xd7, 0x50, 0x78, + 0x49, 0x9e, 0x83, 0x21, 0xca, 0x7b, 0xc5, 0x44, 0x88, 0xbf, 0xa8, 0xe3, 0x0f, 0x30, 0x68, 0x02, + 0xca, 0xd9, 0x47, 0x3c, 0x61, 0x11, 0xc4, 0xaa, 0xaa, 0xa3, 0xf2, 0x7e, 0xd3, 0xdf, 0xf2, 0x53, + 0x90, 0xe0, 0x4c, 0x6c, 0x74, 0x16, 0xa2, 0x86, 0x69, 0xf3, 0x8b, 0x24, 0xd9, 0x4e, 0x5d, 0x59, + 0x31, 0x0b, 0x31, 0x92, 0x8f, 0x28, 0x04, 0xb9, 0xa0, 0x74, 0x0c, 0x9d, 0x4f, 0xf8, 0x2c, 0xc9, + 0x37, 0xe4, 0xbe, 0x9f, 0x6c, 0x48, 0x5b, 0xcc, 0xc1, 0x35, 0x96, 0x4f, 0x45, 0x60, 0xca, 0x57, + 0x7b, 0x15, 0x5b, 0xb6, 0x66, 0xe8, 0x7c, 0xd6, 0x66, 0xd6, 0x82, 0x7c, 0x42, 0xf2, 0xfa, 0x0e, + 0xe6, 0xf2, 0x66, 0x88, 0xe6, 0x4d, 0x13, 0x65, 0x21, 0xc1, 0x2e, 0x8c, 0x18, 0xcc, 0x5e, 0x62, + 0x8a, 0x5b, 0x26, 0x75, 0xb6, 0xb1, 0xe5, 0x5c, 0x53, 0x2d, 0xf7, 0xe9, 0xa0, 0x28, 0xcb, 0x4f, + 0x42, 0x72, 0xce, 0xd0, 0x6d, 0xac, 0xdb, 0x4d, 0xea, 0x7c, 0x9b, 0x75, 0xa3, 0xb2, 0xc3, 0x39, + 0xb0, 0x02, 0x51, 0xb8, 0x6a, 0x9a, 0x94, 0x32, 0xa6, 0x90, 0x9f, 0x2c, 0x03, 0x2c, 0xac, 0x75, + 0x54, 0xd1, 0x93, 0xfb, 0x57, 0x11, 0xef, 0xa4, 0xab, 0xa3, 0xff, 0x23, 0xc1, 0xf1, 0x56, 0x87, + 0xda, 0xc1, 0x7b, 0xf6, 0x7e, 0xfd, 0xe9, 0x79, 0x48, 0xae, 0xd2, 0xf7, 0xfb, 0x97, 0xf1, 0x1e, + 0xca, 0xc2, 0x10, 0xae, 0x9e, 0x3d, 0x77, 0xee, 0x91, 0x27, 0x99, 0xb5, 0x5f, 0x1a, 0x50, 0x04, + 0x00, 0x4d, 0x41, 0xd2, 0xc6, 0x15, 0xf3, 0xec, 0xb9, 0xf3, 0x3b, 0x8f, 0x30, 0xf3, 0x22, 0x79, + 0x8e, 0x0b, 0xca, 0x25, 0x48, 0xaf, 0x5f, 0xff, 0xd4, 0xb4, 0x54, 0x88, 0x43, 0xd4, 0x6e, 0x36, + 0xee, 0xa8, 0x8d, 0xbc, 0x1a, 0x87, 0x19, 0x3f, 0x25, 0x8d, 0x50, 0x6e, 0xee, 0xc1, 0x75, 0x90, + 0xf6, 0xe9, 0x80, 0x62, 0x74, 0x48, 0x59, 0xbb, 0x6a, 0x52, 0xfe, 0x35, 0x09, 0x52, 0x6e, 0x42, + 0xb4, 0x86, 0x1d, 0x74, 0xc1, 0x9f, 0xe5, 0x70, 0xb7, 0x39, 0x36, 0x1b, 0x6e, 0xcb, 0x4b, 0xdc, + 0x14, 0x1f, 0x3a, 0x7a, 0x9c, 0x1a, 0xa2, 0x69, 0xd8, 0xfc, 0x39, 0x59, 0x0f, 0x52, 0x17, 0x19, + 0x3d, 0x04, 0x88, 0x46, 0xb8, 0xf2, 0x55, 0xc3, 0xd1, 0xf4, 0x5a, 0xd9, 0x34, 0xae, 0xf1, 0x47, + 0xba, 0x51, 0x25, 0x4d, 0x6b, 0xae, 0xd0, 0x8a, 0x55, 0x02, 0x27, 0x42, 0x27, 0x5d, 0x2e, 0xc1, + 0x24, 0x8e, 0x04, 0x01, 0x51, 0x44, 0x17, 0x60, 0xc8, 0x6c, 0x6e, 0x96, 0x45, 0xc4, 0x18, 0x3e, + 0x7b, 0xbc, 0x9d, 0xff, 0x0b, 0xfb, 0xe0, 0x11, 0x60, 0xd0, 0x6c, 0x6e, 0x12, 0x6b, 0xb9, 0x0b, + 0x52, 0x6d, 0x84, 0x19, 0xbe, 0xea, 0xc9, 0x41, 0x3f, 0x1b, 0xc1, 0x7b, 0x50, 0x36, 0x2d, 0xcd, + 0xb0, 0x34, 0x67, 0x8f, 0x66, 0xa9, 0x51, 0x25, 0x2d, 0x2a, 0x56, 0x39, 0x5c, 0xde, 0x81, 0xb1, + 0x35, 0xba, 0x8a, 0xf5, 0x24, 0x3f, 0xe7, 0xc9, 0x27, 0xf5, 0x96, 0xaf, 0xa3, 0x64, 0x91, 0x16, + 0xc9, 0x0a, 0xcf, 0x76, 0xb4, 0xce, 0xc7, 0xf7, 0x6f, 0x9d, 0xc1, 0x3c, 0xf0, 0x0f, 0x8f, 0x06, + 0x9c, 0x93, 0x4f, 0x9f, 0xbe, 0xf0, 0xd5, 0xaf, 0x61, 0xf6, 0x4a, 0x23, 0xb2, 0xdd, 0x27, 0xd5, + 0x6c, 0x8f, 0x30, 0x9a, 0xed, 0xe9, 0x42, 0xf2, 0x93, 0x30, 0xb2, 0xaa, 0x5a, 0xce, 0x1a, 0x76, + 0x2e, 0x61, 0xb5, 0x8a, 0xad, 0xe0, 0xac, 0x3b, 0x22, 0x66, 0x5d, 0x04, 0x31, 0x3a, 0xb5, 0xb2, + 0x59, 0x87, 0xfe, 0x96, 0xb7, 0x21, 0x46, 0x6f, 0x84, 0xba, 0x33, 0x32, 0xa7, 0x60, 0x33, 0x32, + 0x89, 0xa5, 0x7b, 0x0e, 0xb6, 0x45, 0x12, 0x4b, 0x0b, 0xe8, 0x31, 0x31, 0xaf, 0x46, 0xbb, 0xcf, + 0xab, 0xdc, 0x10, 0xf9, 0xec, 0x5a, 0x87, 0xa1, 0x02, 0x09, 0xc5, 0x0b, 0x45, 0x57, 0x10, 0xc9, + 0x13, 0x04, 0x2d, 0xc1, 0x98, 0xa9, 0x5a, 0x0e, 0x7d, 0x0a, 0xb3, 0x4d, 0x7b, 0xc1, 0x6d, 0x7d, + 0xba, 0xd5, 0xf3, 0x02, 0x9d, 0xe5, 0xad, 0x8c, 0x98, 0x7e, 0xa0, 0xfc, 0x07, 0x31, 0x18, 0xe4, + 0xca, 0x78, 0x33, 0x0c, 0x71, 0xb5, 0x72, 0xeb, 0x3c, 0x31, 0xdb, 0x3a, 0x31, 0xcd, 0xba, 0x13, + 0x08, 0xe7, 0x27, 0x68, 0xd0, 0x7d, 0x90, 0xa8, 0x6c, 0xab, 0x9a, 0x5e, 0xd6, 0xaa, 0x62, 0x43, + 0xe1, 0xb5, 0x9b, 0xd3, 0x43, 0x73, 0x04, 0xb6, 0x50, 0x54, 0x86, 0x68, 0xe5, 0x42, 0x95, 0x64, + 0x02, 0xdb, 0x58, 0xab, 0x6d, 0x3b, 0xdc, 0xc3, 0x78, 0x09, 0x3d, 0x01, 0x31, 0x62, 0x10, 0xfc, + 0xa1, 0x64, 0xb6, 0x65, 0x5b, 0xc7, 0xcd, 0xf2, 0x0a, 0x09, 0xd2, 0xf0, 0x87, 0xbe, 0x39, 0x2d, + 0x29, 0x94, 0x02, 0xcd, 0xc1, 0x48, 0x5d, 0xb5, 0x9d, 0x32, 0x9d, 0xc1, 0x48, 0xf3, 0x71, 0xbe, + 0xaa, 0x6e, 0x51, 0x08, 0x57, 0x2c, 0x17, 0x7d, 0x98, 0x50, 0x31, 0x50, 0x15, 0x9d, 0x84, 0x34, + 0x65, 0x52, 0x31, 0x1a, 0x0d, 0xcd, 0x61, 0xb9, 0xd5, 0x20, 0xd5, 0xfb, 0x28, 0x81, 0xcf, 0x51, + 0x30, 0xcd, 0xb0, 0x8e, 0x41, 0x92, 0x3e, 0xcd, 0xa2, 0x28, 0xec, 0x1a, 0x72, 0x82, 0x00, 0x68, + 0xe5, 0xfd, 0x30, 0xe6, 0xc5, 0x47, 0x86, 0x92, 0x60, 0x5c, 0x3c, 0x30, 0x45, 0x7c, 0x18, 0x26, + 0x75, 0xbc, 0x4b, 0x2f, 0x46, 0x07, 0xb0, 0x93, 0x14, 0x1b, 0x91, 0xba, 0x2b, 0x41, 0x8a, 0x7b, + 0x61, 0xb4, 0x22, 0x94, 0xcf, 0x70, 0x81, 0xe2, 0x8e, 0xb8, 0x50, 0x8a, 0x76, 0x14, 0x12, 0xaa, + 0x69, 0x32, 0x84, 0x61, 0x1e, 0x1f, 0x4d, 0x93, 0x56, 0x9d, 0x82, 0x71, 0xda, 0x47, 0x0b, 0xdb, + 0xcd, 0xba, 0xc3, 0x99, 0xa4, 0x28, 0xce, 0x18, 0xa9, 0x50, 0x18, 0x9c, 0xe2, 0xde, 0x0d, 0x23, + 0xf8, 0xaa, 0x56, 0xc5, 0x7a, 0x05, 0x33, 0xbc, 0x11, 0x8a, 0x97, 0x12, 0x40, 0x8a, 0xf4, 0x00, + 0xb8, 0x71, 0xaf, 0x2c, 0x62, 0xf2, 0x28, 0xe3, 0x27, 0xe0, 0x79, 0x06, 0x96, 0x33, 0x10, 0x2b, + 0xaa, 0x8e, 0x4a, 0x12, 0x0c, 0x67, 0x97, 0x4d, 0x34, 0x29, 0x85, 0xfc, 0x94, 0x5f, 0x8f, 0x40, + 0xec, 0x8a, 0xe1, 0x60, 0xf4, 0xa8, 0x2f, 0x01, 0x1c, 0x6d, 0x67, 0xcf, 0x6b, 0x5a, 0x4d, 0xc7, + 0xd5, 0x25, 0xbb, 0xe6, 0xfb, 0x8e, 0x82, 0x67, 0x4e, 0x91, 0x80, 0x39, 0x4d, 0x42, 0xdc, 0x32, + 0x9a, 0x7a, 0x55, 0xdc, 0xe0, 0xa5, 0x05, 0x54, 0x82, 0x84, 0x6b, 0x25, 0xb1, 0x5e, 0x56, 0x32, + 0x46, 0xac, 0x84, 0xd8, 0x30, 0x07, 0x28, 0x43, 0x9b, 0xdc, 0x58, 0x0a, 0x90, 0x74, 0x83, 0x17, + 0xb7, 0xb6, 0xfe, 0x0c, 0xd6, 0x23, 0x23, 0x93, 0x89, 0x3b, 0xf6, 0xae, 0xf2, 0x98, 0xc5, 0xa5, + 0xdd, 0x0a, 0xae, 0xbd, 0x80, 0x59, 0xf1, 0x6f, 0x3a, 0x0c, 0xd1, 0x7e, 0x79, 0x66, 0xc5, 0xbe, + 0xeb, 0x70, 0x1c, 0x92, 0xb6, 0x56, 0xd3, 0x55, 0xa7, 0x69, 0x61, 0x6e, 0x79, 0x1e, 0x40, 0xfe, + 0x92, 0x04, 0x83, 0xcc, 0x92, 0x7d, 0x7a, 0x93, 0xda, 0xeb, 0x2d, 0xd2, 0x49, 0x6f, 0xd1, 0x83, + 0xeb, 0x2d, 0x0f, 0xe0, 0x0a, 0x63, 0xf3, 0xa7, 0xf6, 0x6d, 0x32, 0x06, 0x26, 0xe2, 0x9a, 0x56, + 0xe3, 0x8e, 0xea, 0x23, 0x92, 0xff, 0x93, 0x44, 0x92, 0x58, 0x5e, 0x8f, 0xf2, 0x30, 0x22, 0xe4, + 0x2a, 0x6f, 0xd5, 0xd5, 0x1a, 0xb7, 0x9d, 0x13, 0x1d, 0x85, 0xbb, 0x58, 0x57, 0x6b, 0xca, 0x30, + 0x97, 0x87, 0x14, 0xda, 0x8f, 0x43, 0xa4, 0xc3, 0x38, 0x04, 0x06, 0x3e, 0x7a, 0xb0, 0x81, 0x0f, + 0x0c, 0x51, 0x2c, 0x3c, 0x44, 0x9f, 0x8f, 0xd0, 0xc5, 0x8c, 0x69, 0xd8, 0x6a, 0xfd, 0x87, 0xe1, + 0x11, 0xc7, 0x20, 0x69, 0x1a, 0xf5, 0x32, 0xab, 0x61, 0x37, 0xdb, 0x13, 0xa6, 0x51, 0x57, 0x5a, + 0x86, 0x3d, 0x7e, 0x9b, 0xdc, 0x65, 0xf0, 0x36, 0x68, 0x6d, 0x28, 0xac, 0x35, 0x0b, 0x52, 0x4c, + 0x15, 0x7c, 0x2e, 0x7b, 0x98, 0xe8, 0x80, 0x4e, 0x8e, 0x52, 0xeb, 0xdc, 0xcb, 0xc4, 0x66, 0x98, + 0x0a, 0xc7, 0x23, 0x14, 0x2c, 0xf4, 0xb7, 0x5b, 0x05, 0xfb, 0xcd, 0x52, 0xe1, 0x78, 0xf2, 0xcf, + 0x4a, 0x00, 0x8b, 0x44, 0xb3, 0xb4, 0xbf, 0x64, 0x16, 0xb2, 0xa9, 0x08, 0xe5, 0x40, 0xcb, 0x53, + 0x9d, 0x06, 0x8d, 0xb7, 0x9f, 0xb2, 0xfd, 0x72, 0xcf, 0xc1, 0x88, 0x67, 0x8c, 0x36, 0x16, 0xc2, + 0x4c, 0x75, 0xc9, 0xaa, 0xd7, 0xb0, 0xa3, 0xa4, 0xae, 0xfa, 0x4a, 0xf2, 0x3f, 0x93, 0x20, 0x49, + 0x65, 0x5a, 0xc2, 0x8e, 0x1a, 0x18, 0x43, 0xe9, 0xe0, 0x63, 0x78, 0x02, 0x80, 0xb1, 0xb1, 0xb5, + 0x97, 0x31, 0xb7, 0xac, 0x24, 0x85, 0xac, 0x69, 0x2f, 0x63, 0x74, 0xde, 0x55, 0x78, 0xb4, 0xbb, + 0xc2, 0x45, 0xd6, 0xcd, 0xd5, 0x7e, 0x04, 0x86, 0xe8, 0xa7, 0xa9, 0x76, 0x6d, 0x9e, 0x48, 0x0f, + 0xea, 0xcd, 0xc6, 0xfa, 0xae, 0x2d, 0xbf, 0x08, 0x43, 0xeb, 0xbb, 0x6c, 0x6f, 0xe4, 0x18, 0x24, + 0x2d, 0xc3, 0xe0, 0x73, 0x32, 0xcb, 0x85, 0x12, 0x04, 0x40, 0xa7, 0x20, 0xb1, 0x1f, 0x10, 0xf1, + 0xf6, 0x03, 0xbc, 0x0d, 0x8d, 0x68, 0x5f, 0x1b, 0x1a, 0xa7, 0xfe, 0xbd, 0x04, 0xc3, 0xbe, 0xf8, + 0x80, 0x1e, 0x81, 0x43, 0x85, 0xc5, 0x95, 0xb9, 0xcb, 0xe5, 0x85, 0x62, 0xf9, 0xe2, 0x62, 0x7e, + 0xde, 0x7b, 0xbb, 0x95, 0x3d, 0x7c, 0xfd, 0xc6, 0x0c, 0xf2, 0xe1, 0x6e, 0xe8, 0x3b, 0xba, 0x71, + 0x4d, 0x47, 0x67, 0x60, 0x32, 0x48, 0x92, 0x2f, 0xac, 0x95, 0x96, 0xd7, 0xd3, 0x52, 0xf6, 0xd0, + 0xf5, 0x1b, 0x33, 0xe3, 0x3e, 0x8a, 0xfc, 0xa6, 0x8d, 0x75, 0xa7, 0x95, 0x60, 0x6e, 0x65, 0x69, + 0x69, 0x61, 0x3d, 0x1d, 0x69, 0x21, 0xe0, 0x01, 0xfb, 0x01, 0x18, 0x0f, 0x12, 0x2c, 0x2f, 0x2c, + 0xa6, 0xa3, 0x59, 0x74, 0xfd, 0xc6, 0xcc, 0xa8, 0x0f, 0x7b, 0x59, 0xab, 0x67, 0x13, 0xef, 0xff, + 0xf4, 0xd4, 0xc0, 0x2f, 0xfe, 0xc2, 0x94, 0x44, 0x7a, 0x36, 0x12, 0x88, 0x11, 0xe8, 0x21, 0x38, + 0xb2, 0xb6, 0x30, 0xbf, 0x5c, 0x2a, 0x96, 0x97, 0xd6, 0xe6, 0xc5, 0x4e, 0xb3, 0xe8, 0xdd, 0xd8, + 0xf5, 0x1b, 0x33, 0xc3, 0xbc, 0x4b, 0x9d, 0xb0, 0x57, 0x95, 0xd2, 0x95, 0x95, 0xf5, 0x52, 0x5a, + 0x62, 0xd8, 0xab, 0x16, 0xbe, 0x6a, 0x38, 0xec, 0xdb, 0x75, 0x0f, 0xc3, 0xd1, 0x36, 0xd8, 0x6e, + 0xc7, 0xc6, 0xaf, 0xdf, 0x98, 0x19, 0x59, 0xb5, 0x30, 0xf3, 0x1f, 0x4a, 0x31, 0x0b, 0x99, 0x56, + 0x8a, 0x95, 0xd5, 0x95, 0xb5, 0xfc, 0x62, 0x7a, 0x26, 0x9b, 0xbe, 0x7e, 0x63, 0x26, 0x25, 0x82, + 0x21, 0xdd, 0xce, 0x77, 0x7b, 0x76, 0x27, 0x57, 0x3c, 0x7f, 0x7c, 0x1a, 0xee, 0xe9, 0x70, 0x92, + 0x24, 0xce, 0x20, 0x0e, 0x74, 0x96, 0xd4, 0x71, 0x37, 0x3d, 0xdb, 0x63, 0xc3, 0xb9, 0xf7, 0xd2, + 0xe9, 0xe0, 0xe7, 0x54, 0xd9, 0xae, 0x8b, 0x3b, 0xf9, 0x03, 0x12, 0x8c, 0x5e, 0xd2, 0x6c, 0xc7, + 0xb0, 0xb4, 0x8a, 0x5a, 0xa7, 0x2f, 0xb6, 0xce, 0xf7, 0x1b, 0x5b, 0x43, 0xae, 0xfe, 0x34, 0x0c, + 0x5e, 0x55, 0xeb, 0x2c, 0xa8, 0x45, 0xe9, 0x07, 0x66, 0x3a, 0x1c, 0xec, 0xb8, 0xa1, 0x4d, 0x30, + 0x60, 0x64, 0xf2, 0x2f, 0x47, 0x60, 0x8c, 0x3a, 0x83, 0xcd, 0x3e, 0x3d, 0x46, 0xd6, 0x58, 0x05, + 0x88, 0x59, 0xaa, 0xc3, 0x37, 0x0d, 0x0b, 0xb3, 0xfc, 0x8c, 0xf1, 0xbe, 0x3e, 0x4e, 0xcc, 0x8a, + 0xb8, 0xa2, 0x50, 0x5a, 0xf4, 0x76, 0x48, 0x34, 0xd4, 0xdd, 0x32, 0xe5, 0xc3, 0x56, 0x2e, 0xf9, + 0xfd, 0xf1, 0xb9, 0x75, 0x73, 0x7a, 0x6c, 0x4f, 0x6d, 0xd4, 0x73, 0xb2, 0xe0, 0x23, 0x2b, 0x43, + 0x0d, 0x75, 0x97, 0x88, 0x88, 0x4c, 0x18, 0x23, 0xd0, 0xca, 0xb6, 0xaa, 0xd7, 0x30, 0x6b, 0x84, + 0x6e, 0x81, 0x16, 0x2e, 0xed, 0xbb, 0x91, 0xc3, 0x5e, 0x23, 0x3e, 0x76, 0xb2, 0x32, 0xd2, 0x50, + 0x77, 0xe7, 0x28, 0x80, 0xb4, 0x98, 0x4b, 0x7c, 0xf4, 0x93, 0xd3, 0x03, 0xf4, 0xdc, 0xf6, 0x1b, + 0x12, 0x80, 0xa7, 0x31, 0xf4, 0x76, 0x48, 0x57, 0xdc, 0x12, 0xa5, 0x15, 0x27, 0x90, 0xf7, 0x77, + 0x1a, 0x8b, 0x90, 0xbe, 0xd9, 0xdc, 0xfc, 0xf5, 0x9b, 0xd3, 0x92, 0x32, 0x56, 0x09, 0x0d, 0xc5, + 0xdb, 0x60, 0xb8, 0x69, 0x56, 0x55, 0x07, 0x97, 0xe9, 0x3a, 0x2e, 0xd2, 0x73, 0x9e, 0x9f, 0x22, + 0xbc, 0x6e, 0xdd, 0x9c, 0x46, 0xac, 0x5b, 0x3e, 0x62, 0x99, 0xce, 0xfe, 0xc0, 0x20, 0x84, 0xc0, + 0xd7, 0xa7, 0xaf, 0x4a, 0x30, 0x5c, 0xf4, 0xdd, 0x9c, 0xcc, 0xc0, 0x50, 0xc3, 0xd0, 0xb5, 0x1d, + 0x6e, 0x8f, 0x49, 0x45, 0x14, 0x51, 0x16, 0x12, 0xec, 0x11, 0xab, 0xb3, 0x27, 0xb6, 0x42, 0x45, + 0x99, 0x50, 0x5d, 0xc3, 0x9b, 0xb6, 0x26, 0x46, 0x43, 0x11, 0x45, 0x74, 0x11, 0xd2, 0x36, 0xae, + 0x34, 0x2d, 0xcd, 0xd9, 0x2b, 0x57, 0x0c, 0xdd, 0x51, 0x2b, 0x0e, 0x7b, 0x0e, 0x59, 0x38, 0x76, + 0xeb, 0xe6, 0xf4, 0x11, 0x26, 0x6b, 0x18, 0x43, 0x56, 0xc6, 0x04, 0x68, 0x8e, 0x41, 0x48, 0x0b, + 0x55, 0xec, 0xa8, 0x5a, 0xdd, 0xce, 0xb0, 0x2b, 0x08, 0xa2, 0xe8, 0xeb, 0xcb, 0xe7, 0x86, 0xfc, + 0x1b, 0x5b, 0x17, 0x21, 0x6d, 0x98, 0xd8, 0x0a, 0x24, 0xa2, 0x52, 0xb8, 0xe5, 0x30, 0x86, 0xac, + 0x8c, 0x09, 0x90, 0x48, 0x52, 0x1d, 0x32, 0xcc, 0x62, 0xa1, 0x68, 0x36, 0x37, 0xbd, 0xfd, 0xb0, + 0xc9, 0x96, 0xd1, 0xc8, 0xeb, 0x7b, 0x85, 0x47, 0x3d, 0xee, 0x61, 0x3a, 0xf9, 0x6b, 0x5f, 0x38, + 0x3d, 0xc9, 0x4d, 0xc3, 0xdb, 0x9f, 0xba, 0x8c, 0xf7, 0xc8, 0xf0, 0x73, 0xd4, 0x55, 0x8a, 0x49, + 0xd2, 0xce, 0x17, 0x55, 0xad, 0x2e, 0x9e, 0xf5, 0x2b, 0xbc, 0x84, 0x72, 0x30, 0x68, 0x3b, 0xaa, + 0xd3, 0xb4, 0xf9, 0x79, 0xae, 0xdc, 0xc9, 0xd4, 0x0a, 0x86, 0x5e, 0x5d, 0xa3, 0x98, 0x0a, 0xa7, + 0x40, 0x17, 0x61, 0x90, 0x1f, 0x94, 0xc7, 0xf7, 0xed, 0xdf, 0xf4, 0x46, 0x04, 0xa3, 0x26, 0x1a, + 0xa9, 0xe2, 0x3a, 0xae, 0xb1, 0xb4, 0x6a, 0x5b, 0x25, 0xab, 0x0f, 0xfa, 0xcd, 0xbd, 0xc2, 0xc2, + 0xbe, 0x9d, 0x90, 0x6b, 0x2a, 0xcc, 0x4f, 0x56, 0xc6, 0x5c, 0xd0, 0x1a, 0x85, 0xa0, 0xcb, 0x81, + 0x2b, 0xbe, 0xfc, 0xc3, 0x94, 0x77, 0x77, 0xea, 0xbe, 0xcf, 0xa6, 0xc5, 0xfe, 0x84, 0xff, 0x82, + 0xf0, 0x45, 0x48, 0x37, 0xf5, 0x4d, 0x43, 0xa7, 0x6f, 0x6f, 0x79, 0x7e, 0x4f, 0xd6, 0x77, 0x51, + 0xbf, 0x71, 0x84, 0x31, 0x64, 0x65, 0xcc, 0x05, 0x5d, 0x62, 0xab, 0x80, 0x2a, 0x8c, 0x7a, 0x58, + 0xd4, 0x51, 0x93, 0x3d, 0x1d, 0xf5, 0x2e, 0xee, 0xa8, 0x87, 0xc2, 0xad, 0x78, 0xbe, 0x3a, 0xe2, + 0x02, 0x09, 0x19, 0xba, 0x04, 0xe0, 0x85, 0x07, 0xba, 0x4f, 0x31, 0xdc, 0x79, 0xe0, 0xbd, 0x18, + 0x23, 0xd6, 0x7b, 0x1e, 0x2d, 0x7a, 0x27, 0x4c, 0x34, 0x34, 0xbd, 0x6c, 0xe3, 0xfa, 0x56, 0x99, + 0x2b, 0x98, 0xb0, 0xa4, 0x9f, 0x4e, 0x2a, 0x2c, 0xee, 0xcf, 0x1e, 0x6e, 0xdd, 0x9c, 0xce, 0xf2, + 0x10, 0xda, 0xca, 0x52, 0x56, 0xc6, 0x1b, 0x9a, 0xbe, 0x86, 0xeb, 0x5b, 0x45, 0x17, 0x96, 0x4b, + 0xbd, 0xff, 0x93, 0xd3, 0x03, 0xdc, 0x5d, 0x07, 0xe4, 0xf3, 0x74, 0xef, 0x9c, 0xbb, 0x19, 0xb6, + 0xc9, 0x9a, 0x44, 0x15, 0x05, 0x7e, 0xa1, 0xc0, 0x03, 0x30, 0x37, 0x7f, 0xe5, 0x3f, 0xce, 0x48, + 0xf2, 0xe7, 0x24, 0x18, 0x2c, 0x5e, 0x59, 0x55, 0x35, 0x0b, 0x2d, 0xc0, 0xb8, 0x67, 0x39, 0x41, + 0x27, 0x3f, 0x7e, 0xeb, 0xe6, 0x74, 0x26, 0x6c, 0x5c, 0xae, 0x97, 0x7b, 0x06, 0x2c, 0xdc, 0x7c, + 0xa1, 0xd3, 0xc2, 0x35, 0xc0, 0xaa, 0x05, 0x45, 0x6e, 0x5d, 0xd6, 0x86, 0xba, 0x59, 0x82, 0x21, + 0x26, 0xad, 0x8d, 0x72, 0x10, 0x37, 0xc9, 0x0f, 0x7e, 0x30, 0x30, 0xd5, 0xd1, 0x78, 0x29, 0xbe, + 0xbb, 0x91, 0x49, 0x48, 0xe4, 0x0f, 0x47, 0x00, 0x8a, 0x57, 0xae, 0xac, 0x5b, 0x9a, 0x59, 0xc7, + 0xce, 0xed, 0xec, 0xf9, 0x3a, 0x1c, 0xf2, 0xad, 0x92, 0xac, 0x4a, 0xa8, 0xf7, 0x33, 0xb7, 0x6e, + 0x4e, 0x1f, 0x0f, 0xf7, 0xde, 0x87, 0x26, 0x2b, 0x13, 0xde, 0x7a, 0xc9, 0xaa, 0xb4, 0xe5, 0x5a, + 0xb5, 0x1d, 0x97, 0x6b, 0xb4, 0x33, 0x57, 0x1f, 0x9a, 0x9f, 0x6b, 0xd1, 0x76, 0xda, 0xab, 0x76, + 0x0d, 0x86, 0x3d, 0x95, 0xd8, 0xa8, 0x08, 0x09, 0x87, 0xff, 0xe6, 0x1a, 0x96, 0x3b, 0x6b, 0x58, + 0x90, 0x71, 0x2d, 0xbb, 0x94, 0xf2, 0x9f, 0x4a, 0x00, 0x9e, 0xcd, 0xfe, 0x78, 0x9a, 0x18, 0x09, + 0xe5, 0x3c, 0xf0, 0x46, 0x0f, 0x94, 0xaa, 0x71, 0xea, 0x90, 0x3e, 0x7f, 0x2a, 0x02, 0x13, 0x1b, + 0x22, 0xf2, 0xfc, 0xd8, 0xeb, 0x60, 0x15, 0x86, 0xb0, 0xee, 0x58, 0x1a, 0x55, 0x02, 0x19, 0xed, + 0x87, 0x3b, 0x8d, 0x76, 0x9b, 0x3e, 0xd1, 0x8f, 0x47, 0x89, 0x4d, 0x77, 0xce, 0x26, 0xa4, 0x8d, + 0x0f, 0x46, 0x21, 0xd3, 0x89, 0x12, 0xcd, 0xc1, 0x58, 0xc5, 0xc2, 0xec, 0x7a, 0x95, 0x7f, 0xe7, + 0xaf, 0x90, 0xf5, 0x32, 0xcb, 0x10, 0x82, 0xac, 0x8c, 0x0a, 0x08, 0x9f, 0x3d, 0x6a, 0x40, 0xd2, + 0x3e, 0x62, 0x76, 0xf4, 0x96, 0x56, 0x7f, 0x79, 0x9e, 0xcc, 0xa7, 0x0f, 0xd1, 0x48, 0x90, 0x01, + 0x9b, 0x3f, 0x46, 0x3d, 0x28, 0x9d, 0x40, 0x5e, 0x82, 0x31, 0x4d, 0xd7, 0x1c, 0x4d, 0xad, 0x97, + 0x37, 0xd5, 0xba, 0xaa, 0x57, 0x0e, 0x92, 0x35, 0xb3, 0x90, 0xcf, 0x9b, 0x0d, 0xb1, 0x93, 0x95, + 0x51, 0x0e, 0x29, 0x30, 0x00, 0xba, 0x04, 0x43, 0xa2, 0xa9, 0xd8, 0x81, 0xb2, 0x0d, 0x41, 0xee, + 0x4b, 0xf0, 0x7e, 0x3a, 0x0a, 0xe3, 0x0a, 0xae, 0xfe, 0xc5, 0x50, 0xec, 0x6f, 0x28, 0x96, 0x00, + 0x98, 0xbb, 0x93, 0x00, 0x7b, 0x80, 0xd1, 0x20, 0x01, 0x23, 0xc9, 0x38, 0x14, 0x6d, 0xc7, 0x37, + 0x1e, 0x37, 0x23, 0x90, 0xf2, 0x8f, 0xc7, 0x9f, 0xd3, 0x59, 0x09, 0x2d, 0x78, 0x91, 0x28, 0xc6, + 0x3f, 0xb9, 0xdb, 0x21, 0x12, 0xb5, 0x58, 0x6f, 0xf7, 0x10, 0xf4, 0x3f, 0x23, 0x30, 0xb8, 0xaa, + 0x5a, 0x6a, 0xc3, 0x46, 0x95, 0x96, 0x4c, 0x53, 0x6c, 0x3f, 0xb6, 0x7c, 0x58, 0x9d, 0xef, 0x76, + 0xf4, 0x48, 0x34, 0x3f, 0xda, 0x26, 0xd1, 0x7c, 0x0b, 0x8c, 0x92, 0xe5, 0xb0, 0xef, 0x0a, 0x03, + 0xd1, 0xf6, 0x48, 0xe1, 0xa8, 0xc7, 0x25, 0x58, 0xcf, 0x56, 0xcb, 0x57, 0xfc, 0x77, 0x18, 0x86, + 0x09, 0x86, 0x17, 0x98, 0x09, 0xf9, 0x61, 0x6f, 0x59, 0xea, 0xab, 0x94, 0x15, 0x68, 0xa8, 0xbb, + 0x25, 0x56, 0x40, 0x8b, 0x80, 0xb6, 0xdd, 0x9d, 0x91, 0xb2, 0xa7, 0x4e, 0x42, 0x7f, 0xe2, 0xd6, + 0xcd, 0xe9, 0xa3, 0x8c, 0xbe, 0x15, 0x47, 0x56, 0xc6, 0x3d, 0xa0, 0xe0, 0xf6, 0x18, 0x00, 0xe9, + 0x57, 0x99, 0x5d, 0xd4, 0x66, 0xcb, 0x9d, 0x43, 0xb7, 0x6e, 0x4e, 0x8f, 0x33, 0x2e, 0x5e, 0x9d, + 0xac, 0x24, 0x49, 0xa1, 0x48, 0x7e, 0xfb, 0x2c, 0xfb, 0xd3, 0x12, 0x20, 0x2f, 0xe4, 0x2b, 0xd8, + 0x36, 0xc9, 0xfa, 0x8c, 0x24, 0xe2, 0xbe, 0xac, 0x59, 0xea, 0x9e, 0x88, 0x7b, 0xf4, 0x22, 0x11, + 0xf7, 0x79, 0xca, 0x93, 0x5e, 0x78, 0x8c, 0xf4, 0xba, 0xb5, 0xcc, 0x4d, 0x24, 0x1c, 0x0f, 0x07, + 0xe4, 0x7f, 0x25, 0xc1, 0xd1, 0x16, 0x8b, 0x72, 0x85, 0xfd, 0x4b, 0x80, 0x2c, 0x5f, 0x25, 0xff, + 0x7e, 0x22, 0x13, 0x7a, 0xdf, 0x06, 0x3a, 0x6e, 0xb5, 0xc4, 0xdd, 0xdb, 0x17, 0xe1, 0xd9, 0xb5, + 0xf8, 0x7f, 0x2a, 0xc1, 0xa4, 0xbf, 0x79, 0xb7, 0x23, 0xcb, 0x90, 0xf2, 0xb7, 0xce, 0xbb, 0x70, + 0x4f, 0x3f, 0x5d, 0xe0, 0xd2, 0x07, 0xe8, 0xd1, 0xb3, 0x9e, 0xbb, 0xb2, 0xbd, 0xb3, 0x47, 0xfa, + 0xd6, 0x86, 0x90, 0x29, 0xec, 0xb6, 0x31, 0x3a, 0x1e, 0xff, 0x57, 0x82, 0xd8, 0xaa, 0x61, 0xd4, + 0x91, 0x01, 0xe3, 0xba, 0xe1, 0x94, 0x89, 0x65, 0xe1, 0xaa, 0xff, 0x76, 0x7a, 0xb2, 0x30, 0xb7, + 0x3f, 0x25, 0x7d, 0xe7, 0xe6, 0x74, 0x2b, 0x2b, 0x65, 0x4c, 0x37, 0x9c, 0x02, 0x85, 0xf0, 0x0b, + 0xea, 0xef, 0x84, 0x91, 0x60, 0x63, 0x2c, 0x4a, 0x3e, 0xb7, 0xef, 0xc6, 0x82, 0x6c, 0x6e, 0xdd, + 0x9c, 0x9e, 0xf4, 0x3c, 0xc6, 0x05, 0xcb, 0x4a, 0x6a, 0xd3, 0xd7, 0x3a, 0xbb, 0xde, 0xf5, 0xbd, + 0x4f, 0x4e, 0x4b, 0xa7, 0xbe, 0x28, 0x01, 0x78, 0x3b, 0x0f, 0xe8, 0x21, 0x38, 0x52, 0x58, 0x59, + 0x2e, 0x96, 0xd7, 0xd6, 0xf3, 0xeb, 0x1b, 0x6b, 0xc1, 0x9b, 0xdc, 0x62, 0x7b, 0xdc, 0x36, 0x71, + 0x45, 0xdb, 0xd2, 0x70, 0x15, 0xdd, 0x07, 0x93, 0x41, 0x6c, 0x52, 0x2a, 0x15, 0xd3, 0x52, 0x36, + 0x75, 0xfd, 0xc6, 0x4c, 0x82, 0xe5, 0x62, 0xb8, 0x8a, 0x4e, 0xc2, 0xa1, 0x56, 0xbc, 0x85, 0xe5, + 0xf9, 0x74, 0x24, 0x3b, 0x72, 0xfd, 0xc6, 0x4c, 0xd2, 0x4d, 0xda, 0x90, 0x0c, 0xc8, 0x8f, 0xc9, + 0xf9, 0x45, 0xb3, 0x70, 0xfd, 0xc6, 0xcc, 0x20, 0x53, 0x60, 0x36, 0xf6, 0xfe, 0x4f, 0x4f, 0x0d, + 0xdc, 0xf6, 0xfb, 0xde, 0x7f, 0x34, 0xd4, 0x71, 0xd7, 0xbb, 0x86, 0x75, 0x6c, 0x6b, 0xf6, 0x81, + 0x76, 0xbd, 0xfb, 0xda, 0x49, 0x97, 0x7f, 0x27, 0x0e, 0xa9, 0x79, 0xd6, 0x0a, 0x19, 0x08, 0x8c, + 0xde, 0x04, 0x83, 0x26, 0x9d, 0x46, 0xdc, 0x63, 0xb4, 0x0e, 0x06, 0xcf, 0x26, 0x1b, 0xf7, 0x2e, + 0x17, 0x9b, 0x7a, 0x6c, 0x7e, 0x99, 0x83, 0xdd, 0x31, 0xf3, 0x6e, 0x4d, 0xa5, 0xf6, 0xb5, 0xdf, + 0xc3, 0x72, 0x16, 0xbe, 0xb5, 0x12, 0xe6, 0x27, 0xb3, 0x7b, 0x21, 0xeb, 0x04, 0xc2, 0x6e, 0x87, + 0xbd, 0x57, 0x82, 0x43, 0x14, 0xcb, 0x9b, 0x88, 0x29, 0xa6, 0x48, 0xf6, 0x4f, 0x75, 0xea, 0xc2, + 0xa2, 0x6a, 0x7b, 0x77, 0x3d, 0xd8, 0x7d, 0xae, 0x7b, 0xf8, 0x44, 0x78, 0xdc, 0xd7, 0x78, 0x98, + 0xad, 0xac, 0x4c, 0xd4, 0x5b, 0x28, 0x6d, 0x34, 0x1f, 0xb8, 0xd0, 0x17, 0xdb, 0xdf, 0x56, 0xbb, + 0xff, 0x72, 0xdf, 0x33, 0x30, 0xec, 0xc5, 0x12, 0x9b, 0xff, 0xbf, 0x97, 0xfe, 0xe7, 0x0e, 0x3f, + 0x31, 0x7a, 0x9f, 0x04, 0x87, 0xbc, 0xd9, 0xdc, 0xcf, 0x96, 0xfd, 0x5f, 0x9c, 0x07, 0xf7, 0xb1, + 0x10, 0x0a, 0x2b, 0xa7, 0x2d, 0x5f, 0x59, 0x99, 0x6c, 0xb6, 0x92, 0x92, 0x25, 0xd8, 0x88, 0x3f, + 0xb2, 0xda, 0x19, 0xf1, 0xe9, 0xc7, 0xfe, 0x43, 0x73, 0x90, 0x01, 0xfb, 0x5f, 0x1d, 0xa6, 0x61, + 0x39, 0xb8, 0x4a, 0x37, 0xe4, 0x12, 0x8a, 0x5b, 0x96, 0x97, 0x01, 0xb5, 0x0e, 0x6e, 0xf8, 0x02, + 0xa3, 0xf7, 0x0a, 0x05, 0x4d, 0x42, 0xdc, 0x7f, 0xc5, 0x8f, 0x15, 0x72, 0x89, 0xf7, 0xf3, 0xe9, + 0xf3, 0xb6, 0xfb, 0xfc, 0xbf, 0x88, 0xc0, 0x29, 0xff, 0xf1, 0xd0, 0x4b, 0x4d, 0x6c, 0xed, 0xb9, + 0x2e, 0x6a, 0xaa, 0x35, 0x4d, 0xf7, 0xbf, 0x7b, 0x38, 0xea, 0x9f, 0xf0, 0x29, 0xae, 0xd0, 0x93, + 0xac, 0xc3, 0xf0, 0xaa, 0x5a, 0xc3, 0x0a, 0x7e, 0xa9, 0x89, 0x6d, 0xa7, 0xcd, 0x25, 0xf3, 0xc3, + 0x30, 0x68, 0x6c, 0x6d, 0x89, 0x23, 0xed, 0x98, 0xc2, 0x4b, 0xa4, 0xcb, 0x75, 0xad, 0xa1, 0xb1, + 0xdb, 0x60, 0x31, 0x85, 0x15, 0xd0, 0x34, 0x0c, 0x57, 0x8c, 0xa6, 0xce, 0x3d, 0x2e, 0x13, 0x13, + 0x1f, 0x54, 0x69, 0xea, 0xcc, 0xe3, 0xe4, 0xa7, 0x21, 0xc5, 0xda, 0xe3, 0x33, 0xee, 0x51, 0x48, + 0xd0, 0xeb, 0x54, 0x5e, 0xab, 0x43, 0xa4, 0x7c, 0x99, 0x5d, 0x48, 0x67, 0x5c, 0x58, 0xc3, 0xac, + 0x50, 0x28, 0x74, 0x54, 0xe5, 0xc9, 0xde, 0xa1, 0x81, 0x29, 0xca, 0x55, 0xe3, 0x6f, 0xc4, 0xe1, + 0x10, 0x3f, 0xa1, 0x53, 0x4d, 0xed, 0xcc, 0xb6, 0xe3, 0x88, 0x97, 0x11, 0xc0, 0x53, 0x5d, 0xd5, + 0xd4, 0xe4, 0x3d, 0x88, 0x5d, 0x72, 0x1c, 0x13, 0x9d, 0x82, 0xb8, 0xd5, 0xac, 0x63, 0xb1, 0xe3, + 0xe3, 0xee, 0xc9, 0xab, 0xa6, 0x36, 0x4b, 0x10, 0x94, 0x66, 0x1d, 0x2b, 0x0c, 0x05, 0x95, 0x60, + 0x7a, 0xab, 0x59, 0xaf, 0xef, 0x95, 0xab, 0x98, 0xfe, 0x2f, 0x2c, 0xf7, 0xbf, 0x49, 0xe0, 0x5d, + 0x53, 0x15, 0xdf, 0xa4, 0x24, 0xba, 0x39, 0x4e, 0xd1, 0x8a, 0x14, 0x4b, 0xfc, 0x27, 0x89, 0x92, + 0xc0, 0x91, 0x7f, 0x2f, 0x02, 0x09, 0xc1, 0x9a, 0xde, 0x10, 0xc7, 0x75, 0x5c, 0x71, 0x0c, 0x71, + 0x62, 0xe2, 0x96, 0x11, 0x82, 0x68, 0x8d, 0x0f, 0x51, 0xf2, 0xd2, 0x80, 0x42, 0x0a, 0x04, 0xe6, + 0xde, 0xdb, 0x27, 0x30, 0xb3, 0x49, 0x46, 0x2d, 0x66, 0x1a, 0x62, 0x69, 0x76, 0x69, 0x40, 0xa1, + 0x25, 0x94, 0x81, 0x41, 0xe2, 0x19, 0x0e, 0xfb, 0xd0, 0x27, 0x81, 0xf3, 0x32, 0x3a, 0x0c, 0x71, + 0x53, 0x75, 0x2a, 0xec, 0x4a, 0x1d, 0xa9, 0x60, 0x45, 0xf4, 0x38, 0x0c, 0xb2, 0x07, 0xd6, 0xe1, + 0x7f, 0x34, 0x43, 0x94, 0xc1, 0xbe, 0x64, 0x47, 0xe4, 0x5e, 0x55, 0x1d, 0x07, 0x5b, 0x3a, 0x61, + 0xc8, 0xd0, 0x11, 0x82, 0xd8, 0xa6, 0x51, 0xdd, 0xe3, 0xff, 0xfc, 0x86, 0xfe, 0xe6, 0xff, 0x6d, + 0x83, 0xda, 0x43, 0x99, 0x56, 0xb2, 0xff, 0xf9, 0x95, 0x12, 0xc0, 0x02, 0x41, 0x2a, 0xc1, 0x84, + 0x5a, 0xad, 0x6a, 0xec, 0xff, 0xd0, 0x94, 0x37, 0x35, 0x1a, 0x21, 0x6c, 0xfa, 0x1f, 0xdd, 0x3a, + 0x8d, 0x05, 0xf2, 0x08, 0x0a, 0x1c, 0xbf, 0x90, 0x84, 0x21, 0x93, 0x09, 0x25, 0x5f, 0x80, 0xf1, + 0x16, 0x49, 0x89, 0x7c, 0x3b, 0x9a, 0x5e, 0x15, 0x8f, 0x19, 0xc8, 0x6f, 0x02, 0xa3, 0x5f, 0xa3, + 0x64, 0x67, 0x51, 0xf4, 0x77, 0xe1, 0xdd, 0x9d, 0x1f, 0xbb, 0x8c, 0xfa, 0x1e, 0xbb, 0xa8, 0xa6, + 0x56, 0x48, 0x52, 0xfe, 0xfc, 0x89, 0x4b, 0x9e, 0x57, 0xb0, 0xe7, 0x2d, 0xb3, 0x86, 0x55, 0x23, + 0xb3, 0xb4, 0x98, 0x7d, 0x49, 0x95, 0x6a, 0x6a, 0x36, 0x35, 0x47, 0xef, 0xeb, 0x98, 0xf6, 0x05, + 0xdf, 0x6f, 0xfa, 0xf0, 0x25, 0x36, 0x9f, 0x5f, 0x5d, 0x70, 0xed, 0xf8, 0xcb, 0x11, 0x38, 0xee, + 0xb3, 0x63, 0x1f, 0x72, 0xab, 0x39, 0x67, 0xdb, 0x5b, 0x7c, 0x1f, 0xcf, 0x8c, 0x2f, 0x43, 0x8c, + 0xe0, 0xa3, 0x1e, 0xff, 0x0b, 0x23, 0xf3, 0x2b, 0x5f, 0xfb, 0x27, 0x72, 0xf0, 0xd4, 0x2a, 0x30, + 0x2a, 0x94, 0x49, 0xe1, 0x7d, 0xfd, 0xeb, 0x2f, 0xed, 0x7d, 0x18, 0xd4, 0xbe, 0x7d, 0x6a, 0x0c, + 0xeb, 0xf0, 0xdb, 0xe7, 0x3a, 0x3e, 0x43, 0x65, 0x11, 0xb3, 0x7b, 0x12, 0xb5, 0x8f, 0x70, 0xdc, + 0xe9, 0xfe, 0x7f, 0xb7, 0x11, 0xec, 0x33, 0x1d, 0xdb, 0x85, 0xc3, 0xcf, 0x92, 0xb6, 0xbd, 0x65, + 0xb2, 0x08, 0xec, 0x87, 0xdd, 0xd3, 0x3c, 0x89, 0xff, 0x43, 0x3d, 0x71, 0x52, 0x07, 0x9e, 0x7c, + 0x7c, 0x81, 0x78, 0xdf, 0x6c, 0xc7, 0xf9, 0x62, 0xd6, 0x37, 0x59, 0x28, 0x3e, 0x4a, 0xf9, 0x97, + 0x24, 0x38, 0xd2, 0xd2, 0x34, 0x8f, 0xf1, 0xf3, 0x6d, 0x9e, 0x2a, 0x1c, 0x28, 0xb3, 0x99, 0x6f, + 0x23, 0xec, 0xfd, 0x3d, 0x85, 0x65, 0x52, 0x04, 0xa4, 0x7d, 0x0a, 0x0e, 0x05, 0x85, 0x15, 0x6a, + 0xba, 0x17, 0x46, 0x83, 0x3b, 0xc2, 0x5c, 0x5d, 0x23, 0x81, 0x3d, 0x61, 0xb9, 0x1c, 0xd6, 0xb3, + 0xdb, 0xd7, 0x12, 0x24, 0x5d, 0x54, 0x9e, 0x02, 0xf7, 0xdd, 0x55, 0x8f, 0x52, 0xfe, 0xb0, 0x04, + 0x33, 0xc1, 0x16, 0x7c, 0xc9, 0xd0, 0xfe, 0x84, 0xbd, 0x6d, 0x43, 0xfc, 0xba, 0x04, 0x77, 0x75, + 0x91, 0x89, 0x2b, 0xe0, 0x65, 0x98, 0xf4, 0xed, 0x04, 0x88, 0x10, 0x2e, 0x86, 0xfd, 0x54, 0xef, + 0x34, 0xd4, 0x5d, 0xf8, 0x1e, 0x23, 0x4a, 0xf9, 0xec, 0x37, 0xa7, 0x27, 0x5a, 0xeb, 0x6c, 0x65, + 0xa2, 0x75, 0xf5, 0x7e, 0x1b, 0xed, 0xe3, 0x55, 0x09, 0x1e, 0x08, 0x76, 0xb5, 0x4d, 0x3e, 0xfb, + 0xa3, 0x1a, 0x87, 0xff, 0x20, 0xc1, 0xa9, 0x7e, 0x84, 0xe3, 0x03, 0xb2, 0x09, 0x13, 0x5e, 0xa6, + 0x1d, 0x1e, 0x8f, 0x7d, 0xe5, 0xef, 0xcc, 0x4a, 0x91, 0xcb, 0xed, 0x0e, 0x28, 0xde, 0xe4, 0x8e, + 0xe5, 0x1f, 0x72, 0x57, 0xc9, 0xc1, 0xdd, 0x5c, 0xa1, 0xe4, 0xc0, 0x7e, 0x6e, 0x9b, 0xb1, 0x88, + 0xb4, 0x19, 0x0b, 0x2f, 0x35, 0x97, 0xaf, 0xf2, 0xb8, 0xd5, 0x66, 0x0f, 0xee, 0x6d, 0x30, 0xd1, + 0xc6, 0x94, 0xb9, 0x57, 0xef, 0xc3, 0x92, 0x15, 0xd4, 0x6a, 0xac, 0xf2, 0x1e, 0x4c, 0xd3, 0x76, + 0xdb, 0x28, 0xfa, 0x4e, 0x77, 0xb9, 0xc1, 0x63, 0x4b, 0xdb, 0xa6, 0x79, 0xdf, 0x17, 0x60, 0x90, + 0x8d, 0x33, 0xef, 0xee, 0x01, 0x0c, 0x85, 0x33, 0x90, 0x7f, 0x4e, 0xc4, 0xb2, 0xa2, 0x10, 0xbb, + 0xbd, 0x0f, 0xf5, 0xd3, 0xd7, 0xdb, 0xe4, 0x43, 0x3e, 0x65, 0x7c, 0x43, 0x44, 0xb5, 0xf6, 0xd2, + 0x71, 0x75, 0x54, 0x6e, 0x5b, 0x54, 0x63, 0xba, 0xb9, 0xb3, 0xe1, 0xeb, 0x17, 0x44, 0xf8, 0x72, + 0xfb, 0xd4, 0x23, 0x7c, 0xfd, 0x68, 0x54, 0xef, 0x06, 0xb2, 0x1e, 0x62, 0xfe, 0x59, 0x0c, 0x64, + 0xdf, 0x93, 0xe0, 0x28, 0xed, 0x9b, 0x7f, 0x23, 0x62, 0xbf, 0x2a, 0x7f, 0x08, 0x90, 0x6d, 0x55, + 0xca, 0x6d, 0xbd, 0x3b, 0x6d, 0x5b, 0x95, 0x2b, 0x81, 0xf9, 0xe5, 0x21, 0x40, 0xd5, 0xc0, 0x76, + 0x13, 0xc5, 0x66, 0xb7, 0xe4, 0xd2, 0x55, 0xdf, 0x6e, 0x46, 0x9b, 0xe1, 0x8c, 0xdd, 0x86, 0xe1, + 0xfc, 0xba, 0x04, 0xd9, 0x76, 0x5d, 0xe6, 0xc3, 0xa7, 0xc1, 0xe1, 0xc0, 0x21, 0x41, 0x78, 0x04, + 0x1f, 0xea, 0x67, 0x2b, 0x27, 0xe4, 0x46, 0x87, 0x2c, 0x7c, 0xa7, 0xf3, 0x80, 0xe9, 0xa0, 0x85, + 0xb6, 0x66, 0xd6, 0x3f, 0x32, 0xf7, 0xf9, 0x42, 0x4b, 0x5c, 0xfd, 0x33, 0x91, 0x7b, 0xef, 0xc2, + 0x54, 0x07, 0xa9, 0xef, 0xf4, 0xbc, 0xb7, 0xdd, 0x71, 0x30, 0x6f, 0x77, 0xfa, 0xfe, 0x18, 0xf7, + 0x84, 0xe0, 0x0d, 0x6c, 0xdf, 0x5a, 0xac, 0xdd, 0x13, 0x2e, 0xf9, 0xad, 0x70, 0xac, 0x2d, 0x15, + 0x97, 0x2d, 0x07, 0xb1, 0x6d, 0xcd, 0x76, 0xb8, 0x58, 0xf7, 0x75, 0x12, 0x2b, 0x44, 0x4d, 0x69, + 0x64, 0x04, 0x69, 0xca, 0x7a, 0xd5, 0x30, 0xea, 0x5c, 0x0c, 0xf9, 0x32, 0x8c, 0xfb, 0x60, 0xbc, + 0x91, 0xf3, 0x10, 0x33, 0x0d, 0xfe, 0x79, 0x82, 0xe1, 0xb3, 0xc7, 0x3b, 0xee, 0xde, 0x1b, 0x46, + 0x9d, 0x77, 0x9b, 0xe2, 0xcb, 0x93, 0x80, 0x18, 0x33, 0xba, 0x91, 0x2f, 0x9a, 0x58, 0x83, 0x89, + 0x00, 0x94, 0x37, 0xf2, 0x86, 0x0e, 0x09, 0xce, 0x7e, 0xe7, 0x10, 0xc4, 0x29, 0x57, 0xf4, 0x31, + 0x29, 0xf0, 0x95, 0xa0, 0xd9, 0x4e, 0x6c, 0xda, 0xaf, 0x89, 0xb3, 0x67, 0xfa, 0xc6, 0xe7, 0x39, + 0xdb, 0xa9, 0x77, 0xff, 0xdb, 0x6f, 0x7f, 0x24, 0x72, 0x0f, 0x92, 0xcf, 0x74, 0x58, 0x8d, 0xfb, + 0xfc, 0xe5, 0x33, 0x81, 0xb7, 0xef, 0xa7, 0xfb, 0x6b, 0x4a, 0x48, 0x36, 0xdb, 0x2f, 0x3a, 0x17, + 0xec, 0x02, 0x15, 0xec, 0x1c, 0x7a, 0xb4, 0xb7, 0x60, 0x67, 0xde, 0x11, 0x74, 0x9a, 0x77, 0xa1, + 0xdf, 0x91, 0x60, 0xb2, 0xdd, 0x92, 0x0e, 0x3d, 0xd1, 0x9f, 0x14, 0xad, 0x29, 0x45, 0xf6, 0xc9, + 0x03, 0x50, 0xf2, 0xae, 0xcc, 0xd3, 0xae, 0xe4, 0xd1, 0xd3, 0x07, 0xe8, 0xca, 0x19, 0xff, 0xfe, + 0xfe, 0xff, 0x96, 0xe0, 0x44, 0xd7, 0x15, 0x12, 0xca, 0xf7, 0x27, 0x65, 0x97, 0xdc, 0x29, 0x5b, + 0x78, 0x23, 0x2c, 0x78, 0x8f, 0x9f, 0xa5, 0x3d, 0xbe, 0x8c, 0x16, 0x0e, 0xd2, 0xe3, 0xb6, 0x87, + 0x28, 0xe8, 0x37, 0x83, 0x37, 0x0b, 0xbb, 0x9b, 0x53, 0xcb, 0xc2, 0xa3, 0x87, 0x63, 0xb4, 0x26, + 0xb5, 0xf2, 0xf3, 0xb4, 0x0b, 0x0a, 0x5a, 0x7d, 0x83, 0x83, 0x76, 0xe6, 0x1d, 0xc1, 0xc0, 0xff, + 0x2e, 0xf4, 0xbf, 0xa4, 0xf6, 0x17, 0x05, 0x1f, 0xef, 0x2a, 0x62, 0xe7, 0x45, 0x55, 0xf6, 0x89, + 0xfd, 0x13, 0xf2, 0x4e, 0x36, 0x68, 0x27, 0x6b, 0x08, 0xdf, 0xee, 0x4e, 0xb6, 0x1d, 0x44, 0xf4, + 0x55, 0x09, 0x26, 0xdb, 0xad, 0x49, 0x7a, 0xb8, 0x65, 0x97, 0x45, 0x56, 0x0f, 0xb7, 0xec, 0xb6, + 0x00, 0x92, 0xdf, 0x44, 0x3b, 0x7f, 0x1e, 0x3d, 0xd6, 0xa9, 0xf3, 0x5d, 0x47, 0x91, 0xf8, 0x62, + 0xd7, 0x24, 0xbf, 0x87, 0x2f, 0xf6, 0xb3, 0x8e, 0xe9, 0xe1, 0x8b, 0x7d, 0xad, 0x31, 0x7a, 0xfb, + 0xa2, 0xdb, 0xb3, 0x3e, 0x87, 0xd1, 0x46, 0x5f, 0x96, 0x60, 0x24, 0x90, 0x11, 0xa3, 0x47, 0xba, + 0x0a, 0xda, 0x6e, 0xc1, 0x90, 0x3d, 0xbb, 0x1f, 0x12, 0xde, 0x97, 0x05, 0xda, 0x97, 0x39, 0x94, + 0x3f, 0x48, 0x5f, 0x82, 0x67, 0xa5, 0x5f, 0x97, 0x60, 0xa2, 0x4d, 0x96, 0xd9, 0xc3, 0x0b, 0x3b, + 0x27, 0xcd, 0xd9, 0x27, 0xf6, 0x4f, 0xc8, 0x7b, 0x75, 0x91, 0xf6, 0xea, 0x2d, 0xe8, 0xa9, 0x83, + 0xf4, 0xca, 0x37, 0x3f, 0xdf, 0xf4, 0xee, 0x5d, 0xf9, 0xda, 0x41, 0xe7, 0xf7, 0x29, 0x98, 0xe8, + 0xd0, 0xe3, 0xfb, 0xa6, 0xe3, 0xfd, 0x79, 0x8e, 0xf6, 0xe7, 0x59, 0xb4, 0xf2, 0xc6, 0xfa, 0xd3, + 0x3a, 0xad, 0x7f, 0xbe, 0xf5, 0x05, 0x60, 0x77, 0x2b, 0x6a, 0x9b, 0xac, 0x66, 0x1f, 0xdd, 0x17, + 0x0d, 0xef, 0xd4, 0x13, 0xb4, 0x53, 0x67, 0xd1, 0xc3, 0x9d, 0x3a, 0xe5, 0xbb, 0x5c, 0xa7, 0xe9, + 0x5b, 0xc6, 0x99, 0x77, 0xb0, 0x14, 0xf8, 0x5d, 0xe8, 0x27, 0xc5, 0xc5, 0xa6, 0x93, 0x5d, 0xdb, + 0xf5, 0xe5, 0xb1, 0xd9, 0x07, 0xfa, 0xc0, 0xe4, 0x72, 0xdd, 0x43, 0xe5, 0x9a, 0x42, 0xc7, 0x3b, + 0xc9, 0x45, 0x72, 0x59, 0xf4, 0x01, 0xc9, 0xbd, 0x0b, 0x79, 0xaa, 0x3b, 0x6f, 0x7f, 0xb2, 0x9b, + 0x7d, 0xb0, 0x2f, 0x5c, 0x2e, 0xc9, 0x7d, 0x54, 0x92, 0x19, 0x34, 0xd5, 0x51, 0x12, 0x96, 0xfa, + 0xde, 0xee, 0x9b, 0x03, 0xd7, 0x8f, 0xc0, 0x74, 0x87, 0x16, 0x9d, 0xdd, 0x1e, 0x67, 0x5c, 0x5d, + 0x1e, 0xc2, 0xf6, 0x7c, 0xe8, 0x7a, 0xbb, 0x3f, 0xd3, 0xda, 0xe7, 0x81, 0xd8, 0x6f, 0xc5, 0x00, + 0x2d, 0xd9, 0xb5, 0x39, 0x0b, 0xb3, 0x7f, 0x22, 0xc9, 0xbd, 0x3c, 0xf4, 0xc2, 0x4b, 0x7a, 0x43, + 0x2f, 0xbc, 0x96, 0x02, 0x6f, 0xa6, 0x22, 0xfb, 0x7b, 0x97, 0xd9, 0xf7, 0xc3, 0xa9, 0xe8, 0x0f, + 0xe5, 0xe1, 0x54, 0xfb, 0x7b, 0xd5, 0xb1, 0xdb, 0xf7, 0x00, 0x23, 0x7e, 0xd0, 0x47, 0x28, 0xfc, + 0x3d, 0xe4, 0x60, 0x97, 0xf7, 0x90, 0x99, 0x8e, 0x8f, 0x1e, 0x39, 0x35, 0x3a, 0x27, 0x3e, 0x5a, + 0x3a, 0xd4, 0xdf, 0x4d, 0x58, 0xfe, 0x55, 0x53, 0x6f, 0x0b, 0xe1, 0x38, 0x64, 0x5b, 0xcd, 0xc9, + 0x75, 0xea, 0x8f, 0x44, 0x21, 0xbd, 0x64, 0xd7, 0x4a, 0x55, 0xcd, 0xb9, 0x43, 0xb6, 0xf6, 0x74, + 0xe7, 0x47, 0x2d, 0xe8, 0xd6, 0xcd, 0xe9, 0x51, 0xa6, 0xd3, 0x2e, 0x9a, 0x6c, 0xc0, 0x58, 0xe8, + 0x29, 0x31, 0xb7, 0xac, 0xe2, 0x41, 0x5e, 0x34, 0x87, 0x58, 0xc9, 0xf4, 0x0d, 0x82, 0xcf, 0xbe, + 0xd1, 0x6e, 0x7b, 0x63, 0x66, 0x06, 0x75, 0xe9, 0x4e, 0xbe, 0x00, 0xf4, 0xc6, 0x2c, 0x0b, 0x99, + 0xf0, 0xa0, 0xb8, 0x23, 0xf6, 0x07, 0x12, 0x0c, 0x2f, 0xd9, 0x22, 0x15, 0xc4, 0x3f, 0xa6, 0xef, + 0x8f, 0x1e, 0x77, 0xbf, 0xf8, 0x1d, 0xed, 0xcf, 0x6e, 0xc5, 0x57, 0xc0, 0x3d, 0x25, 0x1c, 0x82, + 0x09, 0x5f, 0x3f, 0xdd, 0xfe, 0xff, 0x76, 0x84, 0xc6, 0xc7, 0x02, 0xae, 0x69, 0xba, 0x9b, 0x45, + 0xe2, 0x3f, 0xaf, 0xaf, 0x2b, 0x3c, 0x3d, 0xc7, 0x0e, 0xaa, 0xe7, 0x1d, 0x1a, 0x20, 0x42, 0xfa, + 0x74, 0x37, 0xbe, 0x96, 0x5a, 0xdf, 0xfe, 0x48, 0xfb, 0xf8, 0xac, 0x4e, 0xe8, 0x85, 0x8f, 0xfc, + 0xba, 0x04, 0x23, 0x4b, 0x76, 0x6d, 0x43, 0xaf, 0xfe, 0x7f, 0x6f, 0xbf, 0x5b, 0x70, 0x28, 0xd0, + 0xd3, 0x3b, 0xa4, 0xd2, 0xb3, 0xaf, 0xc6, 0x20, 0xba, 0x64, 0xd7, 0xd0, 0x4b, 0x30, 0x16, 0x4e, + 0x1a, 0x3a, 0xe6, 0x82, 0xad, 0x33, 0x42, 0xe7, 0xf5, 0x5a, 0xe7, 0xd9, 0x03, 0xed, 0xc0, 0x48, + 0x70, 0xe6, 0x38, 0xd9, 0x85, 0x49, 0x00, 0x33, 0xfb, 0x70, 0xbf, 0x98, 0x6e, 0x63, 0x6f, 0x87, + 0x84, 0x1b, 0xf4, 0xee, 0xee, 0x42, 0x2d, 0x90, 0x3a, 0x67, 0xb7, 0x6d, 0xc2, 0x0a, 0xd1, 0x5e, + 0x38, 0xa4, 0x74, 0xd3, 0x5e, 0x08, 0xb7, 0xab, 0xf6, 0x3a, 0xb9, 0xd6, 0x26, 0x80, 0xcf, 0x0f, + 0xee, 0xed, 0xc2, 0xc1, 0x43, 0xcb, 0x9e, 0xee, 0x0b, 0xcd, 0x3d, 0x74, 0xba, 0xcd, 0xc9, 0xf8, + 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x80, 0x38, 0xe7, 0x40, 0xba, 0x97, 0x00, 0x00, } r := bytes.NewReader(gzipped) gzipr, err := compress_gzip.NewReader(r) diff --git a/x/staking/types/validator.go b/x/staking/types/validator.go index e551e7525..b23a7708f 100644 --- a/x/staking/types/validator.go +++ b/x/staking/types/validator.go @@ -103,9 +103,7 @@ func (v Validators) Less(i, j int) bool { // Implements sort interface func (v Validators) Swap(i, j int) { - it := v[i] - v[i] = v[j] - v[j] = it + v[i], v[j] = v[j], v[i] } // ValidatorsByVotingPower implements sort.Interface for []Validator based on diff --git a/x/upgrade/client/cli/tx.go b/x/upgrade/client/cli/tx.go index 32472ad84..bb8382af0 100644 --- a/x/upgrade/client/cli/tx.go +++ b/x/upgrade/client/cli/tx.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/x/gov/client/cli" gov "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/upgrade/types" @@ -69,11 +70,14 @@ func NewCmdSubmitUpgradeProposal() *cobra.Command { return err } - if err = msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := gov.NewMsgClient(svcMsgClientConn) + _, err = msgClient.SubmitProposal(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } @@ -128,11 +132,14 @@ func NewCmdSubmitCancelUpgradeProposal() *cobra.Command { return err } - if err = msg.ValidateBasic(); err != nil { + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := gov.NewMsgClient(svcMsgClientConn) + _, err = msgClient.SubmitProposal(cmd.Context(), msg) + if err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) }, } diff --git a/x/upgrade/types/query.pb.gw.go b/x/upgrade/types/query.pb.gw.go index cf1f9def8..166646416 100644 --- a/x/upgrade/types/query.pb.gw.go +++ b/x/upgrade/types/query.pb.gw.go @@ -328,11 +328,11 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_CurrentPlan_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "upgrade", "v1beta1", "current_plan"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_CurrentPlan_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "upgrade", "v1beta1", "current_plan"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_AppliedPlan_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "upgrade", "v1beta1", "applied_plan", "name"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_AppliedPlan_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "upgrade", "v1beta1", "applied_plan", "name"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_UpgradedConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "upgrade", "v1beta1", "upgraded_consensus_state", "last_height"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_UpgradedConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "upgrade", "v1beta1", "upgraded_consensus_state", "last_height"}, "", runtime.AssumeColonVerbOpt(false))) ) var (