diff --git a/build.sh b/.build.sh similarity index 96% rename from build.sh rename to .build.sh index e48187f1cb93..1f7a1488371b 100755 --- a/build.sh +++ b/.build.sh @@ -11,7 +11,7 @@ set -ue # - DEBUG # Source builder's functions library -. /usr/local/share/cosmos-sdk/buildlib.sh +. /usr/local/share/tendermint/buildlib.sh # These variables are now available # - BASEDIR diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 099db02d8a33..000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,64 +0,0 @@ -version: 2.1 - -executors: - docs: - docker: - - image: tendermintdev/docker-website-deployment - environment: - AWS_REGION: us-east-1 - -commands: - make: - parameters: - description: - type: string - target: - type: string - steps: - - attach_workspace: - at: /tmp/workspace - - restore_cache: - name: "Restore source code cache" - keys: - - go-src-v1-{{ .Revision }} - - checkout - - restore_cache: - name: "Restore go modules cache" - keys: - - go-mod-v2-{{ checksum "go.sum" }} - - run: - name: << parameters.description >> - command: | - make << parameters.target >> - -jobs: - build-docs: - executor: docs - steps: - - checkout - - run: - name: "Build docs" - command: make build-docs LEDGER_ENABLED=false - - run: - name: "Upload docs to S3" - command: make sync-docs LEDGER_ENABLED=false - -workflows: - version: 2 - test-suite: - jobs: - - build-docs: - context: docs-deployment-master - filters: - branches: - only: - - docs-staging - - build-docs: - context: docs-deployment-release - filters: - branches: - only: - - master - tags: - only: - - /v.*/ diff --git a/.github/workflows/atlas.yml b/.github/workflows/atlas.yml new file mode 100644 index 000000000000..25e749439dab --- /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/docker.yml b/.github/workflows/docker.yml index 9456ebf30255..6116e6d92107 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -42,6 +42,7 @@ jobs: uses: docker/setup-buildx-action@v1 - name: Login to DockerHub + if: ${{ github.event_name != 'pull_request' }} uses: docker/login-action@v1 with: username: ${{ secrets.DOCKER_USERNAME }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000000..493d1c21c231 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,31 @@ +name: Documentation +# This job builds and deploys documenation to github pages. +# It runs on every push to master. +on: + push: + branches: + - master + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + container: + image: tendermintdev/docker-website-deployment + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v2.3.1 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Install and Build 🔧 + run: | + apk add rsync + make build-docs LEDGER_ENABLED=false + + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@4.0.0 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages + FOLDER: ~/output diff --git a/.github/workflows/linkchecker.yml b/.github/workflows/linkchecker.yml index d16019158c89..887e9903587f 100644 --- a/.github/workflows/linkchecker.yml +++ b/.github/workflows/linkchecker.yml @@ -7,6 +7,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@master - - uses: gaurav-nelson/github-action-markdown-link-check@1.0.11 + - uses: gaurav-nelson/github-action-markdown-link-check@1.0.12 with: folder-path: "docs" diff --git a/.github/workflows/proto-docker.yml b/.github/workflows/proto-docker.yml new file mode 100644 index 000000000000..7a9aecfb25ee --- /dev/null +++ b/.github/workflows/proto-docker.yml @@ -0,0 +1,46 @@ +name: Build & Push SDK Proto Builder +on: + pull_request: + push: + branches: + - master + paths: + - "contrib/devtools/dockerfile" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Prepare + id: prep + run: | + DOCKER_IMAGE=tendermintdev/sdk-proto-gen + VERSION=noop + if [[ $GITHUB_REF == refs/tags/* ]]; then + VERSION=${GITHUB_REF#refs/tags/} + elif [[ $GITHUB_REF == refs/heads/* ]]; then + VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g') + if [ "${{ github.event.repository.default_branch }}" = "$VERSION" ]; then + VERSION=latest + fi + fi + TAGS="${DOCKER_IMAGE}:${VERSION}" + echo ::set-output name=tags::${TAGS} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to DockerHub + if: ${{ github.event_name != 'pull_request' }} + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUBTM_USERNAME }} + password: ${{ secrets.DOCKERHUBTM_TOKEN }} + + - name: Publish to Docker Hub + uses: docker/build-push-action@v2 + with: + context: ./contrib/devtools + file: ./contrib/devtools/dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.prep.outputs.tags }} diff --git a/.github/workflows/release-sims.yml b/.github/workflows/release-sims.yml index 68ca31051135..82e68ff41b07 100644 --- a/.github/workflows/release-sims.yml +++ b/.github/workflows/release-sims.yml @@ -30,7 +30,7 @@ jobs: - name: install runsim run: | export GO111MODULE="on" && go get github.com/cosmos/tools/cmd/runsim@v1.0.0 - - uses: actions/cache@v2.1.3 + - uses: actions/cache@v2.1.4 with: path: ~/go/bin key: ${{ runner.os }}-go-runsim-binary @@ -40,7 +40,7 @@ jobs: needs: [build, install-runsim] steps: - uses: actions/checkout@v2 - - uses: actions/cache@v2.1.3 + - uses: actions/cache@v2.1.4 with: path: ~/go/bin key: ${{ runner.os }}-go-runsim-binary diff --git a/.github/workflows/sims.yml b/.github/workflows/sims.yml index d50ddb63a289..a84570b80745 100644 --- a/.github/workflows/sims.yml +++ b/.github/workflows/sims.yml @@ -39,7 +39,7 @@ jobs: run: go version - name: Install runsim run: export GO111MODULE="on" && go get github.com/cosmos/tools/cmd/runsim@v1.0.0 - - uses: actions/cache@v2.1.3 + - uses: actions/cache@v2.1.4 with: path: ~/go/bin key: ${{ runner.os }}-go-runsim-binary @@ -60,7 +60,7 @@ jobs: **/**.go go.mod go.sum - - uses: actions/cache@v2.1.3 + - uses: actions/cache@v2.1.4 with: path: ~/go/bin key: ${{ runner.os }}-go-runsim-binary @@ -88,7 +88,7 @@ jobs: go.sum SET_ENV_NAME_INSERTIONS: 1 SET_ENV_NAME_LINES: 1 - - uses: actions/cache@v2.1.3 + - uses: actions/cache@v2.1.4 with: path: ~/go/bin key: ${{ runner.os }}-go-runsim-binary @@ -116,7 +116,7 @@ jobs: go.sum SET_ENV_NAME_INSERTIONS: 1 SET_ENV_NAME_LINES: 1 - - uses: actions/cache@v2.1.3 + - uses: actions/cache@v2.1.4 with: path: ~/go/bin key: ${{ runner.os }}-go-runsim-binary @@ -144,7 +144,7 @@ jobs: go.sum SET_ENV_NAME_INSERTIONS: 1 SET_ENV_NAME_LINES: 1 - - uses: actions/cache@v2.1.3 + - uses: actions/cache@v2.1.4 with: path: ~/go/bin key: ${{ runner.os }}-go-runsim-binary diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2cbcb1477a51..b499f2822817 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,11 +26,31 @@ jobs: - name: install tparse run: | export GO111MODULE="on" && go get github.com/mfridman/tparse@v0.8.3 - - uses: actions/cache@v2.1.3 + - uses: actions/cache@v2.1.4 with: path: ~/go/bin key: ${{ runner.os }}-go-tparse-binary + build: + runs-on: ubuntu-latest + strategy: + matrix: + go-arch: ["amd64", "arm", "arm64"] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - uses: technote-space/get-diff-action@v4 + id: git_diff + with: + PATTERNS: | + **/**.go + go.mod + go.sum + - name: Build + run: GOARCH=${{ matrix.go-arch }} LEDGER_ENABLED=false make build + test-cosmovisor: runs-on: ubuntu-latest steps: @@ -188,6 +208,23 @@ jobs: name: "${{ github.sha }}-${{ matrix.part }}-race-output" path: ./${{ matrix.part }}-race-output.txt + test-rosetta: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v4 + id: git_diff + with: + PATTERNS: | + **/**.go + go.mod + go.sum + - name: test rosetta + run: | + make test-rosetta + # if: env.GIT_DIFF + race-detector-report: runs-on: ubuntu-latest needs: [test-race, install-tparse] @@ -217,7 +254,7 @@ jobs: with: name: "${{ github.sha }}-03-race-output" if: env.GIT_DIFF - - uses: actions/cache@v2.1.3 + - uses: actions/cache@v2.1.4 with: path: ~/go/bin key: ${{ runner.os }}-go-tparse-binary diff --git a/.mergify.yml b/.mergify.yml index fbf9d7015ae8..ed1892e5cbae 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -8,3 +8,11 @@ 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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 776c3c0ac755..45c93da693c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,199 +36,89 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] -## [v0.40.0-rc7](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0-rc7) - 2020-01-07 - -### Improvements - -- (tendermint) [\#8281](https://github.com/cosmos/cosmos-sdk/pull/8281) Update tendermint to v0.34.1 - -### Bug Fixes - -- (x/gentx) [\#8255](https://github.com/cosmos/cosmos-sdk/pull/8255) Fix gentx command setting wrong amount - -## [v0.40.0-rc6](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0-rc6) - 2020-01-04 +### Client Breaking Changes -### Improvements +* [\#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. -- (x/staking) [\#8178](https://github.com/cosmos/cosmos-sdk/pull/8178) Update default historical header number for stargate +### API Breaking Changes -### Bug Fixes +* (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. -- (x/distribution) [\#8240](https://github.com/cosmos/cosmos-sdk/pull/8240) fix setting withdraw address in x/distribution -- (x/gentx) [\#8183](https://github.com/cosmos/cosmos-sdk/pull/8183) change gentx cmd amount to arg from flag -- (x/ibc) [\#8165](https://github.com/cosmos/cosmos-sdk/pull/8165) Support IBC upgrade on same revision -- (x/ibc) [\#8169](https://github.com/cosmos/cosmos-sdk/pull/8169) fix IBC software upgrade proposals from automatically failing, add unpackinterfaces function -- (x/ibc) [\#8170](https://github.com/cosmos/cosmos-sdk/pull/8170) fix ClientUpdateProposal unpacker -- (x/ibc) [\#8187](https://github.com/cosmos/cosmos-sdk/pull/8187) fix IBC upgrade, issue with commit & halting logic -- (x/ibc) [\#8200](https://github.com/cosmos/cosmos-sdk/pull/8200) fix IBC timeout verification bug -- (rest) [\#8221](https://github.com/cosmos/cosmos-sdk/pull/8221) fix unregistered interface failure when calling GET /blocks/latest via REST API +### State Machine Breaking -## [v0.40.0-rc5](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0-rc5) - 2020-12-14 +* (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 ### Improvements -* (auth/tx) Add new auth/tx gRPC & gRPC-Gateway endpoints for basic querying & broadcasting support - * [\#7842](https://github.com/cosmos/cosmos-sdk/pull/7842) Add TxsByEvent gRPC endpoint - * [\#7852](https://github.com/cosmos/cosmos-sdk/pull/7852) Add tx broadcast gRPC endpoint -* (client/keys) [\#8043](https://github.com/cosmos/cosmos-sdk/pull/8043) Add support for export of unarmored private key -* (client/tx) [\#7801](https://github.com/cosmos/cosmos-sdk/pull/7801) Update sign-batch multisig to work online -* (crypto) [\#7987](https://github.com/cosmos/cosmos-sdk/pull/7987) Fix the inconsistency of CryptoCdc, only use - `codec/legacy.Cdc`. -* (logging) [\#8072](https://github.com/cosmos/cosmos-sdk/pull/8072) Refactor logging: - * Use [zerolog](https://github.com/rs/zerolog) over Tendermint's go-kit logging wrapper. - * Introduce Tendermint's `--log_format=plain|json` flag. Using format `json` allows for emitting structured JSON - logs which can be consumed by an external logging facility (e.g. Loggly). Both formats log to STDERR. - * The existing `--log_level` flag and it's default value now solely relates to the global logging - level (e.g. `info`, `debug`, etc...) instead of `:`. -* (SDK) [\#7925](https://github.com/cosmos/cosmos-sdk/pull/7925) Updated dependencies - * Updated gRPC dependency to v1.33.2 - * Updated iavl dependency to v0.15-rc5 - * Updated tendermint dependency to v0.34.0 -* (version) [\#7848](https://github.com/cosmos/cosmos-sdk/pull/7848) [\#7941](https://github.com/cosmos/cosmos-sdk/pull/7941) - `version --long` output now shows the list of build dependencies and replaced build dependencies. -* (x/genutil) [\#8099](https://github.com/cosmos/cosmos-sdk/pull/8099) `init` now supports a `--recover` flag to recover - the private validator key from a given mnemonic - -### Client Breaking - -* (crypto) [\#7419](https://github.com/cosmos/cosmos-sdk/pull/7419) The SDK doesn't use Tendermint's `crypto.PubKey` - interface anymore, and uses instead it's own `PubKey` interface, defined in `crypto/types`. Replace all instances of - `crypto.PubKey` by `cryptotypes.Pubkey`. -* (x/staking) [\#7419](https://github.com/cosmos/cosmos-sdk/pull/7419) The `TmConsPubKey` method on ValidatorI has been - removed and replaced instead by `ConsPubKey` (which returns a SDK `cryptotypes.PubKey`) and `TmConsPublicKey` (which - returns a Tendermint proto PublicKey). - -### State Machine Breaking Changes - -* (x/staking) [\#7979](https://github.com/cosmos/cosmos-sdk/pull/7979) keeper pubkey storage serialization migration - from bech32 to protobuf. - -### Features -* (codec/types) [\#8106](https://github.com/cosmos/cosmos-sdk/pull/8106) Adding `NewAnyWithCustomTypeURL` to correctly - marshal Messages in TxBuilder. - -### API Breaking - -* [\#8080](https://github.com/cosmos/cosmos-sdk/pull/8080) Updated the `codec.Marshaler` interface - * Moved `MarshalAny` and `UnmarshalAny` helper functions to `codec.Marshaler` and renamed to `MarshalInterface` and - `UnmarshalInterface` respectively. These functions must take interface as a parameter (not a concrete type nor `Any` - object). Underneath they use `Any` wrapping for correct protobuf serialization. -* (client) [\#8107](https://github.com/cosmos/cosmos-sdk/pull/8107) Renamed `PrintOutput` and `PrintOutputLegacy` - methods of the `context.Client` object to `PrintProto` and `PrintObjectLegacy`. -* (grpc/tmservice) [\#8060](https://github.com/cosmos/cosmos-sdk/pull/8060) TmService gRPC service's validator pubkey - type changed from bech32 format to `Any` -* (x/auth/tx) [\#8106](https://github.com/cosmos/cosmos-sdk/pull/8106) change related to missing append functionality in - client transaction signing - + added `overwriteSig` argument to `x/auth/client.SignTx` and `client/tx.Sign` functions. - + removed `x/auth/tx.go:wrapper.GetSignatures`. The `wrapper` provides `TxBuilder` functionality, and it's a private - structure. That function was not used at all and it's not exposed through the `TxBuilder` interface. - ### Bug Fixes -* (crypto) [\#7966](https://github.com/cosmos/cosmos-sdk/issues/7966) `Bip44Params` `String()` function now correctly - returns the absolute HD path by adding the `m/` prefix. -* (crypto/keys) [\#7838](https://github.com/cosmos/cosmos-sdk/pull/7838) Add support for TM secp256k1 keys back to the - SDK for consensus pubkeys -* (x/auth/client/cli) [\#8106](https://github.com/cosmos/cosmos-sdk/pull/8106) fixing regression bugs in transaction signing. +* (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 -## [v0.40.0-rc4](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0-rc4) - 2020-11-30 +## [v0.41.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.1) - 2021-02-17 ### Bug Fixes -* (store) [#8048](https://github.com/cosmos/cosmos-sdk/pull/8048) Fix issue where `SetInitialVersion` was never getting called, causing all queries to return empty on chains with non-zero initial height - -## [v0.40.0-rc3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0-rc3) - 2020-11-06 +* (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 -* (tendermint) [\#7828](https://github.com/cosmos/cosmos-sdk/pull/7828) Update tendermint dependency to v0.34.0-rc6 +* (x/ibc) [\#8458](https://github.com/cosmos/cosmos-sdk/pull/8458) Add `packet_connection` attribute to ibc events to enable relayer filtering +* [\#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.40.0-rc2](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0-rc2) - 2020-11-02 +## [v0.41.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.0) - 2021-01-26 -### Client Breaking - -* (x/upgrade) [#7697](https://github.com/cosmos/cosmos-sdk/pull/7697) Rename flag name "--time" to "--upgrade-time", "--info" to "--upgrade-info", to keep it consistent with help message. -* (x/auth) [#7788](https://github.com/cosmos/cosmos-sdk/pull/7788) Remove `tx auth` subcommands, all auth subcommands exist as `tx ` - -### API Breaking - -* (x/staking/types) [\#7447](https://github.com/cosmos/cosmos-sdk/issues/7447) Remove bech32 PubKey support: - * `ValidatorI` interface update. `GetConsPubKey` renamed to `TmConsPubKey` (consensus public key must be a tendermint key). `TmConsPubKey`, `GetConsAddr` methods return error. - * `Validator` update. Methods changed in `ValidatorI` (as described above) and `ToTmValidator` return error. - * `Validator.ConsensusPubkey` type changed from `string` to `codectypes.Any`. - * `MsgCreateValidator.Pubkey` type changed from `string` to `codectypes.Any`. -* Deprecating and renaming `MakeEncodingConfig` to `MakeTestEncodingConfig` (both in `simapp` and `simapp/params` packages). -* (tx) [\#7688](https://github.com/cosmos/cosmos-sdk/pull/7688) The gRPC simulate service method has been moved from `cosmos.base.v1beta1.simulate` to `cosmos.tx.v1beta1`, as a method in the Tx service. -* [#7764](https://github.com/cosmos/cosmos-sdk/pull/7764) Added module initialization options: - * `server/types.AppExporter` requires extra argument: `AppOptions`. - * `server.AddCommands` requires extra argument: `addStartFlags types.ModuleInitFlags` - * `x/crisis.NewAppModule` has a new attribute: `skipGenesisInvariants`. [PR](https://github.com/cosmos/cosmos-sdk/pull/7764) -* [#7918](https://github.com/cosmos/cosmos-sdk/pull/7918) Add x/capability safety checks: - * All outward facing APIs will now check that capability is not nil and name is not empty before performing any state-machine changes - * `SetIndex` has been renamed to `InitializeIndex` - - -### Features +### State Machine Breaking -* (tx) [\#7688](https://github.com/cosmos/cosmos-sdk/pull/7688) Add a new Tx gRPC service with methods `Simulate` and `GetTx` (by hash). -* __Modules__ - * `x/crisis` has a new function: `AddModuleInitFlags`, which will register optional crisis module flags for the start command. +* (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 -* (client) [\#7699](https://github.com/cosmos/cosmos-sdk/pull/7699) Fix panic in context when setting invalid nodeURI. `WithNodeURI` does not set the `Client` in the context. -* (x/gov) [#7641](https://github.com/cosmos/cosmos-sdk/pull/7641) Fix tally calculation precision error. - -### Improvements - -* (rest) [#7649](https://github.com/cosmos/cosmos-sdk/pull/7649) Return an unsigned tx in legacy GET /tx endpoint when signature conversion fails -* (cli) [#7764](https://github.com/cosmos/cosmos-sdk/pull/7764) Update x/banking and x/crisis InitChain to improve node startup time - -## [v0.40.0-rc1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0-rc1) - 2020-10-19 - -### Client Breaking Changes - -* __Modules__ - * (x/staking) [\#7499](https://github.com/cosmos/cosmos-sdk/pull/7499) `BondStatus` is now a protobuf `enum` instead - of an `int32`, and JSON serialized using its protobuf name, so expect names like `BOND_STATUS_UNBONDING` as opposed - to `Unbonding`. - * (x/staking) [\#7556](https://github.com/cosmos/cosmos-sdk/pull/7556) The ABCI's `Result.Data` field for - `MsgBeginRedelegate` and `MsgUndelegate` responses does not contain custom binary marshaled `completionTime`, but the - protobuf encoded `MsgBeginRedelegateResponse` and `MsgUndelegateResponse` structs respectively - * (x/evidence) [\#7538](https://github.com/cosmos/cosmos-sdk/pull/7538) The ABCI's `Result.Data` field for - `MsgSubmitEvidence` responses does not contain the raw evidence's hash, but the protobuf encoded - `MsgSubmitEvidenceResponse` struct. - * (x/gov) [\#7533](https://github.com/cosmos/cosmos-sdk/pull/7533) The ABCI's `Result.Data` field for - `MsgSubmitProposal` responses does not contain a raw binary encoding of the `proposalID`, but the protobuf encoded - `MsgSubmitSubmitProposalResponse` struct. - -### API Breaking +* (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 -* (AppModule) [\#7518](https://github.com/cosmos/cosmos-sdk/pull/7518) [\#7584](https://github.com/cosmos/cosmos-sdk/pull/7584) Rename `AppModule.RegisterQueryServices` to `AppModule.RegisterServices`, as this method now registers multiple services (the gRPC query service and the protobuf Msg service). A `Configurator` struct is used to hold the different services. +## [v0.40.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.1) - 2021-01-19 -### Features +### Improvements -* (modules) [\#7540](https://github.com/cosmos/cosmos-sdk/issues/7540) Protobuf service definitions can now be used for -packing `Msg`s in transactions as defined in [ADR 031](./docs/architecture/adr-031-msg-service.md). All modules now -define a `Msg` protobuf service. -* (codec) [\#7519](https://github.com/cosmos/cosmos-sdk/pull/7519) `InterfaceRegistry` now inherits `jsonpb.AnyResolver`, and has a `RegisterCustomTypeURL` method to support ADR 031 packing of `Any`s. `AnyResolver` is now a required parameter to `RejectUnknownFields`. -* (baseapp) [\#7519](https://github.com/cosmos/cosmos-sdk/pull/7519) Add `ServiceMsgRouter` to BaseApp to handle routing of protobuf service `Msg`s. The two new types defined in ADR 031, `sdk.ServiceMsg` and `sdk.MsgRequest` are introduced with this router. -* (cli) [\#7221](https://github.com/cosmos/cosmos-sdk/pull/7221) Add the option of emitting amino encoded json from the CLI +* (x/bank) [\#8302](https://github.com/cosmos/cosmos-sdk/issues/8302) Add gRPC and CLI queries for client denomination metadata. +* (tendermint) Bump Tendermint version to [v0.34.3](https://github.com/tendermint/tendermint/releases/tag/v0.34.3). ### Bug Fixes -* (kvstore) [\#7415](https://github.com/cosmos/cosmos-sdk/pull/7415) Allow new stores to be registered during on-chain upgrades. -* (rest) [\#7730](https://github.com/cosmos/cosmos-sdk/pull/7730) Fix fetch txs by height on legacy REST endpoint +* [\#8085](https://github.com/cosmos/cosmos-sdk/pull/8058) fix zero time checks +* [\#8280](https://github.com/cosmos/cosmos-sdk/pull/8280) fix GET /upgrade/current query +* (x/auth) [\#8287](https://github.com/cosmos/cosmos-sdk/pull/8287) Fix `tx sign --signature-only` to return correct sequence value in signature. +* (build) [\8300](https://github.com/cosmos/cosmos-sdk/pull/8300), [\8301](https://github.com/cosmos/cosmos-sdk/pull/8301) Fix reproducible builds +* (types/errors) [\#8355][https://github.com/cosmos/cosmos-sdk/pull/8355] Fix errorWrap `Is` method. +* (x/ibc) [\#8341](https://github.com/cosmos/cosmos-sdk/pull/8341) Fix query latest consensus state. +* (proto) [\#8350][https://github.com/cosmos/cosmos-sdk/pull/8350], [\#8361](https://github.com/cosmos/cosmos-sdk/pull/8361) Update gogo proto deps with v1.3.2 security fixes +* (x/ibc) [\#8359](https://github.com/cosmos/cosmos-sdk/pull/8359) Add missing UnpackInterfaces functions to IBC Query Responses. Fixes 'cannot unpack Any' error for IBC types. +* (x/bank) [\#8317](https://github.com/cosmos/cosmos-sdk/pull/8317) Fix panic when querying for a not found client denomination metadata. -### Improvements - -* (tendermint) [\#7527](https://github.com/cosmos/cosmos-sdk/pull/7527) Update sdk to tendermint 0.34-rc5 -* (iavl) [\#7549](https://github.com/cosmos/cosmos-sdk/pull/7549) Update sdk to IAVL 0.15.0-rc4 -## [v0.40.0-rc0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0-rc0) - 2020-10-13 +## [v0.40.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0) - 2021-01-08 v0.40.0, known as the Stargate release of the Cosmos SDK, is one of the largest releases -of the Cosmos SDK since launch. Please read through this changelog and [release notes](./RELEASE_NOTES.md) to make sure you are aware of any relevant breaking changes. +of the Cosmos SDK since launch. Please read through this changelog and [release notes](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/RELEASE_NOTES.md) to make +sure you are aware of any relevant breaking changes. ### Client Breaking Changes @@ -236,8 +126,10 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * (client/keys) [\#5889](https://github.com/cosmos/cosmos-sdk/pull/5889) remove `keys update` command. * (x/auth) [\#5844](https://github.com/cosmos/cosmos-sdk/pull/5844) `tx sign` command now returns an error when signing is attempted with offline/multisig keys. * (x/auth) [\#6108](https://github.com/cosmos/cosmos-sdk/pull/6108) `tx sign` command's `--validate-signatures` flag is migrated into a `tx validate-signatures` standalone command. + * (x/auth) [#7788](https://github.com/cosmos/cosmos-sdk/pull/7788) Remove `tx auth` subcommands, all auth subcommands exist as `tx ` * (x/genutil) [\#6651](https://github.com/cosmos/cosmos-sdk/pull/6651) The `gentx` command has been improved. No longer are `--from` and `--name` flags required. Instead, a single argument, `name`, is required which refers to the key pair in the Keyring. In addition, an optional `--moniker` flag can be provided to override the moniker found in `config.toml`. + * (x/upgrade) [#7697](https://github.com/cosmos/cosmos-sdk/pull/7697) Rename flag name "--time" to "--upgrade-time", "--info" to "--upgrade-info", to keep it consistent with help message. * __REST / Queriers__ * (api) [\#6426](https://github.com/cosmos/cosmos-sdk/pull/6426) The ability to start an out-of-process API REST server has now been removed. Instead, the API server is now started in-process along with the application and Tendermint. Configuration options have been added to `app.toml` to enable/disable the API server along with additional HTTP server options. * (client) [\#7246](https://github.com/cosmos/cosmos-sdk/pull/7246) The rest server endpoint `/swagger-ui/` is replaced by `/swagger/`, and contains swagger documentation for gRPC Gateway routes in addition to legacy REST routes. Swagger API is exposed only if set in `app.toml`. @@ -248,6 +140,9 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * __General__ * (baseapp) [\#6384](https://github.com/cosmos/cosmos-sdk/pull/6384) The `Result.Data` is now a Protocol Buffer encoded binary blob of type `TxData`. The `TxData` contains `Data` which contains a list of Protocol Buffer encoded message data and the corresponding message type. * (client) [\#5783](https://github.com/cosmos/cosmos-sdk/issues/5783) Unify all coins representations on JSON client requests for governance proposals. + * (crypto) [\#7419](https://github.com/cosmos/cosmos-sdk/pull/7419) The SDK doesn't use Tendermint's `crypto.PubKey` + interface anymore, and uses instead it's own `PubKey` interface, defined in `crypto/types`. Replace all instances of + `crypto.PubKey` by `cryptotypes.Pubkey`. * (store/rootmulti) [\#6390](https://github.com/cosmos/cosmos-sdk/pull/6390) Proofs of empty stores are no longer supported. * (store/types) [\#5730](https://github.com/cosmos/cosmos-sdk/pull/5730) store.types.Cp() is removed in favour of types.CopyBytes(). * (x/auth) [\#6054](https://github.com/cosmos/cosmos-sdk/pull/6054) Remove custom JSON marshaling for base accounts as multsigs cannot be bech32 decoded. @@ -255,14 +150,29 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * (x/bank) [\#5785](https://github.com/cosmos/cosmos-sdk/issues/5785) In x/bank errors, JSON strings coerced to valid UTF-8 bytes at JSON marshalling time are now replaced by human-readable expressions. This change can potentially break compatibility with all those client side tools that parse log messages. + * (x/evidence) [\#7538](https://github.com/cosmos/cosmos-sdk/pull/7538) The ABCI's `Result.Data` field for + `MsgSubmitEvidence` responses does not contain the raw evidence's hash, but the protobuf encoded + `MsgSubmitEvidenceResponse` struct. + * (x/gov) [\#7533](https://github.com/cosmos/cosmos-sdk/pull/7533) The ABCI's `Result.Data` field for + `MsgSubmitProposal` responses does not contain a raw binary encoding of the `proposalID`, but the protobuf encoded + `MsgSubmitSubmitProposalResponse` struct. * (x/gov) [\#6859](https://github.com/cosmos/cosmos-sdk/pull/6859) `ProposalStatus` and `VoteOption` are now JSON serialized using its protobuf name, so expect names like `PROPOSAL_STATUS_DEPOSIT_PERIOD` as opposed to `DepositPeriod`. + * (x/staking) [\#7499](https://github.com/cosmos/cosmos-sdk/pull/7499) `BondStatus` is now a protobuf `enum` instead + of an `int32`, and JSON serialized using its protobuf name, so expect names like `BOND_STATUS_UNBONDING` as opposed + to `Unbonding`. + * (x/staking) [\#7556](https://github.com/cosmos/cosmos-sdk/pull/7556) The ABCI's `Result.Data` field for + `MsgBeginRedelegate` and `MsgUndelegate` responses does not contain custom binary marshaled `completionTime`, but the + protobuf encoded `MsgBeginRedelegateResponse` and `MsgUndelegateResponse` structs respectively ### API Breaking Changes * __Baseapp / Client__ + * (AppModule) [\#7518](https://github.com/cosmos/cosmos-sdk/pull/7518) [\#7584](https://github.com/cosmos/cosmos-sdk/pull/7584) Rename `AppModule.RegisterQueryServices` to `AppModule.RegisterServices`, as this method now registers multiple services (the gRPC query service and the protobuf Msg service). A `Configurator` struct is used to hold the different services. * (baseapp) [\#5865](https://github.com/cosmos/cosmos-sdk/pull/5865) The `SimulationResponse` returned from tx simulation is now JSON encoded instead of Amino binary. * (client) [\#6290](https://github.com/cosmos/cosmos-sdk/pull/6290) `CLIContext` is renamed to `Context`. `Context` and all related methods have been moved from package context to client. * (client) [\#6525](https://github.com/cosmos/cosmos-sdk/pull/6525) Removed support for `indent` in JSON responses. Clients should consider piping to an external tool such as `jq`. + * (client) [\#8107](https://github.com/cosmos/cosmos-sdk/pull/8107) Renamed `PrintOutput` and `PrintOutputLegacy` + methods of the `context.Client` object to `PrintProto` and `PrintObjectLegacy`. * (client/flags) [\#6632](https://github.com/cosmos/cosmos-sdk/pull/6632) Remove NewCompletionCmd(), the function is now available in tendermint. * (client/input) [\#5904](https://github.com/cosmos/cosmos-sdk/pull/5904) Removal of unnecessary `GetCheckPassword`, `PrintPrefixed` functions. * (client/keys) [\#5889](https://github.com/cosmos/cosmos-sdk/pull/5889) Rename `NewKeyBaseFromDir()` -> `NewLegacyKeyBaseFromDir()`. @@ -270,6 +180,10 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * (client/rpc) [\#6290](https://github.com/cosmos/cosmos-sdk/pull/6290) `client` package and subdirs reorganization. * (client/lcd) [\#6290](https://github.com/cosmos/cosmos-sdk/pull/6290) `CliCtx` of struct `RestServer` in package client/lcd has been renamed to `ClientCtx`. * (codec) [\#6330](https://github.com/cosmos/cosmos-sdk/pull/6330) `codec.RegisterCrypto` has been moved to the `crypto/codec` package and the global `codec.Cdc` Amino instance has been deprecated and moved to the `codec/legacy_global` package. + * (codec) [\#8080](https://github.com/cosmos/cosmos-sdk/pull/8080) Updated the `codec.Marshaler` interface + * Moved `MarshalAny` and `UnmarshalAny` helper functions to `codec.Marshaler` and renamed to `MarshalInterface` and + `UnmarshalInterface` respectively. These functions must take interface as a parameter (not a concrete type nor `Any` + object). Underneath they use `Any` wrapping for correct protobuf serialization. * (crypto) [\#6780](https://github.com/cosmos/cosmos-sdk/issues/6780) Move ledger code to its own package. * (crypto/types/multisig) [\#6373](https://github.com/cosmos/cosmos-sdk/pull/6373) `multisig.Multisignature` has been renamed to `AminoMultisignature` * (codec) `*codec.LegacyAmino` is now a wrapper around Amino which provides backwards compatibility with protobuf `Any`. ALL legacy code should use `*codec.LegacyAmino` instead of `*amino.Codec` directly @@ -284,6 +198,7 @@ of the Cosmos SDK since launch. Please read through this changelog and [release information on how to implement the new `Keyring` interface. * [\#5858](https://github.com/cosmos/cosmos-sdk/pull/5858) Make Keyring store keys by name and address's hexbytes representation. * (export) [\#5952](https://github.com/cosmos/cosmos-sdk/pull/5952) `AppExporter` now returns ABCI consensus parameters to be included in marshaled exported state. These parameters must be returned from the application via the `BaseApp`. + * (simapp) Deprecating and renaming `MakeEncodingConfig` to `MakeTestEncodingConfig` (both in `simapp` and `simapp/params` packages). * (store) [\#5803](https://github.com/cosmos/cosmos-sdk/pull/5803) The `store.CommitMultiStore` interface now includes the new `snapshots.Snapshotter` interface as well. * (types) [\#5579](https://github.com/cosmos/cosmos-sdk/pull/5579) The `keepRecent` field has been removed from the `PruningOptions` type. The `PruningOptions` type now only includes fields `KeepEvery` and `SnapshotEvery`, where `KeepEvery` @@ -308,16 +223,46 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * (modules) [\#6447](https://github.com/cosmos/cosmos-sdk/issues/6447) Rename `blacklistedAddrs` to `blockedAddrs`. * (modules) [\#6834](https://github.com/cosmos/cosmos-sdk/issues/6834) Add `RegisterInterfaces` method to `AppModuleBasic` to support registration of protobuf interface types. * (modules) [\#6734](https://github.com/cosmos/cosmos-sdk/issues/6834) Add `TxEncodingConfig` parameter to `AppModuleBasic.ValidateGenesis` command to support JSON tx decoding in `genutil`. + * (modules) [#7764](https://github.com/cosmos/cosmos-sdk/pull/7764) Added module initialization options: + * `server/types.AppExporter` requires extra argument: `AppOptions`. + * `server.AddCommands` requires extra argument: `addStartFlags types.ModuleInitFlags` + * `x/crisis.NewAppModule` has a new attribute: `skipGenesisInvariants`. [PR](https://github.com/cosmos/cosmos-sdk/pull/7764) * (types) [\#6327](https://github.com/cosmos/cosmos-sdk/pull/6327) `sdk.Msg` now inherits `proto.Message`, as a result all `sdk.Msg` types now use pointer semantics. * (types) [\#7032](https://github.com/cosmos/cosmos-sdk/pull/7032) All types ending with `ID` (e.g. `ProposalID`) now end with `Id` (e.g. `ProposalId`), to match default Protobuf generated format. Also see [\#7033](https://github.com/cosmos/cosmos-sdk/pull/7033) for more details. * (x/auth) [\#6029](https://github.com/cosmos/cosmos-sdk/pull/6029) Module accounts have been moved from `x/supply` to `x/auth`. * (x/auth) [\#6443](https://github.com/cosmos/cosmos-sdk/issues/6443) Move `FeeTx` and `TxWithMemo` interfaces from `x/auth/ante` to `types`. + * (x/auth) [\#7006](https://github.com/cosmos/cosmos-sdk/pull/7006) All `AccountRetriever` methods now take `client.Context` as a parameter instead of as a struct member. + * (x/auth) [\#6270](https://github.com/cosmos/cosmos-sdk/pull/6270) The passphrase argument has been removed from the signature of the following functions and methods: `BuildAndSign`, ` MakeSignature`, ` SignStdTx`, `TxBuilder.BuildAndSign`, `TxBuilder.Sign`, `TxBuilder.SignStdTx` + * (x/auth) [\#6428](https://github.com/cosmos/cosmos-sdk/issues/6428): + * `NewAnteHandler` and `NewSigVerificationDecorator` both now take a `SignModeHandler` parameter. + * `SignatureVerificationGasConsumer` now has the signature: `func(meter sdk.GasMeter, sig signing.SignatureV2, params types.Params) error`. + * The `SigVerifiableTx` interface now has a `GetSignaturesV2() ([]signing.SignatureV2, error)` method and no longer has the `GetSignBytes` method. + * (x/auth/tx) [\#8106](https://github.com/cosmos/cosmos-sdk/pull/8106) change related to missing append functionality in + client transaction signing + + added `overwriteSig` argument to `x/auth/client.SignTx` and `client/tx.Sign` functions. + + removed `x/auth/tx.go:wrapper.GetSignatures`. The `wrapper` provides `TxBuilder` functionality, and it's a private + structure. That function was not used at all and it's not exposed through the `TxBuilder` interface. * (x/bank) [\#7327](https://github.com/cosmos/cosmos-sdk/pull/7327) AddCoins and SubtractCoins no longer return a resultingValue and will only return an error. + * (x/capability) [#7918](https://github.com/cosmos/cosmos-sdk/pull/7918) Add x/capability safety checks: + * All outward facing APIs will now check that capability is not nil and name is not empty before performing any state-machine changes + * `SetIndex` has been renamed to `InitializeIndex` * (x/evidence) [\#7251](https://github.com/cosmos/cosmos-sdk/pull/7251) New evidence types and light client evidence handling. The module function names changed. + * (x/evidence) [\#5952](https://github.com/cosmos/cosmos-sdk/pull/5952) Remove APIs for getting and setting `x/evidence` parameters. `BaseApp` now uses a `ParamStore` to manage Tendermint consensus parameters which is managed via the `x/params` `Substore` type. + * (x/gov) [\#6147](https://github.com/cosmos/cosmos-sdk/pull/6147) The `Content` field on `Proposal` and `MsgSubmitProposal` + is now `Any` in concordance with [ADR 019](docs/architecture/adr-019-protobuf-state-encoding.md) and `GetContent` should now + be used to retrieve the actual proposal `Content`. Also the `NewMsgSubmitProposal` constructor now may return an `error` * (x/ibc) [\#6374](https://github.com/cosmos/cosmos-sdk/pull/6374) `VerifyMembership` and `VerifyNonMembership` now take a `specs []string` argument to specify the proof format used for verification. Most SDK chains can simply use `commitmenttypes.GetSDKSpecs()` for this argument. * (x/params) [\#5619](https://github.com/cosmos/cosmos-sdk/pull/5619) The `x/params` keeper now accepts a `codec.Marshaller` instead of a reference to an amino codec. Amino is still used for JSON serialization. * (x/staking) [\#6451](https://github.com/cosmos/cosmos-sdk/pull/6451) `DefaultParamspace` and `ParamKeyTable` in staking module are moved from keeper to types to enforce consistency. + * (x/staking) [\#7419](https://github.com/cosmos/cosmos-sdk/pull/7419) The `TmConsPubKey` method on ValidatorI has been + removed and replaced instead by `ConsPubKey` (which returns a SDK `cryptotypes.PubKey`) and `TmConsPublicKey` (which + returns a Tendermint proto PublicKey). + * (x/staking/types) [\#7447](https://github.com/cosmos/cosmos-sdk/issues/7447) Remove bech32 PubKey support: + * `ValidatorI` interface update. `GetConsPubKey` renamed to `TmConsPubKey` (consensus public key must be a tendermint key). `TmConsPubKey`, `GetConsAddr` methods return error. + * `Validator` update. Methods changed in `ValidatorI` (as described above) and `ToTmValidator` return error. + * `Validator.ConsensusPubkey` type changed from `string` to `codectypes.Any`. + * `MsgCreateValidator.Pubkey` type changed from `string` to `codectypes.Any`. * (x/supply) [\#6010](https://github.com/cosmos/cosmos-sdk/pull/6010) All `x/supply` types and APIs have been moved to `x/bank`. * [\#6409](https://github.com/cosmos/cosmos-sdk/pull/6409) Rename all IsEmpty methods to Empty across the codebase and enforce consistency. * [\#6231](https://github.com/cosmos/cosmos-sdk/pull/6231) Simplify `AppModule` interface, `Route` and `NewHandler` methods become only `Route` @@ -325,16 +270,7 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * (x/slashing) [\#6212](https://github.com/cosmos/cosmos-sdk/pull/6212) Remove `Get*` prefixes from key construction functions * (server) [\#6079](https://github.com/cosmos/cosmos-sdk/pull/6079) Remove `UpgradeOldPrivValFile` (deprecated in Tendermint Core v0.28). * [\#5719](https://github.com/cosmos/cosmos-sdk/pull/5719) Bump Go requirement to 1.14+ - * (x/evidence) [\#5952](https://github.com/cosmos/cosmos-sdk/pull/5952) Remove APIs for getting and setting `x/evidence` parameters. `BaseApp` now uses a `ParamStore` to manage Tendermint consensus parameters which is managed via the `x/params` `Substore` type. - * (x/gov) [\#6147](https://github.com/cosmos/cosmos-sdk/pull/6147) The `Content` field on `Proposal` and `MsgSubmitProposal` - is now `Any` in concordance with [ADR 019](docs/architecture/adr-019-protobuf-state-encoding.md) and `GetContent` should now - be used to retrieve the actual proposal `Content`. Also the `NewMsgSubmitProposal` constructor now may return an `error` - * (x/auth) [\#7006](https://github.com/cosmos/cosmos-sdk/pull/7006) All `AccountRetriever` methods now take `client.Context` as a parameter instead of as a struct member. - * (x/auth) [\#6270](https://github.com/cosmos/cosmos-sdk/pull/6270) The passphrase argument has been removed from the signature of the following functions and methods: `BuildAndSign`, ` MakeSignature`, ` SignStdTx`, `TxBuilder.BuildAndSign`, `TxBuilder.Sign`, `TxBuilder.SignStdTx` - * (x/auth) [\#6428](https://github.com/cosmos/cosmos-sdk/issues/6428): - * `NewAnteHandler` and `NewSigVerificationDecorator` both now take a `SignModeHandler` parameter. - * `SignatureVerificationGasConsumer` now has the signature: `func(meter sdk.GasMeter, sig signing.SignatureV2, params types.Params) error`. - * The `SigVerifiableTx` interface now has a `GetSignaturesV2() ([]signing.SignatureV2, error)` method and no longer has the `GetSignBytes` method. + ### State Machine Breaking @@ -403,6 +339,8 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * Every reference of `crypto.Pubkey` in context of a `Validator` is now of type string. `GetPubKeyFromBech32` must be used to get the `crypto.Pubkey`. * The `Keeper` constructor now takes a `codec.Marshaler` instead of a concrete Amino codec. This exact type provided is specified by `ModuleCdc`. + * (x/staking) [\#7979](https://github.com/cosmos/cosmos-sdk/pull/7979) keeper pubkey storage serialization migration + from bech32 to protobuf. * (x/supply) [\#6010](https://github.com/cosmos/cosmos-sdk/pull/6010) Removed the `x/supply` module by merging the existing types and APIs into the `x/bank` module. * (x/supply) [\#5533](https://github.com/cosmos/cosmos-sdk/pull/5533) Migrate the `x/supply` module to use Protocol Buffers for state serialization instead of Amino. @@ -422,8 +360,11 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * (x/auth) [\#6213](https://github.com/cosmos/cosmos-sdk/issues/6213) Introduce new protobuf based path for transaction signing, see [ADR020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md) for more details * (x/auth) [\#6350](https://github.com/cosmos/cosmos-sdk/pull/6350) New sign-batch command to sign StdTx batch files. * (baseapp) [\#5803](https://github.com/cosmos/cosmos-sdk/pull/5803) Added support for taking state snapshots at regular height intervals, via options `snapshot-interval` and `snapshot-keep-recent`. + * (baseapp) [\#7519](https://github.com/cosmos/cosmos-sdk/pull/7519) Add `ServiceMsgRouter` to BaseApp to handle routing of protobuf service `Msg`s. The two new types defined in ADR 031, `sdk.ServiceMsg` and `sdk.MsgRequest` are introduced with this router. * (client) [\#5921](https://github.com/cosmos/cosmos-sdk/issues/5921) Introduce new gRPC and gRPC Gateway based APIs for querying app & module data. See [ADR021](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-021-protobuf-query-encoding.md) for more details * (cli) [\#7485](https://github.com/cosmos/cosmos-sdk/pull/7485) Introduce a new optional `--keyring-dir` flag that allows clients to specify a Keyring directory if it does not reside in the directory specified by `--home`. + * (cli) [\#7221](https://github.com/cosmos/cosmos-sdk/pull/7221) Add the option of emitting amino encoded json from the CLI + * (codec) [\#7519](https://github.com/cosmos/cosmos-sdk/pull/7519) `InterfaceRegistry` now inherits `jsonpb.AnyResolver`, and has a `RegisterCustomTypeURL` method to support ADR 031 packing of `Any`s. `AnyResolver` is now a required parameter to `RejectUnknownFields`. * (coin) [\#6755](https://github.com/cosmos/cosmos-sdk/pull/6755) Add custom regex validation for `Coin` denom by overwriting `CoinDenomRegex` when using `/types/coin.go`. * (config) [\#7265](https://github.com/cosmos/cosmos-sdk/pull/7265) Support Tendermint block pruning through a new `min-retain-blocks` configuration that can be set in either `app.toml` or via the CLI. This parameter is used in conjunction with other criteria to determine the height at which Tendermint should prune blocks. * (events) [\#7121](https://github.com/cosmos/cosmos-sdk/pull/7121) The application now derives what events are indexed by Tendermint via the `index-events` configuration in `app.toml`, which is a list of events taking the form `{eventType}.{attributeKey}`. @@ -432,7 +373,13 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * (genesis) [\#7089](https://github.com/cosmos/cosmos-sdk/pull/7089) The `export` command now adds a `initial_height` field in the exported JSON. Baseapp's `CommitMultiStore` now also has a `SetInitialVersion` setter, so it can set the initial store version inside `InitChain` and start a new chain from a given height. * __General__ * (crypto/multisig) [\#6241](https://github.com/cosmos/cosmos-sdk/pull/6241) Add Multisig type directly to the repo. Previously this was in tendermint. + * (codec/types) [\#8106](https://github.com/cosmos/cosmos-sdk/pull/8106) Adding `NewAnyWithCustomTypeURL` to correctly + marshal Messages in TxBuilder. * (tests) [\#6489](https://github.com/cosmos/cosmos-sdk/pull/6489) Introduce package `testutil`, new in-process testing network framework for use in integration and unit tests. + * (tx) Add new auth/tx gRPC & gRPC-Gateway endpoints for basic querying & broadcasting support + * [\#7842](https://github.com/cosmos/cosmos-sdk/pull/7842) Add TxsByEvent gRPC endpoint + * [\#7852](https://github.com/cosmos/cosmos-sdk/pull/7852) Add tx broadcast gRPC endpoint + * (tx) [\#7688](https://github.com/cosmos/cosmos-sdk/pull/7688) Add a new Tx gRPC service with methods `Simulate` and `GetTx` (by hash). * (store) [\#5803](https://github.com/cosmos/cosmos-sdk/pull/5803) Added `rootmulti.Store` methods for taking and restoring snapshots, based on `iavl.Store` export/import. * (store) [\#6324](https://github.com/cosmos/cosmos-sdk/pull/6324) IAVL store query proofs now return CommitmentOp which wraps an ics23 CommitmentProof * (store) [\#6390](https://github.com/cosmos/cosmos-sdk/pull/6390) `RootMulti` store query proofs now return `CommitmentOp` which wraps `CommitmentProofs` @@ -440,8 +387,12 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * `ProofRuntime` only decodes and verifies `ics23.CommitmentProof` * __Modules__ * (modules) [\#5921](https://github.com/cosmos/cosmos-sdk/issues/5921) Introduction of Query gRPC service definitions along with REST annotations for gRPC Gateway for each module + * (modules) [\#7540](https://github.com/cosmos/cosmos-sdk/issues/7540) Protobuf service definitions can now be used for + packing `Msg`s in transactions as defined in [ADR 031](./docs/architecture/adr-031-msg-service.md). All modules now + define a `Msg` protobuf service. * (x/auth/vesting) [\#7209](https://github.com/cosmos/cosmos-sdk/pull/7209) Create new `MsgCreateVestingAccount` message type along with CLI handler that allows for the creation of delayed and continuous vesting types. * (x/capability) [\#5828](https://github.com/cosmos/cosmos-sdk/pull/5828) Capability module integration as outlined in [ADR 3 - Dynamic Capability Store](https://github.com/cosmos/tree/master/docs/architecture/adr-003-dynamic-capability-store.md). + * (x/crisis) `x/crisis` has a new function: `AddModuleInitFlags`, which will register optional crisis module flags for the start command. * (x/ibc) [\#5277](https://github.com/cosmos/cosmos-sdk/pull/5277) `x/ibc` changes from IBC alpha. For more details check the the [`x/ibc/core/spec`](https://github.com/cosmos/cosmos-sdk/tree/master/x/ibc/core/spec) directory, or the ICS specs below: * [ICS 002 - Client Semantics](https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics) subpackage * [ICS 003 - Connection Semantics](https://github.com/cosmos/ics/blob/master/spec/ics-003-connection-semantics) subpackage @@ -461,10 +412,14 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * __Baseapp / Client / REST__ * (client) [\#5964](https://github.com/cosmos/cosmos-sdk/issues/5964) `--trust-node` is now false by default - for real. Users must ensure it is set to true if they don't want to enable the verifier. * (client) [\#6402](https://github.com/cosmos/cosmos-sdk/issues/6402) Fix `keys add` `--algo` flag which only worked for Tendermint's `secp256k1` default key signing algorithm. + * (client) [\#7699](https://github.com/cosmos/cosmos-sdk/pull/7699) Fix panic in context when setting invalid nodeURI. `WithNodeURI` does not set the `Client` in the context. * (export) [\#6510](https://github.com/cosmos/cosmos-sdk/pull/6510/) Field TimeIotaMs now is included in genesis file while exporting. * (rest) [\#5906](https://github.com/cosmos/cosmos-sdk/pull/5906) Fix an issue that make some REST calls panic when sending invalid or incomplete requests. + * (crypto) [\#7966](https://github.com/cosmos/cosmos-sdk/issues/7966) `Bip44Params` `String()` function now correctly + returns the absolute HD path by adding the `m/` prefix. * (crypto/keyring) [\#5844](https://github.com/cosmos/cosmos-sdk/pull/5844) `Keyring.Sign()` methods no longer decode amino signatures when method receivers are offline/multisig keys. + * (store) [\#7415](https://github.com/cosmos/cosmos-sdk/pull/7415) Allow new stores to be registered during on-chain upgrades. * __Modules__ * (modules) [\#5569](https://github.com/cosmos/cosmos-sdk/issues/5569) `InitGenesis`, for the relevant modules, now ensures module accounts exist. * (x/auth) [\#5892](https://github.com/cosmos/cosmos-sdk/pull/5892) Add `RegisterKeyTypeCodec` to register new @@ -472,6 +427,8 @@ of the Cosmos SDK since launch. Please read through this changelog and [release * (x/bank) [\#6536](https://github.com/cosmos/cosmos-sdk/pull/6536) Fix bug in `WriteGeneratedTxResponse` function used by multiple REST endpoints. Now it writes a Tx in StdTx format. * (x/genutil) [\#5938](https://github.com/cosmos/cosmos-sdk/pull/5938) Fix `InitializeNodeValidatorFiles` error handling. + * (x/gentx) [\#8183](https://github.com/cosmos/cosmos-sdk/pull/8183) change gentx cmd amount to arg from flag + * (x/gov) [#7641](https://github.com/cosmos/cosmos-sdk/pull/7641) Fix tally calculation precision error. * (x/staking) [\#6529](https://github.com/cosmos/cosmos-sdk/pull/6529) Export validator addresses (previously was empty). * (x/staking) [\#5949](https://github.com/cosmos/cosmos-sdk/pull/5949) Skip staking `HistoricalInfoKey` in simulations as headers are not exported. * (x/staking) [\#6061](https://github.com/cosmos/cosmos-sdk/pull/6061) Allow a validator to immediately unjail when no signing info is present due to @@ -490,8 +447,13 @@ falling below their minimum self-delegation and never having been bonded. The va internet connection. Previously, `--generate-only` served this purpose in addition to only allowing txs to be generated. Now, `--generate-only` solely allows txs to be generated without being broadcasted and disallows Keybase use and `--offline` allows the use of Keybase but does not allow any functionality that requires an online connection. + * (cli) [#7764](https://github.com/cosmos/cosmos-sdk/pull/7764) Update x/banking and x/crisis InitChain to improve node startup time * (client) [\#5856](https://github.com/cosmos/cosmos-sdk/pull/5856) Added the possibility to set `--offline` flag with config command. * (client) [\#5895](https://github.com/cosmos/cosmos-sdk/issues/5895) show config options in the config command's help screen. + * (client/keys) [\#8043](https://github.com/cosmos/cosmos-sdk/pull/8043) Add support for export of unarmored private key + * (client/tx) [\#7801](https://github.com/cosmos/cosmos-sdk/pull/7801) Update sign-batch multisig to work online + * (x/genutil) [\#8099](https://github.com/cosmos/cosmos-sdk/pull/8099) `init` now supports a `--recover` flag to recover + the private validator key from a given mnemonic * __Modules__ * (x/auth) [\#5702](https://github.com/cosmos/cosmos-sdk/pull/5702) Add parameter querying support for `x/auth`. * (x/auth/ante) [\#6040](https://github.com/cosmos/cosmos-sdk/pull/6040) `AccountKeeper` interface used for `NewAnteHandler` and handler's decorators to add support of using custom `AccountKeeper` implementations. @@ -503,11 +465,23 @@ falling below their minimum self-delegation and never having been bonded. The va * (x/staking) [\#5584](https://github.com/cosmos/cosmos-sdk/pull/5584) Add util function `ToTmValidator` that converts a `staking.Validator` type to `*tmtypes.Validator`. * (x/staking) [\#6163](https://github.com/cosmos/cosmos-sdk/pull/6163) CLI and REST call to unbonding delegations and delegations now accept pagination. + * (x/staking) [\#8178](https://github.com/cosmos/cosmos-sdk/pull/8178) Update default historical header number for stargate * __General__ - * (tendermint) [\#6365](https://github.com/cosmos/cosmos-sdk/issues/6365) Update tendermint version to v0.34, and make necessary upgrades to the SDK + * (crypto) [\#7987](https://github.com/cosmos/cosmos-sdk/pull/7987) Fix the inconsistency of CryptoCdc, only use + `codec/legacy.Cdc`. + * (logging) [\#8072](https://github.com/cosmos/cosmos-sdk/pull/8072) Refactor logging: + * Use [zerolog](https://github.com/rs/zerolog) over Tendermint's go-kit logging wrapper. + * Introduce Tendermint's `--log_format=plain|json` flag. Using format `json` allows for emitting structured JSON + logs which can be consumed by an external logging facility (e.g. Loggly). Both formats log to STDERR. + * The existing `--log_level` flag and it's default value now solely relates to the global logging + level (e.g. `info`, `debug`, etc...) instead of `:`. + * (rest) [#7649](https://github.com/cosmos/cosmos-sdk/pull/7649) Return an unsigned tx in legacy GET /tx endpoint when signature conversion fails * (simulation) [\#6002](https://github.com/cosmos/cosmos-sdk/pull/6002) Add randomized consensus params into simulation. * (store) [\#6481](https://github.com/cosmos/cosmos-sdk/pull/6481) Move `SimpleProofsFromMap` from Tendermint into the SDK. * (store) [\#6719](https://github.com/cosmos/cosmos-sdk/6754) Add validity checks to stores for nil and empty keys. + * (SDK) Updated dependencies + * Updated iavl dependency to v0.15.3 + * Update tendermint to v0.34.1 * (types) [\#7027](https://github.com/cosmos/cosmos-sdk/pull/7027) `Coin(s)` and `DecCoin(s)` updates: * Bump denomination max length to 128 * Allow uppercase letters and numbers in denominations to support [ADR 001](./docs/architecture/adr-001-coin-source-tracing.md) @@ -521,6 +495,8 @@ falling below their minimum self-delegation and never having been bonded. The va * (types) [\#6128](https://github.com/cosmos/cosmos-sdk/pull/6137) Add `String()` method to `GasMeter`. * (types) [\#6195](https://github.com/cosmos/cosmos-sdk/pull/6195) Add codespace to broadcast(sync/async) response. * (types) \#6897 Add KV type from tendermint to `types` directory. + * (version) [\#7848](https://github.com/cosmos/cosmos-sdk/pull/7848) [\#7941](https://github.com/cosmos/cosmos-sdk/pull/7941) + `version --long` output now shows the list of build dependencies and replaced build dependencies. ## [v0.39.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.39.1) - 2020-08-11 diff --git a/Makefile b/Makefile index d72d69f20d2d..f49f95e73449 100644 --- a/Makefile +++ b/Makefile @@ -201,14 +201,7 @@ build-docs: cp -r .vuepress/dist/* ~/output/$${path_prefix}/ ; \ cp ~/output/$${path_prefix}/index.html ~/output ; \ done < versions ; - -sync-docs: - cd ~/output && \ - echo "role_arn = ${DEPLOYMENT_ROLE_ARN}" >> /root/.aws/config ; \ - echo "CI job = ${CIRCLE_BUILD_URL}" >> version.html ; \ - aws s3 sync . s3://${WEBSITE_BUCKET} --profile terraform --delete ; \ - aws cloudfront create-invalidation --distribution-id ${CF_DISTRIBUTION_ID} --profile terraform --path "/*" ; -.PHONY: sync-docs +.PHONY: build-docs ############################################################################### ### Tests & Simulation ### @@ -316,6 +309,11 @@ test-cover: @export VERSION=$(VERSION); bash -x contrib/test_cover.sh .PHONY: test-cover +test-rosetta: + docker build -t rosetta-ci:latest -f contrib/rosetta/node/Dockerfile . + docker-compose -f contrib/rosetta/docker-compose.yaml up --abort-on-container-exit --exit-code-from test_rosetta --build +.PHONY: test-rosetta + benchmark: @go test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION) .PHONY: benchmark @@ -470,3 +468,15 @@ localnet-stop: docker-compose down .PHONY: localnet-start localnet-stop + +############################################################################### +### rosetta ### +############################################################################### +# builds rosetta test data dir +rosetta-data: + -docker container rm data_dir_build + docker build -t rosetta-ci:latest -f contrib/rosetta/node/Dockerfile . + docker run --name data_dir_build -t rosetta-ci:latest sh /rosetta/data.sh + docker cp data_dir_build:/tmp/data.tar.gz "$(CURDIR)/contrib/rosetta/node/data.tar.gz" + docker container rm data_dir_build +.PHONY: rosetta-data diff --git a/baseapp/grpcrouter.go b/baseapp/grpcrouter.go index de553589c975..95186f0b16da 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 a4342e0b8955..c1db08a555aa 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. - // 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 + // 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) + } + + // 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 } - // 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) + // 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 } - // Attach the sdk.Context into the gRPC's context.Context. - grpcCtx = context.WithValue(grpcCtx, sdk.SdkContextKey, sdkCtx) + // 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() - // Add relevant gRPC headers - if height == 0 { - height = sdkCtx.BlockHeight() // If height was not set in the request, set it to the latest + 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 } - md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(height, 10)) - grpc.SetHeader(grpcCtx, md) - return handler(grpcCtx, req) + return res, nil } // Loop through all services and methods, add the interceptor, and register diff --git a/buf.yaml b/buf.yaml index 33388763d7a3..37f716cabaa2 100644 --- a/buf.yaml +++ b/buf.yaml @@ -1,3 +1,5 @@ +version: v1beta1 + build: roots: - proto diff --git a/client/cmd.go b/client/cmd.go index eccf59c3515c..437022695d47 100644 --- a/client/cmd.go +++ b/client/cmd.go @@ -221,6 +221,19 @@ func readTxCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, err clientCtx = clientCtx.WithSignModeStr(signModeStr) } + if clientCtx.FeeGranter == nil || flagSet.Changed(flags.FlagFeeAccount) { + granter, _ := flagSet.GetString(flags.FlagFeeAccount) + + if granter != "" { + granterAcc, err := sdk.AccAddressFromBech32(granter) + if err != nil { + return clientCtx, err + } + + clientCtx = clientCtx.WithFeeGranterAddress(granterAcc) + } + } + if clientCtx.From == "" || flagSet.Changed(flags.FlagFrom) { from, _ := flagSet.GetString(flags.FlagFrom) fromAddr, fromName, keyType, err := GetFromFields(clientCtx.Keyring, from, clientCtx.GenerateOnly) diff --git a/client/cmd_test.go b/client/cmd_test.go index 02e2c414f63a..65161fe4752b 100644 --- a/client/cmd_test.go +++ b/client/cmd_test.go @@ -97,8 +97,7 @@ func TestSetCmdClientContextHandler(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { - ctx := context.Background() - ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{}) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &client.Context{}) cmd := newCmd() _ = testutil.ApplyMockIODiscardOutErr(cmd) diff --git a/client/context.go b/client/context.go index 0e3d1181af6e..f7f555339c2c 100644 --- a/client/context.go +++ b/client/context.go @@ -44,6 +44,7 @@ type Context struct { TxConfig TxConfig AccountRetriever AccountRetriever NodeURI string + FeeGranter sdk.AccAddress // TODO: Deprecated (remove). LegacyAmino *codec.LegacyAmino @@ -166,6 +167,13 @@ func (ctx Context) WithFromAddress(addr sdk.AccAddress) Context { return ctx } +// WithFeeGranterAddress returns a copy of the context with an updated fee granter account +// address. +func (ctx Context) WithFeeGranterAddress(addr sdk.AccAddress) Context { + ctx.FeeGranter = addr + return ctx +} + // WithBroadcastMode returns a copy of the context with an updated broadcast // mode. func (ctx Context) WithBroadcastMode(mode string) Context { diff --git a/client/flags/flags.go b/client/flags/flags.go index 282a61c4c9fb..1afb55b46733 100644 --- a/client/flags/flags.go +++ b/client/flags/flags.go @@ -70,6 +70,7 @@ const ( FlagCountTotal = "count-total" FlagTimeoutHeight = "timeout-height" FlagKeyAlgorithm = "algo" + FlagFeeAccount = "fee-account" // Tendermint logging flags FlagLogLevel = "log_level" @@ -112,6 +113,7 @@ func AddTxFlagsToCmd(cmd *cobra.Command) { cmd.Flags().String(FlagKeyringBackend, DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") cmd.Flags().String(FlagSignMode, "", "Choose sign mode (direct|amino-json), this is an advanced feature") cmd.Flags().Uint64(FlagTimeoutHeight, 0, "Set a block timeout height to prevent the tx from being committed past a certain height") + cmd.Flags().String(FlagFeeAccount, "", "Fee account pays fees for the transaction instead of deducting from the signer") // --gas can accept integers and "auto" cmd.Flags().String(FlagGas, "", fmt.Sprintf("gas limit to set per-transaction; set to %q to calculate sufficient gas automatically (default %d)", GasFlagAuto, DefaultGasLimit)) diff --git a/client/grpc/reflection/reflection.pb.go b/client/grpc/reflection/reflection.pb.go index 99878b2a342f..66dbef0c7105 100644 --- a/client/grpc/reflection/reflection.pb.go +++ b/client/grpc/reflection/reflection.pb.go @@ -589,10 +589,7 @@ func (m *ListAllInterfacesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthReflection - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthReflection } if (iNdEx + skippy) > l { @@ -674,10 +671,7 @@ func (m *ListAllInterfacesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthReflection - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthReflection } if (iNdEx + skippy) > l { @@ -759,10 +753,7 @@ func (m *ListImplementationsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthReflection - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthReflection } if (iNdEx + skippy) > l { @@ -844,10 +835,7 @@ func (m *ListImplementationsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthReflection - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthReflection } if (iNdEx + skippy) > l { diff --git a/client/grpc/tmservice/block.go b/client/grpc/tmservice/block.go index e92fbec72bd2..554156982dda 100644 --- a/client/grpc/tmservice/block.go +++ b/client/grpc/tmservice/block.go @@ -8,12 +8,12 @@ import ( "github.com/cosmos/cosmos-sdk/client" ) -func getBlock(clientCtx client.Context, height *int64) (*ctypes.ResultBlock, error) { +func getBlock(ctx context.Context, clientCtx client.Context, height *int64) (*ctypes.ResultBlock, error) { // get the node node, err := clientCtx.GetNode() if err != nil { return nil, err } - return node.Block(context.Background(), height) + return node.Block(ctx, height) } diff --git a/client/grpc/tmservice/query.pb.go b/client/grpc/tmservice/query.pb.go index 0a27b4a2e517..8055c8c162aa 100644 --- a/client/grpc/tmservice/query.pb.go +++ b/client/grpc/tmservice/query.pb.go @@ -2202,10 +2202,7 @@ func (m *GetValidatorSetByHeightRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2344,10 +2341,7 @@ func (m *GetValidatorSetByHeightResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2433,10 +2427,7 @@ func (m *GetLatestValidatorSetRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2575,10 +2566,7 @@ func (m *GetLatestValidatorSetResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2734,10 +2722,7 @@ func (m *Validator) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2806,10 +2791,7 @@ func (m *GetBlockByHeightRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2931,10 +2913,7 @@ func (m *GetBlockByHeightResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2984,10 +2963,7 @@ func (m *GetLatestBlockRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3109,10 +3085,7 @@ func (m *GetLatestBlockResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3162,10 +3135,7 @@ func (m *GetSyncingRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3235,10 +3205,7 @@ func (m *GetSyncingResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3288,10 +3255,7 @@ func (m *GetNodeInfoRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3413,10 +3377,7 @@ func (m *GetNodeInfoResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3692,10 +3653,7 @@ func (m *VersionInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3841,10 +3799,7 @@ func (m *Module) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/client/grpc/tmservice/service.go b/client/grpc/tmservice/service.go index 58d44af579cc..57668c0d7292 100644 --- a/client/grpc/tmservice/service.go +++ b/client/grpc/tmservice/service.go @@ -34,8 +34,8 @@ func NewQueryServer(clientCtx client.Context, interfaceRegistry codectypes.Inter } // GetSyncing implements ServiceServer.GetSyncing -func (s queryServer) GetSyncing(_ context.Context, _ *GetSyncingRequest) (*GetSyncingResponse, error) { - status, err := getNodeStatus(s.clientCtx) +func (s queryServer) GetSyncing(ctx context.Context, _ *GetSyncingRequest) (*GetSyncingResponse, error) { + status, err := getNodeStatus(ctx, s.clientCtx) if err != nil { return nil, err } @@ -45,8 +45,8 @@ func (s queryServer) GetSyncing(_ context.Context, _ *GetSyncingRequest) (*GetSy } // GetLatestBlock implements ServiceServer.GetLatestBlock -func (s queryServer) GetLatestBlock(context.Context, *GetLatestBlockRequest) (*GetLatestBlockResponse, error) { - status, err := getBlock(s.clientCtx, nil) +func (s queryServer) GetLatestBlock(ctx context.Context, _ *GetLatestBlockRequest) (*GetLatestBlockResponse, error) { + status, err := getBlock(ctx, s.clientCtx, nil) if err != nil { return nil, err } @@ -64,7 +64,7 @@ func (s queryServer) GetLatestBlock(context.Context, *GetLatestBlockRequest) (*G } // GetBlockByHeight implements ServiceServer.GetBlockByHeight -func (s queryServer) GetBlockByHeight(_ context.Context, req *GetBlockByHeightRequest) (*GetBlockByHeightResponse, error) { +func (s queryServer) GetBlockByHeight(ctx context.Context, req *GetBlockByHeightRequest) (*GetBlockByHeightResponse, error) { chainHeight, err := rpc.GetChainHeight(s.clientCtx) if err != nil { return nil, err @@ -74,7 +74,7 @@ func (s queryServer) GetBlockByHeight(_ context.Context, req *GetBlockByHeightRe return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length") } - res, err := getBlock(s.clientCtx, &req.Height) + res, err := getBlock(ctx, s.clientCtx, &req.Height) if err != nil { return nil, err } @@ -96,7 +96,7 @@ func (s queryServer) GetLatestValidatorSet(ctx context.Context, req *GetLatestVa return nil, err } - validatorsRes, err := rpc.GetValidators(s.clientCtx, nil, &page, &limit) + validatorsRes, err := rpc.GetValidators(ctx, s.clientCtx, nil, &page, &limit) if err != nil { return nil, err } @@ -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 { @@ -147,7 +150,7 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length") } - validatorsRes, err := rpc.GetValidators(s.clientCtx, &req.Height, &page, &limit) + validatorsRes, err := rpc.GetValidators(ctx, s.clientCtx, &req.Height, &page, &limit) if err != nil { return nil, err @@ -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 { @@ -175,7 +179,7 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida // GetNodeInfo implements ServiceServer.GetNodeInfo func (s queryServer) GetNodeInfo(ctx context.Context, req *GetNodeInfoRequest) (*GetNodeInfoResponse, error) { - status, err := getNodeStatus(s.clientCtx) + status, err := getNodeStatus(ctx, s.clientCtx) if err != nil { return nil, err } diff --git a/client/grpc/tmservice/service_test.go b/client/grpc/tmservice/service_test.go index dd8f0dd50ee1..fb83ab9c2046 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] - - // nil pagination - _, err := s.queryClient.GetValidatorSetByHeight(context.Background(), &tmservice.GetValidatorSetByHeightRequest{ - Height: 1, - Pagination: nil, - }) - s.Require().NoError(err) +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) + } + }) + } +} - _, err = s.queryClient.GetValidatorSetByHeight(context.Background(), &tmservice.GetValidatorSetByHeightRequest{ - Height: 1, - Pagination: &qtypes.PageRequest{ - Offset: 0, - Limit: 10, - }}) - 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) + } + }) + } +} - // no pagination rest - _, err = rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d", val.APIAddress, 1)) - 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))) + } + }) + } +} - // 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/tmservice/status.go b/client/grpc/tmservice/status.go index f1a8da8e8362..9e9c3d200943 100644 --- a/client/grpc/tmservice/status.go +++ b/client/grpc/tmservice/status.go @@ -8,10 +8,10 @@ import ( "github.com/cosmos/cosmos-sdk/client" ) -func getNodeStatus(clientCtx client.Context) (*ctypes.ResultStatus, error) { +func getNodeStatus(ctx context.Context, clientCtx client.Context) (*ctypes.ResultStatus, error) { node, err := clientCtx.GetNode() if err != nil { return &ctypes.ResultStatus{}, err } - return node.Status(context.Background()) + return node.Status(ctx) } diff --git a/client/grpc_query.go b/client/grpc_query.go index fc8cdeeb80d5..011523944c54 100644 --- a/client/grpc_query.go +++ b/client/grpc_query.go @@ -24,51 +24,86 @@ 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 } + err = protoCodec.Unmarshal(abciRes.Value, reply) + if err != nil { + return err + } + + for _, callOpt := range opts { + header, ok := callOpt.(grpc.HeaderCallOption) + if !ok { + continue + } + + *header.HeaderAddr = outMd + } + + if ctx.InterfaceRegistry != nil { + return types.UnpackInterfaces(reply, ctx.InterfaceRegistry) + } + + return nil +} + +// NewStream implements the grpc ClientConn.NewStream method +func (Context) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { + return nil, fmt.Errorf("streaming rpc not supported") +} + +// 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 - 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 + return abci.ResponseQuery{}, nil, err } if height < 0 { - return sdkerrors.Wrapf( + return abci.ResponseQuery{}, nil, sdkerrors.Wrapf( sdkerrors.ErrInvalidRequest, "client.Context.Invoke: height (%d) from %q must be >= 0", height, grpctypes.GRPCBlockHeightHeader) } @@ -76,19 +111,14 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, args, reply ctx = ctx.WithHeight(height) } - req := abci.RequestQuery{ + abciReq := abci.RequestQuery{ Path: method, Data: reqBz, } - res, err := ctx.QueryABCI(req) + abciRes, err := ctx.QueryABCI(abciReq) if err != nil { - return err - } - - err = protoCodec.Unmarshal(res.Value, reply) - if err != nil { - return err + return abci.ResponseQuery{}, nil, err } // Create header metadata. For now the headers contain: @@ -96,28 +126,7 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, args, reply // 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 - } - - if ctx.InterfaceRegistry != nil { - return types.UnpackInterfaces(reply, ctx.InterfaceRegistry) - } - - return nil -} - -// NewStream implements the grpc ClientConn.NewStream method -func (Context) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { - return nil, fmt.Errorf("streaming rpc not supported") -} + md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(abciRes.Height, 10)) -func isBroadcast(method string) bool { - return method == "/cosmos.tx.v1beta1.Service/BroadcastTx" + return abciRes, md, nil } diff --git a/client/keys/migrate.go b/client/keys/migrate.go index d80bbe163097..836a2655b087 100644 --- a/client/keys/migrate.go +++ b/client/keys/migrate.go @@ -62,7 +62,7 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error { var ( tmpDir string - migrator keyring.InfoImporter + migrator keyring.Importer ) if dryRun, _ := cmd.Flags().GetBool(flags.FlagDryRun); dryRun { @@ -73,10 +73,10 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error { defer os.RemoveAll(tmpDir) - migrator, err = keyring.NewInfoImporter(keyringServiceName, "test", tmpDir, buf) + migrator, err = keyring.New(keyringServiceName, keyring.BackendTest, tmpDir, buf) } else { backend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) - migrator, err = keyring.NewInfoImporter(keyringServiceName, backend, rootDir, buf) + migrator, err = keyring.New(keyringServiceName, backend, rootDir, buf) } if err != nil { @@ -86,12 +86,12 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error { )) } - for _, key := range oldKeys { - legKeyInfo, err := legacyKb.Export(key.GetName()) - if err != nil { - return err - } + if len(oldKeys) == 0 { + cmd.Print("Migration Aborted: no keys to migrate") + return nil + } + for _, key := range oldKeys { keyName := key.GetName() keyType := key.GetType() @@ -107,7 +107,12 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error { } if keyType != keyring.TypeLocal { - if err := migrator.Import(keyName, legKeyInfo); err != nil { + pubkeyArmor, err := legacyKb.ExportPubKey(keyName) + if err != nil { + return err + } + + if err := migrator.ImportPubKey(keyName, pubkeyArmor); err != nil { return err } @@ -127,10 +132,11 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error { return err } - if err := migrator.Import(keyName, armoredPriv); err != nil { + if err := migrator.ImportPrivKey(keyName, armoredPriv, migratePassphrase); err != nil { return err } } + cmd.Print("Migration Complete") return err } diff --git a/client/keys/parse.go b/client/keys/parse.go index 2af792c03c8e..b1169d68cc0e 100644 --- a/client/keys/parse.go +++ b/client/keys/parse.go @@ -1,7 +1,6 @@ package keys import ( - "context" "encoding/hex" "errors" "fmt" @@ -86,7 +85,7 @@ hexadecimal into bech32 cosmos prefixed format and vice versa. } func parseKey(cmd *cobra.Command, args []string) error { - config, _ := sdk.GetSealedConfig(context.Background()) + config, _ := sdk.GetSealedConfig(cmd.Context()) return doParseKey(cmd, config, args) } diff --git a/client/keys/testdata/keys/keys.db/000002.ldb b/client/keys/testdata/keys/keys.db/000002.ldb deleted file mode 100644 index b36586df3626..000000000000 Binary files a/client/keys/testdata/keys/keys.db/000002.ldb and /dev/null differ diff --git a/client/keys/testdata/keys/keys.db/CURRENT b/client/keys/testdata/keys/keys.db/CURRENT index cacca7574c03..aa5bb8ea5090 100644 --- a/client/keys/testdata/keys/keys.db/CURRENT +++ b/client/keys/testdata/keys/keys.db/CURRENT @@ -1 +1 @@ -MANIFEST-000004 +MANIFEST-000005 diff --git a/client/keys/testdata/keys/keys.db/CURRENT.bak b/client/keys/testdata/keys/keys.db/CURRENT.bak index feda7d6b2481..4fb1dad19e03 100644 --- a/client/keys/testdata/keys/keys.db/CURRENT.bak +++ b/client/keys/testdata/keys/keys.db/CURRENT.bak @@ -1 +1 @@ -MANIFEST-000000 +MANIFEST-000003 diff --git a/client/keys/testdata/keys/keys.db/LOG b/client/keys/testdata/keys/keys.db/LOG index 386101e4fb91..e37648b855bb 100644 --- a/client/keys/testdata/keys/keys.db/LOG +++ b/client/keys/testdata/keys/keys.db/LOG @@ -1,18 +1,30 @@ -=============== Mar 30, 2020 (CEST) =============== -02:07:34.137606 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed -02:07:34.144547 db@open opening -02:07:34.144770 version@stat F·[] S·0B[] Sc·[] -02:07:34.145843 db@janitor F·2 G·0 -02:07:34.145875 db@open done T·1.315251ms -02:07:34.335635 db@close closing -02:07:34.335736 db@close done T·98.95µs -=============== Mar 30, 2020 (CEST) =============== -02:08:33.239115 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed -02:08:33.239264 version@stat F·[] S·0B[] Sc·[] -02:08:33.239281 db@open opening -02:08:33.239310 journal@recovery F·1 -02:08:33.239398 journal@recovery recovering @1 -02:08:33.322008 memdb@flush created L0@2 N·4 S·391B "cos..ess,v4":"run..nfo,v3" -02:08:33.323091 version@stat F·[1] S·391B[391B] Sc·[0.25] -02:08:33.421979 db@janitor F·3 G·0 -02:08:33.422153 db@open done T·182.707962ms +=============== Feb 2, 2021 (IST) =============== +00:03:25.348369 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +00:03:25.350695 db@open opening +00:03:25.350888 version@stat F·[] S·0B[] Sc·[] +00:03:25.351864 db@janitor F·2 G·0 +00:03:25.351881 db@open done T·1.169825ms +00:03:25.351895 db@close closing +00:03:25.351929 db@close done T·33.042µs +=============== Feb 2, 2021 (IST) =============== +00:03:34.450638 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +00:03:34.450722 version@stat F·[] S·0B[] Sc·[] +00:03:34.450737 db@open opening +00:03:34.450765 journal@recovery F·1 +00:03:34.450851 journal@recovery recovering @1 +00:03:34.451173 version@stat F·[] S·0B[] Sc·[] +00:03:34.454278 db@janitor F·2 G·0 +00:03:34.454298 db@open done T·3.548046ms +00:03:34.454307 db@close closing +00:03:34.454327 db@close done T·19.017µs +=============== Feb 2, 2021 (IST) =============== +00:03:42.025705 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +00:03:42.025892 version@stat F·[] S·0B[] Sc·[] +00:03:42.025907 db@open opening +00:03:42.025943 journal@recovery F·1 +00:03:42.026790 journal@recovery recovering @2 +00:03:42.026946 version@stat F·[] S·0B[] Sc·[] +00:03:42.031645 db@janitor F·2 G·0 +00:03:42.031661 db@open done T·5.750008ms +00:03:42.283102 db@close closing +00:03:42.283162 db@close done T·58.775µs diff --git a/client/keys/testdata/keys/keys.db/MANIFEST-000004 b/client/keys/testdata/keys/keys.db/MANIFEST-000004 deleted file mode 100644 index 557b4bdbbc93..000000000000 Binary files a/client/keys/testdata/keys/keys.db/MANIFEST-000004 and /dev/null differ diff --git a/client/keys/testdata/keys/keys.db/MANIFEST-000005 b/client/keys/testdata/keys/keys.db/MANIFEST-000005 new file mode 100644 index 000000000000..a9e8a261c788 Binary files /dev/null and b/client/keys/testdata/keys/keys.db/MANIFEST-000005 differ diff --git a/client/query.go b/client/query.go index 18a5c3c2f4ee..bb65d9bf3833 100644 --- a/client/query.go +++ b/client/query.go @@ -57,6 +57,11 @@ func (ctx Context) GetFromAddress() sdk.AccAddress { return ctx.FromAddress } +// GetFeeGranterAddress returns the fee granter address from the context +func (ctx Context) GetFeeGranterAddress() sdk.AccAddress { + return ctx.FeeGranter +} + // GetFromName returns the key name for the current context. func (ctx Context) GetFromName() string { return ctx.FromName diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 66c594966ed7..7760aead9935 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -50,7 +50,7 @@ func ValidatorCommand() *cobra.Command { page, _ := cmd.Flags().GetInt(flags.FlagPage) limit, _ := cmd.Flags().GetInt(flags.FlagLimit) - result, err := GetValidators(clientCtx, height, &page, &limit) + result, err := GetValidators(cmd.Context(), clientCtx, height, &page, &limit) if err != nil { return err } @@ -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( @@ -117,21 +119,27 @@ func validatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error) { } // GetValidators from client -func GetValidators(clientCtx client.Context, height *int64, page, limit *int) (ResultValidatorsOutput, error) { +func GetValidators(ctx context.Context, clientCtx client.Context, height *int64, page, limit *int) (ResultValidatorsOutput, error) { // get the node node, err := clientCtx.GetNode() if err != nil { return ResultValidatorsOutput{}, err } - validatorsRes, err := node.Validators(context.Background(), height, page, limit) + validatorsRes, err := node.Validators(ctx, height, page, limit) if err != nil { 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++ { @@ -172,7 +180,7 @@ func ValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - output, err := GetValidators(clientCtx, &height, &page, &limit) + output, err := GetValidators(r.Context(), clientCtx, &height, &page, &limit) if rest.CheckInternalServerError(w, err) { return } @@ -189,7 +197,7 @@ func LatestValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFu return } - output, err := GetValidators(clientCtx, nil, &page, &limit) + output, err := GetValidators(r.Context(), clientCtx, nil, &page, &limit) if rest.CheckInternalServerError(w, err) { return } diff --git a/client/tx/legacy_test.go b/client/tx/legacy_test.go index b10c51e34402..5dcab771c495 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.go b/client/tx/tx.go index 594293732bc8..62e3e9098eea 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -117,6 +117,7 @@ func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { } } + tx.SetFeeGranter(clientCtx.GetFeeGranterAddress()) err = Sign(txf, clientCtx.GetFromName(), tx, true) if err != nil { return err diff --git a/client/tx_config.go b/client/tx_config.go index 6992a7a2402d..8220e917b2bb 100644 --- a/client/tx_config.go +++ b/client/tx_config.go @@ -42,5 +42,6 @@ type ( SetFeeAmount(amount sdk.Coins) SetGasLimit(limit uint64) SetTimeoutHeight(height uint64) + SetFeeGranter(feeGranter sdk.AccAddress) } ) diff --git a/codec/amino.go b/codec/amino.go index 78fd650ca4b8..ea4c8d4fd842 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/amino_codec_test.go b/codec/amino_codec_test.go index d95ddc8d97fa..0526661404d5 100644 --- a/codec/amino_codec_test.go +++ b/codec/amino_codec_test.go @@ -121,8 +121,7 @@ func TestAminoCodecUnpackAnyFails(t *testing.T) { func TestAminoCodecFullDecodeAndEncode(t *testing.T) { // This tx comes from https://github.com/cosmos/cosmos-sdk/issues/8117. txSigned := `{"type":"cosmos-sdk/StdTx","value":{"msg":[{"type":"cosmos-sdk/MsgCreateValidator","value":{"description":{"moniker":"fulltest","identity":"satoshi","website":"example.com","details":"example inc"},"commission":{"rate":"0.500000000000000000","max_rate":"1.000000000000000000","max_change_rate":"0.200000000000000000"},"min_self_delegation":"1000000","delegator_address":"cosmos14pt0q5cwf38zt08uu0n6yrstf3rndzr5057jys","validator_address":"cosmosvaloper14pt0q5cwf38zt08uu0n6yrstf3rndzr52q28gr","pubkey":{"type":"tendermint/PubKeyEd25519","value":"CYrOiM3HtS7uv1B1OAkknZnFYSRpQYSYII8AtMMtev0="},"value":{"denom":"umuon","amount":"700000000"}}}],"fee":{"amount":[{"denom":"umuon","amount":"6000"}],"gas":"160000"},"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AwAOXeWgNf1FjMaayrSnrOOKz+Fivr6DiI/i0x0sZCHw"},"signature":"RcnfS/u2yl7uIShTrSUlDWvsXo2p2dYu6WJC8VDVHMBLEQZWc8bsINSCjOnlsIVkUNNe1q/WCA9n3Gy1+0zhYA=="}],"memo":"","timeout_height":"0"}}` - _, legacyCdc := simapp.MakeCodecs() - + var legacyCdc = simapp.MakeTestEncodingConfig().Amino var tx legacytx.StdTx err := legacyCdc.UnmarshalJSON([]byte(txSigned), &tx) require.NoError(t, err) diff --git a/codec/types/any.go b/codec/types/any.go index eebf13ca6cf0..1d9d34ff2a22 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 @@ -113,8 +116,3 @@ func (any *Any) pack(x proto.Message) error { func (any *Any) GetCachedValue() interface{} { return any.cachedValue } - -// ClearCachedValue clears the cached value from the Any -func (any *Any) ClearCachedValue() { - any.cachedValue = nil -} diff --git a/codec/types/any_internal_test.go b/codec/types/any_internal_test.go new file mode 100644 index 000000000000..fda205f591ca --- /dev/null +++ b/codec/types/any_internal_test.go @@ -0,0 +1,53 @@ +package types + +import ( + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/require" +) + +type Dog struct { + Name string `protobuf:"bytes,1,opt,name=size,proto3" json:"size,omitempty"` +} + +func (d Dog) Greet() string { return d.Name } + +// We implement a minimal proto.Message interface +func (d *Dog) Reset() { d.Name = "" } +func (d *Dog) String() string { return d.Name } +func (d *Dog) ProtoMessage() {} +func (d *Dog) XXX_MessageName() string { return "tests/dog" } + +type Animal interface { + Greet() string +} + +var _ Animal = (*Dog)(nil) +var _ proto.Message = (*Dog)(nil) + +func TestAnyPackUnpack(t *testing.T) { + registry := NewInterfaceRegistry() + registry.RegisterInterface("Animal", (*Animal)(nil)) + registry.RegisterImplementations( + (*Animal)(nil), + &Dog{}, + ) + + spot := &Dog{Name: "Spot"} + var animal Animal + + // with cache + any, err := NewAnyWithValue(spot) + require.NoError(t, err) + require.Equal(t, spot, any.GetCachedValue()) + err = registry.UnpackAny(any, &animal) + require.NoError(t, err) + require.Equal(t, spot, animal) + + // without cache + any.cachedValue = nil + err = registry.UnpackAny(any, &animal) + require.NoError(t, err) + require.Equal(t, spot, animal) +} diff --git a/codec/types/any_test.go b/codec/types/any_test.go new file mode 100644 index 000000000000..cea3e0444efb --- /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/codec/types/types_test.go b/codec/types/types_test.go index b2d014269502..43f85375508e 100644 --- a/codec/types/types_test.go +++ b/codec/types/types_test.go @@ -9,16 +9,15 @@ import ( "github.com/gogo/protobuf/grpc" "github.com/gogo/protobuf/jsonpb" "github.com/gogo/protobuf/proto" - grpc2 "google.golang.org/grpc" - "github.com/stretchr/testify/require" + grpc2 "google.golang.org/grpc" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/testutil/testdata" ) -func TestPackUnpack(t *testing.T) { +func TestAnyPackUnpack(t *testing.T) { registry := testdata.NewTestInterfaceRegistry() spot := &testdata.Dog{Name: "Spot"} @@ -31,12 +30,6 @@ func TestPackUnpack(t *testing.T) { err = registry.UnpackAny(any, &animal) require.NoError(t, err) require.Equal(t, spot, animal) - - // without cache - any.ClearCachedValue() - err = registry.UnpackAny(any, &animal) - require.NoError(t, err) - require.Equal(t, spot, animal) } type TestI interface { diff --git a/contrib/devtools/dockerfile b/contrib/devtools/dockerfile new file mode 100644 index 000000000000..565e66ea330f --- /dev/null +++ b/contrib/devtools/dockerfile @@ -0,0 +1,21 @@ +FROM bufbuild/buf:latest as BUILDER + +FROM golang:alpine + +ENV GOLANG_PROTOBUF_VERSION=1.3.5 \ + GOGO_PROTOBUF_VERSION=1.3.2 \ + GRPC_GATEWAY_VERSION=1.14.7 + + +RUN GO111MODULE=on go get \ + github.com/golang/protobuf/protoc-gen-go@v${GOLANG_PROTOBUF_VERSION} \ + github.com/gogo/protobuf/protoc-gen-gogo@v${GOGO_PROTOBUF_VERSION} \ + github.com/gogo/protobuf/protoc-gen-gogofast@v${GOGO_PROTOBUF_VERSION} \ + github.com/gogo/protobuf/protoc-gen-gogofaster@v${GOGO_PROTOBUF_VERSION} \ + github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway@v${GRPC_GATEWAY_VERSION} \ + github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger@v${GRPC_GATEWAY_VERSION} \ + github.com/regen-network/cosmos-proto/protoc-gen-gocosmos@latest + +RUN GO111MODULE=on go get -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc + +COPY --from=BUILDER /usr/local/bin /usr/local/bin diff --git a/contrib/rosetta/README.md b/contrib/rosetta/README.md new file mode 100644 index 000000000000..b1d33659fec8 --- /dev/null +++ b/contrib/rosetta/README.md @@ -0,0 +1,24 @@ +# rosetta + +This directory contains the files required to run the rosetta CI. It builds `simapp` based on the current codebase. + +## docker-compose.yaml + +Builds: +- cosmos-sdk simapp node, with prefixed data directory, keys etc. This is required to test historical balances. +- faucet is required so we can test construction API, it was literally impossible to put there a deterministic address to request funds for +- rosetta is the rosetta node used by rosetta-cli to interact with the cosmos-sdk app +- test_rosetta runs the rosetta-cli test against construction API and data API + +## configuration + +Contains the required files to set up rosetta cli and make it work against its workflows + +## node + +Contains the files for a deterministic network, with fixed keys and some actions on there, to test parsing of msgs and historical balances. + +## Notes + +- Keyring password is 12345678 +- data.sh creates node data, it's required in case consensus breaking changes are made to quickly recreate replicable node data for rosetta diff --git a/contrib/rosetta/configuration/bootstrap.json b/contrib/rosetta/configuration/bootstrap.json new file mode 100644 index 000000000000..1793988f37e5 --- /dev/null +++ b/contrib/rosetta/configuration/bootstrap.json @@ -0,0 +1,12 @@ +[ + { + "account_identifier": { + "address":"cosmos158nkd0l9tyemv2crp579rmj8dg37qty8lzff88" + }, + "currency":{ + "symbol":"stake", + "decimals":0 + }, + "value": "999990000000" + } +] \ No newline at end of file diff --git a/contrib/rosetta/configuration/data.sh b/contrib/rosetta/configuration/data.sh new file mode 100644 index 000000000000..45297d5a21bf --- /dev/null +++ b/contrib/rosetta/configuration/data.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +set -e + +wait_simd() { + timeout 30 sh -c 'until nc -z $0 $1; do sleep 1; done' localhost 9090 +} +# this script is used to recreate the data dir +echo clearing /root/.simapp +rm -rf /root/.simapp +echo initting new chain +# init config files +simd init simd --chain-id testing + +# create accounts +simd keys add fd --keyring-backend=test + +addr=$(simd keys show fd -a --keyring-backend=test) +val_addr=$(simd keys show fd --keyring-backend=test --bech val -a) + +# give the accounts some money +simd add-genesis-account "$addr" 1000000000000stake --keyring-backend=test + +# save configs for the daemon +simd gentx fd 10000000stake --chain-id testing --keyring-backend=test + +# input genTx to the genesis file +simd collect-gentxs +# verify genesis file is fine +simd validate-genesis +echo changing network settings +sed -i 's/127.0.0.1/0.0.0.0/g' /root/.simapp/config/config.toml + +# start simd +echo starting simd... +simd start --pruning=nothing & +pid=$! +echo simd started with PID $pid + +echo awaiting for simd to be ready +wait_simd +echo simd is ready +sleep 10 + + +# send transaction to deterministic address +echo sending transaction with addr $addr +simd tx bank send "$addr" cosmos1wjmt63j4fv9nqda92nsrp2jp2vsukcke4va3pt 100stake --yes --keyring-backend=test --broadcast-mode=block --chain-id=testing + +sleep 10 + +echo stopping simd... +kill -9 $pid + +echo zipping data dir and saving to /tmp/data.tar.gz + +tar -czvf /tmp/data.tar.gz /root/.simapp + +echo new address for bootstrap.json "$addr" "$val_addr" diff --git a/contrib/rosetta/configuration/faucet.py b/contrib/rosetta/configuration/faucet.py new file mode 100644 index 000000000000..44536a84bb3b --- /dev/null +++ b/contrib/rosetta/configuration/faucet.py @@ -0,0 +1,25 @@ +from http.server import HTTPServer, BaseHTTPRequestHandler +import subprocess + +import os + + +class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): + + def do_POST(self): + try: + content_len = int(self.headers.get('Content-Length')) + addr = self.rfile.read(content_len).decode("utf-8") + print("sending funds to " + addr) + subprocess.call(['sh', './send_funds.sh', addr]) + self.send_response(200) + self.end_headers() + except Exception as e: + print("failed " + str(e)) + os._exit(1) + + +if __name__ == "__main__": + print("starting faucet server...") + httpd = HTTPServer(('0.0.0.0', 8000), SimpleHTTPRequestHandler) + httpd.serve_forever() diff --git a/contrib/rosetta/configuration/rosetta.json b/contrib/rosetta/configuration/rosetta.json new file mode 100644 index 000000000000..39a0bb3811dd --- /dev/null +++ b/contrib/rosetta/configuration/rosetta.json @@ -0,0 +1,51 @@ +{ + "network": { + "blockchain": "app", + "network": "network" + }, + "online_url": "http://rosetta:8080", + "data_directory": "", + "http_timeout": 300, + "max_retries": 5, + "retry_elapsed_time": 0, + "max_online_connections": 0, + "max_sync_concurrency": 0, + "tip_delay": 60, + "log_configuration": true, + "construction": { + "offline_url": "http://rosetta:8080", + "max_offline_connections": 0, + "stale_depth": 0, + "broadcast_limit": 0, + "ignore_broadcast_failures": false, + "clear_broadcasts": false, + "broadcast_behind_tip": false, + "block_broadcast_limit": 0, + "rebroadcast_all": false, + "constructor_dsl_file": "transfer.ros", + "end_conditions": { + "create_account": 1, + "transfer": 3 + } + }, + "data": { + "active_reconciliation_concurrency": 0, + "inactive_reconciliation_concurrency": 0, + "inactive_reconciliation_frequency": 0, + "log_blocks": false, + "log_transactions": false, + "log_balance_changes": false, + "log_reconciliations": false, + "ignore_reconciliation_error": false, + "exempt_accounts": "", + "bootstrap_balances": "bootstrap.json", + "interesting_accounts": "", + "reconciliation_disabled": false, + "inactive_discrepency_search_disabled": false, + "balance_tracking_disabled": false, + "coin_tracking_disabled": false, + "end_conditions": { + "tip": true + } + } +} \ No newline at end of file diff --git a/contrib/rosetta/configuration/run_tests.sh b/contrib/rosetta/configuration/run_tests.sh new file mode 100755 index 000000000000..cd7af92acda2 --- /dev/null +++ b/contrib/rosetta/configuration/run_tests.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +set -e + +addr="abcd" + +send_tx() { + echo '12345678' | simd tx bank send $addr "$1" "$2" +} + +detect_account() { + line=$1 +} + +wait_for_rosetta() { + timeout 30 sh -c 'until nc -z $0 $1; do sleep 1; done' rosetta 8080 +} + +echo "waiting for rosetta instance to be up" +wait_for_rosetta + +echo "checking data API" +rosetta-cli check:data --configuration-file ./config/rosetta.json + +echo "checking construction API" +rosetta-cli check:construction --configuration-file ./config/rosetta.json + +echo "checking staking API" +rosetta-cli check:construction --configuration-file ./config/staking.json diff --git a/contrib/rosetta/configuration/send_funds.sh b/contrib/rosetta/configuration/send_funds.sh new file mode 100644 index 000000000000..3a897539d225 --- /dev/null +++ b/contrib/rosetta/configuration/send_funds.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +set -e +addr=$(simd keys show fd -a --keyring-backend=test) +echo "12345678" | simd tx bank send "$addr" "$1" 100stake --chain-id="testing" --node tcp://cosmos:26657 --yes --keyring-backend=test \ No newline at end of file diff --git a/contrib/rosetta/configuration/staking.json b/contrib/rosetta/configuration/staking.json new file mode 100644 index 000000000000..9c5e5da3ba46 --- /dev/null +++ b/contrib/rosetta/configuration/staking.json @@ -0,0 +1,30 @@ +{ + "network": { + "blockchain": "app", + "network": "network" + }, + "online_url": "http://rosetta:8080", + "data_directory": "", + "http_timeout": 300, + "max_retries": 5, + "retry_elapsed_time": 0, + "max_online_connections": 0, + "max_sync_concurrency": 0, + "tip_delay": 60, + "log_configuration": true, + "construction": { + "offline_url": "http://rosetta:8080", + "max_offline_connections": 0, + "stale_depth": 0, + "broadcast_limit": 0, + "ignore_broadcast_failures": false, + "clear_broadcasts": false, + "broadcast_behind_tip": false, + "block_broadcast_limit": 0, + "rebroadcast_all": false, + "constructor_dsl_file": "staking.ros", + "end_conditions": { + "staking": 3 + } + } +} \ No newline at end of file diff --git a/contrib/rosetta/configuration/staking.ros b/contrib/rosetta/configuration/staking.ros new file mode 100644 index 000000000000..4f89a43b9893 --- /dev/null +++ b/contrib/rosetta/configuration/staking.ros @@ -0,0 +1,147 @@ +request_funds(1){ + find_account{ + currency = {"symbol":"stake", "decimals":0}; + random_account = find_balance({ + "minimum_balance":{ + "value": "0", + "currency": {{currency}} + }, + "create_limit":1 + }); + }, + send_funds{ + account_identifier = {{random_account.account_identifier}}; + address = {{account_identifier.address}}; + idk = http_request({ + "method": "POST", + "url": "http:\/\/faucet:8000", + "timeout": 10, + "body": {{random_account.account_identifier.address}} + }); + }, + // Create a separate scenario to request funds so that + // the address we are using to request funds does not + // get rolled back if funds do not yet exist. + request{ + loaded_account = find_balance({ + "account_identifier": {{random_account.account_identifier}}, + "minimum_balance":{ + "value": "100", + "currency": {{currency}} + } + }); + } +} +create_account(1){ + create{ + network = {"network":"network", "blockchain":"app"}; + key = generate_key({"curve_type": "secp256k1"}); + account = derive({ + "network_identifier": {{network}}, + "public_key": {{key.public_key}} + }); + // If the account is not saved, the key will be lost! + save_account({ + "account_identifier": {{account.account_identifier}}, + "keypair": {{key}} + }); + } +} + +staking(1){ + stake{ + stake.network = {"network":"network", "blockchain":"app"}; + currency = {"symbol":"stake", "decimals":0}; + sender = find_balance({ + "minimum_balance":{ + "value": "100", + "currency": {{currency}} + } + }); + // Set the recipient_amount as some value <= sender.balance-max_fee + max_fee = "0"; + fee_amount = "1"; + fee_value = 0 - {{fee_amount}}; + available_amount = {{sender.balance.value}} - {{max_fee}}; + recipient_amount = "1"; + print_message({"recipient_amount":{{recipient_amount}}}); + // Find recipient and construct operations + recipient = {{sender.account_identifier}}; + sender_amount = 0 - {{recipient_amount}}; + stake.confirmation_depth = "1"; + stake.operations = [ + { + "operation_identifier":{"index":0}, + "type":"fee", + "account":{{sender.account_identifier}}, + "amount":{ + "value":{{fee_value}}, + "currency":{{currency}} + } + }, + { + "operation_identifier":{"index":1}, + "type":"cosmos.staking.v1beta1.MsgDelegate", + "account":{{sender.account_identifier}}, + "amount":{ + "value":{{sender_amount}}, + "currency":{{currency}} + } + }, + { + "operation_identifier":{"index":2}, + "type":"cosmos.staking.v1beta1.MsgDelegate", + "account": { + "address": "staking_account", + "sub_account": { + "address" : "cosmosvaloper158nkd0l9tyemv2crp579rmj8dg37qty86kaut5" + } + }, + "amount":{ + "value":{{recipient_amount}}, + "currency":{{currency}} + } + } + ]; + }, + undelegate{ + print_message({"undelegate":{{sender}}}); + + undelegate.network = {"network":"network", "blockchain":"app"}; + undelegate.confirmation_depth = "1"; + undelegate.operations = [ + { + "operation_identifier":{"index":0}, + "type":"fee", + "account":{{sender.account_identifier}}, + "amount":{ + "value":{{fee_value}}, + "currency":{{currency}} + } + }, + { + "operation_identifier":{"index":1}, + "type":"cosmos.staking.v1beta1.MsgUndelegate", + "account":{{sender.account_identifier}}, + "amount":{ + "value":{{recipient_amount}}, + "currency":{{currency}} + } + }, + { + "operation_identifier":{"index":2}, + "type":"cosmos.staking.v1beta1.MsgUndelegate", + "account": { + "address": "staking_account", + "sub_account": { + "address" : "cosmosvaloper158nkd0l9tyemv2crp579rmj8dg37qty86kaut5" + } + }, + "amount":{ + "value":{{sender_amount}}, + "currency":{{currency}} + } + } + ]; + } +} diff --git a/contrib/rosetta/configuration/transfer.ros b/contrib/rosetta/configuration/transfer.ros new file mode 100644 index 000000000000..a1cb3f8caf89 --- /dev/null +++ b/contrib/rosetta/configuration/transfer.ros @@ -0,0 +1,109 @@ +request_funds(1){ + find_account{ + currency = {"symbol":"stake", "decimals":0}; + random_account = find_balance({ + "minimum_balance":{ + "value": "0", + "currency": {{currency}} + }, + "create_limit":1 + }); + }, + send_funds{ + account_identifier = {{random_account.account_identifier}}; + address = {{account_identifier.address}}; + idk = http_request({ + "method": "POST", + "url": "http:\/\/faucet:8000", + "timeout": 10, + "body": {{random_account.account_identifier.address}} + }); + }, + // Create a separate scenario to request funds so that + // the address we are using to request funds does not + // get rolled back if funds do not yet exist. + request{ + loaded_account = find_balance({ + "account_identifier": {{random_account.account_identifier}}, + "minimum_balance":{ + "value": "100", + "currency": {{currency}} + } + }); + } +} +create_account(1){ + create{ + network = {"network":"network", "blockchain":"app"}; + key = generate_key({"curve_type": "secp256k1"}); + account = derive({ + "network_identifier": {{network}}, + "public_key": {{key.public_key}} + }); + // If the account is not saved, the key will be lost! + save_account({ + "account_identifier": {{account.account_identifier}}, + "keypair": {{key}} + }); + } +} +transfer(3){ + transfer{ + transfer.network = {"network":"network", "blockchain":"app"}; + currency = {"symbol":"stake", "decimals":0}; + sender = find_balance({ + "minimum_balance":{ + "value": "100", + "currency": {{currency}} + } + }); + // Set the recipient_amount as some value <= sender.balance-max_fee + max_fee = "0"; + fee_amount = "1"; + fee_value = 0 - {{fee_amount}}; + available_amount = {{sender.balance.value}} - {{max_fee}}; + recipient_amount = random_number({"minimum": "1", "maximum": {{available_amount}}}); + print_message({"recipient_amount":{{recipient_amount}}}); + // Find recipient and construct operations + sender_amount = 0 - {{recipient_amount}}; + recipient = find_balance({ + "not_account_identifier":[{{sender.account_identifier}}], + "minimum_balance":{ + "value": "0", + "currency": {{currency}} + }, + "create_limit": 100, + "create_probability": 50 + }); + transfer.confirmation_depth = "1"; + transfer.operations = [ + { + "operation_identifier":{"index":0}, + "type":"fee", + "account":{{sender.account_identifier}}, + "amount":{ + "value":{{fee_value}}, + "currency":{{currency}} + } + }, + { + "operation_identifier":{"index":1}, + "type":"cosmos.bank.v1beta1.MsgSend", + "account":{{sender.account_identifier}}, + "amount":{ + "value":{{sender_amount}}, + "currency":{{currency}} + } + }, + { + "operation_identifier":{"index":2}, + "type":"cosmos.bank.v1beta1.MsgSend", + "account":{{recipient.account_identifier}}, + "amount":{ + "value":{{recipient_amount}}, + "currency":{{currency}} + } + } + ]; + } +} diff --git a/contrib/rosetta/docker-compose.yaml b/contrib/rosetta/docker-compose.yaml new file mode 100644 index 000000000000..0a9e82de8a9e --- /dev/null +++ b/contrib/rosetta/docker-compose.yaml @@ -0,0 +1,39 @@ +version: "3" + +services: + cosmos: + image: rosetta-ci:latest + command: ["simd", "start", "--pruning", "nothing", "--grpc-web.enable", "true", "--grpc-web.address", "0.0.0.0:9091"] + ports: + - 9090:9090 + - 26657:26657 + logging: + driver: "none" + + rosetta: + image: rosetta-ci:latest + command: [ + "simd", + "rosetta", + "--blockchain", "app", + "--network", "network", + "--tendermint", "cosmos:26657", + "--grpc", "cosmos:9090", + "--addr", ":8080", + ] + ports: + - 8080:8080 + + faucet: + image: rosetta-ci:latest + working_dir: /rosetta + command: ["python3", "faucet.py"] + expose: + - 8080 + + test_rosetta: + image: tendermintdev/rosetta-cli:v0.6.6 + volumes: + - ./configuration:/rosetta/config:z + command: ["./config/run_tests.sh"] + working_dir: /rosetta diff --git a/contrib/rosetta/node/Dockerfile b/contrib/rosetta/node/Dockerfile new file mode 100644 index 000000000000..0887f522f656 --- /dev/null +++ b/contrib/rosetta/node/Dockerfile @@ -0,0 +1,32 @@ +FROM golang:1.15-alpine as build + +RUN apk add --no-cache tar + +# prepare node data +WORKDIR /node +COPY ./contrib/rosetta/node/data.tar.gz data.tar.gz +RUN tar -zxvf data.tar.gz -C . + +# build simd +WORKDIR /simd +COPY . ./ +RUN go build -o simd ./simapp/simd/ + +FROM alpine +RUN apk add gcc libc-dev python3 --no-cache + +ENV PATH=$PATH:/bin + +COPY --from=build /simd/simd /bin/simd + +WORKDIR /rosetta +COPY ./contrib/rosetta/configuration ./ +RUN chmod +x run_tests.sh +RUN chmod +x send_funds.sh +RUN chmod +x faucet.py + +COPY --from=build /node/root /root/ +WORKDIR /root/.simapp + +RUN chmod -R 0777 ./ + diff --git a/contrib/rosetta/node/data.tar.gz b/contrib/rosetta/node/data.tar.gz new file mode 100644 index 000000000000..ad285ac62e75 Binary files /dev/null and b/contrib/rosetta/node/data.tar.gz differ diff --git a/contrib/rosetta/rosetta-cli/Dockerfile b/contrib/rosetta/rosetta-cli/Dockerfile new file mode 100644 index 000000000000..e0bc3c961ff3 --- /dev/null +++ b/contrib/rosetta/rosetta-cli/Dockerfile @@ -0,0 +1,18 @@ +FROM golang:1.15-alpine as build + +RUN apk add git gcc libc-dev --no-cache + +ARG ROSETTA_VERSION="v0.5.23" + +# build rosetta CLI +WORKDIR /rosetta +RUN git clone https://github.com/coinbase/rosetta-cli . +RUN git checkout tags/$ROSETTA_VERSION +RUN go build -o rosetta-cli ./main.go + +FROM alpine +RUN apk add gcc libc-dev python3 --no-cache + +ENV PATH=$PATH:/bin + +COPY --from=build /rosetta/rosetta-cli /bin/rosetta-cli diff --git a/cosmovisor/README.md b/cosmovisor/README.md deleted file mode 100644 index 3892efd0db2d..000000000000 --- a/cosmovisor/README.md +++ /dev/null @@ -1,148 +0,0 @@ -# Cosmovisor - -This is a tiny shim around Cosmos SDK binaries that use the upgrade -module that allows for smooth and configurable management of upgrading -binaries as a live chain is upgraded, and can be used to simplify validator -devops while doing upgrades or to make syncing a full node for genesis -simple. The `cosmovisor` will monitor the stdout of the daemon to look -for messages from the upgrade module indicating a pending or required upgrade -and act appropriately. (With better integrations possible in the future). - -## Arguments - -`cosmovisor` is a shim around a native binary. All arguments passed to the `cosmovisor` -command will be passed to the current daemon binary (as a subprocess). - It will return stdout and stderr of the subprocess as -it's own. Because of that, it cannot accept any command line arguments, nor -print anything to output (unless it dies before executing a binary). - -Configuration will be passed in the following environmental variables: - -* `DAEMON_HOME` is the location where upgrade binaries should be kept (can -be `$HOME/.gaiad` or `$HOME/.xrnd`) -* `DAEMON_NAME` is the name of the binary itself (eg. `xrnd`, `gaiad`, `simd`) -* `DAEMON_ALLOW_DOWNLOAD_BINARIES` (optional) if set to `true` will enable auto-downloading of new binaries -(for security reasons, this is intended for fullnodes rather than validators) -* `DAEMON_RESTART_AFTER_UPGRADE` (optional) if set to `true` it will restart the sub-process with the same args -(but new binary) after a successful upgrade. By default, the `cosmovisor` dies afterward and allows the cosmovisor -to restart it if needed. Note that this will not auto-restart the child if there was an error. - -## Folder Layout - -`$DAEMON_HOME/cosmovisor` is expected to belong completely to the cosmovisor and -subprocesses -controlled by it. Under this folder, we will see the following: - -``` -. -├── current -> genesis or upgrades/ -├── genesis -│   └── bin -│   └── $DAEMON_NAME -└── upgrades - └── - └── bin - └── $DAEMON_NAME -``` - -Each version of the chain is stored under either `genesis` or `upgrades/`, which holds `bin/$DAEMON_NAME` -along with any other needed files (maybe the cli client? maybe some dlls?). `current` is a symlink to the currently -active folder (so `current/bin/$DAEMON_NAME` is the binary) - -Note: the `` after `upgrades` is the URI-encoded name of the upgrade as specified in the upgrade module plan. - -Please note that `$DAEMON_HOME/cosmovisor` just stores the *binaries* and associated *program code*. -The `cosmovisor` binary can be stored in any typical location (eg `/usr/local/bin`). The actual blockchain -program will store it's data under `$GAIA_HOME` etc, which is independent of the `$DAEMON_HOME`. You can -choose to export `GAIA_HOME=$DAEMON_HOME` and then end up with a configuation like the following, but this -is left as a choice to the admin for best directory layout. - -``` -.gaiad -├── config -├── data -└── cosmovisor -``` - -## Usage - -Basic Usage: - -* The admin is responsible for installing the `cosmovisor` and setting it as a eg. systemd service to auto-restart, along with proper environmental variables -* The admin is responsible for installing the `genesis` folder manually -* The `cosmovisor` will set the `current` link to point to `genesis` at first start (when no `current` link exists) -* The admin is (generally) responsible for installing the `upgrades/` folders manually -* The `cosmovisor` handles switching over the binaries at the correct points, so the admin can prepare days in advance and relax at upgrade time - -Note that chains that wish to support upgrades may package up a genesis `cosmovisor` tar file with this info, just as they -prepare the genesis binary tar file. In fact, they may offer a tar file will all upgrades up to current point for easy download -for those who wish to sync a fullnode from start. - -The `DAEMON` specific code, like the tendermint config, the application db, syncing blocks, etc is done as normal. -The same eg. `GAIA_HOME` directives and command-line flags work, just the binary name is different. - -## Upgradeable Binary Specification - -In the basic version, the `cosmovisor` will read the stdout log messages -to determine when an upgrade is needed. We are considering more complex solutions -via signaling of some sort, but starting with the simple design: - -* when an upgrade is needed the binary will print a line that matches this -regular expression: `UPGRADE "(.*)" NEEDED at height (\d+):(.*)`. -* the second match in the above regular expression can be a JSON object with -a `binaries` key as described above - -The name (first regexp) will be used to select the new binary to run. If it is present, -the current subprocess will be killed, `current` will be upgraded to the new directory, -and the new binary will be launched. - -**Question** should we just kill the `cosmovisor` after it does the updates? -so it gets a clean restart and just runs the new binary (under `current`). -it should be safe to restart (as a service). - -## Auto-Download - -Generally, the system requires that the administrator place all relevant binaries -on the disk before the upgrade happens. However, for people who don't need such -control and want an easier setup (maybe they are syncing a non-validating fullnode -and want to do little maintenance), there is another option. - -If you set `DAEMON_ALLOW_DOWNLOAD_BINARIES=on` then when an upgrade is triggered and no local binary -can be found, the `cosmovisor` will attempt to download and install the binary itself. -The plan stored in the upgrade module has an info field for arbitrary json. -This info is expected to be outputed on the halt log message. There are two -valid format to specify a download in such a message: - -1. Store an os/architecture -> binary URI map in the upgrade plan info field -as JSON under the `"binaries"` key, eg: -```json -{ - "binaries": { - "linux/amd64":"https://example.com/gaia.zip?checksum=sha256:aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f" - } -} -``` -The `"any"` key, if it exists, will be used as a default if there is not a specific os/architecture key. -2. Store a link to a file that contains all information in the above format (eg. if you want -to specify lots of binaries, changelog info, etc without filling up the blockchain). - -e.g `https://example.com/testnet-1001-info.json?checksum=sha256:deaaa99fda9407c4dbe1d04bd49bab0cc3c1dd76fa392cd55a9425be074af01e` - -This file contained in link will be retrieved by [go-getter](https://github.com/hashicorp/go-getter) -and the "binaries" field will be parsed as above. - -If there is no local binary, `DAEMON_ALLOW_DOWNLOAD_BINARIES=true`, and we can access a canonical url for the new binary, -then the `cosmovisor` will download it with [go-getter](https://github.com/hashicorp/go-getter) and -unpack it into the `upgrades/` folder to be run as if we installed it manually - -Note that for this mechanism to provide strong security guarantees, all URLs should include a -sha{256,512} checksum. This ensures that no false binary is run, even if someone hacks the server -or hijacks the dns. go-getter will always ensure the downloaded file matches the checksum if it -is provided. And also handles unpacking archives into directories (so these download links should be -a zip of all data in the bin directory). - -To properly create a checksum on linux, you can use the `sha256sum` utility. eg. -`sha256sum ./testdata/repo/zip_directory/autod.zip` -which should return `29139e1381b8177aec909fab9a75d11381cab5adf7d3af0c05ff1c9c117743a7`. -You can also use `sha512sum` if you like longer hashes, or `md5sum` if you like to use broken hashes. -Make sure to set the hash algorithm properly in the checksum argument to the url. diff --git a/cosmovisor/README.md b/cosmovisor/README.md new file mode 120000 index 000000000000..a37c5c784512 --- /dev/null +++ b/cosmovisor/README.md @@ -0,0 +1 @@ +../docs/run-node/cosmovisor.md \ No newline at end of file diff --git a/crypto/armor_test.go b/crypto/armor_test.go index abbc7870aaee..70854f6dcb41 100644 --- a/crypto/armor_test.go +++ b/crypto/armor_test.go @@ -158,6 +158,8 @@ func TestUnarmorInfoBytesErrors(t *testing.T) { } func BenchmarkBcryptGenerateFromPassword(b *testing.B) { + b.ReportAllocs() + passphrase := []byte("passphrase") for securityParam := 9; securityParam < 16; securityParam++ { param := securityParam diff --git a/crypto/codec/amino.go b/crypto/codec/amino.go index d50a08864c24..50119ed19858 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/algo.go b/crypto/hd/algo.go index f934ad08aece..7ed908f3b6fa 100644 --- a/crypto/hd/algo.go +++ b/crypto/hd/algo.go @@ -4,7 +4,9 @@ import ( bip39 "github.com/cosmos/go-bip39" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/crypto/keys/sr25519" "github.com/cosmos/cosmos-sdk/crypto/types" + substratebip39 "github.com/sikkatech/go-substrate-bip39" ) // PubKeyType defines an algorithm to derive key-pairs which can be used for cryptographic signing. @@ -25,6 +27,8 @@ const ( var ( // Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters. Secp256k1 = secp256k1Algo{} + // Sr25519 represents the Sr25519Type signature system. + Sr25519 = sr25519Algo{} ) type DeriveFn func(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) @@ -69,3 +73,25 @@ func (s secp256k1Algo) Generate() GenerateFn { return &secp256k1.PrivKey{Key: bzArr} } } + +type sr25519Algo struct { +} + +func (s sr25519Algo) Name() PubKeyType { + return Sr25519Type +} + +// Derive derives and returns the sr25519 private key for the given seed and HD path. +func (s sr25519Algo) Derive() DeriveFn { + return func(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) { + seed, err := substratebip39.SeedFromMnemonic(mnemonic, bip39Passphrase) + return seed[:], err + } +} + +// Generate generates a sr25519 private key from the given bytes. +func (s sr25519Algo) Generate() GenerateFn { + return func(bz []byte) types.PrivKey { + return &sr25519.PrivKey{Key: bz} + } +} diff --git a/crypto/hd/hdpath.go b/crypto/hd/hdpath.go index 04469d730591..058ddcc45667 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 6dfda043eb04..8b1c40692c82 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 f88ffdc37010..164e6bfc19d5 100644 --- a/crypto/keyring/keyring.go +++ b/crypto/keyring/keyring.go @@ -108,6 +108,7 @@ type Signer interface { type Importer interface { // ImportPrivKey imports ASCII armored passphrase-encrypted private keys. ImportPrivKey(uid, armor, passphrase string) error + // ImportPubKey imports ASCII armored public keys. ImportPubKey(uid string, armor string) error } diff --git a/crypto/keyring/legacy.go b/crypto/keyring/legacy.go index 59bdc3fc5dad..94bff8232f42 100644 --- a/crypto/keyring/legacy.go +++ b/crypto/keyring/legacy.go @@ -2,7 +2,6 @@ package keyring import ( "fmt" - "io" "strings" "github.com/pkg/errors" @@ -43,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 } @@ -185,49 +184,6 @@ func (kb dbKeybase) Close() error { return kb.db.Close() } func infoKey(name string) []byte { return []byte(fmt.Sprintf("%s.%s", name, infoSuffix)) } -// InfoImporter is implemented by those types that want to provide functions necessary -// to migrate keys from LegacyKeybase types to Keyring types. -type InfoImporter interface { - // Import imports ASCII-armored private keys. - Import(uid string, armor string) error -} - -type keyringMigrator struct { - kr keystore -} - -func NewInfoImporter( - appName, backend, rootDir string, userInput io.Reader, opts ...Option, -) (InfoImporter, error) { - keyring, err := New(appName, backend, rootDir, userInput, opts...) - if err != nil { - return keyringMigrator{}, err - } - - kr := keyring.(keystore) - - return keyringMigrator{kr}, nil -} - -func (m keyringMigrator) Import(uid string, armor string) error { - _, err := m.kr.Key(uid) - if err == nil { - return fmt.Errorf("cannot overwrite key %q", uid) - } - - infoBytes, err := crypto.UnarmorInfoBytes(armor) - if err != nil { - return err - } - - info, err := unmarshalInfo(infoBytes) - if err != nil { - return err - } - - return m.kr.writeInfo(info) -} - // KeybaseOption overrides options for the db. type KeybaseOption func(*kbOptions) diff --git a/crypto/keyring/legacy_test.go b/crypto/keyring/legacy_test.go index 27503bdea056..d1b0dbf3e682 100644 --- a/crypto/keyring/legacy_test.go +++ b/crypto/keyring/legacy_test.go @@ -1,7 +1,6 @@ package keyring_test import ( - "io" "path/filepath" "testing" @@ -43,15 +42,4 @@ func TestLegacyKeybase(t *testing.T) { armoredInfo, err := kb.Export(keys[0].GetName()) require.NoError(t, err) require.NotEmpty(t, armoredInfo) - - importer, err := keyring.NewInfoImporter("cosmos", "memory", "", nil) - require.NoError(t, err) - err = importer.Import("test", "") - require.Error(t, err) - require.Equal(t, io.EOF, err) - require.NoError(t, importer.Import("test", armoredInfo)) - - err = importer.Import("test", armoredInfo) - require.Error(t, err) - require.Equal(t, `public key already exist in keybase`, err.Error()) } diff --git a/crypto/keys/ed25519/ed25519.go b/crypto/keys/ed25519/ed25519.go index 17368c4b12ff..7c70d573ec30 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 59cce4066ac2..27cef37f92d6 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 848dca4f47ed..227b18701306 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"` } @@ -314,10 +315,7 @@ func (m *PubKey) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthKeys - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthKeys } if (iNdEx + skippy) > l { @@ -401,10 +399,7 @@ func (m *PrivKey) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthKeys - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthKeys } if (iNdEx + skippy) > l { diff --git a/crypto/keys/internal/benchmarking/bench.go b/crypto/keys/internal/benchmarking/bench.go index a789da91f9c9..a55936d5c3c8 100644 --- a/crypto/keys/internal/benchmarking/bench.go +++ b/crypto/keys/internal/benchmarking/bench.go @@ -25,6 +25,7 @@ func (zeroReader) Read(buf []byte) (int, error) { // BenchmarkKeyGeneration benchmarks the given key generation algorithm using // a dummy reader. func BenchmarkKeyGeneration(b *testing.B, generateKey func(reader io.Reader) types.PrivKey) { + b.ReportAllocs() var zero zeroReader for i := 0; i < b.N; i++ { generateKey(zero) diff --git a/crypto/keys/multisig/keys.pb.go b/crypto/keys/multisig/keys.pb.go index 0dd70ae220bd..27ccf0fa5a27 100644 --- a/crypto/keys/multisig/keys.pb.go +++ b/crypto/keys/multisig/keys.pb.go @@ -258,10 +258,7 @@ func (m *LegacyAminoPubKey) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthKeys - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthKeys } if (iNdEx + skippy) > l { diff --git a/crypto/keys/secp256k1/bench_test.go b/crypto/keys/secp256k1/bench_test.go index 423f7a5a52de..a9f694de7552 100644 --- a/crypto/keys/secp256k1/bench_test.go +++ b/crypto/keys/secp256k1/bench_test.go @@ -9,6 +9,7 @@ import ( ) func BenchmarkKeyGeneration(b *testing.B) { + b.ReportAllocs() benchmarkKeygenWrapper := func(reader io.Reader) types.PrivKey { priv := genPrivKey(reader) return &PrivKey{Key: priv} @@ -17,11 +18,13 @@ func BenchmarkKeyGeneration(b *testing.B) { } func BenchmarkSigning(b *testing.B) { + b.ReportAllocs() priv := GenPrivKey() benchmarking.BenchmarkSigning(b, priv) } func BenchmarkVerification(b *testing.B) { + b.ReportAllocs() priv := GenPrivKey() benchmarking.BenchmarkVerification(b, priv) } diff --git a/crypto/keys/secp256k1/keys.pb.go b/crypto/keys/secp256k1/keys.pb.go index 6dd45721bf4b..afcbab54b05d 100644 --- a/crypto/keys/secp256k1/keys.pb.go +++ b/crypto/keys/secp256k1/keys.pb.go @@ -313,10 +313,7 @@ func (m *PubKey) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthKeys - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthKeys } if (iNdEx + skippy) > l { @@ -400,10 +397,7 @@ func (m *PrivKey) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthKeys - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthKeys } if (iNdEx + skippy) > l { diff --git a/crypto/keys/secp256k1/secp256k1.go b/crypto/keys/secp256k1/secp256k1.go index eebe72a45242..231a895a6103 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/keys/sr25519/keys.pb.go b/crypto/keys/sr25519/keys.pb.go new file mode 100644 index 000000000000..2b3288d30f7d --- /dev/null +++ b/crypto/keys/sr25519/keys.pb.go @@ -0,0 +1,492 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/crypto/sr25519/keys.proto + +package sr25519 + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/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 + +// PubKey defines a sr25519 public key +type PubKey struct { + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` +} + +func (m *PubKey) Reset() { *m = PubKey{} } +func (*PubKey) ProtoMessage() {} +func (*PubKey) Descriptor() ([]byte, []int) { + return fileDescriptor_daddabaf35039fbd, []int{0} +} +func (m *PubKey) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PubKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PubKey.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 *PubKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_PubKey.Merge(m, src) +} +func (m *PubKey) XXX_Size() int { + return m.Size() +} +func (m *PubKey) XXX_DiscardUnknown() { + xxx_messageInfo_PubKey.DiscardUnknown(m) +} + +var xxx_messageInfo_PubKey proto.InternalMessageInfo + +func (m *PubKey) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +// PrivKey defines a sr25519 private key. +type PrivKey struct { + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` +} + +func (m *PrivKey) Reset() { *m = PrivKey{} } +func (m *PrivKey) String() string { return proto.CompactTextString(m) } +func (*PrivKey) ProtoMessage() {} +func (*PrivKey) Descriptor() ([]byte, []int) { + return fileDescriptor_daddabaf35039fbd, []int{1} +} +func (m *PrivKey) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PrivKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PrivKey.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 *PrivKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_PrivKey.Merge(m, src) +} +func (m *PrivKey) XXX_Size() int { + return m.Size() +} +func (m *PrivKey) XXX_DiscardUnknown() { + xxx_messageInfo_PrivKey.DiscardUnknown(m) +} + +var xxx_messageInfo_PrivKey proto.InternalMessageInfo + +func (m *PrivKey) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func init() { + proto.RegisterType((*PubKey)(nil), "cosmos.crypto.sr25519.PubKey") + proto.RegisterType((*PrivKey)(nil), "cosmos.crypto.sr25519.PrivKey") +} + +func init() { proto.RegisterFile("cosmos/crypto/sr25519/keys.proto", fileDescriptor_daddabaf35039fbd) } + +var fileDescriptor_daddabaf35039fbd = []byte{ + // 183 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x48, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0x2f, 0x2e, 0x32, 0x32, 0x35, 0x35, + 0xb4, 0xd4, 0xcf, 0x4e, 0xad, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x85, 0xa8, + 0xd0, 0x83, 0xa8, 0xd0, 0x83, 0xaa, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xab, 0xd0, 0x07, + 0xb1, 0x20, 0x8a, 0x95, 0x14, 0xb8, 0xd8, 0x02, 0x4a, 0x93, 0xbc, 0x53, 0x2b, 0x85, 0x04, 0xb8, + 0x98, 0xb3, 0x53, 0x2b, 0x25, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0x40, 0x4c, 0x2b, 0x96, 0x19, + 0x0b, 0xe4, 0x19, 0x94, 0xa4, 0xb9, 0xd8, 0x03, 0x8a, 0x32, 0xcb, 0xb0, 0x2a, 0x71, 0xf2, 0x3a, + 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, + 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0x83, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, + 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0x98, 0x93, 0xc1, 0x94, 0x6e, 0x71, 0x4a, 0x36, 0xcc, 0xf5, + 0x20, 0x57, 0xc3, 0xbc, 0x90, 0xc4, 0x06, 0x76, 0x91, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x2a, + 0xf0, 0xa3, 0x3f, 0xe2, 0x00, 0x00, 0x00, +} + +func (m *PubKey) 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 *PubKey) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PubKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintKeys(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PrivKey) 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 *PrivKey) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PrivKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintKeys(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintKeys(dAtA []byte, offset int, v uint64) int { + offset -= sovKeys(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *PubKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovKeys(uint64(l)) + } + return n +} + +func (m *PrivKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovKeys(uint64(l)) + } + return n +} + +func sovKeys(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozKeys(x uint64) (n int) { + return sovKeys(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PubKey) 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 ErrIntOverflowKeys + } + 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: PubKey: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PubKey: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKeys + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKeys + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKeys(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKeys + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PrivKey) 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 ErrIntOverflowKeys + } + 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: PrivKey: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PrivKey: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKeys + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKeys + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKeys(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKeys + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipKeys(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, ErrIntOverflowKeys + } + 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, ErrIntOverflowKeys + } + 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, ErrIntOverflowKeys + } + 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, ErrInvalidLengthKeys + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupKeys + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthKeys + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthKeys = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowKeys = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupKeys = fmt.Errorf("proto: unexpected end of group") +) diff --git a/crypto/keys/sr25519/sr25519.go b/crypto/keys/sr25519/sr25519.go new file mode 100644 index 000000000000..2ad549c69e5d --- /dev/null +++ b/crypto/keys/sr25519/sr25519.go @@ -0,0 +1,144 @@ +package sr25519 + +import ( + "bytes" + "crypto/subtle" + "fmt" + + "github.com/tendermint/tendermint/crypto" + tmsr25519 "github.com/tendermint/tendermint/crypto/sr25519" + + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/errors" +) + +var _ cryptotypes.PrivKey = &PrivKey{} +var _ codec.AminoMarshaler = &PrivKey{} + +const ( + PrivKeySize = 32 + keyType = "sr25519" + PrivKeyName = "tendermint/PrivKeySr25519" + PubKeyName = "tendermint/PubKeySr25519" +) + +// Bytes returns the byte representation of the Private Key. +func (privKey *PrivKey) Bytes() []byte { + return privKey.Key +} + +// PubKey gets the corresponding public key from the private key. +func (privKey *PrivKey) PubKey() cryptotypes.PubKey { + return &PubKey{Key: tmsr25519.PrivKey(privKey.Key).PubKey().Bytes()} +} + +// Equals - you probably don't need to use this. +// Runs in constant time based on length of the +func (privKey *PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool { + return privKey.Type() == other.Type() && subtle.ConstantTimeCompare(privKey.Bytes(), other.Bytes()) == 1 +} + +func (privKey *PrivKey) Type() string { + return keyType +} + +// MarshalAmino overrides Amino binary marshalling. +func (privKey PrivKey) MarshalAmino() ([]byte, error) { + return privKey.Key, nil +} + +// UnmarshalAmino overrides Amino binary marshalling. +func (privKey *PrivKey) UnmarshalAmino(bz []byte) error { + if len(bz) != PrivKeySize { + return fmt.Errorf("invalid privkey size") + } + privKey.Key = bz + + return nil +} + +// MarshalAminoJSON overrides Amino JSON marshalling. +func (privKey PrivKey) MarshalAminoJSON() ([]byte, error) { + // When we marshal to Amino JSON, we don't marshal the "key" field itself, + // just its contents (i.e. the key bytes). + return privKey.MarshalAmino() +} + +// UnmarshalAminoJSON overrides Amino JSON marshalling. +func (privKey *PrivKey) UnmarshalAminoJSON(bz []byte) error { + return privKey.UnmarshalAmino(bz) +} + +// GenPrivKey generates a new sr25519 private key. +// It uses OS randomness in conjunction with the current global random seed +// in tendermint/libs/common to generate the private key. +func GenPrivKey() *PrivKey { + return &PrivKey{Key: tmsr25519.GenPrivKey().Bytes()} +} + +// GenPrivKeyFromSecret hashes the secret with SHA2, and uses +// that 32 byte output to create the private key. +// NOTE: secret should be the output of a KDF like bcrypt, +// if it's derived from user input. +func GenPrivKeyFromSecret(secret []byte) *PrivKey { + return &PrivKey{Key: tmsr25519.GenPrivKeyFromSecret(secret).Bytes()} +} + +//------------------------------------- + +var _ cryptotypes.PubKey = &PubKey{} +var _ codec.AminoMarshaler = &PubKey{} + +// PubKeySize is comprised of 32 bytes for one field element +// (the x-coordinate), plus one byte for the parity of the y-coordinate. +const PubKeySize = 33 + +// Address returns a Bitcoin style addresses: RIPEMD160(SHA256(pubkey)) +func (pubKey *PubKey) Address() crypto.Address { + return tmsr25519.PubKey(pubKey.Key).Address() +} + +// Bytes returns the pubkey byte format. +func (pubKey *PubKey) Bytes() []byte { + return pubKey.Key +} + +func (pubKey *PubKey) String() string { + return fmt.Sprintf("PubKeySr25519{%X}", pubKey.Key) +} + +func (pubKey *PubKey) Type() string { + return keyType +} + +func (pubKey *PubKey) Equals(other cryptotypes.PubKey) bool { + return pubKey.Type() == other.Type() && bytes.Equal(pubKey.Bytes(), other.Bytes()) +} + +// MarshalAmino overrides Amino binary marshalling. +func (pubKey PubKey) MarshalAmino() ([]byte, error) { + return pubKey.Key, nil +} + +// UnmarshalAmino overrides Amino binary marshalling. +func (pubKey *PubKey) UnmarshalAmino(bz []byte) error { + if len(bz) != PubKeySize { + return errors.Wrap(errors.ErrInvalidPubKey, "invalid pubkey size") + } + pubKey.Key = bz + + return nil +} + +// MarshalAminoJSON overrides Amino JSON marshalling. +func (pubKey PubKey) MarshalAminoJSON() ([]byte, error) { + // When we marshal to Amino JSON, we don't marshal the "key" field itself, + // just its contents (i.e. the key bytes). + return pubKey.MarshalAmino() +} + +// UnmarshalAminoJSON overrides Amino JSON marshalling. +func (pubKey *PubKey) UnmarshalAminoJSON(bz []byte) error { + return pubKey.UnmarshalAmino(bz) +} diff --git a/crypto/keys/sr25519/sr25519_nocgo.go b/crypto/keys/sr25519/sr25519_nocgo.go new file mode 100644 index 000000000000..fe3008bbdecd --- /dev/null +++ b/crypto/keys/sr25519/sr25519_nocgo.go @@ -0,0 +1,15 @@ +package sr25519 + +import ( + tmsr25519 "github.com/tendermint/tendermint/crypto/sr25519" +) + +// Sign produces a signature on the provided message. +func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) { + return tmsr25519.PrivKey(privKey.Key).Sign(msg) +} + +// VerifySignature - verifies a signature +func (pubKey *PubKey) VerifySignature(msg []byte, sigStr []byte) bool { + return tmsr25519.PubKey(pubKey.Key).VerifySignature(msg, sigStr) +} diff --git a/crypto/types/multisig.pb.go b/crypto/types/multisig.pb.go index 05b8b6977a9f..b9c907ade0a4 100644 --- a/crypto/types/multisig.pb.go +++ b/crypto/types/multisig.pb.go @@ -345,10 +345,7 @@ func (m *MultiSignature) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthMultisig - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthMultisig } if (iNdEx + skippy) > l { @@ -452,10 +449,7 @@ func (m *CompactBitArray) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthMultisig - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthMultisig } if (iNdEx + skippy) > l { diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 0c487c3ee021..ce94542714cd 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -196,7 +196,7 @@ module.exports = { [ "@vuepress/google-analytics", { - ga: "UA-51029217-12" + ga: "UA-51029217-2" } ], [ diff --git a/docs/.vuepress/enhanceApp.js b/docs/.vuepress/enhanceApp.js new file mode 100644 index 000000000000..79974546e9b2 --- /dev/null +++ b/docs/.vuepress/enhanceApp.js @@ -0,0 +1,6 @@ +export default ({ router }) => { + router.addRoutes([ + { path: '/master/spec/*', redirect: '/master/modules/' }, + { path: '/master/spec/governance/', redirect: '/master/modules/gov/' }, + ]) +} \ No newline at end of file diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 54cc9c89e4f7..19e6d6e2db49 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -53,6 +53,7 @@ Read about the [PROCESS](./PROCESS.md). - [ADR 023: Protocol Buffer Naming and Versioning](./adr-023-protobuf-naming.md) - [ADR 026: IBC Client Recovery Mechanisms](./adr-026-ibc-client-recovery-mechanisms.md) - [ADR 029: Fee Grant Module](./adr-029-fee-grant-module.md) +- [ADR 030: Message Authorization Module](architecture/adr-030-authz-module.md) - [ADR 031: Protobuf Msg Services](./adr-031-msg-service.md) ### Proposed @@ -72,4 +73,5 @@ Read about the [PROCESS](./PROCESS.md). - [ADR 028: Public Key Addresses](./adr-028-public-key-addresses.md) - [ADR 032: Typed Events](./adr-032-typed-events.md) - [ADR 035: Rosetta API Support](./adr-035-rosetta-api-support.md) -- [ADR 037: Governance Split Votes](./adr-037-gov-split-vote.md) \ No newline at end of file +- [ADR 037: Governance Split Votes](./adr-037-gov-split-vote.md) +- [ADR 038: State Listening](./adr-038-state-listening.md) \ No newline at end of file diff --git a/docs/architecture/adr-024-coin-metadata.md b/docs/architecture/adr-024-coin-metadata.md index 4f266fa0ec8c..5195a9c40008 100644 --- a/docs/architecture/adr-024-coin-metadata.md +++ b/docs/architecture/adr-024-coin-metadata.md @@ -34,7 +34,7 @@ UX and remove the requirement for making any assumptions on the unit of denomina The `x/bank` module will be updated to store and index metadata by `denom`, specifically the "base" or smallest unit -- the unit the Cosmos SDK state-machine works with. -Metadata may also include a non-zero length list of denominations. Each entry containts the name of +Metadata may also include a non-zero length list of denominations. Each entry contains the name of the denomination `denom`, the exponent to the base and a list of aliases. An entry is to be interpreted as `1 denom = 10^exponent base_denom` (e.g. `1 ETH = 10^18 wei` and `1 uatom = 10^0 uatom`). @@ -92,6 +92,7 @@ As an example, the ATOM's metadata can be defined as follows: ``` Given the above metadata, a client may infer the following things: + - 4.3atom = 4.3 * (10^6) = 4,300,000uatom - The string "atom" can be used as a display name in a list of tokens. - The balance 4300000 can be displayed as 4,300,000uatom or 4,300matom or 4.3atom. diff --git a/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md index dc34bbf33e9d..66eeaa13ea3f 100644 --- a/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md +++ b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md @@ -4,10 +4,11 @@ - 2020/06/23: Initial version - 2020/08/06: Revisions per review & to reference version +- 2021/01/15: Revision to support substitute clients for unfreezing ## Status -*Proposed* +*Accepted* ## Context @@ -36,18 +37,22 @@ We elect not to deal with chains which have actually halted, which is necessaril 1. `allow_governance_override_after_expiry` (boolean, default false) 1. Require Tendermint light clients (ICS 07) to expose the following additional internal query functions 1. `Expired() boolean`, which returns whether or not the client has passed the trusting period since the last update (in which case no headers can be validated) -1. Require Tendermint light clients (ICS 07) to expose the following additional state mutation functions - 1. `Unfreeze()`, which unfreezes a light client after misbehaviour and clears any frozen height previously set 1. Require Tendermint light clients (ICS 07) & solo machine clients (ICS 06) to be created with the following additional flags 1. `allow_governance_override_after_misbehaviour` (boolean, default false) +1. Require Tendermint light clients (ICS 07) to expose the following additional state mutation functions + 1. `Unfreeze()`, which unfreezes a light client after misbehaviour and clears any frozen height previously set 1. Add a new governance proposal type, `ClientUpdateProposal`, in the `x/ibc` module - 1. Extend the base `Proposal` with a client identifier (`string`) and a header (`bytes`, encoded in a client-type-specific format) - 1. If this governance proposal passes, the client is updated with the provided header, if and only if: + 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, 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) 1. `allow_governance_override_after_misbehaviour` is true and the client has been frozen (`Frozen()` returns true) 1. In this case, additionally, the client is unfrozen by calling `Unfreeze()` -Note additionally that the header submitted by governance must be new enough that it will be possible to update the light client after the new header is inserted into the client state (which will only happen after the governance proposal has passed). + +Note that clients frozen due to misbehaviour must wait for the evidence to expire to avoid becoming refrozen. This ADR does not address planned upgrades, which are handled separately as per the [specification](https://github.com/cosmos/ics/tree/master/spec/ics-007-tendermint-client#upgrades). @@ -58,11 +63,13 @@ This ADR does not address planned upgrades, which are handled separately as per - Establishes a mechanism for client recovery in the case of expiry - Establishes a mechanism for client recovery in the case of misbehaviour - Clients can elect to disallow this recovery mechanism if they do not wish to allow for it +- Constructing an ClientUpdate Proposal is as difficult as creating a new client ### Negative - Additional complexity in client creation which must be understood by the user -- Governance participants must pick a new header, which is a bit different from their usual tasks +- Coping state of the substitute adds complexity +- Governance participants must vote on a substitute client ### Neutral diff --git a/docs/architecture/adr-028-public-key-addresses.md b/docs/architecture/adr-028-public-key-addresses.md index 00c6dddc0bdb..bacaec79cb7d 100644 --- a/docs/architecture/adr-028-public-key-addresses.md +++ b/docs/architecture/adr-028-public-key-addresses.md @@ -3,6 +3,7 @@ ## Changelog - 2020/08/18: Initial version +- 2021/01/15: Analysis and algorithm update ## Status @@ -10,42 +11,81 @@ Proposed ## Abstract -This ADR defines a canonical 20-byte address format for new public key algorithms, multisig public keys, and module -accounts using string prefixes. +This ADR defines an address format for all addressable SDK accounts. That includes: new public key algorithms, multisig public keys, and module accounts. ## Context Issue [\#3685](https://github.com/cosmos/cosmos-sdk/issues/3685) identified that public key -address spaces are currently overlapping. One initial proposal was extending the address length and -adding prefixes for different types of addresses. +address spaces are currently overlapping. We confirmed that it significantly decreases security of Cosmos SDK. + + +### Problem + +An attacker can control an input for an address generation function. This leads to a birthday attack, which significantly decreases the security space. +To overcome this, we need to separate the inputs for different kind of account types: +a security break of one account type shouldn't impact the security of other account types. + + +### Initial proposals + +One initial proposal was extending the address length and +adding prefixes for different types of addresses. @ethanfrey explained an alternate approach originally used in https://github.com/iov-one/weave: > I spent quite a bit of time thinking about this issue while building weave... The other cosmos Sdk. - > Basically I define a condition to be a type and format as human readable string with some binary data appended. This condition is hashed into an Address (again at 20 bytes). The use of this prefix makes it impossible to find a preimage for a given address with a different condition (eg ed25519 vs secp256k1). - > This is explained in depth here https://weave.readthedocs.io/en/latest/design/permissions.html - > And the code is here, look mainly at the top where we process conditions. https://github.com/iov-one/weave/blob/master/conditions.go And explained how this approach should be sufficiently collision resistant: -> Yeah, AFAIK, 20 bytes should be collision resistance when the preimages are unique and not malleable. A space of 2^160 would expect some collision to be likely around 2^80 elements (birthday paradox). And if you want to find a collision for some existing element in the database, it is still 2^160. 2^80 only is if all these elements are written to state. +> Yeah, AFAIK, 20 bytes should be collision resistance when the preimages are unique and not malleable. A space of 2^160 would expect some collision to be likely around 2^80 elements (birthday paradox). And if you want to find a collision for some existing element in the database, it is still 2^160. 2^80 only is if all these elements are written to state. > The good example you brought up was eg. a public key bytes being a valid public key on two algorithms supported by the codec. Meaning if either was broken, you would break accounts even if they were secured with the safer variant. This is only as the issue when no differentiating type info is present in the preimage (before hashing into an address). - > I would like to hear an argument if the 20 bytes space is an actual issue for security, as I would be happy to increase my address sizes in weave. I just figured cosmos and ethereum and bitcoin all use 20 bytes, it should be good enough. And the arguments above which made me feel it was secure. But I have not done a deeper analysis. -In discussions in [\#5694](https://github.com/cosmos/cosmos-sdk/issues/5694), we agreed to go with an -approach similar to this where essentially we take the first 20 bytes of the `sha256` hash of -the key type concatenated with the key bytes, summarized as `Sha256(KeyTypePrefix || Keybytes)[:20]`. +This led to the first proposal (which we proved to be not good enough): +we concatenate a key type with a public key, hash it and take the first 20 bytes of that hash, summarized as `sha256(keyTypePrefix || keybytes)[:20]`. + + +### Review and Discussions + +In [\#5694](https://github.com/cosmos/cosmos-sdk/issues/5694) we discussed various solutions. +We agreed that 20 bytes it's not future proof, and extending the address length is the only way to allow addresses of different types, various signature types, etc. +This disqualifies the initial proposal. + +In the issue we discussed various modifications: ++ Choice of the hash function. ++ Move the prefix out of the hash function: `keyTypePrefix + sha256(keybytes)[:20]` [post-hash-prefix-proposal]. ++ Use double hashing: `sha256(keyTypePrefix + sha256(keybytes)[:20])`. ++ Increase to keybytes hash slice from 20 byte to 32 or 40 bytes. We concluded that 32 bytes, produced by a good hash functions is future secure. + +### Requirements + ++ Support currently used tools - we don't want to break an ecosystem, or add a long adaptation period. Ref: https://github.com/cosmos/cosmos-sdk/issues/8041 ++ Try to keep the address length small - addresses are widely used in state, both as part of a key and object value. + + +### Scope + +This ADR only defines a process for the generation of address bytes. For end-user interactions with addresses (through the API, or CLI, etc.), we still use bech32 to format these addresses as strings. This ADR doesn't change that. +Using bech32 for string encoding gives us support for checksum error codes and handling of user typos. + ## Decision +We define the following account types, for which we define the address function: + +1. simple accounts: represented by a regular public key (ie: secp256k1, sr25519) +2. naive multisig: accounts composed by other addressable objects (ie: naive multisig) +3. composed accounts with a native address key (ie: bls, group module accounts) +4. module accounts: basically any accounts which cannot sign transactions and which are managed internally by modules + + ### Legacy Public Key Addresses Don't Change -`secp256k1` and multisig public keys are currently in use in existing Cosmos SDK zones. They use the following -address formats: +Currently (Jan 2021), the only officially supported SDK user accounts are `secp256k1` basic accounts and legacy amino multisig. +They are used in existing Cosmos SDK zones. They use the following address formats: - secp256k1: `ripemd160(sha256(pk_bytes))[:20]` - legacy amino multisig: `sha256(aminoCdc.Marshal(pk))[:20]` @@ -56,113 +96,233 @@ The current multisig public keys use amino serialization to generate the address those public keys and their address formatting, and call them "legacy amino" multisig public keys in protobuf. We will also create multisig public keys without amino addresses to be described below. +### Hash Function Choice -### Canonical Address Format +As in other parts of the Cosmos SDK, we will use `sha256`. -We have three types of accounts we would like to create addresses for in the future: -- regular public key addresses for new signature algorithms (ex. `sr25519`). -- public key addresses for multisig public keys that don't use amino encoding -- module accounts: basically any accounts which cannot sign transactions and -which are managed internally by modules +### Basic Address -To address all of these use cases we propose the following basic `AddressHash` function, -based on the discussions in [\#5694](https://github.com/cosmos/cosmos-sdk/issues/5694): +We start with defining a base hash algorithm for generating addresses. Notably, it's used for accounts represented by a single key pair. For each public key schema we have to have an associated `typ` string, which we discuss in a section below. `hash` is the cryptographic hash function defined in the previous section. ```go -func AddressHash(prefix string, contents []byte) []byte { - preImage := []byte(prefix) - if len(contents) != 0 { - preImage = append(preImage, 0) - preImage = append(preImage, contents...) - } - return sha256.Sum256(preImage)[:20] +const A_LEN = 32 + +func Hash(typ string, key []byte) []byte { + return hash(hash(typ) + key)[:A_LEN] } ``` -`AddressHash` always take a string `prefix` as a starting point which should represent the -type of public key (ex. `sr25519`) or module account being used (ex. `staking` or `group`). -For public keys, the `contents` parameter is used to specify the binary contents of the public -key. For module accounts, `contents` can be left empty (for modules which don't manage "sub-accounts"), -or can be some module-specific content to specify different pools (ex. `bonded` or `not-bonded` for `staking`) -or managed accounts (ex. different accounts managed by the `group` module). +The `+` is bytes concatenation, which doesn't use any separator. -In the `preImage`, the byte value `0` is used as the separator between `prefix` and `contents`. This is a logical -choice given that `0` is an invalid value for a string character and is commonly used as a null terminator. +This algorithm is the outcome of a consultation session with a professional cryptographer. +Motivation: this algorithm keeps the address relatively small (length of the `typ` doesn't impact the length of the final address) +and it's more secure than [post-hash-prefix-proposal] (which uses the first 20 bytes of a pubkey hash, significantly reducing the address space). +Moreover the cryptographer motivated the choice of adding `typ` in the hash to protect against a switch table attack. -### Canonical Public Key Address Prefixes +We use the `address.Hash` function for generating addresses for all accounts represented by a single key: +* simple public keys: `address.Hash(keyType, pubkey)` ++ aggregated keys (eg: BLS): `address.Hash(keyType, aggregatedPubKey)` ++ modules: `address.Hash("module", moduleName)` -All public key types will have a unique protobuf message type such as: -```proto -package cosmos.crypto.sr25519; +### Composed Addresses -message PubKey { - bytes key = 1; +For simple composed accounts (like new naive multisig), we generalize the `address.Hash`. The address is constructed by recursively creating addresses for the sub accounts, sorting the addresses and composing them into a single address. It ensures that the ordering of keys doesn't impact the resulting address. + +```go +// We don't need a PubKey interface - we need anything which is addressable. +type Addressable interface { + Address() []byte +} + +func NewComposed(typ string, subaccounts []Addressable) []byte { + addresses = map(subaccounts, \a -> LengthPrefix(a.Address())) + addresses = sort(addresses) + return address.Hash(typ, addresses[0] + ... + addresses[n]) } ``` - -All protobuf messages have unique fully qualified names, in this example `cosmos.crypto.sr25519.PubKey`. -These names are derived directly from .proto files in a standardized way and used -in other places such as the type URL in `Any`s. Since there is an easy and obvious -way to get this name for every protobuf type, we can use this message name as the -key type `prefix` when creating addresses. For all basic public keys, `contents` -should just be the raw unencoded public key bytes. -Thus the canonical address for new public key types would be `AddressHash(proto.MessageName(pk), pk.Bytes)`. +The `typ` parameter should be a schema descriptor, containing all significant attributes with deterministic serialization (eg: utf8 string). +`LengthPrefix` is a function which prepends 1 byte to the address. The value of that byte is the length of the address bits before prepending. The address must be at most 255 bits long. +We are using `LengthPrefix` to eliminate conflicts - it assures, that for 2 lists of addresses: `as = {a1, a2, ..., an}` and `bs = {b1, b2, ..., bm}` such that every `bi` and `ai` is at most 255 long, `concatenate(map(as, \a -> LengthPrefix(a))) = map(bs, \b -> LengthPrefix(b))` iff `as = bs`. + +Implementation Tip: account implementations should cache addresses. -### Multisig Addresses +#### Multisig Addresses -For new multisig public keys, we define a custom address format not based on any encoding scheme -(amino or protobuf). This avoids issues with non-determinism in the encoding scheme. It also -ensures that multisig public keys which differ simply in the ordering of keys have the same -address by sorting child public keys first. +For new multisig public keys, we define the `typ` parameter not based on any encoding scheme (amino or protobuf). This avoids issues with non-determinism in the encoding scheme. + +Example: -First we define a proto message for multisig public keys: ```proto package cosmos.crypto.multisig; message PubKey { uint32 threshold = 1; - repeated google.protobuf.Any public_keys = 2; + repeated google.protobuf.Any pubkeys = 2; } ``` -We define the following `Address()` function for this public key: - -``` +```go func (multisig PubKey) Address() { - // first gather all the addresses of each nested public key - var addresses [][]byte - for key := range multisig.Keys { - addresses = append(joinedAddresses, key.Address()) + // first gather all nested pub keys + var keys []address.Addressable // cryptotypes.PubKey implements Addressable + for _, _key := range multisig.Pubkeys { + keys = append(keys, key.GetCachedValue().(cryptotypes.PubKey)) } - // then sort them in ascending order - addresses = Sort(addresses) + // form the type from the message name (cosmos.crypto.multisig.PubKey) and the threshold joined together + prefix := fmt.Sprintf("%s/%d", proto.MessageName(multisig), multisig.Threshold) - // then concatenate them together - var joinedAddresses []byte - for addr := range addresses { - joinedAddresses := append(joinedAddresses, addr...) - } + // use the Composed function defined above + return address.NewComposed(prefix, keys) +} +``` - // form the string prefix from the message name (cosmos.crypto.multisig.PubKey) and the threshold joined together - prefix := fmt.Sprintf("%s/%d", proto.MessageName(multisig), multisig.Threshold) +#### Module Account Addresses - // use the standard AddressHash function - return AddressHash(prefix, joinedAddresses) +NOTE: this section is not finalize and it's in active discussion. + +In Basic Address section we defined a module account address as: + +``` +address.Hash("module", moduleName) +``` + +We use `"module"` as a schema type for all module derived addresses. Module accounts can have sub accounts. The derivation process has a defined order: module name, submodule key, subsubmodule key. +Module account addresses are heavily used in the SDK so it makes sense to optimize the derivation process: instead of using of using `LengthPrefix` for the module name, we use a null byte (`'\x00'`) as a separator. This works, because null byte is not a part of a valid module name. + +``` +func Module(moduleName string, key []byte) []byte{ + return Hash("module", []byte(moduleName) + 0 + key) } -``` +``` + +**Example** A lending BTC pool address would be: +``` +btcPool := address.Module("lending", btc.Addrress()}) +``` + +If we want to create an address for a module account depending on more than one key, we can concatenate them: +``` +btcAtomAMM := address.Module("amm", btc.Addrress() + atom.Address()}) +``` + +We can continue the derivation process and can create an address for a submodule account. + +``` +func Submodule(address []byte, derivationKey []byte) { + return Hash("module", address + derivationKey) +} +``` + +NOTE: if `address` is not a hash based address (with `LEN` length) then we should use `LengthPrefix`. An alternative would be to use one `Module` function, which takes a slice of keys and mapped with `LengthPrefix`. For final version we need to validate what's the most common use. + + +**Example** For a cosmwasm smart-contract address we could use the following construction: +``` +smartContractAddr := Submodule(Module("cosmwasm", smartContractsNamespace), smartContractKey) +``` + + + +### Schema Types + +A `typ` parameter used in `Hash` function SHOULD be unique for each account type. +Since all SDK account types are serialized in the state, we propose to use the protobuf message name string. + +Example: all public key types have a unique protobuf message type similar to: + +```proto +package cosmos.crypto.sr25519; + +message PubKey { + bytes key = 1; +} +``` + +All protobuf messages have unique fully qualified names, in this example `cosmos.crypto.sr25519.PubKey`. +These names are derived directly from .proto files in a standardized way and used +in other places such as the type URL in `Any`s. We can easily obtain the name using +`proto.MessageName(msg)`. + + ## Consequences +### Backwards Compatibility + +This ADR is compatible with what was committed and directly supported in the SDK repository. + ### Positive -- a simple algorithm for generating addresses for new public keys and module accounts + +- a simple algorithm for generating addresses for new public keys, complex accounts and modules +- the algorithm generalizes _native composed keys_ +- increased security and collision resistance of addresses +- the approach is extensible for future use-cases - one can use other address types, as long as they don't conflict with the address length specified here (20 or 32 bytes). +- support new account types. ### Negative + - addresses do not communicate key type, a prefixed approach would have done this +- addresses are 60% longer and will consume more storage space +- requires a refactor of KVStore store keys to handle variable length addresses ### Neutral + - protobuf message names are used as key type prefixes -## References + +## Further Discussions + +Some accounts can have a fixed name or may be constructed in other way (eg: modules). We were discussing an idea of an account with a predefined name (eg: `me.regen`), which could be used by institutions. +Without going into details, these kinds of addresses are compatible with the hash based addresses described here as long as they don't have the same length. +More specifically, any special account address must not have a length equal to 20 or 32 bytes. + + +## Appendix: Consulting session + +End of Dec 2020 we had a session with [Alan Szepieniec](https://scholar.google.be/citations?user=4LyZn8oAAAAJ&hl=en) to consult the approach presented above. + +Alan general observations: ++ we don’t need 2-preimage resistance ++ we need 32bytes address space for collision resistance ++ when an attacker can control an input for object with an address then we have a problem with birthday attack ++ there is an issue with smart-contracts for hashing ++ sha2 mining can be use to breaking address pre-image + +Hashing algorithm ++ any attack breaking blake3 will break blake2 ++ Alan is pretty confident about the current security analysis of the blake hash algorithm. It was a finalist, and the author is well known in security analysis. + + +Algorithm: ++ Alan recommends to hash the prefix: `address(pub_key) = hash(hash(key_type) + pub_key)[:32]`, main benefits: + + we are free to user arbitrary long prefix names + + we still don’t risk collisions + + switch tables ++ discussion about penalization -> about adding prefix post hash ++ Aaron asked about post hash prefixes (`address(pub_key) = key_type + hash(pub_key)`) and differences. Alan noted that this approach has longer address space and it’s stronger. + +Algorithm for complex / composed keys: ++ merging tree like addresses with same algorithm are fine + +Module addresses: Should module addresses have different size to differentiate it? ++ we will need to set a pre-image prefix for module addresse to keept them in 32-byte space: `hash(hash('module') + module_key)` ++ Aaron observation: we already need to deal with variable length (to not break secp256k1 keys). + +Discssion about arithmetic hash function for ZKP ++ Posseidon / Rescue ++ Problem: much bigger risk because we don’t know much techniques and history of crypto-analysis of arithmetic constructions. It’s still a new ground and area of active research. + +Post quantum signature size ++ Alan suggestion: Falcon: speed / size ration - very good. ++ Aaron - should we think about it? + Alan: based on early extrapolation this thing will get able to break EC cryptography in 2050 . But that’s a lot of uncertainty. But there is magic happening with recurions / linking / simulation and that can speedup the progress. + +Other ideas ++ Let’s say we use same key and two different address algorithms for 2 different use cases. Is it still safe to use it? Alan: if we want to hide the public key (which is not our use case), then it’s less secure but there are fixes. + + +### References ++ [Notes](https://hackmd.io/_NGWI4xZSbKzj1BkCqyZMw) diff --git a/docs/architecture/adr-030-authz-module.md b/docs/architecture/adr-030-authz-module.md new file mode 100644 index 000000000000..c61940fce8ea --- /dev/null +++ b/docs/architecture/adr-030-authz-module.md @@ -0,0 +1,230 @@ +# ADR 030: Authorization Module + +## Changelog + +- 2019-11-06: Initial Draft +- 2020-10-12: Updated Draft +- 2021-11-13: Accepted + +## Status + +Accepted + +## Abstract + +This ADR defines the `x/authz` module which allows accounts to grant authorizations to perform actions +on behalf of that account to other accounts. + +## Context + +The concrete use cases which motivated this module include: +- the desire to delegate the ability to vote on proposals to other accounts besides the account which one has +delegated stake +- "sub-keys" functionality, as originally proposed in [\#4480](https://github.com/cosmos/cosmos-sdk/issues/4480) which +is a term used to describe the functionality provided by this module together with +the `fee_grant` module from [ADR 029](./adr-029-fee-grant-module.md) and the [group module](https://github.com/regen-network/cosmos-modules/tree/master/incubator/group). + +The "sub-keys" functionality roughly refers to the ability for one account to grant some subset of its capabilities to +other accounts with possibly less robust, but easier to use security measures. For instance, a master account representing +an organization could grant the ability to spend small amounts of the organization's funds to individual employee accounts. +Or an individual (or group) with a multisig wallet could grant the ability to vote on proposals to any one of the member +keys. + +The current +implementation is based on work done by the [Gaian's team at Hackatom Berlin 2019](https://github.com/cosmos-gaians/cosmos-sdk/tree/hackatom/x/delegation). + +## Decision + +We will create a module named `authz` which provides functionality for +granting arbitrary privileges from one account (the _granter_) to another account (the _grantee_). Authorizations +must be granted for a particular `Msg` service methods one by one using an implementation +of `Authorization`. + +### Types + +Authorizations determine exactly what privileges are granted. They are extensible +and can be defined for any `Msg` service method even outside of the module where +the `Msg` method is defined. `Authorization`s use the new `ServiceMsg` type from +ADR 031. + +#### Authorization + +```go +type Authorization interface { + // 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. + // Returns: + // + allow: true if msg is authorized + // + updated: new Authorization instance which should overwrite the current one with new state + // + delete: true if Authorization has been exhausted and can be deleted from state + Accept(msg sdk.ServiceMsg, block abci.Header) (allow bool, updated Authorization, delete bool) +} +``` + +For example a `SendAuthorization` like this is defined for `MsgSend` that takes +a `SpendLimit` and updates it down to zero: + +```go +type SendAuthorization struct { + // SpendLimit specifies the maximum amount of tokens that can be spent + // by this authorization and will be updated as tokens are spent. If it is + // empty, there is no spend limit and any amount of coins can be spent. + SpendLimit sdk.Coins +} + +func (cap SendAuthorization) MethodName() string { + return "/cosmos.bank.v1beta1.Msg/Send" +} + +func (cap SendAuthorization) Accept(msg sdk.ServiceMsg, block abci.Header) (allow bool, updated Authorization, delete bool) { + switch req := msg.Request.(type) { + case bank.MsgSend: + left, invalid := cap.SpendLimit.SafeSub(req.Amount) + if invalid { + return false, nil, false + } + if left.IsZero() { + return true, nil, true + } + return true, SendAuthorization{SpendLimit: left}, false + } + return false, nil, false +} +``` + +A different type of capability for `MsgSend` could be implemented +using the `Authorization` interface with no need to change the underlying +`bank` module. + +### `Msg` Service + +```proto +service Msg { + // GrantAuthorization grants the provided authorization to the grantee on the granter's + // account with the provided expiration time. + rpc GrantAuthorization(MsgGrantAuthorization) returns (MsgGrantAuthorizationResponse); + + // ExecAuthorized attempts to execute the provided messages using + // authorizations granted to the grantee. Each message should have only + // one signer corresponding to the granter of the authorization. + // The grantee signing this message must have an authorization from the granter. + rpc ExecAuthorized(MsgExecAuthorized) returns (MsgExecAuthorizedResponse) + + + // RevokeAuthorization revokes any authorization corresponding to the provided method name on the + // granter's account that has been granted to the grantee. + rpc RevokeAuthorization(MsgRevokeAuthorization) returns (MsgRevokeAuthorizationResponse); +} + +message MsgGrantAuthorization{ + string granter = 1; + string grantee = 2; + google.protobuf.Any authorization = 3 [(cosmos_proto.accepts_interface) = "Authorization"]; + google.protobuf.Timestamp expiration = 4; +} + +message MsgExecAuthorized { + string grantee = 1; + repeated google.protobuf.Any msgs = 2; +} + +message MsgRevokeAuthorization{ + string granter = 1; + string grantee = 2; + string method_name = 3; +} +``` + +### Router Middleware + +The `authz` `Keeper` will expose a `DispatchActions` method which allows other modules to send `ServiceMsg`s +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. + DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, msgs []sdk.ServiceMsg) sdk.Result` +} +``` + +This allows the functionality provided by `authz` to be used for future inter-module object capabilities +permissions as described in [ADR 033](https://github.com/cosmos/cosmos-sdk/7459) + +### CLI + +#### `tx exec` Method + +When a CLI user wants to run a transaction on behalf of another account using `MsgExecAuthorized`, they +can use the `exec` method. For instance `gaiacli tx gov vote 1 yes --from --generate-only | gaiacli tx authz exec --send-as --from ` +would send a transaction like this: + +```go +MsgExecAuthorized { + Grantee: mykey, + Msgs: []sdk.SericeMsg{ + ServiceMsg { + MethodName:"/cosmos.gov.v1beta1.Msg/Vote" + Request: MsgVote { + ProposalID: 1, + Voter: cosmos3thsdgh983egh823 + Option: Yes + } + } + } +} +``` + +#### `tx grant --from ` + +This CLI command will send a `MsgGrantAuthorization` transaction. `authorization` should be encoded as +JSON on the CLI. + +#### `tx revoke --from ` + +This CLI command will send a `MsgRevokeAuthorization` transaction. + +### Built-in Authorizations + +#### `SendAuthorization` + +```proto +// SendAuthorization allows the grantee to spend up to spend_limit coins from +// the granter's account. +message SendAuthorization { + repeated cosmos.base.v1beta1.Coin spend_limit = 1; +} +``` + +#### `GenericAuthorization` + +```proto +// GenericAuthorization gives the grantee unrestricted permissions to execute +// the provide method on behalf of the granter's account. +message GenericAuthorization { + string method_name = 1; +} +``` + +## Consequences + +### Positive + +- 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 +SDK users + +### Negative + +### Neutral + +## References + +- Initial Hackatom implementation: https://github.com/cosmos-gaians/cosmos-sdk/tree/hackatom/x/delegation +- Post-Hackatom spec: https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#delegation-module +- B-Harvest subkeys spec: https://github.com/cosmos/cosmos-sdk/issues/4480 diff --git a/docs/architecture/adr-035-rosetta-api-support.md b/docs/architecture/adr-035-rosetta-api-support.md index 2da663f5729d..b7807ce1d24b 100644 --- a/docs/architecture/adr-035-rosetta-api-support.md +++ b/docs/architecture/adr-035-rosetta-api-support.md @@ -5,6 +5,7 @@ - Jonathan Gimeno (@jgimeno) - David Grierson (@senormonito) - Alessio Treglia (@alessio) +- Frojdy Dymylja (@fdymylja) ## Context @@ -35,9 +36,11 @@ We will achieve these delivering on these principles by the following: 1. There will be an external repo called [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway) for the implementation of the core Rosetta API features, particularly: - a. The types and interfaces. This separates design from implementation detail. - b. Some core implementations: specifically, the `Service` functionality as this is independent of the Cosmos SDK version. -2. Due to differences between the Cosmos release series, each series will have its own specific API implementations of `Network` struct and `Adapter` interface. + a. The types and interfaces (`Client`, `OfflineClient`...), this separates design from implementation detail. + b. The `Server` functionality as this is independent of the Cosmos SDK version. + c. The `Online/OfflineNetwork`, which is not exported, and implements the rosetta API using the `Client` interface to query the node, build tx and so on. + d. The `errors` package to extend rosetta errors. +2. Due to differences between the Cosmos release series, each series will have its own specific implementation of `Client` interface. 3. There will be two options for starting an API service in applications: a. API shares the application process b. API-specific process. @@ -49,143 +52,130 @@ We will achieve these delivering on these principles by the following: As section will describe the proposed external library, including the service implementation, plus the defined types and interfaces. -#### Service +#### Server -`Service` is a simple `struct` that is started and listens to the port specified in the options. This is meant to be used across all the Cosmos SDK versions that are actively supported. +`Server` is a simple `struct` that is started and listens to the port specified in the settings. This is meant to be used across all the Cosmos SDK versions that are actively supported. The constructor follows: -`func New(options Options, network Network) (*Service, error)` - -#### Types - -`Service` accepts an `Options` `struct` that holds service configuration values, such as the port the service would be listening to: - -```golang -type Options struct { - ListenAddress string -} -``` - -The `Network` type holds network-specific properties (i.e. configuration values) and adapters. Pre-configured concrete types will be available for each Cosmos SDK release. Applications can also create their own custom types. - -```golang -type Network struct { - Properties rosetta.NetworkProperties - Adapter rosetta.Adapter -} -``` - -A `NetworkProperties` `struct` comprises basic values that are required by a Rosetta API `Service`: - -```golang -type NetworkProperties struct { - // Mandatory properties - Blockchain string - Network string - SupportedOperations []string +`func NewServer(settings Settings) (Server, error)` + +`Settings`, which are used to construct a new server, are the following: +```go +// Settings define the rosetta server settings +type Settings struct { + // Network contains the information regarding the network + Network *types.NetworkIdentifier + // Client is the online API handler + Client crgtypes.Client + // Listen is the address the handler will listen at + Listen string + // Offline defines if the rosetta service should be exposed in offline mode + Offline bool + // Retries is the number of readiness checks that will be attempted when instantiating the handler + // valid only for online API + Retries int + // RetryWait is the time that will be waited between retries + RetryWait time.Duration } ``` -Rosetta API services use `Blockchain` and `Network` as identifiers, e.g. the developers of _gaia_, the application that powers the Cosmos Hub, may want to set those to `Cosmos Hub` and `cosmos-hub-3` respectively. - -`SupportedOperations` contains the transaction types that are supported by the library. At the present time, -only `cosmos-sdk/MsgSend` is supported in Launchpad. Additional operations will be added in due time. - -For Launchpad we will map the amino type name to the operation supported, in Stargate we will use the protoc one. - -#### Interfaces - -Every SDK version uses a different format to connect (rpc, gRpc, etc), we have abstracted this in what is called the -Adapter. This is an interface that defines the methods an adapter implementation must provide in order to be used -in the `Network` interface. - -Each Cosmos SDK release series will have their own Adapter implementations. -Developers can implement their own custom adapters as required. - -```golang -type Adapter interface { - DataAPI - ConstructionAPI -} +#### Types -type DataAPI interface { - server.NetworkAPIServicer - server.AccountAPIServicer - server.MempoolAPIServicer - server.BlockAPIServicer - server.ConstructionAPIServicer +Package types uses a mixture of rosetta types and custom defined type wrappers, that the client must parse and return while executing operations. + + +##### Interfaces + +Every SDK version uses a different format to connect (rpc, gRPC, etc), query and build transactions, we have abstracted this in what is the `Client` interface. +The client uses rosetta types, whilst the `Online/OfflineNetwork` takes care of returning correctly parsed rosetta responses and errors. + +Each Cosmos SDK release series will have their own `Client` implementations. +Developers can implement their own custom `Client`s as required. + +```go +// Client defines the API the client implementation should provide. +type Client interface { + // Needed if the client needs to perform some action before connecting. + Bootstrap() error + // Ready checks if the servicer constraints for queries are satisfied + // for example the node might still not be ready, it's useful in process + // when the rosetta instance might come up before the node itself + // the servicer must return nil if the node is ready + Ready() error + + // Data API + + // Balances fetches the balance of the given address + // if height is not nil, then the balance will be displayed + // at the provided height, otherwise last block balance will be returned + Balances(ctx context.Context, addr string, height *int64) ([]*types.Amount, error) + // BlockByHashAlt gets a block and its transaction at the provided height + BlockByHash(ctx context.Context, hash string) (BlockResponse, error) + // BlockByHeightAlt gets a block given its height, if height is nil then last block is returned + BlockByHeight(ctx context.Context, height *int64) (BlockResponse, error) + // BlockTransactionsByHash gets the block, parent block and transactions + // given the block hash. + BlockTransactionsByHash(ctx context.Context, hash string) (BlockTransactionsResponse, error) + // BlockTransactionsByHash gets the block, parent block and transactions + // given the block hash. + BlockTransactionsByHeight(ctx context.Context, height *int64) (BlockTransactionsResponse, error) + // GetTx gets a transaction given its hash + GetTx(ctx context.Context, hash string) (*types.Transaction, error) + // GetUnconfirmedTx gets an unconfirmed Tx given its hash + // NOTE(fdymylja): NOT IMPLEMENTED YET! + GetUnconfirmedTx(ctx context.Context, hash string) (*types.Transaction, error) + // Mempool returns the list of the current non confirmed transactions + Mempool(ctx context.Context) ([]*types.TransactionIdentifier, error) + // Peers gets the peers currently connected to the node + Peers(ctx context.Context) ([]*types.Peer, error) + // Status returns the node status, such as sync data, version etc + Status(ctx context.Context) (*types.SyncStatus, error) + + // Construction API + + // PostTx posts txBytes to the node and returns the transaction identifier plus metadata related + // to the transaction itself. + PostTx(txBytes []byte) (res *types.TransactionIdentifier, meta map[string]interface{}, err error) + // ConstructionMetadataFromOptions + ConstructionMetadataFromOptions(ctx context.Context, options map[string]interface{}) (meta map[string]interface{}, err error) + OfflineClient } -type ConstructionAPI interface { - server.ConstructionAPIServicer +// OfflineClient defines the functionalities supported without having access to the node +type OfflineClient interface { + NetworkInformationProvider + // SignedTx returns the signed transaction given the tx bytes (msgs) plus the signatures + SignedTx(ctx context.Context, txBytes []byte, sigs []*types.Signature) (signedTxBytes []byte, err error) + // TxOperationsAndSignersAccountIdentifiers returns the operations related to a transaction and the account + // identifiers if the transaction is signed + TxOperationsAndSignersAccountIdentifiers(signed bool, hexBytes []byte) (ops []*types.Operation, signers []*types.AccountIdentifier, err error) + // ConstructionPayload returns the construction payload given the request + ConstructionPayload(ctx context.Context, req *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error) + // PreprocessOperationsToOptions returns the options given the preprocess operations + PreprocessOperationsToOptions(ctx context.Context, req *types.ConstructionPreprocessRequest) (options map[string]interface{}, err error) + // AccountIdentifierFromPublicKey returns the account identifier given the public key + AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error) } ``` -Example in pseudo-code of an Adapter interface: - -```golang -type SomeAdapter struct { - cosmosClient client - tendermintClient client -} - -func NewSomeAdapter(cosmosClient client, tendermintClient client) rosetta.Adapter { - return &SomeAdapter{cosmosClient: cosmosClient, tendermintClient: tendermintClient} -} - -func (s SomeAdapter) NetworkStatus(ctx context.Context, request *types.NetworkRequest) (*types.NetworkStatusResponse, *types.Error) { - resp := s.tendermintClient.CallStatus() - // ... Parse status Response - // build NetworkStatusResponse - return networkStatusResp, nil -} - -func (s SomeAdapter) AccountBalance(ctx context.Context, request *types.AccountBalanceRequest) (*types.AccountBalanceResponse, *types.Error) { - resp := s.cosmosClient.Account() - // ... Parse cosmos specific account response - // build AccountBalanceResponse - return AccountBalanceResponse, nil -} - -// And we repeat for all the methods defined in the interface. -``` +### 2. Cosmos SDK Implementation -For further information about the `Servicer` interfaces, please refer to the [Coinbase's rosetta-sdk-go's documentation](https://pkg.go.dev/github.com/coinbase/rosetta-sdk-go@v0.5.9/server). +The cosmos sdk implementation, based on version, takes care of satisfying the `Client` interface. +In Stargate, Launchpad and 0.37, we have introduced the concept of rosetta.Msg, this message is not in the shared repository as the sdk.Msg type differs between cosmos-sdk versions. -### 2. Cosmos SDK Implementation +The rosetta.Msg interface follows: -As described, each Cosmos SDK release series will have version specific implementations of `Network` and `Adapter`, as -well as a `NewNetwork` constructor. - -Due to separation of interface and implementation, application developers have the option to override as needed, -using this code as reference. - -```golang -// NewNetwork returns the default application configuration. -func NewNetwork(options Options) service.Network { - cosmosClient := cosmos.NewClient(fmt.Sprintf("http://%s", options.CosmosEndpoint)) - tendermintClient := tendermint.NewClient(fmt.Sprintf("http://%s", options.TendermintEndpoint)) - - return service.Network{ - Properties: rosetta.NetworkProperties{ - Blockchain: options.Blockchain, - Network: options.Network, - SupportedOperations: []string{OperationTransfer}, - }, - Adapter: newAdapter( - cosmosClient, - tendermintClient, - properties{ - Blockchain: options.Blockchain, - Network: options.Network, - OfflineMode: options.OfflineMode, - }, - ), - } +```go +// Msg represents a cosmos-sdk message that can be converted from and to a rosetta operation. +type Msg interface { + sdk.Msg + ToOperations(withStatus, hasError bool) []*types.Operation + FromOperations(ops []*types.Operation) (sdk.Msg, error) } ``` +Hence developers who want to extend the rosetta set of supported operations just need to extend their module's sdk.Msgs with the `ToOperations` and `FromOperations` methods. ### 3. API service invocation As stated at the start, application developers will have two methods for invocation of the Rosetta API service: @@ -195,67 +185,13 @@ As stated at the start, application developers will have two methods for invocat #### Shared Process (Only Stargate) -Rosetta API service could run within the same execution process as the application. New configuration option and -command line flags would be provided to support this: - -```golang - if config.Rosetta.Enable { - .... - get contecxt, flags, etc - ... - - h, err := service.New( - service.Options{ListenAddress: config.Rosetta.ListenAddress}, - rosetta.NewNetwork(cdc, options), - ) - if err != nil { - } - - ... - - go func() { - if err := h.Start(config); err != nil { - errCh <- err - } - }() - } +Rosetta API service could run within the same execution process as the application. This would be enabled via app.toml settings, and if gRPC is not enabled the rosetta instance would be spinned in offline mode (tx building capabilities only). -``` #### Separate API service -Client application developers can write a new command to launch a Rosetta API server as a separate process too: - -```golang -func RosettaCommand(cdc *codec.Codec) *cobra.Command { - - ... - cmd := &cobra.Command{ - Use: "rosetta", - .... - - RunE: func(cmd *cobra.Command, args []string) error { - .... - get contecxt, flags, etc - ... - - h, err := service.New( - service.Options{Endpoint: endpoint}, - rosetta.NewNetwork(cdc, options), - ) - if err != nil { - return err - } - - ... - - h.Start() - } - } - ... +Client application developers can write a new command to launch a Rosetta API server as a separate process too, using the rosetta command contained in the `/server/rosetta` package. Construction of the command depends on cosmos sdk version. Examples can be found inside `simd` for stargate, and `contrib/rosetta/simapp` for other release series. -} -``` ## Status diff --git a/docs/architecture/adr-037-gov-split-vote.md b/docs/architecture/adr-037-gov-split-vote.md index af9cd8d6f6a9..3dfa59cca912 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/architecture/adr-038-state-listening.md b/docs/architecture/adr-038-state-listening.md new file mode 100644 index 000000000000..cd78e72e2caa --- /dev/null +++ b/docs/architecture/adr-038-state-listening.md @@ -0,0 +1,612 @@ +# ADR 038: KVStore state listening + +## Changelog + +- 11/23/2020: Initial draft + +## Status + +Proposed + +## Abstract + +This ADR defines a set of changes to enable listening to state changes of individual KVStores and exposing these data to consumers. + +## Context + +Currently, KVStore data can be remotely accessed through [Queries](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/messages-and-queries.md#queries) +which proceed either through Tendermint and the ABCI, or through the gRPC server. +In addition to these request/response queries, it would be beneficial to have a means of listening to state changes as they occur in real time. + +## Decision + +We will modify the `MultiStore` interface and its concrete (`rootmulti` and `cachemulti`) implementations and introduce a new `listenkv.Store` to allow listening to state changes in underlying KVStores. +We will also introduce the tooling for writing these state changes out to files and configuring this service. + +### Listening interface + +In a new file, `store/types/listening.go`, we will create a `WriteListener` interface for streaming out state changes from a KVStore. + +```go +// WriteListener interface for streaming data out from a listenkv.Store +type WriteListener interface { + // if value is nil then it was deleted + // storeKey indicates the source KVStore, to facilitate using the the same WriteListener across separate KVStores + // set bool indicates if it was a set; true: set, false: delete + OnWrite(storeKey types.StoreKey, set bool, key []byte, value []byte) +} +``` + +### Listener type + +We will create a concrete implementation of the `WriteListener` interface in `store/types/listening.go`, that writes out protobuf +encoded KV pairs to an underlying `io.Writer`. + +This will include defining a simple protobuf type for the KV pairs. In addition to the key and value fields this message +will include the StoreKey for the originating KVStore so that we can write out from separate KVStores to the same stream/file +and determine the source of each KV pair. + +```protobuf +message StoreKVPair { + optional string store_key = 1; // the store key for the KVStore this pair originates from + required bool set = 2; // true indicates a set operation, false indicates a delete operation + required bytes key = 3; + required bytes value = 4; +} +``` + +```go +// StoreKVPairWriteListener is used to configure listening to a KVStore by writing out length-prefixed +// protobuf encoded StoreKVPairs to an underlying io.Writer +type StoreKVPairWriteListener struct { + writer io.Writer + marshaller codec.BinaryMarshaler +} + +// NewStoreKVPairWriteListener wraps creates a StoreKVPairWriteListener with a provdied io.Writer and codec.BinaryMarshaler +func NewStoreKVPairWriteListener(w io.Writer, m codec.BinaryMarshaler) *StoreKVPairWriteListener { + return &StoreKVPairWriteListener{ + writer: w, + marshaller: m, + } +} + +// OnWrite satisfies the WriteListener interface by writing length-prefixed protobuf encoded StoreKVPairs +func (wl *StoreKVPairWriteListener) OnWrite(storeKey types.StoreKey, set bool, key []byte, value []byte) { + kvPair := new(types.StoreKVPair) + kvPair.StoreKey = storeKey.Name() + kvPair.Set = set + kvPair.Key = key + kvPair.Value = value + if by, err := wl.marshaller.MarshalBinaryLengthPrefixed(kvPair); err == nil { + wl.writer.Write(by) + } +} +``` + +### ListenKVStore + +We will create a new `Store` type `listenkv.Store` that the `MultiStore` wraps around a `KVStore` to enable state listening. +We can configure the `Store` with a set of `WriteListener`s which stream the output to specific destinations. + +```go +// Store implements the KVStore interface with listening enabled. +// Operations are traced on each core KVStore call and written to any of the +// underlying listeners with the proper key and operation permissions +type Store struct { + parent types.KVStore + listeners []types.WriteListener + parentStoreKey types.StoreKey +} + +// NewStore returns a reference to a new traceKVStore given a parent +// KVStore implementation and a buffered writer. +func NewStore(parent types.KVStore, psk types.StoreKey, listeners []types.WriteListener) *Store { + return &Store{parent: parent, listeners: listeners, parentStoreKey: psk} +} + +// Set implements the KVStore interface. It traces a write operation and +// delegates the Set call to the parent KVStore. +func (s *Store) Set(key []byte, value []byte) { + types.AssertValidKey(key) + s.parent.Set(key, value) + s.onWrite(true, key, value) +} + +// Delete implements the KVStore interface. It traces a write operation and +// delegates the Delete call to the parent KVStore. +func (s *Store) Delete(key []byte) { + s.parent.Delete(key) + s.onWrite(false, key, nil) +} + +// onWrite writes a KVStore operation to all of the WriteListeners +func (s *Store) onWrite(set bool, key, value []byte) { + for _, l := range s.listeners { + l.OnWrite(s.parentStoreKey, set, key, value) + } +} +``` + +### MultiStore interface updates + +We will update the `MultiStore` interface to allow us to wrap a set of listeners around a specific `KVStore`. +Additionally, we will update the `CacheWrap` and `CacheWrapper` interfaces to enable listening in the caching layer. + +```go +type MultiStore interface { + ... + + // ListeningEnabled returns if listening is enabled for the KVStore belonging the provided StoreKey + ListeningEnabled(key StoreKey) bool + + // SetListeners sets the WriteListeners for the KVStore belonging to the provided StoreKey + // It appends the listeners to a current set, if one already exists + SetListeners(key StoreKey, listeners []WriteListener) +} +``` + +```go +type CacheWrap interface { + ... + + // CacheWrapWithListeners recursively wraps again with listening enabled + CacheWrapWithListeners(storeKey types.StoreKey, listeners []WriteListener) CacheWrap +} + +type CacheWrapper interface { + ... + + // CacheWrapWithListeners recursively wraps again with listening enabled + CacheWrapWithListeners(storeKey types.StoreKey, listeners []WriteListener) CacheWrap +} +``` + +### MultiStore implementation updates + +We will modify all of the `Store` and `MultiStore` implementations to satisfy these new interfaces, and adjust the `rootmulti` `GetKVStore` method +to wrap the returned `KVStore` with a `listenkv.Store` if listening is turned on for that `Store`. + +```go +func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { + store := rs.stores[key].(types.KVStore) + + if rs.TracingEnabled() { + store = tracekv.NewStore(store, rs.traceWriter, rs.traceContext) + } + if rs.ListeningEnabled(key) { + store = listenkv.NewStore(key, store, rs.listeners[key]) + } + + return store +} +``` + +We will also adjust the `cachemulti` constructor methods and the `rootmulti` `CacheMultiStore` method to forward the listeners +to and enable listening in the cache layer. + +```go +func (rs *Store) CacheMultiStore() types.CacheMultiStore { + stores := make(map[types.StoreKey]types.CacheWrapper) + for k, v := range rs.stores { + stores[k] = v + } + return cachemulti.NewStore(rs.db, stores, rs.keysByName, rs.traceWriter, rs.traceContext, rs.listeners) +} +``` + +### Exposing the data + +We will introduce a new `StreamingService` interface for exposing `WriteListener` data streams to external consumers. + +```go +// Hook interface used to hook into the ABCI message processing of the BaseApp +type Hook interface { + ListenBeginBlock(ctx sdk.Context, req abci.RequestBeginBlock, res abci.ResponseBeginBlock) // update the streaming service with the latest BeginBlock messages + ListenEndBlock(ctx sdk.Context, req abci.RequestEndBlock, res abci.ResponseEndBlock) // update the steaming service with the latest EndBlock messages + ListenDeliverTx(ctx sdk.Context, req abci.RequestDeliverTx, res abci.ResponseDeliverTx) // update the steaming service with the latest DeliverTx messages +} + +// StreamingService interface for registering WriteListeners with the BaseApp and updating the service with the ABCI messages using the hooks +type StreamingService interface { + Stream(wg *sync.WaitGroup, quitChan <-chan struct{}) // streaming service loop, awaits kv pairs and writes them to some destination stream or file + Listeners() map[sdk.StoreKey][]storeTypes.WriteListener // returns the streaming service's listeners for the BaseApp to register + Hook +} +``` + +#### Writing state changes to files + +We will introduce an implementation of `StreamingService` which writes state changes out to files as length-prefixed protobuf encoded `StoreKVPair`s. +This service uses the same `StoreKVPairWriteListener` for every KVStore, writing all the KV pairs from every KVStore +out to the same files, relying on the `StoreKey` field in the `StoreKVPair` protobuf message to later distinguish the source for each pair. + +The file naming schema is as such: +* After every `BeginBlock` request a new file is created with the name `block-{N}-begin`, where N is the block number. All +subsequent state changes are written out to this file until the first `DeliverTx` request is received. At the head of these files, + the length-prefixed protobuf encoded `BeginBlock` request is written, and the response is written at the tail. +* After every `DeliverTx` request a new file is created with the name `block-{N}-tx-{M}` where N is the block number and M +is the tx number in the block (i.e. 0, 1, 2...). All subsequent state changes are written out to this file until the next +`DeliverTx` request is received or an `EndBlock` request is received. At the head of these files, the length-prefixed protobuf + encoded `DeliverTx` request is written, and the response is written at the tail. +* After every `EndBlock` request a new file is created with the name `block-{N}-end`, where N is the block number. All +subsequent state changes are written out to this file until the next `BeginBlock` request is received. At the head of these files, + the length-prefixed protobuf encoded `EndBlock` request is written, and the response is written at the tail. + +```go +// FileStreamingService is a concrete implementation of StreamingService that writes state changes out to a file +type FileStreamingService struct { + listeners map[sdk.StoreKey][]storeTypes.WriteListener // the listeners that will be initialized with BaseApp + srcChan <-chan []byte // the channel that all of the WriteListeners write their data out to + filePrefix string // optional prefix for each of the generated files + writeDir string // directory to write files into + dstFile *os.File // the current write output file + marshaller codec.BinaryMarshaler // marshaller used for re-marshalling the ABCI messages to write them out to the destination files + stateCache [][]byte // cache the protobuf binary encoded StoreKVPairs in the order they are received +} +``` + +This streaming service uses a single instance of a simple intermediate `io.Writer` as the underlying `io.Writer` for its single `StoreKVPairWriteListener`, +It collects KV pairs from every KVStore synchronously off of the same channel, caching them in the order they are received, and then writing +them out to a file generated in response to an ABCI message hook. Files are named as outlined above, with optional prefixes to avoid potential naming collisions +across separate instances. + +```go +// intermediateWriter is used so that we do not need to update the underlying io.Writer inside the StoreKVPairWriteListener +// everytime we begin writing to a new file +type intermediateWriter struct { + outChan chan <-[]byte +} + +// NewIntermediateWriter create an instance of an intermediateWriter that sends to the provided channel +func NewIntermediateWriter(outChan chan <-[]byte) *intermediateWriter { + return &intermediateWriter{ + outChan: outChan, + } +} + +// Write satisfies io.Writer +func (iw *intermediateWriter) Write(b []byte) (int, error) { + iw.outChan <- b + return len(b), nil +} + +// NewFileStreamingService creates a new FileStreamingService for the provided writeDir, (optional) filePrefix, and storeKeys +func NewFileStreamingService(writeDir, filePrefix string, storeKeys []sdk.StoreKey, m codec.BinaryMarshaler) (*FileStreamingService, error) { + listenChan := make(chan []byte, 0) + iw := NewIntermediateWriter(listenChan) + listener := listen.NewStoreKVPairWriteListener(iw, m) + listners := make(map[sdk.StoreKey][]storeTypes.WriteListener, len(storeKeys)) + // in this case, we are using the same listener for each Store + for _, key := range storeKeys { + listeners[key] = listener + } + // check that the writeDir exists and is writeable so that we can catch the error here at initialization if it is not + // we don't open a dstFile until we receive our first ABCI message + if err := fileutil.IsDirWriteable(writeDir); err != nil { + return nil, err + } + return &FileStreamingService{ + listeners: listeners, + srcChan: listenChan, + filePrefix: filePrefix, + writeDir: writeDir, + marshaller: m, + stateCache: make([][]byte, 0), + }, nil +} + +// Listeners returns the StreamingService's underlying WriteListeners, use for registering them with the BaseApp +func (fss *FileStreamingService) Listeners() map[sdk.StoreKey][]storeTypes.WriteListener { + return fss.listeners +} + +func (fss *FileStreamingService) ListenBeginBlock(ctx sdk.Context, req abci.RequestBeginBlock, res abci.ResponseBeginBlock) { + // NOTE: this could either be done synchronously or asynchronously + // create a new file with the req info according to naming schema + // write req to file + // write all state changes cached for this stage to file + // reset cache + // write res to file + // close file +} + +func (fss *FileStreamingService) ListenEndBlock(ctx sdk.Context, req abci.RequestBeginBlock, res abci.ResponseBeginBlock) { + // NOTE: this could either be done synchronously or asynchronously + // create a new file with the req info according to naming schema + // write req to file + // write all state changes cached for this stage to file + // reset cache + // write res to file + // close file +} + +func (fss *FileStreamingService) ListenDeliverTx(ctx sdk.Context, req abci.RequestDeliverTx, res abci.ResponseDeliverTx) { + // NOTE: this could either be done synchronously or asynchronously + // create a new file with the req info according to naming schema + // NOTE: if the tx failed, handle accordingly + // write req to file + // write all state changes cached for this stage to file + // reset cache + // write res to file + // close file +} + +// Stream spins up a goroutine select loop which awaits length-prefixed binary encoded KV pairs and caches them in the order they were received +func (fss *FileStreamingService) Stream(wg *sync.WaitGroup, quitChan <-chan struct{}) { + wg.Add(1) + go func() { + defer wg.Done() + for { + select { + case <-quitChan: + return + case by := <-fss.srcChan: + append(fss.stateCache, by) + } + } + }() +} +``` + +Writing to a file is the simplest approach for streaming the data out to consumers. +This approach also provides the advantages of being persistent and durable, and the files can be read directly, +or an auxiliary streaming services can read from the files and serve the data over a remote interface. + +#### Auxiliary streaming service + +We will create a separate standalone process that reads and internally queues the state as it is written out to these files +and serves the data over a gRPC API. This API will allow filtering of requested data, e.g. by block number, block/tx hash, ABCI message type, +whether a DeliverTx message failed or succeeded, etc. In addition to unary RPC endpoints this service will expose `stream` RPC endpoints for realtime subscriptions. + +#### File pruning + +Without pruning the number of files can grow indefinitely, this may need to be managed by +the developer in an application or even module-specific manner (e.g. log rotation). +The file naming schema facilitates pruning by block number and/or ABCI message. +The gRPC auxiliary streaming service introduced above will include an option to remove the files as it consumes their data. + +### Configuration + +We will provide detailed documentation on how to configure a `FileStreamingService` from within an app's `AppCreator`, +using the provided `AppOptions` and TOML configuration fields. + +#### BaseApp registration + +We will add a new method to the `BaseApp` to enable the registration of `StreamingService`s: + +```go +// RegisterStreamingService is used to register a streaming service with the BaseApp +func (app *BaseApp) RegisterHooks(s StreamingService) { + // set the listeners for each StoreKey + for key, lis := range s.Listeners() { + app.cms.SetListeners(key, lis) + } + // register the streaming service hooks within the BaseApp + // BaseApp will pass BeginBlock, DeliverTx, and EndBlock requests and responses to the streaming services to update their ABCI context using these hooks + app.hooks = append(app.hooks, s) +} +``` + +We will also modify the `BeginBlock`, `EndBlock`, and `DeliverTx` methods to pass ABCI requests and responses to any streaming service hooks registered +with the `BaseApp`. + + +```go +func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { + + ... + + // Call the streaming service hooks with the BeginBlock messages + for _ hook := range app.hooks { + hook.ListenBeginBlock(app.deliverState.ctx, req, res) + } + + return res +} +``` + +```go +func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { + + ... + + // Call the streaming service hooks with the EndBlock messages + for _, hook := range app.hooks { + hook.ListenEndBlock(app.deliverState.ctx, req, res) + } + + return res +} +``` + +```go +func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { + + ... + + gInfo, result, err := app.runTx(runTxModeDeliver, req.Tx) + if err != nil { + resultStr = "failed" + res := sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace) + // If we throw and error, be sure to still call the streaming service's hook + for _, hook := range app.hooks { + hook.ListenDeliverTx(app.deliverState.ctx, req, res) + } + return res + } + + res := abci.ResponseDeliverTx{ + GasWanted: int64(gInfo.GasWanted), // TODO: Should type accept unsigned ints? + GasUsed: int64(gInfo.GasUsed), // TODO: Should type accept unsigned ints? + Log: result.Log, + Data: result.Data, + Events: sdk.MarkEventsToIndex(result.Events, app.indexEvents), + } + + // Call the streaming service hooks with the DeliverTx messages + for _, hook := range app.hook { + hook.ListenDeliverTx(app.deliverState.ctx, req, res) + } + + return res +} +``` + +#### TOML Configuration + +We will provide standard TOML configuration options for configuring a `FileStreamingService` for specific `Store`s. +Note: the actual namespace is TBD. + +```toml +[store] + streamers = [ # if len(streamers) > 0 we are streaming + "file", + ] + +[streamers] + [streamers.file] + keys = ["list", "of", "store", "keys", "we", "want", "to", "expose", "for", "this", "streaming", "service"] + writeDir = "path to the write directory" + prefix = "optional prefix to prepend to the generated file names" +``` + +We will also provide a mapping of the TOML `store.streamers` "file" configuration option to a helper functions for constructing the specified +streaming service. In the future, as other streaming services are added, their constructors will be added here as well. + +```go +// StreamingServiceConstructor is used to construct a streaming service +type StreamingServiceConstructor func(opts servertypes.AppOptions, keys []sdk.StoreKey) (StreamingService, error) + +// StreamingServiceType enum for specifying the type of StreamingService +type StreamingServiceType int + +const ( + Unknown StreamingServiceType = iota + File + // add more in the future +) + +// NewStreamingServiceType returns the StreamingServiceType corresponding to the provided name +func NewStreamingServiceType(name string) StreamingServiceType { + switch strings.ToLower(name) { + case "file", "f": + return File + default: + return Unknown + } +} + +// String returns the string name of a StreamingServiceType +func (sst StreamingServiceType) String() string { + switch sst { + case File: + return "file" + default: + return "" + } +} + +// StreamingServiceConstructorLookupTable is a mapping of StreamingServiceTypes to StreamingServiceConstructors +var StreamingServiceConstructorLookupTable = map[StreamingServiceType]StreamingServiceConstructor{ + File: FileStreamingConstructor, +} + +// NewStreamingServiceConstructor returns the StreamingServiceConstructor corresponding to the provided name +func NewStreamingServiceConstructor(name string) (StreamingServiceConstructor, error) { + ssType := NewStreamingServiceType(name) + if ssType == Unknown { + return nil, fmt.Errorf("unrecognized streaming service name %s", name) + } + if constructor, ok := StreamingServiceConstructorLookupTable[ssType]; ok { + return constructor, nil + } + return nil, fmt.Errorf("streaming service constructor of type %s not found", ssType.String()) +} + +// FileStreamingConstructor is the StreamingServiceConstructor function for creating a FileStreamingService +func FileStreamingConstructor(opts servertypes.AppOptions, keys []sdk.StoreKey) (StreamingService, error) { + filePrefix := cast.ToString(opts.Get("streamers.file.prefix")) + fileDir := cast.ToString(opts.Get("streamers.file.writeDir")) + return streaming.NewFileStreamingService(fileDir, filePrefix, keys), nil +} +``` + +#### Example configuration + +As a demonstration, we will implement the state watching features as part of SimApp. +For example, the below is a very rudimentary integration of the state listening features into the SimApp `AppCreator` function: + + +```go +func NewSimApp( + logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, skipUpgradeHeights map[int64]bool, + homePath string, invCheckPeriod uint, encodingConfig simappparams.EncodingConfig, + appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp), +) *SimApp { + + ... + + keys := sdk.NewKVStoreKeys( + authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, + minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, + govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, + evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey, + ) + + // configure state listening capabilities using AppOptions + listeners := cast.ToStringSlice(appOpts.Get("store.streamers")) + for _, listenerName := range listeners { + // get the store keys allowed to be exposed for this streaming service/state listeners + exposeKeyStrs := cast.ToStringSlice(appOpts.Get(fmt.Sprintf("streamers.%s.keys", listenerName)) + exposeStoreKeys = make([]storeTypes.StoreKey, 0, len(exposeKeyStrs)) + for _, keyStr := range exposeKeyStrs { + if storeKey, ok := keys[keyStr]; ok { + exposeStoreKeys = append(exposeStoreKeys, storeKey) + } + } + // get the constructor for this listener name + constructor, err := baseapp.NewStreamingServiceConstructor(listenerName) + if err != nil { + tmos.Exit(err.Error()) // or continue? + } + // generate the streaming service using the constructor, appOptions, and the StoreKeys we want to expose + streamingService, err := constructor(appOpts, exposeStoreKeys) + if err != nil { + tmos.Exit(err.Error()) + } + // register the streaming service with the BaseApp + bApp.RegisterStreamingService(streamingService) + // waitgroup and quit channel for optional shutdown coordination of the streaming service + wg := new(sync.WaitGroup) + quitChan := new(chan struct{})) + // kick off the background streaming service loop + streamingService.Stream(wg, quitChan) // maybe this should be done from inside BaseApp instead? + } + + ... + + return app +} +``` + +## Consequences + +These changes will provide a means of subscribing to KVStore state changes in real time. + +### Backwards Compatibility + +- This ADR changes the `MultiStore`, `CacheWrap`, and `CacheWrapper` interfaces, implementations supporting the previous version of these interfaces will not support the new ones + +### Positive + +- Ability to listen to KVStore state changes in real time and expose these events to external consumers + +### Negative + +- Changes `MultiStore`, `CacheWrap`, and `CacheWrapper` interfaces + +### Neutral + +- Introduces additional- but optional- complexity to configuring and running a cosmos application +- If an application developer opts to use these features to expose data, they need to be aware of the ramifications/risks of that data exposure as it pertains to the specifics of their application diff --git a/docs/basics/README.md b/docs/basics/README.md index ad54c6bee783..03934dcd5112 100644 --- a/docs/basics/README.md +++ b/docs/basics/README.md @@ -10,7 +10,8 @@ This repository contains reference documentation on the basic concepts of the Co 1. [Anatomy of an SDK Application](./app-anatomy.md) 2. [Lifecycle of a transaction](./tx-lifecycle.md) -3. [Accounts](./accounts.md) -4. [Gas and Fees](./gas-fees.md) +3. [Lifecycle of a query](./query-lifecycle.md) +4. [Accounts](./accounts.md) +5. [Gas and Fees](./gas-fees.md) After reading the basics, head on to the [Core Reference](../core/README.md) for more advanced material. diff --git a/docs/basics/accounts.md b/docs/basics/accounts.md index 930914d73501..3b50c7a145b7 100644 --- a/docs/basics/accounts.md +++ b/docs/basics/accounts.md @@ -1,5 +1,5 @@ # Accounts diff --git a/docs/basics/app-anatomy.md b/docs/basics/app-anatomy.md index 9bd2256f0f6a..225da66c3c29 100644 --- a/docs/basics/app-anatomy.md +++ b/docs/basics/app-anatomy.md @@ -113,17 +113,19 @@ The `EncodingConfig` structure is the last important part of the `app.go` file. Here are descriptions of what each of the four fields means: -- `InterfaceRegistry`: The `InterfaceRegistry` is used by the Protobuf codec to handle interfaces, which are encoded and decoded (we also say "unpacked") using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). `Any` could be thought as a struct which contains a `type_url` (the concrete type of the interface) and a `value` (its encoded bytes). `InterfaceRegistry` provides a mechanism for registering interfaces and implementations that can be safely unpacked from `Any`. Each of the application's modules implements the `RegisterInterfaces` method, which can be used to register the module's own interfaces and implementations. +- `InterfaceRegistry`: The `InterfaceRegistry` is used by the Protobuf codec to handle interfaces, which are encoded and decoded (we also say "unpacked") using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). `Any` could be thought as a struct which contains a `type_url` (name of a concrete type implementing the interface) and a `value` (its encoded bytes). `InterfaceRegistry` provides a mechanism for registering interfaces and implementations that can be safely unpacked from `Any`. Each of the application's modules implements the `RegisterInterfaces` method, which can be used to register the module's own interfaces and implementations. + - You can read more about Any in [ADR-19](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-019-protobuf-state-encoding.md#usage-of-any-to-encode-interfaces). - To go more into details, the SDK uses an implementation of the Protobuf specification called [`gogoprotobuf`](https://github.com/gogo/protobuf). By default, the [gogo protobuf implementation of `Any`](https://godoc.org/github.com/gogo/protobuf/types) uses [global type registration](https://github.com/gogo/protobuf/blob/master/proto/properties.go#L540) to decode values packed in `Any` into concrete Go types. This introduces a vulnerability where any malicious module in the dependency tree could registry a type with the global protobuf registry and cause it to be loaded and unmarshaled by a transaction that referenced it in the `type_url` field. For more information, please refer to [ADR-019](../architecture/adr-019-protobuf-state-encoding.md). -- `Marshaler`: The `Marshaler` is the default codec used throughout the SDK. It is composed of a `BinaryMarshaler` used to encode and decode state, and a `JSONMarshaler` used to output data to the users (for example in the [CLI](#cli)). By default, the SDK uses Protobuf as `Marshaler`. +- `Marshaler`: the default codec used throughout the SDK. It is composed of a `BinaryMarshaler` used to encode and decode state, and a `JSONMarshaler` used to output data to the users (for example in the [CLI](#cli)). By default, the SDK uses Protobuf as `Marshaler`. - `TxConfig`: `TxConfig` defines an interface a client can utilize to generate an application-defined concrete transaction type. Currently, the SDK handles two transaction types: `SIGN_MODE_DIRECT` (which uses Protobuf binary as over-the-wire encoding) and `SIGN_MODE_LEGACY_AMINO_JSON` (which depends on Amino). Read more about transactions [here](../core/transactions.md). - `Amino`: Some legacy parts of the SDK still use Amino for backwards-compatibility. Each module exposes a `RegisterLegacyAmino` method to register the module's specific types within Amino. This `Amino` codec should not be used by app developers anymore, and will be removed in future releases. -The SDK exposes a `MakeCodecs` function used to create a `EncodingConfig`. It uses Protobuf as default `Marshaler`, and passes it down to the app's `appCodec` field. It also instantiates a legacy `Amino` codec inside the app's `legacyAmino` field. +The SDK exposes a `MakeTestEncodingConfig` function used to create a `EncodingConfig` for the app constructor (`NewApp`). It uses Protobuf as a default `Marshaler`. +NOTE: this function is marked deprecated and should only be used to create an app or in tests. We are working on refactoring codec management in a post Stargate release. -See an example of a `MakeCodecs` from `simapp`: +See an example of a `MakeTestEncodingConfig` from `simapp`: -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/app.go#L429-L435 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/simd/cmd/root.go#L179-196 ## Modules @@ -158,7 +160,7 @@ Each module should also implement the `RegisterServices` method as part of the [ ### gRPC `Query` Services -gRPC `Query` services are introduced in the v0.40 Stargate release. They allow users to query the state using [gRPC](https://grpc.io). They are enabled by default, and can be configued under the `grpc.enable` and `grpc.address` fields inside `app.toml`. +gRPC `Query` services are introduced in the v0.40 Stargate release. They allow users to query the state using [gRPC](https://grpc.io). They are enabled by default, and can be configued under the `grpc.enable` and `grpc.address` fields inside [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml). gRPC `Query` services are defined in the module's Protobuf definition files, specifically inside `query.proto`. The `query.proto` definition file exposes a single `Query` [Protobuf service](https://developers.google.com/protocol-buffers/docs/proto#services). Each gRPC query endpoint corresponds to a service method, starting with the `rpc` keyword, inside the `Query` service. @@ -206,7 +208,7 @@ Some external clients may not wish to use gRPC. The SDK provides in this case a The REST endpoints are defined in the Protobuf files, along with the gRPC services, using Protobuf annotations. Modules that want to expose REST queries should add `google.api.http` annotations to their `rpc` methods. By default, all REST endpoints defined in the SDK have an URL starting with the `/cosmos/` prefix. -The SDK also provides a development endpoint to generate [Swagger](https://swagger.io/) definition files for these REST endpoints. This endpoint can be enabled inside the `app.toml` config file, under the `api.swagger` key. +The SDK also provides a development endpoint to generate [Swagger](https://swagger.io/) definition files for these REST endpoints. This endpoint can be enabled inside the [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) config file, under the `api.swagger` key. #### Legacy API REST Endpoints diff --git a/docs/basics/gas-fees.md b/docs/basics/gas-fees.md index 270066996e72..2ab586497cd9 100644 --- a/docs/basics/gas-fees.md +++ b/docs/basics/gas-fees.md @@ -1,5 +1,5 @@ # Gas and Fees diff --git a/docs/basics/query-lifecycle.md b/docs/basics/query-lifecycle.md new file mode 100644 index 000000000000..4b4c509bb354 --- /dev/null +++ b/docs/basics/query-lifecycle.md @@ -0,0 +1,140 @@ + + +# Query Lifecycle + +This document describes the lifecycle of a query in a SDK application, from the user interface to application stores and back. {synopsis} + +## Pre-requisite Readings + +- [Transaction Lifecycle](./tx-lifecycle.md) {prereq} + +## Query Creation + +A [**query**](../building-modules/messages-and-queries.md#queries) is a request for information made by end-users of applications through an interface and processed by a full-node. Users can query information about the network, the application itself, and application state directly from the application's stores or modules. Note that queries are different from [transactions](../core/transactions.md) (view the lifecycle [here](./tx-lifecycle.md)), particularly in that they do not require consensus to be processed (as they do not trigger state-transitions); they can be fully handled by one full-node. + +For the purpose of explaining the query lifecycle, let's say `MyQuery` is requesting a list of delegations made by a certain delegator address in the application called `simapp`. As to be expected, the [`staking`](../../x/staking/spec/README.md) module handles this query. But first, there are a few ways `MyQuery` can be created by users. + +### CLI + +The main interface for an application is the command-line interface. Users connect to a full-node and run the CLI directly from their machines - the CLI interacts directly with the full-node. To create `MyQuery` from their terminal, users type the following command: + +```bash +simd query staking delegations +``` + +This query command was defined by the [`staking`](../../x/staking/spec/README.md) module developer and added to the list of subcommands by the application developer when creating the CLI. + +Note that the general format is as follows: + +```bash +simd query [moduleName] [command] --flag +``` + +To provide values such as `--node` (the full-node the CLI connects to), the user can use the [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) config file to set them or provide them as flags. + +The CLI understands a specific set of commands, defined in a hierarchical structure by the application developer: from the [root command](../core/cli.md#root-command) (`simd`), the type of command (`Myquery`), the module that contains the command (`staking`), and command itself (`delegations`). Thus, the CLI knows exactly which module handles this command and directly passes the call there. + +### gRPC + +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: + +```bash +grpcurl \ + -plaintext # We want results in plain test + -import-path ./proto \ # Import these .proto files + -proto ./proto/cosmos/staking/v1beta1/query.proto \ # Look into this .proto file for the Query protobuf service + -d '{"address":"$MY_DELEGATOR"}' \ # Query arguments + localhost:9090 \ # gRPC server endpoint + cosmos.staking.v1beta1.Query/Delegations # Fully-qualified service method name +``` + +### REST + +Another interface through which users can make queries is through HTTP Requests to a [REST server](../core/grpc_rest.md#rest-server). The REST server is fully auto-generated from Protobuf services, using [gRPC-gateway](https://github.com/grpc-ecosystem/grpc-gateway). + +An example HTTP request for `MyQuery` looks like: + +```bash +GET http://localhost:1317/cosmos/staking/v1beta1/delegators/{delegatorAddr}/delegations +``` + +## How Queries are Handled by the CLI + +The examples above show how an external user can interact with a node by querying its state. To understand more in details the exact lifecycle of a query, let's dig into how the CLI prepares the query, and how the node handles it. The interactions from the users' perspective are a bit different, but the underlying functions are almost identical because they are implementations of the same command defined by the module developer. This step of processing happens within the CLI, gRPC or REST server and heavily involves a `client.Context`. + +### Context + +The first thing that is created in the execution of a CLI command is a `client.Context`. A `client.Context` is an object that stores all the data needed to process a request on the user side. In particular, a `client.Context` stores the following: + +- **Codec**: The [encoder/decoder](../core/encoding.md) used by the application, used to marshal the parameters and query before making the Tendermint RPC request and unmarshal the returned response into a JSON object. The default codec used by the CLI is Protobuf. +- **Account Decoder**: The account decoder from the [`auth`](../..//x/auth/spec/README.md) module, which translates `[]byte`s into accounts. +- **RPC Client**: The Tendermint RPC Client, or node, to which the request will be relayed to. +- **Keyring**: A [Key Manager](../basics/accounts.md#keyring) used to sign transactions and handle other operations with keys. +- **Output Writer**: A [Writer](https://golang.org/pkg/io/#Writer) used to output the response. +- **Configurations**: The flags configured by the user for this command, including `--height`, specifying the height of the blockchain to query and `--indent`, which indicates to add an indent to the JSON response. + +The `client.Context` also contains various functions such as `Query()` which retrieves the RPC Client and makes an ABCI call to relay a query to a full-node. + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/client/context.go#L20-L50 + +The `client.Context`'s primary role is to store data used during interactions with the end-user and provide methods to interact with this data - it is used before and after the query is processed by the full-node. Specifically, in handling `MyQuery`, the `client.Context` is utilized to encode the query parameters, retrieve the full-node, and write the output. Prior to being relayed to a full-node, the query needs to be encoded into a `[]byte` form, as full-nodes are application-agnostic and do not understand specific types. The full-node (RPC Client) itself is retrieved using the `client.Context`, which knows which node the user CLI is connected to. The query is relayed to this full-node to be processed. Finally, the `client.Context` contains a `Writer` to write output when the response is returned. These steps are further described in later sections. + +### Arguments and Route Creation + +At this point in the lifecycle, the user has created a CLI command with all of the data they wish to include in their query. A `client.Context` exists to assist in the rest of the `MyQuery`'s journey. Now, the next step is to parse the command or request, extract the arguments, and encode everything. These steps all happen on the user side within the interface they are interacting with. + +#### Encoding + +In our case (querying an address's delegations), `MyQuery` contains an [address](./accounts.md#addresses) `delegatorAddress` as its only argument. However, the request can only contain `[]byte`s, as it will be relayed to a consensus engine (e.g. Tendermint Core) of a full-node that has no inherent knowledge of the application types. Thus, the `codec` of `client.Context` is used to marshal the address. + +Here is what the code looks like for the CLI command: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/staking/client/cli/query.go#L324-L327 + +#### gRPC Query Client Creation + +The SDK leverages code generated from Protobuf services to make queries. The `staking` module's `MyQuery` service generates a `queryClient`, which the CLI will use to make queries. Here is the relevant code: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/staking/client/cli/query.go#L318-L342 + +Under the hood, the `client.Context` has a `Query()` function used to retrieve the pre-configured node and relay a query to it; the function takes the query fully-qualified service method name as path (in our case: `/cosmos.staking.v1beta1.Query/Delegations`), and arguments as parameters. It first retrieves the RPC Client (called the [**node**](../core/node.md)) configured by the user to relay this query to, and creates the `ABCIQueryOptions` (parameters formatted for the ABCI call). The node is then used to make the ABCI call, `ABCIQueryWithOptions()`. + +Here is what the code looks like: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/client/query.go#L65-L91 + +## RPC + +With a call to `ABCIQueryWithOptions()`, `MyQuery` is received by a [full-node](../core/encoding.md) which will then process the request. Note that, while the RPC is made to the consensus engine (e.g. Tendermint Core) of a full-node, queries are not part of consensus and will not be broadcasted to the rest of the network, as they do not require anything the network needs to agree upon. + +Read more about ABCI Clients and Tendermint RPC in the Tendermint documentation [here](https://tendermint.com/rpc). + +## Application Query Handling + +When a query is received by the full-node after it has been relayed from the underlying consensus engine, it is now being handled within an environment that understands application-specific types and has a copy of the state. [`baseapp`](../core/baseapp.md) implements the ABCI [`Query()`](../core/baseapp.md#query) function and handles gRPC queries. The query route is parsed, and it it matches the fully-qualified service method name of an existing service method (most likely in one of the modules), then `baseapp` will relay the request to the relevant module. + +Apart from gRPC routes, `baseapp` also handles four different types of queries: `app`, `store`, `p2p`, and `custom`. The first three types (`app`, `store`, `p2p`) are purely application-level and thus directly handled by `baseapp` or the stores, but the `custom` query type requires `baseapp` to route the query to a module's [legacy queriers](../building-modules/query-services.md#legacy-queriers). To learn more about these queries, please refer to [this guide](../core/grpc_rest.md#tendermint-rpc). + +Since `MyQuery` has a Protobuf fully-qualified service method name from the `staking` module (recall `/cosmos.staking.v1beta1.Query/Delegations`), `baseapp` first parses the path, then uses its own internal `GRPCQueryRouter` to retrieve the corresponding gRPC handler, and routes the query to the module. The gRPC handler is responsible for recognizing this query, retrieving the appropriate values from the application's stores, and returning a response. Read more about query services [here](../building-modules/query-services.md). + +Once a result is received from the querier, `baseapp` begins the process of returning a response to the user. + +## Response + +Since `Query()` is an ABCI function, `baseapp` returns the response as an [`abci.ResponseQuery`](https://tendermint.com/docs/spec/abci/abci.html#messages) type. The `client.Context` `Query()` routine receives the response and. + +### CLI Response + +The application [`codec`](../core/encoding.md) is used to unmarshal the response to a JSON and the `client.Context` prints the output to the command line, applying any configurations such as the output type (text, JSON or YAML). + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/client/context.go#L248-L283 + +And that's a wrap! The result of the query is outputted to the console by the CLI. + +## Next {hide} + +Read more about [accounts](./accounts.md). {hide} diff --git a/docs/building-modules/beginblock-endblock.md b/docs/building-modules/beginblock-endblock.md index 09a48df8807a..cb8dbba1dda2 100644 --- a/docs/building-modules/beginblock-endblock.md +++ b/docs/building-modules/beginblock-endblock.md @@ -24,7 +24,7 @@ The actual implementation of `BeginBlocker` and `EndBlocker` in `./abci.go` are A specificity of the `EndBlocker` is that it can return validator updates to the underlying consensus engine in the form of an [`[]abci.ValidatorUpdates`](https://tendermint.com/docs/app-dev/abci-spec.html#validatorupdate). This is the preferred way to implement custom validator changes. -It is possible for developers to defined the order of execution between the `BeginBlocker`/`EndBlocker` functions of each of their application's modules via the module's manager `SetOrderBeginBlocker`/`SetOrderEndBlocker` methods. For more on the module manager, click [here](./module-manager.md#manager). +It is possible for developers to define the order of execution between the `BeginBlocker`/`EndBlocker` functions of each of their application's modules via the module's manager `SetOrderBeginBlocker`/`SetOrderEndBlocker` methods. For more on the module manager, click [here](./module-manager.md#manager). See an example implementation of `BeginBlocker` from the `distr` module: diff --git a/docs/building-modules/module-interfaces.md b/docs/building-modules/module-interfaces.md index 12c692cf52e5..0f811db4bf75 100644 --- a/docs/building-modules/module-interfaces.md +++ b/docs/building-modules/module-interfaces.md @@ -126,9 +126,9 @@ service Query{ } ``` -gRPC gateway is started in-process along with the application and Tendermint. It can be enabled or disabled by setting gRPC Configuration `enable` in `app.toml`. +gRPC gateway is started in-process along with the application and Tendermint. It can be enabled or disabled by setting gRPC Configuration `enable` in [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml). -The SDK provides a command for generating [Swagger](https://swagger.io/) documentation (`protoc-gen-swagger`). Setting `swagger` in `app.toml` defines if swagger documentation should be automatically registered. +The SDK provides a command for generating [Swagger](https://swagger.io/) documentation (`protoc-gen-swagger`). Setting `swagger` in [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) defines if swagger documentation should be automatically registered. ## Legacy REST diff --git a/docs/building-modules/module-manager.md b/docs/building-modules/module-manager.md index cd8cc4b9eca0..919ec1425c84 100644 --- a/docs/building-modules/module-manager.md +++ b/docs/building-modules/module-manager.md @@ -74,7 +74,8 @@ Let us go through the methods of `AppModule`: - `LegacyQuerierHandler(*codec.LegacyAmino)` (deprecated): Returns a [`querier`](./query-services.md#legacy-queriers) given the query `path`, in order to process the `query`. - `RegisterServices(Configurator)`: Allows a module to register services. - `BeginBlock(sdk.Context, abci.RequestBeginBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. Implement empty if no logic needs to be triggered at the beginning of each block for this module. -- `EndBlock(sdk.Context, abci.RequestEndBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. the `staking` module). Implement empty if no logic needs to be triggered at the beginning of each block for this module. +- `EndBlock(sdk.Context, abci.RequestEndBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the end of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. the `staking` module). Implement empty if no logic needs to be triggered at the end of each block for this module. + ### Implementing the Application Module Interfaces @@ -132,7 +133,7 @@ The module manager is used throughout the application whenever an action on a co - `SetOrderInitGenesis(moduleNames ...string)`: Sets the order in which the [`InitGenesis`](./genesis.md#initgenesis) function of each module will be called when the application is first started. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function). - `SetOrderExportGenesis(moduleNames ...string)`: Sets the order in which the [`ExportGenesis`](./genesis.md#exportgenesis) function of each module will be called in case of an export. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function). - `SetOrderBeginBlockers(moduleNames ...string)`: Sets the order in which the `BeginBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function). -- `SetOrderEndBlockers(moduleNames ...string)`: Sets the order in which the `EndBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function). +- `SetOrderEndBlockers(moduleNames ...string)`: Sets the order in which the `EndBlock()` function of each module will be called at the end of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function). - `RegisterInvariants(ir sdk.InvariantRegistry)`: Registers the [invariants](./invariants.md) of each module. - `RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino)`: Registers legacy [`Msg`](./messages-and-queries.md#messages) and [`querier`](./query-services.md#legacy-queriers) routes. - `RegisterServices(cfg Configurator)`: Registers all module services. diff --git a/docs/core/README.md b/docs/core/README.md index 23df86d26909..94a993f5a556 100644 --- a/docs/core/README.md +++ b/docs/core/README.md @@ -9,17 +9,19 @@ parent: This repository contains reference documentation on the core concepts of the Cosmos SDK. 1. [`BaseApp`](./baseapp.md) -2. [Transaction](./transactions.md) -3. [Context](./context.md) -4. [Node Client](./node.md) -5. [Store](./store.md) -6. [Encoding](./encoding.md) -7. [gRPC, REST and Tendermint Endpoints](./grpc_rest.md) -8. [Events](./events.md) -9. [Telemetry](./telemetry.md) -10. [Object-Capabilities](./ocap.md) -11. [RunTx recovery middleware](./runtx_middleware.md) -12. [Protobuf documentation](./proto-docs.md) +1. [Transaction](./transactions.md) +1. [Context](./context.md) +1. [Node Client](./node.md) +1. [Store](./store.md) +1. [Encoding](./encoding.md) +1. [gRPC, REST and Tendermint Endpoints](./grpc_rest.md) +1. [Command-Line Interface](./cli.md) +1. [Events](./events.md) +1. [Telemetry](./telemetry.md) +1. [Object-Capabilities](./ocap.md) +1. [RunTx recovery middleware](./runtx_middleware.md) +1. [Simulation](./simulation.md) +1. [Protobuf documentation](./proto-docs.md) After reading about the core concepts, check the [IBC documentation](../ibc/README.md) to learn more about the IBC core concepts and how to integrate it to you application. diff --git a/docs/core/cli.md b/docs/core/cli.md new file mode 100644 index 000000000000..10203e769e8c --- /dev/null +++ b/docs/core/cli.md @@ -0,0 +1,143 @@ + + +# Command-Line Interface + +This document describes how commmand-line interface (CLI) works on a high-level, for an [**application**](../basics/app-anatomy.md). A separate document for implementing a CLI for an SDK [**module**](../building-modules/intro.md) can be found [here](../building-modules/module-interfaces.md#cli). {synopsis} + +## Command-Line Interface + +### Example Command + +There is no set way to create a CLI, but SDK modules typically use the [Cobra Library](https://github.com/spf13/cobra). Building a CLI with Cobra entails defining commands, arguments, and flags. [**Commands**](#commands) understand the actions users wish to take, such as `tx` for creating a transaction and `query` for querying the application. Each command can also have nested subcommands, necessary for naming the specific transaction type. Users also supply **Arguments**, such as account numbers to send coins to, and [**Flags**](#flags) to modify various aspects of the commands, such as gas prices or which node to broadcast to. + +Here is an example of a command a user might enter to interact with the simapp CLI `simd` in order to send some tokens: + +```bash +simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --gas auto --gas-prices +``` + +The first four strings specify the command: + +- The root command for the entire application `simd`. +- The subcommand `tx`, which contains all commands that let users create transactions. +- The subcommand `bank` to indicate which module to route the command to ([`x/bank`](../../x/bank/spec/README.md) module in this case). +- The type of transaction `send`. + +The next two strings are arguments: the `from_address` the user wishes to send from, the `to_address` of the recipient, and the `amount` they want to send. Finally, the last few strings of the command are optional flags to indicate how much the user is willing to pay in fees (calculated using the amount of gas used to execute the transaction and the gas prices provided by the user). + +The CLI interacts with a [node](../core/node.md) to handle this command. The interface itself is defined in a `main.go` file. + +### Building the CLI + +The `main.go` file needs to have a `main()` function that creates a root command, to which all the application commands will be added as subcommands. The root command additionally handles: + +- **setting configurations** by reading in configuration files (e.g. the sdk config file). +- **adding any flags** to it, such as `--chain-id`. +- **instantiating the `codec`** by calling the application's `MakeCodec()` function (called `MakeTestEncodingConfig` in `simapp`). The [`codec`](../core/encoding.md) is used to encode and decode data structures for the application - stores can only persist `[]byte`s so the developer must define a serialization format for their data structures or use the default, Protobuf. +- **adding subcommand** for all the possible user interactions, including [transaction commands](#transaction-commands) and [query commands](#query-commands). + +The `main()` function finally creates an executor and [execute](https://godoc.org/github.com/spf13/cobra#Command.Execute) the root command. See an example of `main()` function from the `simapp` application: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/main.go#L12-L24 + +The rest of the document will detail what needs to be implemented for each step and include smaller portions of code from the `simapp` CLI files. + +## Adding Commands to the CLI + +Every application CLI first constructs a root command, then adds functionality by aggregating subcommands (often with further nested subcommands) using `rootCmd.AddCommand()`. The bulk of an application's unique capabilities lies in its transaction and query commands, called `TxCmd` and `QueryCmd` respectively. + +### Root Command + +The root command (called `rootCmd`) is what the user first types into the command line to indicate which application they wish to interact with. The string used to invoke the command (the "Use" field) is typically the name of the application suffixed with `-d`, e.g. `simd` or `gaiad`. The root command typically includes the following commands to support basic functionality in the application. + +- **Status** command from the SDK rpc client tools, which prints information about the status of the connected [`Node`](../core/node.md). The Status of a node includes `NodeInfo`,`SyncInfo` and `ValidatorInfo`. +- **Keys** [commands](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/client/keys) from the SDK client tools, which includes a collection of subcommands for using the key functions in the SDK crypto tools, including adding a new key and saving it to the keyring, listing all public keys stored in the keyring, and deleting a key. For example, users can type `simd keys add ` to add a new key and save an encrypted copy to the keyring, using the flag `--recover` to recover a private key from a seed phrase or the flag `--multisig` to group multiple keys together to create a multisig key. For full details on the `add` key command, see the code [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/client/keys/add.go). For more details about usage of `--keyring-backend` for storage of key credentials look at the [keyring docs](../run-node/keyring.md). +- **Server** commands from the SDK server package. These commands are responsible for providing the mechanisms necessary to start an ABCI Tendermint application and provides the CLI framework (based on [cobra](github.com/spf13/cobra)) necessary to fully bootstrap an application. The package exposes two core functions: `StartCmd` and `ExportCmd` which creates commands to start the application and export state respectively. Click [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/server) to learn more. +- [**Transaction**](#transaction-commands) commands. +- [**Query**](#query-commands) commands. + +Next is an example `rootCmd` function from the `simapp` application. It instantiates the root command, adds a [_persistent_ flag](#flags) and `PreRun` function to be run before every execution, and adds all of the necessary subcommands. + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L37-L93 + +The root-level `status` and `keys` subcommands are common across most applications and do not interact with application state. The bulk of an application's functionality - what users can actually _do_ with it - is enabled by its `tx` and `query` commands. + +### Transaction Commands + +[Transactions](./transactions.md) are objects wrapping [`Msg`s](../building-modules/messages-and-queries.md#messages) that trigger state changes. To enable the creation of transactions using the CLI interface, a function `txCmd` is generally added to the `rootCmd`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L86-L92 + +This `txCmd` function adds all the transaction available to end-users for the application. This typically includes: + +- **Sign command** from the [`auth`](../../x/auth/spec/README.md) module that signs messages in a transaction. To enable multisig, add the `auth` module's `MultiSign` command. Since every transaction requires some sort of signature in order to be valid, thithe signing command is necessary for every application. +- **Broadcast command** from the SDK client tools, to broadcast transactions. +- **All [module transaction commands](../building-modules/module-interfaces.md#transaction-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/module-manager.md#basic-manager) `AddTxCommands()` function. + +Here is an example of a `txCmd` aggregating these subcommands from the `simapp` application: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L123-L149 + +### Query Commands + +[**Queries**](../building-modules/messages-and-queries.md#queries) are objects that allow users to retrieve information about the application's state. To enable the creation of transactions using the CLI interface, a function `txCmd` is generally added to the `rootCmd`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L86-L92 + +This `queryCmd` function adds all the queries available to end-users for the application. This typically includes: + +- **QueryTx** and/or other transaction query commands] from the `auth` module which allow the user to search for a transaction by inputting its hash, a list of tags, or a block height. These queries allow users to see if transactions have been included in a block. +- **Account command** from the `auth` module, which displays the state (e.g. account balance) of an account given an address. +- **Validator command** from the SDK rpc client tools, which displays the validator set of a given height. +- **Block command** from the SDK rpc client tools, which displays the block data for a given height. +- **All [module query commands](../building-modules/module-interfaces.md#query-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/module-manager.md#basic-manager) `AddQueryCommands()` function. + +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 + +## Flags + +Flags are used to modify commands; developers can include them in a `flags.go` file with their CLI. Users can explicitly include them in commands or pre-configure them by inside their [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml). Commonly pre-configured flags include the `--node` to connect to and `--chain-id` of the blockchain the user wishes to interact with. + +A _persistent_ flag (as opposed to a _local_ flag) added to a command transcends all of its children: subcommands will inherit the configured values for these flags. Additionally, all flags have default values when they are added to commands; some toggle an option off but others are empty values that the user needs to override to create valid commands. A flag can be explicitly marked as _required_ so that an error is automatically thrown if the user does not provide a value, but it is also acceptable to handle unexpected missing flags differently. + +Flags are added to commands directly (generally in the [module's CLI file](../building-modules/module-interfaces.md#flags) where module commands are defined) and no flag except for the `rootCmd` persistent flags has to be added at application level. It is common to add a _persistent_ flag for `--chain-id`, the unique identifier of the blockchain the application pertains to, to the root command. Adding this flag can be done in the `main()` function. Adding this flag makes sense as the chain ID should not be changing across commands in this application CLI. Here is an example from the `simapp` application: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L118 + +## Environment variables + +Each flag is bound to it's respecteve named environment variable. Then name of the environment variable consist of two parts - capital case `basename` followed by flag name of the flag. `-` must be substituted with `_`. For example flag `--home` for application with basename `GAIA` is bound to `GAIA_HOME`. It allows to reduce amount of flags typed for routine operations. For example instead of: +```sh +gaia --home=./ --node= --chain-id="testchain-1" --keyring-backend=test tx ... --from= +``` +this will be more convinient: +```sh +# define env variables in .env, .envrc etc +GAIA_HOME= +GAIA_NODE= +GAIA_CHAIN_ID="testchain-1" +GAIA_KEYRING_BACKEND="test" + +# and later just use +gaia tx ... --from= +``` + +## Configurations + +It is vital that the root command of an application uses `PersistentPreRun()` cobra command property for executing the command, so all child commands have access to the server and client contexts. These contexts are set as their default values initially and maybe modified, scoped to the command, in their respective `PersistentPreRun()` functions. Note that the `client.Context` is typically pre-populated with "default" values that may be useful for all commands to inherit and override if necessary. + +Here is an example of an `PersistentPreRun()` function from `simapp``: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L54-L60 + +The `SetCmdClientContextHandler` call reads persistent flags via `ReadPersistentCommandFlags` which creates a `client.Context` and sets that on the root command's `Context`. + +The `InterceptConfigsPreRunHandler` call creates a viper literal, default `server.Context`, and a logger and sets that on the root command's `Context`. The `server.Context` will be modified and saved to disk via the internal `interceptConfigs` call, which either reads or creates a Tendermint configuration based on the home path provided. In addition, `interceptConfigs` also reads and loads the application configuration, `app.toml`, and binds that to the `server.Context` viper literal. This is vital so the application can get access to not only the CLI flags, but also to the application configuration values provided by this file. + +## Next {hide} + +Learn about [events](./events.md) {hide} diff --git a/docs/core/context.md b/docs/core/context.md index c9e89b16a471..ee022d900ee0 100644 --- a/docs/core/context.md +++ b/docs/core/context.md @@ -13,7 +13,7 @@ The `context` is a data structure intended to be passed from function to functio ## Context Definition -The SDK `Context` is a custom data structure that contains Go's stdlib [`context`](https://golang.org/pkg/context) as its base, and has many additional types within its definition that are specific to the Cosmos SDK. he `Context` is integral to transaction processing in that it allows modules to easily access their respective [store](./store.md#base-layer-kvstores) in the [`multistore`](./store.md#multistore) and retrieve transactional context such as the block header and gas meter. +The SDK `Context` is a custom data structure that contains Go's stdlib [`context`](https://golang.org/pkg/context) as its base, and has many additional types within its definition that are specific to the Cosmos SDK. The `Context` is integral to transaction processing in that it allows modules to easily access their respective [store](./store.md#base-layer-kvstores) in the [`multistore`](./store.md#multistore) and retrieve transactional context such as the block header and gas meter. +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/context.go#L16-L39 diff --git a/docs/core/events.md b/docs/core/events.md index bffa432ba4bc..d199d94ced52 100644 --- a/docs/core/events.md +++ b/docs/core/events.md @@ -1,5 +1,5 @@ # Events @@ -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 e89b0653ad2a..ca59d3f14908 100644 --- a/docs/core/grpc_rest.md +++ b/docs/core/grpc_rest.md @@ -24,15 +24,15 @@ Cosmos SDK v0.40 introduced Protobuf as the main [encoding](./encoding) library, 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 `$TMHOME/config/app.toml`: +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 -`$TMHOME` is the directory where the node's configuration and databases are stored. By default, it's set to `~/.{app_name}`. +`~/.simapp` is the directory where the node's configuration and databases are stored. By default, it's set to `~/.{app_name}`. :: Once the gRPC server is started, you can send requests to it using a gRPC client. Some examples are given in our [Interact with the Node](../run-node/interact-node.md#using-grpc) tutorial. @@ -43,11 +43,11 @@ An overview of all available gRPC endpoints shipped with the Cosmos SDK is [Prot In Cosmos SDK v0.40, the node continues to serve a REST server. However, the existing routes present in version v0.39 and earlier are now marked as deprecated, and new routes have been added via gRPC-gateway. -All routes are configured under the following fields in `$TMHOME/config/app.toml`: +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 `$TMHOME/config/app.toml`, along with comments, please refer to that file directly. +- some additional API configuration options are defined in `~/.simapp/config/app.toml`, along with comments, please refer to that file directly. ### gRPC-gateway REST Routes @@ -55,7 +55,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. @@ -69,13 +69,13 @@ For application developers, Legacy REST API routes needs to be wired up to the R A [Swagger](https://swagger.io/) (or OpenAPIv2) specification file is exposed under the `/swagger` route on the API server. Swagger is an open specification describing the API endpoints a server serves, including description, input arguments, return types and much more about each endpoint. -Enabling the `/swagger` endpoint is configurable inside `$TMHOME/config/app.toml` via the `api.swagger` field, which is set to true by default. +Enabling the `/swagger` endpoint is configurable inside `~/.simapp/config/app.toml` via the `api.swagger` field, which is set to true by default. For application developers, you may want to generate your own Swagger definitions based on your custom modules. The SDK's [Swagger generation script](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/scripts/protoc-swagger-gen.sh) is a good place to start. ## Tendermint RPC -Independently from the Cosmos SDK, Tendermint also exposes a RPC server. This RPC server can be configured by tuning parameters under the `rpc` table in the `$TMHOME/config/config.toml`, the default listening address is `tcp://0.0.0.0:26657`. An OpenAPI specification of all Tendermint RPC endpoints is available [here](https://docs.tendermint.com/master/rpc/). +Independently from the Cosmos SDK, Tendermint also exposes a RPC server. This RPC server can be configured by tuning parameters under the `rpc` table in the `~/.simapp/config/config.toml`, the default listening address is `tcp://0.0.0.0:26657`. An OpenAPI specification of all Tendermint RPC endpoints is available [here](https://docs.tendermint.com/master/rpc/). Some Tendermint RPC endpoints are directly related to the Cosmos SDK: @@ -98,4 +98,4 @@ Some Tendermint RPC endpoints are directly related to the Cosmos SDK: ## Next {hide} -Learn about [events](./events.md) {hide} +Learn about [the CLI](./cli.md) {hide} diff --git a/docs/core/node.md b/docs/core/node.md index 005aab3ab188..661322b2c010 100644 --- a/docs/core/node.md +++ b/docs/core/node.md @@ -16,7 +16,7 @@ The full-node client of any SDK application is built by running a `main` functio In general, developers will implement the `main.go` function with the following structure: -- First, an [`appCodec`](./encoding.md) is instanciated for the application. +- First, an [`appCodec`](./encoding.md) is instantiated for the application. - Then, the `config` is retrieved and config parameters are set. This mainly involves setting the bech32 prefixes for [addresses and pubkeys](../basics/accounts.md#addresses-and-pubkeys). +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/config.go#L13-L24 - Using [cobra](https://github.com/spf13/cobra), the root command of the full-node client is created. After that, all the custom commands of the application are added using the `AddCommand()` method of `rootCmd`. diff --git a/docs/core/ocap.md b/docs/core/ocap.md index 523595c32479..3f0ec2e1d7ec 100644 --- a/docs/core/ocap.md +++ b/docs/core/ocap.md @@ -1,5 +1,5 @@ # Object-Capability Model diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index 6c4f113995db..2e39a4befc9a 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -26,6 +26,49 @@ - [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/base/abci/v1beta1/abci.proto](#cosmos/base/abci/v1beta1/abci.proto) + - [ABCIMessageLog](#cosmos.base.abci.v1beta1.ABCIMessageLog) + - [Attribute](#cosmos.base.abci.v1beta1.Attribute) + - [GasInfo](#cosmos.base.abci.v1beta1.GasInfo) + - [MsgData](#cosmos.base.abci.v1beta1.MsgData) + - [Result](#cosmos.base.abci.v1beta1.Result) + - [SearchTxsResult](#cosmos.base.abci.v1beta1.SearchTxsResult) + - [SimulationResponse](#cosmos.base.abci.v1beta1.SimulationResponse) + - [StringEvent](#cosmos.base.abci.v1beta1.StringEvent) + - [TxMsgData](#cosmos.base.abci.v1beta1.TxMsgData) + - [TxResponse](#cosmos.base.abci.v1beta1.TxResponse) + +- [cosmos/authz/v1beta1/tx.proto](#cosmos/authz/v1beta1/tx.proto) + - [MsgExecAuthorizedRequest](#cosmos.authz.v1beta1.MsgExecAuthorizedRequest) + - [MsgExecAuthorizedResponse](#cosmos.authz.v1beta1.MsgExecAuthorizedResponse) + - [MsgGrantAuthorizationRequest](#cosmos.authz.v1beta1.MsgGrantAuthorizationRequest) + - [MsgGrantAuthorizationResponse](#cosmos.authz.v1beta1.MsgGrantAuthorizationResponse) + - [MsgRevokeAuthorizationRequest](#cosmos.authz.v1beta1.MsgRevokeAuthorizationRequest) + - [MsgRevokeAuthorizationResponse](#cosmos.authz.v1beta1.MsgRevokeAuthorizationResponse) + + - [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/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/bank.proto](#cosmos/bank/v1beta1/bank.proto) - [DenomUnit](#cosmos.bank.v1beta1.DenomUnit) - [Input](#cosmos.bank.v1beta1.Input) @@ -39,15 +82,15 @@ - [Balance](#cosmos.bank.v1beta1.Balance) - [GenesisState](#cosmos.bank.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/bank/v1beta1/query.proto](#cosmos/bank/v1beta1/query.proto) - [QueryAllBalancesRequest](#cosmos.bank.v1beta1.QueryAllBalancesRequest) - [QueryAllBalancesResponse](#cosmos.bank.v1beta1.QueryAllBalancesResponse) - [QueryBalanceRequest](#cosmos.bank.v1beta1.QueryBalanceRequest) - [QueryBalanceResponse](#cosmos.bank.v1beta1.QueryBalanceResponse) + - [QueryDenomMetadataRequest](#cosmos.bank.v1beta1.QueryDenomMetadataRequest) + - [QueryDenomMetadataResponse](#cosmos.bank.v1beta1.QueryDenomMetadataResponse) + - [QueryDenomsMetadataRequest](#cosmos.bank.v1beta1.QueryDenomsMetadataRequest) + - [QueryDenomsMetadataResponse](#cosmos.bank.v1beta1.QueryDenomsMetadataResponse) - [QueryParamsRequest](#cosmos.bank.v1beta1.QueryParamsRequest) - [QueryParamsResponse](#cosmos.bank.v1beta1.QueryParamsResponse) - [QuerySupplyOfRequest](#cosmos.bank.v1beta1.QuerySupplyOfRequest) @@ -65,18 +108,6 @@ - [Msg](#cosmos.bank.v1beta1.Msg) -- [cosmos/base/abci/v1beta1/abci.proto](#cosmos/base/abci/v1beta1/abci.proto) - - [ABCIMessageLog](#cosmos.base.abci.v1beta1.ABCIMessageLog) - - [Attribute](#cosmos.base.abci.v1beta1.Attribute) - - [GasInfo](#cosmos.base.abci.v1beta1.GasInfo) - - [MsgData](#cosmos.base.abci.v1beta1.MsgData) - - [Result](#cosmos.base.abci.v1beta1.Result) - - [SearchTxsResult](#cosmos.base.abci.v1beta1.SearchTxsResult) - - [SimulationResponse](#cosmos.base.abci.v1beta1.SimulationResponse) - - [StringEvent](#cosmos.base.abci.v1beta1.StringEvent) - - [TxMsgData](#cosmos.base.abci.v1beta1.TxMsgData) - - [TxResponse](#cosmos.base.abci.v1beta1.TxResponse) - - [cosmos/base/kv/v1beta1/kv.proto](#cosmos/base/kv/v1beta1/kv.proto) - [Pair](#cosmos.base.kv.v1beta1.Pair) - [Pairs](#cosmos.base.kv.v1beta1.Pairs) @@ -233,6 +264,32 @@ - [Msg](#cosmos.evidence.v1beta1.Msg) +- [cosmos/feegrant/v1beta1/feegrant.proto](#cosmos/feegrant/v1beta1/feegrant.proto) + - [BasicFeeAllowance](#cosmos.feegrant.v1beta1.BasicFeeAllowance) + - [Duration](#cosmos.feegrant.v1beta1.Duration) + - [ExpiresAt](#cosmos.feegrant.v1beta1.ExpiresAt) + - [FeeAllowanceGrant](#cosmos.feegrant.v1beta1.FeeAllowanceGrant) + - [PeriodicFeeAllowance](#cosmos.feegrant.v1beta1.PeriodicFeeAllowance) + +- [cosmos/feegrant/v1beta1/genesis.proto](#cosmos/feegrant/v1beta1/genesis.proto) + - [GenesisState](#cosmos.feegrant.v1beta1.GenesisState) + +- [cosmos/feegrant/v1beta1/query.proto](#cosmos/feegrant/v1beta1/query.proto) + - [QueryFeeAllowanceRequest](#cosmos.feegrant.v1beta1.QueryFeeAllowanceRequest) + - [QueryFeeAllowanceResponse](#cosmos.feegrant.v1beta1.QueryFeeAllowanceResponse) + - [QueryFeeAllowancesRequest](#cosmos.feegrant.v1beta1.QueryFeeAllowancesRequest) + - [QueryFeeAllowancesResponse](#cosmos.feegrant.v1beta1.QueryFeeAllowancesResponse) + + - [Query](#cosmos.feegrant.v1beta1.Query) + +- [cosmos/feegrant/v1beta1/tx.proto](#cosmos/feegrant/v1beta1/tx.proto) + - [MsgGrantFeeAllowance](#cosmos.feegrant.v1beta1.MsgGrantFeeAllowance) + - [MsgGrantFeeAllowanceResponse](#cosmos.feegrant.v1beta1.MsgGrantFeeAllowanceResponse) + - [MsgRevokeFeeAllowance](#cosmos.feegrant.v1beta1.MsgRevokeFeeAllowance) + - [MsgRevokeFeeAllowanceResponse](#cosmos.feegrant.v1beta1.MsgRevokeFeeAllowanceResponse) + + - [Msg](#cosmos.feegrant.v1beta1.Msg) + - [cosmos/genutil/v1beta1/genesis.proto](#cosmos/genutil/v1beta1/genesis.proto) - [GenesisState](#cosmos.genutil.v1beta1.GenesisState) @@ -245,6 +302,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) @@ -279,6 +337,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) @@ -956,238 +1016,253 @@ IntProto defines a Protobuf wrapper around an Int object. - +

Top

-## cosmos/bank/v1beta1/bank.proto +## cosmos/authz/v1beta1/authz.proto - + -### DenomUnit -DenomUnit represents a struct that describes a given -denomination unit of the basic token. +### AuthorizationGrant +AuthorizationGrant gives permissions to execute +the provide method with expiration time. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `denom` | [string](#string) | | denom represents the string name of the given denom unit (e.g uatom). | -| `exponent` | [uint32](#uint32) | | exponent represents power of 10 exponent that one must raise the base_denom to in order to equal the given DenomUnit's denom 1 denom = 1^exponent base_denom (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with exponent = 6, thus: 1 atom = 10^6 uatom). | -| `aliases` | [string](#string) | repeated | aliases is a list of string aliases for the given denom | +| `authorization` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `expiration` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | - + -### Input -Input models transaction input. +### GenericAuthorization +GenericAuthorization gives the grantee unrestricted permissions to execute +the provided method on behalf of the granter's account. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `address` | [string](#string) | | | -| `coins` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `method_name` | [string](#string) | | method name to grant unrestricted permissions to execute Note: MethodName() is already a method on `GenericAuthorization` type, we need some custom naming here so using `MessageName` | - + -### Metadata -Metadata represents a struct that describes -a basic token. +### SendAuthorization +SendAuthorization allows the grantee to spend up to spend_limit coins from +the granter's account. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `description` | [string](#string) | | | -| `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. | +| `spend_limit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | + - + -### Output -Output models transaction outputs. + + + + + + + +

Top

+ +## cosmos/base/abci/v1beta1/abci.proto + + + + + +### ABCIMessageLog +ABCIMessageLog defines a structure containing an indexed tx ABCI message log. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `address` | [string](#string) | | | -| `coins` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `msg_index` | [uint32](#uint32) | | | +| `log` | [string](#string) | | | +| `events` | [StringEvent](#cosmos.base.abci.v1beta1.StringEvent) | repeated | Events contains a slice of Event objects that were emitted during some execution. | - + -### Params -Params defines the parameters for the bank module. +### Attribute +Attribute defines an attribute wrapper where the key and value are +strings instead of raw bytes. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `send_enabled` | [SendEnabled](#cosmos.bank.v1beta1.SendEnabled) | repeated | | -| `default_send_enabled` | [bool](#bool) | | | +| `key` | [string](#string) | | | +| `value` | [string](#string) | | | - + -### SendEnabled -SendEnabled maps coin denom to a send_enabled status (whether a denom is -sendable). +### GasInfo +GasInfo defines tx execution gas context. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `denom` | [string](#string) | | | -| `enabled` | [bool](#bool) | | | +| `gas_wanted` | [uint64](#uint64) | | GasWanted is the maximum units of work we allow this tx to perform. | +| `gas_used` | [uint64](#uint64) | | GasUsed is the amount of gas actually consumed. | - + -### Supply -Supply represents a struct that passively keeps track of the total supply -amounts in the network. +### MsgData +MsgData defines the data returned in a Result object during message +execution. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `total` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `msg_type` | [string](#string) | | | +| `data` | [bytes](#bytes) | | | - - + - +### Result +Result is the union of ResponseFormat and ResponseCheckTx. - +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `data` | [bytes](#bytes) | | Data is any data returned from message or handler execution. It MUST be length prefixed in order to separate data from multiple message executions. | +| `log` | [string](#string) | | Log contains the log information from message or handler execution. | +| `events` | [tendermint.abci.Event](#tendermint.abci.Event) | repeated | Events contains a slice of Event objects that were emitted during message or handler execution. | - -

Top

-## cosmos/bank/v1beta1/genesis.proto - + -### Balance -Balance defines an account address and balance pair used in the bank module's -genesis state. +### SearchTxsResult +SearchTxsResult defines a structure for querying txs pageable | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `address` | [string](#string) | | address is the address of the balance holder. | -| `coins` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | coins defines the different coins this balance holds. | +| `total_count` | [uint64](#uint64) | | Count of all txs | +| `count` | [uint64](#uint64) | | Count of txs in current page | +| `page_number` | [uint64](#uint64) | | Index of current page, start from 1 | +| `page_total` | [uint64](#uint64) | | Count of total pages | +| `limit` | [uint64](#uint64) | | Max count txs per page | +| `txs` | [TxResponse](#cosmos.base.abci.v1beta1.TxResponse) | repeated | List of txs in current page | - + -### GenesisState -GenesisState defines the bank module's genesis state. +### SimulationResponse +SimulationResponse defines the response generated when a transaction is +successfully simulated. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `params` | [Params](#cosmos.bank.v1beta1.Params) | | params defines all the paramaters of the module. | -| `balances` | [Balance](#cosmos.bank.v1beta1.Balance) | repeated | balances is an array containing the balances of all the accounts. | -| `supply` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | supply represents the total supply. | -| `denom_metadata` | [Metadata](#cosmos.bank.v1beta1.Metadata) | repeated | denom_metadata defines the metadata of the differents coins. | - +| `gas_info` | [GasInfo](#cosmos.base.abci.v1beta1.GasInfo) | | | +| `result` | [Result](#cosmos.base.abci.v1beta1.Result) | | | - - - + - +### StringEvent +StringEvent defines en Event object wrapper where all the attributes +contain key/value pairs that are strings instead of raw bytes. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `type` | [string](#string) | | | +| `attributes` | [Attribute](#cosmos.base.abci.v1beta1.Attribute) | repeated | | - -

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; - } +### TxMsgData +TxMsgData defines a list of MsgData. A transaction will have a MsgData object +for each message. | 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. | - +| `data` | [MsgData](#cosmos.base.abci.v1beta1.MsgData) | repeated | | - -### 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; - } +### TxResponse +TxResponse defines a structure containing relevant tx data and metadata. The +tags are stringified and the log is JSON decoded. | 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 | +| `height` | [int64](#int64) | | The block height | +| `txhash` | [string](#string) | | The transaction hash. | +| `codespace` | [string](#string) | | Namespace for the Code | +| `code` | [uint32](#uint32) | | Response code. | +| `data` | [string](#string) | | Result bytes, if any. | +| `raw_log` | [string](#string) | | The output of the application's logger (raw string). May be non-deterministic. | +| `logs` | [ABCIMessageLog](#cosmos.base.abci.v1beta1.ABCIMessageLog) | repeated | The output of the application's logger (typed). May be non-deterministic. | +| `info` | [string](#string) | | Additional information. May be non-deterministic. | +| `gas_wanted` | [int64](#int64) | | Amount of gas requested for transaction. | +| `gas_used` | [int64](#int64) | | Amount of gas consumed by transaction. | +| `tx` | [google.protobuf.Any](#google.protobuf.Any) | | The request transaction bytes. | +| `timestamp` | [string](#string) | | Time of the previous block. For heights > 1, it's the weighted median of the timestamps of the valid votes in the block.LastCommit. For height == 1, it's genesis time. | @@ -1203,236 +1278,301 @@ corresponding request message has used PageRequest. - +

Top

-## cosmos/bank/v1beta1/query.proto +## cosmos/authz/v1beta1/tx.proto - + -### QueryAllBalancesRequest -QueryBalanceRequest is the request type for the Query/AllBalances RPC method. +### MsgExecAuthorizedRequest +MsgExecAuthorizedRequest attempts to execute the provided messages using +authorizations granted to the grantee. Each message should have only +one signer corresponding to the granter of the authorization. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `address` | [string](#string) | | address is the address to query balances for. | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | +| `grantee` | [string](#string) | | | +| `msgs` | [google.protobuf.Any](#google.protobuf.Any) | repeated | | - + -### QueryAllBalancesResponse -QueryAllBalancesResponse is the response type for the Query/AllBalances RPC -method. +### MsgExecAuthorizedResponse +MsgExecAuthorizedResponse defines the Msg/MsgExecAuthorizedResponse response type. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `balances` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | balances is the balances of all the coins. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | +| `result` | [cosmos.base.abci.v1beta1.Result](#cosmos.base.abci.v1beta1.Result) | | | - + -### QueryBalanceRequest -QueryBalanceRequest is the request type for the Query/Balance RPC method. +### MsgGrantAuthorizationRequest +MsgGrantAuthorizationRequest grants the provided authorization to the grantee on the granter's +account with the provided expiration time. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `address` | [string](#string) | | address is the address to query balances for. | -| `denom` | [string](#string) | | denom is the coin denom to query balances for. | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | +| `authorization` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `expiration` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | - + -### QueryBalanceResponse -QueryBalanceResponse is the response type for the Query/Balance RPC method. +### MsgGrantAuthorizationResponse +MsgGrantAuthorizationResponse defines the Msg/MsgGrantAuthorization response type. -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `balance` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | balance is the balance of the coin. | + +### MsgRevokeAuthorizationRequest +MsgRevokeAuthorizationRequest revokes any authorization with the provided sdk.Msg type on the +granter's account with that has been granted to the grantee. - -### QueryParamsRequest -QueryParamsRequest defines the request type for querying x/bank parameters. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | +| `method_name` | [string](#string) | | | - + -### QueryParamsResponse -QueryParamsResponse defines the response type for querying x/bank parameters. +### MsgRevokeAuthorizationResponse +MsgRevokeAuthorizationResponse defines the Msg/MsgRevokeAuthorizationResponse response type. -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `params` | [Params](#cosmos.bank.v1beta1.Params) | | | + + + - -### QuerySupplyOfRequest -QuerySupplyOfRequest is the request type for the Query/SupplyOf RPC method. + + +### Msg +Msg defines the authz Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `GrantAuthorization` | [MsgGrantAuthorizationRequest](#cosmos.authz.v1beta1.MsgGrantAuthorizationRequest) | [MsgGrantAuthorizationResponse](#cosmos.authz.v1beta1.MsgGrantAuthorizationResponse) | GrantAuthorization grants the provided authorization to the grantee on the granter's account with the provided expiration time. | | +| `ExecAuthorized` | [MsgExecAuthorizedRequest](#cosmos.authz.v1beta1.MsgExecAuthorizedRequest) | [MsgExecAuthorizedResponse](#cosmos.authz.v1beta1.MsgExecAuthorizedResponse) | ExecAuthorized attempts to execute the provided messages using authorizations granted to the grantee. Each message should have only one signer corresponding to the granter of the authorization. | | +| `RevokeAuthorization` | [MsgRevokeAuthorizationRequest](#cosmos.authz.v1beta1.MsgRevokeAuthorizationRequest) | [MsgRevokeAuthorizationResponse](#cosmos.authz.v1beta1.MsgRevokeAuthorizationResponse) | RevokeAuthorization revokes any authorization corresponding to the provided method name on the granter's account that has been granted to the grantee. | | + + + + + + +

Top

+ +## cosmos/authz/v1beta1/genesis.proto + + + + + +### GenesisState +GenesisState defines the authz module's genesis state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `denom` | [string](#string) | | denom is the coin denom to query balances for. | +| `authorization` | [GrantAuthorization](#cosmos.authz.v1beta1.GrantAuthorization) | repeated | | - + -### QuerySupplyOfResponse -QuerySupplyOfResponse is the response type for the Query/SupplyOf RPC method. +### GrantAuthorization +GrantAuthorization defines the GenesisState/GrantAuthorization type. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | amount is the supply of the coin. | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | +| `authorization` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `expiration` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | + - + -### QueryTotalSupplyRequest -QueryTotalSupplyRequest is the request type for the Query/TotalSupply RPC -method. + + + +

Top

+## cosmos/base/query/v1beta1/pagination.proto - -### QueryTotalSupplyResponse -QueryTotalSupplyResponse is the response type for the Query/TotalSupply RPC -method + + + +### 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 | | ----- | ---- | ----- | ----------- | -| `supply` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | supply is the supply of the coins | +| `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; + } - -### Query -Query defines the gRPC querier service. +| 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 | -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `Balance` | [QueryBalanceRequest](#cosmos.bank.v1beta1.QueryBalanceRequest) | [QueryBalanceResponse](#cosmos.bank.v1beta1.QueryBalanceResponse) | Balance queries the balance of a single coin for a single account. | GET|/cosmos/bank/v1beta1/balances/{address}/{denom}| -| `AllBalances` | [QueryAllBalancesRequest](#cosmos.bank.v1beta1.QueryAllBalancesRequest) | [QueryAllBalancesResponse](#cosmos.bank.v1beta1.QueryAllBalancesResponse) | AllBalances queries the balance of all coins for a single account. | GET|/cosmos/bank/v1beta1/balances/{address}| -| `TotalSupply` | [QueryTotalSupplyRequest](#cosmos.bank.v1beta1.QueryTotalSupplyRequest) | [QueryTotalSupplyResponse](#cosmos.bank.v1beta1.QueryTotalSupplyResponse) | TotalSupply queries the total supply of all coins. | GET|/cosmos/bank/v1beta1/supply| -| `SupplyOf` | [QuerySupplyOfRequest](#cosmos.bank.v1beta1.QuerySupplyOfRequest) | [QuerySupplyOfResponse](#cosmos.bank.v1beta1.QuerySupplyOfResponse) | SupplyOf queries the supply of a single coin. | GET|/cosmos/bank/v1beta1/supply/{denom}| -| `Params` | [QueryParamsRequest](#cosmos.bank.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.bank.v1beta1.QueryParamsResponse) | Params queries the parameters of x/bank module. | GET|/cosmos/bank/v1beta1/params| + + + + + + + + + - +

Top

-## cosmos/bank/v1beta1/tx.proto +## cosmos/authz/v1beta1/query.proto - + -### MsgMultiSend -MsgMultiSend represents an arbitrary multi-in, multi-out send message. +### QueryAuthorizationRequest +QueryAuthorizationRequest is the request type for the Query/Authorization RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `inputs` | [Input](#cosmos.bank.v1beta1.Input) | repeated | | -| `outputs` | [Output](#cosmos.bank.v1beta1.Output) | repeated | | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | +| `method_name` | [string](#string) | | | - + + +### QueryAuthorizationResponse +QueryAuthorizationResponse is the response type for the Query/Authorization RPC method. -### MsgMultiSendResponse -MsgMultiSendResponse defines the Msg/MultiSend response type. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `authorization` | [AuthorizationGrant](#cosmos.authz.v1beta1.AuthorizationGrant) | | authorization is a authorization granted for grantee by granter. | - -### MsgSend -MsgSend represents a message to send coins from one account to another. + + +### QueryAuthorizationsRequest +QueryAuthorizationsRequest is the request type for the Query/Authorizations RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `from_address` | [string](#string) | | | -| `to_address` | [string](#string) | | | -| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an pagination for the request. | - + -### MsgSendResponse -MsgSendResponse defines the Msg/Send response type. +### 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. | @@ -1445,202 +1585,190 @@ MsgSendResponse defines the Msg/Send response type. - + -### Msg -Msg defines the bank Msg service. +### Query +Query defines the gRPC querier service. | Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | | ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `Send` | [MsgSend](#cosmos.bank.v1beta1.MsgSend) | [MsgSendResponse](#cosmos.bank.v1beta1.MsgSendResponse) | Send defines a method for sending coins from one account to another account. | | -| `MultiSend` | [MsgMultiSend](#cosmos.bank.v1beta1.MsgMultiSend) | [MsgMultiSendResponse](#cosmos.bank.v1beta1.MsgMultiSendResponse) | MultiSend defines a method for sending coins from some accounts to other accounts. | | +| `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

-## cosmos/base/abci/v1beta1/abci.proto +## cosmos/bank/v1beta1/bank.proto - + -### ABCIMessageLog -ABCIMessageLog defines a structure containing an indexed tx ABCI message log. +### DenomUnit +DenomUnit represents a struct that describes a given +denomination unit of the basic token. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `msg_index` | [uint32](#uint32) | | | -| `log` | [string](#string) | | | -| `events` | [StringEvent](#cosmos.base.abci.v1beta1.StringEvent) | repeated | Events contains a slice of Event objects that were emitted during some execution. | +| `denom` | [string](#string) | | denom represents the string name of the given denom unit (e.g uatom). | +| `exponent` | [uint32](#uint32) | | exponent represents power of 10 exponent that one must raise the base_denom to in order to equal the given DenomUnit's denom 1 denom = 1^exponent base_denom (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with exponent = 6, thus: 1 atom = 10^6 uatom). | +| `aliases` | [string](#string) | repeated | aliases is a list of string aliases for the given denom | - + -### Attribute -Attribute defines an attribute wrapper where the key and value are -strings instead of raw bytes. +### Input +Input models transaction input. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `key` | [string](#string) | | | -| `value` | [string](#string) | | | +| `address` | [string](#string) | | | +| `coins` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | - + -### GasInfo -GasInfo defines tx execution gas context. +### Metadata +Metadata represents a struct that describes +a basic token. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `gas_wanted` | [uint64](#uint64) | | GasWanted is the maximum units of work we allow this tx to perform. | -| `gas_used` | [uint64](#uint64) | | GasUsed is the amount of gas actually consumed. | +| `description` | [string](#string) | | | +| `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. | - + -### MsgData -MsgData defines the data returned in a Result object during message -execution. +### Output +Output models transaction outputs. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `msg_type` | [string](#string) | | | -| `data` | [bytes](#bytes) | | | +| `address` | [string](#string) | | | +| `coins` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | - + -### Result -Result is the union of ResponseFormat and ResponseCheckTx. +### Params +Params defines the parameters for the bank module. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `data` | [bytes](#bytes) | | Data is any data returned from message or handler execution. It MUST be length prefixed in order to separate data from multiple message executions. | -| `log` | [string](#string) | | Log contains the log information from message or handler execution. | -| `events` | [tendermint.abci.Event](#tendermint.abci.Event) | repeated | Events contains a slice of Event objects that were emitted during message or handler execution. | +| `send_enabled` | [SendEnabled](#cosmos.bank.v1beta1.SendEnabled) | repeated | | +| `default_send_enabled` | [bool](#bool) | | | - + -### SearchTxsResult -SearchTxsResult defines a structure for querying txs pageable +### SendEnabled +SendEnabled maps coin denom to a send_enabled status (whether a denom is +sendable). | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `total_count` | [uint64](#uint64) | | Count of all txs | -| `count` | [uint64](#uint64) | | Count of txs in current page | -| `page_number` | [uint64](#uint64) | | Index of current page, start from 1 | -| `page_total` | [uint64](#uint64) | | Count of total pages | -| `limit` | [uint64](#uint64) | | Max count txs per page | -| `txs` | [TxResponse](#cosmos.base.abci.v1beta1.TxResponse) | repeated | List of txs in current page | +| `denom` | [string](#string) | | | +| `enabled` | [bool](#bool) | | | - + -### SimulationResponse -SimulationResponse defines the response generated when a transaction is -successfully simulated. +### Supply +Supply represents a struct that passively keeps track of the total supply +amounts in the network. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `gas_info` | [GasInfo](#cosmos.base.abci.v1beta1.GasInfo) | | | -| `result` | [Result](#cosmos.base.abci.v1beta1.Result) | | | +| `total` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | + - + -### StringEvent -StringEvent defines en Event object wrapper where all the attributes -contain key/value pairs that are strings instead of raw bytes. + + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `type` | [string](#string) | | | -| `attributes` | [Attribute](#cosmos.base.abci.v1beta1.Attribute) | repeated | | + +

Top

+## cosmos/bank/v1beta1/genesis.proto - + -### TxMsgData -TxMsgData defines a list of MsgData. A transaction will have a MsgData object -for each message. +### Balance +Balance defines an account address and balance pair used in the bank module's +genesis state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `data` | [MsgData](#cosmos.base.abci.v1beta1.MsgData) | repeated | | +| `address` | [string](#string) | | address is the address of the balance holder. | +| `coins` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | coins defines the different coins this balance holds. | - + -### TxResponse -TxResponse defines a structure containing relevant tx data and metadata. The -tags are stringified and the log is JSON decoded. +### GenesisState +GenesisState defines the bank module's genesis state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `height` | [int64](#int64) | | The block height | -| `txhash` | [string](#string) | | The transaction hash. | -| `codespace` | [string](#string) | | Namespace for the Code | -| `code` | [uint32](#uint32) | | Response code. | -| `data` | [string](#string) | | Result bytes, if any. | -| `raw_log` | [string](#string) | | The output of the application's logger (raw string). May be non-deterministic. | -| `logs` | [ABCIMessageLog](#cosmos.base.abci.v1beta1.ABCIMessageLog) | repeated | The output of the application's logger (typed). May be non-deterministic. | -| `info` | [string](#string) | | Additional information. May be non-deterministic. | -| `gas_wanted` | [int64](#int64) | | Amount of gas requested for transaction. | -| `gas_used` | [int64](#int64) | | Amount of gas consumed by transaction. | -| `tx` | [google.protobuf.Any](#google.protobuf.Any) | | The request transaction bytes. | -| `timestamp` | [string](#string) | | Time of the previous block. For heights > 1, it's the weighted median of the timestamps of the valid votes in the block.LastCommit. For height == 1, it's genesis time. | +| `params` | [Params](#cosmos.bank.v1beta1.Params) | | params defines all the paramaters of the module. | +| `balances` | [Balance](#cosmos.bank.v1beta1.Balance) | repeated | balances is an array containing the balances of all the accounts. | +| `supply` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | supply represents the total supply. | +| `denom_metadata` | [Metadata](#cosmos.bank.v1beta1.Metadata) | repeated | denom_metadata defines the metadata of the differents coins. | @@ -1656,239 +1784,216 @@ tags are stringified and the log is JSON decoded. - +

Top

-## cosmos/base/kv/v1beta1/kv.proto +## cosmos/bank/v1beta1/query.proto - + -### Pair -Pair defines a key/value bytes tuple. +### QueryAllBalancesRequest +QueryBalanceRequest is the request type for the Query/AllBalances RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `key` | [bytes](#bytes) | | | -| `value` | [bytes](#bytes) | | | +| `address` | [string](#string) | | address is the address to query balances for. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - + -### Pairs -Pairs defines a repeated slice of Pair objects. +### QueryAllBalancesResponse +QueryAllBalancesResponse is the response type for the Query/AllBalances RPC +method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `pairs` | [Pair](#cosmos.base.kv.v1beta1.Pair) | repeated | | +| `balances` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | balances is the balances of all the coins. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | - - + - +### QueryBalanceRequest +QueryBalanceRequest is the request type for the Query/Balance RPC method. - +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | address is the address to query balances for. | +| `denom` | [string](#string) | | denom is the coin denom to query balances for. | - -

Top

-## cosmos/base/reflection/v1beta1/reflection.proto - + + +### QueryBalanceResponse +QueryBalanceResponse is the response type for the Query/Balance RPC method. -### ListAllInterfacesRequest -ListAllInterfacesRequest is the request type of the ListAllInterfaces RPC. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `balance` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | balance is the balance of the coin. | - -### ListAllInterfacesResponse -ListAllInterfacesResponse is the response type of the ListAllInterfaces RPC. + + +### QueryDenomMetadataRequest +QueryDenomMetadataRequest is the request type for the Query/DenomMetadata RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `interface_names` | [string](#string) | repeated | interface_names is an array of all the registered interfaces. | +| `denom` | [string](#string) | | denom is the coin denom to query the metadata for. | - + -### ListImplementationsRequest -ListImplementationsRequest is the request type of the ListImplementations -RPC. +### QueryDenomMetadataResponse +QueryDenomMetadataResponse is the response type for the Query/DenomMetadata RPC +method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `interface_name` | [string](#string) | | interface_name defines the interface to query the implementations for. | +| `metadata` | [Metadata](#cosmos.bank.v1beta1.Metadata) | | metadata describes and provides all the client information for the requested token. | - + -### ListImplementationsResponse -ListImplementationsResponse is the response type of the ListImplementations -RPC. +### QueryDenomsMetadataRequest +QueryDenomsMetadataRequest is the request type for the Query/DenomsMetadata RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `implementation_message_names` | [string](#string) | repeated | | - - - - - - - - +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - - -### ReflectionService -ReflectionService defines a service for interface reflection. -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `ListAllInterfaces` | [ListAllInterfacesRequest](#cosmos.base.reflection.v1beta1.ListAllInterfacesRequest) | [ListAllInterfacesResponse](#cosmos.base.reflection.v1beta1.ListAllInterfacesResponse) | ListAllInterfaces lists all the interfaces registered in the interface registry. | GET|/cosmos/base/reflection/v1beta1/interfaces| -| `ListImplementations` | [ListImplementationsRequest](#cosmos.base.reflection.v1beta1.ListImplementationsRequest) | [ListImplementationsResponse](#cosmos.base.reflection.v1beta1.ListImplementationsResponse) | ListImplementations list all the concrete types that implement a given interface. | GET|/cosmos/base/reflection/v1beta1/interfaces/{interface_name}/implementations| - + +### QueryDenomsMetadataResponse +QueryDenomsMetadataResponse is the response type for the Query/DenomsMetadata RPC +method. - -

Top

+| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `metadatas` | [Metadata](#cosmos.bank.v1beta1.Metadata) | repeated | metadata provides the client information for all the registered tokens. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | -## cosmos/base/snapshots/v1beta1/snapshot.proto - -### Metadata -Metadata contains SDK-specific snapshot metadata. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `chunk_hashes` | [bytes](#bytes) | repeated | SHA-256 chunk hashes | +### QueryParamsRequest +QueryParamsRequest defines the request type for querying x/bank parameters. - + -### Snapshot -Snapshot contains Tendermint state sync snapshot info. +### QueryParamsResponse +QueryParamsResponse defines the response type for querying x/bank parameters. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `height` | [uint64](#uint64) | | | -| `format` | [uint32](#uint32) | | | -| `chunks` | [uint32](#uint32) | | | -| `hash` | [bytes](#bytes) | | | -| `metadata` | [Metadata](#cosmos.base.snapshots.v1beta1.Metadata) | | | +| `params` | [Params](#cosmos.bank.v1beta1.Params) | | | - - + - +### QuerySupplyOfRequest +QuerySupplyOfRequest is the request type for the Query/SupplyOf RPC method. - +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `denom` | [string](#string) | | denom is the coin denom to query balances for. | - -

Top

-## cosmos/base/store/v1beta1/commit_info.proto - + -### CommitID -CommitID defines the committment information when a specific store is -committed. +### QuerySupplyOfResponse +QuerySupplyOfResponse is the response type for the Query/SupplyOf RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `version` | [int64](#int64) | | | -| `hash` | [bytes](#bytes) | | | - - +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | amount is the supply of the coin. | - -### CommitInfo -CommitInfo defines commit information used by the multi-store when committing -a version/height. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `version` | [int64](#int64) | | | -| `store_infos` | [StoreInfo](#cosmos.base.store.v1beta1.StoreInfo) | repeated | | +### QueryTotalSupplyRequest +QueryTotalSupplyRequest is the request type for the Query/TotalSupply RPC +method. - + -### StoreInfo -StoreInfo defines store-specific commit information. It contains a reference -between a store name and the commit ID. +### QueryTotalSupplyResponse +QueryTotalSupplyResponse is the response type for the Query/TotalSupply RPC +method | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `name` | [string](#string) | | | -| `commit_id` | [CommitID](#cosmos.base.store.v1beta1.CommitID) | | | +| `supply` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | supply is the supply of the coins | @@ -1900,305 +2005,339 @@ between a store name and the commit ID. + + + +### Query +Query defines the gRPC querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Balance` | [QueryBalanceRequest](#cosmos.bank.v1beta1.QueryBalanceRequest) | [QueryBalanceResponse](#cosmos.bank.v1beta1.QueryBalanceResponse) | Balance queries the balance of a single coin for a single account. | GET|/cosmos/bank/v1beta1/balances/{address}/{denom}| +| `AllBalances` | [QueryAllBalancesRequest](#cosmos.bank.v1beta1.QueryAllBalancesRequest) | [QueryAllBalancesResponse](#cosmos.bank.v1beta1.QueryAllBalancesResponse) | AllBalances queries the balance of all coins for a single account. | GET|/cosmos/bank/v1beta1/balances/{address}| +| `TotalSupply` | [QueryTotalSupplyRequest](#cosmos.bank.v1beta1.QueryTotalSupplyRequest) | [QueryTotalSupplyResponse](#cosmos.bank.v1beta1.QueryTotalSupplyResponse) | TotalSupply queries the total supply of all coins. | GET|/cosmos/bank/v1beta1/supply| +| `SupplyOf` | [QuerySupplyOfRequest](#cosmos.bank.v1beta1.QuerySupplyOfRequest) | [QuerySupplyOfResponse](#cosmos.bank.v1beta1.QuerySupplyOfResponse) | SupplyOf queries the supply of a single coin. | GET|/cosmos/bank/v1beta1/supply/{denom}| +| `Params` | [QueryParamsRequest](#cosmos.bank.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.bank.v1beta1.QueryParamsResponse) | Params queries the parameters of x/bank module. | GET|/cosmos/bank/v1beta1/params| +| `DenomMetadata` | [QueryDenomMetadataRequest](#cosmos.bank.v1beta1.QueryDenomMetadataRequest) | [QueryDenomMetadataResponse](#cosmos.bank.v1beta1.QueryDenomMetadataResponse) | DenomsMetadata queries the client metadata of a given coin denomination. | GET|/cosmos/bank/v1beta1/denoms_metadata/{denom}| +| `DenomsMetadata` | [QueryDenomsMetadataRequest](#cosmos.bank.v1beta1.QueryDenomsMetadataRequest) | [QueryDenomsMetadataResponse](#cosmos.bank.v1beta1.QueryDenomsMetadataResponse) | DenomsMetadata queries the client metadata for all registered coin denominations. | GET|/cosmos/bank/v1beta1/denoms_metadata| + - +

Top

-## cosmos/base/store/v1beta1/snapshot.proto +## cosmos/bank/v1beta1/tx.proto - + -### SnapshotIAVLItem -SnapshotIAVLItem is an exported IAVL node. +### MsgMultiSend +MsgMultiSend represents an arbitrary multi-in, multi-out send message. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `key` | [bytes](#bytes) | | | -| `value` | [bytes](#bytes) | | | -| `version` | [int64](#int64) | | | -| `height` | [int32](#int32) | | | - - +| `inputs` | [Input](#cosmos.bank.v1beta1.Input) | repeated | | +| `outputs` | [Output](#cosmos.bank.v1beta1.Output) | repeated | | - -### SnapshotItem -SnapshotItem is an item contained in a rootmulti.Store snapshot. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `store` | [SnapshotStoreItem](#cosmos.base.store.v1beta1.SnapshotStoreItem) | | | -| `iavl` | [SnapshotIAVLItem](#cosmos.base.store.v1beta1.SnapshotIAVLItem) | | | +### MsgMultiSendResponse +MsgMultiSendResponse defines the Msg/MultiSend response type. - + -### SnapshotStoreItem -SnapshotStoreItem contains metadata about a snapshotted store. +### MsgSend +MsgSend represents a message to send coins from one account to another. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `name` | [string](#string) | | | +| `from_address` | [string](#string) | | | +| `to_address` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | - - + - +### MsgSendResponse +MsgSendResponse defines the Msg/Send response type. - - -

Top

-## cosmos/base/tendermint/v1beta1/query.proto + + + - -### GetBlockByHeightRequest -GetBlockByHeightRequest is the request type for the Query/GetBlockByHeight RPC method. + +### Msg +Msg defines the bank Msg service. -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `height` | [int64](#int64) | | | +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Send` | [MsgSend](#cosmos.bank.v1beta1.MsgSend) | [MsgSendResponse](#cosmos.bank.v1beta1.MsgSendResponse) | Send defines a method for sending coins from one account to another account. | | +| `MultiSend` | [MsgMultiSend](#cosmos.bank.v1beta1.MsgMultiSend) | [MsgMultiSendResponse](#cosmos.bank.v1beta1.MsgMultiSendResponse) | MultiSend defines a method for sending coins from some accounts to other accounts. | | + + +

Top

+## cosmos/base/kv/v1beta1/kv.proto - -### GetBlockByHeightResponse -GetBlockByHeightResponse is the response type for the Query/GetBlockByHeight RPC method. + + + +### Pair +Pair defines a key/value bytes tuple. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `block_id` | [tendermint.types.BlockID](#tendermint.types.BlockID) | | | -| `block` | [tendermint.types.Block](#tendermint.types.Block) | | | +| `key` | [bytes](#bytes) | | | +| `value` | [bytes](#bytes) | | | - + -### GetLatestBlockRequest -GetLatestBlockRequest is the request type for the Query/GetLatestBlock RPC method. +### Pairs +Pairs defines a repeated slice of Pair objects. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pairs` | [Pair](#cosmos.base.kv.v1beta1.Pair) | repeated | | - -### GetLatestBlockResponse -GetLatestBlockResponse is the response type for the Query/GetLatestBlock RPC method. + + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `block_id` | [tendermint.types.BlockID](#tendermint.types.BlockID) | | | -| `block` | [tendermint.types.Block](#tendermint.types.Block) | | | + + + +

Top

+## cosmos/base/reflection/v1beta1/reflection.proto - -### GetLatestValidatorSetRequest -GetLatestValidatorSetRequest is the request type for the Query/GetValidatorSetByHeight RPC method. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an pagination for the request. | +### ListAllInterfacesRequest +ListAllInterfacesRequest is the request type of the ListAllInterfaces RPC. - + -### GetLatestValidatorSetResponse -GetLatestValidatorSetResponse is the response type for the Query/GetValidatorSetByHeight RPC method. +### ListAllInterfacesResponse +ListAllInterfacesResponse is the response type of the ListAllInterfaces RPC. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `block_height` | [int64](#int64) | | | -| `validators` | [Validator](#cosmos.base.tendermint.v1beta1.Validator) | repeated | | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines an pagination for the response. | +| `interface_names` | [string](#string) | repeated | interface_names is an array of all the registered interfaces. | - + -### GetNodeInfoRequest -GetNodeInfoRequest is the request type for the Query/GetNodeInfo RPC method. +### ListImplementationsRequest +ListImplementationsRequest is the request type of the ListImplementations +RPC. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `interface_name` | [string](#string) | | interface_name defines the interface to query the implementations for. | - -### GetNodeInfoResponse -GetNodeInfoResponse is the request type for the Query/GetNodeInfo RPC method. + + + +### ListImplementationsResponse +ListImplementationsResponse is the response type of the ListImplementations +RPC. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `default_node_info` | [tendermint.p2p.DefaultNodeInfo](#tendermint.p2p.DefaultNodeInfo) | | | -| `application_version` | [VersionInfo](#cosmos.base.tendermint.v1beta1.VersionInfo) | | | +| `implementation_message_names` | [string](#string) | repeated | | + - + -### GetSyncingRequest -GetSyncingRequest is the request type for the Query/GetSyncing RPC method. + + +### ReflectionService +ReflectionService defines a service for interface reflection. +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `ListAllInterfaces` | [ListAllInterfacesRequest](#cosmos.base.reflection.v1beta1.ListAllInterfacesRequest) | [ListAllInterfacesResponse](#cosmos.base.reflection.v1beta1.ListAllInterfacesResponse) | ListAllInterfaces lists all the interfaces registered in the interface registry. | GET|/cosmos/base/reflection/v1beta1/interfaces| +| `ListImplementations` | [ListImplementationsRequest](#cosmos.base.reflection.v1beta1.ListImplementationsRequest) | [ListImplementationsResponse](#cosmos.base.reflection.v1beta1.ListImplementationsResponse) | ListImplementations list all the concrete types that implement a given interface. | GET|/cosmos/base/reflection/v1beta1/interfaces/{interface_name}/implementations| + - -### GetSyncingResponse -GetSyncingResponse is the response type for the Query/GetSyncing RPC method. + + +

Top

+ +## cosmos/base/snapshots/v1beta1/snapshot.proto + + + + + +### Metadata +Metadata contains SDK-specific snapshot metadata. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `syncing` | [bool](#bool) | | | +| `chunk_hashes` | [bytes](#bytes) | repeated | SHA-256 chunk hashes | - + -### GetValidatorSetByHeightRequest -GetValidatorSetByHeightRequest is the request type for the Query/GetValidatorSetByHeight RPC method. +### Snapshot +Snapshot contains Tendermint state sync snapshot info. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `height` | [int64](#int64) | | | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an pagination for the request. | +| `height` | [uint64](#uint64) | | | +| `format` | [uint32](#uint32) | | | +| `chunks` | [uint32](#uint32) | | | +| `hash` | [bytes](#bytes) | | | +| `metadata` | [Metadata](#cosmos.base.snapshots.v1beta1.Metadata) | | | + - + -### GetValidatorSetByHeightResponse -GetValidatorSetByHeightResponse is the response type for the Query/GetValidatorSetByHeight RPC method. + + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `block_height` | [int64](#int64) | | | -| `validators` | [Validator](#cosmos.base.tendermint.v1beta1.Validator) | repeated | | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines an pagination for the response. | + +

Top

+## cosmos/base/store/v1beta1/commit_info.proto - + -### Module -Module is the type for VersionInfo +### CommitID +CommitID defines the committment information when a specific store is +committed. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `path` | [string](#string) | | module path | -| `version` | [string](#string) | | module version | -| `sum` | [string](#string) | | checksum | +| `version` | [int64](#int64) | | | +| `hash` | [bytes](#bytes) | | | - + -### Validator -Validator is the type for the validator-set. +### CommitInfo +CommitInfo defines commit information used by the multi-store when committing +a version/height. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `address` | [string](#string) | | | -| `pub_key` | [google.protobuf.Any](#google.protobuf.Any) | | | -| `voting_power` | [int64](#int64) | | | -| `proposer_priority` | [int64](#int64) | | | +| `version` | [int64](#int64) | | | +| `store_infos` | [StoreInfo](#cosmos.base.store.v1beta1.StoreInfo) | repeated | | - + -### VersionInfo -VersionInfo is the type for the GetNodeInfoResponse message. +### StoreInfo +StoreInfo defines store-specific commit information. It contains a reference +between a store name and the commit ID. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `name` | [string](#string) | | | -| `app_name` | [string](#string) | | | -| `version` | [string](#string) | | | -| `git_commit` | [string](#string) | | | -| `build_tags` | [string](#string) | | | -| `go_version` | [string](#string) | | | -| `build_deps` | [Module](#cosmos.base.tendermint.v1beta1.Module) | repeated | | +| `commit_id` | [CommitID](#cosmos.base.store.v1beta1.CommitID) | | | @@ -2210,74 +2349,59 @@ VersionInfo is the type for the GetNodeInfoResponse message. - - - -### Service -Service defines the gRPC querier service for tendermint queries. - -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `GetNodeInfo` | [GetNodeInfoRequest](#cosmos.base.tendermint.v1beta1.GetNodeInfoRequest) | [GetNodeInfoResponse](#cosmos.base.tendermint.v1beta1.GetNodeInfoResponse) | GetNodeInfo queries the current node info. | GET|/cosmos/base/tendermint/v1beta1/node_info| -| `GetSyncing` | [GetSyncingRequest](#cosmos.base.tendermint.v1beta1.GetSyncingRequest) | [GetSyncingResponse](#cosmos.base.tendermint.v1beta1.GetSyncingResponse) | GetSyncing queries node syncing. | GET|/cosmos/base/tendermint/v1beta1/syncing| -| `GetLatestBlock` | [GetLatestBlockRequest](#cosmos.base.tendermint.v1beta1.GetLatestBlockRequest) | [GetLatestBlockResponse](#cosmos.base.tendermint.v1beta1.GetLatestBlockResponse) | GetLatestBlock returns the latest block. | GET|/cosmos/base/tendermint/v1beta1/blocks/latest| -| `GetBlockByHeight` | [GetBlockByHeightRequest](#cosmos.base.tendermint.v1beta1.GetBlockByHeightRequest) | [GetBlockByHeightResponse](#cosmos.base.tendermint.v1beta1.GetBlockByHeightResponse) | GetBlockByHeight queries block for given height. | GET|/cosmos/base/tendermint/v1beta1/blocks/{height}| -| `GetLatestValidatorSet` | [GetLatestValidatorSetRequest](#cosmos.base.tendermint.v1beta1.GetLatestValidatorSetRequest) | [GetLatestValidatorSetResponse](#cosmos.base.tendermint.v1beta1.GetLatestValidatorSetResponse) | GetLatestValidatorSet queries latest validator-set. | GET|/cosmos/base/tendermint/v1beta1/validators/latest| -| `GetValidatorSetByHeight` | [GetValidatorSetByHeightRequest](#cosmos.base.tendermint.v1beta1.GetValidatorSetByHeightRequest) | [GetValidatorSetByHeightResponse](#cosmos.base.tendermint.v1beta1.GetValidatorSetByHeightResponse) | GetValidatorSetByHeight queries validator-set at a given height. | GET|/cosmos/base/tendermint/v1beta1/validators/{height}| - - +

Top

-## cosmos/capability/v1beta1/capability.proto +## cosmos/base/store/v1beta1/snapshot.proto - + -### Capability -Capability defines an implementation of an object capability. The index -provided to a Capability must be globally unique. +### SnapshotIAVLItem +SnapshotIAVLItem is an exported IAVL node. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `index` | [uint64](#uint64) | | | +| `key` | [bytes](#bytes) | | | +| `value` | [bytes](#bytes) | | | +| `version` | [int64](#int64) | | | +| `height` | [int32](#int32) | | | - + -### CapabilityOwners -CapabilityOwners defines a set of owners of a single Capability. The set of -owners must be unique. +### SnapshotItem +SnapshotItem is an item contained in a rootmulti.Store snapshot. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `owners` | [Owner](#cosmos.capability.v1beta1.Owner) | repeated | | +| `store` | [SnapshotStoreItem](#cosmos.base.store.v1beta1.SnapshotStoreItem) | | | +| `iavl` | [SnapshotIAVLItem](#cosmos.base.store.v1beta1.SnapshotIAVLItem) | | | - + -### Owner -Owner defines a single capability owner. An owner is defined by the name of -capability and the module name. +### SnapshotStoreItem +SnapshotStoreItem contains metadata about a snapshotted store. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `module` | [string](#string) | | | | `name` | [string](#string) | | | @@ -2294,207 +2418,236 @@ capability and the module name. - +

Top

-## cosmos/capability/v1beta1/genesis.proto +## cosmos/base/tendermint/v1beta1/query.proto - + -### GenesisOwners -GenesisOwners defines the capability owners with their corresponding index. +### GetBlockByHeightRequest +GetBlockByHeightRequest is the request type for the Query/GetBlockByHeight RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `index` | [uint64](#uint64) | | index is the index of the capability owner. | -| `index_owners` | [CapabilityOwners](#cosmos.capability.v1beta1.CapabilityOwners) | | index_owners are the owners at the given index. | +| `height` | [int64](#int64) | | | - + -### GenesisState -GenesisState defines the capability module's genesis state. +### GetBlockByHeightResponse +GetBlockByHeightResponse is the response type for the Query/GetBlockByHeight RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `index` | [uint64](#uint64) | | index is the capability global index. | -| `owners` | [GenesisOwners](#cosmos.capability.v1beta1.GenesisOwners) | repeated | owners represents a map from index to owners of the capability index index key is string to allow amino marshalling. | +| `block_id` | [tendermint.types.BlockID](#tendermint.types.BlockID) | | | +| `block` | [tendermint.types.Block](#tendermint.types.Block) | | | - - + - +### GetLatestBlockRequest +GetLatestBlockRequest is the request type for the Query/GetLatestBlock RPC method. - - -

Top

-## cosmos/crisis/v1beta1/genesis.proto + + + +### GetLatestBlockResponse +GetLatestBlockResponse is the response type for the Query/GetLatestBlock RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `block_id` | [tendermint.types.BlockID](#tendermint.types.BlockID) | | | +| `block` | [tendermint.types.Block](#tendermint.types.Block) | | | + - -### GenesisState -GenesisState defines the crisis module's genesis state. + + + +### GetLatestValidatorSetRequest +GetLatestValidatorSetRequest is the request type for the Query/GetValidatorSetByHeight RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `constant_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | constant_fee is the fee used to verify the invariant in the crisis module. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an pagination for the request. | - - + - +### GetLatestValidatorSetResponse +GetLatestValidatorSetResponse is the response type for the Query/GetValidatorSetByHeight RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `block_height` | [int64](#int64) | | | +| `validators` | [Validator](#cosmos.base.tendermint.v1beta1.Validator) | repeated | | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines an pagination for the response. | - - -

Top

-## cosmos/crisis/v1beta1/tx.proto + + + +### GetNodeInfoRequest +GetNodeInfoRequest is the request type for the Query/GetNodeInfo RPC method. - -### MsgVerifyInvariant -MsgVerifyInvariant represents a message to verify a particular invariance. + + + + +### GetNodeInfoResponse +GetNodeInfoResponse is the request type for the Query/GetNodeInfo RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `sender` | [string](#string) | | | -| `invariant_module_name` | [string](#string) | | | -| `invariant_route` | [string](#string) | | | +| `default_node_info` | [tendermint.p2p.DefaultNodeInfo](#tendermint.p2p.DefaultNodeInfo) | | | +| `application_version` | [VersionInfo](#cosmos.base.tendermint.v1beta1.VersionInfo) | | | - + -### MsgVerifyInvariantResponse -MsgVerifyInvariantResponse defines the Msg/VerifyInvariant response type. +### GetSyncingRequest +GetSyncingRequest is the request type for the Query/GetSyncing RPC method. - - + - +### GetSyncingResponse +GetSyncingResponse is the response type for the Query/GetSyncing RPC method. - +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `syncing` | [bool](#bool) | | | -### Msg -Msg defines the bank Msg service. -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `VerifyInvariant` | [MsgVerifyInvariant](#cosmos.crisis.v1beta1.MsgVerifyInvariant) | [MsgVerifyInvariantResponse](#cosmos.crisis.v1beta1.MsgVerifyInvariantResponse) | VerifyInvariant defines a method to verify a particular invariance. | | - - -

Top

+ + +### GetValidatorSetByHeightRequest +GetValidatorSetByHeightRequest is the request type for the Query/GetValidatorSetByHeight RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `height` | [int64](#int64) | | | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an pagination for the request. | -## cosmos/crypto/ed25519/keys.proto - -### PrivKey -PrivKey defines a ed25519 private key. + + + +### GetValidatorSetByHeightResponse +GetValidatorSetByHeightResponse is the response type for the Query/GetValidatorSetByHeight RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `key` | [bytes](#bytes) | | | +| `block_height` | [int64](#int64) | | | +| `validators` | [Validator](#cosmos.base.tendermint.v1beta1.Validator) | repeated | | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines an pagination for the response. | - + -### 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. +### Module +Module is the type for VersionInfo | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `key` | [bytes](#bytes) | | | +| `path` | [string](#string) | | module path | +| `version` | [string](#string) | | module version | +| `sum` | [string](#string) | | checksum | - - + - +### Validator +Validator is the type for the validator-set. - +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | | +| `pub_key` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `voting_power` | [int64](#int64) | | | +| `proposer_priority` | [int64](#int64) | | | - -

Top

-## cosmos/crypto/multisig/keys.proto - + -### LegacyAminoPubKey -LegacyAminoPubKey specifies a public key type -which nests multiple public keys and a threshold, -it uses legacy amino address rules. +### VersionInfo +VersionInfo is the type for the GetNodeInfoResponse message. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `threshold` | [uint32](#uint32) | | | -| `public_keys` | [google.protobuf.Any](#google.protobuf.Any) | repeated | | +| `name` | [string](#string) | | | +| `app_name` | [string](#string) | | | +| `version` | [string](#string) | | | +| `git_commit` | [string](#string) | | | +| `build_tags` | [string](#string) | | | +| `go_version` | [string](#string) | | | +| `build_deps` | [Module](#cosmos.base.tendermint.v1beta1.Module) | repeated | | @@ -2506,47 +2659,75 @@ it uses legacy amino address rules. + + + +### Service +Service defines the gRPC querier service for tendermint queries. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `GetNodeInfo` | [GetNodeInfoRequest](#cosmos.base.tendermint.v1beta1.GetNodeInfoRequest) | [GetNodeInfoResponse](#cosmos.base.tendermint.v1beta1.GetNodeInfoResponse) | GetNodeInfo queries the current node info. | GET|/cosmos/base/tendermint/v1beta1/node_info| +| `GetSyncing` | [GetSyncingRequest](#cosmos.base.tendermint.v1beta1.GetSyncingRequest) | [GetSyncingResponse](#cosmos.base.tendermint.v1beta1.GetSyncingResponse) | GetSyncing queries node syncing. | GET|/cosmos/base/tendermint/v1beta1/syncing| +| `GetLatestBlock` | [GetLatestBlockRequest](#cosmos.base.tendermint.v1beta1.GetLatestBlockRequest) | [GetLatestBlockResponse](#cosmos.base.tendermint.v1beta1.GetLatestBlockResponse) | GetLatestBlock returns the latest block. | GET|/cosmos/base/tendermint/v1beta1/blocks/latest| +| `GetBlockByHeight` | [GetBlockByHeightRequest](#cosmos.base.tendermint.v1beta1.GetBlockByHeightRequest) | [GetBlockByHeightResponse](#cosmos.base.tendermint.v1beta1.GetBlockByHeightResponse) | GetBlockByHeight queries block for given height. | GET|/cosmos/base/tendermint/v1beta1/blocks/{height}| +| `GetLatestValidatorSet` | [GetLatestValidatorSetRequest](#cosmos.base.tendermint.v1beta1.GetLatestValidatorSetRequest) | [GetLatestValidatorSetResponse](#cosmos.base.tendermint.v1beta1.GetLatestValidatorSetResponse) | GetLatestValidatorSet queries latest validator-set. | GET|/cosmos/base/tendermint/v1beta1/validatorsets/latest| +| `GetValidatorSetByHeight` | [GetValidatorSetByHeightRequest](#cosmos.base.tendermint.v1beta1.GetValidatorSetByHeightRequest) | [GetValidatorSetByHeightResponse](#cosmos.base.tendermint.v1beta1.GetValidatorSetByHeightResponse) | GetValidatorSetByHeight queries validator-set at a given height. | GET|/cosmos/base/tendermint/v1beta1/validatorsets/{height}| + - +

Top

-## cosmos/crypto/multisig/v1beta1/multisig.proto +## cosmos/capability/v1beta1/capability.proto - + -### CompactBitArray -CompactBitArray is an implementation of a space efficient bit array. -This is used to ensure that the encoded data takes up a minimal amount of -space after proto encoding. -This is not thread safe, and is not intended for concurrent usage. +### Capability +Capability defines an implementation of an object capability. The index +provided to a Capability must be globally unique. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `extra_bits_stored` | [uint32](#uint32) | | | -| `elems` | [bytes](#bytes) | | | +| `index` | [uint64](#uint64) | | | - + -### MultiSignature -MultiSignature wraps the signatures from a multisig.LegacyAminoPubKey. -See cosmos.tx.v1betata1.ModeInfo.Multi for how to specify which signers -signed and with which modes. +### CapabilityOwners +CapabilityOwners defines a set of owners of a single Capability. The set of +owners must be unique. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `signatures` | [bytes](#bytes) | repeated | | +| `owners` | [Owner](#cosmos.capability.v1beta1.Owner) | repeated | | + + + + + + + + +### Owner +Owner defines a single capability owner. An owner is defined by the name of +capability and the module name. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `module` | [string](#string) | | | +| `name` | [string](#string) | | | @@ -2562,41 +2743,39 @@ signed and with which modes. - +

Top

-## cosmos/crypto/secp256k1/keys.proto +## cosmos/capability/v1beta1/genesis.proto - + -### PrivKey -PrivKey defines a secp256k1 private key. +### GenesisOwners +GenesisOwners defines the capability owners with their corresponding index. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `key` | [bytes](#bytes) | | | +| `index` | [uint64](#uint64) | | index is the index of the capability owner. | +| `index_owners` | [CapabilityOwners](#cosmos.capability.v1beta1.CapabilityOwners) | | index_owners are the owners at the given index. | - + -### PubKey -PubKey defines a secp256k1 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. +### GenesisState +GenesisState defines the capability module's genesis state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `key` | [bytes](#bytes) | | | +| `index` | [uint64](#uint64) | | index is the capability global index. | +| `owners` | [GenesisOwners](#cosmos.capability.v1beta1.GenesisOwners) | repeated | owners represents a map from index to owners of the capability index index key is string to allow amino marshalling. | @@ -2612,390 +2791,959 @@ This prefix is followed with the x-coordinate. - +

Top

-## cosmos/distribution/v1beta1/distribution.proto +## cosmos/crisis/v1beta1/genesis.proto - + -### CommunityPoolSpendProposal -CommunityPoolSpendProposal details a proposal for use of community funds, -together with how many coins are proposed to be spent, and to which -recipient account. +### GenesisState +GenesisState defines the crisis module's genesis state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `title` | [string](#string) | | | -| `description` | [string](#string) | | | -| `recipient` | [string](#string) | | | -| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `constant_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | constant_fee is the fee used to verify the invariant in the crisis module. | + - + -### CommunityPoolSpendProposalWithDeposit + + + + + + + +

Top

+ +## cosmos/crisis/v1beta1/tx.proto + + + + + +### MsgVerifyInvariant +MsgVerifyInvariant represents a message to verify a particular invariance. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sender` | [string](#string) | | | +| `invariant_module_name` | [string](#string) | | | +| `invariant_route` | [string](#string) | | | + + + + + + + + +### MsgVerifyInvariantResponse +MsgVerifyInvariantResponse defines the Msg/VerifyInvariant response type. + + + + + + + + + + + + + + +### Msg +Msg defines the bank Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `VerifyInvariant` | [MsgVerifyInvariant](#cosmos.crisis.v1beta1.MsgVerifyInvariant) | [MsgVerifyInvariantResponse](#cosmos.crisis.v1beta1.MsgVerifyInvariantResponse) | VerifyInvariant defines a method to verify a particular invariance. | | + + + + + + +

Top

+ +## cosmos/crypto/ed25519/keys.proto + + + + + +### PrivKey +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 | +| ----- | ---- | ----- | ----------- | +| `key` | [bytes](#bytes) | | | + + + + + + + + +### PubKey +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 | +| ----- | ---- | ----- | ----------- | +| `key` | [bytes](#bytes) | | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/crypto/multisig/keys.proto + + + + + +### LegacyAminoPubKey +LegacyAminoPubKey specifies a public key type +which nests multiple public keys and a threshold, +it uses legacy amino address rules. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `threshold` | [uint32](#uint32) | | | +| `public_keys` | [google.protobuf.Any](#google.protobuf.Any) | repeated | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/crypto/multisig/v1beta1/multisig.proto + + + + + +### CompactBitArray +CompactBitArray is an implementation of a space efficient bit array. +This is used to ensure that the encoded data takes up a minimal amount of +space after proto encoding. +This is not thread safe, and is not intended for concurrent usage. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `extra_bits_stored` | [uint32](#uint32) | | | +| `elems` | [bytes](#bytes) | | | + + + + + + + + +### MultiSignature +MultiSignature wraps the signatures from a multisig.LegacyAminoPubKey. +See cosmos.tx.v1betata1.ModeInfo.Multi for how to specify which signers +signed and with which modes. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `signatures` | [bytes](#bytes) | repeated | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/crypto/secp256k1/keys.proto + + + + + +### PrivKey +PrivKey defines a secp256k1 private key. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [bytes](#bytes) | | | + + + + + + + + +### PubKey +PubKey defines a secp256k1 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. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [bytes](#bytes) | | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/distribution/v1beta1/distribution.proto + + + + + +### CommunityPoolSpendProposal +CommunityPoolSpendProposal details a proposal for use of community funds, +together with how many coins are proposed to be spent, and to which +recipient account. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `title` | [string](#string) | | | +| `description` | [string](#string) | | | +| `recipient` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | + + + + + + + + +### CommunityPoolSpendProposalWithDeposit CommunityPoolSpendProposalWithDeposit defines a CommunityPoolSpendProposal with a deposit | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `title` | [string](#string) | | | -| `description` | [string](#string) | | | -| `recipient` | [string](#string) | | | -| `amount` | [string](#string) | | | -| `deposit` | [string](#string) | | | +| `title` | [string](#string) | | | +| `description` | [string](#string) | | | +| `recipient` | [string](#string) | | | +| `amount` | [string](#string) | | | +| `deposit` | [string](#string) | | | + + + + + + + + +### DelegationDelegatorReward +DelegationDelegatorReward represents the properties +of a delegator's delegation reward. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_address` | [string](#string) | | | +| `reward` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | + + + + + + + + +### DelegatorStartingInfo +DelegatorStartingInfo represents the starting info for a delegator reward +period. It tracks the previous validator period, the delegation's amount of +staking token, and the creation height (to check later on if any slashes have +occurred). NOTE: Even though validators are slashed to whole staking tokens, +the delegators within the validator may be left with less than a full token, +thus sdk.Dec is used. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `previous_period` | [uint64](#uint64) | | | +| `stake` | [string](#string) | | | +| `height` | [uint64](#uint64) | | | + + + + + + + + +### FeePool +FeePool is the global fee pool for distribution. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `community_pool` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | + + + + + + + + +### Params +Params defines the set of params for the distribution module. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `community_tax` | [string](#string) | | | +| `base_proposer_reward` | [string](#string) | | | +| `bonus_proposer_reward` | [string](#string) | | | +| `withdraw_addr_enabled` | [bool](#bool) | | | + + + + + + + + +### ValidatorAccumulatedCommission +ValidatorAccumulatedCommission represents accumulated commission +for a validator kept as a running counter, can be withdrawn at any time. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `commission` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | + + + + + + + + +### ValidatorCurrentRewards +ValidatorCurrentRewards represents current rewards and current +period for a validator kept as a running counter and incremented +each block as long as the validator's tokens remain constant. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `rewards` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | +| `period` | [uint64](#uint64) | | | + + + + + + + + +### ValidatorHistoricalRewards +ValidatorHistoricalRewards represents historical rewards for a validator. +Height is implicit within the store key. +Cumulative reward ratio is the sum from the zeroeth period +until this period of rewards / tokens, per the spec. +The reference count indicates the number of objects +which might need to reference this historical entry at any point. +ReferenceCount = + number of outstanding delegations which ended the associated period (and + might need to read that record) + + number of slashes which ended the associated period (and might need to + read that record) + + one per validator for the zeroeth period, set on initialization + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `cumulative_reward_ratio` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | +| `reference_count` | [uint32](#uint32) | | | + + + + + + + + +### ValidatorOutstandingRewards +ValidatorOutstandingRewards represents outstanding (un-withdrawn) rewards +for a validator inexpensive to track, allows simple sanity checks. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `rewards` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | + + + + + + + + +### ValidatorSlashEvent +ValidatorSlashEvent represents a validator slash event. +Height is implicit within the store key. +This is needed to calculate appropriate amount of staking tokens +for delegations which are withdrawn after a slash has occurred. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_period` | [uint64](#uint64) | | | +| `fraction` | [string](#string) | | | + + + + + + + + +### ValidatorSlashEvents +ValidatorSlashEvents is a collection of ValidatorSlashEvent messages. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_slash_events` | [ValidatorSlashEvent](#cosmos.distribution.v1beta1.ValidatorSlashEvent) | repeated | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/distribution/v1beta1/genesis.proto + + + + + +### DelegatorStartingInfoRecord +DelegatorStartingInfoRecord used for import / export via genesis json. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_address` | [string](#string) | | delegator_address is the address of the delegator. | +| `validator_address` | [string](#string) | | validator_address is the address of the validator. | +| `starting_info` | [DelegatorStartingInfo](#cosmos.distribution.v1beta1.DelegatorStartingInfo) | | starting_info defines the starting info of a delegator. | + + + + + + + + +### DelegatorWithdrawInfo +DelegatorWithdrawInfo is the address for where distributions rewards are +withdrawn to by default this struct is only used at genesis to feed in +default withdraw addresses. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_address` | [string](#string) | | delegator_address is the address of the delegator. | +| `withdraw_address` | [string](#string) | | withdraw_address is the address to withdraw the delegation rewards to. | + + + + + + + + +### GenesisState +GenesisState defines the distribution module's genesis state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#cosmos.distribution.v1beta1.Params) | | params defines all the paramaters of the module. | +| `fee_pool` | [FeePool](#cosmos.distribution.v1beta1.FeePool) | | fee_pool defines the fee pool at genesis. | +| `delegator_withdraw_infos` | [DelegatorWithdrawInfo](#cosmos.distribution.v1beta1.DelegatorWithdrawInfo) | repeated | fee_pool defines the delegator withdraw infos at genesis. | +| `previous_proposer` | [string](#string) | | fee_pool defines the previous proposer at genesis. | +| `outstanding_rewards` | [ValidatorOutstandingRewardsRecord](#cosmos.distribution.v1beta1.ValidatorOutstandingRewardsRecord) | repeated | fee_pool defines the outstanding rewards of all validators at genesis. | +| `validator_accumulated_commissions` | [ValidatorAccumulatedCommissionRecord](#cosmos.distribution.v1beta1.ValidatorAccumulatedCommissionRecord) | repeated | fee_pool defines the accumulated commisions of all validators at genesis. | +| `validator_historical_rewards` | [ValidatorHistoricalRewardsRecord](#cosmos.distribution.v1beta1.ValidatorHistoricalRewardsRecord) | repeated | fee_pool defines the historical rewards of all validators at genesis. | +| `validator_current_rewards` | [ValidatorCurrentRewardsRecord](#cosmos.distribution.v1beta1.ValidatorCurrentRewardsRecord) | repeated | fee_pool defines the current rewards of all validators at genesis. | +| `delegator_starting_infos` | [DelegatorStartingInfoRecord](#cosmos.distribution.v1beta1.DelegatorStartingInfoRecord) | repeated | fee_pool defines the delegator starting infos at genesis. | +| `validator_slash_events` | [ValidatorSlashEventRecord](#cosmos.distribution.v1beta1.ValidatorSlashEventRecord) | repeated | fee_pool defines the validator slash events at genesis. | + + + + + + + + +### ValidatorAccumulatedCommissionRecord +ValidatorAccumulatedCommissionRecord is used for import / export via genesis +json. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_address` | [string](#string) | | validator_address is the address of the validator. | +| `accumulated` | [ValidatorAccumulatedCommission](#cosmos.distribution.v1beta1.ValidatorAccumulatedCommission) | | accumulated is the accumulated commission of a validator. | + + + + + + + + +### ValidatorCurrentRewardsRecord +ValidatorCurrentRewardsRecord is used for import / export via genesis json. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_address` | [string](#string) | | validator_address is the address of the validator. | +| `rewards` | [ValidatorCurrentRewards](#cosmos.distribution.v1beta1.ValidatorCurrentRewards) | | rewards defines the current rewards of a validator. | - + -### DelegationDelegatorReward -DelegationDelegatorReward represents the properties -of a delegator's delegation reward. +### ValidatorHistoricalRewardsRecord +ValidatorHistoricalRewardsRecord is used for import / export via genesis +json. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_address` | [string](#string) | | | -| `reward` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | +| `validator_address` | [string](#string) | | validator_address is the address of the validator. | +| `period` | [uint64](#uint64) | | period defines the period the historical rewards apply to. | +| `rewards` | [ValidatorHistoricalRewards](#cosmos.distribution.v1beta1.ValidatorHistoricalRewards) | | rewards defines the historical rewards of a validator. | - + -### DelegatorStartingInfo -DelegatorStartingInfo represents the starting info for a delegator reward -period. It tracks the previous validator period, the delegation's amount of -staking token, and the creation height (to check later on if any slashes have -occurred). NOTE: Even though validators are slashed to whole staking tokens, -the delegators within the validator may be left with less than a full token, -thus sdk.Dec is used. +### ValidatorOutstandingRewardsRecord +ValidatorOutstandingRewardsRecord is used for import/export via genesis json. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `previous_period` | [uint64](#uint64) | | | -| `stake` | [string](#string) | | | -| `height` | [uint64](#uint64) | | | +| `validator_address` | [string](#string) | | validator_address is the address of the validator. | +| `outstanding_rewards` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | outstanding_rewards represents the oustanding rewards of a validator. | - + -### FeePool -FeePool is the global fee pool for distribution. +### ValidatorSlashEventRecord +ValidatorSlashEventRecord is used for import / export via genesis json. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `community_pool` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | +| `validator_address` | [string](#string) | | validator_address is the address of the validator. | +| `height` | [uint64](#uint64) | | height defines the block height at which the slash event occured. | +| `period` | [uint64](#uint64) | | period is the period of the slash event. | +| `validator_slash_event` | [ValidatorSlashEvent](#cosmos.distribution.v1beta1.ValidatorSlashEvent) | | validator_slash_event describes the slash event. | + + + + + + + + + + + +

Top

+ +## cosmos/distribution/v1beta1/query.proto + + + + + +### QueryCommunityPoolRequest +QueryCommunityPoolRequest is the request type for the Query/CommunityPool RPC +method. + + - -### Params -Params defines the set of params for the distribution module. + + + + +### QueryCommunityPoolResponse +QueryCommunityPoolResponse is the response type for the Query/CommunityPool +RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `community_tax` | [string](#string) | | | -| `base_proposer_reward` | [string](#string) | | | -| `bonus_proposer_reward` | [string](#string) | | | -| `withdraw_addr_enabled` | [bool](#bool) | | | +| `pool` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | pool defines community pool's coins. | - + -### ValidatorAccumulatedCommission -ValidatorAccumulatedCommission represents accumulated commission -for a validator kept as a running counter, can be withdrawn at any time. +### QueryDelegationRewardsRequest +QueryDelegationRewardsRequest is the request type for the +Query/DelegationRewards RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `commission` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | +| `delegator_address` | [string](#string) | | delegator_address defines the delegator address to query for. | +| `validator_address` | [string](#string) | | validator_address defines the validator address to query for. | - + -### ValidatorCurrentRewards -ValidatorCurrentRewards represents current rewards and current -period for a validator kept as a running counter and incremented -each block as long as the validator's tokens remain constant. +### QueryDelegationRewardsResponse +QueryDelegationRewardsResponse is the response type for the +Query/DelegationRewards RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `rewards` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | -| `period` | [uint64](#uint64) | | | +| `rewards` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | rewards defines the rewards accrued by a delegation. | - + -### ValidatorHistoricalRewards -ValidatorHistoricalRewards represents historical rewards for a validator. -Height is implicit within the store key. -Cumulative reward ratio is the sum from the zeroeth period -until this period of rewards / tokens, per the spec. -The reference count indicates the number of objects -which might need to reference this historical entry at any point. -ReferenceCount = - number of outstanding delegations which ended the associated period (and - might need to read that record) - + number of slashes which ended the associated period (and might need to - read that record) - + one per validator for the zeroeth period, set on initialization +### QueryDelegationTotalRewardsRequest +QueryDelegationTotalRewardsRequest is the request type for the +Query/DelegationTotalRewards RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `cumulative_reward_ratio` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | -| `reference_count` | [uint32](#uint32) | | | +| `delegator_address` | [string](#string) | | delegator_address defines the delegator address to query for. | - + -### ValidatorOutstandingRewards -ValidatorOutstandingRewards represents outstanding (un-withdrawn) rewards -for a validator inexpensive to track, allows simple sanity checks. +### QueryDelegationTotalRewardsResponse +QueryDelegationTotalRewardsResponse is the response type for the +Query/DelegationTotalRewards RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `rewards` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | | +| `rewards` | [DelegationDelegatorReward](#cosmos.distribution.v1beta1.DelegationDelegatorReward) | repeated | rewards defines all the rewards accrued by a delegator. | +| `total` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | total defines the sum of all the rewards. | - + -### ValidatorSlashEvent -ValidatorSlashEvent represents a validator slash event. -Height is implicit within the store key. -This is needed to calculate appropriate amount of staking tokens -for delegations which are withdrawn after a slash has occurred. +### QueryDelegatorValidatorsRequest +QueryDelegatorValidatorsRequest is the request type for the +Query/DelegatorValidators RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_period` | [uint64](#uint64) | | | -| `fraction` | [string](#string) | | | +| `delegator_address` | [string](#string) | | delegator_address defines the delegator address to query for. | - + -### ValidatorSlashEvents -ValidatorSlashEvents is a collection of ValidatorSlashEvent messages. +### QueryDelegatorValidatorsResponse +QueryDelegatorValidatorsResponse is the response type for the +Query/DelegatorValidators RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_slash_events` | [ValidatorSlashEvent](#cosmos.distribution.v1beta1.ValidatorSlashEvent) | repeated | | +| `validators` | [string](#string) | repeated | validators defines the validators a delegator is delegating for. | - - + - +### QueryDelegatorWithdrawAddressRequest +QueryDelegatorWithdrawAddressRequest is the request type for the +Query/DelegatorWithdrawAddress RPC method. - +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_address` | [string](#string) | | delegator_address defines the delegator address to query for. | - -

Top

-## cosmos/distribution/v1beta1/genesis.proto - + -### DelegatorStartingInfoRecord -DelegatorStartingInfoRecord used for import / export via genesis json. +### QueryDelegatorWithdrawAddressResponse +QueryDelegatorWithdrawAddressResponse is the response type for the +Query/DelegatorWithdrawAddress RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `delegator_address` | [string](#string) | | delegator_address is the address of the delegator. | -| `validator_address` | [string](#string) | | validator_address is the address of the validator. | -| `starting_info` | [DelegatorStartingInfo](#cosmos.distribution.v1beta1.DelegatorStartingInfo) | | starting_info defines the starting info of a delegator. | +| `withdraw_address` | [string](#string) | | withdraw_address defines the delegator address to query for. | - + -### DelegatorWithdrawInfo -DelegatorWithdrawInfo is the address for where distributions rewards are -withdrawn to by default this struct is only used at genesis to feed in -default withdraw addresses. +### QueryParamsRequest +QueryParamsRequest is the request type for the Query/Params RPC method. + + + + + + + + +### QueryParamsResponse +QueryParamsResponse is the response type for the Query/Params RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `delegator_address` | [string](#string) | | delegator_address is the address of the delegator. | -| `withdraw_address` | [string](#string) | | withdraw_address is the address to withdraw the delegation rewards to. | +| `params` | [Params](#cosmos.distribution.v1beta1.Params) | | params defines the parameters of the module. | - + -### GenesisState -GenesisState defines the distribution module's genesis state. +### QueryValidatorCommissionRequest +QueryValidatorCommissionRequest is the request type for the +Query/ValidatorCommission RPC method | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `params` | [Params](#cosmos.distribution.v1beta1.Params) | | params defines all the paramaters of the module. | -| `fee_pool` | [FeePool](#cosmos.distribution.v1beta1.FeePool) | | fee_pool defines the fee pool at genesis. | -| `delegator_withdraw_infos` | [DelegatorWithdrawInfo](#cosmos.distribution.v1beta1.DelegatorWithdrawInfo) | repeated | fee_pool defines the delegator withdraw infos at genesis. | -| `previous_proposer` | [string](#string) | | fee_pool defines the previous proposer at genesis. | -| `outstanding_rewards` | [ValidatorOutstandingRewardsRecord](#cosmos.distribution.v1beta1.ValidatorOutstandingRewardsRecord) | repeated | fee_pool defines the outstanding rewards of all validators at genesis. | -| `validator_accumulated_commissions` | [ValidatorAccumulatedCommissionRecord](#cosmos.distribution.v1beta1.ValidatorAccumulatedCommissionRecord) | repeated | fee_pool defines the accumulated commisions of all validators at genesis. | -| `validator_historical_rewards` | [ValidatorHistoricalRewardsRecord](#cosmos.distribution.v1beta1.ValidatorHistoricalRewardsRecord) | repeated | fee_pool defines the historical rewards of all validators at genesis. | -| `validator_current_rewards` | [ValidatorCurrentRewardsRecord](#cosmos.distribution.v1beta1.ValidatorCurrentRewardsRecord) | repeated | fee_pool defines the current rewards of all validators at genesis. | -| `delegator_starting_infos` | [DelegatorStartingInfoRecord](#cosmos.distribution.v1beta1.DelegatorStartingInfoRecord) | repeated | fee_pool defines the delegator starting infos at genesis. | -| `validator_slash_events` | [ValidatorSlashEventRecord](#cosmos.distribution.v1beta1.ValidatorSlashEventRecord) | repeated | fee_pool defines the validator slash events at genesis. | +| `validator_address` | [string](#string) | | validator_address defines the validator address to query for. | - + -### ValidatorAccumulatedCommissionRecord -ValidatorAccumulatedCommissionRecord is used for import / export via genesis -json. +### QueryValidatorCommissionResponse +QueryValidatorCommissionResponse is the response type for the +Query/ValidatorCommission RPC method | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_address` | [string](#string) | | validator_address is the address of the validator. | -| `accumulated` | [ValidatorAccumulatedCommission](#cosmos.distribution.v1beta1.ValidatorAccumulatedCommission) | | accumulated is the accumulated commission of a validator. | +| `commission` | [ValidatorAccumulatedCommission](#cosmos.distribution.v1beta1.ValidatorAccumulatedCommission) | | commission defines the commision the validator received. | - + -### ValidatorCurrentRewardsRecord -ValidatorCurrentRewardsRecord is used for import / export via genesis json. +### QueryValidatorOutstandingRewardsRequest +QueryValidatorOutstandingRewardsRequest is the request type for the +Query/ValidatorOutstandingRewards RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_address` | [string](#string) | | validator_address is the address of the validator. | -| `rewards` | [ValidatorCurrentRewards](#cosmos.distribution.v1beta1.ValidatorCurrentRewards) | | rewards defines the current rewards of a validator. | +| `validator_address` | [string](#string) | | validator_address defines the validator address to query for. | - + -### ValidatorHistoricalRewardsRecord -ValidatorHistoricalRewardsRecord is used for import / export via genesis -json. +### QueryValidatorOutstandingRewardsResponse +QueryValidatorOutstandingRewardsResponse is the response type for the +Query/ValidatorOutstandingRewards RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_address` | [string](#string) | | validator_address is the address of the validator. | -| `period` | [uint64](#uint64) | | period defines the period the historical rewards apply to. | -| `rewards` | [ValidatorHistoricalRewards](#cosmos.distribution.v1beta1.ValidatorHistoricalRewards) | | rewards defines the historical rewards of a validator. | +| `rewards` | [ValidatorOutstandingRewards](#cosmos.distribution.v1beta1.ValidatorOutstandingRewards) | | | - + -### ValidatorOutstandingRewardsRecord -ValidatorOutstandingRewardsRecord is used for import/export via genesis json. +### QueryValidatorSlashesRequest +QueryValidatorSlashesRequest is the request type for the +Query/ValidatorSlashes RPC method | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_address` | [string](#string) | | validator_address is the address of the validator. | -| `outstanding_rewards` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | outstanding_rewards represents the oustanding rewards of a validator. | +| `validator_address` | [string](#string) | | validator_address defines the validator address to query for. | +| `starting_height` | [uint64](#uint64) | | starting_height defines the optional starting height to query the slashes. | +| `ending_height` | [uint64](#uint64) | | starting_height defines the optional ending height to query the slashes. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - + -### ValidatorSlashEventRecord -ValidatorSlashEventRecord is used for import / export via genesis json. +### QueryValidatorSlashesResponse +QueryValidatorSlashesResponse is the response type for the +Query/ValidatorSlashes RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_address` | [string](#string) | | validator_address is the address of the validator. | -| `height` | [uint64](#uint64) | | height defines the block height at which the slash event occured. | -| `period` | [uint64](#uint64) | | period is the period of the slash event. | -| `validator_slash_event` | [ValidatorSlashEvent](#cosmos.distribution.v1beta1.ValidatorSlashEvent) | | validator_slash_event describes the slash event. | +| `slashes` | [ValidatorSlashEvent](#cosmos.distribution.v1beta1.ValidatorSlashEvent) | repeated | slashes defines the slashes the validator received. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | @@ -3007,293 +3755,294 @@ ValidatorSlashEventRecord is used for import / export via genesis json. - - - - - -

Top

-## cosmos/distribution/v1beta1/query.proto + +### Query +Query defines the gRPC querier service for distribution module. +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Params` | [QueryParamsRequest](#cosmos.distribution.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.distribution.v1beta1.QueryParamsResponse) | Params queries params of the distribution module. | GET|/cosmos/distribution/v1beta1/params| +| `ValidatorOutstandingRewards` | [QueryValidatorOutstandingRewardsRequest](#cosmos.distribution.v1beta1.QueryValidatorOutstandingRewardsRequest) | [QueryValidatorOutstandingRewardsResponse](#cosmos.distribution.v1beta1.QueryValidatorOutstandingRewardsResponse) | ValidatorOutstandingRewards queries rewards of a validator address. | GET|/cosmos/distribution/v1beta1/validators/{validator_address}/outstanding_rewards| +| `ValidatorCommission` | [QueryValidatorCommissionRequest](#cosmos.distribution.v1beta1.QueryValidatorCommissionRequest) | [QueryValidatorCommissionResponse](#cosmos.distribution.v1beta1.QueryValidatorCommissionResponse) | ValidatorCommission queries accumulated commission for a validator. | GET|/cosmos/distribution/v1beta1/validators/{validator_address}/commission| +| `ValidatorSlashes` | [QueryValidatorSlashesRequest](#cosmos.distribution.v1beta1.QueryValidatorSlashesRequest) | [QueryValidatorSlashesResponse](#cosmos.distribution.v1beta1.QueryValidatorSlashesResponse) | ValidatorSlashes queries slash events of a validator. | GET|/cosmos/distribution/v1beta1/validators/{validator_address}/slashes| +| `DelegationRewards` | [QueryDelegationRewardsRequest](#cosmos.distribution.v1beta1.QueryDelegationRewardsRequest) | [QueryDelegationRewardsResponse](#cosmos.distribution.v1beta1.QueryDelegationRewardsResponse) | DelegationRewards queries the total rewards accrued by a delegation. | GET|/cosmos/distribution/v1beta1/delegators/{delegator_address}/rewards/{validator_address}| +| `DelegationTotalRewards` | [QueryDelegationTotalRewardsRequest](#cosmos.distribution.v1beta1.QueryDelegationTotalRewardsRequest) | [QueryDelegationTotalRewardsResponse](#cosmos.distribution.v1beta1.QueryDelegationTotalRewardsResponse) | DelegationTotalRewards queries the total rewards accrued by a each validator. | GET|/cosmos/distribution/v1beta1/delegators/{delegator_address}/rewards| +| `DelegatorValidators` | [QueryDelegatorValidatorsRequest](#cosmos.distribution.v1beta1.QueryDelegatorValidatorsRequest) | [QueryDelegatorValidatorsResponse](#cosmos.distribution.v1beta1.QueryDelegatorValidatorsResponse) | DelegatorValidators queries the validators of a delegator. | GET|/cosmos/distribution/v1beta1/delegators/{delegator_address}/validators| +| `DelegatorWithdrawAddress` | [QueryDelegatorWithdrawAddressRequest](#cosmos.distribution.v1beta1.QueryDelegatorWithdrawAddressRequest) | [QueryDelegatorWithdrawAddressResponse](#cosmos.distribution.v1beta1.QueryDelegatorWithdrawAddressResponse) | DelegatorWithdrawAddress queries withdraw address of a delegator. | GET|/cosmos/distribution/v1beta1/delegators/{delegator_address}/withdraw_address| +| `CommunityPool` | [QueryCommunityPoolRequest](#cosmos.distribution.v1beta1.QueryCommunityPoolRequest) | [QueryCommunityPoolResponse](#cosmos.distribution.v1beta1.QueryCommunityPoolResponse) | CommunityPool queries the community pool coins. | GET|/cosmos/distribution/v1beta1/community_pool| - + -### QueryCommunityPoolRequest -QueryCommunityPoolRequest is the request type for the Query/CommunityPool RPC -method. + +

Top

+## cosmos/distribution/v1beta1/tx.proto - + -### QueryCommunityPoolResponse -QueryCommunityPoolResponse is the response type for the Query/CommunityPool -RPC method. +### MsgFundCommunityPool +MsgFundCommunityPool allows an account to directly +fund the community pool. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `pool` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | pool defines community pool's coins. | - - +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `depositor` | [string](#string) | | | - -### QueryDelegationRewardsRequest -QueryDelegationRewardsRequest is the request type for the -Query/DelegationRewards RPC method. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `delegator_address` | [string](#string) | | delegator_address defines the delegator address to query for. | -| `validator_address` | [string](#string) | | validator_address defines the validator address to query for. | +### MsgFundCommunityPoolResponse +MsgFundCommunityPoolResponse defines the Msg/FundCommunityPool response type. - + -### QueryDelegationRewardsResponse -QueryDelegationRewardsResponse is the response type for the -Query/DelegationRewards RPC method. +### MsgSetWithdrawAddress +MsgSetWithdrawAddress sets the withdraw address for +a delegator (or validator self-delegation). | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `rewards` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | rewards defines the rewards accrued by a delegation. | - - +| `delegator_address` | [string](#string) | | | +| `withdraw_address` | [string](#string) | | | - -### QueryDelegationTotalRewardsRequest -QueryDelegationTotalRewardsRequest is the request type for the -Query/DelegationTotalRewards RPC method. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `delegator_address` | [string](#string) | | delegator_address defines the delegator address to query for. | +### MsgSetWithdrawAddressResponse +MsgSetWithdrawAddressResponse defines the Msg/SetWithdrawAddress response type. - + -### QueryDelegationTotalRewardsResponse -QueryDelegationTotalRewardsResponse is the response type for the -Query/DelegationTotalRewards RPC method. +### MsgWithdrawDelegatorReward +MsgWithdrawDelegatorReward represents delegation withdrawal to a delegator +from a single validator. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `rewards` | [DelegationDelegatorReward](#cosmos.distribution.v1beta1.DelegationDelegatorReward) | repeated | rewards defines all the rewards accrued by a delegator. | -| `total` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | total defines the sum of all the rewards. | - - +| `delegator_address` | [string](#string) | | | +| `validator_address` | [string](#string) | | | - -### QueryDelegatorValidatorsRequest -QueryDelegatorValidatorsRequest is the request type for the -Query/DelegatorValidators RPC method. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `delegator_address` | [string](#string) | | delegator_address defines the delegator address to query for. | +### MsgWithdrawDelegatorRewardResponse +MsgWithdrawDelegatorRewardResponse defines the Msg/WithdrawDelegatorReward response type. - + -### QueryDelegatorValidatorsResponse -QueryDelegatorValidatorsResponse is the response type for the -Query/DelegatorValidators RPC method. +### MsgWithdrawValidatorCommission +MsgWithdrawValidatorCommission withdraws the full commission to the validator +address. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validators` | [string](#string) | repeated | validators defines the validators a delegator is delegating for. | +| `validator_address` | [string](#string) | | | - + -### QueryDelegatorWithdrawAddressRequest -QueryDelegatorWithdrawAddressRequest is the request type for the -Query/DelegatorWithdrawAddress RPC method. +### MsgWithdrawValidatorCommissionResponse +MsgWithdrawValidatorCommissionResponse defines the Msg/WithdrawValidatorCommission response type. -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `delegator_address` | [string](#string) | | delegator_address defines the delegator address to query for. | + + + - -### QueryDelegatorWithdrawAddressResponse -QueryDelegatorWithdrawAddressResponse is the response type for the -Query/DelegatorWithdrawAddress RPC method. + +### Msg +Msg defines the distribution Msg service. -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `withdraw_address` | [string](#string) | | withdraw_address defines the delegator address to query for. | +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `SetWithdrawAddress` | [MsgSetWithdrawAddress](#cosmos.distribution.v1beta1.MsgSetWithdrawAddress) | [MsgSetWithdrawAddressResponse](#cosmos.distribution.v1beta1.MsgSetWithdrawAddressResponse) | SetWithdrawAddress defines a method to change the withdraw address for a delegator (or validator self-delegation). | | +| `WithdrawDelegatorReward` | [MsgWithdrawDelegatorReward](#cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward) | [MsgWithdrawDelegatorRewardResponse](#cosmos.distribution.v1beta1.MsgWithdrawDelegatorRewardResponse) | WithdrawDelegatorReward defines a method to withdraw rewards of delegator from a single validator. | | +| `WithdrawValidatorCommission` | [MsgWithdrawValidatorCommission](#cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission) | [MsgWithdrawValidatorCommissionResponse](#cosmos.distribution.v1beta1.MsgWithdrawValidatorCommissionResponse) | WithdrawValidatorCommission defines a method to withdraw the full commission to the validator address. | | +| `FundCommunityPool` | [MsgFundCommunityPool](#cosmos.distribution.v1beta1.MsgFundCommunityPool) | [MsgFundCommunityPoolResponse](#cosmos.distribution.v1beta1.MsgFundCommunityPoolResponse) | FundCommunityPool defines a method to allow an account to directly fund the community pool. | | + + + +

Top

+## cosmos/evidence/v1beta1/evidence.proto - -### QueryParamsRequest -QueryParamsRequest is the request type for the Query/Params RPC method. + +### Equivocation +Equivocation implements the Evidence interface and defines evidence of double +signing misbehavior. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `height` | [int64](#int64) | | | +| `time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | +| `power` | [int64](#int64) | | | +| `consensus_address` | [string](#string) | | | - -### QueryParamsResponse -QueryParamsResponse is the response type for the Query/Params RPC method. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `params` | [Params](#cosmos.distribution.v1beta1.Params) | | params defines the parameters of the module. | + + + + +

Top

- +## cosmos/evidence/v1beta1/genesis.proto -### QueryValidatorCommissionRequest -QueryValidatorCommissionRequest is the request type for the -Query/ValidatorCommission RPC method + + + + +### GenesisState +GenesisState defines the evidence module's genesis state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_address` | [string](#string) | | validator_address defines the validator address to query for. | +| `evidence` | [google.protobuf.Any](#google.protobuf.Any) | repeated | evidence defines all the evidence at genesis. | + - + -### QueryValidatorCommissionResponse -QueryValidatorCommissionResponse is the response type for the -Query/ValidatorCommission RPC method + + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `commission` | [ValidatorAccumulatedCommission](#cosmos.distribution.v1beta1.ValidatorAccumulatedCommission) | | commission defines the commision the validator received. | + +

Top

+## cosmos/evidence/v1beta1/query.proto - + -### QueryValidatorOutstandingRewardsRequest -QueryValidatorOutstandingRewardsRequest is the request type for the -Query/ValidatorOutstandingRewards RPC method. +### QueryAllEvidenceRequest +QueryEvidenceRequest is the request type for the Query/AllEvidence RPC +method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_address` | [string](#string) | | validator_address defines the validator address to query for. | - +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - -### QueryValidatorOutstandingRewardsResponse -QueryValidatorOutstandingRewardsResponse is the response type for the -Query/ValidatorOutstandingRewards RPC method. + + +### QueryAllEvidenceResponse +QueryAllEvidenceResponse is the response type for the Query/AllEvidence RPC +method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `rewards` | [ValidatorOutstandingRewards](#cosmos.distribution.v1beta1.ValidatorOutstandingRewards) | | | +| `evidence` | [google.protobuf.Any](#google.protobuf.Any) | repeated | evidence returns all evidences. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | - + -### QueryValidatorSlashesRequest -QueryValidatorSlashesRequest is the request type for the -Query/ValidatorSlashes RPC method +### QueryEvidenceRequest +QueryEvidenceRequest is the request type for the Query/Evidence RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_address` | [string](#string) | | validator_address defines the validator address to query for. | -| `starting_height` | [uint64](#uint64) | | starting_height defines the optional starting height to query the slashes. | -| `ending_height` | [uint64](#uint64) | | starting_height defines the optional ending height to query the slashes. | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | +| `evidence_hash` | [bytes](#bytes) | | evidence_hash defines the hash of the requested evidence. | - + -### QueryValidatorSlashesResponse -QueryValidatorSlashesResponse is the response type for the -Query/ValidatorSlashes RPC method. +### QueryEvidenceResponse +QueryEvidenceResponse is the response type for the Query/Evidence RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `slashes` | [ValidatorSlashEvent](#cosmos.distribution.v1beta1.ValidatorSlashEvent) | repeated | slashes defines the slashes the validator received. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | +| `evidence` | [google.protobuf.Any](#google.protobuf.Any) | | evidence returns the requested evidence. | @@ -3306,183 +4055,167 @@ Query/ValidatorSlashes RPC method. - + ### Query -Query defines the gRPC querier service for distribution module. +Query defines the gRPC querier service. | Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | | ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `Params` | [QueryParamsRequest](#cosmos.distribution.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.distribution.v1beta1.QueryParamsResponse) | Params queries params of the distribution module. | GET|/cosmos/distribution/v1beta1/params| -| `ValidatorOutstandingRewards` | [QueryValidatorOutstandingRewardsRequest](#cosmos.distribution.v1beta1.QueryValidatorOutstandingRewardsRequest) | [QueryValidatorOutstandingRewardsResponse](#cosmos.distribution.v1beta1.QueryValidatorOutstandingRewardsResponse) | ValidatorOutstandingRewards queries rewards of a validator address. | GET|/cosmos/distribution/v1beta1/validators/{validator_address}/outstanding_rewards| -| `ValidatorCommission` | [QueryValidatorCommissionRequest](#cosmos.distribution.v1beta1.QueryValidatorCommissionRequest) | [QueryValidatorCommissionResponse](#cosmos.distribution.v1beta1.QueryValidatorCommissionResponse) | ValidatorCommission queries accumulated commission for a validator. | GET|/cosmos/distribution/v1beta1/validators/{validator_address}/commission| -| `ValidatorSlashes` | [QueryValidatorSlashesRequest](#cosmos.distribution.v1beta1.QueryValidatorSlashesRequest) | [QueryValidatorSlashesResponse](#cosmos.distribution.v1beta1.QueryValidatorSlashesResponse) | ValidatorSlashes queries slash events of a validator. | GET|/cosmos/distribution/v1beta1/validators/{validator_address}/slashes| -| `DelegationRewards` | [QueryDelegationRewardsRequest](#cosmos.distribution.v1beta1.QueryDelegationRewardsRequest) | [QueryDelegationRewardsResponse](#cosmos.distribution.v1beta1.QueryDelegationRewardsResponse) | DelegationRewards queries the total rewards accrued by a delegation. | GET|/cosmos/distribution/v1beta1/delegators/{delegator_address}/rewards/{validator_address}| -| `DelegationTotalRewards` | [QueryDelegationTotalRewardsRequest](#cosmos.distribution.v1beta1.QueryDelegationTotalRewardsRequest) | [QueryDelegationTotalRewardsResponse](#cosmos.distribution.v1beta1.QueryDelegationTotalRewardsResponse) | DelegationTotalRewards queries the total rewards accrued by a each validator. | GET|/cosmos/distribution/v1beta1/delegators/{delegator_address}/rewards| -| `DelegatorValidators` | [QueryDelegatorValidatorsRequest](#cosmos.distribution.v1beta1.QueryDelegatorValidatorsRequest) | [QueryDelegatorValidatorsResponse](#cosmos.distribution.v1beta1.QueryDelegatorValidatorsResponse) | DelegatorValidators queries the validators of a delegator. | GET|/cosmos/distribution/v1beta1/delegators/{delegator_address}/validators| -| `DelegatorWithdrawAddress` | [QueryDelegatorWithdrawAddressRequest](#cosmos.distribution.v1beta1.QueryDelegatorWithdrawAddressRequest) | [QueryDelegatorWithdrawAddressResponse](#cosmos.distribution.v1beta1.QueryDelegatorWithdrawAddressResponse) | DelegatorWithdrawAddress queries withdraw address of a delegator. | GET|/cosmos/distribution/v1beta1/delegators/{delegator_address}/withdraw_address| -| `CommunityPool` | [QueryCommunityPoolRequest](#cosmos.distribution.v1beta1.QueryCommunityPoolRequest) | [QueryCommunityPoolResponse](#cosmos.distribution.v1beta1.QueryCommunityPoolResponse) | CommunityPool queries the community pool coins. | GET|/cosmos/distribution/v1beta1/community_pool| +| `Evidence` | [QueryEvidenceRequest](#cosmos.evidence.v1beta1.QueryEvidenceRequest) | [QueryEvidenceResponse](#cosmos.evidence.v1beta1.QueryEvidenceResponse) | Evidence queries evidence based on evidence hash. | GET|/cosmos/evidence/v1beta1/evidence/{evidence_hash}| +| `AllEvidence` | [QueryAllEvidenceRequest](#cosmos.evidence.v1beta1.QueryAllEvidenceRequest) | [QueryAllEvidenceResponse](#cosmos.evidence.v1beta1.QueryAllEvidenceResponse) | AllEvidence queries all evidence. | GET|/cosmos/evidence/v1beta1/evidence| - +

Top

-## cosmos/distribution/v1beta1/tx.proto +## cosmos/evidence/v1beta1/tx.proto - + -### MsgFundCommunityPool -MsgFundCommunityPool allows an account to directly -fund the community pool. +### MsgSubmitEvidence +MsgSubmitEvidence represents a message that supports submitting arbitrary +Evidence of misbehavior such as equivocation or counterfactual signing. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | -| `depositor` | [string](#string) | | | - - - - - - - - -### MsgFundCommunityPoolResponse -MsgFundCommunityPoolResponse defines the Msg/FundCommunityPool response type. +| `submitter` | [string](#string) | | | +| `evidence` | [google.protobuf.Any](#google.protobuf.Any) | | | - + -### MsgSetWithdrawAddress -MsgSetWithdrawAddress sets the withdraw address for -a delegator (or validator self-delegation). +### MsgSubmitEvidenceResponse +MsgSubmitEvidenceResponse defines the Msg/SubmitEvidence response type. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `delegator_address` | [string](#string) | | | -| `withdraw_address` | [string](#string) | | | - +| `hash` | [bytes](#bytes) | | hash defines the hash of the evidence. | - + -### MsgSetWithdrawAddressResponse -MsgSetWithdrawAddressResponse defines the Msg/SetWithdrawAddress response type. + + + +### Msg +Msg defines the evidence Msg service. +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `SubmitEvidence` | [MsgSubmitEvidence](#cosmos.evidence.v1beta1.MsgSubmitEvidence) | [MsgSubmitEvidenceResponse](#cosmos.evidence.v1beta1.MsgSubmitEvidenceResponse) | SubmitEvidence submits an arbitrary Evidence of misbehavior such as equivocation or counterfactual signing. | | - + -### MsgWithdrawDelegatorReward -MsgWithdrawDelegatorReward represents delegation withdrawal to a delegator -from a single validator. -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `delegator_address` | [string](#string) | | | -| `validator_address` | [string](#string) | | | + +

Top

+## cosmos/feegrant/v1beta1/feegrant.proto + +### BasicFeeAllowance +BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens +that optionally expires. The delegatee can use up to SpendLimit to cover fees. - -### MsgWithdrawDelegatorRewardResponse -MsgWithdrawDelegatorRewardResponse defines the Msg/WithdrawDelegatorReward response type. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `spend_limit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | spend_limit specifies the maximum amount of tokens that can be spent by this allowance and will be updated as tokens are spent. If it is empty, there is no spend limit and any amount of coins can be spent. | +| `expiration` | [ExpiresAt](#cosmos.feegrant.v1beta1.ExpiresAt) | | expiration specifies an optional time when this allowance expires | - + -### MsgWithdrawValidatorCommission -MsgWithdrawValidatorCommission withdraws the full commission to the validator -address. +### Duration +Duration is a span of a clock time or number of blocks. +This is designed to be added to an ExpiresAt struct. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `validator_address` | [string](#string) | | | - +| `duration` | [google.protobuf.Duration](#google.protobuf.Duration) | | | +| `blocks` | [uint64](#uint64) | | | - - -### MsgWithdrawValidatorCommissionResponse -MsgWithdrawValidatorCommissionResponse defines the Msg/WithdrawValidatorCommission response type. + +### ExpiresAt +ExpiresAt is a point in time where something expires. +It may be *either* block time or block height +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | +| `height` | [int64](#int64) | | | - - - - -### Msg -Msg defines the distribution Msg service. + -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `SetWithdrawAddress` | [MsgSetWithdrawAddress](#cosmos.distribution.v1beta1.MsgSetWithdrawAddress) | [MsgSetWithdrawAddressResponse](#cosmos.distribution.v1beta1.MsgSetWithdrawAddressResponse) | SetWithdrawAddress defines a method to change the withdraw address for a delegator (or validator self-delegation). | | -| `WithdrawDelegatorReward` | [MsgWithdrawDelegatorReward](#cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward) | [MsgWithdrawDelegatorRewardResponse](#cosmos.distribution.v1beta1.MsgWithdrawDelegatorRewardResponse) | WithdrawDelegatorReward defines a method to withdraw rewards of delegator from a single validator. | | -| `WithdrawValidatorCommission` | [MsgWithdrawValidatorCommission](#cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission) | [MsgWithdrawValidatorCommissionResponse](#cosmos.distribution.v1beta1.MsgWithdrawValidatorCommissionResponse) | WithdrawValidatorCommission defines a method to withdraw the full commission to the validator address. | | -| `FundCommunityPool` | [MsgFundCommunityPool](#cosmos.distribution.v1beta1.MsgFundCommunityPool) | [MsgFundCommunityPoolResponse](#cosmos.distribution.v1beta1.MsgFundCommunityPoolResponse) | FundCommunityPool defines a method to allow an account to directly fund the community pool. | | +### FeeAllowanceGrant +FeeAllowanceGrant is stored in the KVStore to record a grant with full context - +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | +| `allowance` | [google.protobuf.Any](#google.protobuf.Any) | | | - -

Top

-## cosmos/evidence/v1beta1/evidence.proto - + -### Equivocation -Equivocation implements the Evidence interface and defines evidence of double -signing misbehavior. +### PeriodicFeeAllowance +PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap, +as well as a limit per time period. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `height` | [int64](#int64) | | | -| `time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | -| `power` | [int64](#int64) | | | -| `consensus_address` | [string](#string) | | | +| `basic` | [BasicFeeAllowance](#cosmos.feegrant.v1beta1.BasicFeeAllowance) | | basic specifies a struct of `BasicFeeAllowance` | +| `period` | [Duration](#cosmos.feegrant.v1beta1.Duration) | | period specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset | +| `period_spend_limit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | period_spend_limit specifies the maximum number of coins that can be spent in the period | +| `period_can_spend` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | period_can_spend is the number of coins left to be spent before the period_reset time | +| `period_reset` | [ExpiresAt](#cosmos.feegrant.v1beta1.ExpiresAt) | | period_reset is the time at which this period resets and a new one begins, it is calculated from the start time of the first transaction after the last period ended | @@ -3498,22 +4231,22 @@ signing misbehavior. - +

Top

-## cosmos/evidence/v1beta1/genesis.proto +## cosmos/feegrant/v1beta1/genesis.proto - + ### GenesisState -GenesisState defines the evidence module's genesis state. +GenesisState contains a set of fee allowances, persisted from the store | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `evidence` | [google.protobuf.Any](#google.protobuf.Any) | repeated | evidence defines all the evidence at genesis. | +| `fee_allowances` | [FeeAllowanceGrant](#cosmos.feegrant.v1beta1.FeeAllowanceGrant) | repeated | | @@ -3529,70 +4262,70 @@ GenesisState defines the evidence module's genesis state. - +

Top

-## cosmos/evidence/v1beta1/query.proto +## cosmos/feegrant/v1beta1/query.proto - + -### QueryAllEvidenceRequest -QueryEvidenceRequest is the request type for the Query/AllEvidence RPC -method. +### QueryFeeAllowanceRequest +QueryFeeAllowanceRequest is the request type for the Query/FeeAllowance 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. | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | - + -### QueryAllEvidenceResponse -QueryAllEvidenceResponse is the response type for the Query/AllEvidence RPC -method. +### QueryFeeAllowanceResponse +QueryFeeAllowanceResponse is the response type for the Query/FeeAllowance RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `evidence` | [google.protobuf.Any](#google.protobuf.Any) | repeated | evidence returns all evidences. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | +| `fee_allowance` | [FeeAllowanceGrant](#cosmos.feegrant.v1beta1.FeeAllowanceGrant) | | fee_allowance is a fee_allowance granted for grantee by granter. | - + -### QueryEvidenceRequest -QueryEvidenceRequest is the request type for the Query/Evidence RPC method. +### QueryFeeAllowancesRequest +QueryFeeAllowancesRequest is the request type for the Query/FeeAllowances RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `evidence_hash` | [bytes](#bytes) | | evidence_hash defines the hash of the requested evidence. | +| `grantee` | [string](#string) | | | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an pagination for the request. | - + -### QueryEvidenceResponse -QueryEvidenceResponse is the response type for the Query/Evidence RPC method. +### QueryFeeAllowancesResponse +QueryFeeAllowancesResponse is the response type for the Query/FeeAllowances RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `evidence` | [google.protobuf.Any](#google.protobuf.Any) | | evidence returns the requested evidence. | +| `fee_allowances` | [FeeAllowanceGrant](#cosmos.feegrant.v1beta1.FeeAllowanceGrant) | repeated | fee_allowances are fee_allowance's granted for grantee by granter. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines an pagination for the response. | @@ -3605,53 +4338,75 @@ QueryEvidenceResponse is the response type for the Query/Evidence RPC method. - + ### Query Query defines the gRPC querier service. | Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | | ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `Evidence` | [QueryEvidenceRequest](#cosmos.evidence.v1beta1.QueryEvidenceRequest) | [QueryEvidenceResponse](#cosmos.evidence.v1beta1.QueryEvidenceResponse) | Evidence queries evidence based on evidence hash. | GET|/cosmos/evidence/v1beta1/evidence/{evidence_hash}| -| `AllEvidence` | [QueryAllEvidenceRequest](#cosmos.evidence.v1beta1.QueryAllEvidenceRequest) | [QueryAllEvidenceResponse](#cosmos.evidence.v1beta1.QueryAllEvidenceResponse) | AllEvidence queries all evidence. | GET|/cosmos/evidence/v1beta1/evidence| +| `FeeAllowance` | [QueryFeeAllowanceRequest](#cosmos.feegrant.v1beta1.QueryFeeAllowanceRequest) | [QueryFeeAllowanceResponse](#cosmos.feegrant.v1beta1.QueryFeeAllowanceResponse) | FeeAllowance returns fee granted to the grantee by the granter. | GET|/cosmos/feegrant/v1beta1/fee_allowance/{granter}/{grantee}| +| `FeeAllowances` | [QueryFeeAllowancesRequest](#cosmos.feegrant.v1beta1.QueryFeeAllowancesRequest) | [QueryFeeAllowancesResponse](#cosmos.feegrant.v1beta1.QueryFeeAllowancesResponse) | FeeAllowances returns all the grants for address. | GET|/cosmos/feegrant/v1beta1/fee_allowances/{grantee}| - +

Top

-## cosmos/evidence/v1beta1/tx.proto +## cosmos/feegrant/v1beta1/tx.proto - + -### MsgSubmitEvidence -MsgSubmitEvidence represents a message that supports submitting arbitrary -Evidence of misbehavior such as equivocation or counterfactual signing. +### MsgGrantFeeAllowance +MsgGrantFeeAllowance adds permission for Grantee to spend up to Allowance +of fees from the account of Granter. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `submitter` | [string](#string) | | | -| `evidence` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | +| `allowance` | [google.protobuf.Any](#google.protobuf.Any) | | | - + + +### MsgGrantFeeAllowanceResponse +MsgGrantFeeAllowanceResponse defines the Msg/GrantFeeAllowanceResponse response type. + -### MsgSubmitEvidenceResponse -MsgSubmitEvidenceResponse defines the Msg/SubmitEvidence response type. + + + + + + +### MsgRevokeFeeAllowance +MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `hash` | [bytes](#bytes) | | hash defines the hash of the evidence. | +| `granter` | [string](#string) | | | +| `grantee` | [string](#string) | | | + + + + + + + + +### MsgRevokeFeeAllowanceResponse +MsgRevokeFeeAllowanceResponse defines the Msg/RevokeFeeAllowanceResponse response type. @@ -3664,14 +4419,15 @@ MsgSubmitEvidenceResponse defines the Msg/SubmitEvidence response type. - + ### Msg -Msg defines the evidence Msg service. +Msg defines the feegrant msg service. | Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | | ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `SubmitEvidence` | [MsgSubmitEvidence](#cosmos.evidence.v1beta1.MsgSubmitEvidence) | [MsgSubmitEvidenceResponse](#cosmos.evidence.v1beta1.MsgSubmitEvidenceResponse) | SubmitEvidence submits an arbitrary Evidence of misbehavior such as equivocation or counterfactual signing. | | +| `GrantFeeAllowance` | [MsgGrantFeeAllowance](#cosmos.feegrant.v1beta1.MsgGrantFeeAllowance) | [MsgGrantFeeAllowanceResponse](#cosmos.feegrant.v1beta1.MsgGrantFeeAllowanceResponse) | GrantFeeAllowance grants fee allowance to the grantee on the granter's account with the provided expiration time. | | +| `RevokeFeeAllowance` | [MsgRevokeFeeAllowance](#cosmos.feegrant.v1beta1.MsgRevokeFeeAllowance) | [MsgRevokeFeeAllowanceResponse](#cosmos.feegrant.v1beta1.MsgRevokeFeeAllowanceResponse) | RevokeFeeAllowance revokes any fee allowance of granter's account that has been granted to the grantee. | | @@ -3835,7 +4591,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 | | @@ -3856,6 +4612,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) | | | + + + + + @@ -4313,6 +5085,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 MsgVoteWeighted response type. + + + + + @@ -4329,6 +5128,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. | | @@ -4669,11 +5469,11 @@ liveness activity. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `address` | [string](#string) | | | -| `start_height` | [int64](#int64) | | height at which validator was first a candidate OR was unjailed | -| `index_offset` | [int64](#int64) | | index offset into signed block bit array | -| `jailed_until` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | timestamp validator cannot be unjailed until | -| `tombstoned` | [bool](#bool) | | whether or not a validator has been tombstoned (killed out of validator set) | -| `missed_blocks_counter` | [int64](#int64) | | missed blocks counter (to avoid scanning the array every time) | +| `start_height` | [int64](#int64) | | Height at which validator was first a candidate OR was unjailed | +| `index_offset` | [int64](#int64) | | Index which is incremented each time the validator was a bonded in a block and may have signed a precommit or not. This in conjunction with the `SignedBlocksWindow` param determines the index in the `MissedBlocksBitArray`. | +| `jailed_until` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | Timestamp until which the validator is jailed due to liveness downtime. | +| `tombstoned` | [bool](#bool) | | Whether or not a validator has been tombstoned (killed out of validator set). It is set once the validator commits an equivocation or for any other configured misbehiavor. | +| `missed_blocks_counter` | [int64](#int64) | | A counter kept to avoid unnecessary array reads. Note that `Sum(MissedBlocksBitArray)` always equals `MissedBlocksCounter`. | @@ -4955,8 +5755,8 @@ Commission defines commission parameters for a given validator. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `commission_rates` | [CommissionRates](#cosmos.staking.v1beta1.CommissionRates) | | | -| `update_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | +| `commission_rates` | [CommissionRates](#cosmos.staking.v1beta1.CommissionRates) | | commission_rates defines the initial commission rates to be used for creating a validator. | +| `update_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | update_time is the last time the commission rate was changed. | @@ -4972,9 +5772,9 @@ a validator. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `rate` | [string](#string) | | | -| `max_rate` | [string](#string) | | | -| `max_change_rate` | [string](#string) | | | +| `rate` | [string](#string) | | rate is the commission rate charged to delegators, as a fraction. | +| `max_rate` | [string](#string) | | max_rate defines the maximum commission rate which validator can ever charge, as a fraction. | +| `max_change_rate` | [string](#string) | | max_change_rate defines the maximum daily increase of the validator commission, as a fraction. | @@ -5059,9 +5859,9 @@ validator. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `delegator_address` | [string](#string) | | | -| `validator_address` | [string](#string) | | | -| `shares` | [string](#string) | | | +| `delegator_address` | [string](#string) | | delegator_address is the bech32-encoded address of the delegator. | +| `validator_address` | [string](#string) | | validator_address is the bech32-encoded address of the validator. | +| `shares` | [string](#string) | | shares define the delegation shares received. | @@ -5093,11 +5893,11 @@ Description defines a validator description. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `moniker` | [string](#string) | | | -| `identity` | [string](#string) | | | -| `website` | [string](#string) | | | -| `security_contact` | [string](#string) | | | -| `details` | [string](#string) | | | +| `moniker` | [string](#string) | | moniker defines a human-readable name for the validator. | +| `identity` | [string](#string) | | identity defines an optional identity signature (ex. UPort or Keybase). | +| `website` | [string](#string) | | website defines an optional website link. | +| `security_contact` | [string](#string) | | security_contact defines an optional email for security contact. | +| `details` | [string](#string) | | details define other optional details. | @@ -5131,11 +5931,11 @@ Params defines the parameters for the staking module. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `unbonding_time` | [google.protobuf.Duration](#google.protobuf.Duration) | | | -| `max_validators` | [uint32](#uint32) | | | -| `max_entries` | [uint32](#uint32) | | | -| `historical_entries` | [uint32](#uint32) | | | -| `bond_denom` | [string](#string) | | | +| `unbonding_time` | [google.protobuf.Duration](#google.protobuf.Duration) | | unbonding_time is the time duration of unbonding. | +| `max_validators` | [uint32](#uint32) | | max_validators is the maximum number of validators. | +| `max_entries` | [uint32](#uint32) | | max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio). | +| `historical_entries` | [uint32](#uint32) | | historical_entries is the number of historical entries to persist. | +| `bond_denom` | [string](#string) | | bond_denom defines the bondable coin denomination. | @@ -5168,10 +5968,12 @@ from a particular source validator to a particular destination validator. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `delegator_address` | [string](#string) | | | -| `validator_src_address` | [string](#string) | | | -| `validator_dst_address` | [string](#string) | | | -| `entries` | [RedelegationEntry](#cosmos.staking.v1beta1.RedelegationEntry) | repeated | redelegation entries | +| `delegator_address` | [string](#string) | | delegator_address is the bech32-encoded address of the delegator. | +| `validator_src_address` | [string](#string) | | validator_src_address is the validator redelegation source operator address. | +| `validator_dst_address` | [string](#string) | | validator_dst_address is the validator redelegation destination operator address. | +| `entries` | [RedelegationEntry](#cosmos.staking.v1beta1.RedelegationEntry) | repeated | entries are the redelegation entries. + +redelegation entries | @@ -5186,10 +5988,10 @@ RedelegationEntry defines a redelegation object with relevant metadata. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `creation_height` | [int64](#int64) | | | -| `completion_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | -| `initial_balance` | [string](#string) | | | -| `shares_dst` | [string](#string) | | | +| `creation_height` | [int64](#int64) | | creation_height defines the height which the redelegation took place. | +| `completion_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | completion_time defines the unix time for redelegation completion. | +| `initial_balance` | [string](#string) | | initial_balance defines the initial balance when redelegation started. | +| `shares_dst` | [string](#string) | | shares_dst is the amount of destination-validator shares created by redelegation. | @@ -5241,9 +6043,11 @@ for a single validator in an time-ordered list. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `delegator_address` | [string](#string) | | | -| `validator_address` | [string](#string) | | | -| `entries` | [UnbondingDelegationEntry](#cosmos.staking.v1beta1.UnbondingDelegationEntry) | repeated | unbonding delegation entries | +| `delegator_address` | [string](#string) | | delegator_address is the bech32-encoded address of the delegator. | +| `validator_address` | [string](#string) | | validator_address is the bech32-encoded address of the validator. | +| `entries` | [UnbondingDelegationEntry](#cosmos.staking.v1beta1.UnbondingDelegationEntry) | repeated | entries are the unbonding delegation entries. + +unbonding delegation entries | @@ -5258,10 +6062,10 @@ UnbondingDelegationEntry defines an unbonding object with relevant metadata. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `creation_height` | [int64](#int64) | | | -| `completion_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | -| `initial_balance` | [string](#string) | | | -| `balance` | [string](#string) | | | +| `creation_height` | [int64](#int64) | | creation_height is the height which the unbonding took place. | +| `completion_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | completion_time is the unix time for unbonding completion. | +| `initial_balance` | [string](#string) | | initial_balance defines the tokens initially scheduled to receive at completion. | +| `balance` | [string](#string) | | balance defines the tokens to receive at completion. | @@ -5298,17 +6102,17 @@ multiplied by exchange rate. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `operator_address` | [string](#string) | | | -| `consensus_pubkey` | [google.protobuf.Any](#google.protobuf.Any) | | | -| `jailed` | [bool](#bool) | | | -| `status` | [BondStatus](#cosmos.staking.v1beta1.BondStatus) | | | -| `tokens` | [string](#string) | | | -| `delegator_shares` | [string](#string) | | | -| `description` | [Description](#cosmos.staking.v1beta1.Description) | | | -| `unbonding_height` | [int64](#int64) | | | -| `unbonding_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | -| `commission` | [Commission](#cosmos.staking.v1beta1.Commission) | | | -| `min_self_delegation` | [string](#string) | | | +| `operator_address` | [string](#string) | | operator_address defines the address of the validator's operator; bech encoded in JSON. | +| `consensus_pubkey` | [google.protobuf.Any](#google.protobuf.Any) | | consensus_pubkey is the consensus public key of the validator, as a Protobuf Any. | +| `jailed` | [bool](#bool) | | jailed defined whether the validator has been jailed from bonded status or not. | +| `status` | [BondStatus](#cosmos.staking.v1beta1.BondStatus) | | status is the validator status (bonded/unbonding/unbonded). | +| `tokens` | [string](#string) | | tokens define the delegated tokens (incl. self-delegation). | +| `delegator_shares` | [string](#string) | | delegator_shares defines total shares issued to a validator's delegators. | +| `description` | [Description](#cosmos.staking.v1beta1.Description) | | description defines the description terms for the validator. | +| `unbonding_height` | [int64](#int64) | | unbonding_height defines, if unbonding, the height at which this validator has begun unbonding. | +| `unbonding_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. | +| `commission` | [Commission](#cosmos.staking.v1beta1.Commission) | | commission defines the commission parameters. | +| `min_self_delegation` | [string](#string) | | min_self_delegation is the validator's self declared minimum self delegation. | @@ -7145,17 +7949,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/core/runtx_middleware.md b/docs/core/runtx_middleware.md index 8d205ca284fe..9c978fbbe00b 100644 --- a/docs/core/runtx_middleware.md +++ b/docs/core/runtx_middleware.md @@ -1,5 +1,5 @@ # RunTx recovery middleware @@ -22,12 +22,12 @@ type RecoveryHandler func(recoveryObj interface{}) error **Contract:** -* RecoveryHandler returns `nil` if `recoveryObj` wasn't handled and should be passed to the next recovery middleware; -* RecoveryHandler returns a non-nil `error` if `recoveryObj` was handled; +- RecoveryHandler returns `nil` if `recoveryObj` wasn't handled and should be passed to the next recovery middleware; +- RecoveryHandler returns a non-nil `error` if `recoveryObj` was handled; ## Custom RecoveryHandler register -``BaseApp.AddRunTxRecoveryHandler(handlers ...RecoveryHandler)`` +`BaseApp.AddRunTxRecoveryHandler(handlers ...RecoveryHandler)` BaseApp method adds recovery middleware to the default recovery chain. diff --git a/docs/using-the-sdk/simulation.md b/docs/core/simulation.md similarity index 54% rename from docs/using-the-sdk/simulation.md rename to docs/core/simulation.md index e8fcb8088f07..9b838b40b526 100644 --- a/docs/using-the-sdk/simulation.md +++ b/docs/core/simulation.md @@ -1,10 +1,14 @@ + + # Cosmos Blockchain Simulator The Cosmos SDK offers a full fledged simulation framework to fuzz test every message defined by a module. -On the SDK, this functionality is provided by the[`SimApp`](https://github.com/cosmos/cosmos-sdk/blob/master/simapp/app.go), which is a -`Baseapp` application that is used for running the [`simulation`](https://github.com/cosmos/cosmos-sdk/tree/master/x/simulation) module. +On the SDK, this functionality is provided by the[`SimApp`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/app.go), which is a +`Baseapp` application that is used for running the [`simulation`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/simulation) module. This module defines all the simulation logic as well as the operations for randomized parameters like accounts, balances etc. @@ -26,30 +30,30 @@ provided operations (randomized or not). The simulation app has different commands, each of which tests a different failure type: -* `AppImportExport`: The simulator exports the initial app state and then it -creates a new app with the exported `genesis.json` as an input, checking for -inconsistencies between the stores. -* `AppSimulationAfterImport`: Queues two simulations together. The first one provides the app state (_i.e_ genesis) to the second. Useful to test software upgrades or hard-forks from a live chain. -* `AppStateDeterminism`: Checks that all the nodes return the same values, in the same order. -* `BenchmarkInvariants`: Analysis of the performance of running all modules' invariants (_i.e_ sequentially runs a [benchmark](https://golang.org/pkg/testing/#hdr-Benchmarks) test). An invariant checks for -differences between the values that are on the store and the passive tracker. Eg: total coins held by accounts vs total supply tracker. -* `FullAppSimulation`: General simulation mode. Runs the chain and the specified operations for a given number of blocks. Tests that there're no `panics` on the simulation. It does also run invariant checks on every `Period` but they are not benchmarked. +- `AppImportExport`: The simulator exports the initial app state and then it + creates a new app with the exported `genesis.json` as an input, checking for + inconsistencies between the stores. +- `AppSimulationAfterImport`: Queues two simulations together. The first one provides the app state (_i.e_ genesis) to the second. Useful to test software upgrades or hard-forks from a live chain. +- `AppStateDeterminism`: Checks that all the nodes return the same values, in the same order. +- `BenchmarkInvariants`: Analysis of the performance of running all modules' invariants (_i.e_ sequentially runs a [benchmark](https://golang.org/pkg/testing/#hdr-Benchmarks) test). An invariant checks for + differences between the values that are on the store and the passive tracker. Eg: total coins held by accounts vs total supply tracker. +- `FullAppSimulation`: General simulation mode. Runs the chain and the specified operations for a given number of blocks. Tests that there're no `panics` on the simulation. It does also run invariant checks on every `Period` but they are not benchmarked. Each simulation must receive a set of inputs (_i.e_ flags) such as the number of blocks that the simulation is run, seed, block size, etc. -Check the full list of flags [here](https://github.com/cosmos/cosmos-sdk/blob/adf6ddd4a807c8363e33083a3281f6a5e112ab89/simapp/sim_test.go#L34-L50). +Check the full list of flags [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/config.go#L32-L55). ## Simulator Modes In addition to the various inputs and commands, the simulator runs in three modes: 1. Completely random where the initial state, module parameters and simulation -parameters are **pseudo-randomly generated**. + parameters are **pseudo-randomly generated**. 2. From a `genesis.json` file where the initial state and the module parameters are defined. -This mode is helpful for running simulations on a known state such as a live network export where a new (mostly likely breaking) version of the application needs to be tested. + This mode is helpful for running simulations on a known state such as a live network export where a new (mostly likely breaking) version of the application needs to be tested. 3. From a `params.json` file where the initial state is pseudo-randomly generated but the module and simulation parameters can be provided manually. -This allows for a more controlled and deterministic simulation setup while allowing the state space to still be pseudo-randomly simulated. -The list of available parameters are listed [here](https://github.com/cosmos/cosmos-sdk/blob/adf6ddd4a807c8363e33083a3281f6a5e112ab89/x/simulation/params.go#L170-L178). + This allows for a more controlled and deterministic simulation setup while allowing the state space to still be pseudo-randomly simulated. + The list of available parameters are listed [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/simulation/params.go#L44-L52). ::: tip These modes are not mutually exclusive. So you can for example run a randomly @@ -59,7 +63,7 @@ generated genesis state (`1`) with manually generated simulation params (`3`). ## Usage This is a general example of how simulations are run. For more specific examples -check the SDK [Makefile](https://github.com/cosmos/cosmos-sdk/blob/adf6ddd4a807c8363e33083a3281f6a5e112ab89/Makefile#L88-L123). +check the SDK [Makefile](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/Makefile#L251-L287). ```bash $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ @@ -72,26 +76,26 @@ check the SDK [Makefile](https://github.com/cosmos/cosmos-sdk/blob/adf6ddd4a807c Here are some suggestions when encountering a simulation failure: -* Export the app state at the height were the failure was found. You can do this -by passing the `-ExportStatePath` flag to the simulator. -* Use `-Verbose` logs. They could give you a better hint on all the operations -involved. -* Reduce the simulation `-Period`. This will run the invariants checks more -frequently. -* Print all the failed invariants at once with `-PrintAllInvariants`. -* Try using another `-Seed`. If it can reproduce the same error and if it fails -sooner you will spend less time running the simulations. -* Reduce the `-NumBlocks` . How's the app state at the height previous to the -failure? -* Run invariants on every operation with `-SimulateEveryOperation`. _Note_: this -will slow down your simulation **a lot**. -* Try adding logs to operations that are not logged. You will have to define a -[Logger](https://github.com/cosmos/cosmos-sdk/blob/adf6ddd4a807c8363e33083a3281f6a5e112ab89/x/staking/keeper/keeper.go#L65:17) on your `Keeper`. +- Export the app state at the height were the failure was found. You can do this + by passing the `-ExportStatePath` flag to the simulator. +- Use `-Verbose` logs. They could give you a better hint on all the operations + involved. +- Reduce the simulation `-Period`. This will run the invariants checks more + frequently. +- Print all the failed invariants at once with `-PrintAllInvariants`. +- Try using another `-Seed`. If it can reproduce the same error and if it fails + sooner you will spend less time running the simulations. +- Reduce the `-NumBlocks` . How's the app state at the height previous to the + failure? +- Run invariants on every operation with `-SimulateEveryOperation`. _Note_: this + will slow down your simulation **a lot**. +- Try adding logs to operations that are not logged. You will have to define a + [Logger](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/staking/keeper/keeper.go#L66-L69) on your `Keeper`. ## Use simulation in your SDK-based application Learn how you can integrate the simulation into your SDK-based application: -* Application Simulation Manager -* [Building modules: Simulator](../building-modules/simulator.md) -* Simulator tests +- Application Simulation Manager +- [Building modules: Simulator](../building-modules/simulator.md) +- Simulator tests diff --git a/docs/core/telemetry.md b/docs/core/telemetry.md index b536fb480dff..9e434eef2bec 100644 --- a/docs/core/telemetry.md +++ b/docs/core/telemetry.md @@ -1,5 +1,5 @@ # Telemetry diff --git a/docs/ibc/README.md b/docs/ibc/README.md index 22061db94583..6298c119632b 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 000000000000..6bdf9f7051fc --- /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/ibc/upgrades/README.md b/docs/ibc/upgrades/README.md new file mode 100644 index 000000000000..11ccabe23785 --- /dev/null +++ b/docs/ibc/upgrades/README.md @@ -0,0 +1,14 @@ + + +### Upgrading IBC Chains Overview + +This directory contains information on how to upgrade an IBC chain without breaking counterparty clients and connections. + +IBC-connnected chains must be able to upgrade without breaking connections to other chains. Otherwise there would be a massive disincentive towards upgrading and disrupting high-value IBC connections, thus preventing chains in the IBC ecosystem from evolving and improving. Many chain upgrades may be irrelevant to IBC, however some upgrades could potentially break counterparty clients if not handled correctly. Thus, any IBC chain that wishes to perform a IBC-client-breaking upgrade must perform an IBC upgrade in order to allow counterparty clients to securely upgrade to the new light client. + +1. The [quick-guide](./quick-guide.md) describes how IBC-connected chains can perform client-breaking upgrades and how relayers can securely upgrade counterparty clients using the SDK. +2. The [developer-guide](./developer-guide.md) is a guide for developers intending to develop IBC client implementations with upgrade functionality. diff --git a/docs/ibc/upgrades/developer-guide.md b/docs/ibc/upgrades/developer-guide.md new file mode 100644 index 000000000000..998cb276e70c --- /dev/null +++ b/docs/ibc/upgrades/developer-guide.md @@ -0,0 +1,50 @@ + + +# IBC Client Developer Guide to Upgrades + +Learn how to implement upgrade functionality for your custom IBC client. {synopsis} + +As mentioned in the [README](./README.md), it is vital that high-value IBC clients can upgrade along with their underlying chains to avoid disruption to the IBC ecosystem. Thus, IBC client developers will want to implement upgrade functionality to enable clients to maintain connections and channels even across chain upgrades. + +The IBC protocol allows client implementations to provide a path to upgrading clients given the upgraded client state, upgraded consensus state and proofs for each. + +```go +// Upgrade functions +// NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last +// height committed by the current revision. Clients are responsible for ensuring that the planned last +// height of the current revision is somehow encoded in the proof verification process. +// This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty +// may be cancelled or modified before the last planned height. +VerifyUpgradeAndUpdateState( + ctx sdk.Context, + cdc codec.BinaryMarshaler, + store sdk.KVStore, + newClient ClientState, + newConsState ConsensusState, + proofUpgradeClient, + proofUpgradeConsState []byte, +) (upgradedClient ClientState, upgradedConsensus ConsensusState, err error) +``` + +Note that the clients should have prior knowledge of the merkle path that the upgraded client and upgraded consensus states will use. The height at which the upgrade has occurred should also be encoded in the proof. The Tendermint client implementation accomplishes this by including an `UpgradePath` in the ClientState itself, which is used along with the upgrade height to construct the merkle path under which the client state and consensus state are committed. + +Developers must ensure that the `UpgradeClientMsg` does not pass until the last height of the old chain has been committed, and after the chain upgrades, the `UpgradeClientMsg` should pass once and only once on all counterparty clients. + +Developers must ensure that the new client adopts all of the new Client parameters that must be uniform across every valid light client of a chain (chain-chosen parameters), while maintaining the Client parameters that are customizable by each individual client (client-chosen parameters) from the previous version of the client. + +Upgrades must adhere to the IBC Security Model. IBC does not rely on the assumption of honest relayers for correctness. Thus users should not have to rely on relayers to maintain client correctness and security (though honest relayers must exist to maintain relayer liveness). While relayers may choose any set of client parameters while creating a new `ClientState`, this still holds under the security model since users can always choose a relayer-created client that suits their security and correctness needs or create a Client with their desired parameters if no such client exists. + +However, when upgrading an existing client, one must keep in mind that there are already many users who depend on this client's particular parameters. We cannot give the upgrading relayer free choice over these parameters once they have already been chosen. This would violate the security model since users who rely on the client would have to rely on the upgrading relayer to maintain the same level of security. Thus, developers must make sure that their upgrade mechanism allows clients to upgrade the chain-specified parameters whenever a chain upgrade changes these parameters (examples in the Tendermint client include `UnbondingPeriod`, `ChainID`, `UpgradePath`, etc.), while ensuring that the relayer submitting the `UpgradeClientMsg` cannot alter the client-chosen parameters that the users are relying upon (examples in Tendermint client include `TrustingPeriod`, `TrustLevel`, `MaxClockDrift`, etc). + +Developers should maintain the distinction between Client parameters that are uniform across every valid light client of a chain (chain-chosen parameters), and Client parameters that are customizable by each individual client (client-chosen parameters); since this distinction is necessary to implement the `ZeroCustomFields` method in the `ClientState` interface: + +```go +// Utility function that zeroes out any client customizable fields in client state +// Ledger enforced fields are maintained while all custom fields are zero values +// Used to verify upgrades +ZeroCustomFields() ClientState +``` + +Counterparty clients can upgrade securely by using all of the chain-chosen parameters from the chain-committed `UpgradedClient` and preserving all of the old client-chosen parameters. This enables chains to securely upgrade without relying on an honest relayer, however it can in some cases lead to an invalid final `ClientState` if the new chain-chosen parameters clash with the old client-chosen parameter. This can happen in the Tendermint client case if the upgrading chain lowers the `UnbondingPeriod` (chain-chosen) to a duration below that of a counterparty client's `TrustingPeriod` (client-chosen). Such cases should be clearly documented by developers, so that chains know which upgrades should be avoided to prevent this problem. The final upgraded client should also be validated in `VerifyUpgradeAndUpdateState` before returning to ensure that the client does not upgrade to an invalid `ClientState`. diff --git a/docs/ibc/upgrades/quick-guide.md b/docs/ibc/upgrades/quick-guide.md new file mode 100644 index 000000000000..4717e52f44c1 --- /dev/null +++ b/docs/ibc/upgrades/quick-guide.md @@ -0,0 +1,54 @@ + + +# How to Upgrade IBC Chains and their Clients + +Learn how to upgrade your chain and counterparty clients. {synopsis} + +The information in this doc for upgrading chains is relevant to SDK chains. However, the guide for counterparty clients is relevant to any Tendermint client that enables upgrades. + +### IBC Client Breaking Upgrades + +IBC-connected chains must perform an IBC upgrade if their upgrade will break counterparty IBC clients. The current IBC protocol supports upgrading tendermint chains for a specific subset of IBC-client-breaking upgrades. Here is the exhaustive list of IBC client-breaking upgrades and whether the IBC protocol currently supports such upgrades. + +IBC currently does **NOT** support unplanned upgrades. All of the following upgrades must be planned and committed to in advance by the upgrading chain, in order for counterparty clients to maintain their connections securely. + +Note: Since upgrades are only implemented for Tendermint clients, this doc only discusses upgrades on Tendermint chains that would break counterparty IBC Tendermint Clients. + +1. Changing the Chain-ID: **Supported** +2. Changing the UnbondingPeriod: **Partially Supported**, chains may increase the unbonding period with no issues. However, decreasing the unbonding period may irreversibly break some counterparty clients. Thus, it is **not recommended** that chains reduce the unbonding period. +3. Changing the height (resetting to 0): **Supported**, so long as chains remember to increment the revision number in their chain-id. +4. Changing the ProofSpecs: **Supported**, this should be changed if the proof structure needed to verify IBC proofs is changed across the upgrade. Ex: Switching from an IAVL store, to a SimpleTree Store +5. Changing the UpgradePath: **Supported**, this might involve changing the key under which upgraded clients and consensus states are stored in the upgrade store, or even migrating the upgrade store itself. +6. Migrating the IBC store: **Unsupported**, as the IBC store location is negotiated by the connection. +7. Upgrading to a backwards compatible version of IBC: Supported +8. Upgrading to a non-backwards compatible version of IBC: **Unsupported**, as IBC version is negotiated on connection handshake. +9. Changing the Tendermint LightClient algorithm: **Partially Supported**. Changes to the light client algorithm that do not change the ClientState or ConsensusState struct may be supported, provided that the counterparty is also upgraded to support the new light client algorithm. Changes that require updating the ClientState and ConsensusState structs themselves are theoretically possible by providing a path to translate an older ClientState struct into the new ClientState struct; however this is not currently implemented. + +### Step-by-Step Upgrade Process for SDK chains + +If the IBC-connected chain is conducting an upgrade that will break counterparty clients, it must ensure that the upgrade is first supported by IBC using the list above and then execute the upgrade process described below in order to prevent counterparty clients from breaking. + +1. Create a `SoftwareUpgradeProposal` with an `UpgradePlan` that includes the new IBC ClientState in the `UpgradedClientState`. Note that the `UpgradePlan` must specify an upgrade height **only** (no upgrade time), and the `ClientState` should only include the fields common to all valid clients and zero out any client-customizable fields (such as TrustingPeriod). +2. Vote on and pass the `SoftwareUpgradeProposal` + +Upon the `SoftwareUpgradeProposal` passing, the upgrade module will commit the UpgradedClient under the key: `upgrade/UpgradedIBCState/{upgradeHeight}/upgradedClient`. On the block right before the upgrade height, the upgrade module will also commit an initial consensus state for the next chain under the key: `upgrade/UpgradedIBCState/{upgradeHeight}/upgradedConsState`. + +Once the chain reaches the upgrade height and halts, a relayer can upgrade the counterparty clients to the last block of the old chain. They can then submit the proofs of the `UpgradedClient` and `UpgradedConsensusState` against this last block and upgrade the counterparty client. + +### Step-by-Step Upgrade Process for Relayers Upgrading Counterparty Clients + +Once the upgrading chain has committed to upgrading, relayers must wait till the chain halts at the upgrade height before upgrading counterparty clients. This is because chains may reschedule or cancel upgrade plans before they occur. Thus, relayers must wait till the chain reaches the upgrade height and halts before they can be sure the upgrade will take place. + +Thus, the upgrade process for relayers trying to upgrade the counterparty clients is as follows: + +1. Wait for the upgrading chain to reach the upgrade height and halt +2. Query a full node for the proofs of `UpgradedClient` and `UpgradedConsensusState` at the last height of the old chain. +3. Update the counterparty client to the last height of the old chain using the `UpdateClient` msg. +4. Submit an `UpgradeClient` msg to the counterparty chain with the `UpgradedClient`, `UpgradedConsensusState` and their respective proofs. +5. Submit an `UpdateClient` msg to the counterparty chain with a header from the new upgraded chain. + +The Tendermint client on the counterparty chain will verify that the upgrading chain did indeed commit to the upgraded client and upgraded consensus state at the upgrade height (since the upgrade height is included in the key). If the proofs are verified against the upgrade height, then the client will upgrade to the new client while retaining all of its client-customized fields. Thus, it will retain its old TrustingPeriod, TrustLevel, MaxClockDrift, etc; while adopting the new chain-specified fields such as UnbondingPeriod, ChainId, UpgradePath, etc. Note, this can lead to an invalid client since the old client-chosen fields may no longer be valid given the new chain-chosen fields. Upgrading chains should try to avoid these situations by not altering parameters that can break old clients. For an example, see the UnbondingPeriod example in the supported upgrades section. + +The upgraded consensus state will serve purely as a basis of trust for future `UpdateClientMsgs` and will not contain a consensus root to perform proof verification against. Thus, relayers must submit an `UpdateClientMsg` with a header from the new chain so that the connection can be used for proof verification again. \ No newline at end of file diff --git a/docs/interfaces/README.md b/docs/interfaces/README.md deleted file mode 100644 index f7fc2cd6e29f..000000000000 --- a/docs/interfaces/README.md +++ /dev/null @@ -1,14 +0,0 @@ - - -# Interfaces (Deprecated) - -This repository contains documentation on interfaces for Cosmos SDK applications. - -1. [Introduction to Interaces](./interfaces-intro.md) -2. [Lifecycle of a Query](./query-lifecycle.md) -3. [Command-Line Interface](./cli.md) -4. [Rest Interface](./rest.md) diff --git a/docs/interfaces/cli.md b/docs/interfaces/cli.md deleted file mode 100644 index d447abe82f17..000000000000 --- a/docs/interfaces/cli.md +++ /dev/null @@ -1,136 +0,0 @@ - - -# Command-Line Interface - -This document describes how to create a commmand-line interface (CLI) for an [**application**](../basics/app-anatomy.md). A separate document for implementing a CLI for an SDK [**module**](../building-modules/intro.md) can be found [here](#../building-modules/module-interfaces.md#cli). {synopsis} - -## Pre-requisite Readings - -* [Lifecycle of a Query](./query-lifecycle.md) {prereq} - -## Command-Line Interface - -One of the main entrypoints of an application is the command-line interface. This entrypoint is created via a `main.go` file which compiles to a binary, conventionally placed in the application's `./cmd/cli` folder. The CLI for an application is typically be referred to as the name of the application suffixed with `-cli`, e.g. `appcli`. Here is where the interfaces docs lie in the directory from the [nameservice tutorial](https://cosmos.network/docs/tutorial). - -### Example Command - -There is no set way to create a CLI, but SDK modules typically use the [Cobra Library](https://github.com/spf13/cobra). Building a CLI with Cobra entails defining commands, arguments, and flags. [**Commands**](#commands) understand the actions users wish to take, such as `tx` for creating a transaction and `query` for querying the application. Each command can also have nested subcommands, necessary for naming the specific transaction type. Users also supply **Arguments**, such as account numbers to send coins to, and [**Flags**](#flags) to modify various aspects of the commands, such as gas prices or which node to broadcast to. - -Here is an example of a command a user might enter to interact with the nameservice CLI `nscli` in order to buy a name: - -```bash -nscli tx nameservice buy-name --gas auto --gas-prices -``` - -The first four strings specify the command: - -- The root command for the entire application `nscli`. -- The subcommand `tx`, which contains all commands that let users create transactions. -- The subcommand `nameservice` to indicate which module to route the command to (`nameservice` module in this case). -- The type of transaction `buy-name`. - -The next two strings are arguments: the `name` the user wishes to buy and the `amount` they want to pay for it. Finally, the last few strings of the command are flags to indicate how much the user is willing to pay in fees (calculated using the amount of gas used to execute the transaction and the gas prices provided by the user). - -The CLI interacts with a [node](../core/node.md) (running `nsd`) to handle this command. The interface itself is defined in a `main.go` file. - -### Building the CLI - -The `main.go` file needs to have a `main()` function that does the following to run the command-line interface: - -* **Instantiate the `codec`** by calling the application's `MakeCodec()` function. The [`codec`](../core/encoding.md) is used to code and encode data structures for the application - stores can only persist `[]byte`s so the developer must define a serialization format for their data structures or use the default, [Amino](../core/encoding.md#amino). -* **Configurations** are set by reading in configuration files (e.g. the sdk config file). -* **Create the root command** to which all the application commands will be added as subcommands and add any required flags to it, such as `--chain-id`. -* **Add subcommands** for all the possible user interactions, including [transaction commands](#transaction-commands) and [query commands](#query-commands). -* **Create an Executor** and [execute](https://godoc.org/github.com/spf13/cobra#Command.Execute) the root command. - -See an example of `main()` function from the `nameservice` application: - -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/cmd/nscli/main.go#L23-L66 - -The rest of the document will detail what needs to be implemented for each step and include smaller portions of code from the nameservice CLI `main.go` file. - -## Adding Commands to the CLI - -Every application CLI first constructs a root command, then adds functionality by aggregating subcommands (often with further nested subcommands) using `rootCmd.AddCommand()`. The bulk of an application's unique capabilities lies in its transaction and query commands, called `TxCmd` and `QueryCmd` respectively. - -### Root Command - -The root command (called `rootCmd`) is what the user first types into the command line to indicate which application they wish to interact with. The string used to invoke the command (the "Use" field) is typically the name of the application suffixed with `-cli`, e.g. `appcli`. The root command typically includes the following commands to support basic functionality in the application. - -* **Status** command from the SDK rpc client tools, which prints information about the status of the connected [`Node`](../core/node.md). The Status of a node includes `NodeInfo`,`SyncInfo` and `ValidatorInfo`. -* **Config** [command](https://github.com/cosmos/cosmos-sdk/blob/master/client/config.go) from the SDK client tools, which allows the user to edit a `config.toml` file that sets values for [flags](#flags) such as `--chain-id` and which `--node` they wish to connect to. -The `config` command can be invoked by typing `appcli config` with optional arguments ` [value]` and a `--get` flag to query configurations or `--home` flag to create a new configuration. -* **Keys** [commands](https://github.com/cosmos/cosmos-sdk/blob/master/client/keys) from the SDK client tools, which includes a collection of subcommands for using the key functions in the SDK crypto tools, including adding a new key and saving it to disk, listing all public keys stored in the key manager, and deleting a key. For example, users can type `appcli keys add ` to add a new key and save an encrypted copy to disk, using the flag `--recover` to recover a private key from a seed phrase or the flag `--multisig` to group multiple keys together to create a multisig key. For full details on the `add` key command, see the code [here](https://github.com/cosmos/cosmos-sdk/blob/master/client/keys/add.go). For more details about usage of `--keyring-backend` for storage of key credentials look at the [keyring docs](/keyring.md). -* [**Transaction**](#transaction-commands) commands. -* [**Query**](#query-commands) commands. - -Next is an example `main()` function from the `nameservice` application. It instantiates the root command, adds a [*persistent* flag](#flags) and `PreRun` function to be run before every execution, and adds all of the necessary subcommands. - -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/cmd/nscli/main.go#L23-L66 - -The root-level `status`, `config`, and `keys` subcommands are common across most applications and do not interact with application state. The bulk of an application's functionality - what users can actually *do* with it - is enabled by its transaction commands. - -### Transaction Commands - -[Transactions](#./transactions.md) are objects wrapping [messages](../building-modules/messages-and-queries.md#messages) that trigger state changes. To enable the creation of transactions using the CLI interface, a function `txCmd` is generally added to the `rootCmd`: - -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/cmd/nscli/main.go#L51 - -This `txCmd` function adds all the transaction available to end-users for the application. This typically includes: - -* **Sign command** from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module that signs messages in a transaction. To enable multisig, add the `auth` module's `MultiSign` command. Since every transaction requires some sort of signature in order to be valid, thithe signing command is necessary for every application. -* **Broadcast command** from the SDK client tools, to broadcast transactions. -* **Send command** from the [`bank`](https://github.com/cosmos/cosmos-sdk/tree/master/x/bank/spec) module, which is a transaction that allows accounts to send coins to one another, including gas and fees for transactions. -* **All [module transaction commands](../building-modules/module-interfaces.md#transaction-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/module-manager.md#basic-manager) `AddTxCommands()` function. - -Here is an example of a `txCmd` aggregating these subcommands from the `nameservice` application: - -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/cmd/nscli/main.go#L97-L118 - - -### Query Commands - -[**Queries**](../building-modules/messages-and-queries.md#queries) are objects that allow users to retrieve information about the application's state. To enable the creation of transactions using the CLI interface, a function `txCmd` is generally added to the `rootCmd`: - -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/cmd/nscli/main.go#L50 - -This `queryCmd` function adds all the queries available to end-users for the application. This typically includes: - -* **QueryTx** and/or other transaction query commands] from the `auth` module which allow the user to search for a transaction by inputting its hash, a list of tags, or a block height. These queries allow users to see if transactions have been included in a block. -* **Account command** from the `auth` module, which displays the state (e.g. account balance) of an account given an address. -* **Validator command** from the SDK rpc client tools, which displays the validator set of a given height. -* **Block command** from the SDK rpc client tools, which displays the block data for a given height. -* **All [module query commands](../building-modules/module-interfaces.md#query-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/module-manager.md#basic-manager) `AddQueryCommands()` function. - -Here is an example of a `queryCmd` aggregating subcommands from the `nameservice` application: - -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/cmd/nscli/main.go#L74-L95 - -## Flags - -Flags are used to modify commands; developers can include them in a `flags.go` file with their CLI. Users can explicitly include them in commands or pre-configure them by entering a command in the format `appcli config ` into their command line. Commonly pre-configured flags include the `--node` to connect to and `--chain-id` of the blockchain the user wishes to interact with. - -A *persistent* flag (as opposed to a _local_ flag) added to a command transcends all of its children: subcommands will inherit the configured values for these flags. Additionally, all flags have default values when they are added to commands; some toggle an option off but others are empty values that the user needs to override to create valid commands. A flag can be explicitly marked as _required_ so that an error is automatically thrown if the user does not provide a value, but it is also acceptable to handle unexpected missing flags differently. - -Flags are added to commands directly (generally in the [module's CLI file](../building-modules/module-interfaces.md#flags) where module commands are defined) and no flag except for the `rootCmd` persistent flags has to be added at application level. It is common to add a _persistent_ flag for `--chain-id`, the unique identifier of the blockchain the application pertains to, to the root command. Adding this flag can be done in the `main()` function. Adding this flag makes sense as the chain ID should not be changing across commands in this application CLI. Here is an example from the `nameservice` application: - -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/cmd/nscli/main.go#L41 - - -## Configurations - -The last function to define in `main.go` is `initConfig`, which does exactly what it sounds like - initialize configurations. To call this function, set it as a `PersistentPreRunE` function for the root command, so that it always executes before the main execution of the root command and any of its subcommands. `initConfig()` does the following: - -1. Read in the `config.toml` file. This same file is edited through `config` commands. -2. Use the [Viper](https://github.com/spf13/viper) to read in configurations from the file and set them. -3. Set any persistent flags defined by the user: `--chain-id`, `--encoding`, `--output`, etc. - -Here is an example of an `initConfig()` function from the [nameservice tutorial CLI](https://cosmos.network/docs/tutorial/entrypoint.html#nscli): - -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/cmd/nscli/main.go#L120-L141 - -And an example of how to add `initConfig` as a `PersistentPreRunE` to the root command: - -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/cmd/nscli/main.go#L42-L44 diff --git a/docs/interfaces/interfaces-intro.md b/docs/interfaces/interfaces-intro.md deleted file mode 100644 index fa2c8bf8d496..000000000000 --- a/docs/interfaces/interfaces-intro.md +++ /dev/null @@ -1,44 +0,0 @@ - - -# Interfaces - -Typically, SDK applications include interfaces to let end-users interact with the application. This document introduces the different types of interfaces for SDK applications. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of an SDK Application](../basics/app-anatomy.md) {prereq} -* [Lifecycle of a Transaction](../basics/tx-lifecycle.md) {prereq} - -## Types of Application Interfaces - -SDK applications generally have a Command-Line Interface (CLI) and REST Interface to support interactions with a [full-node](../core/node.md). The SDK is opinionated about how to create these two interfaces; all modules specify [Cobra commands](https://github.com/spf13/cobra) and register routes using [Gorilla Mux routers](https://github.com/gorilla/mux). The CLI and REST Interface are conventionally defined in the application `./app/cmd/cli` folder. - -## Module vs Application Interfaces - -The process of creating an application interface is distinct from creating a [module interface](../building-modules/module-interfaces.md), though the two are closely intertwined. As expected, module interfaces handle the bulk of the underlying logic, defining ways for end-users to create [messages](../building-modules/messages-and-queries.md#messages) and [queries](../building-modules/messages-and-queries.md#queries) to the subset of application state within their scope. On the other hand, application interfaces aggregate module-level interfaces in order to route `messages` and `queries` to the appropriate modules. Application interfaces also handle root-level responsibilities such as signing and broadcasting [transactions](../core/transactions.md) that wrap messages. - -### Module Developer Responsibilities - -With regards to interfaces, module developers need to include the following definitions: - -* **CLI commands:** Specifically, [Transaction commands](../building-modules/module-interfaces.md#transaction-commands) and [Query commands](../building-modules/module-interfaces.md#query-commands). These are commands that users will invoke when interacting with the application to create transactions and queries. For example, if an application enables sending coins through the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module, users will create `tx auth send` transactions. -* **Request Handlers:** Also categorized into Transaction and Query requests. Transactions will require HTTP [Request Types](../building-modules/module-interfaces.md#request-types) in addition to [Request Handlers](../building-modules/module-interfaces.md#request-handlers) in order to encapsulate all of the user's options (e.g. gas prices). -* **REST Routes:** Given a router, the module interface registers paths with the aforementioned [Request Handlers](../building-modules/module-interfaces.md#request-handlers) for each type of request. - -Module interfaces are designed to be generic. Both commands and request types include required user input (through flags or request body) which are different for each application. This section of documents will only detail application interfaces; to read about how to build module interfaces, click [here](../building-modules/module-interfaces.md). - -### Application Developer Responsibilities - -With regards to interfaces, application developers need to include: - -* **CLI Root Command:** The [root command](./cli.md#root-command) adds subcommands to include all of the functionality for the application, mainly module [transaction](./cli.md#transaction-commands) and [query](./cli.md#query-commands) commands from the application's module(s). -* **App Configurations:** All application-specific values are the responsibility of the application developer, including the [`codec`](../core/encoding.md) used to marshal requests before relaying them to a node. -* **User Configurations:** Some values are specific to the user, such as the user's address and which node they are connected to. The CLI has a [configurations](./cli.md#configurations) function to set these values. -* **RegisterRoutes Function:** [Routes](./rest.md#registerroutes) must be registered and passed to an instantiated [REST server](./rest.md#rest-server) so that it knows how to route requests for this particular application. - - -## Next {hide} - -Read about the [Lifecycle of a Query](./query-lifecycle.md) {hide} diff --git a/docs/interfaces/lite/getting_started.md b/docs/interfaces/lite/getting_started.md deleted file mode 100644 index 23c92821b4e3..000000000000 --- a/docs/interfaces/lite/getting_started.md +++ /dev/null @@ -1,20 +0,0 @@ -# Getting Started - -To start a REST server, we need to specify the following parameters: - -| Parameter | Type | Default | Required | Description | -| ----------- | --------- | ----------------------- | -------- | ---------------------------------------------------- | -| chain-id | string | null | true | chain id of the full node to connect | -| node | URL | "tcp://localhost:46657" | true | address of the full node to connect | -| laddr | URL | "tcp://localhost:1317" | true | address to run the rest server on | -| trust-store | DIRECTORY | "$HOME/.lcd" | false | directory for save checkpoints and validator sets | - -For example: - -```bash -gaiacli rest-server --chain-id=test \ - --laddr=tcp://localhost:1317 \ - --node tcp://localhost:26657 \ -``` - -For more information about the Gaia-Lite RPC, see the [swagger documentation](https://cosmos.network/rpc/) diff --git a/docs/interfaces/lite/pics/C2H.png b/docs/interfaces/lite/pics/C2H.png deleted file mode 100644 index 49f7e07f320e..000000000000 Binary files a/docs/interfaces/lite/pics/C2H.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/H2C.png b/docs/interfaces/lite/pics/H2C.png deleted file mode 100644 index 027eafcde1f4..000000000000 Binary files a/docs/interfaces/lite/pics/H2C.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/MA.png b/docs/interfaces/lite/pics/MA.png deleted file mode 100644 index ae3823962855..000000000000 Binary files a/docs/interfaces/lite/pics/MA.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/absence1.png b/docs/interfaces/lite/pics/absence1.png deleted file mode 100755 index 70b4c9e8d227..000000000000 Binary files a/docs/interfaces/lite/pics/absence1.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/absence2.png b/docs/interfaces/lite/pics/absence2.png deleted file mode 100755 index 6ce5bcde31de..000000000000 Binary files a/docs/interfaces/lite/pics/absence2.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/absence3.png b/docs/interfaces/lite/pics/absence3.png deleted file mode 100755 index d3afdc2c5f7f..000000000000 Binary files a/docs/interfaces/lite/pics/absence3.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/architecture.png b/docs/interfaces/lite/pics/architecture.png deleted file mode 100644 index 741a90c26775..000000000000 Binary files a/docs/interfaces/lite/pics/architecture.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/changeProcess.png b/docs/interfaces/lite/pics/changeProcess.png deleted file mode 100755 index 1771c239b92f..000000000000 Binary files a/docs/interfaces/lite/pics/changeProcess.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/commitValidation.png b/docs/interfaces/lite/pics/commitValidation.png deleted file mode 100755 index 89985bcc12ea..000000000000 Binary files a/docs/interfaces/lite/pics/commitValidation.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/create-account.png b/docs/interfaces/lite/pics/create-account.png deleted file mode 100644 index 35a249b97d9e..000000000000 Binary files a/docs/interfaces/lite/pics/create-account.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/deposit.png b/docs/interfaces/lite/pics/deposit.png deleted file mode 100644 index 1fb3acdc5aa2..000000000000 Binary files a/docs/interfaces/lite/pics/deposit.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/existProof.png b/docs/interfaces/lite/pics/existProof.png deleted file mode 100755 index ee5de0851bf1..000000000000 Binary files a/docs/interfaces/lite/pics/existProof.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/high-level.png b/docs/interfaces/lite/pics/high-level.png deleted file mode 100644 index 73a14e0d73db..000000000000 Binary files a/docs/interfaces/lite/pics/high-level.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/light-client-architecture.png b/docs/interfaces/lite/pics/light-client-architecture.png deleted file mode 100644 index df44aeef5055..000000000000 Binary files a/docs/interfaces/lite/pics/light-client-architecture.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/loadbalanceDiagram.png b/docs/interfaces/lite/pics/loadbalanceDiagram.png deleted file mode 100644 index 56956ee9d8ec..000000000000 Binary files a/docs/interfaces/lite/pics/loadbalanceDiagram.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/simpleMerkleTree.png b/docs/interfaces/lite/pics/simpleMerkleTree.png deleted file mode 100755 index 5c4e2b76e4ac..000000000000 Binary files a/docs/interfaces/lite/pics/simpleMerkleTree.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/substoreProof.png b/docs/interfaces/lite/pics/substoreProof.png deleted file mode 100755 index 90dadaef37af..000000000000 Binary files a/docs/interfaces/lite/pics/substoreProof.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/transfer-tokens.png b/docs/interfaces/lite/pics/transfer-tokens.png deleted file mode 100644 index c6635a30c8b5..000000000000 Binary files a/docs/interfaces/lite/pics/transfer-tokens.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/transfer.png b/docs/interfaces/lite/pics/transfer.png deleted file mode 100644 index 7642e996cf32..000000000000 Binary files a/docs/interfaces/lite/pics/transfer.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/trustPropagate.png b/docs/interfaces/lite/pics/trustPropagate.png deleted file mode 100755 index a743cfc72147..000000000000 Binary files a/docs/interfaces/lite/pics/trustPropagate.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/updateValidatorToHeight.png b/docs/interfaces/lite/pics/updateValidatorToHeight.png deleted file mode 100755 index b2c9349b6b45..000000000000 Binary files a/docs/interfaces/lite/pics/updateValidatorToHeight.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/validatorSetChange.png b/docs/interfaces/lite/pics/validatorSetChange.png deleted file mode 100755 index 16dbf39be7ea..000000000000 Binary files a/docs/interfaces/lite/pics/validatorSetChange.png and /dev/null differ diff --git a/docs/interfaces/lite/pics/withdraw.png b/docs/interfaces/lite/pics/withdraw.png deleted file mode 100644 index 2249b5e34839..000000000000 Binary files a/docs/interfaces/lite/pics/withdraw.png and /dev/null differ diff --git a/docs/interfaces/lite/readme.md b/docs/interfaces/lite/readme.md deleted file mode 100644 index e31ccd0752e5..000000000000 --- a/docs/interfaces/lite/readme.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -parent: - order: false ---- - -# Light Client Overview - -**See the Cosmos SDK Light Client RPC documentation [here](https://cosmos.network/rpc/)** - -## Introduction - -A light client allows clients, such as mobile phones, to receive proofs of the state of the -blockchain from any full node. Light clients do not have to trust any full node, since they are able -to verify any proof they receive. - -A light client can provide the same security as a full node with minimal requirements for -bandwidth, computing and storage resource. It can also provide modular functionality -according to users' configuration. These features allow developers to build secure, efficient, -and usable mobile apps, websites, and other applications without deploying or -maintaining any full blockchain nodes. - -### What is a Light Client? - -The Cosmos SDK Light Client (Gaia-lite) is split into two separate components. The first component is generic for -any Tendermint-based application. It handles the security and connectivity aspects of following the header -chain and verify proofs from full nodes against a locally trusted validator set. Furthermore, it exposes the same -API as any Tendermint Core node. The second component is specific for the Cosmos Hub (`gaiad`). It works as a query -endpoint and exposes the application specific functionality, which can be arbitrary. All queries against the -application state must go through the query endpoint. The advantage of the query endpoint is that it can verify -the proofs that the application returns. - -### High-Level Architecture - -An application developer that wants to build a third party client application for the Cosmos Hub (or any -other zone) should build it against its canonical API. That API is a combination of multiple parts. -All zones have to expose ICS0 (TendermintAPI). Beyond that any zone is free to choose any -combination of module APIs, depending on which modules the state machine uses. The Cosmos Hub will -initially support [ICS0](https://cosmos.network/rpc/#/ICS0) (TendermintAPI), [ICS1](https://cosmos.network/rpc/#/ICS1) (KeyAPI), [ICS20](https://cosmos.network/rpc/#/ICS20) (TokenAPI), [ICS21](https://cosmos.network/rpc/#/ICS21) (StakingAPI), -[ICS22](https://cosmos.network/rpc/#/ICS22) (GovernanceAPI) and [ICS23](https://cosmos.network/rpc/#/ICS23) (SlashingAPI). - -![high-level](./pics/high-level.png) - -All applications are expected to run only against Gaia-lite. Gaia-lite is the only piece of software -that offers stability guarantees around the zone API. - -### Comparison - -A full node of ABCI is different from a light client in the following ways: - -|| Full Node | Gaia-lite | Description| -|-| ------------- | ----- | -------------- | -| Execute and verify transactions|Yes|No|A full node will execute and verify all transactions while Gaia-lite won't.| -| Verify and save blocks|Yes|No|A full node will verify and save all blocks while Gaia-lite won't.| -| Consensus participation|Yes|No|Only when a full node is a validator will it participate in consensus. Lite nodes never participate in consensus.| -| Bandwidth cost|High|Low|A full node will receive all blocks. If bandwidth is limited, it will fall behind the main network. What's more, if it happens to be a validator, it will slow down the consensus process. Light clients require little bandwidth, only when serving local requests.| -| Computing resources|High|Low|A full node will execute all transactions and verify all blocks, which requires considerable computing resources.| -| Storage resources|High|Low|A full node will save all blocks and ABCI states. Gaia-lite just saves validator sets and some checkpoints.| -| Power consumption|High|Low|Full nodes must be deployed on machines which have high performance and will be running all the time. Gaia-lite can be deployed on the same machines as users' applications, or on independent machines but with lower performance. Light clients can be shut down anytime when necessary. Gaia-lite consumes very little power, so even mobile devices can meet the power requirements.| -| Provide APIs|All cosmos APIs|Modular APIs|A full node supports all Cosmos APIs. Gaia-lite provides modular APIs according to users' configuration.| -| Secuity level| High|High|A full node will verify all transactions and blocks by itself. A light client can't do this, but it can query data from other full nodes and verify the data independently. Therefore, both full nodes and light clients don't need to trust any third nodes and can achieve high security.| - -According to the above table, Gaia-lite can meet many users' functionality and security requirements, but require little bandwidth, computing, storage, and power. - -## Achieving Security - -### Trusted Validator Set - -The base design philosophy of Gaia-lite follows two rules: - -1. **Doesn't trust any blockchain nodes, including validator nodes and other full nodes** -2. **Only trusts the whole validator set** - -The original trusted validator set should be prepositioned into its trust store. Usually this -validator set comes from a genesis file. During runtime, if Gaia-lite detects a different validator set, -it will verify it and save the new validated validator set to the trust store. - -![validator-set-change](./pics/validatorSetChange.png) - -### Trust Propagation - -From the above section, we come to know how to get a trusted validator set and how lcd keeps track of -validator set evolution. The validator set is the foundation of trust, and the trust can propagate to -other blockchain data, such as blocks and transactions. The propagation architecture is shown as - -follows: - -![change-process](./pics/trustPropagate.png) - -In general, with a trusted validator set, a light client can verify each block commit which contains all pre-commit -data and block header data. Then the block hash, data hash and appHash are trusted. Based on this -and merkle proof, all transactions data and ABCI states can be verified too. diff --git a/docs/interfaces/lite/specification.md b/docs/interfaces/lite/specification.md deleted file mode 100644 index 4ce849acaa1d..000000000000 --- a/docs/interfaces/lite/specification.md +++ /dev/null @@ -1,209 +0,0 @@ -# Specifications - -This specification describes how to implement the LCD. LCD supports modular APIs. Currently, only -ICS0 (TendermintAPI), ICS1 (Key API) and ICS20 (Token API) are supported. Later, if necessary, more -APIs can be included. - -## Build and Verify Proof of ABCI States - -As we all know, storage of cosmos-sdk based application contains multi-substores. Each substore is -implemented by a IAVL store. These substores are organized by simple Merkle tree. To build the tree, -we need to extract name, height and store root hash from these substores to build a set of simple -Merkle leaf nodes, then calculate hash from leaf nodes to root. The root hash of the simple Merkle -tree is the AppHash which will be included in block header. - -![Simple Merkle Tree](./pics/simpleMerkleTree.png) - -As we have discussed in LCD trust-propagation, -the AppHash can be verified by checking voting power against a trusted validator set. Here we just -need to build proof from ABCI state to AppHash. The proof contains two parts: - -* IAVL proof -* Substore to AppHash proof - -### IAVL Proof - -The proof has two types: existence proof and absence proof. If the query key exists in the IAVL -store, then it returns key-value and its existence proof. On the other hand, if the key doesn't -exist, then it only returns absence proof which can demonstrate the key definitely doesn't exist. - -### IAVL Existence Proof - -```go -type CommitID struct { - Version int64 - Hash []byte -} - -type storeCore struct { - CommitID CommitID -} - -type MultiStoreCommitID struct { - Name string - Core storeCore -} - -type proofInnerNode struct { - Height int8 - Size int64 - Version int64 - Left []byte - Right []byte -} - -type KeyExistsProof struct { - MultiStoreCommitInfo []MultiStoreCommitID //All substore commitIDs - StoreName string //Current substore name - Height int64 //The commit height of current substore - RootHash cmn.HexBytes //The root hash of this IAVL tree - Version int64 //The version of the key-value in this IAVL tree - InnerNodes []proofInnerNode //The path from to root node to key-value leaf node -} -``` - -The data structure of exist proof is shown as above. The process to build and verify existence proof -is shown as follows: - -![Exist Proof](./pics/existProof.png) - -Steps to build proof: - -* Access the IAVL tree from the root node. -* Record the visited nodes in InnerNodes, -* Once the target leaf node is found, assign leaf node version to proof version -* Assign the current IAVL tree height to proof height -* Assign the current IAVL tree rootHash to proof rootHash -* Assign the current substore name to proof StoreName -* Read multistore commitInfo from db by height and assign it to proof StoreCommitInfo - -Steps to verify proof: - -* Build leaf node with key, value and proof version. -* Calculate leaf node hash -* Assign the hash to the first innerNode's rightHash, then calculate first innerNode hash -* Propagate the hash calculation process. If prior innerNode is the left child of next innerNode, then assign the prior innerNode hash to the left hash of next innerNode. Otherwise, assign the prior innerNode hash to the right hash of next innerNode. -* The hash of last innerNode should be equal to the rootHash of this proof. Otherwise, the proof is invalid. - -### IAVL Absence Proof - -As we all know, all IAVL leaf nodes are sorted by the key of each leaf nodes. So we can calculate -the position of the target key in the whole key set of this IAVL tree. As shown below, we can find -out the left key and the right key. If we can demonstrate that both left key and right key -definitely exist, and they are adjacent nodes. Thus the target key definitely doesn't exist. - -![Absence Proof1](./pics/absence1.png) - -If the target key is larger than the right most leaf node or less than the left most key, then the -target key definitely doesn't exist. - -![Absence Proof2](./pics/absence2.png)![Absence Proof3](./pics/absence3.png) - -```go -type proofLeafNode struct { - KeyBytes cmn.HexBytes - ValueBytes cmn.HexBytes - Version int64 -} - -type pathWithNode struct { - InnerNodes []proofInnerNode - Node proofLeafNode -} - -type KeyAbsentProof struct { - MultiStoreCommitInfo []MultiStoreCommitID - StoreName string - Height int64 - RootHash cmn.HexBytes - Left *pathWithNode // Proof the left key exist - Right *pathWithNode //Proof the right key exist -} -``` - -The above is the data structure of absence proof. Steps to build proof: - -* Access the IAVL tree from the root node. -* Get the deserved index(Marked as INDEX) of the key in whole key set. -* If the returned index equals to 0, the right index should be 0 and left node doesn't exist -* If the returned index equals to the size of the whole key set, the left node index should be INDEX-1 and the right node doesn't exist. -* Otherwise, the right node index should be INDEX and the left node index should be INDEX-1 -* Assign the current IAVL tree height to proof height -* Assign the current IAVL tree rootHash to proof rootHash -* Assign the current substore name to proof StoreName -* Read multistore commitInfo from db by height and assign it to proof StoreCommitInfo - -Steps to verify proof: - -* If only right node exist, verify its exist proof and verify if it is the left most node -* If only left node exist, verify its exist proof and verify if it is the right most node. -* If both right node and left node exist, verify if they are adjacent. - -### Substores to AppHash Proof - -After verify the IAVL proof, then we can start to verify substore proof against AppHash. Firstly, -iterate MultiStoreCommitInfo and find the substore commitID by proof StoreName. Verify if yhe Hash -in commitID equals to proof RootHash. If not, the proof is invalid. Then sort the substore -commitInfo array by the hash of substore name. Finally, build the simple Merkle tree with all -substore commitInfo array and verify if the Merkle root hash equal to appHash. - -![substore proof](./pics/substoreProof.png) - -```go -func SimpleHashFromTwoHashes(left []byte, right []byte) []byte { - var hasher = ripemd160.New() - - err := encodeByteSlice(hasher, left) - if err != nil { - panic(err) - } - - err = encodeByteSlice(hasher, right) - if err != nil { - panic(err) - } - - return hasher.Sum(nil) -} - -func SimpleHashFromHashes(hashes [][]byte) []byte { - // Recursive impl. - switch len(hashes) { - case 0: - return nil - case 1: - return hashes[0] - default: - left := SimpleHashFromHashes(hashes[:(len(hashes)+1)/2]) - right := SimpleHashFromHashes(hashes[(len(hashes)+1)/2:]) - return SimpleHashFromTwoHashes(left, right) - } -} -``` - -## Verify block header against validator set - -Above sections refer appHash frequently. But where does the trusted appHash come from? Actually, -the appHash exist in block header, next we need to verify blocks header at specific height against -LCD trusted validator set. The validation flow is shown as follows: - -![commit verification](./pics/commitValidation.png) - -When the trusted validator set doesn't match the block header, we need to try to update our -validator set to the height of this block. LCD has a rule that each validator set change should not -affect more than 1/3 voting power. Compare with the trusted validator set, if the voting power of -target validator set changes more than 1/3. We have to verify if there are hidden validator set -changes before the target validator set. Only when all validator set changes obey this rule, can our -validator set update be accomplished. - -For instance: - -![Update validator set to height](./pics/updateValidatorToHeight.png) - -* Update to 10000, tooMuchChangeErr -* Update to 5050, tooMuchChangeErr -* Update to 2575, Success -* Update to 5050, Success -* Update to 10000,tooMuchChangeErr -* Update to 7525, Success -* Update to 10000, Success diff --git a/docs/interfaces/query-lifecycle.md b/docs/interfaces/query-lifecycle.md deleted file mode 100644 index ae4a2e800b50..000000000000 --- a/docs/interfaces/query-lifecycle.md +++ /dev/null @@ -1,162 +0,0 @@ - - -# Query Lifecycle - -This document describes the lifecycle of a query in a SDK application, from the user interface to application stores and back. The query will be referred to as `Query`. {synopsis} - -## Pre-requisite Readings - -* [Introduction to Interfaces](./interfaces-intro.md) {prereq} - -## Query Creation - -A [**query**](../building-modules/messages-and-queries.md#queries) is a request for information made by end-users of applications through an interface and processed by a full-node. Users can query information about the network, the application itself, and application state directly from the application's stores or modules. Note that queries are different from [transactions](../core/transactions.md) (view the lifecycle [here](../basics/tx-lifecycle.md)), particularly in that they do not require consensus to be processed (as they do not trigger state-transitions); they can be fully handled by one full-node. - -For the purpose of explaining the query lifecycle, let's say `Query` is requesting a list of delegations made by a certain delegator address in the application called `app`. As to be expected, the [`staking`](https://github.com/cosmos/cosmos-sdk/tree/master/x/staking/spec) module handles this query. But first, there are a few ways `Query` can be created by users. - -### CLI - -The main interface for an application is the command-line interface. Users connect to a full-node and run the CLI directly from their machines - the CLI interacts directly with the full-node. To create `Query` from their terminal, users type the following command: - -```bash -appcli query staking delegations -``` - -This query command was defined by the [`staking`](https://github.com/cosmos/cosmos-sdk/tree/master/x/staking/spec) module developer and added to the list of subcommands by the application developer when creating the CLI. The code for this particular command is the following: - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/staking/client/cli/query.go#L250-L293 - -Note that the general format is as follows: - -```bash -appcli query [moduleName] [command] --flag -``` - -To provide values such as `--node` (the full-node the CLI connects to), the user can use the `config` command to set themn or provide them as flags. - -The CLI understands a specific set of commands, defined in a hierarchical structure by the application developer: from the [root command](./cli.md#root-command) (`appcli`), the type of command (`query`), the module that contains the command (`staking`), and command itself (`delegations`). Thus, the CLI knows exactly which module handles this command and directly passes the call there. - -### REST - -Another interface through which users can make queries is through HTTP Requests to a [REST server](./rest.md#rest-server). The REST server contains, among other things, a [`Context`](#context) and [mux](./rest.md#gorilla-mux) router. The request looks like this: - -```bash -GET http://localhost:{PORT}/staking/delegators/{delegatorAddr}/delegations -``` - -To provide values such as `--node` (the full-node the CLI connects to) that are required by [`baseReq`](../building-modules/module-interfaces.md#basereq), the user must configure their local REST server with the values or provide them in the request body. - -The router automatically routes the `Query` HTTP request to the staking module `delegatorDelegationsHandlerFn()` function. - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/staking/client/rest/query.go#L103-L106 - -Since this function is defined within the module and thus has no inherent knowledge of the application `Query` belongs to, it takes in the application `codec` and `Context` as parameters. - -To summarize, when users interact with the interfaces, they create a CLI command or HTTP request. `Query` now exists in one of these two forms, but needs to be transformed into an object understood by a full-node. - -## Query Preparation - -The interactions from the users' perspective are a bit different, but the underlying functions are almost identical because they are implementations of the same command defined by the module developer. This step of processing happens within the CLI or REST server and heavily involves a `Context`. - -### Context - -The first thing that is created in the execution of a CLI command is a `Context`, while the REST Server directly provides a `Context` for the REST Request handler. A [Context](../core/context.md) is an immutable object that stores all the data needed to process a request on the user side. In particular, a `Context` stores the following: - -* **Codec**: The [encoder/decoder](../core/encoding.md) used by the application, used to marshal the parameters and query before making the Tendermint RPC request and unmarshal the returned response into a JSON object. -* **Account Decoder**: The account decoder from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module, which translates `[]byte`s into accounts. -* **RPC Client**: The Tendermint RPC Client, or node, to which the request will be relayed to. -* **Keybase**: A [Key Manager](../basics/accounts.md#keybase) used to sign transactions and handle other operations with keys. -* **Output Writer**: A [Writer](https://golang.org/pkg/io/#Writer) used to output the response. -* **Configurations**: The flags configured by the user for this command, including `--height`, specifying the height of the blockchain to query and `--indent`, which indicates to add an indent to the JSON response. - -The `Context` also contains various functions such as `Query()` which retrieves the RPC Client and makes an ABCI call to relay a query to a full-node. - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/client/context/context.go#L23-L47 - -The `Context`'s primary role is to store data used during interactions with the end-user and provide methods to interact with this data - it is used before and after the query is processed by the full-node. Specifically, in handling `Query`, the `Context` is utilized to encode the query parameters, retrieve the full-node, and write the output. Prior to being relayed to a full-node, the query needs to be encoded into a `[]byte` form, as full-nodes are application-agnostic and do not understand specific types. The full-node (RPC Client) itself is retrieved using the `Context`, which knows which node the user CLI is connected to. The query is relayed to this full-node to be processed. Finally, the `Context` contains a `Writer` to write output when the response is returned. These steps are further described in later sections. - -### Arguments and Route Creation - -At this point in the lifecycle, the user has created a CLI command or HTTP Request with all of the data they wish to include in their `Query`. A `Context` exists to assist in the rest of the `Query`'s journey. Now, the next step is to parse the command or request, extract the arguments, create a `queryRoute`, and encode everything. These steps all happen on the user side within the interface they are interacting with. - -#### Encoding - -In this case, `Query` contains an [address](../basics/accounts.md#addresses) `delegatorAddress` as its only argument. However, the request can only contain `[]byte`s, as it will be relayed to a consensus engine (e.g. Tendermint Core) of a full-node that has no inherent knowledge of the application types. Thus, the `codec` of `Context` is used to marshal the address. - -Here is what the code looks like for the CLI command: - -```go -delAddr, err := sdk.AccAddressFromBech32(args[0]) -bz, err := cdc.MarshalJSON(types.NewQueryDelegatorParams(delAddr)) -``` - -Here is what the code looks like for the HTTP Request: - -```go -vars := mux.Vars(r) -bech32delegator := vars["delegatorAddr"] -delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) -cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) -if !ok { - return -} -params := types.NewQueryDelegatorParams(delegatorAddr) -``` - -#### Query Route Creation - -Important to note is that there will never be a "query" object created for `Query`; the SDK actually takes a simpler approach. Instead of an object, all the full-node needs to process a query is its `route` which specifies exactly which module to route the query to and the name of this query type. The `route` will be passed to the application `baseapp`, then module, then [querier](../building-modules/query-services.md#legacy-queriers), and each will understand the `route` and pass it to the appropriate next step. [`baseapp`](../core/baseapp.md#query-routing) will understand this query to be a `custom` query in the module `staking`, and the `staking` module querier supports the type `QueryDelegatorDelegations`. Thus, the route will be `"custom/staking/delegatorDelegations"`. - -Here is what the code looks like: - -```go -route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegatorDelegations) -``` - -Now, `Query` exists as a set of encoded arguments and a route to a specific module and its query type. It is ready to be relayed to a full-node. - -#### ABCI Query Call - -The `Context` has a `Query()` function used to retrieve the pre-configured node and relay a query to it; the function takes the query `route` and arguments as parameters. It first retrieves the RPC Client (called the [**node**](../core/node.md)) configured by the user to relay this query to, and creates the `ABCIQueryOptions` (parameters formatted for the ABCI call). The node is then used to make the ABCI call, `ABCIQueryWithOptions()`. - -Here is what the code looks like: - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/client/context/query.go#L75-L112 - -## RPC - -With a call to `ABCIQueryWithOptions()`, `Query` is received by a [full-node](../core/encoding.md) which will then process the request. Note that, while the RPC is made to the consensus engine (e.g. Tendermint Core) of a full-node, queries are not part of consensus and will not be broadcasted to the rest of the network, as they do not require anything the network needs to agree upon. - -Read more about ABCI Clients and Tendermint RPC in the Tendermint documentation [here](https://tendermint.com/rpc). - -## Application Query Handling - -When a query is received by the full-node after it has been relayed from the underlying consensus engine, it is now being handled within an environment that understands application-specific types and has a copy of the state. [`baseapp`](../core/baseapp.md) implements the ABCI [`Query()`](../core/baseapp.md#query) function and handles four different types of queries: `app`, `store`, `p2p`, and `custom`. The `queryRoute` is parsed such that the first string must be one of the four options, then the rest of the path is parsed within the subroutines handling each type of query. The first three types (`app`, `store`, `p2p`) are purely application-level and thus directly handled by `baseapp` or the stores, but the `custom` query type requires `baseapp` to route the query to a module's [query service](../building-modules/query-services.md). - -Since `Query` is a custom query type from the `staking` module, `baseapp` first parses the path, then uses the `QueryRouter` to retrieve the corresponding querier, and routes the query to the module. The querier is responsible for recognizing this query, retrieving the appropriate values from the application's stores, and returning a response. Read more about query services [here](../building-modules/query-services.md). - -Once a result is received from the querier, `baseapp` begins the process of returning a response to the user. - -## Response - -Since `Query()` is an ABCI function, `baseapp` returns the response as an [`abci.ResponseQuery`](https://tendermint.com/docs/spec/abci/abci.html#messages) type. The `Context` `Query()` routine receives the response and. - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/client/context/query.go#L127-L165 - -### CLI Response - -The application [`codec`](../core/encoding.md) is used to unmarshal the response to a JSON and the `Context` prints the output to the command line, applying any configurations such as `--indent`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/staking/client/cli/query.go#L252-L293 - -### REST Response - -The [REST server](./rest.md#rest-server) uses the `Context` to format the response properly, then uses the HTTP package to write the appropriate response or error. - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/staking/client/rest/utils.go#L115-L148 - -## Next {hide} - -Read about how to build a [Command-Line Interface](./cli.md), or a [REST Interface](./rest.md) {hide} diff --git a/docs/interfaces/rest.md b/docs/interfaces/rest.md deleted file mode 100644 index 00e94b1d9a9d..000000000000 --- a/docs/interfaces/rest.md +++ /dev/null @@ -1,70 +0,0 @@ - - -# REST Interface - -This document describes how to create a REST interface for an SDK **application**. A separate document for creating a [**module**](../building-modules/intro.md) REST interface can be found [here](#../module-interfaces.md#legacy-rest). {synopsis} - -## Pre-requisite Readings - -- [Query Lifecycle](./query-lifecycle.md) {prereq} -- [Application CLI](./cli.md) {prereq} - -## Application REST Interface - -Building the REST Interface for an application is done by [aggregating REST Routes](#registering-routes) defined in the application's modules. This interface is served by a REST Server [REST server](#rest-server), which route requests and output responses in the application itself. The SDK comes with its own REST Server by default. To enable it, the `rest.ServeCommand` command needs to be added as a subcommand of the `rootCmd` in the `main()` function of the [CLI interface](./cli.md): - -```go -rootCmd.AddCommand(rest.ServeCommand(cdc, registerRoutes)) -``` - -Users will then be able to use the application CLI to start a new REST server, a local server through which they can securely interact with the application without downloading the entire state. The command entered by users would look something like this: - -```bash -appcli rest-server --chain-id -``` - -## REST Server - -A REST Server is used to receive and route HTTP Requests, obtain the results from the application, and return a response to the user. The REST Server defined by the SDK `rest` package contains the following: - -- **Router:** A router for HTTP requests. A new router can be instantiated for an application and used to match routes based on path, request method, headers, etc. The SDK uses the [Gorilla Mux Router](https://github.com/gorilla/mux). -- **Context:** A [`Context`](./query-lifecycle.md#context) created for a user interaction. -- **Keybase:** A [Keybase](../basics/accounts.md#keybase) is a key manager. -- **Logger:** A logger from Tendermint `Log`, a log package structured around key-value pairs that allows logging level to be set differently for different keys. The logger takes `Debug()`, `Info()`, and `Error()`s. -- **Listener:** A [listener](https://golang.org/pkg/net/#Listener) from the net package. - -Of the five, the only attribute that application developers need interact with is the `router`: they need to add routes to it so that the REST server can properly handle queries. See the next section for more information on registering routes. - -In order to enable the REST Server in an SDK application, the `rest.ServeCommand` needs to be added to the application's command-line interface. See the [above section](#application-rest-interface) for more information. - -## Registering Routes - -To include routes for each module in an application, the CLI must have some kind of function to register routes in its REST Server. This function is called `RegisterRoutes()`, and is utilized by the `ServeCommand` and must include routes for each of the application's modules. Since each module used by an SDK application implements a [`RegisterRESTRoutes`](../building-modules/module-interfaces.md#legacy-rest) function, application developers simply use the [Module Manager](../building-modules/module-manager.md) to call this function for each module (this is done in the [application's constructor](../basics/app-anatomy.md#constructor-function)). - -At the bare minimum, a `RegisterRoutes()` function should use the SDK client package `RegisterRoutes()` function to be able to route RPC calls, and instruct the application Module Manager to call `RegisterRESTRoutes()` for all of its modules. This is done in the `main.go` file of the CLI (typically located in `./cmd/appcli/main.go`). - -```go -func registerRoutes(rs *rest.RestServer) { - rpc.RegisterRoutes(rs.ClientCtx, rs.Mux) - app.ModuleBasics.RegisterRESTRoutes(rs.ClientCtx, rs.Mux) -} -``` - -This function is specific to the application and passed in to the `ServeCommand`, which should be added to the `rootCmd` as such: - -```go -rootCmd.AddCommand(rest.ServeCommand(cdc, registerRoutes)) -``` - -## Cross-Origin Resource Sharing (CORS) - -[CORS policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) are not enabled by default to help with security. If you would like to use the rest-server in a public environment we recommend you provide a reverse proxy, this can be done with [nginx](https://www.nginx.com/). For testing and development purposes there is an `unsafe_cors` flag that can be passed to the cmd to enable accepting cors from everyone. - -```sh -gaiacli rest-server --chain-id=test \ - --laddr=tcp://localhost:1317 \ - --node tcp://localhost:26657 \ - --unsafe-cors -``` diff --git a/docs/migrations/chain-upgrade-guide-040.md b/docs/migrations/chain-upgrade-guide-040.md index ca8f0b94d514..bce84966452a 100644 --- a/docs/migrations/chain-upgrade-guide-040.md +++ b/docs/migrations/chain-upgrade-guide-040.md @@ -87,7 +87,7 @@ software and restore to their latest snapshot before restarting their nodes. 1) If your chain is using IBC, make sure to add IBC initial genesis state to the genesis file. You can use the following command to add IBC initial genesis state to the genesis file. ```shell - cat new_040_genesis.json | jq '.app_state |= . + {"ibc":{"client_genesis":{"clients":[],"clients_consensus":[],"create_localhost":false},"connection_genesis":{"connections":[],"client_connection_paths":[]},"channel_genesis":{"channels":[],"acknowledgements":[],"commitments":[],"receipts":[],"send_sequences":[],"recv_sequences":[],"ack_sequences":[]}},"transfer":{"port_id":"transfer","denom_traces":[],"params":{"send_enabled":false,"receive_enabled":false}},"capability":{"index":"1","owners":[]}}' > new_040_genesis.json + cat new_v040_genesis.json | jq '.app_state |= . + {"ibc":{"client_genesis":{"clients":[],"clients_consensus":[],"create_localhost":false},"connection_genesis":{"connections":[],"client_connection_paths":[]},"channel_genesis":{"channels":[],"acknowledgements":[],"commitments":[],"receipts":[],"send_sequences":[],"recv_sequences":[],"ack_sequences":[]}},"transfer":{"port_id":"transfer","denom_traces":[],"params":{"send_enabled":false,"receive_enabled":false}},"capability":{"index":"1","owners":[]}}' > tmp_genesis.json && mv tmp_genesis.json new_v040_genesis.json ``` **Note:** This would add IBC state with IBC's `send_enabled: false` and `receive_enabled: false`. Make sure to update them to `true` in the above command if are planning to enable IBC transactions with chain upgrade. Otherwise you can do it via a governance proposal. diff --git a/docs/migrations/rest.md b/docs/migrations/rest.md index 3af7e8ee2c58..b1675543eb05 100644 --- a/docs/migrations/rest.md +++ b/docs/migrations/rest.md @@ -32,7 +32,7 @@ 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 _gGPC-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). diff --git a/docs/package-lock.json b/docs/package-lock.json index 0c83b5840d4b..d6bc2513349f 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -120,9 +120,9 @@ } }, "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "requires": { "@babel/highlight": "^7.10.4" } @@ -188,11 +188,11 @@ } }, "@babel/generator": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", - "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", + "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", "requires": { - "@babel/types": "^7.12.10", + "@babel/types": "^7.12.11", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -279,13 +279,13 @@ } }, "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", + "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-get-function-arity": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/types": "^7.12.11" } }, "@babel/helper-get-function-arity": { @@ -360,14 +360,14 @@ } }, "@babel/helper-replace-supers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", - "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", + "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" + "@babel/helper-member-expression-to-functions": "^7.12.7", + "@babel/helper-optimise-call-expression": "^7.12.10", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.11" } }, "@babel/helper-simple-access": { @@ -387,22 +387,22 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", + "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", "requires": { - "@babel/types": "^7.11.0" + "@babel/types": "^7.12.11" } }, "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==" }, "@babel/helper-validator-option": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz", - "integrity": "sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==" + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz", + "integrity": "sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw==" }, "@babel/helper-wrap-function": { "version": "7.12.3", @@ -436,14 +436,14 @@ } }, "@babel/parser": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", - "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==" + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", + "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==" }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz", - "integrity": "sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A==", + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.12.tgz", + "integrity": "sha512-nrz9y0a4xmUrRq51bYkWJIO5SBZyG2ys2qinHsN0zHDHVsUaModrkpyWWWXfGqYQmOL3x9sQIcTNN/pBGpo09A==", "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-remap-async-to-generator": "^7.12.1", @@ -460,9 +460,9 @@ } }, "@babel/plugin-proposal-decorators": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.1.tgz", - "integrity": "sha512-knNIuusychgYN8fGJHONL0RbFxLGawhXOJNLBk75TniTsZZeA+wdkDuv6wp4lGwzQEKjZi6/WYtnb3udNPmQmQ==", + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.12.tgz", + "integrity": "sha512-fhkE9lJYpw2mjHelBpM2zCbaA11aov2GJs7q4cFaXNrWx0H3bW58H9Esy2rdtYOghFBEYUDRIpvlgi+ZD+AvvQ==", "requires": { "@babel/helper-create-class-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", @@ -709,9 +709,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz", - "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==", + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz", + "integrity": "sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ==", "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -981,15 +981,15 @@ } }, "@babel/preset-env": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.10.tgz", - "integrity": "sha512-Gz9hnBT/tGeTE2DBNDkD7BiWRELZt+8lSysHuDwmYXUIvtwZl0zI+D6mZgXZX0u8YBlLS4tmai9ONNY9tjRgRA==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.11.tgz", + "integrity": "sha512-j8Tb+KKIXKYlDBQyIOy4BLxzv1NUOwlHfZ74rvW+Z0Gp4/cI2IMDPBWAgWceGcE7aep9oL/0K9mlzlMGxA8yNw==", "requires": { "@babel/compat-data": "^7.12.7", "@babel/helper-compilation-targets": "^7.12.5", "@babel/helper-module-imports": "^7.12.5", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-option": "^7.12.1", + "@babel/helper-validator-option": "^7.12.11", "@babel/plugin-proposal-async-generator-functions": "^7.12.1", "@babel/plugin-proposal-class-properties": "^7.12.1", "@babel/plugin-proposal-dynamic-import": "^7.12.1", @@ -1018,7 +1018,7 @@ "@babel/plugin-transform-arrow-functions": "^7.12.1", "@babel/plugin-transform-async-to-generator": "^7.12.1", "@babel/plugin-transform-block-scoped-functions": "^7.12.1", - "@babel/plugin-transform-block-scoping": "^7.12.1", + "@babel/plugin-transform-block-scoping": "^7.12.11", "@babel/plugin-transform-classes": "^7.12.1", "@babel/plugin-transform-computed-properties": "^7.12.1", "@babel/plugin-transform-destructuring": "^7.12.1", @@ -1048,7 +1048,7 @@ "@babel/plugin-transform-unicode-escapes": "^7.12.1", "@babel/plugin-transform-unicode-regex": "^7.12.1", "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.12.10", + "@babel/types": "^7.12.11", "core-js-compat": "^3.8.0", "semver": "^5.5.0" }, @@ -1098,16 +1098,16 @@ } }, "@babel/traverse": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", - "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.10", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.10", - "@babel/types": "^7.12.10", + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", + "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "requires": { + "@babel/code-frame": "^7.12.11", + "@babel/generator": "^7.12.11", + "@babel/helper-function-name": "^7.12.11", + "@babel/helper-split-export-declaration": "^7.12.11", + "@babel/parser": "^7.12.11", + "@babel/types": "^7.12.12", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" @@ -1129,11 +1129,11 @@ } }, "@babel/types": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", - "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", + "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" }, @@ -1251,9 +1251,9 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, "@types/node": { - "version": "14.14.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.12.tgz", - "integrity": "sha512-ASH8OPHMNlkdjrEdmoILmzFfsJICvhBsFfAum4aKZ/9U4B6M6tTmTPh+f3ttWdD74CEGV5XvXWkbyfSdXaTd7g==" + "version": "14.14.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", + "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==" }, "@types/q": { "version": "1.5.4", @@ -1266,21 +1266,21 @@ "integrity": "sha512-QOi5OW45e2R20VygMSNhyQHvpdUwQZqGPc748JLGCYEy+yp8fNFNdbNIGAgZmi9e+2JHPd6i6idRuqivyicIkA==" }, "@vue/babel-helper-vue-transform-on": { - "version": "1.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.0-rc.2.tgz", - "integrity": "sha512-1+7CwjQ0Kasml6rHoNQUmbISwqLNNfFVBUcZl6QBremUl296ZmLrVQPqJP5pyAAWjZke5bpI1hlj+LVVuT7Jcg==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.2.tgz", + "integrity": "sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==" }, "@vue/babel-plugin-jsx": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.0.0-rc.4.tgz", - "integrity": "sha512-ifzYc0jfLqiQebfqzKrJGfmQFE1lIgFlE9Ive8hQMJS/GC9Y+mNtHpqmWyqljbFGsqmsxmMRNFdAUgz0HZN1rg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.0.2.tgz", + "integrity": "sha512-1uZlQCLCeuqJgDYLCmg3qfsvTVtOQiXh278ES4bvPTYYbv2Bi/rElLETK6AdjI9xxzyTUf5n1QEiH8Xxz0eZrg==", "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/plugin-syntax-jsx": "^7.0.0", "@babel/template": "^7.0.0", "@babel/traverse": "^7.0.0", "@babel/types": "^7.0.0", - "@vue/babel-helper-vue-transform-on": "^1.0.0-rc.2", + "@vue/babel-helper-vue-transform-on": "^1.0.2", "camelcase": "^6.0.0", "html-tags": "^3.1.0", "svg-tags": "^1.0.0" @@ -1314,9 +1314,9 @@ } }, "@vue/babel-preset-app": { - "version": "4.5.9", - "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-4.5.9.tgz", - "integrity": "sha512-d2H4hFnJsGnZtJAAZIbo1dmQJ2SI1MYix1Tc9/etlnJtCDPRHeCNodCSeuLgDwnoAyT3unzyHmTtaO56KRDuOQ==", + "version": "4.5.10", + "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-4.5.10.tgz", + "integrity": "sha512-IHOyfWqgNNM863NjGmX6s2MIF+ILkJZardHcr7bGrxu5mNBT+p0GOGRQU4sN/adDkEQ9cyAxokm/GIeeoRrnOg==", "requires": { "@babel/core": "^7.11.0", "@babel/helper-compilation-targets": "^7.9.6", @@ -1337,9 +1337,9 @@ }, "dependencies": { "core-js": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.1.tgz", - "integrity": "sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==" + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.2.tgz", + "integrity": "sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A==" } } }, @@ -1465,17 +1465,17 @@ } }, "@vuepress/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@vuepress/core/-/core-1.7.1.tgz", - "integrity": "sha512-M5sxZq30Ke1vXa4ZZjk6185fwtpiJOqzXNnzcIe0GxtvtaF8Yij6b+KqQKlUJnnUXm+CKxiLCr8PTzDY26N7yw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@vuepress/core/-/core-1.8.0.tgz", + "integrity": "sha512-DrHx3gXa5rUDdvjcUHhmZg1DccMwc3kiYpgtbKCuJpHksz9eAVuOxbcy/b6TGRbbY4Q5EA2Fs3kI9hvPjYdCrQ==", "requires": { "@babel/core": "^7.8.4", "@vue/babel-preset-app": "^4.1.2", - "@vuepress/markdown": "1.7.1", - "@vuepress/markdown-loader": "1.7.1", - "@vuepress/plugin-last-updated": "1.7.1", - "@vuepress/plugin-register-components": "1.7.1", - "@vuepress/shared-utils": "1.7.1", + "@vuepress/markdown": "1.8.0", + "@vuepress/markdown-loader": "1.8.0", + "@vuepress/plugin-last-updated": "1.8.0", + "@vuepress/plugin-register-components": "1.8.0", + "@vuepress/shared-utils": "1.8.0", "autoprefixer": "^9.5.1", "babel-loader": "^8.0.4", "cache-loader": "^3.0.0", @@ -1510,18 +1510,18 @@ }, "dependencies": { "core-js": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.1.tgz", - "integrity": "sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==" + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.2.tgz", + "integrity": "sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A==" } } }, "@vuepress/markdown": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@vuepress/markdown/-/markdown-1.7.1.tgz", - "integrity": "sha512-Ava9vJECHG1+RC53ut1dXSze35IH5tc3qesC06Ny37WS93iDSQy09j8y+a0Lugy12j1369+QQeRFWa40tdHczA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@vuepress/markdown/-/markdown-1.8.0.tgz", + "integrity": "sha512-f2yhoIHTD6gaPeLLidkxuytKGQCT6Gopm0fpyKZwfFZaWWz5+RPPGevq5UTmTb+5vvO2Li44HJc1EV7QONOw9Q==", "requires": { - "@vuepress/shared-utils": "1.7.1", + "@vuepress/shared-utils": "1.8.0", "markdown-it": "^8.4.1", "markdown-it-anchor": "^5.0.2", "markdown-it-chain": "^1.3.0", @@ -1550,19 +1550,19 @@ } }, "@vuepress/markdown-loader": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@vuepress/markdown-loader/-/markdown-loader-1.7.1.tgz", - "integrity": "sha512-GM1F/tRhP9qZydTC89FXJPlLH+BmZijMKom5BYLAMEXsU20A9kABTRoatPjOUbZuKT+gn03JgG97qVd8xa/ETw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@vuepress/markdown-loader/-/markdown-loader-1.8.0.tgz", + "integrity": "sha512-ykYTNe4mC/0CxyEfyM9+oYJqFvOMZWw5qiNZVfg2t+Ip8nVR2pFhQ6fJe07Pbtc59eT4awKSEd8/kcjhTpfeZA==", "requires": { - "@vuepress/markdown": "1.7.1", + "@vuepress/markdown": "1.8.0", "loader-utils": "^1.1.0", "lru-cache": "^5.1.1" } }, "@vuepress/plugin-active-header-links": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-active-header-links/-/plugin-active-header-links-1.7.1.tgz", - "integrity": "sha512-Wgf/oB9oPZLnYoLjQ/xbQc4Qa3RU5tXAo2dB4Xl/7bUL6SqBxO866kX3wPxKdSOIL58tq8iH9XbUe3Sxi8/ISQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-active-header-links/-/plugin-active-header-links-1.8.0.tgz", + "integrity": "sha512-0SqdkJLJSQqhPTgGccu/ev5zRCfeKKMkyPnUMJYsQe4zGhSosmwLcfB7LDo/VLqLhRipipzBnONESr12OgI4SQ==", "requires": { "lodash.debounce": "^4.0.8" } @@ -1573,38 +1573,38 @@ "integrity": "sha512-27fQzRMsqGYpMf+ruyhsdfLv/n6z6b6LutFLE/pH66Itlh6ox9ew31x0pqYBbWIC/a4lBfXYUwFvi+DEvlb1EQ==" }, "@vuepress/plugin-last-updated": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-last-updated/-/plugin-last-updated-1.7.1.tgz", - "integrity": "sha512-VW5jhBuO0WRHDsBmFsKC6QtEyBLCgyhuH9nQ65aairCn3tdoJPz0uQ4g3lr/boVbgsPexO677Sn3dRPgYqnMug==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-last-updated/-/plugin-last-updated-1.8.0.tgz", + "integrity": "sha512-fBwtlffAabpTTalUMPSaJy/5fp3WpIA1FdWOfFcQ12X6179tupZB8fnueNsVJGBvGcdw8fbyzh5C9yKAq9lR/w==", "requires": { "cross-spawn": "^6.0.5" } }, "@vuepress/plugin-nprogress": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-nprogress/-/plugin-nprogress-1.7.1.tgz", - "integrity": "sha512-KtqfI3RitbsEbm22EhbooTvhjfMf6zttKlbND7LcyJwP3MEPVYyzQJuET03hk9z4SgCfNV2r/W3sYyejzzTMog==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-nprogress/-/plugin-nprogress-1.8.0.tgz", + "integrity": "sha512-JmjeJKKWhbF/8z+EnY8BCWHoHKhUWfqXjXOfV+xifITl6BJlf53I/jLFpX7Sij8Whe+SKjH7kNvc+hioUdwJQw==", "requires": { "nprogress": "^0.2.0" } }, "@vuepress/plugin-register-components": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-register-components/-/plugin-register-components-1.7.1.tgz", - "integrity": "sha512-MlFdH6l3rTCJlGMvyssXVG998cq5LSMzxCuQLYcRdtHQT4HbikIcV4HSPGarWInD1mP12+qX/PvKUawGwp1eVg==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-register-components/-/plugin-register-components-1.8.0.tgz", + "integrity": "sha512-ztafaAxn7XFS4F8z51d+QQB6DNAUG54wBSdfKydfnHbAYgtxoALzPlJW7FKQdxvZKxqGrJa9e4rkoZsat13KRQ==", "requires": { - "@vuepress/shared-utils": "1.7.1" + "@vuepress/shared-utils": "1.8.0" } }, "@vuepress/plugin-search": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-search/-/plugin-search-1.7.1.tgz", - "integrity": "sha512-OmiGM5eYg9c+uC50b6/cSxAhqxfD7AIui6JEztFGeECrlP33RLHmteXK9YBBZjp5wTNmoYs+NXI/cWggYUPW8Q==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-search/-/plugin-search-1.8.0.tgz", + "integrity": "sha512-LlOCPg7JJ3I9WEpiBU8vCEFp6I5sa3OBu6SFHk+uK9iyKHwJYdRs4VK9x+igG40Rt/OIDRDo3c9SbLX/E/kqeA==" }, "@vuepress/shared-utils": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@vuepress/shared-utils/-/shared-utils-1.7.1.tgz", - "integrity": "sha512-ydB2ZKsFZE6hFRb9FWqzZksxAPIMJjtBawk50RP6F+YX5HbID/HlyYYZM9aDSbk6RTkjgB5UzJjggA2xM8POlw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@vuepress/shared-utils/-/shared-utils-1.8.0.tgz", + "integrity": "sha512-CVNMiYBntQyb4CuyS4KzmglDqsuBpj8V4QMzL9gCSoMv0VmwKx05fZedu+r0G5OcxYN4qjnu99yl9G6zWhOU9w==", "requires": { "chalk": "^2.3.2", "escape-html": "^1.0.3", @@ -1618,13 +1618,13 @@ } }, "@vuepress/theme-default": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@vuepress/theme-default/-/theme-default-1.7.1.tgz", - "integrity": "sha512-a9HeTrlcWQj3ONHiABmlN2z9TyIxKfQtLsA8AL+WgjN3PikhFuZFIJGzfr+NLt67Y9oiI+S9ZfiaVyvWM+7bWQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@vuepress/theme-default/-/theme-default-1.8.0.tgz", + "integrity": "sha512-CueCaANfICFbLnEL78YxSjgCHXL7mkgQI/cfyEHBgxlr247cYJQ+9IFDQIS0fJNuzHdLRy9UFUvVrCu6go8PUg==", "requires": { - "@vuepress/plugin-active-header-links": "1.7.1", - "@vuepress/plugin-nprogress": "1.7.1", - "@vuepress/plugin-search": "1.7.1", + "@vuepress/plugin-active-header-links": "1.8.0", + "@vuepress/plugin-nprogress": "1.8.0", + "@vuepress/plugin-search": "1.8.0", "docsearch.js": "^2.5.2", "lodash": "^4.17.15", "stylus": "^0.54.8", @@ -2141,17 +2141,17 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "axios": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz", - "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", "requires": { "follow-redirects": "^1.10.0" }, "dependencies": { "follow-redirects": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", - "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", + "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==" } } }, @@ -2542,6 +2542,23 @@ "parse-asn1": "^5.1.5", "readable-stream": "^3.6.0", "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "browserify-zlib": { @@ -2553,15 +2570,15 @@ } }, "browserslist": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.0.tgz", - "integrity": "sha512-/j6k8R0p3nxOC6kx5JGAxsnhc9ixaWJfYc+TNTzxg6+ARaESAvQGV7h0uNOB4t+pLQJZWzcrMxXOxjgsCj3dqQ==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.1.tgz", + "integrity": "sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA==", "requires": { - "caniuse-lite": "^1.0.30001165", + "caniuse-lite": "^1.0.30001173", "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.621", + "electron-to-chromium": "^1.3.634", "escalade": "^3.1.1", - "node-releases": "^1.1.67" + "node-releases": "^1.1.69" } }, "buffer": { @@ -2605,9 +2622,9 @@ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, "cac": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.6.1.tgz", - "integrity": "sha512-uhki4T3Ax68hw7Dufi0bATVAF8ayBSwOKUEJHjObPrUN4tlQ8Lf7oljpTje/mArLxYN0D743c2zJt4C1bVTCqg==" + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.1.tgz", + "integrity": "sha512-LfGt47+ugCY65W4yUEyxnZKd/tJSBJD/gUAxQGiQjH7yqdhbaX2XN0Rli4+0W0DJiDONmYeh0TlJxMtXGZspIg==" }, "cacache": { "version": "12.0.4", @@ -2787,12 +2804,12 @@ } }, "call-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", - "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "requires": { "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.0" + "get-intrinsic": "^1.0.2" } }, "call-me-maybe": { @@ -2847,9 +2864,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001165", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001165.tgz", - "integrity": "sha512-8cEsSMwXfx7lWSUMA2s08z9dIgsnR5NAqjXP23stdsU3AUWkCr/rr4s4OFtHXn5XXr6+7kam3QFVoYyXNPdJPA==" + "version": "1.0.30001178", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001178.tgz", + "integrity": "sha512-VtdZLC0vsXykKni8Uztx45xynytOi71Ufx9T8kHptSw9AL4dpqailUJJHavttuzUe1KYuBYtChiWv+BAb7mPmQ==" }, "caseless": { "version": "0.12.0", @@ -2884,23 +2901,29 @@ } }, "cheerio": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", - "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==", + "version": "1.0.0-rc.5", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.5.tgz", + "integrity": "sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw==", "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.1", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash": "^4.15.0", - "parse5": "^3.0.1" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - } + "cheerio-select-tmp": "^0.1.0", + "dom-serializer": "~1.2.0", + "domhandler": "^4.0.0", + "entities": "~2.1.0", + "htmlparser2": "^6.0.0", + "parse5": "^6.0.0", + "parse5-htmlparser2-tree-adapter": "^6.0.0" + } + }, + "cheerio-select-tmp": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/cheerio-select-tmp/-/cheerio-select-tmp-0.1.1.tgz", + "integrity": "sha512-YYs5JvbpU19VYJyj+F7oYrIE2BOll1/hRU7rEy/5+v9BzkSo3bK81iAeeQEMI92vRIxz677m72UmJUiVwwgjfQ==", + "requires": { + "css-select": "^3.1.2", + "css-what": "^4.0.0", + "domelementtype": "^2.1.0", + "domhandler": "^4.0.0", + "domutils": "^2.4.4" } }, "chokidar": { @@ -3124,11 +3147,6 @@ "requires": { "ms": "2.0.0" } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -3146,35 +3164,6 @@ "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "configstore": { @@ -3235,13 +3224,6 @@ "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", "requires": { "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } } }, "content-type": { @@ -3255,13 +3237,6 @@ "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "requires": { "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } } }, "cookie": { @@ -3431,11 +3406,11 @@ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" }, "core-js-compat": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.1.tgz", - "integrity": "sha512-a16TLmy9NVD1rkjUGbwuyWkiDoN0FDpAwrfLONvHFQx0D9k7J9y0srwMT8QP/Z6HE3MIFaVynEeYwZwPX1o5RQ==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.2.tgz", + "integrity": "sha512-LO8uL9lOIyRRrQmZxHZFl1RV+ZbcsAkFWTktn5SmH40WgLtSNYN4m4W2v9ONT147PxBY/XrRhrWq8TlvObyUjQ==", "requires": { - "browserslist": "^4.15.0", + "browserslist": "^4.16.0", "semver": "7.0.0" }, "dependencies": { @@ -3619,14 +3594,15 @@ } }, "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz", + "integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==", "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" + "boolbase": "^1.0.0", + "css-what": "^4.0.0", + "domhandler": "^4.0.0", + "domutils": "^2.4.3", + "nth-check": "^2.0.0" } }, "css-select-base-adapter": { @@ -3644,9 +3620,9 @@ } }, "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz", + "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==" }, "cssesc": { "version": "3.0.0", @@ -4066,19 +4042,13 @@ } }, "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz", + "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==", "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - } + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" } }, "dom-walk": { @@ -4092,25 +4062,26 @@ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" }, "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==" }, "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", + "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", "requires": { - "domelementtype": "1" + "domelementtype": "^2.1.0" } }, "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz", + "integrity": "sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==", "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "^1.0.1", + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0" } }, "dot-prop": { @@ -4135,35 +4106,6 @@ "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "ecc-jsbn": { @@ -4181,9 +4123,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.621", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.621.tgz", - "integrity": "sha512-FeIuBzArONbAmKmZIsZIFGu/Gc9AVGlVeVbhCq+G2YIl6QkT0TDn2HKN/FMf1btXEB9kEmIuQf3/lBTVAbmFOg==" + "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==" }, "elliptic": { "version": "6.5.3", @@ -4230,9 +4172,9 @@ } }, "enhanced-resolve": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", - "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", "requires": { "graceful-fs": "^4.1.2", "memory-fs": "^0.5.0", @@ -4247,33 +4189,6 @@ "errno": "^0.1.3", "readable-stream": "^2.0.1" } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } } } }, @@ -4297,9 +4212,9 @@ "integrity": "sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA==" }, "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", "requires": { "prr": "~1.0.1" } @@ -4313,22 +4228,24 @@ } }, "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "version": "1.18.0-next.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", + "integrity": "sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==", "requires": { + "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2", "has": "^1.0.3", "has-symbols": "^1.0.1", "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", + "is-negative-zero": "^2.0.1", "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", + "object-inspect": "^1.9.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.3", + "string.prototype.trimstart": "^1.0.3" } }, "es-to-primitive": { @@ -4537,11 +4454,6 @@ "requires": { "ms": "2.0.0" } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -4638,9 +4550,9 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", "requires": { "websocket-driver": ">=0.5.1" } @@ -4746,35 +4658,6 @@ "requires": { "inherits": "^2.0.3", "readable-stream": "^2.3.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "follow-redirects": { @@ -4835,35 +4718,6 @@ "requires": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "fs-extra": { @@ -4885,35 +4739,6 @@ "iferr": "^0.1.5", "imurmurhash": "^0.1.4", "readable-stream": "1 || 2" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "fs.realpath": { @@ -4952,9 +4777,9 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-intrinsic": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", - "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", + "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -5029,11 +4854,11 @@ } }, "global-dirs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", - "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", + "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", "requires": { - "ini": "^1.3.5" + "ini": "1.3.7" } }, "globals": { @@ -5186,6 +5011,23 @@ "inherits": "^2.0.4", "readable-stream": "^3.6.0", "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "hash-sum": { @@ -5252,35 +5094,6 @@ "obuf": "^1.0.0", "readable-stream": "^2.0.1", "wbuf": "^1.1.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "hsl-regex": { @@ -5299,9 +5112,9 @@ "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==" }, "html-entities": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", - "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==" }, "html-minifier": { "version": "3.5.21", @@ -5341,23 +5154,14 @@ "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==" }, "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - } + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.0.0.tgz", + "integrity": "sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.4.4", + "entities": "^2.0.0" } }, "http-cache-semantics": { @@ -5389,6 +5193,11 @@ } } }, + "http-parser-js": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" + }, "http-proxy": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", @@ -6255,9 +6064,9 @@ } }, "markdown-it": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.0.3.tgz", - "integrity": "sha512-M57RsMv+QQmJHz1yCu0gTJRMx/LlxRPtrrw+2kb/CpDVK/graCmWO0qfNnz/SE1FCNdyq3pkMMZ+itTnyT/YGA==", + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.0.4.tgz", + "integrity": "sha512-34RwOXZT8kyuOJy25oJNJoulO8L0bTHYWXcdZBYZqFnjIy3NgjeoM3FmPXIOFQ26/lSHYMr8oc62B6adxXcb3Q==", "requires": { "argparse": "^2.0.1", "entities": "~2.1.0", @@ -6357,35 +6166,6 @@ "requires": { "errno": "^0.1.3", "readable-stream": "^2.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "merge-descriptors": { @@ -6467,21 +6247,21 @@ } }, "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.0.tgz", + "integrity": "sha512-ft3WayFSFUVBuJj7BMLKAQcSlItKtfjsKDDsii3rqFDAZ7t11zRe8ASw/GlmivGwVUYtwkQrxiGGpL6gFvB0ag==" }, "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" }, "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", "requires": { - "mime-db": "1.44.0" + "mime-db": "1.45.0" } }, "mimic-response": { @@ -6731,42 +6511,13 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, "node-releases": { - "version": "1.1.67", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.67.tgz", - "integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==" + "version": "1.1.69", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.69.tgz", + "integrity": "sha512-DGIjo79VDEyAnRlfSqYTsy+yoHd2IOjJiKUozD2MV2D85Vso6Bug56mb9tT/fY5Urt0iqk01H7x+llAruDR2zA==" }, "nopt": { "version": "1.0.10", @@ -6810,11 +6561,11 @@ "integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E=" }, "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", + "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", "requires": { - "boolbase": "~1.0.0" + "boolbase": "^1.0.0" } }, "num2fraction": { @@ -7056,35 +6807,6 @@ "cyclist": "^1.0.1", "inherits": "^2.0.3", "readable-stream": "^2.1.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "param-case": { @@ -7117,11 +6839,16 @@ } }, "parse5": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", - "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "requires": { - "@types/node": "*" + "parse5": "^6.0.1" } }, "parseurl": { @@ -7843,9 +7570,9 @@ "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" }, "prismjs": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.22.0.tgz", - "integrity": "sha512-lLJ/Wt9yy0AiSYBf212kK3mM5L8ycwlyTlSxHBAneXLR0nzFMlZ5y7riFPF3E33zXOF2IH95xdY5jIyZbM9z/w==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", + "integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==", "requires": { "clipboard": "^2.0.0" } @@ -8169,13 +7896,17 @@ } }, "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "readdirp": { @@ -8186,35 +7917,6 @@ "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", "readable-stream": "^2.0.2" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "reduce": { @@ -8280,32 +7982,12 @@ } }, "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "regexpu-core": { @@ -8343,9 +8025,9 @@ "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" }, "regjsparser": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", - "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.6.tgz", + "integrity": "sha512-jjyuCp+IEMIm3N1H1LLTJW1EISEJV9+5oHdEyrt43Pg9cDSb6rrLZei2cVWpl0xTjmmlpec/lEQGYgM7xfpGCQ==", "requires": { "jsesc": "~0.5.0" }, @@ -8368,15 +8050,109 @@ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" }, "renderkid": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.4.tgz", - "integrity": "sha512-K2eXrSOJdq+HuKzlcjOlGoOarUu5SDguDEhE7+Ah4zuOWL40j8A/oHvLlLob9PSTNvVnBd+/q0Er1QfpEuem5g==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.5.tgz", + "integrity": "sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ==", "requires": { - "css-select": "^1.1.0", + "css-select": "^2.0.2", "dom-converter": "^0.2", - "htmlparser2": "^3.3.0", + "htmlparser2": "^3.10.1", "lodash": "^4.17.20", "strip-ansi": "^3.0.0" + }, + "dependencies": { + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==" + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + } + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "repeat-element": { @@ -8527,9 +8303,9 @@ } }, "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex": { "version": "1.1.0", @@ -8903,26 +8679,26 @@ } }, "sockjs": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", - "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==", + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", + "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", "requires": { - "faye-websocket": "^0.10.0", + "faye-websocket": "^0.11.3", "uuid": "^3.4.0", - "websocket-driver": "0.6.5" + "websocket-driver": "^0.7.4" } }, "sockjs-client": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", - "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.0.tgz", + "integrity": "sha512-8Dt3BDi4FYNrCFGTL/HtwVzkARrENdwOUf1ZoW/9p3M8lZdFT35jVdrHza+qgxuG9H3/shR4cuX/X9umUrjP8Q==", "requires": { - "debug": "^3.2.5", + "debug": "^3.2.6", "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" + "faye-websocket": "^0.11.3", + "inherits": "^2.0.4", + "json3": "^3.3.3", + "url-parse": "^1.4.7" }, "dependencies": { "debug": { @@ -8933,14 +8709,6 @@ "ms": "^2.1.1" } }, - "faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", - "requires": { - "websocket-driver": ">=0.5.1" - } - }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9044,6 +8812,16 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } } } }, @@ -9162,35 +8940,6 @@ "requires": { "inherits": "~2.0.1", "readable-stream": "^2.0.2" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "stream-each": { @@ -9212,35 +8961,6 @@ "readable-stream": "^2.3.6", "to-arraybuffer": "^1.0.0", "xtend": "^4.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "stream-shift": { @@ -9297,11 +9017,11 @@ } }, "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "~5.2.0" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { @@ -9430,6 +9150,27 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==" + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, "domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", @@ -9446,6 +9187,14 @@ "requires": { "minimist": "^1.2.5" } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } } } }, @@ -9583,35 +9332,6 @@ "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "thunky": { @@ -10031,9 +9751,9 @@ "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" }, "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "requires": { "punycode": "^2.1.0" } @@ -10215,9 +9935,9 @@ "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==" }, "vue-loader": { - "version": "15.9.5", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.5.tgz", - "integrity": "sha512-oeMOs2b5o5gRqkxfds10bCx6JeXYTwivRgbb8hzOrcThD2z1+GqEKE3EX9A2SGbsYDf4rXwRg6D5n1w0jO5SwA==", + "version": "15.9.6", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.6.tgz", + "integrity": "sha512-j0cqiLzwbeImIC6nVIby2o/ABAWhlppyL/m5oJ67R5MloP0hj/DtFgb0Zmq3J9CG7AJ+AXIvHVnJAPBvrLyuDg==", "requires": { "@vue/component-compiler-utils": "^3.1.0", "hash-sum": "^1.0.2", @@ -10307,12 +10027,12 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==" }, "vuepress": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/vuepress/-/vuepress-1.7.1.tgz", - "integrity": "sha512-AdA3do1L4DNzeF8sMTE+cSUJ5hR/6f3YujU8DVowi/vFOg/SX2lJF8urvDkZUSXzaAT6aSgkI9L+B6D+i7SJjA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/vuepress/-/vuepress-1.8.0.tgz", + "integrity": "sha512-YvNitvoEc+JSJRv1W+IoRnvOTFyTWyUMuGuF2kTIbiSwIHb1hNinc3lqNSeBQJy7IBqyEzK5fnTq1mlynh4gwA==", "requires": { - "@vuepress/core": "1.7.1", - "@vuepress/theme-default": "1.7.1", + "@vuepress/core": "1.8.0", + "@vuepress/theme-default": "1.8.0", "cac": "^6.5.6", "envinfo": "^7.2.0", "opencollective-postinstall": "^2.0.2", @@ -10401,9 +10121,9 @@ } }, "vuepress-theme-cosmos": { - "version": "1.0.179", - "resolved": "https://registry.npmjs.org/vuepress-theme-cosmos/-/vuepress-theme-cosmos-1.0.179.tgz", - "integrity": "sha512-BJw/SGn7AmvaSU5Rw74BIRuAkmVZMuf1Gd1++7vpPudgNHEmWSKOHULpIdtkzn1WPXF8sgf4hZQ7+PhESk7JUQ==", + "version": "1.0.180", + "resolved": "https://registry.npmjs.org/vuepress-theme-cosmos/-/vuepress-theme-cosmos-1.0.180.tgz", + "integrity": "sha512-NooA8qCNq/e4fjchOkKtLTaBkPHX1PAfMezP+1ttwezjnHeA1TIccjlqDbq8U7Ath4BXzmcDL5KMX3RtuDARDg==", "requires": { "@cosmos-ui/vue": "^0.35.0", "@vuepress/plugin-google-analytics": "1.7.1", @@ -10452,9 +10172,9 @@ } }, "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "optional": true }, "braces": { @@ -10467,14 +10187,14 @@ } }, "chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "optional": true, "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.2", + "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -10492,9 +10212,9 @@ } }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", + "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", "optional": true }, "glob-parent": { @@ -10564,9 +10284,9 @@ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" }, "webpack": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz", - "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", + "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", "requires": { "@webassemblyjs/ast": "1.9.0", "@webassemblyjs/helper-module-context": "1.9.0", @@ -10576,7 +10296,7 @@ "ajv": "^6.10.2", "ajv-keywords": "^3.4.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.3.0", + "enhanced-resolve": "^4.5.0", "eslint-scope": "^4.0.3", "json-parse-better-errors": "^1.0.2", "loader-runner": "^2.4.0", @@ -10635,9 +10355,9 @@ } }, "webpack-dev-middleware": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", - "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", "requires": { "memory-fs": "^0.4.1", "mime": "^2.4.4", @@ -10657,9 +10377,9 @@ } }, "webpack-dev-server": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", - "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==", + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz", + "integrity": "sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==", "requires": { "ansi-html": "0.0.7", "bonjour": "^3.5.0", @@ -10681,11 +10401,11 @@ "p-retry": "^3.0.1", "portfinder": "^1.0.26", "schema-utils": "^1.0.0", - "selfsigned": "^1.10.7", + "selfsigned": "^1.10.8", "semver": "^6.3.0", "serve-index": "^1.9.1", - "sockjs": "0.3.20", - "sockjs-client": "1.4.0", + "sockjs": "^0.3.21", + "sockjs-client": "^1.5.0", "spdy": "^4.0.2", "strip-ansi": "^3.0.1", "supports-color": "^6.1.0", @@ -10848,10 +10568,12 @@ } }, "websocket-driver": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", - "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, diff --git a/docs/package.json b/docs/package.json index e4427e8da2fc..4d296f2cc1f0 100644 --- a/docs/package.json +++ b/docs/package.json @@ -14,6 +14,6 @@ "author": "", "license": "ISC", "dependencies": { - "vuepress-theme-cosmos": "^1.0.179" + "vuepress-theme-cosmos": "^1.0.180" } } diff --git a/docs/run-node/README.md b/docs/run-node/README.md index 2ae01b8541e4..27ee9726508a 100644 --- a/docs/run-node/README.md +++ b/docs/run-node/README.md @@ -12,3 +12,5 @@ This folder contains documentation on how to run a node and interact with it. 1. [Running a Node](./run-node.md) 1. [Interacting with a Node](./interact-node.md) 1. [Generating, Signing and Broadcasting Transactions](./txs.md) +1. [Cosmos Upgrade Manager](./cosmovisor.md) +1. [Rosetta API](./rosetta.md) diff --git a/docs/using-the-sdk/cosmovisor.md b/docs/run-node/cosmovisor.md similarity index 93% rename from docs/using-the-sdk/cosmovisor.md rename to docs/run-node/cosmovisor.md index 56ee5f7fa7bb..99cb81e0cc45 100644 --- a/docs/using-the-sdk/cosmovisor.md +++ b/docs/run-node/cosmovisor.md @@ -1,10 +1,6 @@ # Cosmosvisor Quick Start -`cosmovisor` is a small process manager around Cosmos SDK binaries that uses the upgrade module to allow -for smooth and configurable management of upgrading binaries as a live chain is upgraded, and can be -used to simplify validator operations while doing upgrades or to make syncing a full node for genesis -simple. The `cosmovisor` program monitors the stdout of Cosmos SDK application's executable to look for -messages from the upgrade module indicating a pending or required upgrade and act appropriately. +`cosmovisor` is a small process manager around Cosmos SDK binaries that monitors the governance module via stdout to see if there's a chain upgrade proposal coming in. If it see a proposal that gets approved it can be run manually or automatically to download the new code, stop the node, run the migration script, replace the node binary, and start with the new genesis file. ## Installation diff --git a/docs/run-node/interact-node.md b/docs/run-node/interact-node.md index 60ce6c2ed591..bb208f7d8df1 100644 --- a/docs/run-node/interact-node.md +++ b/docs/run-node/interact-node.md @@ -16,7 +16,7 @@ There are multiple ways to interact with a node: using the CLI, using gRPC or us Now that your chain is running, it is time to try sending tokens from the first account you created to a second account. In a new terminal window, start by running the following query command: ```bash -simd query account $MY_VALIDATOR_ADDRESS --chain-id my-test-chain +simd query bank balances $MY_VALIDATOR_ADDRESS --chain-id my-test-chain ``` You should see the current balance of the account you created, equal to the original balance of `stake` you granted it minus the amount you delegated via the `gentx`. Now, create a second account: @@ -31,16 +31,16 @@ RECIPIENT=$(simd keys show recipient -a --keyring-backend test) The command above creates a local key-pair that is not yet registered on the chain. An account is created the first time it receives tokens from another account. Now, run the following command to send tokens to the `recipient` account: ```bash -simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain +simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000000stake --chain-id my-test-chain --keyring-backend test # Check that the recipient account did receive the tokens. -simd query account $RECIPIENT --chain-id my-test-chain +simd query bank balances $RECIPIENT --chain-id my-test-chain ``` Finally, delegate some of the stake tokens sent to the `recipient` account to the validator: ```bash -simd tx staking delegate $(simd keys show my_validator --bech val -a --keyring-backend test) 500stake --from recipient --chain-id my-test-chain +simd tx staking delegate $(simd keys show my_validator --bech val -a --keyring-backend test) 500stake --from recipient --chain-id my-test-chain --keyring-backend test # Query the total delegations to `validator`. simd query staking delegations-to $(simd keys show my_validator --bech val -a --keyring-backend test) --chain-id my-test-chain @@ -62,7 +62,7 @@ Since the code generation library largely depends on your own tech stack, we wil [grpcurl])https://github.com/fullstorydev/grpcurl is like `curl` but for gRPC. It is also available as a Go library, but we will use it only as a CLI command for debugging and testing purposes. Follow the instructions in the previous link to install it. -Assuming you have a local node running (either a localnet, or connected a live network), you should be able to run the following command to list the Protobuf services available (you can replace `localhost:9000` by the gRPC server endpoint of another node, which is configured under the `grpc.address` field inside `app.toml`): +Assuming you have a local node running (either a localnet, or connected a live network), you should be able to run the following command to list the Protobuf services available (you can replace `localhost:9000` by the gRPC server endpoint of another node, which is configured under the `grpc.address` field inside [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml)): ```bash grpcurl -plaintext localhost:9090 list @@ -139,7 +139,7 @@ func queryState() error { // Create a connection to the gRPC server. grpcConn := grpc.Dial( - "127.0.0.1:9090", // Or your gRPC server address. + "127.0.0.1:9090", // your gRPC server address. grpc.WithInsecure(), // The SDK doesn't support any transport security mechanism. ) defer grpcConn.Close() @@ -217,7 +217,7 @@ curl \ Make sure to replace `localhost:1317` with the REST endpoint of your node, configured under the `api.address` field. -The list of all available REST endpoints is available as a Swagger specification file, it can be viewed at `localhost:1317/swagger`. Make sure that the `api.swagger` field is set to true in your `app.toml` file. +The list of all available REST endpoints is available as a Swagger specification file, it can be viewed at `localhost:1317/swagger`. Make sure that the `api.swagger` field is set to true in your [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) file. ### Query for historical state using REST @@ -233,6 +233,10 @@ curl \ Assuming the state at that block has not yet been pruned by the node, this query should return a non-empty response. +### Cross-Origin Resource Sharing (CORS) + +[CORS policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) are not enabled by default to help with security. If you would like to use the rest-server in a public environment we recommend you provide a reverse proxy, this can be done with [nginx](https://www.nginx.com/). For testing and development purposes there is an `enabled-unsafe-cors` field inside [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml). + ## Next {hide} Sending transactions using gRPC and REST requires some additional steps: generating the transaction, signing it, and finally broadcasting it. Read about [generating and signing transactions](./txs.md). {hide} diff --git a/docs/run-node/keyring.md b/docs/run-node/keyring.md index e84a4d07181b..61636f27fd47 100644 --- a/docs/run-node/keyring.md +++ b/docs/run-node/keyring.md @@ -31,7 +31,7 @@ is a list of the most popular operating systems and their respective passwords m GNU/Linux distributions that use GNOME as default desktop environment typically come with [Seahorse](https://wiki.gnome.org/Apps/Seahorse). Users of KDE based distributions are commonly provided with [KDE Wallet Manager](https://userbase.kde.org/KDE_Wallet_Manager). -Whilst the former is in fact a `libsecret` convenient frontend, the former is a `kwallet` +Whilst the former is in fact a `libsecret` convenient frontend, the latter is a `kwallet` client. `os` is the default option since operating system's default credentials managers are diff --git a/docs/run-node/rosetta.md b/docs/run-node/rosetta.md new file mode 100644 index 000000000000..b10bc21e7996 --- /dev/null +++ b/docs/run-node/rosetta.md @@ -0,0 +1,83 @@ +# Rosetta + +Package rosetta implements the rosetta API for the current cosmos sdk release series. + +The client satisfies [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway) `Client` interface implementation. + +## Extension + +There are two ways in which you can customize and extend the implementation with your custom settings. + +### Message extension + +In order to make an `sdk.Msg` understandable by rosetta the only thing which is required is adding the methods to your message that satisfy the `rosetta.Msg` interface. +Examples on how to do so can be found in the staking types such as `MsgDelegate`, or in bank types such as `MsgSend`. + +### Client interface override + +In case more customization is required, it's possible to embed the Client type and override the methods which require customizations. + +Example: +```go +package custom_client +import ( + +"context" +"github.com/coinbase/rosetta-sdk-go/types" +"github.com/cosmos/cosmos-sdk/server/rosetta" +) + +// CustomClient embeds the standard cosmos client +// which means that it implements the cosmos-rosetta-gateway Client +// interface while at the same time allowing to customize certain methods +type CustomClient struct { + *rosetta.Client +} + +func (c *CustomClient) ConstructionPayload(_ context.Context, request *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error) { + // provide custom signature bytes + panic("implement me") +} +``` + +### Error extension + +Since rosetta requires to provide 'returned' errors to network options. In order to declare a new rosetta error, we use the `errors` package in cosmos-rosetta-gateway. + +Example: + +```go +package custom_errors +import crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors" + +var customErrRetriable = true +var CustomError = crgerrs.RegisterError(100, "custom message", customErrRetriable, "description") +``` + +Note: errors must be registered before cosmos-rosetta-gateway's `Server`.`Start` method is called. Otherwise the registration will be ignored. Errors with same code will be ignored too. + +## Integration in app.go + +To integrate rosetta as a command in your application, in app.go, in your root command simply use the `server.RosettaCommand` method. + +Example: + +```go +package app +import ( + +"github.com/cosmos/cosmos-sdk/server" +"github.com/spf13/cobra" +) + +func buildAppCommand(rootCmd *cobra.Command) { + // more app.go init stuff + // ... + // add rosetta command + rootCmd.AddCommand(server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler)) +} +``` + +A full implementation example can be found in `simapp` package. + +NOTE: when using a customized client, the command cannot be used as the constructors required **may** differ, so it's required to create a new one. We intend to provide a way to init a customized client without writing extra code in the future. \ No newline at end of file diff --git a/docs/run-node/run-node.md b/docs/run-node/run-node.md index 393172b9aa0c..d7aaf8f5822d 100644 --- a/docs/run-node/run-node.md +++ b/docs/run-node/run-node.md @@ -44,7 +44,7 @@ Before starting the chain, you need to populate the state with at least one acco Now that you have created a local account, go ahead and grant it some `stake` tokens in your chain's genesis file. Doing so will also make sure your chain is aware of this account's existence: ```bash -simd add-genesis-account $MY_VALIDATOR_ADDRESS 100000000stake +simd add-genesis-account $MY_VALIDATOR_ADDRESS 100000000000stake ``` Recall that `$MY_VALIDATOR_ADDRESS` is a variable that holds the address of the `my_validator` key in the [keyring](./keyring.md#adding-keys-to-the-keyring). Also note that the tokens in the SDK have the `{amount}{denom}` format: `amount` is is a 18-digit-precision decimal number, and `denom` is the unique token identifier with its denomination key (e.g. `atom` or `uatom`). Here, we are granting `stake` tokens, as `stake` is the token identifier used for staking in [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc3/simapp). For your own chain with its own staking denom, that token identifier should be used instead. @@ -53,7 +53,7 @@ Now that your account has some tokens, you need to add a validator to your chain ```bash # Create a gentx. -simd gentx my_validator 100000stake --chain-id my-test-chain --keyring-backend test +simd gentx my_validator 100000000stake --chain-id my-test-chain --keyring-backend test # Add the gentx to the genesis file. simd collect-gentxs @@ -85,6 +85,12 @@ The previous command allow you to run a single node. This is enough for the next The naive way would be to run the same commands again in separate terminal windows. This is possible, however in the SDK, we leverage the power of [Docker Compose](https://docs.docker.com/compose/) to run a localnet. If you need inspiration on how to set up your own localnet with Docker Compose, you can have a look at the SDK's [`docker-compose.yml`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/docker-compose.yml). +## Configuring the Node Using `app.toml` + +The Cosmos SDK automatically generates an `app.toml` file inside `~/.simapp/config`. This file is used to configure your app, such as state pruning strategies, telemetry, gRPC and REST servers configuration, state sync... The file itself is heavily commented, please refer to it directly to tweak your node. + +Make sure to restart your node after modifying `app.toml`. + ## Next {hide} Read about the [Interacting with your Node](./interact-node.md) {hide} diff --git a/docs/run-node/txs.md b/docs/run-node/txs.md index 0c4c9de47ccf..ce977212d232 100644 --- a/docs/run-node/txs.md +++ b/docs/run-node/txs.md @@ -11,14 +11,14 @@ This document describes how to generate an (unsigned) transaction, signing it (w The easiest way to send transactions is using the CLI, as we have seen in the previous page when [interacting with a node](./interact-node.md#using-the-cli). For example, running the following command ```bash -simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain +simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain --keyring-backend test ``` will run the following steps: - generate a transaction with one `Msg` (`x/bank`'s `MsgSend`), and print the generated transaction to the console. - ask the user for confirmation to send the transaction from the `$MY_VALIDATOR_ADDRESS` account. -- fetch `$MY_VALIDATOR_ADDRESS` in the keyring. This is possible because we have [set up the CLI's keyring](./keyring.md) in a previous step. +- fetch `$MY_VALIDATOR_ADDRESS` from the keyring. This is possible because we have [set up the CLI's keyring](./keyring.md) in a previous step. - sign the generated transaction with the keyring's account. - broadcast the signed transaction to the network. This is possible because the CLI connects to the node's Tendermint RPC endpoint. @@ -52,7 +52,7 @@ Some useful flags to consider in the `tx sign` command: #### Signing with Multiple Signers ::: warning -Please note that signing a transaction with multiple signers or with a multisig account, where at least one signer uses `SIGN_MODE_DIRECT`, is not possible as of yet. You may follow [this Github issue](https://github.com/cosmos/cosmos-sdk/issues/8141) for more info. +Please note that signing a transaction with multiple signers or with a multisig account, where at least one signer uses `SIGN_MODE_DIRECT`, is not yet possible. You may follow [this Github issue](https://github.com/cosmos/cosmos-sdk/issues/8141) for more info. ::: Signing with multiple signers is done with the `tx multisign` command. This command assumes that all signers use `SIGN_MODE_LEGACY_AMINO_JSON`. The flow is similar to the `tx sign` command flow, but instead of signing an unsigned transaction file, each signer signs the file signed by previous signer(s). The `tx multisign` command will append signatures to the existing transactions. It is important that signers sign the transaction **in the same order** as given by the transaction, which is retrievable using the `GetSigners()` method. @@ -60,14 +60,13 @@ Signing with multiple signers is done with the `tx multisign` command. This comm For example, starting with the `unsigned_tx.json`, and assuming the transaction has 4 signers, we would run: ```bash -# Let signer 1 sign the unsigned tx. +# Let signer1 sign the unsigned tx. simd tx multisignsign unsigned_tx.json signer_key_1 --chain-id my-test-chain --keyring-backend test > partial_tx_1.json -# Signer 2 appends their signature. +# Now signer1 will send the partial_tx_1.json to the signer2. +# Signer2 appends their signature: simd tx multisignsign partial_tx_1.json signer_key_2 --chain-id my-test-chain --keyring-backend test > partial_tx_2.json -# Signer 3 appends their signature. +# Signer2 sends the partial_tx_2.json file to signer3, and signer3 can append his signature: simd tx multisignsign partial_tx_2.json signer_key_3 --chain-id my-test-chain --keyring-backend test > partial_tx_3.json -# Signer 4 appends their signature. The final output is the fully signed tx. -simd tx multisignsign partial_tx_3.json signer_key_4 --chain-id my-test-chain --keyring-backend test > signed_tx.json ``` ### Broadcasting a Transaction @@ -154,7 +153,7 @@ At this point, `TxBuilder`'s underlying transaction is ready to be signed. ### Signing a Transaction -We chose our encoding config to use Protobuf, which will use `SIGN_MODE_DIRECT` by default. As per [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/docs/architecture/adr-020-protobuf-transaction-encoding.md), each signer needs to sign the `SignerInfo`s of all other signers. This means that we need to perform two steps sequentially: +We set encoding config to use Protobuf, which will use `SIGN_MODE_DIRECT` by default. As per [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/docs/architecture/adr-020-protobuf-transaction-encoding.md), each signer needs to sign the `SignerInfo`s of all other signers. This means that we need to perform two steps sequentially: - for each signer, populate the signer's `SignerInfo` inside `TxBuilder`, - once all `SignerInfo`s are populated, for each signer, sign the `SignDoc` (the payload to be signed). @@ -254,7 +253,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx" ) -func sendTx() error { +func sendTx(ctx context.Context) error { // --snip-- // Create a connection to the gRPC server. @@ -269,7 +268,7 @@ func sendTx() error { txClient := tx.NewServiceClient(grpcConn) // We then call the BroadcastTx method on this client. grpcRes, err := txClient.BroadcastTx( - context.Background(), + ctx, &tx.BroadcastTxRequest{ Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, TxBytes: txBytes, // Proto-binary of the signed transaction, see previous step. diff --git a/docs/spec/_proposals/README.md b/docs/spec/_proposals/README.md deleted file mode 100644 index 213d6288cb70..000000000000 --- a/docs/spec/_proposals/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Spec Proposals - -- [F1 Fee Distribution](./f1-fee-distribution/f1_fee_distr.pdf) \ No newline at end of file diff --git a/docs/spec/_proposals/f1-fee-distribution/f1_fee_distr.pdf b/docs/spec/fee_distribution/f1_fee_distr.pdf similarity index 100% rename from docs/spec/_proposals/f1-fee-distribution/f1_fee_distr.pdf rename to docs/spec/fee_distribution/f1_fee_distr.pdf diff --git a/docs/spec/_proposals/f1-fee-distribution/f1_fee_distr.tex b/docs/spec/fee_distribution/f1_fee_distr.tex similarity index 100% rename from docs/spec/_proposals/f1-fee-distribution/f1_fee_distr.tex rename to docs/spec/fee_distribution/f1_fee_distr.tex diff --git a/docs/using-the-sdk/README.md b/docs/using-the-sdk/README.md index fb645c679cc4..747dfd500553 100644 --- a/docs/using-the-sdk/README.md +++ b/docs/using-the-sdk/README.md @@ -7,4 +7,3 @@ parent: - [Modules](../../x/README.md) - [Simulation](./simulation.md) -- [Cosmos Upgrade Manager](./cosmovisor.md) diff --git a/docs/versions b/docs/versions index d5dc85eb286f..31c880a13dde 100644 --- a/docs/versions +++ b/docs/versions @@ -1,2 +1,3 @@ master master launchpad/backports v0.39 +release/v0.41.x v0.41 diff --git a/go.mod b/go.mod index 042dbaab52c7..bfa44778a47a 100644 --- a/go.mod +++ b/go.mod @@ -4,20 +4,19 @@ module github.com/cosmos/cosmos-sdk require ( github.com/99designs/keyring v1.1.6 - github.com/DataDog/zstd v1.4.5 // indirect - github.com/armon/go-metrics v0.3.5 + github.com/armon/go-metrics v0.3.6 github.com/bgentry/speakeasy v0.1.0 github.com/btcsuite/btcd v0.21.0-beta github.com/btcsuite/btcutil v1.0.2 + github.com/coinbase/rosetta-sdk-go v0.5.9 github.com/confio/ics23/go v0.6.3 github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/iavl v0.15.3 github.com/cosmos/ledger-cosmos-go v0.11.1 - github.com/dgraph-io/ristretto v0.0.3 // indirect - github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25 github.com/gogo/gateway v1.1.0 - github.com/gogo/protobuf v1.3.1 + github.com/gogo/protobuf v1.3.3 github.com/golang/mock v1.4.4 github.com/golang/protobuf v1.4.3 github.com/golang/snappy v0.0.2 // indirect @@ -26,34 +25,39 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/hashicorp/golang-lru v0.5.4 + 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/pelletier/go-toml v1.8.0 // indirect + 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/common v0.16.0 github.com/rakyll/statik v0.1.7 - github.com/regen-network/cosmos-proto v0.3.0 + github.com/regen-network/cosmos-proto v0.3.1 github.com/rs/zerolog v1.20.0 + github.com/sikkatech/go-substrate-bip39 v0.0.0-20191224085309-34f8034ee0a5 github.com/spf13/afero v1.3.4 // indirect github.com/spf13/cast v1.3.1 - github.com/spf13/cobra v1.1.1 - github.com/spf13/jwalterweatherman v1.1.0 // indirect; indirects + github.com/spf13/cobra v1.1.3 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.7.1 - github.com/stretchr/testify v1.6.1 + github.com/stretchr/testify v1.7.0 github.com/tendermint/btcd v0.1.1 + 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.1 - github.com/tendermint/tm-db v0.6.3 + github.com/tendermint/tendermint v0.34.7 + github.com/tendermint/tm-db v0.6.4 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad - golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect - google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d - google.golang.org/grpc v1.33.2 + google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f + google.golang.org/grpc v1.35.0 google.golang.org/protobuf v1.25.0 + gopkg.in/ini.v1 v1.61.0 // indirect gopkg.in/yaml.v2 v2.4.0 + nhooyr.io/websocket v1.8.6 // indirect ) -replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.2-alpha.regen.4 +replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 + +replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/go.sum b/go.sum index 1ad018d60d49..39507d0dc189 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= @@ -11,11 +10,27 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/99designs/keyring v1.1.3/go.mod h1:657DQuMrBZRtuL/voxVyiyb6zpMehlm5vLB9Qwrv904= github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM= github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU= +github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= +github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= +github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ChainSafe/go-schnorrkel v0.0.0-20191217051421-8f63573202b6/go.mod h1:/w/EhyNmwlP1/LTWy7XufwTUZRljuJmuwCvX9TeFqxI= +github.com/ChainSafe/go-schnorrkel v0.0.0-20191224001618-60bb676dd3e4/go.mod h1:+cafF2OKta2sUnPSHcPjZ0MDgwpxFspKAlP2sstJ0eQ= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -27,8 +42,11 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= @@ -38,19 +56,23 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.5 h1:uq4txK6NAUvLQ60rotN+K+JuTnf3XP4TdQmcs9ma5mk= -github.com/armon/go-metrics v0.3.5/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.3.6 h1:x/tmtOF9cDBoXH7XoAGOz2qqm1DknFD1590XmD/DUJ8= +github.com/armon/go-metrics v0.3.6/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -58,7 +80,9 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= +github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M= github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= @@ -77,6 +101,7 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= @@ -84,10 +109,13 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coinbase/rosetta-sdk-go v0.5.8/go.mod h1:xd4wYUhV3LkY78SPH8BUhc88rXfn2jYgN9BfiSjbcvM= +github.com/coinbase/rosetta-sdk-go v0.5.9 h1:CuGQE3HFmYwdEACJnuOtVI9cofqPsGvq6FdFIzaOPKI= +github.com/coinbase/rosetta-sdk-go v0.5.9/go.mod h1:xd4wYUhV3LkY78SPH8BUhc88rXfn2jYgN9BfiSjbcvM= github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= github.com/confio/ics23/go v0.6.3 h1:PuGK2V1NJWZ8sSkNDq91jgT/cahFEW9RGp4Y5jxulf0= github.com/confio/ics23/go v0.6.3/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= @@ -101,6 +129,9 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosmos/cosmos-sdk v0.34.4-0.20191221032601-0255540f231e/go.mod h1:eAsW0ukTzolBNamfJdL4pJreORO0t1o0N/O0V7TqjS8= +github.com/cosmos/ethermint v0.0.0-20190802135314-3f32f9ba8a1f/go.mod h1:3uorjbOIEOqbASxo/tR7bq2S3cf+yy5vWratMU4xY40= +github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= @@ -122,7 +153,10 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgraph-io/badger/v2 v2.2007.1/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= @@ -134,32 +168,44 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ= github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= +github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25 h1:2vLKys4RBU4pn2T/hjXMbvwTr1Cvy5THHrQkbeY9HRk= github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25/go.mod h1:hTr8+TLQmkUkgcuh3mcr5fjrT9c64ZzsBCdCEC6UppY= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/ethereum/go-ethereum v1.9.6/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= +github.com/ethereum/go-ethereum v1.9.23 h1:SIKhg/z4Q7AbvqcxuPYvMxf36che/Rq/Pp0IdYEkbtw= +github.com/ethereum/go-ethereum v1.9.23/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= +github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -167,9 +213,15 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= @@ -178,20 +230,35 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0= github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= @@ -212,6 +279,7 @@ github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -223,9 +291,13 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -245,14 +317,19 @@ github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -289,6 +366,7 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -297,58 +375,87 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/heystraightedge/straightedge v0.0.0-20191221062532-299f5807bc89/go.mod h1:qapig38GVxoRWnBz5nvqugZD+jZxfFKpA0XF8/NJA1s= +github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= +github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/improbable-eng/grpc-web v0.14.0 h1:GdoK+cXABdB+1keuqsV1drSFO2XLYIxqt/4Rj8SWGBk= +github.com/improbable-eng/grpc-web v0.14.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= +github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -366,14 +473,21 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= @@ -389,13 +503,17 @@ github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtb github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -420,11 +538,14 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= -github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= +github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= @@ -454,6 +575,7 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -462,8 +584,8 @@ 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.16.0 h1:VMiIg2fcMM+QkvAaPo6Hatk8OgpVHh4Yzp1+ljjjQAQ= +github.com/prometheus/common v0.16.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= @@ -472,21 +594,29 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= +github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20190704165056-9c2d0518ed81/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/regen-network/cosmos-proto v0.3.0 h1:24dVpPrPi0GDoPVLesf2Ug98iK5QgVscPl0ga4Eoub0= -github.com/regen-network/cosmos-proto v0.3.0/go.mod h1:zuP2jVPHab6+IIyOx3nXHFN+euFNeS3W8XQkcdd4s7A= -github.com/regen-network/protobuf v1.3.2-alpha.regen.4 h1:c9jEnU+xm6vqyrQe3M94UFWqiXxRIKKnqBOh2EACmBE= -github.com/regen-network/protobuf v1.3.2-alpha.regen.4/go.mod h1:/J8/bR1T/NXyIdQDLUaq15LjNE83nRzkyrLAMcPewig= +github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzywPxOvwMdxcg= +github.com/regen-network/cosmos-proto v0.3.1/go.mod h1:jO0sVX6a1B36nmE8C9xBFXpNwWejXC7QqCOnH3O0+YM= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= +github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= @@ -496,10 +626,16 @@ 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= +github.com/sikkatech/go-substrate-bip39 v0.0.0-20191224085309-34f8034ee0a5 h1:daNBIl8o+aq2aCpkFD8x9Zp/1lVhJVRUiOMVx1t9m3c= +github.com/sikkatech/go-substrate-bip39 v0.0.0-20191224085309-34f8034ee0a5/go.mod h1:cHnrb12pwXfPQMmKVQuJ87ITpiF51wAunGGF8rmR66s= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -512,16 +648,21 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.3.4 h1:8q6vk3hthlpb2SouZcnBVKboxWQWMDNF38bwholZrJc= github.com/spf13/afero v1.3.4/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0/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.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= @@ -529,11 +670,17 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= +github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= +github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -546,36 +693,64 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stumble/gorocksdb v0.0.3/go.mod h1:v6IHdFBXk5DJ1K4FZ0xi+eY737quiiBxYtSWXadLybY= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk= github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= +github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc2 h1:crekJuQ57yIBDuKd3/dMJ00ZvOHURuv9RGJSi2hWTW4= +github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc2/go.mod h1:gBPw8WV2Erm4UGHlBRiM3zaEBst4bsuihmMCNQdgP/s= github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI= github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= +github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= +github.com/tendermint/go-amino v0.15.1/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tendermint/iavl v0.12.4/go.mod h1:8LHakzt8/0G3/I8FUU0ReNx98S/EP6eyPJkAUvEXT/o= +github.com/tendermint/tendermint v0.32.1/go.mod h1:jmPDAKuNkev9793/ivn/fTBnfpA9mGBww8MPRNPNxnU= +github.com/tendermint/tendermint v0.32.8/go.mod h1:5/B1XZjNYtVBso8o1l/Eg4A0Mhu42lDcmftoQl95j/E= 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 h1:eXCfMgoqVSzrjzOj6clI9GAejcHH0LvOlRjpCmMJksU= github.com/tendermint/tendermint v0.34.0/go.mod h1:Aj3PIipBFSNO21r+Lq3TtzQ+uKESxkbA3yo/INM4QwQ= -github.com/tendermint/tendermint v0.34.1 h1:TsJXY8+1NThc/XIENMv1ENzazriHBJJjeP6hN3kpC8g= -github.com/tendermint/tendermint v0.34.1/go.mod h1:Aj3PIipBFSNO21r+Lq3TtzQ+uKESxkbA3yo/INM4QwQ= +github.com/tendermint/tendermint v0.34.7 h1:lvBJFNqpDuEzKfLZKtUXOL5dMOpqHonHlO6LCujyl6E= +github.com/tendermint/tendermint v0.34.7/go.mod h1:JVuu3V1ZexOaZG8VJMRl8lnfrGw6hEB2TVnoUwKRbss= +github.com/tendermint/tm-db v0.1.1/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= +github.com/tendermint/tm-db v0.2.0/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI= github.com/tendermint/tm-db v0.6.3 h1:ZkhQcKnB8/2jr5EaZwGndN4owkPsGezW2fSisS9zGbg= github.com/tendermint/tm-db v0.6.3/go.mod h1:lfA1dL9/Y/Y8wwyPp2NMLyn5P5Ptr/gvDFNWtrCWSf8= +github.com/tendermint/tm-db v0.6.4 h1:3N2jlnYQkXNQclQwd/eKV/NzlqPlfK21cpRRIx80XXQ= +github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw= +github.com/tidwall/gjson v1.6.1/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.1.2/go.mod h1:SEzaDwxiPzKzNfUEO4HbYF/m4UCSJDsGgNqsS1LvdoY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vmihailenco/msgpack/v5 v5.0.0-beta.9/go.mod h1:HVxBVPUK/+fZMonk4bi1islLa8V3cfnBug0+4dykPzo= +github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -600,13 +775,16 @@ golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -614,19 +792,18 @@ golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 h1:phUcVbl53swtrUN8kQEXFhUxPlIlWyBfKmidCu7P95o= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -635,14 +812,18 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -658,16 +839,20 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190916140828-c8589233b77d/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -678,8 +863,9 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -689,6 +875,7 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -699,6 +886,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -710,12 +898,17 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= @@ -729,10 +922,7 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -751,9 +941,12 @@ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200110213125-a7a6caa82ab2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -765,13 +958,13 @@ google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -787,29 +980,9 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4 h1:Rt0FRalMgdSlXAVJvX4pr65KfqaxHXSLkSJRD9pw6g0= google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d h1:HV9Z9qMhQEsdlvxNFELgQ11RkMzO3CMkjEySjCtuLes= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f h1:izedQ6yVIc5mZsRuXzmSreCOlzI0lCU1HpG8yEdMiKw= +google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -836,9 +1009,14 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.61.0 h1:LBCdW4FmFYL4s/vDZD1RQYX7oAR6IjujCYgMdbHBR10= +gopkg.in/ini.v1 v1.61.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -846,18 +1024,23 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index a0da4739d2b9..000000000000 --- a/package-lock.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@types/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.33", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", - "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", - "requires": { - "@types/node": "*" - } - }, - "@types/express": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", - "integrity": "sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "*", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.8", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.8.tgz", - "integrity": "sha512-1SJZ+R3Q/7mLkOD9ewCBDYD2k0WyZQtWYqF/2VvoNN2/uhI49J9CDN4OAm+wGMA0DbArA4ef27xl4+JwMtGggw==", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/mime": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.2.tgz", - "integrity": "sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q==" - }, - "@types/node": { - "version": "14.0.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.14.tgz", - "integrity": "sha512-syUgf67ZQpaJj01/tRTknkMNoBBLWJOBODF0Zm4NrXmiSuxjymFrxnTu1QVYRubhVkRcZLYZG8STTwJRdVm/WQ==" - }, - "@types/passport": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.3.tgz", - "integrity": "sha512-nyztuxtDPQv9utCzU0qW7Gl8BY2Dn8BKlYAFFyxKipFxjaVd96celbkLCV/tRqqBUZ+JB8If3UfgV8347DTo3Q==", - "requires": { - "@types/express": "*" - } - }, - "@types/passport-twitter": { - "version": "1.0.35", - "resolved": "https://registry.npmjs.org/@types/passport-twitter/-/passport-twitter-1.0.35.tgz", - "integrity": "sha512-7ceE/w7bvIqDPdOkPuXSDHTwwCnBW/wpn4vB98AlXieJ31nuCzjWZKWjxtyNpie8/StigN1tzF/YCRGgEh2Seg==", - "requires": { - "@types/express": "*", - "@types/passport": "*" - } - }, - "@types/qs": { - "version": "6.9.3", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.3.tgz", - "integrity": "sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA==" - }, - "@types/range-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" - }, - "@types/serve-static": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.4.tgz", - "integrity": "sha512-jTDt0o/YbpNwZbQmE/+2e+lfjJEJJR0I3OFaKQKPWkASkCoW3i6fsUnqudSMcNAfbtmADGu8f4MV4q+GqULmug==", - "requires": { - "@types/express-serve-static-core": "*", - "@types/mime": "*" - } - }, - "oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" - }, - "passport-oauth1": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.1.0.tgz", - "integrity": "sha1-p96YiiEfnPRoc3cTDqdN8ycwyRg=", - "requires": { - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "utils-merge": "1.x.x" - } - }, - "passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" - }, - "passport-twitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/passport-twitter/-/passport-twitter-1.0.4.tgz", - "integrity": "sha1-AaeZ4fdgvy3knyul+6MigvGJMtc=", - "requires": { - "passport-oauth1": "1.x.x", - "xtraverse": "0.1.x" - } - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "xmldom": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz", - "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==" - }, - "xtraverse": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/xtraverse/-/xtraverse-0.1.0.tgz", - "integrity": "sha1-t0G60BjveNip0ug63gB7P3lZxzI=", - "requires": { - "xmldom": "0.1.x" - } - } - } -} diff --git a/proto/cosmos/authz/v1beta1/authz.proto b/proto/cosmos/authz/v1beta1/authz.proto new file mode 100644 index 000000000000..56c403a79ff7 --- /dev/null +++ b/proto/cosmos/authz/v1beta1/authz.proto @@ -0,0 +1,37 @@ +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"; +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 { + option (cosmos_proto.implements_interface) = "Authorization"; + + // method name to grant unrestricted permissions to execute + // Note: MethodName() is already a method on `GenericAuthorization` type, + // we need some custom naming here so using `MessageName` + string method_name = 1 [(gogoproto.customname) = "MessageName"]; +} + +// AuthorizationGrant gives permissions to execute +// the provide method with expiration time. +message AuthorizationGrant { + google.protobuf.Any authorization = 1 [(cosmos_proto.accepts_interface) = "Authorization"]; + google.protobuf.Timestamp expiration = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/authz/v1beta1/genesis.proto b/proto/cosmos/authz/v1beta1/genesis.proto new file mode 100644 index 000000000000..940d42e1f29b --- /dev/null +++ b/proto/cosmos/authz/v1beta1/genesis.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; +package cosmos.authz.v1beta1; + +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"; + +// GenesisState defines the authz module's genesis state. +message GenesisState { + repeated GrantAuthorization authorization = 1 [(gogoproto.nullable) = false]; +} + +// GrantAuthorization defines the GenesisState/GrantAuthorization type. +message GrantAuthorization { + string granter = 1; + string grantee = 2; + + google.protobuf.Any authorization = 3 [(cosmos_proto.accepts_interface) = "Authorization"]; + google.protobuf.Timestamp expiration = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} diff --git a/proto/cosmos/authz/v1beta1/query.proto b/proto/cosmos/authz/v1beta1/query.proto new file mode 100644 index 000000000000..8c7d50a8d7e1 --- /dev/null +++ b/proto/cosmos/authz/v1beta1/query.proto @@ -0,0 +1,54 @@ +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"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/authz/types"; + +// Query defines the gRPC querier service. +service Query { + // Returns any `Authorization` (or `nil`), with the expiration time, granted to the grantee by the granter for the + // provided msg type. + rpc Authorization(QueryAuthorizationRequest) returns (QueryAuthorizationResponse) { + option (google.api.http).get = "/cosmos/authz/v1beta1/granters/{granter}/grantees/{grantee}/grant"; + } + + // Returns list of `Authorization`, granted to the grantee by the granter. + rpc Authorizations(QueryAuthorizationsRequest) returns (QueryAuthorizationsResponse) { + option (google.api.http).get = "/cosmos/authz/v1beta1/granters/{granter}/grantees/{grantee}/grants"; + } +} + +// QueryAuthorizationRequest is the request type for the Query/Authorization RPC method. +message QueryAuthorizationRequest { + string granter = 1; + string grantee = 2; + string method_name = 3; +} + +// QueryAuthorizationResponse is the response type for the Query/Authorization RPC method. +message QueryAuthorizationResponse { + // authorization is a authorization granted for grantee by granter. + cosmos.authz.v1beta1.AuthorizationGrant authorization = 1; +} + +// QueryAuthorizationsRequest is the request type for the Query/Authorizations RPC method. +message QueryAuthorizationsRequest { + string granter = 1; + string grantee = 2; + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 3; +} + +// QueryAuthorizationsResponse is the response type for the Query/Authorizations RPC method. +message QueryAuthorizationsResponse { + // authorizations is a list of grants granted for grantee by granter. + repeated cosmos.authz.v1beta1.AuthorizationGrant authorizations = 1; + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/proto/cosmos/authz/v1beta1/tx.proto b/proto/cosmos/authz/v1beta1/tx.proto new file mode 100644 index 000000000000..69e3e6f1fba2 --- /dev/null +++ b/proto/cosmos/authz/v1beta1/tx.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; +package cosmos.authz.v1beta1; + +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/any.proto"; +import "cosmos/base/abci/v1beta1/abci.proto"; +option go_package = "github.com/cosmos/cosmos-sdk/x/authz/types"; + +// Msg defines the authz Msg service. +service Msg { + // GrantAuthorization grants the provided authorization to the grantee on the granter's + // account with the provided expiration time. + rpc GrantAuthorization(MsgGrantAuthorizationRequest) returns (MsgGrantAuthorizationResponse); + + // ExecAuthorized attempts to execute the provided messages using + // authorizations granted to the grantee. Each message should have only + // one signer corresponding to the granter of the authorization. + rpc ExecAuthorized(MsgExecAuthorizedRequest) returns (MsgExecAuthorizedResponse); + + // RevokeAuthorization revokes any authorization corresponding to the provided method name on the + // granter's account that has been granted to the grantee. + rpc RevokeAuthorization(MsgRevokeAuthorizationRequest) returns (MsgRevokeAuthorizationResponse); +} + +// MsgGrantAuthorizationRequest grants the provided authorization to the grantee on the granter's +// account with the provided expiration time. +message MsgGrantAuthorizationRequest { + string granter = 1; + string grantee = 2; + + google.protobuf.Any authorization = 3 [(cosmos_proto.accepts_interface) = "Authorization"]; + google.protobuf.Timestamp expiration = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} + +// MsgExecAuthorizedResponse defines the Msg/MsgExecAuthorizedResponse response type. +message MsgExecAuthorizedResponse { + cosmos.base.abci.v1beta1.Result result = 1; +} + +// MsgExecAuthorizedRequest attempts to execute the provided messages using +// authorizations granted to the grantee. Each message should have only +// one signer corresponding to the granter of the authorization. +message MsgExecAuthorizedRequest { + string grantee = 1; + repeated google.protobuf.Any msgs = 2; +} + +// MsgGrantAuthorizationResponse defines the Msg/MsgGrantAuthorization response type. +message MsgGrantAuthorizationResponse {} + +// MsgRevokeAuthorizationRequest revokes any authorization with the provided sdk.Msg type on the +// granter's account with that has been granted to the grantee. +message MsgRevokeAuthorizationRequest { + string granter = 1; + string grantee = 2; + string method_name = 3; +} + +// MsgRevokeAuthorizationResponse defines the Msg/MsgRevokeAuthorizationResponse response type. +message MsgRevokeAuthorizationResponse {} diff --git a/proto/cosmos/bank/v1beta1/query.proto b/proto/cosmos/bank/v1beta1/query.proto index 8f8cfe126330..bc5e29137a95 100644 --- a/proto/cosmos/bank/v1beta1/query.proto +++ b/proto/cosmos/bank/v1beta1/query.proto @@ -35,6 +35,16 @@ service Query { rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/cosmos/bank/v1beta1/params"; } + + // DenomsMetadata queries the client metadata of a given coin denomination. + rpc DenomMetadata(QueryDenomMetadataRequest) returns (QueryDenomMetadataResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/denoms_metadata/{denom}"; + } + + // DenomsMetadata queries the client metadata for all registered coin denominations. + rpc DenomsMetadata(QueryDenomsMetadataRequest) returns (QueryDenomsMetadataResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/denoms_metadata"; + } } // QueryBalanceRequest is the request type for the Query/Balance RPC method. @@ -109,3 +119,32 @@ message QueryParamsRequest {} message QueryParamsResponse { Params params = 1 [(gogoproto.nullable) = false]; } + +// QueryDenomsMetadataRequest is the request type for the Query/DenomsMetadata RPC method. +message QueryDenomsMetadataRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryDenomsMetadataResponse is the response type for the Query/DenomsMetadata RPC +// method. +message QueryDenomsMetadataResponse { + // metadata provides the client information for all the registered tokens. + repeated Metadata metadatas = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDenomMetadataRequest is the request type for the Query/DenomMetadata RPC method. +message QueryDenomMetadataRequest { + // denom is the coin denom to query the metadata for. + string denom = 1; +} + +// QueryDenomMetadataResponse is the response type for the Query/DenomMetadata RPC +// method. +message QueryDenomMetadataResponse { + // metadata describes and provides all the client information for the requested token. + Metadata metadata = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/crypto/ed25519/keys.proto b/proto/cosmos/crypto/ed25519/keys.proto index bed9c29cc797..6ffec3448370 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/crypto/sr25519/keys.proto b/proto/cosmos/crypto/sr25519/keys.proto new file mode 100644 index 000000000000..db4bdea74dff --- /dev/null +++ b/proto/cosmos/crypto/sr25519/keys.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package cosmos.crypto.sr25519; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/crypto/keys/sr25519"; + +// PubKey defines a sr25519 public key +message PubKey { + option (gogoproto.goproto_stringer) = false; + bytes key = 1; +} + +// PrivKey defines a sr25519 private key. +message PrivKey { + bytes key = 1; +} diff --git a/proto/cosmos/feegrant/v1beta1/feegrant.proto b/proto/cosmos/feegrant/v1beta1/feegrant.proto new file mode 100644 index 000000000000..534de582d977 --- /dev/null +++ b/proto/cosmos/feegrant/v1beta1/feegrant.proto @@ -0,0 +1,81 @@ +syntax = "proto3"; +package cosmos.feegrant.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types"; + +// BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens +// that optionally expires. The delegatee can use up to SpendLimit to cover fees. +message BasicFeeAllowance { + option (cosmos_proto.implements_interface) = "FeeAllowanceI"; + + // spend_limit specifies the maximum amount of tokens that can be spent + // by this allowance and will be updated as tokens are spent. If it is + // empty, there is no spend limit and any amount of coins can be spent. + repeated cosmos.base.v1beta1.Coin spend_limit = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // expiration specifies an optional time when this allowance expires + ExpiresAt expiration = 2 [(gogoproto.nullable) = false]; +} + +// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap, +// as well as a limit per time period. +message PeriodicFeeAllowance { + option (cosmos_proto.implements_interface) = "FeeAllowanceI"; + + // basic specifies a struct of `BasicFeeAllowance` + BasicFeeAllowance basic = 1 [(gogoproto.nullable) = false]; + + // period specifies the time duration in which period_spend_limit coins can + // be spent before that allowance is reset + Duration period = 2 [(gogoproto.nullable) = false]; + + // period_spend_limit specifies the maximum number of coins that can be spent + // in the period + repeated cosmos.base.v1beta1.Coin period_spend_limit = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // period_can_spend is the number of coins left to be spent before the period_reset time + repeated cosmos.base.v1beta1.Coin period_can_spend = 4 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // period_reset is the time at which this period resets and a new one begins, + // it is calculated from the start time of the first transaction after the + // last period ended + ExpiresAt period_reset = 5 [(gogoproto.nullable) = false]; +} + +// Duration is a span of a clock time or number of blocks. +// This is designed to be added to an ExpiresAt struct. +message Duration { + // sum is the oneof that represents either duration or block + oneof sum { + google.protobuf.Duration duration = 1 [(gogoproto.stdduration) = true]; + uint64 blocks = 2; + } +} + +// ExpiresAt is a point in time where something expires. +// It may be *either* block time or block height +message ExpiresAt { + // sum is the oneof that represents either time or height + oneof sum { + google.protobuf.Timestamp time = 1 [(gogoproto.stdtime) = true]; + int64 height = 2; + } +} + +// FeeAllowanceGrant is stored in the KVStore to record a grant with full context +message FeeAllowanceGrant { + + string granter = 1; + string grantee = 2; + google.protobuf.Any allowance = 3 [(cosmos_proto.accepts_interface) = "FeeAllowanceI"]; +} diff --git a/proto/cosmos/feegrant/v1beta1/genesis.proto b/proto/cosmos/feegrant/v1beta1/genesis.proto new file mode 100644 index 000000000000..a021ea61dfc7 --- /dev/null +++ b/proto/cosmos/feegrant/v1beta1/genesis.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package cosmos.feegrant.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/feegrant/v1beta1/feegrant.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types"; + +// GenesisState contains a set of fee allowances, persisted from the store +message GenesisState { + repeated FeeAllowanceGrant fee_allowances = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/feegrant/v1beta1/query.proto b/proto/cosmos/feegrant/v1beta1/query.proto new file mode 100644 index 000000000000..42d5f302005f --- /dev/null +++ b/proto/cosmos/feegrant/v1beta1/query.proto @@ -0,0 +1,52 @@ +syntax = "proto3"; +package cosmos.feegrant.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/feegrant/v1beta1/feegrant.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "google/api/annotations.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types"; + +// Query defines the gRPC querier service. +service Query { + + // FeeAllowance returns fee granted to the grantee by the granter. + rpc FeeAllowance(QueryFeeAllowanceRequest) returns (QueryFeeAllowanceResponse) { + option (google.api.http).get = "/cosmos/feegrant/v1beta1/fee_allowance/{granter}/{grantee}"; + } + + // FeeAllowances returns all the grants for address. + rpc FeeAllowances(QueryFeeAllowancesRequest) returns (QueryFeeAllowancesResponse) { + option (google.api.http).get = "/cosmos/feegrant/v1beta1/fee_allowances/{grantee}"; + } +} + +// QueryFeeAllowanceRequest is the request type for the Query/FeeAllowance RPC method. +message QueryFeeAllowanceRequest { + string granter = 1; + string grantee = 2; +} + +// QueryFeeAllowanceResponse is the response type for the Query/FeeAllowance RPC method. +message QueryFeeAllowanceResponse { + // fee_allowance is a fee_allowance granted for grantee by granter. + cosmos.feegrant.v1beta1.FeeAllowanceGrant fee_allowance = 1; +} + +// QueryFeeAllowancesRequest is the request type for the Query/FeeAllowances RPC method. +message QueryFeeAllowancesRequest { + string grantee = 1; + + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryFeeAllowancesResponse is the response type for the Query/FeeAllowances RPC method. +message QueryFeeAllowancesResponse { + // fee_allowances are fee_allowance's granted for grantee by granter. + repeated cosmos.feegrant.v1beta1.FeeAllowanceGrant fee_allowances = 1; + + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/proto/cosmos/feegrant/v1beta1/tx.proto b/proto/cosmos/feegrant/v1beta1/tx.proto new file mode 100644 index 000000000000..a36f358f2503 --- /dev/null +++ b/proto/cosmos/feegrant/v1beta1/tx.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; +package cosmos.feegrant.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types"; + +// Msg defines the feegrant msg service. +service Msg { + + // GrantFeeAllowance grants fee allowance to the grantee on the granter's + // account with the provided expiration time. + rpc GrantFeeAllowance(MsgGrantFeeAllowance) returns (MsgGrantFeeAllowanceResponse); + + // RevokeFeeAllowance revokes any fee allowance of granter's account that + // has been granted to the grantee. + rpc RevokeFeeAllowance(MsgRevokeFeeAllowance) returns (MsgRevokeFeeAllowanceResponse); +} + +// MsgGrantFeeAllowance adds permission for Grantee to spend up to Allowance +// of fees from the account of Granter. +message MsgGrantFeeAllowance { + string granter = 1; + string grantee = 2; + google.protobuf.Any allowance = 3 [(cosmos_proto.accepts_interface) = "FeeAllowanceI"]; +} + +// MsgGrantFeeAllowanceResponse defines the Msg/GrantFeeAllowanceResponse response type. +message MsgGrantFeeAllowanceResponse {} + +// MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee. +message MsgRevokeFeeAllowance { + string granter = 1; + string grantee = 2; +} + +// MsgRevokeFeeAllowanceResponse defines the Msg/RevokeFeeAllowanceResponse response type. +message MsgRevokeFeeAllowanceResponse {} diff --git a/proto/cosmos/gov/v1beta1/gov.proto b/proto/cosmos/gov/v1beta1/gov.proto index 1d72e643212c..392534c535a1 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 d4f0c1f99a1d..8b68c4be45a4 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/slashing/v1beta1/slashing.proto b/proto/cosmos/slashing/v1beta1/slashing.proto index 657a90f1bd05..882a0fb60cb5 100644 --- a/proto/cosmos/slashing/v1beta1/slashing.proto +++ b/proto/cosmos/slashing/v1beta1/slashing.proto @@ -15,17 +15,20 @@ message ValidatorSigningInfo { option (gogoproto.goproto_stringer) = false; string address = 1; - // height at which validator was first a candidate OR was unjailed + // Height at which validator was first a candidate OR was unjailed int64 start_height = 2 [(gogoproto.moretags) = "yaml:\"start_height\""]; - // index offset into signed block bit array + // Index which is incremented each time the validator was a bonded + // in a block and may have signed a precommit or not. This in conjunction with the + // `SignedBlocksWindow` param determines the index in the `MissedBlocksBitArray`. int64 index_offset = 3 [(gogoproto.moretags) = "yaml:\"index_offset\""]; - // timestamp validator cannot be unjailed until + // Timestamp until which the validator is jailed due to liveness downtime. google.protobuf.Timestamp jailed_until = 4 [(gogoproto.moretags) = "yaml:\"jailed_until\"", (gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - // whether or not a validator has been tombstoned (killed out of validator - // set) + // Whether or not a validator has been tombstoned (killed out of validator set). It is set + // once the validator commits an equivocation or for any other configured misbehiavor. bool tombstoned = 5; - // missed blocks counter (to avoid scanning the array every time) + // A counter kept to avoid unnecessary array reads. + // Note that `Sum(MissedBlocksBitArray)` always equals `MissedBlocksCounter`. int64 missed_blocks_counter = 6 [(gogoproto.moretags) = "yaml:\"missed_blocks_counter\""]; } diff --git a/proto/cosmos/staking/v1beta1/staking.proto b/proto/cosmos/staking/v1beta1/staking.proto index eadc86e9d13a..e37b28b6d098 100644 --- a/proto/cosmos/staking/v1beta1/staking.proto +++ b/proto/cosmos/staking/v1beta1/staking.proto @@ -27,12 +27,15 @@ message CommissionRates { option (gogoproto.equal) = true; 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]; + // max_rate defines the maximum commission rate which validator can ever charge, as a fraction. string max_rate = 2 [ (gogoproto.moretags) = "yaml:\"max_rate\"", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false ]; + // max_change_rate defines the maximum daily increase of the validator commission, as a fraction. string max_change_rate = 3 [ (gogoproto.moretags) = "yaml:\"max_change_rate\"", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", @@ -45,7 +48,9 @@ message Commission { option (gogoproto.equal) = true; 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]; + // update_time is the last time the commission rate was changed. google.protobuf.Timestamp update_time = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"update_time\""]; } @@ -55,10 +60,15 @@ message Description { option (gogoproto.equal) = true; option (gogoproto.goproto_stringer) = false; + // moniker defines a human-readable name for the validator. string moniker = 1; + // identity defines an optional identity signature (ex. UPort or Keybase). string identity = 2; + // website defines an optional website link. 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; } @@ -75,22 +85,33 @@ message Validator { option (gogoproto.goproto_stringer) = false; 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\""]; + // 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; + // status is the validator status (bonded/unbonding/unbonded). BondStatus status = 4; + // tokens define the delegated tokens (incl. self-delegation). string tokens = 5 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; + // delegator_shares defines total shares issued to a validator's delegators. string delegator_shares = 6 [ (gogoproto.moretags) = "yaml:\"delegator_shares\"", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false ]; + // description defines the description terms for the validator. 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\""]; + // unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. 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]; + // min_self_delegation is the validator's self declared minimum self delegation. string min_self_delegation = 11 [ (gogoproto.moretags) = "yaml:\"min_self_delegation\"", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", @@ -164,8 +185,11 @@ message Delegation { option (gogoproto.goproto_getters) = false; option (gogoproto.goproto_stringer) = false; + // delegator_address is the bech32-encoded address of the delegator. 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\""]; + // shares define the delegation shares received. string shares = 3 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; } @@ -176,8 +200,11 @@ message UnbondingDelegation { option (gogoproto.goproto_getters) = false; option (gogoproto.goproto_stringer) = false; + // delegator_address is the bech32-encoded address of the delegator. 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\""]; + // entries are the unbonding delegation entries. repeated UnbondingDelegationEntry entries = 3 [(gogoproto.nullable) = false]; // unbonding delegation entries } @@ -186,14 +213,18 @@ message UnbondingDelegationEntry { option (gogoproto.equal) = true; option (gogoproto.goproto_stringer) = false; + // creation_height is the height which the unbonding took place. 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\""]; + // initial_balance defines the tokens initially scheduled to receive at completion. string initial_balance = 3 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"initial_balance\"" ]; + // balance defines the tokens to receive at completion. string balance = 4 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; } @@ -202,14 +233,18 @@ message RedelegationEntry { option (gogoproto.equal) = true; option (gogoproto.goproto_stringer) = false; + // creation_height defines the height which the redelegation took place. 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\""]; + // initial_balance defines the initial balance when redelegation started. string initial_balance = 3 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"initial_balance\"" ]; + // shares_dst is the amount of destination-validator shares created by redelegation. string shares_dst = 4 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; } @@ -221,9 +256,13 @@ message Redelegation { option (gogoproto.goproto_getters) = false; option (gogoproto.goproto_stringer) = false; + // delegator_address is the bech32-encoded address of the delegator. 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\""]; + // validator_dst_address is the validator redelegation destination operator 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 } @@ -232,11 +271,16 @@ message Params { option (gogoproto.equal) = true; option (gogoproto.goproto_stringer) = false; + // unbonding_time is the time duration of unbonding. 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\""]; + // max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio). 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\""]; } diff --git a/proto/ibc/core/client/v1/client.proto b/proto/ibc/core/client/v1/client.proto index 11d2195aaf61..4c6308bd5415 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/protocgen.sh b/scripts/protocgen.sh index f92a47cc9556..bd3e4009712b 100755 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -8,15 +8,10 @@ protoc_gen_gocosmos() { return 1 fi - go get github.com/regen-network/cosmos-proto/protoc-gen-gocosmos 2>/dev/null + go get github.com/regen-network/cosmos-proto/protoc-gen-gocosmos@latest 2>/dev/null } -protoc_gen_doc() { - go get -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc 2>/dev/null -} - protoc_gen_gocosmos -protoc_gen_doc proto_dirs=$(find ./proto -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) for dir in $proto_dirs; do @@ -25,12 +20,6 @@ for dir in $proto_dirs; do -I "third_party/proto" \ --gocosmos_out=plugins=interfacetype+grpc,\ Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types:. \ - $(find "${dir}" -maxdepth 1 -name '*.proto') - - # command to generate gRPC gateway (*.pb.gw.go in respective modules) files - buf protoc \ - -I "proto" \ - -I "third_party/proto" \ --grpc-gateway_out=logtostderr=true:. \ $(find "${dir}" -maxdepth 1 -name '*.proto') diff --git a/server/config/config.go b/server/config/config.go index 80e79f04558d..8f1c47e88d2c 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -14,8 +14,11 @@ import ( const ( defaultMinGasPrices = "" - // DefaultGRPCAddress is the default address the gRPC server binds to. + // DefaultGRPCAddress defines the default address to bind the gRPC server to. DefaultGRPCAddress = "0.0.0.0:9090" + + // DefaultGRPCWebAddress defines the default address to bind the gRPC-web server to. + DefaultGRPCWebAddress = "0.0.0.0:9091" ) // BaseConfig defines the server's basic configuration @@ -98,6 +101,29 @@ type APIConfig struct { // Ref: https://github.com/cosmos/cosmos-sdk/issues/6420 } +// RosettaConfig defines the Rosetta API listener configuration. +type RosettaConfig struct { + // Address defines the API server to listen on + Address string `mapstructure:"address"` + + // Blockchain defines the blockchain name + // defaults to DefaultBlockchain + Blockchain string `mapstructure:"blockchain"` + + // Network defines the network name + Network string `mapstructure:"network"` + + // Retries defines the maximum number of retries + // rosetta will do before quitting + Retries int `mapstructure:"retries"` + + // Enable defines if the API server should be enabled. + Enable bool `mapstructure:"enable"` + + // Offline defines if the server must be run in offline mode + Offline bool `mapstructure:"offline"` +} + // GRPCConfig defines configuration for the gRPC server. type GRPCConfig struct { // Enable defines if the gRPC server should be enabled. @@ -107,6 +133,15 @@ type GRPCConfig struct { Address string `mapstructure:"address"` } +// GRPCWebConfig defines configuration for the gRPC-web server. +type GRPCWebConfig struct { + // Enable defines if the gRPC-web should be enabled. + Enable bool `mapstructure:"enable"` + + // Address defines the gRPC-web server to listen on + Address string `mapstructure:"address"` +} + // StateSyncConfig defines the state sync snapshot configuration. type StateSyncConfig struct { // SnapshotInterval sets the interval at which state sync snapshots are taken. @@ -126,6 +161,8 @@ type Config struct { Telemetry telemetry.Config `mapstructure:"telemetry"` API APIConfig `mapstructure:"api"` GRPC GRPCConfig `mapstructure:"grpc"` + Rosetta RosettaConfig `mapstructure:"rosetta"` + GRPCWeb GRPCWebConfig `mapstructure:"grpc-web"` StateSync StateSyncConfig `mapstructure:"state-sync"` } @@ -185,6 +222,18 @@ func DefaultConfig() *Config { Enable: true, Address: DefaultGRPCAddress, }, + Rosetta: RosettaConfig{ + Enable: false, + Address: ":8080", + Blockchain: "app", + Network: "network", + Retries: 3, + Offline: false, + }, + GRPCWeb: GRPCWebConfig{ + Enable: true, + Address: DefaultGRPCWebAddress, + }, StateSync: StateSyncConfig{ SnapshotInterval: 0, SnapshotKeepRecent: 2, @@ -235,10 +284,22 @@ func GetConfig(v *viper.Viper) Config { RPCMaxBodyBytes: v.GetUint("api.rpc-max-body-bytes"), EnableUnsafeCORS: v.GetBool("api.enabled-unsafe-cors"), }, + Rosetta: RosettaConfig{ + Enable: v.GetBool("rosetta.enable"), + Address: v.GetString("rosetta.address"), + Blockchain: v.GetString("rosetta.blockchain"), + Network: v.GetString("rosetta.network"), + Retries: v.GetInt("rosetta.retries"), + Offline: v.GetBool("rosetta.offline"), + }, GRPC: GRPCConfig{ Enable: v.GetBool("grpc.enable"), Address: v.GetString("grpc.address"), }, + GRPCWeb: GRPCWebConfig{ + Enable: v.GetBool("grpc-web.enable"), + Address: v.GetString("grpc-web.address"), + }, StateSync: StateSyncConfig{ SnapshotInterval: v.GetUint64("state-sync.snapshot-interval"), SnapshotKeepRecent: v.GetUint32("state-sync.snapshot-keep-recent"), diff --git a/server/config/toml.go b/server/config/toml.go index b042da74293d..88197defe91b 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -135,6 +135,30 @@ rpc-max-body-bytes = {{ .API.RPCMaxBodyBytes }} # EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk). enabled-unsafe-cors = {{ .API.EnableUnsafeCORS }} +############################################################################### +### Rosetta Configuration ### +############################################################################### + +[rosetta] + +# Enable defines if the Rosetta API server should be enabled. +enable = {{ .Rosetta.Enable }} + +# Address defines the Rosetta API server to listen on. +address = "{{ .Rosetta.Address }}" + +# Network defines the name of the blockchain that will be returned by Rosetta. +blockchain = "{{ .Rosetta.Blockchain }}" + +# Network defines the name of the network that will be returned by Rosetta. +network = "{{ .Rosetta.Network }}" + +# Retries defines the number of retries when connecting to the node before failing. +retries = {{ .Rosetta.Retries }} + +# Offline defines if Rosetta server should run in offline mode. +offline = {{ .Rosetta.Offline }} + ############################################################################### ### gRPC Configuration ### ############################################################################### @@ -147,6 +171,19 @@ enable = {{ .GRPC.Enable }} # Address defines the gRPC server address to bind to. address = "{{ .GRPC.Address }}" +############################################################################### +### gRPC Web Configuration ### +############################################################################### + +[grpc-web] + +# GRPCWebEnable defines if the gRPC-web should be enabled. +# NOTE: gRPC must also be enabled, otherwise, this configuration is a no-op. +enable = {{ .GRPCWeb.Enable }} + +# Address defines the gRPC-web server address to bind to. +address = "{{ .GRPCWeb.Address }}" + ############################################################################### ### State Sync Configuration ### ############################################################################### diff --git a/server/grpc/grpc_web.go b/server/grpc/grpc_web.go new file mode 100644 index 000000000000..67dc4364a80f --- /dev/null +++ b/server/grpc/grpc_web.go @@ -0,0 +1,26 @@ +package grpc + +import ( + "net/http" + + "github.com/improbable-eng/grpc-web/go/grpcweb" + "google.golang.org/grpc" + + "github.com/cosmos/cosmos-sdk/server/config" +) + +// StartGRPCWeb starts a gRPC-Web server on the given address. +func StartGRPCWeb(grpcSrv *grpc.Server, config config.Config) (*http.Server, error) { + wrappedServer := grpcweb.WrapServer(grpcSrv) + handler := func(resp http.ResponseWriter, req *http.Request) { + wrappedServer.ServeHTTP(resp, req) + } + grpcWebSrv := &http.Server{ + Addr: config.GRPCWeb.Address, + Handler: http.HandlerFunc(handler), + } + if err := grpcWebSrv.ListenAndServe(); err != nil { + return nil, err + } + return grpcWebSrv, nil +} diff --git a/server/grpc/grpc_web_test.go b/server/grpc/grpc_web_test.go new file mode 100644 index 000000000000..8f563becc666 --- /dev/null +++ b/server/grpc/grpc_web_test.go @@ -0,0 +1,313 @@ +package grpc_test + +import ( + "bufio" + "bytes" + "encoding/base64" + "encoding/binary" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/textproto" + "strconv" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/codes" + + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/network" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// https://github.com/improbable-eng/grpc-web/blob/master/go/grpcweb/wrapper_test.go used as a reference +// to setup grpcRequest config. + +const grpcWebContentType = "application/grpc-web" + +type GRPCWebTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network + protoCdc *codec.ProtoCodec +} + +func (s *GRPCWebTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := network.DefaultConfig() + cfg.NumValidators = 1 + s.cfg = cfg + s.network = network.New(s.T(), s.cfg) + s.Require().NotNil(s.network) + + _, err := s.network.WaitForHeight(2) + s.Require().NoError(err) + + s.protoCdc = codec.NewProtoCodec(s.cfg.InterfaceRegistry) +} + +func (s *GRPCWebTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *GRPCWebTestSuite) Test_Latest_Validators() { + val := s.network.Validators[0] + for _, contentType := range []string{grpcWebContentType} { + headers, trailers, responses, err := s.makeGrpcRequest( + "/cosmos.base.tendermint.v1beta1.Service/GetLatestValidatorSet", + headerWithFlag(), + serializeProtoMessages([]proto.Message{&tmservice.GetLatestValidatorSetRequest{}}), false) + + s.Require().NoError(err) + s.Require().Equal(1, len(responses)) + s.assertTrailerGrpcCode(trailers, codes.OK, "") + s.assertContentTypeSet(headers, contentType) + var valsSet tmservice.GetLatestValidatorSetResponse + err = s.protoCdc.UnmarshalBinaryBare(responses[0], &valsSet) + s.Require().NoError(err) + pubKey, ok := valsSet.Validators[0].PubKey.GetCachedValue().(cryptotypes.PubKey) + s.Require().Equal(true, ok) + s.Require().Equal(pubKey, val.PubKey) + } +} + +func (s *GRPCWebTestSuite) Test_Total_Supply() { + for _, contentType := range []string{grpcWebContentType} { + headers, trailers, responses, err := s.makeGrpcRequest( + "/cosmos.bank.v1beta1.Query/TotalSupply", + headerWithFlag(), + serializeProtoMessages([]proto.Message{&banktypes.QueryTotalSupplyRequest{}}), false) + + s.Require().NoError(err) + s.Require().Equal(1, len(responses)) + s.assertTrailerGrpcCode(trailers, codes.OK, "") + s.assertContentTypeSet(headers, contentType) + var totalSupply banktypes.QueryTotalSupplyResponse + _ = s.protoCdc.UnmarshalBinaryBare(responses[0], &totalSupply) + } +} + +func (s *GRPCWebTestSuite) assertContentTypeSet(headers http.Header, contentType string) { + s.Require().Equal(contentType, headers.Get("content-type"), `Expected there to be content-type=%v`, contentType) +} + +func (s *GRPCWebTestSuite) assertTrailerGrpcCode(trailers Trailer, code codes.Code, desc string) { + s.Require().NotEmpty(trailers.Get("grpc-status"), "grpc-status must not be empty in trailers") + statusCode, err := strconv.Atoi(trailers.Get("grpc-status")) + s.Require().NoError(err, "no error parsing grpc-status") + s.Require().EqualValues(code, statusCode, "grpc-status must match expected code") + s.Require().EqualValues(desc, trailers.Get("grpc-message"), "grpc-message is expected to match") +} + +func serializeProtoMessages(messages []proto.Message) [][]byte { + out := [][]byte{} + for _, m := range messages { + b, _ := proto.Marshal(m) + out = append(out, b) + } + return out +} + +func (s *GRPCWebTestSuite) makeRequest( + verb string, method string, headers http.Header, body io.Reader, isText bool, +) (*http.Response, error) { + val := s.network.Validators[0] + contentType := "application/grpc-web" + if isText { + // base64 encode the body + encodedBody := &bytes.Buffer{} + encoder := base64.NewEncoder(base64.StdEncoding, encodedBody) + _, err := io.Copy(encoder, body) + if err != nil { + return nil, err + } + err = encoder.Close() + if err != nil { + return nil, err + } + body = encodedBody + contentType = "application/grpc-web-text" + } + + url := fmt.Sprintf("http://%s%s", val.AppConfig.GRPCWeb.Address, method) + req, err := http.NewRequest(verb, url, body) + s.Require().NoError(err, "failed creating a request") + req.Header = headers + + req.Header.Set("Content-Type", contentType) + client := &http.Client{} + resp, err := client.Do(req) + return resp, err +} + +func decodeMultipleBase64Chunks(b []byte) ([]byte, error) { + // grpc-web allows multiple base64 chunks: the implementation may send base64-encoded + // "chunks" with potential padding whenever the runtime needs to flush a byte buffer. + // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md + output := make([]byte, base64.StdEncoding.DecodedLen(len(b))) + outputEnd := 0 + + for inputEnd := 0; inputEnd < len(b); { + chunk := b[inputEnd:] + paddingIndex := bytes.IndexByte(chunk, '=') + if paddingIndex != -1 { + // find the consecutive = + for { + paddingIndex += 1 + if paddingIndex >= len(chunk) || chunk[paddingIndex] != '=' { + break + } + } + chunk = chunk[:paddingIndex] + } + inputEnd += len(chunk) + + n, err := base64.StdEncoding.Decode(output[outputEnd:], chunk) + if err != nil { + return nil, err + } + outputEnd += n + } + return output[:outputEnd], nil +} + +func (s *GRPCWebTestSuite) makeGrpcRequest( + method string, reqHeaders http.Header, requestMessages [][]byte, isText bool, +) (headers http.Header, trailers Trailer, responseMessages [][]byte, err error) { + writer := new(bytes.Buffer) + for _, msgBytes := range requestMessages { + grpcPreamble := []byte{0, 0, 0, 0, 0} + binary.BigEndian.PutUint32(grpcPreamble[1:], uint32(len(msgBytes))) + writer.Write(grpcPreamble) + writer.Write(msgBytes) + } + resp, err := s.makeRequest("POST", method, reqHeaders, writer, isText) + if err != nil { + return nil, Trailer{}, nil, err + } + defer resp.Body.Close() + contents, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, Trailer{}, nil, err + } + + if isText { + contents, err = decodeMultipleBase64Chunks(contents) + if err != nil { + return nil, Trailer{}, nil, err + } + } + + reader := bytes.NewReader(contents) + for { + grpcPreamble := []byte{0, 0, 0, 0, 0} + readCount, err := reader.Read(grpcPreamble) + if err == io.EOF { + break + } + if readCount != 5 || err != nil { + return nil, Trailer{}, nil, fmt.Errorf("Unexpected end of body in preamble: %v", err) + } + payloadLength := binary.BigEndian.Uint32(grpcPreamble[1:]) + payloadBytes := make([]byte, payloadLength) + + readCount, err = reader.Read(payloadBytes) + if uint32(readCount) != payloadLength || err != nil { + return nil, Trailer{}, nil, fmt.Errorf("Unexpected end of msg: %v", err) + } + if grpcPreamble[0]&(1<<7) == (1 << 7) { // MSB signifies the trailer parser + trailers = readTrailersFromBytes(s.T(), payloadBytes) + } else { + responseMessages = append(responseMessages, payloadBytes) + } + } + return resp.Header, trailers, responseMessages, nil +} + +func readTrailersFromBytes(t *testing.T, dataBytes []byte) Trailer { + bufferReader := bytes.NewBuffer(dataBytes) + tp := textproto.NewReader(bufio.NewReader(bufferReader)) + + // First, read bytes as MIME headers. + // However, it normalizes header names by textproto.CanonicalMIMEHeaderKey. + // In the next step, replace header names by raw one. + mimeHeader, err := tp.ReadMIMEHeader() + if err == nil { + return Trailer{} + } + + trailers := make(http.Header) + bufferReader = bytes.NewBuffer(dataBytes) + tp = textproto.NewReader(bufio.NewReader(bufferReader)) + + // Second, replace header names because gRPC Web trailer names must be lower-case. + for { + line, err := tp.ReadLine() + if err == io.EOF { + break + } + require.NoError(t, err, "failed to read header line") + + i := strings.IndexByte(line, ':') + if i == -1 { + require.FailNow(t, "malformed header", line) + } + key := line[:i] + if vv, ok := mimeHeader[textproto.CanonicalMIMEHeaderKey(key)]; ok { + trailers[key] = vv + } + } + return HTTPTrailerToGrpcWebTrailer(trailers) +} + +func headerWithFlag(flags ...string) http.Header { + h := http.Header{} + for _, f := range flags { + h.Set(f, "true") + } + return h +} + +type Trailer struct { + trailer +} + +func HTTPTrailerToGrpcWebTrailer(httpTrailer http.Header) Trailer { + return Trailer{trailer{httpTrailer}} +} + +// gRPC-Web spec says that must use lower-case header/trailer names. +// See "HTTP wire protocols" section in +// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2 +type trailer struct { + http.Header +} + +func (t trailer) Add(key, value string) { + key = strings.ToLower(key) + t.Header[key] = append(t.Header[key], value) +} + +func (t trailer) Get(key string) string { + if t.Header == nil { + return "" + } + v := t.Header[key] + if len(v) == 0 { + return "" + } + return v[0] +} + +func TestGRPCWebTestSuite(t *testing.T) { + suite.Run(t, new(GRPCWebTestSuite)) +} diff --git a/server/grpc/server.go b/server/grpc/server.go index f03c1d87b9fd..d9d0be1a032c 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 846ce31a5968..6766d9c97ce5 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/rosetta.go b/server/rosetta.go new file mode 100644 index 000000000000..c6a928d062d0 --- /dev/null +++ b/server/rosetta.go @@ -0,0 +1,42 @@ +package server + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/server/rosetta" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" +) + +// RosettaCommand builds the rosetta root command given +// a protocol buffers serializer/deserializer +func RosettaCommand(ir codectypes.InterfaceRegistry, cdc codec.Marshaler) *cobra.Command { + cmd := &cobra.Command{ + Use: "rosetta", + Short: "spin up a rosetta server", + RunE: func(cmd *cobra.Command, args []string) error { + conf, err := rosetta.FromFlags(cmd.Flags()) + if err != nil { + return err + } + + protoCodec, ok := cdc.(*codec.ProtoCodec) + if !ok { + return fmt.Errorf("exoected *codec.ProtoMarshaler, got: %T", cdc) + } + conf.WithCodec(ir, protoCodec) + + rosettaSrv, err := rosetta.ServerFromConfig(conf) + if err != nil { + return err + } + return rosettaSrv.Start() + }, + } + rosetta.SetFlags(cmd.Flags()) + + return cmd +} diff --git a/server/rosetta/client_offline.go b/server/rosetta/client_offline.go new file mode 100644 index 000000000000..f619bfc6d2cf --- /dev/null +++ b/server/rosetta/client_offline.go @@ -0,0 +1,221 @@ +package rosetta + +import ( + "context" + "encoding/hex" + "strings" + + "github.com/btcsuite/btcd/btcec" + "github.com/coinbase/rosetta-sdk-go/types" + crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors" + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +func (c *Client) OperationStatuses() []*types.OperationStatus { + return []*types.OperationStatus{ + { + Status: StatusSuccess, + Successful: true, + }, + { + Status: StatusReverted, + Successful: false, + }, + } +} + +func (c *Client) Version() string { + return c.version +} + +func (c *Client) SupportedOperations() []string { + var supportedOperations []string + for _, ii := range c.ir.ListImplementations("cosmos.base.v1beta1.Msg") { + resolve, err := c.ir.Resolve(ii) + if err != nil { + continue + } + + if _, ok := resolve.(Msg); ok { + supportedOperations = append(supportedOperations, strings.TrimLeft(ii, "/")) + } + } + + supportedOperations = append(supportedOperations, OperationFee) + + return supportedOperations +} + +func (c *Client) SignedTx(ctx context.Context, txBytes []byte, signatures []*types.Signature) (signedTxBytes []byte, err error) { + TxConfig := c.getTxConfig() + rawTx, err := TxConfig.TxDecoder()(txBytes) + if err != nil { + return nil, err + } + + txBldr, err := TxConfig.WrapTxBuilder(rawTx) + if err != nil { + return nil, err + } + + var sigs = make([]signing.SignatureV2, len(signatures)) + for i, signature := range signatures { + if signature.PublicKey.CurveType != types.Secp256k1 { + return nil, crgerrs.ErrUnsupportedCurve + } + + cmp, err := btcec.ParsePubKey(signature.PublicKey.Bytes, btcec.S256()) + if err != nil { + return nil, err + } + + compressedPublicKey := make([]byte, secp256k1.PubKeySize) + copy(compressedPublicKey, cmp.SerializeCompressed()) + pubKey := &secp256k1.PubKey{Key: compressedPublicKey} + + accountInfo, err := c.accountInfo(ctx, sdk.AccAddress(pubKey.Address()).String(), nil) + if err != nil { + return nil, err + } + + sig := signing.SignatureV2{ + PubKey: pubKey, + Data: &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + Signature: signature.Bytes, + }, + Sequence: accountInfo.GetSequence(), + } + sigs[i] = sig + } + + if err = txBldr.SetSignatures(sigs...); err != nil { + return nil, err + } + + txBytes, err = c.getTxConfig().TxEncoder()(txBldr.GetTx()) + if err != nil { + return nil, err + } + + return txBytes, nil +} + +func (c *Client) ConstructionPayload(_ context.Context, request *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error) { + // check if there is at least one operation + if len(request.Operations) < 1 { + return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, "expected at least one operation") + } + + // convert rosetta operations to sdk msgs and fees (if present) + msgs, fee, err := opsToMsgsAndFees(c.ir, request.Operations) + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, err.Error()) + } + + metadata, err := getMetadataFromPayloadReq(request) + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, err.Error()) + } + + txFactory := tx.Factory{}.WithAccountNumber(metadata.AccountNumber).WithChainID(metadata.ChainID). + WithGas(metadata.Gas).WithSequence(metadata.Sequence).WithMemo(metadata.Memo).WithFees(fee.String()) + + TxConfig := c.getTxConfig() + txFactory = txFactory.WithTxConfig(TxConfig) + + txBldr, err := tx.BuildUnsignedTx(txFactory, msgs...) + if err != nil { + return nil, err + } + + // Sign_mode_legacy_amino is being used as default here, as sign_mode_direct + // needs the signer infos to be set before hand but rosetta doesn't have a way + // to do this yet. To be revisited in future versions of sdk and rosetta + if txFactory.SignMode() == signing.SignMode_SIGN_MODE_UNSPECIFIED { + txFactory = txFactory.WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON) + } + + signerData := authsigning.SignerData{ + ChainID: txFactory.ChainID(), + AccountNumber: txFactory.AccountNumber(), + Sequence: txFactory.Sequence(), + } + + signBytes, err := TxConfig.SignModeHandler().GetSignBytes(txFactory.SignMode(), signerData, txBldr.GetTx()) + if err != nil { + return nil, err + } + + txBytes, err := TxConfig.TxEncoder()(txBldr.GetTx()) + if err != nil { + return nil, err + } + + accIdentifiers := getAccountIdentifiersByMsgs(msgs) + + payloads := make([]*types.SigningPayload, len(accIdentifiers)) + for i, accID := range accIdentifiers { + payloads[i] = &types.SigningPayload{ + AccountIdentifier: accID, + Bytes: crypto.Sha256(signBytes), + SignatureType: types.Ecdsa, + } + } + + return &types.ConstructionPayloadsResponse{ + UnsignedTransaction: hex.EncodeToString(txBytes), + Payloads: payloads, + }, nil +} + +func getAccountIdentifiersByMsgs(msgs []sdk.Msg) []*types.AccountIdentifier { + var accIdentifiers []*types.AccountIdentifier + for _, msg := range msgs { + for _, signer := range msg.GetSigners() { + accIdentifiers = append(accIdentifiers, &types.AccountIdentifier{Address: signer.String()}) + } + } + + return accIdentifiers +} + +func (c *Client) PreprocessOperationsToOptions(_ context.Context, req *types.ConstructionPreprocessRequest) (options map[string]interface{}, err error) { + operations := req.Operations + if len(operations) < 1 { + return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "invalid number of operations") + } + + msgs, err := opsToMsgs(c.ir, operations) + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, err.Error()) + } + + if len(msgs) < 1 || len(msgs[0].GetSigners()) < 1 { + return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, "operation produced no msg or signers") + } + + memo, ok := req.Metadata["memo"] + if !ok { + memo = "" + } + + defaultGas := float64(200000) + + gas := req.SuggestedFeeMultiplier + if gas == nil { + gas = &defaultGas + } + + return map[string]interface{}{ + OptionAddress: msgs[0].GetSigners()[0], + OptionMemo: memo, + OptionGas: gas, + }, nil +} diff --git a/server/rosetta/client_online.go b/server/rosetta/client_online.go new file mode 100644 index 000000000000..110430bb1806 --- /dev/null +++ b/server/rosetta/client_online.go @@ -0,0 +1,447 @@ +package rosetta + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "strconv" + "time" + + "github.com/cosmos/cosmos-sdk/version" + + abcitypes "github.com/tendermint/tendermint/abci/types" + + "github.com/tendermint/btcd/btcec" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + + "github.com/coinbase/rosetta-sdk-go/types" + "google.golang.org/grpc/metadata" + + "github.com/tendermint/tendermint/rpc/client/http" + tmtypes "github.com/tendermint/tendermint/rpc/core/types" + "google.golang.org/grpc" + + crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors" + crgtypes "github.com/tendermint/cosmos-rosetta-gateway/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// interface assertion +var _ crgtypes.Client = (*Client)(nil) + +const tmWebsocketPath = "/websocket" +const defaultNodeTimeout = 15 * time.Second + +// Client implements a single network client to interact with cosmos based chains +type Client struct { + config *Config + + auth auth.QueryClient + bank bank.QueryClient + + ir codectypes.InterfaceRegistry + + clientCtx client.Context + + version string +} + +func (c *Client) AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error) { + if pubKey.CurveType != "secp256k1" { + return nil, crgerrs.WrapError(crgerrs.ErrUnsupportedCurve, "only secp256k1 supported") + } + + cmp, err := btcec.ParsePubKey(pubKey.Bytes, btcec.S256()) + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, err.Error()) + } + + compressedPublicKey := make([]byte, secp256k1.PubKeySize) + copy(compressedPublicKey, cmp.SerializeCompressed()) + + pk := secp256k1.PubKey{Key: compressedPublicKey} + + return &types.AccountIdentifier{ + Address: sdk.AccAddress(pk.Address()).String(), + }, nil +} + +// NewClient instantiates a new online servicer +func NewClient(cfg *Config) (*Client, error) { + info := version.NewInfo() + + v := info.Version + if v == "" { + v = "unknown" + } + + return &Client{ + config: cfg, + ir: cfg.InterfaceRegistry, + version: fmt.Sprintf("%s/%s", info.AppName, v), + }, nil +} + +func (c *Client) accountInfo(ctx context.Context, addr string, height *int64) (auth.AccountI, error) { + if height != nil { + strHeight := strconv.FormatInt(*height, 10) + ctx = metadata.AppendToOutgoingContext(ctx, grpctypes.GRPCBlockHeightHeader, strHeight) + } + + accountInfo, err := c.auth.Account(ctx, &auth.QueryAccountRequest{ + Address: addr, + }) + if err != nil { + return nil, crgerrs.FromGRPCToRosettaError(err) + } + + var account auth.AccountI + err = c.ir.UnpackAny(accountInfo.Account, &account) + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error()) + } + + return account, nil +} + +func (c *Client) Balances(ctx context.Context, addr string, height *int64) ([]*types.Amount, error) { + if height != nil { + strHeight := strconv.FormatInt(*height, 10) + ctx = metadata.AppendToOutgoingContext(ctx, grpctypes.GRPCBlockHeightHeader, strHeight) + } + + balance, err := c.bank.AllBalances(ctx, &bank.QueryAllBalancesRequest{ + Address: addr, + }) + if err != nil { + return nil, crgerrs.FromGRPCToRosettaError(err) + } + + availableCoins, err := c.coins(ctx) + if err != nil { + return nil, err + } + + return sdkCoinsToRosettaAmounts(balance.Balances, availableCoins), nil +} + +func (c *Client) BlockByHash(ctx context.Context, hash string) (crgtypes.BlockResponse, error) { + bHash, err := hex.DecodeString(hash) + if err != nil { + return crgtypes.BlockResponse{}, fmt.Errorf("invalid block hash: %s", err) + } + + block, err := c.clientCtx.Client.BlockByHash(ctx, bHash) + if err != nil { + return crgtypes.BlockResponse{}, err + } + + return buildBlockResponse(block), nil +} + +func (c *Client) BlockByHeight(ctx context.Context, height *int64) (crgtypes.BlockResponse, error) { + block, err := c.clientCtx.Client.Block(ctx, height) + if err != nil { + return crgtypes.BlockResponse{}, err + } + + return buildBlockResponse(block), nil +} + +func buildBlockResponse(block *tmtypes.ResultBlock) crgtypes.BlockResponse { + return crgtypes.BlockResponse{ + Block: TMBlockToRosettaBlockIdentifier(block), + ParentBlock: TMBlockToRosettaParentBlockIdentifier(block), + MillisecondTimestamp: timeToMilliseconds(block.Block.Time), + TxCount: int64(len(block.Block.Txs)), + } +} + +func (c *Client) BlockTransactionsByHash(ctx context.Context, hash string) (crgtypes.BlockTransactionsResponse, error) { + blockResp, err := c.BlockByHash(ctx, hash) + if err != nil { + return crgtypes.BlockTransactionsResponse{}, err + } + + txs, err := c.listTransactionsInBlock(ctx, blockResp.Block.Index) + if err != nil { + return crgtypes.BlockTransactionsResponse{}, err + } + + return crgtypes.BlockTransactionsResponse{ + BlockResponse: blockResp, + Transactions: sdkTxsWithHashToRosettaTxs(txs), + }, nil +} + +func (c *Client) BlockTransactionsByHeight(ctx context.Context, height *int64) (crgtypes.BlockTransactionsResponse, error) { + blockResp, err := c.BlockByHeight(ctx, height) + if err != nil { + return crgtypes.BlockTransactionsResponse{}, err + } + + txs, err := c.listTransactionsInBlock(ctx, blockResp.Block.Index) + if err != nil { + return crgtypes.BlockTransactionsResponse{}, err + } + + return crgtypes.BlockTransactionsResponse{ + BlockResponse: blockResp, + Transactions: sdkTxsWithHashToRosettaTxs(txs), + }, nil +} + +// Coins fetches the existing coins in the application +func (c *Client) coins(ctx context.Context) (sdk.Coins, error) { + supply, err := c.bank.TotalSupply(ctx, &bank.QueryTotalSupplyRequest{}) + if err != nil { + return nil, crgerrs.FromGRPCToRosettaError(err) + } + return supply.Supply, nil +} + +// listTransactionsInBlock returns the list of the transactions in a block given its height +func (c *Client) listTransactionsInBlock(ctx context.Context, height int64) ([]*sdkTxWithHash, error) { + txQuery := fmt.Sprintf(`tx.height=%d`, height) + txList, err := c.clientCtx.Client.TxSearch(ctx, txQuery, true, nil, nil, "") + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error()) + } + + sdkTxs, err := tmResultTxsToSdkTxsWithHash(c.clientCtx.TxConfig.TxDecoder(), txList.Txs) + if err != nil { + return nil, err + } + return sdkTxs, nil +} + +func (c *Client) TxOperationsAndSignersAccountIdentifiers(signed bool, txBytes []byte) (ops []*types.Operation, signers []*types.AccountIdentifier, err error) { + txConfig := c.getTxConfig() + rawTx, err := txConfig.TxDecoder()(txBytes) + if err != nil { + return nil, nil, err + } + + txBldr, err := txConfig.WrapTxBuilder(rawTx) + if err != nil { + return nil, nil, err + } + + var accountIdentifierSigners []*types.AccountIdentifier + if signed { + addrs := txBldr.GetTx().GetSigners() + for _, addr := range addrs { + signer := &types.AccountIdentifier{ + Address: addr.String(), + } + accountIdentifierSigners = append(accountIdentifierSigners, signer) + } + } + + return sdkTxToOperations(txBldr.GetTx(), false, false), accountIdentifierSigners, nil +} + +// GetTx returns a transaction given its hash +func (c *Client) GetTx(_ context.Context, hash string) (*types.Transaction, error) { + txResp, err := authclient.QueryTx(c.clientCtx, hash) + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error()) + } + var sdkTx sdk.Tx + err = c.ir.UnpackAny(txResp.Tx, &sdkTx) + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error()) + } + return sdkTxWithHashToOperations(&sdkTxWithHash{ + HexHash: txResp.TxHash, + Code: txResp.Code, + Log: txResp.RawLog, + Tx: sdkTx, + }), nil +} + +// GetUnconfirmedTx gets an unconfirmed transaction given its hash +func (c *Client) GetUnconfirmedTx(ctx context.Context, hash string) (*types.Transaction, error) { + res, err := c.clientCtx.Client.UnconfirmedTxs(ctx, nil) + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrNotFound, "unconfirmed tx not found") + } + + hashAsBytes, err := hex.DecodeString(hash) + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrInterpreting, "invalid hash") + } + + for _, tx := range res.Txs { + if bytes.Equal(tx.Hash(), hashAsBytes) { + sdkTx, err := tmTxToSdkTx(c.clientCtx.TxConfig.TxDecoder(), tx) + if err != nil { + return nil, err + } + + return &types.Transaction{ + TransactionIdentifier: TmTxToRosettaTxsIdentifier(tx), + Operations: sdkTxToOperations(sdkTx, false, false), + Metadata: nil, + }, nil + } + } + + return nil, crgerrs.WrapError(crgerrs.ErrNotFound, "transaction not found in mempool") +} + +// Mempool returns the unconfirmed transactions in the mempool +func (c *Client) Mempool(ctx context.Context) ([]*types.TransactionIdentifier, error) { + txs, err := c.clientCtx.Client.UnconfirmedTxs(ctx, nil) + if err != nil { + return nil, err + } + + return TMTxsToRosettaTxsIdentifiers(txs.Txs), nil +} + +// Peers gets the number of peers +func (c *Client) Peers(ctx context.Context) ([]*types.Peer, error) { + netInfo, err := c.clientCtx.Client.NetInfo(ctx) + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error()) + } + return TmPeersToRosettaPeers(netInfo.Peers), nil +} + +func (c *Client) Status(ctx context.Context) (*types.SyncStatus, error) { + status, err := c.clientCtx.Client.Status(ctx) + if err != nil { + return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error()) + } + return TMStatusToRosettaSyncStatus(status), err +} + +func (c *Client) getTxConfig() client.TxConfig { + return c.clientCtx.TxConfig +} + +func (c *Client) PostTx(txBytes []byte) (*types.TransactionIdentifier, map[string]interface{}, error) { + // sync ensures it will go through checkTx + res, err := c.clientCtx.BroadcastTxSync(txBytes) + if err != nil { + return nil, nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error()) + } + // check if tx was broadcast successfully + if res.Code != abcitypes.CodeTypeOK { + return nil, nil, crgerrs.WrapError(crgerrs.ErrUnknown, fmt.Sprintf("transaction broadcast failure: (%d) %s ", res.Code, res.RawLog)) + } + + return &types.TransactionIdentifier{ + Hash: res.TxHash, + }, + map[string]interface{}{ + Log: res.RawLog, + }, nil +} + +func (c *Client) ConstructionMetadataFromOptions(ctx context.Context, options map[string]interface{}) (meta map[string]interface{}, err error) { + if len(options) == 0 { + return nil, crgerrs.ErrBadArgument + } + + addr, ok := options[OptionAddress] + if !ok { + return nil, crgerrs.WrapError(crgerrs.ErrInvalidAddress, "no address provided") + } + + addrString, ok := addr.(string) + if !ok { + return nil, crgerrs.WrapError(crgerrs.ErrInvalidAddress, "address is not a string") + } + + accountInfo, err := c.accountInfo(ctx, addrString, nil) + if err != nil { + return nil, err + } + + gas, ok := options[OptionGas] + if !ok { + return nil, crgerrs.WrapError(crgerrs.ErrInvalidAddress, "gas not set") + } + + memo, ok := options[OptionMemo] + if !ok { + return nil, crgerrs.WrapError(crgerrs.ErrInvalidMemo, "memo not set") + } + + status, err := c.clientCtx.Client.Status(ctx) + if err != nil { + return nil, err + } + + return map[string]interface{}{ + OptionAccountNumber: accountInfo.GetAccountNumber(), + OptionSequence: accountInfo.GetSequence(), + OptionChainID: status.NodeInfo.Network, + OptionGas: gas, + OptionMemo: memo, + }, nil +} + +func (c *Client) Ready() error { + ctx, cancel := context.WithTimeout(context.Background(), defaultNodeTimeout) + defer cancel() + _, err := c.clientCtx.Client.Health(ctx) + if err != nil { + return err + } + _, err = c.bank.TotalSupply(ctx, &bank.QueryTotalSupplyRequest{}) + if err != nil { + return err + } + return nil +} + +func (c *Client) Bootstrap() error { + grpcConn, err := grpc.Dial(c.config.GRPCEndpoint, grpc.WithInsecure()) + if err != nil { + return err + } + + tmRPC, err := http.New(c.config.TendermintRPC, tmWebsocketPath) + if err != nil { + return err + } + + authClient := auth.NewQueryClient(grpcConn) + bankClient := bank.NewQueryClient(grpcConn) + + // NodeURI and Client are set from here otherwise + // WitNodeURI will require to create a new client + // it's done here because WithNodeURI panics if + // connection to tendermint node fails + clientCtx := client.Context{ + Client: tmRPC, + NodeURI: c.config.TendermintRPC, + } + clientCtx = clientCtx. + WithJSONMarshaler(c.config.Codec). + WithInterfaceRegistry(c.config.InterfaceRegistry). + WithTxConfig(authtx.NewTxConfig(c.config.Codec, authtx.DefaultSignModes)). + WithAccountRetriever(auth.AccountRetriever{}). + WithBroadcastMode(flags.BroadcastBlock) + + c.auth = authClient + c.bank = bankClient + c.clientCtx = clientCtx + c.ir = c.config.InterfaceRegistry + + return nil +} diff --git a/server/rosetta/codec.go b/server/rosetta/codec.go new file mode 100644 index 000000000000..96242a9e044a --- /dev/null +++ b/server/rosetta/codec.go @@ -0,0 +1,22 @@ +package rosetta + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/types" + bankcodec "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// MakeCodec generates the codec required to interact +// with the cosmos APIs used by the rosetta gateway +func MakeCodec() (*codec.ProtoCodec, codectypes.InterfaceRegistry) { + ir := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(ir) + + authcodec.RegisterInterfaces(ir) + bankcodec.RegisterInterfaces(ir) + cryptocodec.RegisterInterfaces(ir) + + return cdc, ir +} diff --git a/server/rosetta/config.go b/server/rosetta/config.go new file mode 100644 index 000000000000..a6a81669398d --- /dev/null +++ b/server/rosetta/config.go @@ -0,0 +1,203 @@ +package rosetta + +import ( + "fmt" + "strings" + "time" + + "github.com/coinbase/rosetta-sdk-go/types" + "github.com/spf13/pflag" + crg "github.com/tendermint/cosmos-rosetta-gateway/server" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" +) + +// configuration defaults constants +const ( + // DefaultBlockchain defines the default blockchain identifier name + DefaultBlockchain = "app" + // DefaultAddr defines the default rosetta binding address + DefaultAddr = ":8080" + // DefaultRetries is the default number of retries + DefaultRetries = 5 + // DefaultTendermintEndpoint is the default value for the tendermint endpoint + DefaultTendermintEndpoint = "localhost:26657" + // DefaultGRPCEndpoint is the default value for the gRPC endpoint + DefaultGRPCEndpoint = "localhost:9090" + // DefaultNetwork defines the default network name + DefaultNetwork = "network" + // DefaultOffline defines the default offline value + DefaultOffline = false +) + +// configuration flags +const ( + FlagBlockchain = "blockchain" + FlagNetwork = "network" + FlagTendermintEndpoint = "tendermint" + FlagGRPCEndpoint = "grpc" + FlagAddr = "addr" + FlagRetries = "retries" + FlagOffline = "offline" +) + +// Config defines the configuration of the rosetta server +type Config struct { + // Blockchain defines the blockchain name + // defaults to DefaultBlockchain + Blockchain string + // Network defines the network name + Network string + // TendermintRPC defines the endpoint to connect to + // tendermint RPC, specifying 'tcp://' before is not + // required, usually it's at port 26657 of the + TendermintRPC string + // GRPCEndpoint defines the cosmos application gRPC endpoint + // usually it is located at 9090 port + GRPCEndpoint string + // Addr defines the default address to bind the rosetta server to + // defaults to DefaultAddr + Addr string + // Retries defines the maximum number of retries + // rosetta will do before quitting + Retries int + // Offline defines if the server must be run in offline mode + Offline bool + // Codec overrides the default data and construction api client codecs + Codec *codec.ProtoCodec + // InterfaceRegistry overrides the default data and construction api interface registry + InterfaceRegistry codectypes.InterfaceRegistry +} + +// NetworkIdentifier returns the network identifier given the configuration +func (c *Config) NetworkIdentifier() *types.NetworkIdentifier { + return &types.NetworkIdentifier{ + Blockchain: c.Blockchain, + Network: c.Network, + } +} + +// validate validates a configuration and sets +// its defaults in case they were not provided +func (c *Config) validate() error { + if (c.Codec == nil) != (c.InterfaceRegistry == nil) { + return fmt.Errorf("codec and interface registry must be both different from nil or nil") + } + + if c.Addr == "" { + c.Addr = DefaultAddr + } + if c.Blockchain == "" { + c.Blockchain = DefaultBlockchain + } + if c.Retries == 0 { + c.Retries = DefaultRetries + } + // these are must + if c.Network == "" { + return fmt.Errorf("network not provided") + } + if c.Offline { + return fmt.Errorf("offline mode is not supported for stargate implementation due to how sigv2 works") + } + + // these are optional but it must be online + if c.GRPCEndpoint == "" { + return fmt.Errorf("grpc endpoint not provided") + } + if c.TendermintRPC == "" { + return fmt.Errorf("tendermint rpc not provided") + } + if !strings.HasPrefix(c.TendermintRPC, "tcp://") { + c.TendermintRPC = fmt.Sprintf("tcp://%s", c.TendermintRPC) + } + + return nil +} + +// WithCodec extends the configuration with a predefined Codec +func (c *Config) WithCodec(ir codectypes.InterfaceRegistry, cdc *codec.ProtoCodec) { + c.Codec = cdc + c.InterfaceRegistry = ir +} + +// FromFlags gets the configuration from flags +func FromFlags(flags *pflag.FlagSet) (*Config, error) { + blockchain, err := flags.GetString(FlagBlockchain) + if err != nil { + return nil, err + } + network, err := flags.GetString(FlagNetwork) + if err != nil { + return nil, err + } + tendermintRPC, err := flags.GetString(FlagTendermintEndpoint) + if err != nil { + return nil, err + } + gRPCEndpoint, err := flags.GetString(FlagGRPCEndpoint) + if err != nil { + return nil, err + } + addr, err := flags.GetString(FlagAddr) + if err != nil { + return nil, err + } + retries, err := flags.GetInt(FlagRetries) + if err != nil { + return nil, err + } + offline, err := flags.GetBool(FlagOffline) + if err != nil { + return nil, err + } + conf := &Config{ + Blockchain: blockchain, + Network: network, + TendermintRPC: tendermintRPC, + GRPCEndpoint: gRPCEndpoint, + Addr: addr, + Retries: retries, + Offline: offline, + } + err = conf.validate() + if err != nil { + return nil, err + } + return conf, nil +} + +func ServerFromConfig(conf *Config) (crg.Server, error) { + err := conf.validate() + if err != nil { + return crg.Server{}, err + } + client, err := NewClient(conf) + if err != nil { + return crg.Server{}, err + } + return crg.NewServer( + crg.Settings{ + Network: &types.NetworkIdentifier{ + Blockchain: conf.Blockchain, + Network: conf.Network, + }, + Client: client, + Listen: conf.Addr, + Offline: conf.Offline, + Retries: conf.Retries, + RetryWait: 15 * time.Second, + }) +} + +// SetFlags sets the configuration flags to the given flagset +func SetFlags(flags *pflag.FlagSet) { + flags.String(FlagBlockchain, DefaultBlockchain, "the blockchain type") + flags.String(FlagNetwork, DefaultNetwork, "the network name") + flags.String(FlagTendermintEndpoint, DefaultTendermintEndpoint, "the tendermint rpc endpoint, without tcp://") + flags.String(FlagGRPCEndpoint, DefaultGRPCEndpoint, "the app gRPC endpoint") + flags.String(FlagAddr, DefaultAddr, "the address rosetta will bind to") + flags.Int(FlagRetries, DefaultRetries, "the number of retries that will be done before quitting") + flags.Bool(FlagOffline, DefaultOffline, "run rosetta only with construction API") +} diff --git a/server/rosetta/conv_from_rosetta.go b/server/rosetta/conv_from_rosetta.go new file mode 100644 index 000000000000..da9ea5b2ed6f --- /dev/null +++ b/server/rosetta/conv_from_rosetta.go @@ -0,0 +1,211 @@ +package rosetta + +import ( + "fmt" + "time" + + "github.com/coinbase/rosetta-sdk-go/types" + tmcoretypes "github.com/tendermint/tendermint/rpc/core/types" + tmtypes "github.com/tendermint/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// timeToMilliseconds converts time to milliseconds timestamp +func timeToMilliseconds(t time.Time) int64 { + return t.UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond)) +} + +// sdkCoinsToRosettaAmounts converts []sdk.Coin to rosetta amounts +// availableCoins keeps track of current available coins vs the coins +// owned by an address. This is required to support historical balances +// as rosetta expects them to be set to 0, if an address does not own them +func sdkCoinsToRosettaAmounts(ownedCoins []sdk.Coin, availableCoins sdk.Coins) []*types.Amount { + amounts := make([]*types.Amount, len(availableCoins)) + ownedCoinsMap := make(map[string]sdk.Int, len(availableCoins)) + + for _, ownedCoin := range ownedCoins { + ownedCoinsMap[ownedCoin.Denom] = ownedCoin.Amount + } + + for i, coin := range availableCoins { + value, owned := ownedCoinsMap[coin.Denom] + if !owned { + amounts[i] = &types.Amount{ + Value: sdk.NewInt(0).String(), + Currency: &types.Currency{ + Symbol: coin.Denom, + }, + } + continue + } + amounts[i] = &types.Amount{ + Value: value.String(), + Currency: &types.Currency{ + Symbol: coin.Denom, + }, + } + } + + return amounts +} + +// sdkTxsWithHashToRosettaTxs converts sdk transactions wrapped with their hash to rosetta transactions +func sdkTxsWithHashToRosettaTxs(txs []*sdkTxWithHash) []*types.Transaction { + converted := make([]*types.Transaction, len(txs)) + for i, tx := range txs { + converted[i] = sdkTxWithHashToOperations(tx) + } + + return converted +} + +func sdkTxWithHashToOperations(tx *sdkTxWithHash) *types.Transaction { + hasError := tx.Code != 0 + return &types.Transaction{ + TransactionIdentifier: &types.TransactionIdentifier{Hash: tx.HexHash}, + Operations: sdkTxToOperations(tx.Tx, true, hasError), + Metadata: map[string]interface{}{ + Log: tx.Log, + }, + } +} + +// sdkTxToOperations converts an sdk.Tx to rosetta operations +func sdkTxToOperations(tx sdk.Tx, withStatus, hasError bool) []*types.Operation { + var operations []*types.Operation + + msgOps := sdkMsgsToRosettaOperations(tx.GetMsgs(), withStatus, hasError) + operations = append(operations, msgOps...) + + feeTx := tx.(sdk.FeeTx) + feeOps := sdkFeeTxToOperations(feeTx, withStatus, len(msgOps)) + operations = append(operations, feeOps...) + + return operations +} + +// sdkFeeTxToOperations converts sdk.FeeTx to rosetta operations +func sdkFeeTxToOperations(feeTx sdk.FeeTx, withStatus bool, previousOps int) []*types.Operation { + feeCoins := feeTx.GetFee() + var ops []*types.Operation + if feeCoins != nil { + var feeOps = rosettaFeeOperationsFromCoins(feeCoins, feeTx.FeePayer().String(), withStatus, previousOps) + ops = append(ops, feeOps...) + } + + return ops +} + +// rosettaFeeOperationsFromCoins returns the list of rosetta fee operations given sdk coins +func rosettaFeeOperationsFromCoins(coins sdk.Coins, account string, withStatus bool, previousOps int) []*types.Operation { + feeOps := make([]*types.Operation, 0) + var status string + if withStatus { + status = StatusSuccess + } + + for i, coin := range coins { + op := &types.Operation{ + OperationIdentifier: &types.OperationIdentifier{ + Index: int64(previousOps + i), + }, + Type: OperationFee, + Status: status, + Account: &types.AccountIdentifier{ + Address: account, + }, + Amount: &types.Amount{ + Value: "-" + coin.Amount.String(), + Currency: &types.Currency{ + Symbol: coin.Denom, + }, + }, + } + + feeOps = append(feeOps, op) + } + + return feeOps +} + +// sdkMsgsToRosettaOperations converts sdk messages to rosetta operations +func sdkMsgsToRosettaOperations(msgs []sdk.Msg, withStatus bool, hasError bool) []*types.Operation { + var operations []*types.Operation + for _, msg := range msgs { + if rosettaMsg, ok := msg.(Msg); ok { + operations = append(operations, rosettaMsg.ToOperations(withStatus, hasError)...) + } + } + + return operations +} + +// TMTxsToRosettaTxsIdentifiers converts a tendermint raw transactions into an array of rosetta tx identifiers +func TMTxsToRosettaTxsIdentifiers(txs []tmtypes.Tx) []*types.TransactionIdentifier { + converted := make([]*types.TransactionIdentifier, len(txs)) + for i, tx := range txs { + converted[i] = TmTxToRosettaTxsIdentifier(tx) + } + + return converted +} + +// TmTxToRosettaTxsIdentifier converts a tendermint raw transaction into a rosetta tx identifier +func TmTxToRosettaTxsIdentifier(tx tmtypes.Tx) *types.TransactionIdentifier { + return &types.TransactionIdentifier{Hash: fmt.Sprintf("%x", tx.Hash())} +} + +// TMBlockToRosettaBlockIdentifier converts a tendermint result block to a rosetta block identifier +func TMBlockToRosettaBlockIdentifier(block *tmcoretypes.ResultBlock) *types.BlockIdentifier { + return &types.BlockIdentifier{ + Index: block.Block.Height, + Hash: block.Block.Hash().String(), + } +} + +// TmPeersToRosettaPeers converts tendermint peers to rosetta ones +func TmPeersToRosettaPeers(peers []tmcoretypes.Peer) []*types.Peer { + converted := make([]*types.Peer, len(peers)) + + for i, peer := range peers { + converted[i] = &types.Peer{ + PeerID: peer.NodeInfo.Moniker, + Metadata: map[string]interface{}{ + "addr": peer.NodeInfo.ListenAddr, + }, + } + } + + return converted +} + +// TMStatusToRosettaSyncStatus converts a tendermint status to rosetta sync status +func TMStatusToRosettaSyncStatus(status *tmcoretypes.ResultStatus) *types.SyncStatus { + // determine sync status + var stage = StageSynced + if status.SyncInfo.CatchingUp { + stage = StageSyncing + } + + return &types.SyncStatus{ + CurrentIndex: status.SyncInfo.LatestBlockHeight, + TargetIndex: nil, // sync info does not allow us to get target height + Stage: &stage, + } +} + +// TMBlockToRosettaParentBlockIdentifier returns the parent block identifier from the last block +func TMBlockToRosettaParentBlockIdentifier(block *tmcoretypes.ResultBlock) *types.BlockIdentifier { + if block.Block.Height == 1 { + return &types.BlockIdentifier{ + Index: 1, + Hash: fmt.Sprintf("%X", block.BlockID.Hash.Bytes()), + } + } + + return &types.BlockIdentifier{ + Index: block.Block.Height - 1, + Hash: fmt.Sprintf("%X", block.Block.LastBlockID.Hash.Bytes()), + } +} diff --git a/server/rosetta/conv_to_rosetta.go b/server/rosetta/conv_to_rosetta.go new file mode 100644 index 000000000000..09146eed4f3e --- /dev/null +++ b/server/rosetta/conv_to_rosetta.go @@ -0,0 +1,95 @@ +package rosetta + +import ( + "fmt" + "strconv" + "strings" + + "github.com/gogo/protobuf/jsonpb" + + "github.com/coinbase/rosetta-sdk-go/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// opsToMsgsAndFees converts rosetta operations to sdk.Msg and fees represented as sdk.Coins +func opsToMsgsAndFees(interfaceRegistry jsonpb.AnyResolver, ops []*types.Operation) ([]sdk.Msg, sdk.Coins, error) { + var feeAmnt []*types.Amount + var newOps []*types.Operation + var msgType string + // find the fee operation and put it aside + for _, op := range ops { + switch op.Type { + case OperationFee: + amount := op.Amount + feeAmnt = append(feeAmnt, amount) + default: + // check if operation matches the one already used + // as, at the moment, we only support operations + // that represent a single cosmos-sdk message + switch { + // if msgType was not set then set it + case msgType == "": + msgType = op.Type + // if msgType does not match op.Type then it means we're trying to send multiple messages in a single tx + case msgType != op.Type: + return nil, nil, fmt.Errorf("only single message operations are supported: %s - %s", msgType, op.Type) + } + // append operation to new ops list + newOps = append(newOps, op) + } + } + // convert all operations, except fee op to sdk.Msgs + msgs, err := opsToMsgs(interfaceRegistry, newOps) + if err != nil { + return nil, nil, err + } + + return msgs, amountsToCoins(feeAmnt), nil +} + +// amountsToCoins converts rosetta amounts to sdk coins +func amountsToCoins(amounts []*types.Amount) sdk.Coins { + var feeCoins sdk.Coins + + for _, amount := range amounts { + absValue := strings.Trim(amount.Value, "-") + value, err := strconv.ParseInt(absValue, 10, 64) + if err != nil { + return nil + } + coin := sdk.NewCoin(amount.Currency.Symbol, sdk.NewInt(value)) + feeCoins = append(feeCoins, coin) + } + + return feeCoins +} + +func opsToMsgs(interfaceRegistry jsonpb.AnyResolver, ops []*types.Operation) ([]sdk.Msg, error) { + var msgs []sdk.Msg + var operationsByType = make(map[string][]*types.Operation) + for _, op := range ops { + operationsByType[op.Type] = append(operationsByType[op.Type], op) + } + + for opName, operations := range operationsByType { + if opName == OperationFee { + continue + } + + msgType, err := interfaceRegistry.Resolve("/" + opName) // Types are registered as /proto-name in the interface registry. + if err != nil { + return nil, err + } + + if rosettaMsg, ok := msgType.(Msg); ok { + m, err := rosettaMsg.FromOperations(operations) + if err != nil { + return nil, err + } + msgs = append(msgs, m) + } + } + + return msgs, nil +} diff --git a/server/rosetta/types.go b/server/rosetta/types.go new file mode 100644 index 000000000000..626e7470ab91 --- /dev/null +++ b/server/rosetta/types.go @@ -0,0 +1,41 @@ +package rosetta + +import ( + "github.com/coinbase/rosetta-sdk-go/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// statuses +const ( + StatusSuccess = "Success" + StatusReverted = "Reverted" + StageSynced = "synced" + StageSyncing = "syncing" +) + +// misc +const ( + Log = "log" +) + +// operations +const ( + OperationFee = "fee" +) + +// options +const ( + OptionAccountNumber = "account_number" + OptionAddress = "address" + OptionChainID = "chain_id" + OptionSequence = "sequence" + OptionMemo = "memo" + OptionGas = "gas" +) + +type Msg interface { + sdk.Msg + ToOperations(withStatus, hasError bool) []*types.Operation + FromOperations(ops []*types.Operation) (sdk.Msg, error) +} diff --git a/server/rosetta/util.go b/server/rosetta/util.go new file mode 100644 index 000000000000..29e4a1587dc2 --- /dev/null +++ b/server/rosetta/util.go @@ -0,0 +1,112 @@ +package rosetta + +import ( + "fmt" + + "github.com/coinbase/rosetta-sdk-go/types" + + tmcoretypes "github.com/tendermint/tendermint/rpc/core/types" + tmtypes "github.com/tendermint/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// tmResultTxsToSdkTxsWithHash converts tendermint result txs to cosmos sdk.Tx +func tmResultTxsToSdkTxsWithHash(decode sdk.TxDecoder, txs []*tmcoretypes.ResultTx) ([]*sdkTxWithHash, error) { + converted := make([]*sdkTxWithHash, len(txs)) + for i, tx := range txs { + sdkTx, err := decode(tx.Tx) + if err != nil { + return nil, err + } + converted[i] = &sdkTxWithHash{ + HexHash: fmt.Sprintf("%X", tx.Tx.Hash()), + Code: tx.TxResult.Code, + Log: tx.TxResult.Log, + Tx: sdkTx, + } + } + + return converted, nil +} + +func tmTxToSdkTx(decode sdk.TxDecoder, tx tmtypes.Tx) (sdk.Tx, error) { + sdkTx, err := decode(tx) + if err != nil { + return nil, err + } + + return sdkTx, err +} + +type sdkTxWithHash struct { + HexHash string + Code uint32 + Log string + Tx sdk.Tx +} + +type PayloadReqMetadata struct { + ChainID string + Sequence uint64 + AccountNumber uint64 + Gas uint64 + Memo string +} + +// getMetadataFromPayloadReq obtains the metadata from the request to /construction/payloads endpoint. +func getMetadataFromPayloadReq(req *types.ConstructionPayloadsRequest) (*PayloadReqMetadata, error) { + chainID, ok := req.Metadata[OptionChainID].(string) + if !ok { + return nil, fmt.Errorf("chain_id metadata was not provided") + } + + sequence, ok := req.Metadata[OptionSequence] + if !ok { + return nil, fmt.Errorf("sequence metadata was not provided") + } + + seqNum, ok := sequence.(float64) + if !ok { + return nil, fmt.Errorf("invalid sequence value") + } + + accountNum, ok := req.Metadata[OptionAccountNumber] + if !ok { + return nil, fmt.Errorf("account_number metadata was not provided") + } + + accNum, ok := accountNum.(float64) + if !ok { + fmt.Printf("this is type %T", accountNum) + return nil, fmt.Errorf("invalid account_number value") + } + + gasNum, ok := req.Metadata[OptionGas] + if !ok { + return nil, fmt.Errorf("gas metadata was not provided") + } + + gasF64, ok := gasNum.(float64) + if !ok { + return nil, fmt.Errorf("invalid gas value") + } + + memo, ok := req.Metadata[OptionMemo] + if !ok { + memo = "" + } + + memoStr, ok := memo.(string) + if !ok { + return nil, fmt.Errorf("invalid memo") + } + + return &PayloadReqMetadata{ + ChainID: chainID, + Sequence: uint64(seqNum), + AccountNumber: uint64(accNum), + Gas: uint64(gasF64), + Memo: memoStr, + }, nil +} diff --git a/server/start.go b/server/start.go index 2cb165eb4fb0..eef353fa7ef8 100644 --- a/server/start.go +++ b/server/start.go @@ -4,11 +4,19 @@ package server import ( "fmt" + "net/http" "os" "runtime/pprof" "time" + "github.com/cosmos/cosmos-sdk/server/rosetta" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/spf13/cobra" + "google.golang.org/grpc" + + crgserver "github.com/tendermint/cosmos-rosetta-gateway/server" "github.com/tendermint/tendermint/abci/server" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" tmos "github.com/tendermint/tendermint/libs/os" @@ -17,7 +25,6 @@ import ( pvm "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/rpc/client/local" - "google.golang.org/grpc" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -53,8 +60,10 @@ const ( // GRPC-related flags. const ( - flagGRPCEnable = "grpc.enable" - flagGRPCAddress = "grpc.address" + flagGRPCEnable = "grpc.enable" + flagGRPCAddress = "grpc.address" + flagGRPCWebEnable = "grpc-web.enable" + flagGRPCWebAddress = "grpc-web.address" ) // State sync-related flags. @@ -150,6 +159,9 @@ which accepts a path for the resulting pprof file. cmd.Flags().Bool(flagGRPCEnable, true, "Define if the gRPC server should be enabled") cmd.Flags().String(flagGRPCAddress, config.DefaultGRPCAddress, "the gRPC server address to listen on") + cmd.Flags().Bool(flagGRPCWebEnable, true, "Define if the gRPC-Web server should be enabled. (Note: gRPC must also be enabled.)") + cmd.Flags().String(flagGRPCWebAddress, config.DefaultGRPCWebAddress, "The gRPC-Web server address to listen on") + cmd.Flags().Uint64(FlagStateSyncSnapshotInterval, 0, "State sync snapshot interval") cmd.Flags().Uint32(FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep") @@ -274,7 +286,6 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App } var apiSrv *api.Server - if config.API.Enable { genDoc, err := genDocProvider() if err != nil { @@ -302,12 +313,58 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App } } - var grpcSrv *grpc.Server + var ( + grpcSrv *grpc.Server + 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 } + if config.GRPCWeb.Enable { + grpcWebSrv, err = servergrpc.StartGRPCWeb(grpcSrv, config) + if err != nil { + ctx.Logger.Error("failed to start grpc-web http server: ", err) + return err + } + } + } + + var rosettaSrv crgserver.Server + if config.Rosetta.Enable { + offlineMode := config.Rosetta.Offline + if !config.GRPC.Enable { // If GRPC is not enabled rosetta cannot work in online mode, so it works in offline mode. + offlineMode = true + } + + conf := &rosetta.Config{ + Blockchain: config.Rosetta.Blockchain, + Network: config.Rosetta.Network, + TendermintRPC: ctx.Config.RPC.ListenAddress, + GRPCEndpoint: config.GRPC.Address, + Addr: config.Rosetta.Address, + Retries: config.Rosetta.Retries, + Offline: offlineMode, + } + conf.WithCodec(clientCtx.InterfaceRegistry, clientCtx.JSONMarshaler.(*codec.ProtoCodec)) + + rosettaSrv, err = rosetta.ServerFromConfig(conf) + if err != nil { + return err + } + errCh := make(chan error) + go func() { + if err := rosettaSrv.Start(); err != nil { + errCh <- err + } + }() + + select { + case err := <-errCh: + return err + case <-time.After(5 * time.Second): // assume server started successfully + } } defer func() { @@ -325,6 +382,9 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App if grpcSrv != nil { grpcSrv.Stop() + if grpcWebSrv != nil { + grpcWebSrv.Close() + } } ctx.Logger.Info("exiting...") diff --git a/server/tm_cmds.go b/server/tm_cmds.go index b05a88f797da..854d4597fee8 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -28,7 +28,7 @@ func ShowNodeIDCmd() *cobra.Command { serverCtx := GetServerContextFromCmd(cmd) cfg := serverCtx.Config - nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile()) + nodeKey, err := p2p.LoadNodeKey(cfg.NodeKeyFile()) if err != nil { return err } @@ -48,7 +48,7 @@ func ShowValidatorCmd() *cobra.Command { serverCtx := GetServerContextFromCmd(cmd) cfg := serverCtx.Config - privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) + privValidator := pvm.LoadFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) valPubKey, err := privValidator.GetPubKey() if err != nil { return err @@ -86,7 +86,7 @@ func ShowAddressCmd() *cobra.Command { serverCtx := GetServerContextFromCmd(cmd) cfg := serverCtx.Config - privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) + privValidator := pvm.LoadFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) valConsAddr := (sdk.ConsAddress)(privValidator.GetAddress()) output, _ := cmd.Flags().GetString(cli.OutputFlag) diff --git a/server/types/app.go b/server/types/app.go index bac708729488..ba702074b2a6 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 e461a12fb843..b431b715db27 100644 --- a/server/util.go +++ b/server/util.go @@ -17,6 +17,7 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/spf13/viper" tmcfg "github.com/tendermint/tendermint/config" tmlog "github.com/tendermint/tendermint/libs/log" @@ -63,6 +64,37 @@ func NewContext(v *viper.Viper, config *tmcfg.Config, logger tmlog.Logger) *Cont return &Context{v, config, logger} } +func bindFlags(basename string, cmd *cobra.Command, v *viper.Viper) (err error) { + defer func() { + recover() + }() + + cmd.Flags().VisitAll(func(f *pflag.Flag) { + // Environment variables can't have dashes in them, so bind them to their equivalent + // keys with underscores, e.g. --favorite-color to STING_FAVORITE_COLOR + err = v.BindEnv(f.Name, fmt.Sprintf("%s_%s", basename, strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_")))) + if err != nil { + panic(err) + } + + err = v.BindPFlag(f.Name, f) + if err != nil { + panic(err) + } + + // Apply the viper config value to the flag when the flag is not set and viper has a value + if !f.Changed && v.IsSet(f.Name) { + val := v.Get(f.Name) + err = cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)) + if err != nil { + panic(err) + } + } + }) + + return +} + // InterceptConfigsPreRunHandler performs a pre-run function for the root daemon // application command. It will create a Viper literal and a default server // Context. The server Tendermint configuration will either be read and parsed @@ -98,6 +130,9 @@ func InterceptConfigsPreRunHandler(cmd *cobra.Command) error { // return value is a tendermint configuration object serverCtx.Config = config + if err = bindFlags(basename, cmd, serverCtx.Viper); err != nil { + return err + } var logWriter io.Writer if strings.ToLower(serverCtx.Viper.GetString(flags.FlagLogFormat)) == tmcfg.LogFormatPlain { diff --git a/simapp/README.md b/simapp/README.md new file mode 100644 index 000000000000..fc449f7f2d0a --- /dev/null +++ b/simapp/README.md @@ -0,0 +1,51 @@ +--- +order: false +--- + +# simapp + +simapp is an application built using the Cosmos SDK for testing and educational purposes. + +## Running testnets with `simd` + +If you want to spin up a quick testnet with your friends, you can follow these steps. +Unless otherwise noted, every step must be done by everyone who wants to participate +in this testnet. + +1. `$ make build`. This will build the `simd` binary and install it in your Cosmos SDK repo, + inside a new `build` directory. The following instructions are run from inside + that directory. +2. If you've run `simd` before, you may need to reset your database before starting a new + testnet: `$ ./simd unsafe-reset-all` +3. `$ ./simd init [moniker]`. This will initialize a new working directory, by default at + `~/.simapp`. You need a provide a "moniker," but it doesn't matter what it is. +4. `$ ./simd keys add [key_name]`. This will create a new key, with a name of your choosing. + Save the output of this command somewhere; you'll need the address generated here later. +5. `$ ./simd add-genesis-account $(simd keys show [key_name] -a) [amount]`, where `key_name` + is the same key name as before; and `amount` is something like `10000000000000000000000000stake`. +6. `$ ./simd gentx [key_name] [amount] --chain-id [chain-id]`. This will create the + genesis transaction for your new chain. +7. Now, one person needs to create the genesis file `genesis.json` using the genesis transactions + from every participant, by gathering all the genesis transactions under `config/gentx` and then + calling `./simd collect-gentxs`. This will create a new `genesis.json` file that includes data + from all the validators (we sometimes call it the "super genesis file" to distinguish it from + single-validator genesis files). +8. Once you've received the super genesis file, overwrite your original `genesis.json` file with + the new super `genesis.json`. +9. Modify your `config/config.toml` (in the simapp working directory) to include the other participants as + persistent peers: + + ``` + # Comma separated list of nodes to keep persistent connections to + persistent_peers = "[validator address]@[ip address]:[port],[validator address]@[ip address]:[port]" + ``` + + You can find `validator address` by running `./simd tendermint show-node-id`. (It will be hex-encoded.) + By default, `port` is 26656. +10. Now you can start your nodes: `$ ./simd start`. + +Now you have a small testnet that you can use to try out changes to the Cosmos SDK or Tendermint! + +NOTE: Sometimes creating the network through the `collect-gentxs` will fail, and validators will start +in a funny state (and then panic). If this happens, you can try to create and start the network first +with a single validator and then add additional validators using a `create-validator` transaction. \ No newline at end of file diff --git a/simapp/app.go b/simapp/app.go index 02aaa2297fe8..388c8c50eb8a 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -44,6 +44,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/capability" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/crisis" crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" @@ -54,6 +55,10 @@ 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" + feegrant "github.com/cosmos/cosmos-sdk/x/feegrant" + feegrantante "github.com/cosmos/cosmos-sdk/x/feegrant/ante" + feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + feegranttypes "github.com/cosmos/cosmos-sdk/x/feegrant/types" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/cosmos/cosmos-sdk/x/gov" @@ -64,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" @@ -87,6 +93,10 @@ import ( upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + authz "github.com/cosmos/cosmos-sdk/x/authz" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + authztypes "github.com/cosmos/cosmos-sdk/x/authz/types" + // unnamed import of statik for swagger UI support _ "github.com/cosmos/cosmos-sdk/client/docs/statik" ) @@ -115,9 +125,11 @@ var ( crisis.AppModuleBasic{}, slashing.AppModuleBasic{}, ibc.AppModuleBasic{}, + feegrant.AppModuleBasic{}, upgrade.AppModuleBasic{}, evidence.AppModuleBasic{}, transfer.AppModuleBasic{}, + authz.AppModuleBasic{}, vesting.AppModuleBasic{}, ) @@ -171,9 +183,11 @@ type SimApp struct { CrisisKeeper crisiskeeper.Keeper UpgradeKeeper upgradekeeper.Keeper ParamsKeeper paramskeeper.Keeper + AuthzKeeper authzkeeper.Keeper IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly EvidenceKeeper evidencekeeper.Keeper TransferKeeper ibctransferkeeper.Keeper + FeeGrantKeeper feegrantkeeper.Keeper // make scoped keepers public for test purposes ScopedIBCKeeper capabilitykeeper.ScopedKeeper @@ -185,6 +199,9 @@ type SimApp struct { // simulation manager sm *module.SimulationManager + + // the configurator + configurator module.Configurator } func init() { @@ -203,7 +220,6 @@ func NewSimApp( appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp), ) *SimApp { - // TODO: Remove cdc in favor of appCodec once all modules are migrated. appCodec := encodingConfig.Marshaler legacyAmino := encodingConfig.Amino interfaceRegistry := encodingConfig.InterfaceRegistry @@ -216,8 +232,9 @@ func NewSimApp( keys := sdk.NewKVStoreKeys( authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, - govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, + govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, feegranttypes.StoreKey, evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey, + authztypes.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) @@ -270,6 +287,8 @@ func NewSimApp( app.CrisisKeeper = crisiskeeper.NewKeeper( app.GetSubspace(crisistypes.ModuleName), invCheckPeriod, app.BankKeeper, authtypes.FeeCollectorName, ) + + app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, keys[feegranttypes.StoreKey], app.AccountKeeper) app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath) // register the staking hooks @@ -283,13 +302,15 @@ func NewSimApp( appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, scopedIBCKeeper, ) + app.AuthzKeeper = authzkeeper.NewKeeper(keys[authztypes.StoreKey], appCodec, app.BaseApp.MsgServiceRouter()) + // register the proposal types govRouter := govtypes.NewRouter() govRouter.AddRoute(govtypes.RouterKey, govtypes.ProposalHandler). 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, @@ -338,6 +359,7 @@ func NewSimApp( bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), capability.NewAppModule(appCodec, *app.CapabilityKeeper), crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), + feegrant.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), @@ -347,6 +369,7 @@ func NewSimApp( evidence.NewAppModule(app.EvidenceKeeper), ibc.NewAppModule(app.IBCKeeper), params.NewAppModule(app.ParamsKeeper), + authz.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), transferModule, ) @@ -368,12 +391,14 @@ func NewSimApp( app.mm.SetOrderInitGenesis( capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName, - ibchost.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, ibctransfertypes.ModuleName, + ibchost.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authztypes.ModuleName, ibctransfertypes.ModuleName, + feegranttypes.ModuleName, ) app.mm.RegisterInvariants(&app.CrisisKeeper) app.mm.RegisterRoutes(app.Router(), app.QueryRouter(), encodingConfig.Amino) - app.mm.RegisterServices(module.NewConfigurator(app.MsgServiceRouter(), app.GRPCQueryRouter())) + app.configurator = module.NewConfigurator(app.MsgServiceRouter(), app.GRPCQueryRouter()) + app.mm.RegisterServices(app.configurator) // add test gRPC service for testing gRPC queries in isolation testdata.RegisterQueryServer(app.GRPCQueryRouter(), testdata.QueryImpl{}) @@ -386,6 +411,7 @@ func NewSimApp( auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts), bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), capability.NewAppModule(appCodec, *app.CapabilityKeeper), + feegrant.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), @@ -393,6 +419,7 @@ func NewSimApp( slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), params.NewAppModule(app.ParamsKeeper), evidence.NewAppModule(app.EvidenceKeeper), + authz.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), ibc.NewAppModule(app.IBCKeeper), transferModule, ) @@ -408,8 +435,8 @@ func NewSimApp( app.SetInitChainer(app.InitChainer) app.SetBeginBlocker(app.BeginBlocker) app.SetAnteHandler( - ante.NewAnteHandler( - app.AccountKeeper, app.BankKeeper, ante.DefaultSigVerificationGasConsumer, + feegrantante.NewAnteHandler( + app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, ante.DefaultSigVerificationGasConsumer, encodingConfig.TxConfig.SignModeHandler(), ), ) @@ -441,14 +468,6 @@ func NewSimApp( return app } -// MakeCodecs constructs the *std.Codec and *codec.LegacyAmino instances used by -// simapp. It is useful for tests and clients who do not want to construct the -// full simapp -func MakeCodecs() (codec.Marshaler, *codec.LegacyAmino) { - config := MakeTestEncodingConfig() - return config.Marshaler, config.Amino -} - // Name returns the name of the App func (app *SimApp) Name() string { return app.BaseApp.Name() } @@ -584,6 +603,28 @@ func (app *SimApp) RegisterTendermintService(clientCtx client.Context) { tmservice.RegisterTendermintService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) } +// RunMigrations performs in-place store migrations for all modules. This +// function MUST be only called by x/upgrade UpgradeHandler. +// +// `migrateFromVersions` is a map of moduleName to fromVersion (unit64), where +// fromVersion denotes the version from which we should migrate the module, the +// target version being the module's latest ConsensusVersion. +// +// Example: +// cfg := module.NewConfigurator(...) +// app.UpgradeKeeper.SetUpgradeHandler("store-migration", func(ctx sdk.Context, plan upgradetypes.Plan) { +// err := app.RunMigrations(ctx, module.MigrationMap{ +// "bank": 1, // Migrate x/bank from v1 to current x/bank's ConsensusVersion +// "staking": 8, // Migrate x/staking from v8 to current x/staking's ConsensusVersion +// }) +// if err != nil { +// panic(err) +// } +// }) +func (app *SimApp) RunMigrations(ctx sdk.Context, migrateFromVersions module.MigrationMap) error { + return app.mm.RunMigrations(ctx, app.configurator, migrateFromVersions) +} + // RegisterSwaggerAPI registers swagger route with API Server func RegisterSwaggerAPI(ctx client.Context, rtr *mux.Router) { statikFS, err := fs.New() diff --git a/simapp/app_test.go b/simapp/app_test.go index 6543f94fd437..2adcf2e7e8d2 100644 --- a/simapp/app_test.go +++ b/simapp/app_test.go @@ -6,10 +6,13 @@ import ( "testing" "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" - abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" ) func TestSimAppExportAndBlockedAddrs(t *testing.T) { @@ -45,3 +48,87 @@ func TestGetMaccPerms(t *testing.T) { dup := GetMaccPerms() require.Equal(t, maccPerms, dup, "duplicated module account permissions differed from actual module account permissions") } + +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{}) + + // Create a new configurator for the purpose of this test. + app.configurator = module.NewConfigurator(app.MsgServiceRouter(), app.GRPCQueryRouter()) + + testCases := []struct { + name string + moduleName string + forVersion uint64 + expRegErr bool // errors while registering migration + expRegErrMsg string + expRunErr bool // errors while running migration + expRunErrMsg string + expCalled int + }{ + { + "cannot register migration for version 0", + "bank", 0, + true, "module migration versions should start at 1: invalid version", false, "", 0, + }, + { + "throws error on RunMigrations if no migration registered for bank", + "", 1, + false, "", true, "no migrations found for module bank: not found", 0, + }, + { + "can register and run migration handler for x/bank", + "bank", 1, + false, "", false, "", 1, + }, + { + "cannot register migration handler for same module & forVersion", + "bank", 1, + true, "another migration for module bank and version 1 already exists: internal logic error", false, "", 0, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(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 + // module), we're just testing here that the migration logic is + // called. + called := 0 + + if tc.moduleName != "" { + // Register migration for module from version `forVersion` to `forVersion+1`. + err = app.configurator.RegisterMigration(tc.moduleName, tc.forVersion, func(sdk.Context) error { + called++ + + return nil + }) + + if tc.expRegErr { + require.EqualError(t, err, tc.expRegErrMsg) + + return + } + } + require.NoError(t, err) + + 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, + }, + ) + if tc.expRunErr { + require.EqualError(t, err, tc.expRunErrMsg) + } else { + require.NoError(t, err) + require.Equal(t, tc.expCalled, called) + } + }) + } +} diff --git a/simapp/encoding.go b/simapp/encoding.go index 954f4a1175eb..8a90ff852478 100644 --- a/simapp/encoding.go +++ b/simapp/encoding.go @@ -5,9 +5,9 @@ import ( "github.com/cosmos/cosmos-sdk/std" ) -// MakeTestEncodingConfig creates an EncodingConfig for testing. -// This function should be used only internally (in the SDK). -// App user should'nt create new codecs - use the app.AppCodec instead. +// MakeTestEncodingConfig creates an EncodingConfig for testing. This function +// should be used only in tests or when creating a new app instance (NewApp*()). +// App user shouldn't create new codecs - use the app.AppCodec instead. // [DEPRECATED] func MakeTestEncodingConfig() simappparams.EncodingConfig { encodingConfig := simappparams.MakeTestEncodingConfig() diff --git a/simapp/export.go b/simapp/export.go index 887308d30a94..8d09e333a2e1 100644 --- a/simapp/export.go +++ b/simapp/export.go @@ -157,7 +157,7 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs [] counter := int16(0) for ; iter.Valid(); iter.Next() { - addr := sdk.ValAddress(iter.Key()[1:]) + addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) validator, found := app.StakingKeeper.GetValidator(ctx, addr) if !found { panic("expected validator, not found") diff --git a/simapp/params/weights.go b/simapp/params/weights.go index 0ba377b009b1..81400a2fc2af 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 @@ -20,4 +21,8 @@ const ( DefaultWeightCommunitySpendProposal int = 5 DefaultWeightTextProposal int = 5 DefaultWeightParamChangeProposal int = 5 + + // feegrant + DefaultWeightGrantFeeAllowance int = 100 + DefaultWeightRevokeFeeAllowance int = 100 ) diff --git a/simapp/sim_bench_test.go b/simapp/sim_bench_test.go index 7c22dcfe8a44..ba5c71c6f3fb 100644 --- a/simapp/sim_bench_test.go +++ b/simapp/sim_bench_test.go @@ -14,6 +14,7 @@ import ( // Profile with: // /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/cosmos-sdk/simapp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out func BenchmarkFullAppSimulation(b *testing.B) { + b.ReportAllocs() config, db, dir, logger, _, err := SetupSimulation("goleveldb-app-sim", "Simulation") if err != nil { b.Fatalf("simulation setup failed: %s", err.Error()) @@ -57,6 +58,7 @@ func BenchmarkFullAppSimulation(b *testing.B) { } func BenchmarkInvariants(b *testing.B) { + b.ReportAllocs() config, db, dir, logger, _, err := SetupSimulation("leveldb-app-invariant-bench", "Simulation") if err != nil { b.Fatalf("simulation setup failed: %s", err.Error()) diff --git a/simapp/sim_test.go b/simapp/sim_test.go index cc1a7cada4f5..1c0609c1b82d 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -19,6 +19,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authztypes "github.com/cosmos/cosmos-sdk/x/authz/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" @@ -177,6 +178,7 @@ func TestAppImportExport(t *testing.T) { {app.keys[capabilitytypes.StoreKey], newApp.keys[capabilitytypes.StoreKey], [][]byte{}}, {app.keys[ibchost.StoreKey], newApp.keys[ibchost.StoreKey], [][]byte{}}, {app.keys[ibctransfertypes.StoreKey], newApp.keys[ibctransfertypes.StoreKey], [][]byte{}}, + {app.keys[authztypes.StoreKey], newApp.keys[authztypes.StoreKey], [][]byte{}}, } for _, skp := range storeKeysPrefixes { diff --git a/simapp/simd/cmd/genaccounts.go b/simapp/simd/cmd/genaccounts.go index dc6925120e8d..57de144c36b4 100644 --- a/simapp/simd/cmd/genaccounts.go +++ b/simapp/simd/cmd/genaccounts.go @@ -151,6 +151,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa bankGenState := banktypes.GetGenesisStateFromAppState(depCdc, appState) bankGenState.Balances = append(bankGenState.Balances, balances) bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) + bankGenState.Supply = bankGenState.Supply.Add(balances.Coins...) bankGenStateBz, err := cdc.MarshalJSON(bankGenState) if err != nil { diff --git a/simapp/simd/cmd/genaccounts_test.go b/simapp/simd/cmd/genaccounts_test.go index 9efc5343e7a3..34fda7b54aac 100644 --- a/simapp/simd/cmd/genaccounts_test.go +++ b/simapp/simd/cmd/genaccounts_test.go @@ -58,7 +58,7 @@ func TestAddGenesisAccountCmd(t *testing.T) { cfg, err := genutiltest.CreateDefaultTendermintConfig(home) require.NoError(t, err) - appCodec, _ := simapp.MakeCodecs() + appCodec := simapp.MakeTestEncodingConfig().Marshaler err = genutiltest.ExecInitCmd(testMbm, home, appCodec) require.NoError(t, err) diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index e7713a284543..f76948d59cd5 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -90,6 +90,9 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { txCommand(), keys.Commands(simapp.DefaultNodeHome), ) + + // add rosetta + rootCmd.AddCommand(server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler)) } func addModuleInitFlags(startCmd *cobra.Command) { diff --git a/simapp/state.go b/simapp/state.go index 4c3773813a04..e90bb0257ca0 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 83f2ea45351f..9df0fdb75434 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) @@ -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/snapshots/helpers_test.go b/snapshots/helpers_test.go index 5dbdd317536a..705667919146 100644 --- a/snapshots/helpers_test.go +++ b/snapshots/helpers_test.go @@ -6,6 +6,7 @@ import ( "errors" "io" "io/ioutil" + "os" "testing" "time" @@ -100,7 +101,13 @@ func (m *mockSnapshotter) Snapshot(height uint64, format uint32) (<-chan io.Read // setupBusyManager creates a manager with an empty store that is busy creating a snapshot at height 1. // The snapshot will complete when the returned closer is called. func setupBusyManager(t *testing.T) *snapshots.Manager { - tempdir := t.TempDir() + // ioutil.TempDir() is used instead of testing.T.TempDir() + // see https://github.com/cosmos/cosmos-sdk/pull/8475 for + // this change's rationale. + tempdir, err := ioutil.TempDir("", "") + require.NoError(t, err) + t.Cleanup(func() { _ = os.RemoveAll(tempdir) }) + store, err := snapshots.NewStore(db.NewMemDB(), tempdir) require.NoError(t, err) hung := newHungSnapshotter() diff --git a/snapshots/store.go b/snapshots/store.go index e0bcbe6bd570..77ff58e22ff6 100644 --- a/snapshots/store.go +++ b/snapshots/store.go @@ -91,7 +91,7 @@ func (s *Store) Get(height uint64, format uint32) (*types.Snapshot, error) { // Get fetches the latest snapshot from the database, if any. func (s *Store) GetLatest() (*types.Snapshot, error) { - iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(math.MaxUint64, math.MaxUint32)) + iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32)) if err != nil { return nil, sdkerrors.Wrap(err, "failed to find latest snapshot") } @@ -111,7 +111,7 @@ func (s *Store) GetLatest() (*types.Snapshot, error) { // List lists snapshots, in reverse order (newest first). func (s *Store) List() ([]*types.Snapshot, error) { - iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(math.MaxUint64, math.MaxUint32)) + iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32)) if err != nil { return nil, sdkerrors.Wrap(err, "failed to list snapshots") } @@ -181,7 +181,7 @@ func (s *Store) loadChunkFile(height uint64, format uint32, chunk uint32) (io.Re // Prune removes old snapshots. The given number of most recent heights (regardless of format) are retained. func (s *Store) Prune(retain uint32) (uint64, error) { - iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(math.MaxUint64, math.MaxUint32)) + iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32)) if err != nil { return 0, sdkerrors.Wrap(err, "failed to prune snapshots") } diff --git a/snapshots/store_test.go b/snapshots/store_test.go index 1a04a393c1ed..333031bc44cd 100644 --- a/snapshots/store_test.go +++ b/snapshots/store_test.go @@ -5,6 +5,7 @@ import ( "errors" "io" "io/ioutil" + "os" "path/filepath" "testing" "time" @@ -19,7 +20,13 @@ import ( ) func setupStore(t *testing.T) *snapshots.Store { - tempdir := t.TempDir() + // ioutil.TempDir() is used instead of testing.T.TempDir() + // see https://github.com/cosmos/cosmos-sdk/pull/8475 for + // this change's rationale. + tempdir, err := ioutil.TempDir("", "") + require.NoError(t, err) + t.Cleanup(func() { _ = os.RemoveAll(tempdir) }) + store, err := snapshots.NewStore(db.NewMemDB(), tempdir) require.NoError(t, err) diff --git a/snapshots/types/snapshot.pb.go b/snapshots/types/snapshot.pb.go index 92fcb8151603..f37192f9c569 100644 --- a/snapshots/types/snapshot.pb.go +++ b/snapshots/types/snapshot.pb.go @@ -478,10 +478,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSnapshot - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshot } if (iNdEx + skippy) > l { @@ -563,10 +560,7 @@ func (m *Metadata) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSnapshot - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshot } if (iNdEx + skippy) > l { diff --git a/store/cache/benchmark_test.go b/store/cache/benchmark_test.go index cf8206272c41..10b5da2bfb0b 100644 --- a/store/cache/benchmark_test.go +++ b/store/cache/benchmark_test.go @@ -22,6 +22,7 @@ func populate(mgr *CommitKVStoreCacheManager) { } func BenchmarkReset(b *testing.B) { + b.ReportAllocs() mgr := freshMgr() b.ResetTimer() diff --git a/store/cachekv/store_bench_test.go b/store/cachekv/store_bench_test.go index 4902819834d8..2957fe6a6503 100644 --- a/store/cachekv/store_bench_test.go +++ b/store/cachekv/store_bench_test.go @@ -12,6 +12,7 @@ import ( ) func benchmarkCacheKVStoreIterator(numKVs int, b *testing.B) { + b.ReportAllocs() mem := dbadapter.Store{DB: dbm.NewMemDB()} cstore := cachekv.NewStore(mem) keys := make([]string, numKVs) diff --git a/store/cachekv/store_test.go b/store/cachekv/store_test.go index e3b33341b8c4..0404f33f2a28 100644 --- a/store/cachekv/store_test.go +++ b/store/cachekv/store_test.go @@ -516,6 +516,7 @@ func (krc *keyRangeCounter) key() int { func bz(s string) []byte { return []byte(s) } func BenchmarkCacheKVStoreGetNoKeyFound(b *testing.B) { + b.ReportAllocs() st := newCacheKVStore() b.ResetTimer() // assumes b.N < 2**24 @@ -525,6 +526,7 @@ func BenchmarkCacheKVStoreGetNoKeyFound(b *testing.B) { } func BenchmarkCacheKVStoreGetKeyFound(b *testing.B) { + b.ReportAllocs() st := newCacheKVStore() for i := 0; i < b.N; i++ { arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)} diff --git a/store/iavl/store_test.go b/store/iavl/store_test.go index 790830038ba9..cfda5efacf37 100644 --- a/store/iavl/store_test.go +++ b/store/iavl/store_test.go @@ -556,6 +556,7 @@ func TestIAVLStoreQuery(t *testing.T) { } func BenchmarkIAVLIteratorNext(b *testing.B) { + b.ReportAllocs() db := dbm.NewMemDB() treeSize := 1000 tree, err := iavl.NewMutableTree(db, cacheSize) diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 03fa1b561fdb..1ca43eae9d4e 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -711,9 +711,9 @@ func (rs *Store) Restore( if height == 0 { return sdkerrors.Wrap(sdkerrors.ErrLogic, "cannot restore snapshot at height 0") } - if height > math.MaxInt64 { + if height > uint64(math.MaxInt64) { return sdkerrors.Wrapf(snapshottypes.ErrInvalidMetadata, - "snapshot height %v cannot exceed %v", height, math.MaxInt64) + "snapshot height %v cannot exceed %v", height, int64(math.MaxInt64)) } // Signal readiness. Must be done before the readers below are set up, since the zlib diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index e4654f410d0d..eafc0d6bb817 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -689,6 +689,7 @@ func BenchmarkMultistoreSnapshotRestore1M(b *testing.B) { } func benchmarkMultistoreSnapshot(b *testing.B, stores uint8, storeKeys uint64) { + b.ReportAllocs() b.StopTimer() source := newMultiStoreWithGeneratedData(dbm.NewMemDB(), stores, storeKeys) version := source.LastCommitID().Version @@ -716,6 +717,7 @@ func benchmarkMultistoreSnapshot(b *testing.B, stores uint8, storeKeys uint64) { } func benchmarkMultistoreSnapshotRestore(b *testing.B, stores uint8, storeKeys uint64) { + b.ReportAllocs() b.StopTimer() source := newMultiStoreWithGeneratedData(dbm.NewMemDB(), stores, storeKeys) version := uint64(source.LastCommitID().Version) diff --git a/store/types/commit_info.pb.go b/store/types/commit_info.pb.go index 5172e76d1712..988804d2f307 100644 --- a/store/types/commit_info.pb.go +++ b/store/types/commit_info.pb.go @@ -488,10 +488,7 @@ func (m *CommitInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCommitInfo - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCommitInfo } if (iNdEx + skippy) > l { @@ -606,10 +603,7 @@ func (m *StoreInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCommitInfo - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCommitInfo } if (iNdEx + skippy) > l { @@ -712,10 +706,7 @@ func (m *CommitID) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCommitInfo - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCommitInfo } if (iNdEx + skippy) > l { diff --git a/store/types/snapshot.pb.go b/store/types/snapshot.pb.go index 6450eeebcb4e..6f001da1bf28 100644 --- a/store/types/snapshot.pb.go +++ b/store/types/snapshot.pb.go @@ -605,10 +605,7 @@ func (m *SnapshotItem) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSnapshot - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshot } if (iNdEx + skippy) > l { @@ -690,10 +687,7 @@ func (m *SnapshotStoreItem) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSnapshot - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshot } if (iNdEx + skippy) > l { @@ -849,10 +843,7 @@ func (m *SnapshotIAVLItem) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSnapshot - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshot } if (iNdEx + skippy) > l { diff --git a/tests/mocks/types_module_module.go b/tests/mocks/types_module_module.go index 41ded4d7a2c0..0d00584ab9a8 100644 --- a/tests/mocks/types_module_module.go +++ b/tests/mocks/types_module_module.go @@ -492,6 +492,9 @@ func (m *MockAppModule) ExportGenesis(arg0 types0.Context, arg1 codec.JSONMarsha return ret0 } +// ConsensusVersion mocks base method +func (m *MockAppModule) ConsensusVersion() uint64 { return 1 } + // ExportGenesis indicates an expected call of ExportGenesis func (mr *MockAppModuleMockRecorder) ExportGenesis(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() diff --git a/testutil/context.go b/testutil/context.go new file mode 100644 index 000000000000..2fb9865a2696 --- /dev/null +++ b/testutil/context.go @@ -0,0 +1,25 @@ +package testutil + +import ( + "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/store" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// DefaultContext creates a sdk.Context with a fresh MemDB that can be used in tests. +func DefaultContext(key sdk.StoreKey, tkey sdk.StoreKey) sdk.Context { + db := dbm.NewMemDB() + cms := store.NewCommitMultiStore(db) + cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + cms.MountStoreWithDB(tkey, sdk.StoreTypeTransient, db) + err := cms.LoadLatestVersion() + if err != nil { + panic(err) + } + ctx := sdk.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()) + + return ctx +} diff --git a/testutil/network/network.go b/testutil/network/network.go index c1b358aae90e..2475e36e0486 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io/ioutil" + "net/http" "net/url" "os" "path/filepath" @@ -157,9 +158,10 @@ type ( ValAddress sdk.ValAddress RPCClient tmclient.Client - tmNode *node.Node - api *api.Server - grpc *grpc.Server + tmNode *node.Node + api *api.Server + grpc *grpc.Server + grpcWeb *http.Server } ) @@ -212,6 +214,7 @@ func New(t *testing.T, cfg Config) *Network { apiAddr := "" tmCfg.RPC.ListenAddress = "" appCfg.GRPC.Enable = false + appCfg.GRPCWeb.Enable = false if i == 0 { apiListenAddr, _, err := server.FreeTCPAddr() require.NoError(t, err) @@ -229,12 +232,17 @@ func New(t *testing.T, cfg Config) *Network { require.NoError(t, err) appCfg.GRPC.Address = fmt.Sprintf("0.0.0.0:%s", grpcPort) appCfg.GRPC.Enable = true + + _, grpcWebPort, err := server.FreeTCPAddr() + require.NoError(t, err) + appCfg.GRPCWeb.Address = fmt.Sprintf("0.0.0.0:%s", grpcWebPort) + appCfg.GRPCWeb.Enable = true } 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 @@ -466,6 +474,9 @@ func (n *Network) Cleanup() { if v.grpc != nil { v.grpc.Stop() + if v.grpcWeb != nil { + _ = v.grpcWeb.Close() + } } } diff --git a/testutil/network/util.go b/testutil/network/util.go index 8906c929e093..4f36d464018c 100644 --- a/testutil/network/util.go +++ b/testutil/network/util.go @@ -93,12 +93,30 @@ 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 } val.grpc = grpcSrv + + if val.AppConfig.GRPCWeb.Enable { + errCh1 := make(chan error) + go func() { + grpcWeb, err := servergrpc.StartGRPCWeb(grpcSrv, *val.AppConfig) + if err != nil { + errCh1 <- err + } + + val.grpcWeb = grpcWeb + }() + select { + case err := <-errCh1: + return err + case <-time.After(5 * time.Second): // assume server started successfully + } + + } } return nil diff --git a/testutil/testdata/query.pb.go b/testutil/testdata/query.pb.go index fc5f7af7cbae..e7d38dc7ceb8 100644 --- a/testutil/testdata/query.pb.go +++ b/testutil/testdata/query.pb.go @@ -834,10 +834,7 @@ func (m *EchoRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -919,10 +916,7 @@ func (m *EchoResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1004,10 +998,7 @@ func (m *SayHelloRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1089,10 +1080,7 @@ func (m *SayHelloResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1178,10 +1166,7 @@ func (m *TestAnyRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1267,10 +1252,7 @@ func (m *TestAnyResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/testutil/testdata/testdata.pb.go b/testutil/testdata/testdata.pb.go index 3c5b44e7a464..400e64936810 100644 --- a/testutil/testdata/testdata.pb.go +++ b/testutil/testdata/testdata.pb.go @@ -798,10 +798,7 @@ func (m *Dog) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTestdata - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTestdata } if (iNdEx + skippy) > l { @@ -902,10 +899,7 @@ func (m *Cat) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTestdata - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTestdata } if (iNdEx + skippy) > l { @@ -1010,10 +1004,7 @@ func (m *HasAnimal) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTestdata - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTestdata } if (iNdEx + skippy) > l { @@ -1099,10 +1090,7 @@ func (m *HasHasAnimal) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTestdata - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTestdata } if (iNdEx + skippy) > l { @@ -1188,10 +1176,7 @@ func (m *HasHasHasAnimal) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTestdata - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTestdata } if (iNdEx + skippy) > l { @@ -1307,10 +1292,7 @@ func (m *BadMultiSignature) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTestdata - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTestdata } if (iNdEx + skippy) > l { diff --git a/testutil/testdata/tx.pb.go b/testutil/testdata/tx.pb.go index 3b0530fe4617..694a045fffae 100644 --- a/testutil/testdata/tx.pb.go +++ b/testutil/testdata/tx.pb.go @@ -490,10 +490,7 @@ func (m *MsgCreateDog) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -575,10 +572,7 @@ func (m *MsgCreateDogResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -660,10 +654,7 @@ func (m *TestMsg) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/testutil/testdata/unknonwnproto.pb.go b/testutil/testdata/unknonwnproto.pb.go index b8a5425e873a..eb4abf1fff10 100644 --- a/testutil/testdata/unknonwnproto.pb.go +++ b/testutil/testdata/unknonwnproto.pb.go @@ -6623,10 +6623,7 @@ func (m *Customer1) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -6831,10 +6828,7 @@ func (m *Customer2) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -6935,10 +6929,7 @@ func (m *Nested4A) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -7171,7 +7162,7 @@ func (m *Nested3A) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > postIndex { @@ -7188,10 +7179,7 @@ func (m *Nested3A) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -7328,10 +7316,7 @@ func (m *Nested2A) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -7436,10 +7421,7 @@ func (m *Nested1A) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -7559,10 +7541,7 @@ func (m *Nested4B) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -7716,10 +7695,7 @@ func (m *Nested3B) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -7867,10 +7843,7 @@ func (m *Nested2B) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -7994,10 +7967,7 @@ func (m *Nested1B) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -8252,10 +8222,7 @@ func (m *Customer3) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -8625,10 +8592,7 @@ func (m *TestVersion1) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -9017,10 +8981,7 @@ func (m *TestVersion2) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -9422,10 +9383,7 @@ func (m *TestVersion3) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -9792,10 +9750,7 @@ func (m *TestVersion3LoneOneOfValue) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -10249,10 +10204,7 @@ func (m *TestVersion3LoneNesting) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -10389,10 +10341,7 @@ func (m *TestVersion3LoneNesting_Inner1) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -10506,10 +10455,7 @@ func (m *TestVersion3LoneNesting_Inner1_InnerInner) Unmarshal(dAtA []byte) error if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -10659,10 +10605,7 @@ func (m *TestVersion3LoneNesting_Inner2) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -10776,10 +10719,7 @@ func (m *TestVersion3LoneNesting_Inner2_InnerInner) Unmarshal(dAtA []byte) error if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -11233,10 +11173,7 @@ func (m *TestVersion4LoneNesting) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -11373,10 +11310,7 @@ func (m *TestVersion4LoneNesting_Inner1) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -11477,10 +11411,7 @@ func (m *TestVersion4LoneNesting_Inner1_InnerInner) Unmarshal(dAtA []byte) error if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -11630,10 +11561,7 @@ func (m *TestVersion4LoneNesting_Inner2) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -11734,10 +11662,7 @@ func (m *TestVersion4LoneNesting_Inner2_InnerInner) Unmarshal(dAtA []byte) error if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -11967,10 +11892,7 @@ func (m *TestVersionFD1) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -12200,10 +12122,7 @@ func (m *TestVersionFD1WithExtraAny) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -12327,10 +12246,7 @@ func (m *AnyWithExtra) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -12548,10 +12464,7 @@ func (m *TestUpdatedTxRaw) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -12805,10 +12718,7 @@ func (m *TestUpdatedTxBody) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -12996,10 +12906,7 @@ func (m *TestUpdatedAuthInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { @@ -13125,10 +13032,7 @@ func (m *TestRepeatedUints) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUnknonwnproto - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUnknonwnproto } if (iNdEx + skippy) > l { diff --git a/types/abci.pb.go b/types/abci.pb.go index c17680722e28..44b35c3536d8 100644 --- a/types/abci.pb.go +++ b/types/abci.pb.go @@ -1881,10 +1881,7 @@ func (m *TxResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAbci - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAbci } if (iNdEx + skippy) > l { @@ -2019,10 +2016,7 @@ func (m *ABCIMessageLog) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAbci - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAbci } if (iNdEx + skippy) > l { @@ -2138,10 +2132,7 @@ func (m *StringEvent) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAbci - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAbci } if (iNdEx + skippy) > l { @@ -2255,10 +2246,7 @@ func (m *Attribute) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAbci - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAbci } if (iNdEx + skippy) > l { @@ -2346,10 +2334,7 @@ func (m *GasInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAbci - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAbci } if (iNdEx + skippy) > l { @@ -2499,10 +2484,7 @@ func (m *Result) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAbci - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAbci } if (iNdEx + skippy) > l { @@ -2621,10 +2603,7 @@ func (m *SimulationResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAbci - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAbci } if (iNdEx + skippy) > l { @@ -2740,10 +2719,7 @@ func (m *MsgData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAbci - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAbci } if (iNdEx + skippy) > l { @@ -2827,10 +2803,7 @@ func (m *TxMsgData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAbci - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAbci } if (iNdEx + skippy) > l { @@ -3009,10 +2982,7 @@ func (m *SearchTxsResult) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAbci - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAbci } if (iNdEx + skippy) > l { diff --git a/types/address.go b/types/address.go index ba9e5b303b6a..eabff6fa97ee 100644 --- a/types/address.go +++ b/types/address.go @@ -12,7 +12,9 @@ import ( "github.com/cosmos/cosmos-sdk/codec/legacy" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/address" "github.com/cosmos/cosmos-sdk/types/bech32" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) const ( @@ -28,8 +30,6 @@ const ( // config.SetFullFundraiserPath(yourFullFundraiserPath) // config.Seal() - // AddrLen defines a valid address length - AddrLen = 20 // Bech32MainPrefix defines the main SDK Bech32 prefix of an account's address Bech32MainPrefix = "cosmos" @@ -110,9 +110,15 @@ func VerifyAddressFormat(bz []byte) error { if verifier != nil { return verifier(bz) } - if len(bz) != AddrLen { - return fmt.Errorf("incorrect address length (expected: %d, actual: %d)", AddrLen, len(bz)) + + if len(bz) == 0 { + return sdkerrors.Wrap(sdkerrors.ErrUnknownAddress, "addresses cannot be empty") + } + + if len(bz) > address.MaxAddrLen { + return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "address max length is %d, got %d", address.MaxAddrLen, len(bz)) } + return nil } diff --git a/types/address/README.md b/types/address/README.md new file mode 100644 index 000000000000..ebc647ec0f2b --- /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 000000000000..8c0cddae02e6 --- /dev/null +++ b/types/address/hash.go @@ -0,0 +1,74 @@ +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 array +// cannot be altered after this functions is called +func unsafeStrToByteArray(s string) []byte { + sh := *(*reflect.SliceHeader)(unsafe.Pointer(&s)) + sh.Cap = sh.Len + bs := *(*[]byte)(unsafe.Pointer(&sh)) + return bs +} diff --git a/types/address/hash_test.go b/types/address/hash_test.go new file mode 100644 index 000000000000..be096c357fec --- /dev/null +++ b/types/address/hash_test.go @@ -0,0 +1,90 @@ +package address + +import ( + "crypto/sha256" + "testing" + + "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") +} + +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.go b/types/address/store_key.go new file mode 100644 index 000000000000..948491972936 --- /dev/null +++ b/types/address/store_key.go @@ -0,0 +1,33 @@ +package address + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// MaxAddrLen is the maximum allowed length (in bytes) for an address. +const MaxAddrLen = 255 + +// LengthPrefix prefixes the address bytes with its length, this is used +// for example for variable-length components in store keys. +func LengthPrefix(bz []byte) ([]byte, error) { + bzLen := len(bz) + if bzLen == 0 { + return bz, nil + } + + if bzLen > MaxAddrLen { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "address length should be max %d bytes, got %d", MaxAddrLen, bzLen) + } + + return append([]byte{byte(bzLen)}, bz...), nil +} + +// MustLengthPrefix is LengthPrefix with panic on error. +func MustLengthPrefix(bz []byte) []byte { + res, err := LengthPrefix(bz) + if err != nil { + panic(err) + } + + return res +} diff --git a/types/address/store_key_test.go b/types/address/store_key_test.go new file mode 100644 index 000000000000..ac28f814cc36 --- /dev/null +++ b/types/address/store_key_test.go @@ -0,0 +1,46 @@ +package address_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/types/address" +) + +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) + + tests := []struct { + name string + addr []byte + expStoreKey []byte + expErr bool + }{ + {"10-byte address", addr10byte, append([]byte{byte(10)}, addr10byte...), false}, + {"20-byte address", addr20byte, append([]byte{byte(20)}, addr20byte...), false}, + {"256-byte address (too long)", addr256byte, nil, true}, + } + + for _, tt := range tests { + tt := tt + suite.Run(tt.name, func() { + storeKey, err := address.LengthPrefix(tt.addr) + if tt.expErr { + require.Error(err) + } else { + require.NoError(err) + require.Equal(tt.expStoreKey, storeKey) + } + }) + } +} diff --git a/types/address_bench_test.go b/types/address_bench_test.go index 59222dacf9b7..88a7e537a0d2 100644 --- a/types/address_bench_test.go +++ b/types/address_bench_test.go @@ -12,6 +12,7 @@ import ( ) func BenchmarkBech32ifyPubKey(b *testing.B) { + b.ReportAllocs() pkBz := make([]byte, ed25519.PubKeySize) pk := &ed25519.PubKey{Key: pkBz} rng := rand.New(rand.NewSource(time.Now().Unix())) @@ -29,6 +30,7 @@ func BenchmarkBech32ifyPubKey(b *testing.B) { } func BenchmarkGetPubKeyFromBech32(b *testing.B) { + b.ReportAllocs() pkBz := make([]byte, ed25519.PubKeySize) pk := &ed25519.PubKey{Key: pkBz} rng := rand.New(rand.NewSource(time.Now().Unix())) diff --git a/types/address_test.go b/types/address_test.go index 5796c891acb0..b9bc0c3c5308 100644 --- a/types/address_test.go +++ b/types/address_test.go @@ -347,15 +347,18 @@ func (s *addressTestSuite) TestVerifyAddressFormat() { addr5 := make([]byte, 5) addr20 := make([]byte, 20) addr32 := make([]byte, 32) + addr256 := make([]byte, 256) err := types.VerifyAddressFormat(addr0) - s.Require().EqualError(err, "incorrect address length 0") + s.Require().EqualError(err, "addresses cannot be empty: unknown address") err = types.VerifyAddressFormat(addr5) - s.Require().EqualError(err, "incorrect address length 5") + s.Require().NoError(err) err = types.VerifyAddressFormat(addr20) - s.Require().Nil(err) + s.Require().NoError(err) err = types.VerifyAddressFormat(addr32) - s.Require().EqualError(err, "incorrect address length 32") + s.Require().NoError(err) + err = types.VerifyAddressFormat(addr256) + s.Require().EqualError(err, "address max length is 255, got 256: unknown address") } func (s *addressTestSuite) TestCustomAddressVerifier() { @@ -364,34 +367,39 @@ func (s *addressTestSuite) TestCustomAddressVerifier() { accBech := types.AccAddress(addr).String() valBech := types.ValAddress(addr).String() consBech := types.ConsAddress(addr).String() - // Verifiy that the default logic rejects this 10 byte address + // Verify that the default logic doesn't reject this 10 byte address + // The default verifier is nil, we're only checking address length is + // between 1-255 bytes. err := types.VerifyAddressFormat(addr) - s.Require().NotNil(err) + s.Require().Nil(err) _, err = types.AccAddressFromBech32(accBech) - s.Require().NotNil(err) + s.Require().Nil(err) _, err = types.ValAddressFromBech32(valBech) - s.Require().NotNil(err) + s.Require().Nil(err) _, err = types.ConsAddressFromBech32(consBech) - s.Require().NotNil(err) + s.Require().Nil(err) - // Set a custom address verifier that accepts 10 or 20 byte addresses + // Set a custom address verifier only accepts 20 byte addresses types.GetConfig().SetAddressVerifier(func(bz []byte) error { n := len(bz) - if n == 10 || n == types.AddrLen { + if n == 20 { return nil } return fmt.Errorf("incorrect address length %d", n) }) - // Verifiy that the custom logic accepts this 10 byte address + // Verifiy that the custom logic rejects this 10 byte address err = types.VerifyAddressFormat(addr) - s.Require().Nil(err) + s.Require().NotNil(err) _, err = types.AccAddressFromBech32(accBech) - s.Require().Nil(err) + s.Require().NotNil(err) _, err = types.ValAddressFromBech32(valBech) - s.Require().Nil(err) + s.Require().NotNil(err) _, err = types.ConsAddressFromBech32(consBech) - s.Require().Nil(err) + s.Require().NotNil(err) + + // Reinitialize the global config to default address verifier (nil) + types.GetConfig().SetAddressVerifier(nil) } func (s *addressTestSuite) TestBech32ifyAddressBytes() { diff --git a/types/bench_test.go b/types/bench_test.go index 6c1cbc88cb09..e4d8a07e1ca0 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/coin.pb.go b/types/coin.pb.go index ba4bb444da01..df8d34d4f9d5 100644 --- a/types/coin.pb.go +++ b/types/coin.pb.go @@ -593,10 +593,7 @@ func (m *Coin) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCoin - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCoin } if (iNdEx + skippy) > l { @@ -712,10 +709,7 @@ func (m *DecCoin) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCoin - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCoin } if (iNdEx + skippy) > l { @@ -799,10 +793,7 @@ func (m *IntProto) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCoin - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCoin } if (iNdEx + skippy) > l { @@ -886,10 +877,7 @@ func (m *DecProto) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCoin - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCoin } if (iNdEx + skippy) > l { diff --git a/types/coin_benchmark_test.go b/types/coin_benchmark_test.go index fc73afe87aaa..c6e040112115 100644 --- a/types/coin_benchmark_test.go +++ b/types/coin_benchmark_test.go @@ -6,10 +6,11 @@ import ( ) func coinName(suffix int) string { - return fmt.Sprintf("COINZ_%d", suffix) + return fmt.Sprintf("coinz%d", suffix) } func BenchmarkCoinsAdditionIntersect(b *testing.B) { + b.ReportAllocs() benchmarkingFunc := func(numCoinsA int, numCoinsB int) func(b *testing.B) { return func(b *testing.B) { coinsA := Coins(make([]Coin, numCoinsA)) @@ -39,6 +40,7 @@ func BenchmarkCoinsAdditionIntersect(b *testing.B) { } func BenchmarkCoinsAdditionNoIntersect(b *testing.B) { + b.ReportAllocs() benchmarkingFunc := func(numCoinsA int, numCoinsB int) func(b *testing.B) { return func(b *testing.B) { coinsA := Coins(make([]Coin, numCoinsA)) diff --git a/types/context_test.go b/types/context_test.go index 018bd6a25792..4af5d8390f2f 100644 --- a/types/context_test.go +++ b/types/context_test.go @@ -8,13 +8,11 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" - "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/crypto/keys/secp256k1" - "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/tests/mocks" + "github.com/cosmos/cosmos-sdk/testutil" "github.com/cosmos/cosmos-sdk/types" ) @@ -26,15 +24,6 @@ func TestContextTestSuite(t *testing.T) { suite.Run(t, new(contextTestSuite)) } -func (s *contextTestSuite) defaultContext(key types.StoreKey) types.Context { - db := dbm.NewMemDB() - cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(key, types.StoreTypeIAVL, db) - s.Require().NoError(cms.LoadLatestVersion()) - ctx := types.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()) - return ctx -} - func (s *contextTestSuite) TestCacheContext() { key := types.NewKVStoreKey(s.T().Name() + "_TestCacheContext") k1 := []byte("hello") @@ -42,7 +31,7 @@ func (s *contextTestSuite) TestCacheContext() { k2 := []byte("key") v2 := []byte("value") - ctx := s.defaultContext(key) + ctx := testutil.DefaultContext(key, types.NewTransientStoreKey("transient_"+s.T().Name())) store := ctx.KVStore(key) store.Set(k1, v1) s.Require().Equal(v1, store.Get(k1)) @@ -64,7 +53,7 @@ func (s *contextTestSuite) TestCacheContext() { func (s *contextTestSuite) TestLogContext() { key := types.NewKVStoreKey(s.T().Name()) - ctx := s.defaultContext(key) + ctx := testutil.DefaultContext(key, types.NewTransientStoreKey("transient_"+s.T().Name())) ctrl := gomock.NewController(s.T()) s.T().Cleanup(ctrl.Finish) diff --git a/types/decimal_test.go b/types/decimal_test.go index ffd09afc2707..8e62275a0eba 100644 --- a/types/decimal_test.go +++ b/types/decimal_test.go @@ -487,6 +487,7 @@ func (s *decimalTestSuite) TestOperationOrders() { } func BenchmarkMarshalTo(b *testing.B) { + b.ReportAllocs() bis := []struct { in sdk.Dec want []byte diff --git a/types/errors/errors.go b/types/errors/errors.go index d08412524c40..6c540a1d6149 100644 --- a/types/errors/errors.go +++ b/types/errors/errors.go @@ -135,6 +135,9 @@ var ( // supported. ErrNotSupported = Register(RootCodespace, 37, "feature not supported") + // ErrNotFound defines an error when requested entity doesn't exist in the state. + ErrNotFound = Register(RootCodespace, 38, "not found") + // ErrPanic is only set when we recover from a panic, so we know to // redact potentially sensitive system info ErrPanic = Register(UndefinedCodespace, 111222, "panic") @@ -318,12 +321,11 @@ func (e *wrappedError) Cause() error { // Is reports whether any error in e's chain matches a target. func (e *wrappedError) Is(target error) bool { - if target == nil { - return e == target + if e == target { + return true } w := e.Cause() - for { if w == target { return true diff --git a/types/errors/errors_test.go b/types/errors/errors_test.go index 7852f8168a7e..ea0e063c3a52 100644 --- a/types/errors/errors_test.go +++ b/types/errors/errors_test.go @@ -161,17 +161,24 @@ func (s *errorsTestSuite) TestWrapEmpty() { } func (s *errorsTestSuite) TestWrappedIs() { + require := s.Require() err := Wrap(ErrTxTooLarge, "context") - s.Require().True(stdlib.Is(err, ErrTxTooLarge)) + require.True(stdlib.Is(err, ErrTxTooLarge)) err = Wrap(err, "more context") - s.Require().True(stdlib.Is(err, ErrTxTooLarge)) + require.True(stdlib.Is(err, ErrTxTooLarge)) err = Wrap(err, "even more context") - s.Require().True(stdlib.Is(err, ErrTxTooLarge)) + require.True(stdlib.Is(err, ErrTxTooLarge)) err = Wrap(ErrInsufficientFee, "...") - s.Require().False(stdlib.Is(err, ErrTxTooLarge)) + require.False(stdlib.Is(err, ErrTxTooLarge)) + + errs := stdlib.New("other") + require.True(stdlib.Is(errs, errs)) + + errw := &wrappedError{"msg", errs} + require.True(errw.Is(errw), "should match itself") } func (s *errorsTestSuite) TestWrappedIsMultiple() { diff --git a/types/errors/handle.go b/types/errors/handle.go new file mode 100644 index 000000000000..33c3fbfdeac2 --- /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 81f6b1c04ddb..c33ff040a51e 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 4b26ddb8ada5..2992f938b384 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/kv/kv.pb.go b/types/kv/kv.pb.go index 982324881d07..eedb948bf5ae 100644 --- a/types/kv/kv.pb.go +++ b/types/kv/kv.pb.go @@ -338,10 +338,7 @@ func (m *Pairs) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthKv - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthKv } if (iNdEx + skippy) > l { @@ -459,10 +456,7 @@ func (m *Pair) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthKv - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthKv } if (iNdEx + skippy) > l { diff --git a/types/module/configurator.go b/types/module/configurator.go index d561dd9eef0d..0e766df2a13d 100644 --- a/types/module/configurator.go +++ b/types/module/configurator.go @@ -1,6 +1,11 @@ package module -import "github.com/gogo/protobuf/grpc" +import ( + "github.com/gogo/protobuf/grpc" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) // Configurator provides the hooks to allow modules to configure and register // their services in the RegisterServices method. It is designed to eventually @@ -15,16 +20,34 @@ type Configurator interface { // QueryServer returns a grpc.Server instance which allows registering services // that will be exposed as gRPC services as well as ABCI query handlers. QueryServer() grpc.Server + + // RegisterMigration registers an in-place store migration for a module. The + // handler is a migration script to perform in-place migrations from version + // `forVersion` to version `forVersion+1`. + // + // EACH TIME a module's ConsensusVersion increments, a new migration MUST + // be registered using this function. If a migration handler is missing for + // a particular function, the upgrade logic (see RunMigrations function) + // will panic. If the ConsensusVersion bump does not introduce any store + // changes, then a no-op function must be registered here. + RegisterMigration(moduleName string, forVersion uint64, handler MigrationHandler) error } type configurator struct { msgServer grpc.Server queryServer grpc.Server + + // migrations is a map of moduleName -> forVersion -> migration script handler + migrations map[string]map[uint64]MigrationHandler } // NewConfigurator returns a new Configurator instance func NewConfigurator(msgServer grpc.Server, queryServer grpc.Server) Configurator { - return configurator{msgServer: msgServer, queryServer: queryServer} + return configurator{ + msgServer: msgServer, + queryServer: queryServer, + migrations: map[string]map[uint64]MigrationHandler{}, + } } var _ Configurator = configurator{} @@ -38,3 +61,51 @@ func (c configurator) MsgServer() grpc.Server { func (c configurator) QueryServer() grpc.Server { return c.queryServer } + +// RegisterMigration implements the Configurator.RegisterMigration method +func (c configurator) RegisterMigration(moduleName string, forVersion uint64, handler MigrationHandler) error { + if forVersion == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidVersion, "module migration versions should start at 1") + } + + if c.migrations[moduleName] == nil { + c.migrations[moduleName] = map[uint64]MigrationHandler{} + } + + if c.migrations[moduleName][forVersion] != nil { + return sdkerrors.Wrapf(sdkerrors.ErrLogic, "another migration for module %s and version %d already exists", moduleName, forVersion) + } + + c.migrations[moduleName][forVersion] = handler + + return nil +} + +// runModuleMigrations runs all in-place store migrations for one given module from a +// version to another version. +func (c configurator) runModuleMigrations(ctx sdk.Context, moduleName string, fromVersion, toVersion uint64) error { + // No-op if toVersion is the initial version. + if toVersion <= 1 { + return nil + } + + moduleMigrationsMap, found := c.migrations[moduleName] + if !found { + return sdkerrors.Wrapf(sdkerrors.ErrNotFound, "no migrations found for module %s", moduleName) + } + + // Run in-place migrations for the module sequentially until toVersion. + for i := fromVersion; i < toVersion; i++ { + migrateFn, found := moduleMigrationsMap[i] + if !found { + return sdkerrors.Wrapf(sdkerrors.ErrNotFound, "no migration found for module %s from version %d to version %d", moduleName, i, i+1) + } + + err := migrateFn(ctx) + if err != nil { + return err + } + } + + return nil +} diff --git a/types/module/module.go b/types/module/module.go index 2379c93d5ebd..58bb4927b29a 100644 --- a/types/module/module.go +++ b/types/module/module.go @@ -40,6 +40,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) //__________________________________________________________________________________________ @@ -174,6 +175,12 @@ type AppModule interface { // RegisterServices allows a module to register services RegisterServices(Configurator) + // ConsensusVersion is a sequence number for state-breaking change of the + // module. It should be incremented on each consensus-breaking change + // introduced by the module. To avoid wrong/empty versions, the initial version + // should be set to 1. + ConsensusVersion() uint64 + // ABCI BeginBlock(sdk.Context, abci.RequestBeginBlock) EndBlock(sdk.Context, abci.RequestEndBlock) []abci.ValidatorUpdate @@ -208,6 +215,9 @@ func (gam GenesisOnlyAppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Que // RegisterServices registers all services. func (gam GenesisOnlyAppModule) RegisterServices(Configurator) {} +// ConsensusVersion implements AppModule/ConsensusVersion. +func (gam GenesisOnlyAppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock returns an empty module begin-block func (gam GenesisOnlyAppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {} @@ -328,6 +338,30 @@ func (m *Manager) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) map[st return genesisData } +// MigrationHandler is the migration function that each module registers. +type MigrationHandler func(store 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. +type MigrationMap map[string]uint64 + +// RunMigrations performs in-place store migrations for all modules. +func (m Manager) RunMigrations(ctx sdk.Context, cfg Configurator, migrateFromVersions MigrationMap) error { + c, ok := cfg.(configurator) + if !ok { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", configurator{}, cfg) + } + + for moduleName, module := range m.Modules { + err := c.runModuleMigrations(ctx, moduleName, migrateFromVersions[moduleName], module.ConsensusVersion()) + if err != nil { + return err + } + } + + return nil +} + // BeginBlock performs begin block functionality for all modules. It creates a // child context with an event manager to aggregate events emitted from all // modules. diff --git a/types/msgservice/service_msg_client.go b/types/msgservice/service_msg_client.go new file mode 100644 index 000000000000..de3be89094f8 --- /dev/null +++ b/types/msgservice/service_msg_client.go @@ -0,0 +1,50 @@ +package msgservice + +import ( + "context" + "fmt" + + gogogrpc "github.com/gogo/protobuf/grpc" + grpc "google.golang.org/grpc" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ gogogrpc.ClientConn = &ServiceMsgClientConn{} + +// ServiceMsgClientConn is an instance of grpc.ClientConn that is used to test building +// transactions with MsgClient's. It is intended to be replaced by the work in +// https://github.com/cosmos/cosmos-sdk/issues/7541 when that is ready. +type ServiceMsgClientConn struct { + msgs []sdk.Msg +} + +// Invoke implements the grpc ClientConn.Invoke method +func (t *ServiceMsgClientConn) Invoke(_ context.Context, method string, args, _ interface{}, _ ...grpc.CallOption) error { + req, ok := args.(sdk.MsgRequest) + if !ok { + return fmt.Errorf("%T should implement %T", args, (*sdk.MsgRequest)(nil)) + } + + err := req.ValidateBasic() + if err != nil { + return err + } + + t.msgs = append(t.msgs, sdk.ServiceMsg{ + MethodName: method, + Request: req, + }) + + return nil +} + +// NewStream implements the grpc ClientConn.NewStream method +func (t *ServiceMsgClientConn) NewStream(context.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { + return nil, fmt.Errorf("not supported") +} + +// GetMsgs returns ServiceMsgClientConn.msgs +func (t *ServiceMsgClientConn) GetMsgs() []sdk.Msg { + return t.msgs +} diff --git a/types/query/filtered_pagination_test.go b/types/query/filtered_pagination_test.go index dbd2057da473..1ba2da21d701 100644 --- a/types/query/filtered_pagination_test.go +++ b/types/query/filtered_pagination_test.go @@ -4,10 +4,11 @@ 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" "github.com/cosmos/cosmos-sdk/types/query" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -27,11 +28,12 @@ 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)) - store := ctx.KVStore(app.GetKey(authtypes.StoreKey)) + s.Require().NoError(simapp.FundAccount(app, ctx, addr1, balances)) + store := ctx.KVStore(app.GetKey(types.StoreKey)) // verify pagination with limit > total values pageReq := &query.PageRequest{Key: nil, Limit: 5, CountTotal: true} @@ -100,18 +102,20 @@ 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) } pageReq := &query.PageRequest{Key: nil, Limit: 1, CountTotal: true} - store := ctx.KVStore(app.GetKey(authtypes.StoreKey)) + store := ctx.KVStore(app.GetKey(types.StoreKey)) balancesStore := prefix.NewStore(store, types.BalancesPrefix) - accountStore := prefix.NewStore(balancesStore, addr1.Bytes()) + accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1)) var balResult sdk.Coins pageRes, err := query.FilteredPaginate(accountStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) { @@ -143,7 +147,7 @@ func ExampleFilteredPaginate() { func execFilterPaginate(store sdk.KVStore, pageReq *query.PageRequest, appCodec codec.Marshaler) (balances sdk.Coins, res *query.PageResponse, err error) { balancesStore := prefix.NewStore(store, types.BalancesPrefix) - accountStore := prefix.NewStore(balancesStore, addr1.Bytes()) + accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1)) var balResult sdk.Coins res, err = query.FilteredPaginate(accountStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) { diff --git a/types/query/pagination.pb.go b/types/query/pagination.pb.go index 0a404696d72e..b27db91746a5 100644 --- a/types/query/pagination.pb.go +++ b/types/query/pagination.pb.go @@ -469,10 +469,7 @@ func (m *PageRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthPagination - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthPagination } if (iNdEx + skippy) > l { @@ -575,10 +572,7 @@ func (m *PageResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthPagination - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthPagination } if (iNdEx + skippy) > l { diff --git a/types/query/pagination_test.go b/types/query/pagination_test.go index f0e1377a1e91..bd9dc0af13e1 100644 --- a/types/query/pagination_test.go +++ b/types/query/pagination_test.go @@ -17,10 +17,8 @@ import ( "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" "github.com/cosmos/cosmos-sdk/types/query" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -75,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{} @@ -180,20 +179,21 @@ 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 pageReq := &query.PageRequest{Key: nil, Limit: 1, CountTotal: true} request := types.NewQueryAllBalancesRequest(addr1, pageReq) balResult := sdk.NewCoins() - authStore := ctx.KVStore(app.GetKey(authtypes.StoreKey)) + authStore := ctx.KVStore(app.GetKey(types.StoreKey)) balancesStore := prefix.NewStore(authStore, types.BalancesPrefix) - accountStore := prefix.NewStore(balancesStore, addr1.Bytes()) + accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1)) pageRes, err := query.Paginate(accountStore, request.Pagination, func(key []byte, value []byte) error { var tempRes sdk.Coin err := app.AppCodec().UnmarshalBinaryBare(value, &tempRes) @@ -221,20 +221,5 @@ func setupTest() (*simapp.SimApp, sdk.Context, codec.Marshaler) { ms.LoadLatestVersion() - maccPerms := simapp.GetMaccPerms() - maccPerms[holder] = nil - maccPerms[authtypes.Burner] = []string{authtypes.Burner} - maccPerms[authtypes.Minter] = []string{authtypes.Minter} - maccPerms[multiPerm] = []string{authtypes.Burner, authtypes.Minter, authtypes.Staking} - maccPerms[randomPerm] = []string{"random"} - app.AccountKeeper = authkeeper.NewAccountKeeper( - appCodec, app.GetKey(authtypes.StoreKey), app.GetSubspace(authtypes.ModuleName), - authtypes.ProtoBaseAccount, maccPerms, - ) - app.BankKeeper = bankkeeper.NewBaseKeeper( - appCodec, app.GetKey(authtypes.StoreKey), app.AccountKeeper, - app.GetSubspace(types.ModuleName), make(map[string]bool), - ) - return app, ctx, appCodec } diff --git a/types/service_msg.go b/types/service_msg.go index 61c0f15ab12a..ee6cede2eb16 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/types.go b/types/simulation/types.go index f541b1d764de..46ede62a3536 100644 --- a/types/simulation/types.go +++ b/types/simulation/types.go @@ -2,7 +2,9 @@ package simulation import ( "encoding/json" + "fmt" "math/rand" + "reflect" "time" "github.com/cosmos/cosmos-sdk/baseapp" @@ -75,7 +77,16 @@ func NewOperationMsgBasic(route, name, comment string, ok bool, msg []byte) Oper } // NewOperationMsg - create a new operation message from sdk.Msg -func NewOperationMsg(msg sdk.Msg, ok bool, comment string) OperationMsg { +func NewOperationMsg(msg sdk.Msg, ok bool, comment string, cdc *codec.ProtoCodec) OperationMsg { + if reflect.TypeOf(msg) == reflect.TypeOf(sdk.ServiceMsg{}) { + srvMsg, ok := msg.(sdk.ServiceMsg) + if !ok { + panic(fmt.Sprintf("Expecting %T to implement sdk.ServiceMsg", msg)) + } + bz := cdc.MustMarshalJSON(srvMsg.Request) + + return NewOperationMsgBasic(srvMsg.MethodName, srvMsg.MethodName, comment, ok, bz) + } return NewOperationMsgBasic(msg.Route(), msg.Type(), comment, ok, msg.GetSignBytes()) } diff --git a/types/tx/service.pb.go b/types/tx/service.pb.go index 57f1f627635e..f01a60fe52a9 100644 --- a/types/tx/service.pb.go +++ b/types/tx/service.pb.go @@ -1348,10 +1348,7 @@ func (m *GetTxsEventRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthService - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { @@ -1505,10 +1502,7 @@ func (m *GetTxsEventResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthService - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { @@ -1611,10 +1605,7 @@ func (m *BroadcastTxRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthService - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { @@ -1700,10 +1691,7 @@ func (m *BroadcastTxResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthService - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { @@ -1789,10 +1777,7 @@ func (m *SimulateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthService - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { @@ -1914,10 +1899,7 @@ func (m *SimulateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthService - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { @@ -1999,10 +1981,7 @@ func (m *GetTxRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthService - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { @@ -2124,10 +2103,7 @@ func (m *GetTxResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthService - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthService } if (iNdEx + skippy) > l { diff --git a/types/tx/signing/signing.pb.go b/types/tx/signing/signing.pb.go index 424e7395df30..f06a270c7cbd 100644 --- a/types/tx/signing/signing.pb.go +++ b/types/tx/signing/signing.pb.go @@ -867,10 +867,7 @@ func (m *SignatureDescriptors) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSigning - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSigning } if (iNdEx + skippy) > l { @@ -1011,10 +1008,7 @@ func (m *SignatureDescriptor) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSigning - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSigning } if (iNdEx + skippy) > l { @@ -1134,10 +1128,7 @@ func (m *SignatureDescriptor_Data) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSigning - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSigning } if (iNdEx + skippy) > l { @@ -1240,10 +1231,7 @@ func (m *SignatureDescriptor_Data_Single) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSigning - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSigning } if (iNdEx + skippy) > l { @@ -1363,10 +1351,7 @@ func (m *SignatureDescriptor_Data_Multi) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSigning - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSigning } if (iNdEx + skippy) > l { diff --git a/types/tx/tx.pb.go b/types/tx/tx.pb.go index 750f4266f3fa..a552c7a083e1 100644 --- a/types/tx/tx.pb.go +++ b/types/tx/tx.pb.go @@ -1746,10 +1746,7 @@ func (m *Tx) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1899,10 +1896,7 @@ func (m *TxRaw) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2071,10 +2065,7 @@ func (m *SignDoc) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2277,10 +2268,7 @@ func (m *TxBody) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2400,10 +2388,7 @@ func (m *AuthInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2544,10 +2529,7 @@ func (m *SignerInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2667,10 +2649,7 @@ func (m *ModeInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2739,10 +2718,7 @@ func (m *ModeInfo_Single) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2862,10 +2838,7 @@ func (m *ModeInfo_Multi) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -3032,10 +3005,7 @@ func (m *Fee) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/types/tx/types.go b/types/tx/types.go index 0392b2fcfd08..a0b05b5dcea0 100644 --- a/types/tx/types.go +++ b/types/tx/types.go @@ -132,6 +132,37 @@ func (t *Tx) GetSigners() []sdk.AccAddress { return signers } +func (t *Tx) GetGas() uint64 { + return t.AuthInfo.Fee.GasLimit +} +func (t *Tx) GetFee() sdk.Coins { + return t.AuthInfo.Fee.Amount +} +func (t *Tx) FeePayer() sdk.AccAddress { + feePayer := t.AuthInfo.Fee.Payer + if feePayer != "" { + payerAddr, err := sdk.AccAddressFromBech32(feePayer) + if err != nil { + panic(err) + } + return payerAddr + } + // use first signer as default if no payer specified + return t.GetSigners()[0] +} + +func (t *Tx) FeeGranter() sdk.AccAddress { + feePayer := t.AuthInfo.Fee.Granter + if feePayer != "" { + granterAddr, err := sdk.AccAddressFromBech32(feePayer) + if err != nil { + panic(err) + } + return granterAddr + } + return nil +} + // UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method func (t *Tx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { if t.Body != nil { diff --git a/types/uint.go b/types/uint.go index 2305d7386728..d449c5262b19 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 } diff --git a/types/uint_test.go b/types/uint_test.go index 36705c890a19..b91f9ab4a96a 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 3be56a3de605..e90ee59cebcb 100644 --- a/x/README.md +++ b/x/README.md @@ -8,6 +8,7 @@ parent: Here are some production-grade modules that can be used in Cosmos SDK applications, along with their respective documentation: - [Auth](auth/spec/README.md) - Authentication of accounts and transactions for Cosmos SDK application. +- [Authz](authz/spec/README.md) - Authorization for accounts to perform actions on behalf of other accounts. - [Bank](bank/spec/README.md) - Token transfer functionalities. - [Capability](capability/spec/README.md) - Object capability implementation. - [Crisis](crisis/spec/README.md) - Halting the blockchain under certain circumstances (e.g. if an invariant is broken). diff --git a/x/auth/ante/ante_test.go b/x/auth/ante/ante_test.go index 7659f3e1043d..9b268b1ec60d 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 84e5adb60db7..343e814583d2 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/sigverify_test.go b/x/auth/ante/sigverify_test.go index 337618fab50f..fa15e1f179f3 100644 --- a/x/auth/ante/sigverify_test.go +++ b/x/auth/ante/sigverify_test.go @@ -67,7 +67,7 @@ func (suite *AnteTestSuite) TestSetPubKey() { func (suite *AnteTestSuite) TestConsumeSignatureVerificationGas() { params := types.DefaultParams() msg := []byte{1, 2, 3, 4} - _, cdc := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Amino pkSet1, sigSet1 := generatePubKeysAndSignatures(5, msg, false) multisigKey1 := kmultisig.NewLegacyAminoPubKey(2, pkSet1) diff --git a/x/auth/ante/testutil_test.go b/x/auth/ante/testutil_test.go index 5a1cfc4ec44c..78af9743eadd 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 6a6a9b1d559e..efc7fbee9459 100644 --- a/x/auth/client/cli/cli_test.go +++ b/x/auth/client/cli/cli_test.go @@ -15,7 +15,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" @@ -145,11 +144,21 @@ func (s *IntegrationTestSuite) TestCLISign() { valInfo, err := val1.ClientCtx.Keyring.Key(val1.Moniker) require.NoError(err) + // query account info + queryResJSON, err := authtest.QueryAccountExec(val1.ClientCtx, val1.Address) + require.NoError(err) + var account authtypes.AccountI + require.NoError(val1.ClientCtx.JSONMarshaler.UnmarshalInterfaceJSON(queryResJSON.Bytes(), &account)) + /**** test signature-only ****/ res, err := authtest.TxSignExec(val1.ClientCtx, val1.Address, fileUnsigned.Name(), chainFlag, sigOnlyFlag) require.NoError(err) checkSignatures(require, txCfg, res.Bytes(), valInfo.GetPubKey()) + sigs, err := txCfg.UnmarshalSignatureJSON(res.Bytes()) + require.NoError(err) + require.Equal(1, len(sigs)) + require.Equal(account.GetSequence(), sigs[0].Sequence) /**** test full output ****/ res, err = authtest.TxSignExec(val1.ClientCtx, val1.Address, fileUnsigned.Name(), chainFlag) @@ -221,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(), @@ -250,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 { @@ -273,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, "", }, @@ -426,7 +430,7 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() { // Write the output to disk signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String()) - // Validate Signature + // validate Signature res, err = authtest.TxValidateSignaturesExec(val1.ClientCtx, signedTxFile.Name()) s.Require().NoError(err) s.Require().True(strings.Contains(res.String(), "[OK]")) @@ -814,19 +818,17 @@ func (s *IntegrationTestSuite) TestGetAccountCmd() { testCases := []struct { name string - args []string + address sdk.AccAddress expectErr bool }{ { "invalid address", - []string{addr1.String(), - fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + addr1, true, }, { "valid address", - []string{val.Address.String(), - fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + val.Address, false, }, } @@ -834,18 +836,15 @@ func (s *IntegrationTestSuite) TestGetAccountCmd() { for _, tc := range testCases { tc := tc s.Run(tc.name, func() { - cmd := authcli.GetAccountCmd() clientCtx := val.ClientCtx - out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + out, err := authtest.QueryAccountExec(clientCtx, tc.address) if tc.expectErr { s.Require().Error(err) s.Require().NotEqual("internal", err.Error()) } else { - var any types.Any - s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &any)) var acc authtypes.AccountI - s.Require().NoError(val.ClientCtx.InterfaceRegistry.UnpackAny(&any, &acc)) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalInterfaceJSON(out.Bytes(), &acc)) s.Require().Equal(val.Address, acc.GetAddress()) } }) diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 2590858b4c20..a236e900fbe5 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "strings" @@ -58,7 +57,7 @@ $ query auth params } queryClient := types.NewQueryClient(clientCtx) - res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) + res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) if err != nil { return err } @@ -90,7 +89,7 @@ func GetAccountCmd() *cobra.Command { } queryClient := types.NewQueryClient(clientCtx) - res, err := queryClient.Account(context.Background(), &types.QueryAccountRequest{Address: key.String()}) + res, err := queryClient.Account(cmd.Context(), &types.QueryAccountRequest{Address: key.String()}) if err != nil { return err } diff --git a/x/auth/client/cli/validate_sigs.go b/x/auth/client/cli/validate_sigs.go index 52290ed8e95f..d8d6283a7990 100644 --- a/x/auth/client/cli/validate_sigs.go +++ b/x/auth/client/cli/validate_sigs.go @@ -16,7 +16,7 @@ import ( func GetValidateSignaturesCommand() *cobra.Command { cmd := &cobra.Command{ Use: "validate-signatures [file]", - Short: "Validate transactions signatures", + Short: "validate transactions signatures", Long: `Print the addresses that must sign the transaction, those who have already signed it, and make sure that signatures are in the correct order. @@ -96,7 +96,7 @@ func printAndValidateSigs( success = false } - // Validate the actual signature over the transaction bytes since we can + // validate the actual signature over the transaction bytes since we can // reach out to a full node to query accounts. if !offline && success { accNum, accSeq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, sigAddr) diff --git a/x/auth/client/rest/rest_test.go b/x/auth/client/rest/rest_test.go index e20874f6e716..b78acd2c8a9a 100644 --- a/x/auth/client/rest/rest_test.go +++ b/x/auth/client/rest/rest_test.go @@ -25,8 +25,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" "github.com/cosmos/cosmos-sdk/x/bank/types" + ibcclientcli "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/client/cli" ibccli "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/client/cli" - ibcsolomachinecli "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/client/cli" ) type IntegrationTestSuite struct { @@ -186,8 +186,7 @@ func (s *IntegrationTestSuite) TestBroadcastIBCTxRequest() { res, err := rest.PostRequest(fmt.Sprintf("%s/txs", val.APIAddress), "application/json", []byte(req)) s.Require().NoError(err) - // Make sure the error message is correct. - s.Require().Contains(string(res), "this transaction cannot be broadcasted via legacy REST endpoints") + s.Require().NotContains(string(res), "this transaction cannot be broadcasted via legacy REST endpoints", string(res)) } // Helper function to test querying txs. We will use it to query StdTx and service `Msg`s. @@ -270,7 +269,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) @@ -279,7 +278,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, @@ -480,10 +479,20 @@ func (s *IntegrationTestSuite) testQueryIBCTx(txRes sdk.TxResponse, cmd *cobra.C func (s *IntegrationTestSuite) TestLegacyRestErrMessages() { val := s.network.Validators[0] + // Write client state json to temp file, used for an IBC message. + // Generated by printing the result of cdc.MarshalIntefaceJSON on + // a solo machine client state + clientStateJSON := testutil.WriteToNewTempFile( + s.T(), + `{"@type":"/ibc.lightclients.solomachine.v1.ClientState","sequence":"1","frozen_sequence":"0","consensus_state":{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"testing","timestamp":"10"},"allow_update_after_proposal":false}`, + ) + // Write consensus json to temp file, used for an IBC message. + // Generated by printing the result of cdc.MarshalIntefaceJSON on + // a solo machine consensus state consensusJSON := testutil.WriteToNewTempFile( s.T(), - `{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A/3SXL2ONYaOkxpdR5P8tHTlSlPv1AwQwSFxKRee5JQW"},"diversifier":"diversifier","timestamp":"10"}`, + `{"@type":"/ibc.lightclients.solomachine.v1.ConsensusState","public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"testing","timestamp":"10"}`, ) testCases := []struct { @@ -509,10 +518,10 @@ func (s *IntegrationTestSuite) TestLegacyRestErrMessages() { }, { "Successful IBC message", - ibcsolomachinecli.NewCreateClientCmd(), + ibcclientcli.NewCreateClientCmd(), []string{ - "1", // dummy sequence - consensusJSON.Name(), // path to consensus json, + clientStateJSON.Name(), // path to client state json + consensusJSON.Name(), // path to consensus json, 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()), diff --git a/x/auth/client/testutil/helpers.go b/x/auth/client/testutil/helpers.go index b46079cc6d95..e4f31727768e 100644 --- a/x/auth/client/testutil/helpers.go +++ b/x/auth/client/testutil/helpers.go @@ -23,12 +23,10 @@ func TxSignExec(clientCtx client.Context, from fmt.Stringer, filename string, ex filename, } - args = append(args, extraArgs...) - cmd := cli.GetSignCommand() tmcli.PrepareBaseCmd(cmd, "", "") - return clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + return clitestutil.ExecTestCLICmd(clientCtx, cmd, append(args, extraArgs...)) } func TxBroadcastExec(clientCtx client.Context, filename string, extraArgs ...string) (testutil.BufferWriter, error) { @@ -36,9 +34,7 @@ func TxBroadcastExec(clientCtx client.Context, filename string, extraArgs ...str filename, } - args = append(args, extraArgs...) - - return clitestutil.ExecTestCLICmd(clientCtx, cli.GetBroadcastCommand(), args) + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetBroadcastCommand(), append(args, extraArgs...)) } func TxEncodeExec(clientCtx client.Context, filename string, extraArgs ...string) (testutil.BufferWriter, error) { @@ -47,9 +43,7 @@ func TxEncodeExec(clientCtx client.Context, filename string, extraArgs ...string filename, } - args = append(args, extraArgs...) - - return clitestutil.ExecTestCLICmd(clientCtx, cli.GetEncodeCommand(), args) + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetEncodeCommand(), append(args, extraArgs...)) } func TxValidateSignaturesExec(clientCtx client.Context, filename string) (testutil.BufferWriter, error) { @@ -70,9 +64,7 @@ func TxMultiSignExec(clientCtx client.Context, from string, filename string, ext from, } - args = append(args, extraArgs...) - - return clitestutil.ExecTestCLICmd(clientCtx, cli.GetMultiSignCommand(), args) + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetMultiSignCommand(), append(args, extraArgs...)) } func TxSignBatchExec(clientCtx client.Context, from fmt.Stringer, filename string, extraArgs ...string) (testutil.BufferWriter, error) { @@ -82,9 +74,7 @@ func TxSignBatchExec(clientCtx client.Context, from fmt.Stringer, filename strin filename, } - args = append(args, extraArgs...) - - return clitestutil.ExecTestCLICmd(clientCtx, cli.GetSignBatchCommand(), args) + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetSignBatchCommand(), append(args, extraArgs...)) } func TxDecodeExec(clientCtx client.Context, encodedTx string, extraArgs ...string) (testutil.BufferWriter, error) { @@ -93,9 +83,13 @@ func TxDecodeExec(clientCtx client.Context, encodedTx string, extraArgs ...strin encodedTx, } - args = append(args, extraArgs...) + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetDecodeCommand(), append(args, extraArgs...)) +} + +func QueryAccountExec(clientCtx client.Context, address fmt.Stringer, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{address.String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)} - return clitestutil.ExecTestCLICmd(clientCtx, cli.GetDecodeCommand(), args) + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetAccountCmd(), append(args, extraArgs...)) } // DONTCOVER diff --git a/x/auth/keeper/keeper_bench_test.go b/x/auth/keeper/keeper_bench_test.go index a217eddacae3..1a18dff84557 100644 --- a/x/auth/keeper/keeper_bench_test.go +++ b/x/auth/keeper/keeper_bench_test.go @@ -7,6 +7,7 @@ import ( ) func BenchmarkAccountMapperGetAccountFound(b *testing.B) { + b.ReportAllocs() app, ctx := createTestApp(false) // assumes b.N < 2**24 diff --git a/x/auth/keeper/keeper_test.go b/x/auth/keeper/keeper_test.go index 87744d58c5bb..16cf53a253cf 100644 --- a/x/auth/keeper/keeper_test.go +++ b/x/auth/keeper/keeper_test.go @@ -129,7 +129,7 @@ func TestSupply_ValidatePermissions(t *testing.T) { maccPerms[multiPerm] = []string{types.Burner, types.Minter, types.Staking} maccPerms[randomPerm] = []string{"random"} - cdc, _ := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Marshaler keeper := keeper.NewAccountKeeper( cdc, app.GetKey(types.StoreKey), app.GetSubspace(types.ModuleName), types.ProtoBaseAccount, maccPerms, diff --git a/x/auth/legacy/legacytx/stdsign.go b/x/auth/legacy/legacytx/stdsign.go index 2ba194dad843..466981369624 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/legacytx/stdtx_builder.go b/x/auth/legacy/legacytx/stdtx_builder.go index 153c4b2bbbc1..ae0e97a3f34c 100644 --- a/x/auth/legacy/legacytx/stdtx_builder.go +++ b/x/auth/legacy/legacytx/stdtx_builder.go @@ -84,6 +84,9 @@ func (s *StdTxBuilder) SetTimeoutHeight(height uint64) { s.TimeoutHeight = height } +// SetFeeGranter does nothing for stdtx +func (s *StdTxBuilder) SetFeeGranter(_ sdk.AccAddress) {} + // StdTxConfig is a context.TxConfig for StdTx type StdTxConfig struct { Cdc *codec.LegacyAmino diff --git a/x/auth/legacy/v040/store.go b/x/auth/legacy/v040/store.go new file mode 100644 index 000000000000..9fb81b60a749 --- /dev/null +++ b/x/auth/legacy/v040/store.go @@ -0,0 +1,4 @@ +package v040 + +// AddrLen defines a valid address length +const AddrLen = 20 diff --git a/x/auth/module.go b/x/auth/module.go index 73aa9a1066a9..9e4af71ff037 100644 --- a/x/auth/module.go +++ b/x/auth/module.go @@ -147,6 +147,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(gs) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock returns the begin blocker for the auth module. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} diff --git a/x/auth/signing/verify_test.go b/x/auth/signing/verify_test.go index 7e842d13537c..22cd3eb2754f 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/simulation/decoder_test.go b/x/auth/simulation/decoder_test.go index c7ececcf95cc..0caeccee0784 100644 --- a/x/auth/simulation/decoder_test.go +++ b/x/auth/simulation/decoder_test.go @@ -22,7 +22,7 @@ var ( func TestDecodeStore(t *testing.T) { app := simapp.Setup(false) - cdc, _ := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Marshaler acc := types.NewBaseAccountWithAddress(delAddr1) dec := simulation.NewDecodeStore(app.AccountKeeper) diff --git a/x/auth/spec/01_concepts.md b/x/auth/spec/01_concepts.md index 47316b4cbbf6..9f8c9b8d8f2d 100644 --- a/x/auth/spec/01_concepts.md +++ b/x/auth/spec/01_concepts.md @@ -19,7 +19,7 @@ signature verification, as well as costs proportional to the tx size. Operators should set minimum gas prices when starting their nodes. They must set the unit costs of gas in each token denomination they wish to support: -`gaiad start ... --minimum-gas-prices=0.00001stake;0.05photinos` +`simd start ... --minimum-gas-prices=0.00001stake;0.05photinos` When adding transactions to mempool or gossipping transactions, validators check if the transaction's gas prices, which are determined by the provided fees, meet diff --git a/x/auth/spec/05_vesting.md b/x/auth/spec/05_vesting.md index 50bf70d03fcf..a253f7de604b 100644 --- a/x/auth/spec/05_vesting.md +++ b/x/auth/spec/05_vesting.md @@ -88,52 +88,28 @@ type VestingAccount interface { GetStartTime() int64 GetEndTime() int64 } +``` +### BaseVestingAccount ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/vesting/v1beta1/vesting.proto#L10-L33 -// BaseVestingAccount implements the VestingAccount interface. It contains all -// the necessary fields needed for any vesting account implementation. -type BaseVestingAccount struct { - BaseAccount - - OriginalVesting Coins // coins in account upon initialization - DelegatedFree Coins // coins that are vested and delegated - DelegatedVesting Coins // coins that vesting and delegated - - EndTime int64 // when the coins become unlocked -} - -// ContinuousVestingAccount implements the VestingAccount interface. It -// continuously vests by unlocking coins linearly with respect to time. -type ContinuousVestingAccount struct { - BaseVestingAccount - - StartTime int64 // when the coins start to vest -} +### ContinuousVestingAccount ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/vesting/v1beta1/vesting.proto#L35-L43 -// DelayedVestingAccount implements the VestingAccount interface. It vests all -// coins after a specific time, but non prior. In other words, it keeps them -// locked until a specified time. -type DelayedVestingAccount struct { - BaseVestingAccount -} +### DelayedVestingAccount ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/vesting/v1beta1/vesting.proto#L45-L53 -// VestingPeriod defines a length of time and amount of coins that will vest -type Period struct { - Length int64 // length of the period, in seconds - Amount Coins // amount of coins vesting during this period -} +### Period ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/vesting/v1beta1/vesting.proto#L56-L62 +```go // Stores all vesting periods passed as part of a PeriodicVestingAccount type Periods []Period -// PeriodicVestingAccount implements the VestingAccount interface. It -// periodically vests by unlocking coins during each specified period -type PeriodicVestingAccount struct { - BaseVestingAccount - StartTime int64 - Periods Periods // the vesting schedule -} ``` +### PeriodicVestingAccount ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/vesting/v1beta1/vesting.proto#L64-L73 + In order to facilitate less ad-hoc type checking and assertions and to support flexibility in account balance usage, the existing `x/bank` `ViewKeeper` interface is updated to contain the following: diff --git a/x/auth/spec/07_params.md b/x/auth/spec/07_params.md index 93ddc89c2219..1bf24e7f6d6e 100644 --- a/x/auth/spec/07_params.md +++ b/x/auth/spec/07_params.md @@ -8,8 +8,8 @@ The auth module contains the following parameters: | Key | Type | Example | | ---------------------- | --------------- | ------- | -| MaxMemoCharacters | string (uint64) | "256" | -| TxSigLimit | string (uint64) | "7" | -| TxSizeCostPerByte | string (uint64) | "10" | -| SigVerifyCostED25519 | string (uint64) | "590" | -| SigVerifyCostSecp256k1 | string (uint64) | "1000" | +| MaxMemoCharacters | uint64 | 256 | +| TxSigLimit | uint64 | 7 | +| TxSizeCostPerByte | uint64 | 10 | +| SigVerifyCostED25519 | uint64 | 590 | +| SigVerifyCostSecp256k1 | uint64 | 1000 | diff --git a/x/auth/tx/service_test.go b/x/auth/tx/service_test.go index b7b3322dc33f..4f5db8e9c99d 100644 --- a/x/auth/tx/service_test.go +++ b/x/auth/tx/service_test.go @@ -175,14 +175,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 +194,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 +231,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, "", }, diff --git a/x/auth/tx/sigs.go b/x/auth/tx/sigs.go index 5916a42c3342..e2d5d63a6015 100644 --- a/x/auth/tx/sigs.go +++ b/x/auth/tx/sigs.go @@ -118,6 +118,7 @@ func (g config) MarshalSignatureJSON(sigs []signing.SignatureV2) ([]byte, error) descs[i] = &signing.SignatureDescriptor{ PublicKey: any, Data: descData, + Sequence: sig.Sequence, } } diff --git a/x/auth/types/account.go b/x/auth/types/account.go index eb9939ffce10..653f89bcac4a 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/auth.pb.go b/x/auth/types/auth.pb.go index 4a05cf8158e7..fa52ac5a8b90 100644 --- a/x/auth/types/auth.pb.go +++ b/x/auth/types/auth.pb.go @@ -652,10 +652,7 @@ func (m *BaseAccount) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAuth - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAuth } if (iNdEx + skippy) > l { @@ -805,10 +802,7 @@ func (m *ModuleAccount) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAuth - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAuth } if (iNdEx + skippy) > l { @@ -953,10 +947,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthAuth - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAuth } if (iNdEx + skippy) > l { diff --git a/x/auth/types/common_test.go b/x/auth/types/common_test.go index 5c6ff0fb6488..8588778994ec 100644 --- a/x/auth/types/common_test.go +++ b/x/auth/types/common_test.go @@ -6,5 +6,6 @@ import ( var ( app = simapp.Setup(false) - appCodec, legacyAmino = simapp.MakeCodecs() + ecdc = simapp.MakeTestEncodingConfig() + appCodec, legacyAmino = ecdc.Marshaler, ecdc.Amino ) diff --git a/x/auth/types/genesis.pb.go b/x/auth/types/genesis.pb.go index 3af036f4f450..4e7396ab7cb3 100644 --- a/x/auth/types/genesis.pb.go +++ b/x/auth/types/genesis.pb.go @@ -288,10 +288,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/auth/types/query.pb.go b/x/auth/types/query.pb.go index ff8be08a3ff6..fcb0efa96614 100644 --- a/x/auth/types/query.pb.go +++ b/x/auth/types/query.pb.go @@ -610,10 +610,7 @@ func (m *QueryAccountRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -699,10 +696,7 @@ func (m *QueryAccountResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -752,10 +746,7 @@ func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -838,10 +829,7 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/auth/vesting/client/cli/tx.go b/x/auth/vesting/client/cli/tx.go index 9cacdfdaab25..c6c2ac1432d2 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 490fc59971f1..48be156b83a8 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/module.go b/x/auth/vesting/module.go index 3cc579a40e37..259143469388 100644 --- a/x/auth/vesting/module.go +++ b/x/auth/vesting/module.go @@ -127,3 +127,6 @@ func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.Valid func (am AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { return am.DefaultGenesis(cdc) } + +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } diff --git a/x/auth/vesting/types/common_test.go b/x/auth/vesting/types/common_test.go index 4f361059ad3c..8e57a28c0a62 100644 --- a/x/auth/vesting/types/common_test.go +++ b/x/auth/vesting/types/common_test.go @@ -5,6 +5,6 @@ import ( ) var ( - app = simapp.Setup(false) - appCodec, _ = simapp.MakeCodecs() + app = simapp.Setup(false) + appCodec = simapp.MakeTestEncodingConfig().Marshaler ) diff --git a/x/auth/vesting/types/tx.pb.go b/x/auth/vesting/types/tx.pb.go index ab3283c238aa..d07a154dd326 100644 --- a/x/auth/vesting/types/tx.pb.go +++ b/x/auth/vesting/types/tx.pb.go @@ -624,10 +624,7 @@ func (m *MsgCreateVestingAccount) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -677,10 +674,7 @@ func (m *MsgCreateVestingAccountResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/x/auth/vesting/types/vesting.pb.go b/x/auth/vesting/types/vesting.pb.go index c491c7504b51..7672ce66f8e0 100644 --- a/x/auth/vesting/types/vesting.pb.go +++ b/x/auth/vesting/types/vesting.pb.go @@ -857,10 +857,7 @@ func (m *BaseVestingAccount) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthVesting - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthVesting } if (iNdEx + skippy) > l { @@ -965,10 +962,7 @@ func (m *ContinuousVestingAccount) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthVesting - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthVesting } if (iNdEx + skippy) > l { @@ -1054,10 +1048,7 @@ func (m *DelayedVestingAccount) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthVesting - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthVesting } if (iNdEx + skippy) > l { @@ -1160,10 +1151,7 @@ func (m *Period) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthVesting - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthVesting } if (iNdEx + skippy) > l { @@ -1302,10 +1290,7 @@ func (m *PeriodicVestingAccount) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthVesting - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthVesting } if (iNdEx + skippy) > l { diff --git a/x/authz/client/cli/cli_test.go b/x/authz/client/cli/cli_test.go new file mode 100644 index 000000000000..4ad3d93f3d1e --- /dev/null +++ b/x/authz/client/cli/cli_test.go @@ -0,0 +1,728 @@ +// +build norace + +package cli_test + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/gogo/protobuf/proto" + + "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" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz/client/cli" + 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" + + "github.com/cosmos/cosmos-sdk/x/authz/types" + bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network + grantee sdk.AccAddress +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := network.DefaultConfig() + cfg.NumValidators = 1 + + s.cfg = cfg + s.network = network.New(s.T(), cfg) + + val := s.network.Validators[0] + + // Create new account in the keyring. + info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + newAddr := sdk.AccAddress(info.GetPubKey().Address()) + + // Send some funds to the new account. + _, err = banktestutil.MsgSendExec( + val.ClientCtx, + val.Address, + newAddr, + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(200))), 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()), + ) + s.Require().NoError(err) + s.grantee = newAddr + + // create a proposal with deposit + _, err = govtestutil.MsgSubmitProposal(val.ClientCtx, val.Address.String(), + "Text Proposal 1", "Where is the title!?", govtypes.ProposalTypeText, + fmt.Sprintf("--%s=%s", govcli.FlagDeposit, sdk.NewCoin(s.cfg.BondDenom, govtypes.DefaultMinDepositTokens).String())) + s.Require().NoError(err) + + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +var typeMsgSend = types.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) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { + val := s.network.Validators[0] + grantee := s.grantee + + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + pastHour := time.Now().Add(time.Minute * time.Duration(-60)).Unix() + + testCases := []struct { + name string + args []string + respType proto.Message + expectedCode uint32 + expectErr bool + }{ + { + "Invalid granter Address", + []string{ + "grantee_addr", + "send", + fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, "granter"), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + }, + nil, + 0, + true, + }, + { + "Invalid grantee Address", + []string{ + "grantee_addr", + "send", + fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), + }, + nil, 0, + true, + }, + { + "Invalid expiration time", + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, pastHour), + }, + nil, 0, + true, + }, + { + "fail with error invalid msg-type", + []string{ + grantee.String(), + "generic", + 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()), + }, + &sdk.TxResponse{}, 29, + false, + }, + { + "Valid tx send authorization", + []string{ + grantee.String(), + "send", + fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), + 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()), + }, + &sdk.TxResponse{}, 0, + false, + }, + { + "Valid tx generic authorization", + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgVote), + 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()), + }, + &sdk.TxResponse{}, 0, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + clientCtx := val.ClientCtx + out, err := execGrantAuthorization( + val, + tc.args, + ) + if tc.expectErr { + s.Require().Error(err) + } else { + 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()) + } + }) + } +} + +func execGrantAuthorization(val *network.Validator, args []string) (testutil.BufferWriter, error) { + cmd := cli.NewCmdGrantAuthorization() + clientCtx := val.ClientCtx + return clitestutil.ExecTestCLICmd(clientCtx, cmd, args) +} + +func (s *IntegrationTestSuite) TestCmdRevokeAuthorizations() { + val := s.network.Validators[0] + + grantee := s.grantee + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + // send-authorization + _, 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) + + // generic-authorization + _, err = execGrantAuthorization( + val, + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgVote), + 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 + respType proto.Message + expectedCode uint32 + expectErr bool + }{ + { + "invalid grantee address", + []string{ + "invlid grantee", + typeMsgSend, + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + }, + nil, + 0, + true, + }, + { + "invalid granter address", + []string{ + grantee.String(), + typeMsgSend, + fmt.Sprintf("--%s=%s", flags.FlagFrom, "granter"), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + }, + nil, + 0, + true, + }, + { + "Valid tx send authorization", + []string{ + grantee.String(), + typeMsgSend, + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + &sdk.TxResponse{}, 0, + false, + }, + { + "Valid tx generic authorization", + []string{ + grantee.String(), + typeMsgVote, + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + &sdk.TxResponse{}, 0, + false, + }, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.NewCmdRevokeAuthorization() + 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().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestExecAuthorizationWithExpiration() { + val := s.network.Validators[0] + grantee := s.grantee + tenSeconds := time.Now().Add(time.Second * time.Duration(10)).Unix() + + _, err := execGrantAuthorization( + val, + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgVote), + 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, tenSeconds), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + // msg vote + voteTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.gov.v1beta1.Msg/Vote","proposal_id":"1","voter":"%s","option":"VOTE_OPTION_YES"}],"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()) + execMsg := testutil.WriteToNewTempFile(s.T(), voteTx) + + // waiting for authorization to expires + time.Sleep(12 * time.Second) + + cmd := cli.NewCmdExecAuthorization() + clientCtx := val.ClientCtx + + res, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, []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), + }) + s.Require().NoError(err) + s.Require().Contains(res.String(), "authorization not found") +} + +func (s *IntegrationTestSuite) TestNewExecGenericAuthorized() { + val := s.network.Validators[0] + grantee := s.grantee + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := execGrantAuthorization( + val, + []string{ + grantee.String(), + "generic", + fmt.Sprintf("--%s=%s", cli.FlagMsgType, typeMsgVote), + 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", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + + // msg vote + voteTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.gov.v1beta1.Msg/Vote","proposal_id":"1","voter":"%s","option":"VOTE_OPTION_YES"}],"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()) + execMsg := testutil.WriteToNewTempFile(s.T(), voteTx) + + 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{ + 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), + }, + &sdk.TxResponse{}, + 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) + } else { + 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()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewExecGrantAuthorized() { + 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=12%stoken", cli.FlagSpendLimit, val.Moniker), + 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", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + ) + s.Require().NoError(err) + tokens := sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), sdk.NewInt(12)), + ) + normalGeneratedTx, err := bankcli.MsgSendExec( + val.ClientCtx, + val.Address, + grantee, + tokens, + 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) + execMsg := testutil.WriteToNewTempFile(s.T(), normalGeneratedTx.String()) + 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{ + 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), + }, + &sdk.TxResponse{}, + 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) + } else { + 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()) + } + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/authz/client/cli/query.go b/x/authz/client/cli/query.go new file mode 100644 index 000000000000..ef7f326f71e7 --- /dev/null +++ b/x/authz/client/cli/query.go @@ -0,0 +1,138 @@ +package cli + +import ( + "context" + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd() *cobra.Command { + authorizationQueryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the authz module", + Long: "", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + authorizationQueryCmd.AddCommand( + GetCmdQueryAuthorization(), + GetCmdQueryAuthorizations(), + ) + + return authorizationQueryCmd +} + +// GetCmdQueryAuthorizations implements the query authorizations command. +func GetCmdQueryAuthorizations() *cobra.Command { + cmd := &cobra.Command{ + Use: "authorizations [granter-addr] [grantee-addr]", + Args: cobra.ExactArgs(2), + Short: "query list of authorizations for a granter-grantee pair", + Long: strings.TrimSpace( + fmt.Sprintf(`Query list of authorizations for a granter-grantee pair: +Example: +$ %s query %s authorizations cosmos1skj.. cosmos1skjwj.. +`, version.AppName, types.ModuleName), + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + granterAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + granteeAddr, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := queryClient.Authorizations( + context.Background(), + &types.QueryAuthorizationsRequest{ + Granter: granterAddr.String(), + Grantee: granteeAddr.String(), + Pagination: pageReq, + }, + ) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "authorizations") + return cmd +} + +// GetCmdQueryAuthorization implements the query authorization command. +func GetCmdQueryAuthorization() *cobra.Command { + cmd := &cobra.Command{ + Use: "authorization [granter-addr] [grantee-addr] [msg-type]", + Args: cobra.ExactArgs(3), + Short: "query authorization for a granter-grantee pair", + Long: strings.TrimSpace( + 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()), + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + granter, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + grantee, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + msgAuthorized := args[2] + + res, err := queryClient.Authorization( + context.Background(), + &types.QueryAuthorizationRequest{ + Granter: granter.String(), + Grantee: grantee.String(), + MethodName: msgAuthorized, + }, + ) + if err != nil { + return err + } + + return clientCtx.PrintProto(res.Authorization) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/x/authz/client/cli/tx.go b/x/authz/client/cli/tx.go new file mode 100644 index 000000000000..81540ef1f58b --- /dev/null +++ b/x/authz/client/cli/tx.go @@ -0,0 +1,222 @@ +package cli + +import ( + "context" + "errors" + "fmt" + "strings" + "time" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "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" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +const FlagSpendLimit = "spend-limit" +const FlagMsgType = "msg-type" +const FlagExpiration = "expiration" + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd() *cobra.Command { + AuthorizationTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Authorization transactions subcommands", + Long: "Authorize and revoke access to execute transactions on behalf of your address", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + AuthorizationTxCmd.AddCommand( + NewCmdGrantAuthorization(), + NewCmdRevokeAuthorization(), + NewCmdExecAuthorization(), + ) + + return AuthorizationTxCmd +} + +func NewCmdGrantAuthorization() *cobra.Command { + cmd := &cobra.Command{ + 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: + +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), + ), + Args: cobra.RangeArgs(2, 3), + 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 + switch args[1] { + case "send": + limit, err := cmd.Flags().GetString(FlagSpendLimit) + if err != nil { + return err + } + + spendLimit, err := sdk.ParseCoinsNormalized(limit) + if err != nil { + return err + } + + if !spendLimit.IsAllPositive() { + return fmt.Errorf("spend-limit should be greater than zero") + } + + authorization = &types.SendAuthorization{ + SpendLimit: spendLimit, + } + case "generic": + msgType, err := cmd.Flags().GetString(FlagMsgType) + if err != nil { + return err + } + + authorization = types.NewGenericAuthorization(msgType) + 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{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.GrantAuthorization(context.Background(), msg) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) + }, + } + 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().Int64(FlagExpiration, time.Now().AddDate(1, 0, 0).Unix(), "The Unix timestamp. Default is one year.") + return cmd +} + +func NewCmdRevokeAuthorization() *cobra.Command { + cmd := &cobra.Command{ + Use: "revoke [grantee_address] [msg_type] --from=[granter_address]", + Short: "revoke authorization", + Long: strings.TrimSpace( + 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()), + ), + 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 + } + + granter := clientCtx.GetFromAddress() + + msgAuthorized := args[1] + + msg := types.NewMsgRevokeAuthorization(granter, grantee, msgAuthorized) + + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.RevokeAuthorization(context.Background(), &msg) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) + }, + } + flags.AddTxFlagsToCmd(cmd) + return cmd +} + +func NewCmdExecAuthorization() *cobra.Command { + cmd := &cobra.Command{ + Use: "exec [msg_tx_json_file] --from [grantee]", + Short: "execute tx on behalf of granter account", + Long: strings.TrimSpace( + fmt.Sprintf(`execute tx on behalf of granter account: +Example: + $ %s tx %s exec tx.json --from grantee + $ %s tx bank send --from --chain-id --generate-only > tx.json && %s tx %s exec tx.json --from grantee + `, version.AppName, types.ModuleName, version.AppName, version.AppName, types.ModuleName), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + grantee := clientCtx.GetFromAddress() + + if offline, _ := cmd.Flags().GetBool(flags.FlagOffline); offline { + return errors.New("cannot broadcast tx during offline mode") + } + + theTx, err := authclient.ReadTxFromFile(clientCtx, args[0]) + if err != nil { + return err + } + msgs := theTx.GetMsgs() + serviceMsgs := make([]sdk.ServiceMsg, len(msgs)) + for i, msg := range msgs { + srvMsg, ok := msg.(sdk.ServiceMsg) + if !ok { + return fmt.Errorf("tx contains %T which is not a sdk.ServiceMsg", msg) + } + serviceMsgs[i] = srvMsg + } + + msg := types.NewMsgExecAuthorized(grantee, serviceMsgs) + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.ExecAuthorized(context.Background(), &msg) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/authz/client/rest/grpc_query_test.go b/x/authz/client/rest/grpc_query_test.go new file mode 100644 index 000000000000..8ed8e857c6af --- /dev/null +++ b/x/authz/client/rest/grpc_query_test.go @@ -0,0 +1,235 @@ +// +build norace + +package rest_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" + 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" +) + +type IntegrationTestSuite struct { + suite.Suite + cfg network.Config + network *network.Network + grantee sdk.AccAddress +} + +var typeMsgSend = types.SendAuthorization{}.MethodName() + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := network.DefaultConfig() + + cfg.NumValidators = 1 + s.cfg = cfg + s.network = network.New(s.T(), cfg) + + val := s.network.Validators[0] + // Create new account in the keyring. + info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + newAddr := sdk.AccAddress(info.GetPubKey().Address()) + + // Send some funds to the new account. + _, err = banktestutil.MsgSendExec( + val.ClientCtx, + val.Address, + newAddr, + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(200))), 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()), + ) + s.Require().NoError(err) + + // grant authorization + _, err = authztestutil.MsgGrantAuthorizationExec(val.ClientCtx, val.Address.String(), newAddr.String(), typeMsgSend, "100stake") + s.Require().NoError(err) + + s.grantee = newAddr + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestQueryAuthorizationGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + testCases := []struct { + name string + url string + expectErr bool + errorMsg string + }{ + { + "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), + 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), + 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), + 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), + 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"), + 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), + false, + "", + }, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, _ := rest.GetRequest(tc.url) + if tc.expectErr { + s.Require().Contains(string(resp), tc.errorMsg) + } else { + var authorization types.QueryAuthorizationResponse + err := val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &authorization) + s.Require().NoError(err) + authorization.Authorization.UnpackInterfaces(val.ClientCtx.InterfaceRegistry) + auth := authorization.Authorization.GetAuthorizationGrant() + s.Require().Equal(auth.MethodName(), types.SendAuthorization{}.MethodName()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryAuthorizationsGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + testCases := []struct { + name string + url string + expectErr bool + errMsg string + preRun func() + postRun func(*types.QueryAuthorizationsResponse) + }{ + { + "fail invalid granter address", + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grants", baseURL, "invalid_granter", s.grantee.String()), + true, + "decoding bech32 failed: invalid index of 1: invalid request", + func() {}, + func(_ *types.QueryAuthorizationsResponse) {}, + }, + { + "fail invalid grantee address", + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grants", baseURL, val.Address.String(), "invalid_grantee"), + true, + "decoding bech32 failed: invalid index of 1: invalid request", + func() {}, + func(_ *types.QueryAuthorizationsResponse) {}, + }, + { + "fail empty grantee address", + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grants", baseURL, "", "invalid_grantee"), + true, + "Not Implemented", + func() {}, + func(_ *types.QueryAuthorizationsResponse) {}, + }, + { + "valid query: expect single grant", + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grants", baseURL, val.Address.String(), s.grantee.String()), + false, + "", + func() { + }, + func(authorizations *types.QueryAuthorizationsResponse) { + s.Require().Equal(len(authorizations.Authorizations), 1) + }, + }, + { + "valid query: expect two grants", + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grants", baseURL, val.Address.String(), s.grantee.String()), + false, + "", + func() { + _, err := authztestutil.MsgGrantAuthorizationExec(val.ClientCtx, val.Address.String(), s.grantee.String(), "/cosmos.gov.v1beta1.Msg/Vote", "") + s.Require().NoError(err) + }, + func(authorizations *types.QueryAuthorizationsResponse) { + s.Require().Equal(len(authorizations.Authorizations), 2) + }, + }, + { + "valid query: expect single grant with pagination", + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grants?pagination.limit=1", baseURL, val.Address.String(), s.grantee.String()), + false, + "", + func() {}, + func(authorizations *types.QueryAuthorizationsResponse) { + s.Require().Equal(len(authorizations.Authorizations), 1) + }, + }, + { + "valid query: expect two grants with pagination", + fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grants?pagination.limit=2", baseURL, val.Address.String(), s.grantee.String()), + false, + "", + func() {}, + func(authorizations *types.QueryAuthorizationsResponse) { + s.Require().Equal(len(authorizations.Authorizations), 2) + }, + }, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + tc.preRun() + resp, _ := rest.GetRequest(tc.url) + if tc.expectErr { + s.Require().Contains(string(resp), tc.errMsg) + } else { + var authorizations types.QueryAuthorizationsResponse + err := val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &authorizations) + s.Require().NoError(err) + tc.postRun(&authorizations) + } + + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/authz/client/testutil/test_helpers.go b/x/authz/client/testutil/test_helpers.go new file mode 100644 index 000000000000..a56429a4d96a --- /dev/null +++ b/x/authz/client/testutil/test_helpers.go @@ -0,0 +1,34 @@ +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" +) + +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) +} diff --git a/x/authz/exported/keeper.go b/x/authz/exported/keeper.go new file mode 100644 index 000000000000..9858ba821729 --- /dev/null +++ b/x/authz/exported/keeper.go @@ -0,0 +1,26 @@ +package exported + +import ( + "time" + + "github.com/cosmos/cosmos-sdk/x/authz/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type Keeper interface { + // DispatchActions executes the provided messages via authorization grants from the message signer to the grantee + DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, msgs []sdk.ServiceMsg) sdk.Result + + // 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 + + // 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) + + // 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) +} diff --git a/x/authz/genesis.go b/x/authz/genesis.go new file mode 100644 index 000000000000..e84ddf5987a0 --- /dev/null +++ b/x/authz/genesis.go @@ -0,0 +1,47 @@ +package authz + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz/keeper" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +// InitGenesis new authz genesis +func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, data *types.GenesisState) { + for _, entry := range data.Authorization { + grantee, err := sdk.AccAddressFromBech32(entry.Grantee) + if err != nil { + panic(err) + } + granter, err := sdk.AccAddressFromBech32(entry.Granter) + if err != nil { + panic(err) + } + authorization, ok := entry.Authorization.GetCachedValue().(types.Authorization) + if !ok { + panic("expected authorization") + } + + err = keeper.Grant(ctx, grantee, granter, authorization, entry.Expiration) + if err != nil { + panic(err) + } + } +} + +// ExportGenesis returns a GenesisState for a given context and keeper. +func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) *types.GenesisState { + var entries []types.GrantAuthorization + keeper.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant types.AuthorizationGrant) bool { + exp := grant.Expiration + entries = append(entries, types.GrantAuthorization{ + Granter: granter.String(), + Grantee: grantee.String(), + Expiration: exp, + Authorization: grant.Authorization, + }) + return false + }) + + return types.NewGenesisState(entries) +} diff --git a/x/authz/genesis_test.go b/x/authz/genesis_test.go new file mode 100644 index 000000000000..2d770d4c5210 --- /dev/null +++ b/x/authz/genesis_test.go @@ -0,0 +1,59 @@ +package authz_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/simapp" + 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" +) + +type GenesisTestSuite struct { + suite.Suite + + ctx sdk.Context + keeper keeper.Keeper +} + +func (suite *GenesisTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1}) + suite.keeper = app.AuthzKeeper +} + +var ( + granteePub = secp256k1.GenPrivKey().PubKey() + granterPub = secp256k1.GenPrivKey().PubKey() + granteeAddr = sdk.AccAddress(granteePub.Address()) + granterAddr = sdk.AccAddress(granterPub.Address()) +) + +func (suite *GenesisTestSuite) TestImportExportGenesis() { + coins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1_000))) + + now := suite.ctx.BlockHeader().Time + grant := &types.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) + + // Clear keeper + suite.keeper.Revoke(suite.ctx, granteeAddr, granterAddr, grant.MethodName()) + + authz.InitGenesis(suite.ctx, suite.keeper, genesis) + newGenesis := authz.ExportGenesis(suite.ctx, suite.keeper) + suite.Require().Equal(genesis, newGenesis) +} + +func TestGenesisTestSuite(t *testing.T) { + suite.Run(t, new(GenesisTestSuite)) +} diff --git a/x/authz/keeper/grpc_query.go b/x/authz/keeper/grpc_query.go new file mode 100644 index 000000000000..7b1fcb1995dd --- /dev/null +++ b/x/authz/keeper/grpc_query.go @@ -0,0 +1,120 @@ +package keeper + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + proto "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +var _ types.QueryServer = Keeper{} + +// Authorizations implements the Query/Authorizations gRPC method. +func (k Keeper) Authorizations(c context.Context, req *types.QueryAuthorizationsRequest) (*types.QueryAuthorizationsResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + granter, err := sdk.AccAddressFromBech32(req.Granter) + + if err != nil { + return nil, err + } + grantee, err := sdk.AccAddressFromBech32(req.Grantee) + + if err != nil { + return nil, err + } + ctx := sdk.UnwrapSDKContext(c) + + store := ctx.KVStore(k.storeKey) + key := types.GetAuthorizationStoreKey(grantee, granter, "") + authStore := prefix.NewStore(store, key) + var authorizations []*types.AuthorizationGrant + pageRes, err := query.FilteredPaginate(authStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + auth, err := unmarshalAuthorization(k.cdc, value) + if err != nil { + return false, err + } + auth1 := auth.GetAuthorizationGrant() + if accumulate { + msg, ok := auth1.(proto.Message) + if !ok { + return false, status.Errorf(codes.Internal, "can't protomarshal %T", msg) + } + + authorizationAny, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return false, status.Errorf(codes.Internal, err.Error()) + } + authorizations = append(authorizations, &types.AuthorizationGrant{ + Authorization: authorizationAny, + Expiration: auth.Expiration, + }) + } + return true, nil + }) + if err != nil { + return nil, err + } + + return &types.QueryAuthorizationsResponse{ + Authorizations: authorizations, + Pagination: pageRes, + }, nil +} + +// unmarshal an authorization from a store value +func unmarshalAuthorization(cdc codec.BinaryMarshaler, value []byte) (v types.AuthorizationGrant, err error) { + err = cdc.UnmarshalBinaryBare(value, &v) + return v, err +} + +// Authorization implements the Query/Authorization gRPC method. +func (k Keeper) Authorization(c context.Context, req *types.QueryAuthorizationRequest) (*types.QueryAuthorizationResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + if req.MethodName == "" { + return nil, status.Errorf(codes.InvalidArgument, "empty method-name") + } + + granter, err := sdk.AccAddressFromBech32(req.Granter) + + if err != nil { + return nil, err + } + grantee, err := sdk.AccAddressFromBech32(req.Grantee) + + if err != nil { + return nil, err + } + ctx := sdk.UnwrapSDKContext(c) + + authorization, expiration := k.GetOrRevokeAuthorization(ctx, grantee, granter, req.MethodName) + if authorization == nil { + return nil, status.Errorf(codes.NotFound, "no authorization found for %s type", req.MethodName) + } + + authorizationAny, err := codectypes.NewAnyWithValue(authorization) + if err != nil { + return nil, status.Errorf(codes.Internal, err.Error()) + } + + return &types.QueryAuthorizationResponse{ + Authorization: &types.AuthorizationGrant{ + Authorization: authorizationAny, + Expiration: expiration, + }, + }, nil +} diff --git a/x/authz/keeper/grpc_query_test.go b/x/authz/keeper/grpc_query_test.go new file mode 100644 index 000000000000..cc301fbca140 --- /dev/null +++ b/x/authz/keeper/grpc_query_test.go @@ -0,0 +1,157 @@ +package keeper_test + +import ( + gocontext "context" + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +func (suite *TestSuite) TestGRPCQueryAuthorization() { + app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs + var ( + req *types.QueryAuthorizationRequest + expAuthorization types.Authorization + ) + testCases := []struct { + msg string + malleate func() + expPass bool + postTest func(res *types.QueryAuthorizationResponse) + }{ + { + "fail invalid granter addr", + func() { + req = &types.QueryAuthorizationRequest{} + }, + false, + func(res *types.QueryAuthorizationResponse) {}, + }, + { + "fail invalid grantee addr", + func() { + req = &types.QueryAuthorizationRequest{ + Granter: addrs[0].String(), + } + }, + false, + func(res *types.QueryAuthorizationResponse) {}, + }, + { + "fail invalid msg-type", + func() { + req = &types.QueryAuthorizationRequest{ + Granter: addrs[0].String(), + Grantee: addrs[1].String(), + } + }, + false, + func(res *types.QueryAuthorizationResponse) {}, + }, + { + "Success", + func() { + now := ctx.BlockHeader().Time + newCoins := sdk.NewCoins(sdk.NewInt64Coin("steak", 100)) + expAuthorization = &types.SendAuthorization{SpendLimit: newCoins} + err := app.AuthzKeeper.Grant(ctx, addrs[0], addrs[1], expAuthorization, now.Add(time.Hour)) + suite.Require().NoError(err) + req = &types.QueryAuthorizationRequest{ + Granter: addrs[1].String(), + Grantee: addrs[0].String(), + MethodName: expAuthorization.MethodName(), + } + }, + true, + func(res *types.QueryAuthorizationResponse) { + var auth types.Authorization + err := suite.app.InterfaceRegistry().UnpackAny(res.Authorization.Authorization, &auth) + suite.Require().NoError(err) + suite.Require().NotNil(auth) + suite.Require().Equal(auth.String(), expAuthorization.String()) + }, + }, + } + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + result, err := queryClient.Authorization(gocontext.Background(), req) + if testCase.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + testCase.postTest(result) + }) + } +} + +func (suite *TestSuite) TestGRPCQueryAuthorizations() { + app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs + var ( + req *types.QueryAuthorizationsRequest + expAuthorization types.Authorization + ) + testCases := []struct { + msg string + malleate func() + expPass bool + postTest func(res *types.QueryAuthorizationsResponse) + }{ + { + "fail invalid granter addr", + func() { + req = &types.QueryAuthorizationsRequest{} + }, + false, + func(res *types.QueryAuthorizationsResponse) {}, + }, + { + "fail invalid grantee addr", + func() { + req = &types.QueryAuthorizationsRequest{ + Granter: addrs[0].String(), + } + }, + false, + func(res *types.QueryAuthorizationsResponse) {}, + }, + { + "Success", + func() { + now := ctx.BlockHeader().Time + newCoins := sdk.NewCoins(sdk.NewInt64Coin("steak", 100)) + expAuthorization = &types.SendAuthorization{SpendLimit: newCoins} + err := app.AuthzKeeper.Grant(ctx, addrs[0], addrs[1], expAuthorization, now.Add(time.Hour)) + suite.Require().NoError(err) + req = &types.QueryAuthorizationsRequest{ + Granter: addrs[1].String(), + Grantee: addrs[0].String(), + } + }, + true, + func(res *types.QueryAuthorizationsResponse) { + var auth types.Authorization + suite.Require().Equal(1, len(res.Authorizations)) + err := suite.app.InterfaceRegistry().UnpackAny(res.Authorizations[0].Authorization, &auth) + suite.Require().NoError(err) + suite.Require().NotNil(auth) + suite.Require().Equal(auth.String(), expAuthorization.String()) + }, + }, + } + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + result, err := queryClient.Authorizations(gocontext.Background(), req) + if testCase.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + testCase.postTest(result) + }) + } +} diff --git a/x/authz/keeper/keeper.go b/x/authz/keeper/keeper.go new file mode 100644 index 000000000000..2888e17449b9 --- /dev/null +++ b/x/authz/keeper/keeper.go @@ -0,0 +1,210 @@ +package keeper + +import ( + "fmt" + "time" + + "github.com/gogo/protobuf/proto" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + 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/types" +) + +type Keeper struct { + storeKey sdk.StoreKey + cdc codec.BinaryMarshaler + router *baseapp.MsgServiceRouter +} + +// NewKeeper constructs a message authorization Keeper +func NewKeeper(storeKey sdk.StoreKey, cdc codec.BinaryMarshaler, router *baseapp.MsgServiceRouter) Keeper { + return Keeper{ + storeKey: storeKey, + cdc: cdc, + router: router, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// getAuthorizationGrant returns grant between granter and grantee for the given msg type +func (k Keeper) getAuthorizationGrant(ctx sdk.Context, grantStoreKey []byte) (grant types.AuthorizationGrant, found bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(grantStoreKey) + if bz == nil { + return grant, false + } + k.cdc.MustUnmarshalBinaryBare(bz, &grant) + return grant, true +} + +func (k Keeper) update(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, updated types.Authorization) error { + grantStoreKey := types.GetAuthorizationStoreKey(grantee, granter, updated.MethodName()) + grant, found := k.getAuthorizationGrant(ctx, grantStoreKey) + if !found { + return sdkerrors.Wrapf(sdkerrors.ErrNotFound, "authorization not found") + } + + msg, ok := updated.(proto.Message) + if !ok { + sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", updated) + } + + any, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return err + } + + grant.Authorization = any + store := ctx.KVStore(k.storeKey) + store.Set(grantStoreKey, k.cdc.MustMarshalBinaryBare(&grant)) + return nil +} + +// DispatchActions attempts to execute the provided messages via authorization +// grants from the message signer to the grantee. +func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, serviceMsgs []sdk.ServiceMsg) (*sdk.Result, error) { + var msgResult *sdk.Result + var err error + for _, serviceMsg := range serviceMsgs { + signers := serviceMsg.GetSigners() + if len(signers) != 1 { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "authorization can be given to msg with only one signer") + } + granter := signers[0] + if !granter.Equals(grantee) { + authorization, _ := k.GetOrRevokeAuthorization(ctx, grantee, granter, serviceMsg.MethodName) + 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") + } + if del { + k.Revoke(ctx, grantee, granter, serviceMsg.Type()) + } else if updated != nil { + err = k.update(ctx, grantee, granter, updated) + if err != nil { + return nil, err + } + } + } + handler := k.router.Handler(serviceMsg.Route()) + + if handler == nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s", serviceMsg.Route()) + } + + msgResult, err = handler(ctx, serviceMsg.Request) + if err != nil { + return nil, sdkerrors.Wrapf(err, "failed to execute message; message %s", serviceMsg.MethodName) + } + } + + return msgResult, nil +} + +// 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 { + store := ctx.KVStore(k.storeKey) + + grant, err := types.NewAuthorizationGrant(authorization, expiration) + if err != nil { + return err + } + + bz := k.cdc.MustMarshalBinaryBare(&grant) + grantStoreKey := types.GetAuthorizationStoreKey(grantee, granter, authorization.MethodName()) + store.Set(grantStoreKey, bz) + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventGrantAuthorization, + sdk.NewAttribute(types.AttributeKeyGrantType, authorization.MethodName()), + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(types.AttributeKeyGranterAddress, granter.String()), + sdk.NewAttribute(types.AttributeKeyGranteeAddress, grantee.String()), + ), + ) + return nil +} + +// Revoke method revokes any authorization for the provided message type granted to the grantee by the granter. +func (k Keeper) Revoke(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) error { + store := ctx.KVStore(k.storeKey) + grantStoreKey := types.GetAuthorizationStoreKey(grantee, granter, msgType) + _, found := k.getAuthorizationGrant(ctx, grantStoreKey) + if !found { + return sdkerrors.Wrap(sdkerrors.ErrNotFound, "authorization not found") + } + store.Delete(grantStoreKey) + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventRevokeAuthorization, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(types.AttributeKeyGrantType, msgType), + sdk.NewAttribute(types.AttributeKeyGranterAddress, granter.String()), + sdk.NewAttribute(types.AttributeKeyGranteeAddress, grantee.String()), + ), + ) + return nil +} + +// 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) { + store := ctx.KVStore(k.storeKey) + key := types.GetAuthorizationStoreKey(grantee, granter, "") + iter := sdk.KVStorePrefixIterator(store, key) + defer iter.Close() + var authorization types.AuthorizationGrant + for ; iter.Valid(); iter.Next() { + k.cdc.MustUnmarshalBinaryBare(iter.Value(), &authorization) + authorizations = append(authorizations, authorization.GetAuthorizationGrant()) + } + return authorizations +} + +// 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) { + grant, found := k.getAuthorizationGrant(ctx, types.GetAuthorizationStoreKey(grantee, granter, msgType)) + if !found { + return nil, time.Time{} + } + if grant.Expiration.Before(ctx.BlockHeader().Time) { + k.Revoke(ctx, grantee, granter, msgType) + return nil, time.Time{} + } + + return grant.GetAuthorizationGrant(), grant.Expiration +} + +// IterateGrants iterates over all authorization grants +func (k Keeper) IterateGrants(ctx sdk.Context, + handler func(granterAddr sdk.AccAddress, granteeAddr sdk.AccAddress, grant types.AuthorizationGrant) bool) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.GrantKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + var grant types.AuthorizationGrant + granterAddr, granteeAddr := types.ExtractAddressesFromGrantKey(iter.Key()) + k.cdc.MustUnmarshalBinaryBare(iter.Value(), &grant) + if handler(granterAddr, granteeAddr, grant) { + break + } + } +} diff --git a/x/authz/keeper/keeper_test.go b/x/authz/keeper/keeper_test.go new file mode 100644 index 000000000000..3d53ac8ce4da --- /dev/null +++ b/x/authz/keeper/keeper_test.go @@ -0,0 +1,210 @@ +package keeper_test + +import ( + "testing" + "time" + + proto "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/baseapp" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" + + "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" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +type TestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + addrs []sdk.AccAddress + queryClient types.QueryClient +} + +func (s *TestSuite) SetupTest() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + now := tmtime.Now() + ctx = ctx.WithBlockHeader(tmproto.Header{Time: now}) + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, app.AuthzKeeper) + queryClient := types.NewQueryClient(queryHelper) + s.queryClient = queryClient + + s.app = app + s.ctx = ctx + s.queryClient = queryClient + s.addrs = simapp.AddTestAddrsIncremental(app, ctx, 3, sdk.NewInt(30000000)) + +} + +func (s *TestSuite) TestKeeper() { + app, ctx, addrs := s.app, s.ctx, s.addrs + + granterAddr := addrs[0] + granteeAddr := addrs[1] + recipientAddr := addrs[2] + + s.T().Log("verify that no authorization returns nil") + authorization, expiration := app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + s.Require().Nil(authorization) + s.Require().Equal(expiration, time.Time{}) + now := s.ctx.BlockHeader().Time + s.Require().NotNil(now) + + 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)) + s.Require().NoError(err) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + s.Require().Nil(authorization) + + s.T().Log("verify if authorization is accepted") + x = &types.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()) + s.Require().NotNil(authorization) + s.Require().Equal(authorization.MethodName(), types.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()) + s.Require().Nil(authorization) + + s.T().Log("verify revoke fails with wrong information") + err = app.AuthzKeeper.Revoke(ctx, recipientAddr, granterAddr, types.SendAuthorization{}.MethodName()) + s.Require().Error(err) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, recipientAddr, granterAddr, types.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()) + s.Require().NoError(err) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + s.Require().Nil(authorization) + +} + +func (s *TestSuite) TestKeeperIter() { + app, ctx, addrs := s.app, s.ctx, s.addrs + + granterAddr := addrs[0] + granteeAddr := addrs[1] + + s.T().Log("verify that no authorization returns nil") + authorization, expiration := app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, "Abcd") + s.Require().Nil(authorization) + s.Require().Equal(time.Time{}, expiration) + now := s.ctx.BlockHeader().Time + s.Require().NotNil(now) + + 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)) + s.Require().NoError(err) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, "abcd") + s.Require().Nil(authorization) + + app.AuthzKeeper.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant types.AuthorizationGrant) bool { + s.Require().Equal(granter, granterAddr) + s.Require().Equal(grantee, granteeAddr) + return true + }) + +} + +func (s *TestSuite) TestKeeperFees() { + app, addrs := s.app, s.addrs + + granterAddr := addrs[0] + granteeAddr := addrs[1] + recipientAddr := addrs[2] + s.Require().NoError(simapp.FundAccount(app, s.ctx, granterAddr, sdk.NewCoins(sdk.NewInt64Coin("steak", 10000)))) + now := s.ctx.BlockHeader().Time + s.Require().NotNil(now) + + smallCoin := sdk.NewCoins(sdk.NewInt64Coin("steak", 20)) + someCoin := sdk.NewCoins(sdk.NewInt64Coin("steak", 123)) + + msgs := types.NewMsgExecAuthorized(granteeAddr, []sdk.ServiceMsg{ + { + MethodName: types.SendAuthorization{}.MethodName(), + Request: &banktypes.MsgSend{ + Amount: sdk.NewCoins(sdk.NewInt64Coin("steak", 2)), + FromAddress: granterAddr.String(), + ToAddress: recipientAddr.String(), + }, + }, + }) + + s.Require().NoError(msgs.UnpackInterfaces(app.AppCodec())) + + s.T().Log("verify dispatch fails with invalid authorization") + executeMsgs, err := msgs.GetServiceMsgs() + s.Require().NoError(err) + result, err := app.AuthzKeeper.DispatchActions(s.ctx, granteeAddr, executeMsgs) + + s.Require().Nil(result) + s.Require().NotNil(err) + + s.T().Log("verify dispatch executes with correct information") + // grant authorization + err = app.AuthzKeeper.Grant(s.ctx, granteeAddr, granterAddr, &types.SendAuthorization{SpendLimit: smallCoin}, now) + s.Require().NoError(err) + authorization, _ := app.AuthzKeeper.GetOrRevokeAuthorization(s.ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + s.Require().NotNil(authorization) + + s.Require().Equal(authorization.MethodName(), types.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) + + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(s.ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + s.Require().NotNil(authorization) + + s.T().Log("verify dispatch fails with overlimit") + // grant authorization + + msgs = types.NewMsgExecAuthorized(granteeAddr, []sdk.ServiceMsg{ + { + MethodName: types.SendAuthorization{}.MethodName(), + Request: &banktypes.MsgSend{ + Amount: someCoin, + FromAddress: granterAddr.String(), + ToAddress: recipientAddr.String(), + }, + }, + }) + + s.Require().NoError(msgs.UnpackInterfaces(app.AppCodec())) + executeMsgs, err = msgs.GetServiceMsgs() + s.Require().NoError(err) + + result, err = app.AuthzKeeper.DispatchActions(s.ctx, granteeAddr, executeMsgs) + s.Require().Nil(result) + s.Require().NotNil(err) + + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(s.ctx, granteeAddr, granterAddr, types.SendAuthorization{}.MethodName()) + s.Require().NotNil(authorization) +} + +func TestTestSuite(t *testing.T) { + suite.Run(t, new(TestSuite)) +} diff --git a/x/authz/keeper/msg_server.go b/x/authz/keeper/msg_server.go new file mode 100644 index 000000000000..53beb129dfaa --- /dev/null +++ b/x/authz/keeper/msg_server.go @@ -0,0 +1,75 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +var _ types.MsgServer = Keeper{} + +// GrantAuthorization implements the MsgServer.GrantAuthorization method. +func (k Keeper) GrantAuthorization(goCtx context.Context, msg *types.MsgGrantAuthorizationRequest) (*types.MsgGrantAuthorizationResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + grantee, err := sdk.AccAddressFromBech32(msg.Grantee) + if err != nil { + return nil, err + } + granter, err := sdk.AccAddressFromBech32(msg.Granter) + if err != nil { + return nil, err + } + + authorization := msg.GetGrantAuthorization() + // If the granted service Msg doesn't exist, we throw an error. + if k.router.Handler(authorization.MethodName()) == nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "%s doesn't exist.", authorization.MethodName()) + } + + err = k.Grant(ctx, grantee, granter, authorization, msg.Expiration) + if err != nil { + return nil, err + } + + return &types.MsgGrantAuthorizationResponse{}, nil +} + +// RevokeAuthorization implements the MsgServer.RevokeAuthorization method. +func (k Keeper) RevokeAuthorization(goCtx context.Context, msg *types.MsgRevokeAuthorizationRequest) (*types.MsgRevokeAuthorizationResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + grantee, err := sdk.AccAddressFromBech32(msg.Grantee) + if err != nil { + return nil, err + } + granter, err := sdk.AccAddressFromBech32(msg.Granter) + if err != nil { + return nil, err + } + + err = k.Revoke(ctx, grantee, granter, msg.MethodName) + if err != nil { + return nil, err + } + + return &types.MsgRevokeAuthorizationResponse{}, nil +} + +// ExecAuthorized implements the MsgServer.ExecAuthorized method. +func (k Keeper) ExecAuthorized(goCtx context.Context, msg *types.MsgExecAuthorizedRequest) (*types.MsgExecAuthorizedResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + grantee, err := sdk.AccAddressFromBech32(msg.Grantee) + if err != nil { + return nil, err + } + msgs, err := msg.GetServiceMsgs() + if err != nil { + return nil, err + } + result, err := k.DispatchActions(ctx, grantee, msgs) + if err != nil { + return nil, err + } + return &types.MsgExecAuthorizedResponse{Result: result}, nil +} diff --git a/x/authz/module.go b/x/authz/module.go new file mode 100644 index 000000000000..b39c02487fe0 --- /dev/null +++ b/x/authz/module.go @@ -0,0 +1,197 @@ +package authz + +import ( + "context" + "encoding/json" + "math/rand" + + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" + + sdkclient "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "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/types/module" + "github.com/cosmos/cosmos-sdk/x/authz/keeper" + "github.com/cosmos/cosmos-sdk/x/authz/types" + + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + + "github.com/cosmos/cosmos-sdk/x/authz/client/cli" + "github.com/cosmos/cosmos-sdk/x/authz/simulation" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} +) + +// AppModuleBasic defines the basic application module used by the authz module. +type AppModuleBasic struct { + cdc codec.Marshaler +} + +// Name returns the authz module's name. +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterServices registers a gRPC query service to respond to the +// module-specific gRPC queries. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) + types.RegisterMsgServer(cfg.MsgServer(), am.keeper) +} + +// RegisterLegacyAminoCodec registers the authz module's types for the given codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {} + +// RegisterInterfaces registers the authz module's interface types +func (AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +// DefaultGenesis returns default genesis state as raw bytes for the authz +// module. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) +} + +// ValidateGenesis performs genesis state validation for the authz module. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config sdkclient.TxEncodingConfig, bz json.RawMessage) error { + var data types.GenesisState + if err := cdc.UnmarshalJSON(bz, &data); err != nil { + return sdkerrors.Wrapf(err, "failed to unmarshal %s genesis state", types.ModuleName) + } + + return types.ValidateGenesis(data) +} + +// RegisterRESTRoutes registers the REST routes for the authz module. +func (AppModuleBasic) RegisterRESTRoutes(clientCtx sdkclient.Context, r *mux.Router) { +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the authz module. +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx sdkclient.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) +} + +// GetQueryCmd returns the cli query commands for the authz module +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// GetTxCmd returns the transaction commands for the authz module +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// AppModule implements the sdk.AppModule interface +type AppModule struct { + AppModuleBasic + keeper keeper.Keeper + accountKeeper types.AccountKeeper + bankKeeper types.BankKeeper + registry cdctypes.InterfaceRegistry +} + +// NewAppModule creates a new AppModule object +func NewAppModule(cdc codec.Marshaler, keeper keeper.Keeper, ak types.AccountKeeper, bk types.BankKeeper, registry cdctypes.InterfaceRegistry) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{cdc: cdc}, + keeper: keeper, + accountKeeper: ak, + bankKeeper: bk, + registry: registry, + } +} + +// Name returns the authz module's name. +func (AppModule) Name() string { + return types.ModuleName +} + +// RegisterInvariants does nothing, there are no invariants to enforce +func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// Route returns the message routing key for the staking module. +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(types.RouterKey, nil) +} + +func (am AppModule) NewHandler() sdk.Handler { + return nil +} + +// QuerierRoute returns the route we respond to for abci queries +func (AppModule) QuerierRoute() string { return "" } + +// LegacyQuerierHandler returns the authz module sdk.Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return nil +} + +// InitGenesis performs genesis initialization for the authz module. It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + InitGenesis(ctx, am.keeper, &genesisState) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the authz +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + gs := ExportGenesis(ctx, am.keeper) + return cdc.MustMarshalJSON(gs) +} + +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {} + +// EndBlock does nothing +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// ____________________________________________________________________________ + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the authz module. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents returns all the authz content functions used to +// simulate governance proposals. +func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RandomizedParams creates randomized authz param changes for the simulator. +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { + return nil +} + +// RegisterStoreDecoder registers a decoder for authz module's types +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc) +} + +// WeightedOperations returns the all the gov module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + protoCdc := codec.NewProtoCodec(am.registry) + return simulation.WeightedOperations( + simState.AppParams, simState.Cdc, + am.accountKeeper, am.bankKeeper, am.keeper, am.cdc, protoCdc, + ) +} diff --git a/x/authz/simulation/decoder.go b/x/authz/simulation/decoder.go new file mode 100644 index 000000000000..c87f7a70c13a --- /dev/null +++ b/x/authz/simulation/decoder.go @@ -0,0 +1,26 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +// NewDecodeStore returns a decoder function closure that umarshals the KVPair's +// Value to the corresponding authz type. +func NewDecodeStore(cdc codec.Marshaler) func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.GrantKey): + var grantA, grantB types.AuthorizationGrant + cdc.MustUnmarshalBinaryBare(kvA.Value, &grantA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &grantB) + return fmt.Sprintf("%v\n%v", grantA, grantB) + default: + panic(fmt.Sprintf("invalid authz key %X", kvA.Key)) + } + } +} diff --git a/x/authz/simulation/decoder_test.go b/x/authz/simulation/decoder_test.go new file mode 100644 index 000000000000..86c2dfd6abb4 --- /dev/null +++ b/x/authz/simulation/decoder_test.go @@ -0,0 +1,50 @@ +package simulation_test + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/authz/simulation" + "github.com/cosmos/cosmos-sdk/x/authz/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()) + grantBz, err := cdc.MarshalBinaryBare(&grant) + require.NoError(t, err) + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + {Key: []byte(types.GrantKey), Value: grantBz}, + {Key: []byte{0x99}, Value: []byte{0x99}}, + }, + } + + tests := []struct { + name string + expectedLog string + }{ + {"Grant", fmt.Sprintf("%v\n%v", grant, grant)}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, 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) + } + }) + } +} diff --git a/x/authz/simulation/genesis.go b/x/authz/simulation/genesis.go new file mode 100644 index 000000000000..ea674e24ea62 --- /dev/null +++ b/x/authz/simulation/genesis.go @@ -0,0 +1,38 @@ +package simulation + +import ( + "encoding/json" + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +// Simulation parameter constant. +const authz = "authz" + +// GenAuthorizationGrant returns an empty slice of authorization grants. +func GenAuthorizationGrant(_ *rand.Rand, _ []simtypes.Account) []types.GrantAuthorization { + return []types.GrantAuthorization{} +} + +// RandomizedGenState generates a random GenesisState for authz. +func RandomizedGenState(simState *module.SimulationState) { + var grants []types.GrantAuthorization + + simState.AppParams.GetOrGenerate( + simState.Cdc, authz, &grants, simState.Rand, + func(r *rand.Rand) { grants = GenAuthorizationGrant(r, simState.Accounts) }, + ) + authzGrantsGenesis := types.NewGenesisState(grants) + + bz, err := json.MarshalIndent(&authzGrantsGenesis, "", " ") + if err != nil { + panic(err) + } + + fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, bz) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(authzGrantsGenesis) +} diff --git a/x/authz/simulation/genesis_test.go b/x/authz/simulation/genesis_test.go new file mode 100644 index 000000000000..4672d195ec0a --- /dev/null +++ b/x/authz/simulation/genesis_test.go @@ -0,0 +1,40 @@ +package simulation_test + +import ( + "encoding/json" + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/authz/simulation" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +func TestRandomizedGenState(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + + s := rand.NewSource(1) + r := rand.New(s) + + simState := module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + NumBonded: 3, + Accounts: simtypes.RandomAccounts(r, 3), + InitialStake: 1000, + GenState: make(map[string]json.RawMessage), + } + + simulation.RandomizedGenState(&simState) + var authzGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &authzGenesis) + + require.Len(t, authzGenesis.Authorization, 0) +} diff --git a/x/authz/simulation/operations.go b/x/authz/simulation/operations.go new file mode 100644 index 000000000000..5533d9fa6916 --- /dev/null +++ b/x/authz/simulation/operations.go @@ -0,0 +1,291 @@ +package simulation + +import ( + "context" + "math/rand" + "strings" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/simapp/helpers" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/authz/keeper" + "github.com/cosmos/cosmos-sdk/x/authz/types" + banktype "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/simulation" +) + +// authz message types +const ( + TypeMsgGrantAuthorization = "/cosmos.authz.v1beta1.Msg/GrantAuthorization" + TypeMsgRevokeAuthorization = "/cosmos.authz.v1beta1.Msg/RevokeAuthorization" + TypeMsgExecDelegated = "/cosmos.authz.v1beta1.Msg/ExecAuthorized" +) + +// Simulation operation weights constants +const ( + OpWeightMsgGrantAuthorization = "op_weight_msg_grant_authorization" + OpWeightRevokeAuthorization = "op_weight_msg_revoke_authorization" + OpWeightExecAuthorized = "op_weight_msg_execute_authorized" +) + +// WeightedOperations returns all the operations from the module with their respective weights +func WeightedOperations( + appParams simtypes.AppParams, cdc codec.JSONMarshaler, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, appCdc cdctypes.AnyUnpacker, protoCdc *codec.ProtoCodec) simulation.WeightedOperations { + + var ( + weightMsgGrantAuthorization int + weightRevokeAuthorization int + weightExecAuthorized int + ) + + appParams.GetOrGenerate(cdc, OpWeightMsgGrantAuthorization, &weightMsgGrantAuthorization, nil, + func(_ *rand.Rand) { + weightMsgGrantAuthorization = simappparams.DefaultWeightMsgDelegate + }, + ) + + appParams.GetOrGenerate(cdc, OpWeightRevokeAuthorization, &weightRevokeAuthorization, nil, + func(_ *rand.Rand) { + weightRevokeAuthorization = simappparams.DefaultWeightMsgUndelegate + }, + ) + + appParams.GetOrGenerate(cdc, OpWeightExecAuthorized, &weightExecAuthorized, nil, + func(_ *rand.Rand) { + weightExecAuthorized = simappparams.DefaultWeightMsgSend + }, + ) + + return simulation.WeightedOperations{ + simulation.NewWeightedOperation( + weightMsgGrantAuthorization, + SimulateMsgGrantAuthorization(ak, bk, k, protoCdc), + ), + simulation.NewWeightedOperation( + weightRevokeAuthorization, + SimulateMsgRevokeAuthorization(ak, bk, k, protoCdc), + ), + simulation.NewWeightedOperation( + weightExecAuthorized, + SimulateMsgExecuteAuthorized(ak, bk, k, appCdc, protoCdc), + ), + } +} + +// SimulateMsgGrantAuthorization generates a MsgGrantAuthorization with random values. +// nolint: funlen +func SimulateMsgGrantAuthorization(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, + protoCdc *codec.ProtoCodec) simtypes.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + granter := accs[0] + grantee := accs[1] + + account := ak.GetAccount(ctx, granter.Address) + + spendableCoins := bk.SpendableCoins(ctx, account.GetAddress()) + fees, err := simtypes.RandomFees(r, ctx, spendableCoins) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgGrantAuthorization, err.Error()), nil, err + } + + blockTime := ctx.BlockTime() + msg, err := types.NewMsgGrantAuthorization(granter.Address, grantee.Address, + types.NewSendAuthorization(spendableCoins.Sub(fees)), blockTime.AddDate(1, 0, 0)) + + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgGrantAuthorization, err.Error()), nil, err + } + txGen := simappparams.MakeTestEncodingConfig().TxConfig + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + authzMsgClient := types.NewMsgClient(svcMsgClientConn) + _, err = authzMsgClient.GrantAuthorization(context.Background(), msg) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgGrantAuthorization, err.Error()), nil, err + } + tx, err := helpers.GenTx( + txGen, + svcMsgClientConn.GetMsgs(), + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{account.GetAccountNumber()}, + []uint64{account.GetSequence()}, + granter.PrivKey, + ) + + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgGrantAuthorization, "unable to generate mock tx"), nil, err + } + + _, _, err = app.Deliver(txGen.TxEncoder(), tx) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, svcMsgClientConn.GetMsgs()[0].Type(), "unable to deliver tx"), nil, err + } + return simtypes.NewOperationMsg(svcMsgClientConn.GetMsgs()[0], true, "", protoCdc), nil, err + } +} + +// SimulateMsgRevokeAuthorization generates a MsgRevokeAuthorization with random values. +// nolint: funlen +func SimulateMsgRevokeAuthorization(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, protoCdc *codec.ProtoCodec) simtypes.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + hasGrant := false + var targetGrant types.AuthorizationGrant + var granterAddr sdk.AccAddress + var granteeAddr sdk.AccAddress + k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant types.AuthorizationGrant) bool { + targetGrant = grant + granterAddr = granter + granteeAddr = grantee + hasGrant = true + return true + }) + + if !hasGrant { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgRevokeAuthorization, "no grants"), nil, nil + } + + granter, ok := simtypes.FindAccount(accs, granterAddr) + if !ok { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgRevokeAuthorization, "Account not found"), nil, nil + } + account := ak.GetAccount(ctx, granter.Address) + + spendableCoins := bk.SpendableCoins(ctx, account.GetAddress()) + fees, err := simtypes.RandomFees(r, ctx, spendableCoins) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgRevokeAuthorization, "fee error"), nil, err + } + auth := targetGrant.GetAuthorizationGrant() + msg := types.NewMsgRevokeAuthorization(granterAddr, granteeAddr, auth.MethodName()) + + txGen := simappparams.MakeTestEncodingConfig().TxConfig + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + authzMsgClient := types.NewMsgClient(svcMsgClientConn) + _, err = authzMsgClient.RevokeAuthorization(context.Background(), &msg) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgRevokeAuthorization, err.Error()), nil, err + } + tx, err := helpers.GenTx( + txGen, + svcMsgClientConn.GetMsgs(), + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{account.GetAccountNumber()}, + []uint64{account.GetSequence()}, + granter.PrivKey, + ) + + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgRevokeAuthorization, err.Error()), nil, err + } + + _, _, err = app.Deliver(txGen.TxEncoder(), tx) + return simtypes.NewOperationMsg(svcMsgClientConn.GetMsgs()[0], true, "", protoCdc), nil, err + } +} + +// SimulateMsgExecuteAuthorized generates a MsgExecuteAuthorized with random values. +// nolint: funlen +func SimulateMsgExecuteAuthorized(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, cdc cdctypes.AnyUnpacker, protoCdc *codec.ProtoCodec) simtypes.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + + hasGrant := false + var targetGrant types.AuthorizationGrant + var granterAddr sdk.AccAddress + var granteeAddr sdk.AccAddress + k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant types.AuthorizationGrant) bool { + targetGrant = grant + granterAddr = granter + granteeAddr = grantee + hasGrant = true + return true + }) + + if !hasGrant { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, "Not found"), nil, nil + } + + grantee, _ := simtypes.FindAccount(accs, granteeAddr) + granterAccount := ak.GetAccount(ctx, granterAddr) + granteeAccount := ak.GetAccount(ctx, granteeAddr) + + granterspendableCoins := bk.SpendableCoins(ctx, granterAccount.GetAddress()) + if granterspendableCoins.Empty() { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, "no coins"), nil, nil + } + + if targetGrant.Expiration.Before(ctx.BlockHeader().Time) { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, "grant expired"), nil, nil + } + + granteespendableCoins := bk.SpendableCoins(ctx, granteeAccount.GetAddress()) + fees, err := simtypes.RandomFees(r, ctx, granteespendableCoins) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, "fee error"), nil, err + } + sendCoins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(10))) + + execMsg := sdk.ServiceMsg{ + MethodName: types.SendAuthorization{}.MethodName(), + Request: banktype.NewMsgSend( + granterAddr, + granteeAddr, + sendCoins, + ), + } + + 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 + } + + txGen := simappparams.MakeTestEncodingConfig().TxConfig + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + authzMsgClient := types.NewMsgClient(svcMsgClientConn) + _, err = authzMsgClient.ExecAuthorized(context.Background(), &msg) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, err.Error()), nil, err + } + tx, err := helpers.GenTx( + txGen, + svcMsgClientConn.GetMsgs(), + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{granteeAccount.GetAccountNumber()}, + []uint64{granteeAccount.GetSequence()}, + grantee.PrivKey, + ) + + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, err.Error()), nil, err + } + _, _, err = app.Deliver(txGen.TxEncoder(), tx) + if err != nil { + if strings.Contains(err.Error(), "insufficient fee") { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, "insufficient fee"), nil, nil + } + return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, err.Error()), nil, err + } + msg.UnpackInterfaces(cdc) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, "unmarshal error"), nil, err + } + return simtypes.NewOperationMsg(svcMsgClientConn.GetMsgs()[0], true, "success", protoCdc), nil, nil + } +} diff --git a/x/authz/simulation/operations_test.go b/x/authz/simulation/operations_test.go new file mode 100644 index 000000000000..b3b447dcdb09 --- /dev/null +++ b/x/authz/simulation/operations_test.go @@ -0,0 +1,196 @@ +package simulation_test + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/suite" + + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + sdk "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/authz/simulation" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +type SimTestSuite struct { + suite.Suite + + ctx sdk.Context + app *simapp.SimApp + protoCdc *codec.ProtoCodec +} + +func (suite *SimTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + suite.app = app + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{}) + suite.protoCdc = codec.NewProtoCodec(suite.app.InterfaceRegistry()) +} + +func (suite *SimTestSuite) TestWeightedOperations() { + cdc := suite.app.AppCodec() + appParams := make(simtypes.AppParams) + + weightesOps := simulation.WeightedOperations(appParams, cdc, suite.app.AccountKeeper, + suite.app.BankKeeper, suite.app.AuthzKeeper, cdc, suite.protoCdc, + ) + + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accs := suite.getTestingAccounts(r, 3) + + expected := []struct { + weight int + opMsgRoute string + opMsgName string + }{ + {simappparams.DefaultWeightMsgDelegate, types.ModuleName, simulation.TypeMsgGrantAuthorization}, + {simappparams.DefaultWeightMsgUndelegate, types.ModuleName, simulation.TypeMsgRevokeAuthorization}, + {simappparams.DefaultWeightMsgSend, types.ModuleName, simulation.TypeMsgExecDelegated}, + } + + for i, w := range weightesOps { + operationMsg, _, _ := w.Op()(r, suite.app.BaseApp, suite.ctx, accs, "") + // the following checks are very much dependent from the ordering of the output given + // by WeightedOperations. if the ordering in WeightedOperations changes some tests + // will fail + suite.Require().Equal(expected[i].weight, w.Weight(), "weight should be the same") + suite.Require().Equal(expected[i].opMsgRoute, operationMsg.Route, "route should be the same") + suite.Require().Equal(expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") + } +} + +func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Account { + accounts := simtypes.RandomAccounts(r, n) + + initAmt := sdk.TokensFromConsensusPower(200000) + initCoins := sdk.NewCoins(sdk.NewCoin("foo", initAmt)) + + // add coins to the accounts + for _, account := range accounts { + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, account.Address) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + suite.Require().NoError(simapp.FundAccount(suite.app, suite.ctx, account.Address, initCoins)) + } + + return accounts +} + +func (suite *SimTestSuite) TestSimulateGrantAuthorization() { + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accounts := suite.getTestingAccounts(r, 2) + blockTime := time.Now().UTC() + ctx := suite.ctx.WithBlockTime(blockTime) + + // begin a new block + suite.app.BeginBlock(abci.RequestBeginBlock{ + Header: tmproto.Header{ + Height: suite.app.LastBlockHeight() + 1, + AppHash: suite.app.LastCommitID().Hash, + }, + }) + + granter := accounts[0] + grantee := accounts[1] + + // execute operation + op := simulation.SimulateMsgGrantAuthorization(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.AuthzKeeper, suite.protoCdc) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgGrantAuthorizationRequest + suite.app.AppCodec().UnmarshalJSON(operationMsg.Msg, &msg) + suite.Require().True(operationMsg.OK) + suite.Require().Equal(granter.Address.String(), msg.Granter) + suite.Require().Equal(grantee.Address.String(), msg.Grantee) + suite.Require().Len(futureOperations, 0) + +} + +func (suite *SimTestSuite) TestSimulateRevokeAuthorization() { + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accounts := suite.getTestingAccounts(r, 3) + + // begin a new block + suite.app.BeginBlock(abci.RequestBeginBlock{ + Header: tmproto.Header{ + Height: suite.app.LastBlockHeight() + 1, + AppHash: suite.app.LastCommitID().Hash, + }}) + + initAmt := sdk.TokensFromConsensusPower(200000) + initCoins := sdk.NewCoins(sdk.NewCoin("foo", initAmt)) + + granter := accounts[0] + grantee := accounts[1] + authorization := types.NewSendAuthorization(initCoins) + + err := suite.app.AuthzKeeper.Grant(suite.ctx, grantee.Address, granter.Address, authorization, time.Now().Add(30*time.Hour)) + suite.Require().NoError(err) + + // execute operation + op := simulation.SimulateMsgRevokeAuthorization(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.AuthzKeeper, suite.protoCdc) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgRevokeAuthorizationRequest + suite.app.AppCodec().UnmarshalJSON(operationMsg.Msg, &msg) + + 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().Len(futureOperations, 0) + +} + +func (suite *SimTestSuite) TestSimulateExecAuthorization() { + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accounts := suite.getTestingAccounts(r, 3) + + // begin a new block + suite.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash}}) + + initAmt := sdk.TokensFromConsensusPower(200000) + initCoins := sdk.NewCoins(sdk.NewCoin("foo", initAmt)) + + granter := accounts[0] + grantee := accounts[1] + authorization := types.NewSendAuthorization(initCoins) + + err := suite.app.AuthzKeeper.Grant(suite.ctx, grantee.Address, granter.Address, authorization, time.Now().Add(30*time.Hour)) + suite.Require().NoError(err) + + // execute operation + op := simulation.SimulateMsgExecuteAuthorized(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.AuthzKeeper, suite.app.AppCodec(), suite.protoCdc) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgExecAuthorizedRequest + + suite.app.AppCodec().UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Equal(grantee.Address.String(), msg.Grantee) + suite.Require().Len(futureOperations, 0) + +} + +func TestSimTestSuite(t *testing.T) { + suite.Run(t, new(SimTestSuite)) +} diff --git a/x/authz/spec/01_concepts.md b/x/authz/spec/01_concepts.md new file mode 100644 index 000000000000..0f5e482f1619 --- /dev/null +++ b/x/authz/spec/01_concepts.md @@ -0,0 +1,37 @@ + + +# Concepts + +## Authorization +Any concrete type of authorization defined in the `x/authz` module must fulfill the `Authorization` interface outlined below. Authorizations determine exactly what privileges are granted. They are extensible and can be defined for any Msg service method even outside of the module where the Msg method is defined. Authorizations use the new `ServiceMsg` type from [ADR 031](../../../architecture/adr-031-msg-service.md). + + ++++ https://github.com/cosmos/cosmos-sdk/blob/c95de9c4177442dee4c69d96917efc955b5d19d9/x/authz/types/authorizations.go#L15-L24 + + +## Built-in Authorizations + +Cosmos-SDK `x/authz` module comes with following authorization types + +### SendAuthorization + +`SendAuthorization` implements `Authorization` interface for the `cosmos.bank.v1beta1.Msg/Send` ServiceMsg, that takes a `SpendLimit` and updates it down to zero. + ++++ https://github.com/cosmos/cosmos-sdk/blob/c95de9c4177442dee4c69d96917efc955b5d19d9/proto/cosmos/authz/v1beta1/authz.proto#L12-L19 + ++++ https://github.com/cosmos/cosmos-sdk/blob/c95de9c4177442dee4c69d96917efc955b5d19d9/x/authz/types/send_authorization.go#L23-L45 + +- `spent_limit` keeps track of how many coins left in the authorization. + + +### GenericAuthorization + +`GenericAuthorization` implements the `Authorization` interface, that gives unrestricted permission to execute the provided ServiceMsg on behalf of granter's account. + ++++ https://github.com/cosmos/cosmos-sdk/blob/c95de9c4177442dee4c69d96917efc955b5d19d9/proto/cosmos/authz/v1beta1/authz.proto#L21-L30 + ++++ https://github.com/cosmos/cosmos-sdk/blob/c95de9c4177442dee4c69d96917efc955b5d19d9/x/authz/types/generic_authorization.go#L20-L28 + +- `method_name` holds ServiceMsg type. diff --git a/x/authz/spec/02_state.md b/x/authz/spec/02_state.md new file mode 100644 index 000000000000..3afc5eddbafb --- /dev/null +++ b/x/authz/spec/02_state.md @@ -0,0 +1,14 @@ + + +# State + +## AuthorizationGrant + +Authorizations are identified by combining granter address (the address bytes of the granter), grantee address (the address bytes of the grantee) and ServiceMsg type (its method name). + +- AuthorizationGrant: `0x01 | granter_address_len (1 byte) | granter_address_bytes | grantee_address_len (1 byte) | grantee_address_bytes | msgType_bytes-> ProtocolBuffer(AuthorizationGrant)` + + ++++ https://github.com/cosmos/cosmos-sdk/blob/c95de9c4177442dee4c69d96917efc955b5d19d9/proto/cosmos/authz/v1beta1/authz.proto#L32-L37 diff --git a/x/authz/spec/03_messages.md b/x/authz/spec/03_messages.md new file mode 100644 index 000000000000..9b3cffa35c24 --- /dev/null +++ b/x/authz/spec/03_messages.md @@ -0,0 +1,42 @@ + + +# Messages + +In this section we describe the processing of messages for the authz module. + +## Msg/GrantAuthorization + +An authorization-grant is created using the `MsgGrantAuthorization` message. + ++++ https://github.com/cosmos/cosmos-sdk/blob/c95de9c4177442dee4c69d96917efc955b5d19d9/proto/cosmos/authz/v1beta1/tx.proto#L27-L35 + +This message is expected to fail if: + +- both granter & grantee have same address. +- provided `Expiration` time less than current unix timestamp. +- provided `Authorization` is not implemented. + +## Msg/RevokeAuthorization + +An allowed authorization can be removed with `MsgRevokeAuthorization` message. + ++++ https://github.com/cosmos/cosmos-sdk/blob/c95de9c4177442dee4c69d96917efc955b5d19d9/proto/cosmos/authz/v1beta1/tx.proto#L53-L59 + +This message is expected to fail if: + +- both granter & grantee have same address. +- provided `MethodName` is empty. + +## Msg/ExecAuthorizedRequest + +When a grantee wants to execute transaction on behalf of a granter, it must send MsgExecAuthorizedRequest. + ++++ https://github.com/cosmos/cosmos-sdk/blob/c95de9c4177442dee4c69d96917efc955b5d19d9/proto/cosmos/authz/v1beta1/tx.proto#L42-L48 + +This message is expected to fail if: + +- authorization not implemented for the provided msg. +- grantee don't have permission to run transaction. +- if granted authorization is expired. \ No newline at end of file diff --git a/x/authz/spec/04_events.md b/x/authz/spec/04_events.md new file mode 100644 index 000000000000..4d0a9858af7f --- /dev/null +++ b/x/authz/spec/04_events.md @@ -0,0 +1,28 @@ + + +# Events + +The authz module emits the following events: + +## Keeper + +### GrantAuthorization + +| Type | Attribute Key | Attribute Value | +|----------------------|-------------------|--------------------| +| grant-authorization | module | authz | +| grant-authorization | grant-type | {msgType} | +| grant-authorization | granter | {granterAddress} | +| grant-authorization | grantee | {granteeAddress} | + + +### RevokeAuthorization + +| Type | Attribute Key | Attribute Value | +|----------------------|-------------------|--------------------| +| revoke-authorization | module | authz | +| revoke-authorization | grant-type | {msgType} | +| revoke-authorization | granter | {granterAddress} | +| revoke-authorization | grantee | {granteeAddress} | diff --git a/x/authz/spec/README.md b/x/authz/spec/README.md new file mode 100644 index 000000000000..07ec1ba0c1e6 --- /dev/null +++ b/x/authz/spec/README.md @@ -0,0 +1,26 @@ + + +# `authz` + +## Contents + +## Abstract +`x/authz` is an implementation of a Cosmos SDK module, per [ADR 30](../../../architecture/adr-030-authz-module.md), that allows +granting arbitrary privileges from one account (the granter) to another account (the grantee). Authorizations must be granted for a particular Msg service method one by one using an implementation of the `Authorization` interface. + +1. **[Concept](01_concepts.md)** + - [Authorization](01_concepts.md#Authorization) + - [Built-in Authorizations](01_concepts.md#Built-in-Authorization) +2. **[State](02_state.md)** +3. **[Messages](03_messages.md)** + - [Msg/GrantAuthorization](03_messages.md#MsgGrantAuthorization) + - [Msg/RevokeAuthorization](03_messages.md#MsgRevokeAuthorization) + - [Msg/ExecAuthorized](03_messages.md#MsgExecAuthorized) +4. **[Events](04_events.md)** + - [Keeper](04_events.md#Keeper) + diff --git a/x/authz/types/authorizations.go b/x/authz/types/authorizations.go new file mode 100644 index 000000000000..18533fe07201 --- /dev/null +++ b/x/authz/types/authorizations.go @@ -0,0 +1,63 @@ +package types + +import ( + "time" + + "github.com/gogo/protobuf/proto" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +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) { + auth := AuthorizationGrant{ + Expiration: expiration, + } + msg, ok := authorization.(proto.Message) + if !ok { + return AuthorizationGrant{}, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", authorization) + } + + any, err := types.NewAnyWithValue(msg) + if err != nil { + return AuthorizationGrant{}, err + } + + auth.Authorization = any + + return auth, nil +} + +var ( + _ types.UnpackInterfacesMessage = &AuthorizationGrant{} +) + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (auth AuthorizationGrant) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var authorization 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) + if !ok { + return nil + } + return authorization +} diff --git a/x/authz/types/authz.pb.go b/x/authz/types/authz.pb.go new file mode 100644 index 000000000000..3343083205f5 --- /dev/null +++ b/x/authz/types/authz.pb.go @@ -0,0 +1,759 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/authz/v1beta1/authz.proto + +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" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + _ "github.com/golang/protobuf/ptypes/timestamp" + _ "github.com/regen-network/cosmos-proto" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// 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_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 { + // method name to grant unrestricted permissions to execute + // Note: MethodName() is already a method on `GenericAuthorization` type, + // we need some custom naming here so using `MessageName` + MessageName string `protobuf:"bytes,1,opt,name=method_name,json=methodName,proto3" json:"method_name,omitempty"` +} + +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} +} +func (m *GenericAuthorization) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenericAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenericAuthorization.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 *GenericAuthorization) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenericAuthorization.Merge(m, src) +} +func (m *GenericAuthorization) XXX_Size() int { + return m.Size() +} +func (m *GenericAuthorization) XXX_DiscardUnknown() { + xxx_messageInfo_GenericAuthorization.DiscardUnknown(m) +} + +var xxx_messageInfo_GenericAuthorization proto.InternalMessageInfo + +func (m *GenericAuthorization) GetMessageName() string { + if m != nil { + return m.MessageName + } + return "" +} + +// 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"` +} + +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} +} +func (m *AuthorizationGrant) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AuthorizationGrant) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AuthorizationGrant.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 *AuthorizationGrant) XXX_Merge(src proto.Message) { + xxx_messageInfo_AuthorizationGrant.Merge(m, src) +} +func (m *AuthorizationGrant) XXX_Size() int { + return m.Size() +} +func (m *AuthorizationGrant) XXX_DiscardUnknown() { + xxx_messageInfo_AuthorizationGrant.DiscardUnknown(m) +} + +var xxx_messageInfo_AuthorizationGrant proto.InternalMessageInfo + +func (m *AuthorizationGrant) GetAuthorization() *types1.Any { + if m != nil { + return m.Authorization + } + return nil +} + +func (m *AuthorizationGrant) GetExpiration() time.Time { + if m != nil { + return m.Expiration + } + return 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") +} + +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 +} + +func (m *GenericAuthorization) 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 *GenericAuthorization) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenericAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.MessageName) > 0 { + i -= len(m.MessageName) + copy(dAtA[i:], m.MessageName) + i = encodeVarintAuthz(dAtA, i, uint64(len(m.MessageName))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AuthorizationGrant) 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 *AuthorizationGrant) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AuthorizationGrant) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expiration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expiration):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintAuthz(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x12 + if m.Authorization != nil { + { + size, err := m.Authorization.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 (m *GenericAuthorization) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.MessageName) + if l > 0 { + n += 1 + l + sovAuthz(uint64(l)) + } + return n +} + +func (m *AuthorizationGrant) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Authorization != nil { + l = m.Authorization.Size() + n += 1 + l + sovAuthz(uint64(l)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Expiration) + 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 (m *GenericAuthorization) 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: GenericAuthorization: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenericAuthorization: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MessageName", 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.MessageName = 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 (m *AuthorizationGrant) 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: AuthorizationGrant: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AuthorizationGrant: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authorization", 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.Authorization == nil { + m.Authorization = &types1.Any{} + } + if err := m.Authorization.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Expiration", 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 err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Expiration, 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/authz/types/codec.go b/x/authz/types/codec.go new file mode 100644 index 000000000000..88b30932df9f --- /dev/null +++ b/x/authz/types/codec.go @@ -0,0 +1,25 @@ +package types + +import ( + types "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +// RegisterInterfaces registers the interfaces types with the interface registry +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.MsgRequest)(nil), + &MsgGrantAuthorizationRequest{}, + &MsgRevokeAuthorizationRequest{}, + &MsgExecAuthorizedRequest{}, + ) + + registry.RegisterInterface( + "cosmos.authz.v1beta1.Authorization", + (*Authorization)(nil), + &SendAuthorization{}, + &GenericAuthorization{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} diff --git a/x/authz/types/errors.go b/x/authz/types/errors.go new file mode 100644 index 000000000000..dc4357b1f9a2 --- /dev/null +++ b/x/authz/types/errors.go @@ -0,0 +1,10 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// x/authz module sentinel errors +var ( + ErrInvalidExpirationTime = sdkerrors.Register(ModuleName, 3, "expiration time of authorization should be more than current time") +) diff --git a/x/authz/types/events.go b/x/authz/types/events.go new file mode 100644 index 000000000000..9da0f68b14fd --- /dev/null +++ b/x/authz/types/events.go @@ -0,0 +1,14 @@ +package types + +// authz module events +const ( + EventGrantAuthorization = "grant-authorization" + EventRevokeAuthorization = "revoke-authorization" + EventExecuteAuthorization = "execute-authorization" + + AttributeKeyGrantType = "grant-type" + AttributeKeyGranteeAddress = "grantee" + AttributeKeyGranterAddress = "granter" + + AttributeValueCategory = ModuleName +) diff --git a/x/authz/types/expected_keepers.go b/x/authz/types/expected_keepers.go new file mode 100644 index 000000000000..ef7869dd2c7d --- /dev/null +++ b/x/authz/types/expected_keepers.go @@ -0,0 +1,16 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// AccountKeeper defines the expected account keeper (noalias) +type AccountKeeper interface { + GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI +} + +// BankKeeper defines the expected interface needed to retrieve account balances. +type BankKeeper interface { + SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins +} diff --git a/x/authz/types/generic_authorization.go b/x/authz/types/generic_authorization.go new file mode 100644 index 000000000000..242608c6311a --- /dev/null +++ b/x/authz/types/generic_authorization.go @@ -0,0 +1,28 @@ +package types + +import ( + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + _ Authorization = &GenericAuthorization{} +) + +// NewGenericAuthorization creates a new GenericAuthorization object. +func NewGenericAuthorization(methodName string) *GenericAuthorization { + return &GenericAuthorization{ + MessageName: methodName, + } +} + +// MethodName implements Authorization.MethodName. +func (cap GenericAuthorization) MethodName() string { + return cap.MessageName +} + +// Accept implements Authorization.Accept. +func (cap GenericAuthorization) Accept(msg sdk.ServiceMsg, block tmproto.Header) (allow bool, updated Authorization, delete bool) { + return true, &cap, false +} diff --git a/x/authz/types/genesis.go b/x/authz/types/genesis.go new file mode 100644 index 000000000000..6ae3529d2ec0 --- /dev/null +++ b/x/authz/types/genesis.go @@ -0,0 +1,41 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec/types" +) + +// NewGenesisState creates new GenesisState object +func NewGenesisState(entries []GrantAuthorization) *GenesisState { + return &GenesisState{ + Authorization: entries, + } +} + +// ValidateGenesis check the given genesis state has no integrity issues +func ValidateGenesis(data GenesisState) error { + return nil +} + +// DefaultGenesisState - Return a default genesis state +func DefaultGenesisState() *GenesisState { + return &GenesisState{} +} + +var _ types.UnpackInterfacesMessage = GenesisState{} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (data GenesisState) UnpackInterfaces(unpacker types.AnyUnpacker) error { + for _, authorization := range data.Authorization { + err := authorization.UnpackInterfaces(unpacker) + if err != nil { + return err + } + } + return nil +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (msg GrantAuthorization) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var authorization Authorization + return unpacker.UnpackAny(msg.Authorization, &authorization) +} diff --git a/x/authz/types/genesis.pb.go b/x/authz/types/genesis.pb.go new file mode 100644 index 000000000000..0cf5c8557754 --- /dev/null +++ b/x/authz/types/genesis.pb.go @@ -0,0 +1,680 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/authz/v1beta1/genesis.proto + +package types + +import ( + fmt "fmt" + 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" + _ "github.com/golang/protobuf/ptypes/timestamp" + _ "github.com/regen-network/cosmos-proto" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// 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 + +// GenesisState defines the authz module's genesis state. +type GenesisState struct { + Authorization []GrantAuthorization `protobuf:"bytes,1,rep,name=authorization,proto3" json:"authorization"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_4c2fbb971da7c892, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.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 *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetAuthorization() []GrantAuthorization { + if m != nil { + return m.Authorization + } + return nil +} + +// GrantAuthorization defines the GenesisState/GrantAuthorization type. +type GrantAuthorization struct { + Granter string `protobuf:"bytes,1,opt,name=granter,proto3" json:"granter,omitempty"` + Grantee string `protobuf:"bytes,2,opt,name=grantee,proto3" json:"grantee,omitempty"` + Authorization *types.Any `protobuf:"bytes,3,opt,name=authorization,proto3" json:"authorization,omitempty"` + Expiration time.Time `protobuf:"bytes,4,opt,name=expiration,proto3,stdtime" json:"expiration"` +} + +func (m *GrantAuthorization) Reset() { *m = GrantAuthorization{} } +func (m *GrantAuthorization) String() string { return proto.CompactTextString(m) } +func (*GrantAuthorization) ProtoMessage() {} +func (*GrantAuthorization) Descriptor() ([]byte, []int) { + return fileDescriptor_4c2fbb971da7c892, []int{1} +} +func (m *GrantAuthorization) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GrantAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GrantAuthorization.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 *GrantAuthorization) XXX_Merge(src proto.Message) { + xxx_messageInfo_GrantAuthorization.Merge(m, src) +} +func (m *GrantAuthorization) XXX_Size() int { + return m.Size() +} +func (m *GrantAuthorization) XXX_DiscardUnknown() { + xxx_messageInfo_GrantAuthorization.DiscardUnknown(m) +} + +var xxx_messageInfo_GrantAuthorization proto.InternalMessageInfo + +func (m *GrantAuthorization) GetGranter() string { + if m != nil { + return m.Granter + } + return "" +} + +func (m *GrantAuthorization) GetGrantee() string { + if m != nil { + return m.Grantee + } + return "" +} + +func (m *GrantAuthorization) GetAuthorization() *types.Any { + if m != nil { + return m.Authorization + } + return nil +} + +func (m *GrantAuthorization) GetExpiration() time.Time { + if m != nil { + return m.Expiration + } + return time.Time{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.authz.v1beta1.GenesisState") + proto.RegisterType((*GrantAuthorization)(nil), "cosmos.authz.v1beta1.GrantAuthorization") +} + +func init() { + proto.RegisterFile("cosmos/authz/v1beta1/genesis.proto", fileDescriptor_4c2fbb971da7c892) +} + +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, +} + +func (m *GenesisState) 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 *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Authorization) > 0 { + for iNdEx := len(m.Authorization) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Authorization[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GrantAuthorization) 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 *GrantAuthorization) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GrantAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expiration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expiration):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintGenesis(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x22 + if m.Authorization != nil { + { + size, err := m.Authorization.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Authorization) > 0 { + for _, e := range m.Authorization { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *GrantAuthorization) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if m.Authorization != nil { + l = m.Authorization.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Expiration) + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) 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 ErrIntOverflowGenesis + } + 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: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authorization", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authorization = append(m.Authorization, GrantAuthorization{}) + if err := m.Authorization[len(m.Authorization)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GrantAuthorization) 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 ErrIntOverflowGenesis + } + 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: GrantAuthorization: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GrantAuthorization: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Granter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Grantee = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authorization", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Authorization == nil { + m.Authorization = &types.Any{} + } + if err := m.Authorization.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Expiration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Expiration, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/authz/types/keys.go b/x/authz/types/keys.go new file mode 100644 index 000000000000..7ee571b27f74 --- /dev/null +++ b/x/authz/types/keys.go @@ -0,0 +1,52 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" +) + +const ( + // ModuleName is the module name constant used in many places + ModuleName = "authz" + + // StoreKey is the store key string for authz + StoreKey = ModuleName + + // RouterKey is the message route for authz + RouterKey = ModuleName + + // QuerierRoute is the querier route for authz + QuerierRoute = ModuleName +) + +// Keys for authz store +// Items are stored with the following key: values +// +// - 0x01: Grant + +var ( + // Keys for store prefixes + GrantKey = []byte{0x01} // prefix for each key +) + +// GetAuthorizationStoreKey - return authorization store key +func GetAuthorizationStoreKey(grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) []byte { + return append(append(append( + GrantKey, + address.MustLengthPrefix(granter)...), + address.MustLengthPrefix(grantee)...), + []byte(msgType)..., + ) +} + +// ExtractAddressesFromGrantKey - split granter & grantee address from the authorization key +func ExtractAddressesFromGrantKey(key []byte) (granterAddr, granteeAddr sdk.AccAddress) { + // key if of format: + // 0x01 + granterAddrLen := key[1] // remove prefix key + granterAddr = sdk.AccAddress(key[2 : 2+granterAddrLen]) + granteeAddrLen := int(key[2+granterAddrLen]) + granteeAddr = sdk.AccAddress(key[3+granterAddrLen : 3+granterAddrLen+byte(granteeAddrLen)]) + + return granterAddr, granteeAddr +} diff --git a/x/authz/types/keys_test.go b/x/authz/types/keys_test.go new file mode 100644 index 000000000000..1d6d7cef6623 --- /dev/null +++ b/x/authz/types/keys_test.go @@ -0,0 +1,20 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var granter = sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) +var grantee = sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) +var msgType = SendAuthorization{}.MethodName() + +func TestGrantkey(t *testing.T) { + granter1, grantee1 := ExtractAddressesFromGrantKey(GetAuthorizationStoreKey(grantee, granter, msgType)) + require.Equal(t, granter, granter1) + require.Equal(t, grantee, grantee1) +} diff --git a/x/authz/types/msgs.go b/x/authz/types/msgs.go new file mode 100644 index 000000000000..59ae59c20aa6 --- /dev/null +++ b/x/authz/types/msgs.go @@ -0,0 +1,215 @@ +package types + +import ( + "time" + + "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" +) + +var ( + _ sdk.MsgRequest = &MsgGrantAuthorizationRequest{} + _ sdk.MsgRequest = &MsgRevokeAuthorizationRequest{} + _ sdk.MsgRequest = &MsgExecAuthorizedRequest{} + + _ types.UnpackInterfacesMessage = &MsgGrantAuthorizationRequest{} + _ types.UnpackInterfacesMessage = &MsgExecAuthorizedRequest{} +) + +// NewMsgGrantAuthorization creates a new MsgGrantAuthorization +//nolint:interfacer +func NewMsgGrantAuthorization(granter sdk.AccAddress, grantee sdk.AccAddress, authorization Authorization, expiration time.Time) (*MsgGrantAuthorizationRequest, error) { + m := &MsgGrantAuthorizationRequest{ + Granter: granter.String(), + Grantee: grantee.String(), + Expiration: expiration, + } + err := m.SetAuthorization(authorization) + if err != nil { + return nil, err + } + return m, nil +} + +// GetSigners implements Msg +func (msg MsgGrantAuthorizationRequest) GetSigners() []sdk.AccAddress { + granter, err := sdk.AccAddressFromBech32(msg.Granter) + if err != nil { + panic(err) + } + return []sdk.AccAddress{granter} +} + +// ValidateBasic implements Msg +func (msg MsgGrantAuthorizationRequest) ValidateBasic() error { + granter, err := sdk.AccAddressFromBech32(msg.Granter) + if err != nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid granter address") + } + grantee, err := sdk.AccAddressFromBech32(msg.Grantee) + if err != nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid granter address") + } + + if granter.Equals(grantee) { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "granter and grantee cannot be same") + } + + if msg.Expiration.Unix() < time.Now().Unix() { + return sdkerrors.Wrap(ErrInvalidExpirationTime, "Time can't be in the past") + } + + return nil +} + +// GetGrantAuthorization returns the cache value from the MsgGrantAuthorization.Authorization if present. +func (msg *MsgGrantAuthorizationRequest) GetGrantAuthorization() Authorization { + authorization, ok := msg.Authorization.GetCachedValue().(Authorization) + if !ok { + return nil + } + return authorization +} + +// SetAuthorization converts Authorization to any and adds it to MsgGrantAuthorization.Authorization. +func (msg *MsgGrantAuthorizationRequest) SetAuthorization(authorization Authorization) error { + m, ok := authorization.(proto.Message) + if !ok { + return sdkerrors.Wrapf(sdkerrors.ErrPackAny, "can't proto marshal %T", m) + } + any, err := types.NewAnyWithValue(m) + if err != nil { + return err + } + msg.Authorization = any + return nil +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (msg MsgExecAuthorizedRequest) UnpackInterfaces(unpacker types.AnyUnpacker) error { + for _, x := range msg.Msgs { + var msgExecAuthorized sdk.MsgRequest + err := unpacker.UnpackAny(x, &msgExecAuthorized) + if err != nil { + return err + } + } + + return nil +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (msg MsgGrantAuthorizationRequest) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var authorization Authorization + return unpacker.UnpackAny(msg.Authorization, &authorization) +} + +// NewMsgRevokeAuthorization creates a new MsgRevokeAuthorization +//nolint:interfacer +func NewMsgRevokeAuthorization(granter sdk.AccAddress, grantee sdk.AccAddress, methodName string) MsgRevokeAuthorizationRequest { + return MsgRevokeAuthorizationRequest{ + Granter: granter.String(), + Grantee: grantee.String(), + MethodName: methodName, + } +} + +// GetSigners implements Msg +func (msg MsgRevokeAuthorizationRequest) GetSigners() []sdk.AccAddress { + granter, err := sdk.AccAddressFromBech32(msg.Granter) + if err != nil { + panic(err) + } + return []sdk.AccAddress{granter} +} + +// ValidateBasic implements MsgRequest.ValidateBasic +func (msg MsgRevokeAuthorizationRequest) ValidateBasic() error { + granter, err := sdk.AccAddressFromBech32(msg.Granter) + if err != nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid granter address") + } + grantee, err := sdk.AccAddressFromBech32(msg.Grantee) + if err != nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid grantee address") + } + + if granter.Equals(grantee) { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "granter and grantee cannot be same") + } + + if msg.MethodName == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "missing method name") + } + + return nil +} + +// NewMsgExecAuthorized creates a new MsgExecAuthorized +//nolint:interfacer +func NewMsgExecAuthorized(grantee sdk.AccAddress, msgs []sdk.ServiceMsg) MsgExecAuthorizedRequest { + msgsAny := make([]*types.Any, len(msgs)) + for i, msg := range msgs { + bz, err := proto.Marshal(msg.Request) + if err != nil { + panic(err) + } + + anyMsg := &types.Any{ + TypeUrl: msg.MethodName, + Value: bz, + } + + msgsAny[i] = anyMsg + } + + return MsgExecAuthorizedRequest{ + Grantee: grantee.String(), + Msgs: msgsAny, + } +} + +// GetServiceMsgs returns the cache values from the MsgExecAuthorized.Msgs if present. +func (msg MsgExecAuthorizedRequest) GetServiceMsgs() ([]sdk.ServiceMsg, error) { + msgs := make([]sdk.ServiceMsg, len(msg.Msgs)) + for i, msgAny := range msg.Msgs { + msgReq, ok := msgAny.GetCachedValue().(sdk.MsgRequest) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "messages contains %T which is not a sdk.MsgRequest", msgAny) + } + srvMsg := sdk.ServiceMsg{ + MethodName: msgAny.TypeUrl, + Request: msgReq, + } + + msgs[i] = srvMsg + } + + return msgs, nil +} + +// GetSigners implements Msg +func (msg MsgExecAuthorizedRequest) GetSigners() []sdk.AccAddress { + grantee, err := sdk.AccAddressFromBech32(msg.Grantee) + if err != nil { + panic(err) + } + return []sdk.AccAddress{grantee} +} + +// ValidateBasic implements Msg +func (msg MsgExecAuthorizedRequest) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Grantee) + if err != nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "invalid grantee address") + } + + if len(msg.Msgs) == 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "messages cannot be empty") + } + + return nil +} diff --git a/x/authz/types/msgs_test.go b/x/authz/types/msgs_test.go new file mode 100644 index 000000000000..3c6cea890576 --- /dev/null +++ b/x/authz/types/msgs_test.go @@ -0,0 +1,103 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz/types" +) + +var ( + coinsPos = sdk.NewCoins(sdk.NewInt64Coin("steak", 100)) + granter = sdk.AccAddress("_______granter______") + grantee = sdk.AccAddress("_______grantee______") +) + +func TestMsgExecAuthorized(t *testing.T) { + tests := []struct { + title string + grantee sdk.AccAddress + msgs []sdk.ServiceMsg + expectPass bool + }{ + {"nil grantee address", nil, []sdk.ServiceMsg{}, false}, + {"zero-messages test: should fail", grantee, []sdk.ServiceMsg{}, false}, + {"valid test: msg type", grantee, []sdk.ServiceMsg{ + { + MethodName: types.SendAuthorization{}.MethodName(), + Request: &banktypes.MsgSend{ + Amount: sdk.NewCoins(sdk.NewInt64Coin("steak", 2)), + FromAddress: granter.String(), + ToAddress: grantee.String(), + }, + }, + }, true}, + } + for i, tc := range tests { + msg := types.NewMsgExecAuthorized(tc.grantee, tc.msgs) + if tc.expectPass { + require.NoError(t, msg.ValidateBasic(), "test: %v", i) + } else { + require.Error(t, msg.ValidateBasic(), "test: %v", i) + } + } +} +func TestMsgRevokeAuthorization(t *testing.T) { + tests := []struct { + title string + granter, grantee sdk.AccAddress + msgType string + expectPass bool + }{ + {"nil Granter address", nil, grantee, "hello", false}, + {"nil Grantee address", granter, nil, "hello", false}, + {"nil Granter and Grantee address", nil, nil, "hello", false}, + {"valid test case", granter, grantee, "hello", true}, + } + for i, tc := range tests { + msg := types.NewMsgRevokeAuthorization(tc.granter, tc.grantee, tc.msgType) + if tc.expectPass { + require.NoError(t, msg.ValidateBasic(), "test: %v", i) + } else { + require.Error(t, msg.ValidateBasic(), "test: %v", i) + } + } +} + +func TestMsgGrantAuthorization(t *testing.T) { + tests := []struct { + title string + granter, grantee sdk.AccAddress + authorization types.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 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}, + } + for i, tc := range tests { + msg, err := types.NewMsgGrantAuthorization( + tc.granter, tc.grantee, tc.authorization, tc.expiration, + ) + if !tc.expectErr { + require.NoError(t, err) + } else { + continue + } + if tc.expectPass { + require.NoError(t, msg.ValidateBasic(), "test: %v", i) + } else { + require.Error(t, msg.ValidateBasic(), "test: %v", i) + } + } +} diff --git a/x/authz/types/query.pb.go b/x/authz/types/query.pb.go new file mode 100644 index 000000000000..91accaa109d5 --- /dev/null +++ b/x/authz/types/query.pb.go @@ -0,0 +1,1278 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/authz/v1beta1/query.proto + +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" + status "google.golang.org/grpc/status" + 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 + +// QueryAuthorizationRequest is the request type for the Query/Authorization RPC method. +type QueryAuthorizationRequest struct { + Granter string `protobuf:"bytes,1,opt,name=granter,proto3" json:"granter,omitempty"` + Grantee string `protobuf:"bytes,2,opt,name=grantee,proto3" json:"grantee,omitempty"` + MethodName string `protobuf:"bytes,3,opt,name=method_name,json=methodName,proto3" json:"method_name,omitempty"` +} + +func (m *QueryAuthorizationRequest) Reset() { *m = QueryAuthorizationRequest{} } +func (m *QueryAuthorizationRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAuthorizationRequest) ProtoMessage() {} +func (*QueryAuthorizationRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_376d714ffdeb1545, []int{0} +} +func (m *QueryAuthorizationRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAuthorizationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAuthorizationRequest.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 *QueryAuthorizationRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAuthorizationRequest.Merge(m, src) +} +func (m *QueryAuthorizationRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAuthorizationRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAuthorizationRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAuthorizationRequest proto.InternalMessageInfo + +func (m *QueryAuthorizationRequest) GetGranter() string { + if m != nil { + return m.Granter + } + return "" +} + +func (m *QueryAuthorizationRequest) GetGrantee() string { + if m != nil { + return m.Grantee + } + return "" +} + +func (m *QueryAuthorizationRequest) GetMethodName() string { + if m != nil { + return m.MethodName + } + return "" +} + +// QueryAuthorizationResponse is the response type for the Query/Authorization RPC method. +type QueryAuthorizationResponse struct { + // authorization is a authorization granted for grantee by granter. + Authorization *AuthorizationGrant `protobuf:"bytes,1,opt,name=authorization,proto3" json:"authorization,omitempty"` +} + +func (m *QueryAuthorizationResponse) Reset() { *m = QueryAuthorizationResponse{} } +func (m *QueryAuthorizationResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAuthorizationResponse) ProtoMessage() {} +func (*QueryAuthorizationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_376d714ffdeb1545, []int{1} +} +func (m *QueryAuthorizationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAuthorizationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAuthorizationResponse.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 *QueryAuthorizationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAuthorizationResponse.Merge(m, src) +} +func (m *QueryAuthorizationResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAuthorizationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAuthorizationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAuthorizationResponse proto.InternalMessageInfo + +func (m *QueryAuthorizationResponse) GetAuthorization() *AuthorizationGrant { + if m != nil { + return m.Authorization + } + return nil +} + +// QueryAuthorizationsRequest is the request type for the Query/Authorizations RPC method. +type QueryAuthorizationsRequest struct { + Granter string `protobuf:"bytes,1,opt,name=granter,proto3" json:"granter,omitempty"` + Grantee string `protobuf:"bytes,2,opt,name=grantee,proto3" json:"grantee,omitempty"` + // pagination defines an pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,3,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAuthorizationsRequest) Reset() { *m = QueryAuthorizationsRequest{} } +func (m *QueryAuthorizationsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAuthorizationsRequest) ProtoMessage() {} +func (*QueryAuthorizationsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_376d714ffdeb1545, []int{2} +} +func (m *QueryAuthorizationsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAuthorizationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAuthorizationsRequest.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 *QueryAuthorizationsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAuthorizationsRequest.Merge(m, src) +} +func (m *QueryAuthorizationsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAuthorizationsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAuthorizationsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAuthorizationsRequest proto.InternalMessageInfo + +func (m *QueryAuthorizationsRequest) GetGranter() string { + if m != nil { + return m.Granter + } + return "" +} + +func (m *QueryAuthorizationsRequest) GetGrantee() string { + if m != nil { + return m.Grantee + } + return "" +} + +func (m *QueryAuthorizationsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryAuthorizationsResponse is the response type for the Query/Authorizations RPC method. +type QueryAuthorizationsResponse struct { + // authorizations is a list of grants granted for grantee by granter. + Authorizations []*AuthorizationGrant `protobuf:"bytes,1,rep,name=authorizations,proto3" json:"authorizations,omitempty"` + // pagination defines an pagination for the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAuthorizationsResponse) Reset() { *m = QueryAuthorizationsResponse{} } +func (m *QueryAuthorizationsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAuthorizationsResponse) ProtoMessage() {} +func (*QueryAuthorizationsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_376d714ffdeb1545, []int{3} +} +func (m *QueryAuthorizationsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAuthorizationsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAuthorizationsResponse.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 *QueryAuthorizationsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAuthorizationsResponse.Merge(m, src) +} +func (m *QueryAuthorizationsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAuthorizationsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAuthorizationsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAuthorizationsResponse proto.InternalMessageInfo + +func (m *QueryAuthorizationsResponse) GetAuthorizations() []*AuthorizationGrant { + if m != nil { + return m.Authorizations + } + return nil +} + +func (m *QueryAuthorizationsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +func init() { + proto.RegisterType((*QueryAuthorizationRequest)(nil), "cosmos.authz.v1beta1.QueryAuthorizationRequest") + proto.RegisterType((*QueryAuthorizationResponse)(nil), "cosmos.authz.v1beta1.QueryAuthorizationResponse") + proto.RegisterType((*QueryAuthorizationsRequest)(nil), "cosmos.authz.v1beta1.QueryAuthorizationsRequest") + proto.RegisterType((*QueryAuthorizationsResponse)(nil), "cosmos.authz.v1beta1.QueryAuthorizationsResponse") +} + +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, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// 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 { + // Returns any `Authorization` (or `nil`), with the expiration time, granted to the grantee by the granter for the + // provided msg type. + Authorization(ctx context.Context, in *QueryAuthorizationRequest, opts ...grpc.CallOption) (*QueryAuthorizationResponse, error) + // Returns list of `Authorization`, granted to the grantee by the granter. + Authorizations(ctx context.Context, in *QueryAuthorizationsRequest, opts ...grpc.CallOption) (*QueryAuthorizationsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Authorization(ctx context.Context, in *QueryAuthorizationRequest, opts ...grpc.CallOption) (*QueryAuthorizationResponse, error) { + out := new(QueryAuthorizationResponse) + err := c.cc.Invoke(ctx, "/cosmos.authz.v1beta1.Query/Authorization", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Authorizations(ctx context.Context, in *QueryAuthorizationsRequest, opts ...grpc.CallOption) (*QueryAuthorizationsResponse, error) { + out := new(QueryAuthorizationsResponse) + err := c.cc.Invoke(ctx, "/cosmos.authz.v1beta1.Query/Authorizations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Returns any `Authorization` (or `nil`), with the expiration time, granted to the grantee by the granter for the + // provided msg type. + Authorization(context.Context, *QueryAuthorizationRequest) (*QueryAuthorizationResponse, error) + // Returns list of `Authorization`, granted to the grantee by the granter. + Authorizations(context.Context, *QueryAuthorizationsRequest) (*QueryAuthorizationsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Authorization(ctx context.Context, req *QueryAuthorizationRequest) (*QueryAuthorizationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Authorization not implemented") +} +func (*UnimplementedQueryServer) Authorizations(ctx context.Context, req *QueryAuthorizationsRequest) (*QueryAuthorizationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Authorizations not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Authorization_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAuthorizationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Authorization(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.authz.v1beta1.Query/Authorization", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Authorization(ctx, req.(*QueryAuthorizationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Authorizations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAuthorizationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Authorizations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.authz.v1beta1.Query/Authorizations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Authorizations(ctx, req.(*QueryAuthorizationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.authz.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Authorization", + Handler: _Query_Authorization_Handler, + }, + { + MethodName: "Authorizations", + Handler: _Query_Authorizations_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/authz/v1beta1/query.proto", +} + +func (m *QueryAuthorizationRequest) 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 *QueryAuthorizationRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAuthorizationRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.MethodName) > 0 { + i -= len(m.MethodName) + copy(dAtA[i:], m.MethodName) + i = encodeVarintQuery(dAtA, i, uint64(len(m.MethodName))) + i-- + dAtA[i] = 0x1a + } + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAuthorizationResponse) 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 *QueryAuthorizationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAuthorizationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Authorization != nil { + { + size, err := m.Authorization.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 *QueryAuthorizationsRequest) 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 *QueryAuthorizationsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAuthorizationsRequest) 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] = 0x1a + } + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAuthorizationsResponse) 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 *QueryAuthorizationsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAuthorizationsResponse) 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.Authorizations) > 0 { + for iNdEx := len(m.Authorizations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Authorizations[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 encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryAuthorizationRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.MethodName) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAuthorizationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Authorization != nil { + l = m.Authorization.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAuthorizationsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAuthorizationsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Authorizations) > 0 { + for _, e := range m.Authorizations { + 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 sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryAuthorizationRequest) 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: QueryAuthorizationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAuthorizationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Granter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Grantee = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MethodName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MethodName = string(dAtA[iNdEx:postIndex]) + 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 *QueryAuthorizationResponse) 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: QueryAuthorizationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAuthorizationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authorization", 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.Authorization == nil { + m.Authorization = &AuthorizationGrant{} + } + if err := m.Authorization.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 *QueryAuthorizationsRequest) 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: QueryAuthorizationsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAuthorizationsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Granter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Grantee = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + 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 *QueryAuthorizationsResponse) 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: QueryAuthorizationsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAuthorizationsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authorizations", 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.Authorizations = append(m.Authorizations, &AuthorizationGrant{}) + if err := m.Authorizations[len(m.Authorizations)-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 skipQuery(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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/authz/types/query.pb.gw.go b/x/authz/types/query.pb.gw.go new file mode 100644 index 000000000000..d1cd24bc0b54 --- /dev/null +++ b/x/authz/types/query.pb.gw.go @@ -0,0 +1,362 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/authz/v1beta1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage + +var ( + filter_Query_Authorization_0 = &utilities.DoubleArray{Encoding: map[string]int{"granter": 0, "grantee": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} +) + +func request_Query_Authorization_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAuthorizationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["granter"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "granter") + } + + protoReq.Granter, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "granter", err) + } + + val, ok = pathParams["grantee"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "grantee") + } + + protoReq.Grantee, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "grantee", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Authorization_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Authorization(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Authorization_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAuthorizationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["granter"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "granter") + } + + protoReq.Granter, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "granter", err) + } + + val, ok = pathParams["grantee"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "grantee") + } + + protoReq.Grantee, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "grantee", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Authorization_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Authorization(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Authorizations_0 = &utilities.DoubleArray{Encoding: map[string]int{"granter": 0, "grantee": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} +) + +func request_Query_Authorizations_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAuthorizationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["granter"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "granter") + } + + protoReq.Granter, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "granter", err) + } + + val, ok = pathParams["grantee"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "grantee") + } + + protoReq.Grantee, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "grantee", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Authorizations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Authorizations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Authorizations_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAuthorizationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["granter"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "granter") + } + + protoReq.Granter, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "granter", err) + } + + val, ok = pathParams["grantee"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "grantee") + } + + protoReq.Grantee, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "grantee", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_Authorizations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Authorizations(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// 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_Authorization_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_Authorization_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_Authorization_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Authorizations_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_Authorizations_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_Authorizations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Authorization_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_Authorization_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_Authorization_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Authorizations_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_Authorizations_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_Authorizations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +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_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))) +) + +var ( + forward_Query_Authorization_0 = runtime.ForwardResponseMessage + + forward_Query_Authorizations_0 = runtime.ForwardResponseMessage +) diff --git a/x/authz/types/send_authorization.go b/x/authz/types/send_authorization.go new file mode 100644 index 000000000000..2bb2a63a74f6 --- /dev/null +++ b/x/authz/types/send_authorization.go @@ -0,0 +1,45 @@ +package types + +import ( + "reflect" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +var ( + _ Authorization = &SendAuthorization{} +) + +// NewSendAuthorization creates a new SendAuthorization object. +func NewSendAuthorization(spendLimit sdk.Coins) *SendAuthorization { + return &SendAuthorization{ + SpendLimit: spendLimit, + } +} + +// MethodName implements Authorization.MethodName. +func (authorization SendAuthorization) MethodName() string { + return "/cosmos.bank.v1beta1.Msg/Send" +} + +// 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) + if ok { + limitLeft, isNegative := authorization.SpendLimit.SafeSub(msg.Amount) + if isNegative { + return false, nil, false + } + if limitLeft.IsZero() { + return true, nil, true + } + + return true, &SendAuthorization{SpendLimit: limitLeft}, false + } + } + return false, nil, false +} diff --git a/x/authz/types/tx.pb.go b/x/authz/types/tx.pb.go new file mode 100644 index 000000000000..7705f5d44afc --- /dev/null +++ b/x/authz/types/tx.pb.go @@ -0,0 +1,1609 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/authz/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + types1 "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + _ "github.com/golang/protobuf/ptypes/timestamp" + _ "github.com/regen-network/cosmos-proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// 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 + +// MsgGrantAuthorizationRequest grants the provided authorization to the grantee on the granter's +// account with the provided expiration time. +type MsgGrantAuthorizationRequest struct { + Granter string `protobuf:"bytes,1,opt,name=granter,proto3" json:"granter,omitempty"` + Grantee string `protobuf:"bytes,2,opt,name=grantee,proto3" json:"grantee,omitempty"` + Authorization *types.Any `protobuf:"bytes,3,opt,name=authorization,proto3" json:"authorization,omitempty"` + Expiration time.Time `protobuf:"bytes,4,opt,name=expiration,proto3,stdtime" json:"expiration"` +} + +func (m *MsgGrantAuthorizationRequest) Reset() { *m = MsgGrantAuthorizationRequest{} } +func (m *MsgGrantAuthorizationRequest) String() string { return proto.CompactTextString(m) } +func (*MsgGrantAuthorizationRequest) ProtoMessage() {} +func (*MsgGrantAuthorizationRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_3ceddab7d8589ad1, []int{0} +} +func (m *MsgGrantAuthorizationRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGrantAuthorizationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGrantAuthorizationRequest.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 *MsgGrantAuthorizationRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGrantAuthorizationRequest.Merge(m, src) +} +func (m *MsgGrantAuthorizationRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgGrantAuthorizationRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGrantAuthorizationRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGrantAuthorizationRequest proto.InternalMessageInfo + +func (m *MsgGrantAuthorizationRequest) GetGranter() string { + if m != nil { + return m.Granter + } + return "" +} + +func (m *MsgGrantAuthorizationRequest) GetGrantee() string { + if m != nil { + return m.Grantee + } + return "" +} + +func (m *MsgGrantAuthorizationRequest) GetAuthorization() *types.Any { + if m != nil { + return m.Authorization + } + return nil +} + +func (m *MsgGrantAuthorizationRequest) GetExpiration() time.Time { + if m != nil { + return m.Expiration + } + return time.Time{} +} + +// MsgExecAuthorizedResponse defines the Msg/MsgExecAuthorizedResponse response type. +type MsgExecAuthorizedResponse struct { + Result *types1.Result `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` +} + +func (m *MsgExecAuthorizedResponse) Reset() { *m = MsgExecAuthorizedResponse{} } +func (m *MsgExecAuthorizedResponse) String() string { return proto.CompactTextString(m) } +func (*MsgExecAuthorizedResponse) ProtoMessage() {} +func (*MsgExecAuthorizedResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3ceddab7d8589ad1, []int{1} +} +func (m *MsgExecAuthorizedResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgExecAuthorizedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgExecAuthorizedResponse.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 *MsgExecAuthorizedResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgExecAuthorizedResponse.Merge(m, src) +} +func (m *MsgExecAuthorizedResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgExecAuthorizedResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgExecAuthorizedResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgExecAuthorizedResponse proto.InternalMessageInfo + +func (m *MsgExecAuthorizedResponse) GetResult() *types1.Result { + if m != nil { + return m.Result + } + return nil +} + +// MsgExecAuthorizedRequest attempts to execute the provided messages using +// authorizations granted to the grantee. Each message should have only +// one signer corresponding to the granter of the authorization. +type MsgExecAuthorizedRequest struct { + Grantee string `protobuf:"bytes,1,opt,name=grantee,proto3" json:"grantee,omitempty"` + Msgs []*types.Any `protobuf:"bytes,2,rep,name=msgs,proto3" json:"msgs,omitempty"` +} + +func (m *MsgExecAuthorizedRequest) Reset() { *m = MsgExecAuthorizedRequest{} } +func (m *MsgExecAuthorizedRequest) String() string { return proto.CompactTextString(m) } +func (*MsgExecAuthorizedRequest) ProtoMessage() {} +func (*MsgExecAuthorizedRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_3ceddab7d8589ad1, []int{2} +} +func (m *MsgExecAuthorizedRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgExecAuthorizedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgExecAuthorizedRequest.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 *MsgExecAuthorizedRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgExecAuthorizedRequest.Merge(m, src) +} +func (m *MsgExecAuthorizedRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgExecAuthorizedRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgExecAuthorizedRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgExecAuthorizedRequest proto.InternalMessageInfo + +func (m *MsgExecAuthorizedRequest) GetGrantee() string { + if m != nil { + return m.Grantee + } + return "" +} + +func (m *MsgExecAuthorizedRequest) GetMsgs() []*types.Any { + if m != nil { + return m.Msgs + } + return nil +} + +// MsgGrantAuthorizationResponse defines the Msg/MsgGrantAuthorization response type. +type MsgGrantAuthorizationResponse struct { +} + +func (m *MsgGrantAuthorizationResponse) Reset() { *m = MsgGrantAuthorizationResponse{} } +func (m *MsgGrantAuthorizationResponse) String() string { return proto.CompactTextString(m) } +func (*MsgGrantAuthorizationResponse) ProtoMessage() {} +func (*MsgGrantAuthorizationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3ceddab7d8589ad1, []int{3} +} +func (m *MsgGrantAuthorizationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGrantAuthorizationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGrantAuthorizationResponse.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 *MsgGrantAuthorizationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGrantAuthorizationResponse.Merge(m, src) +} +func (m *MsgGrantAuthorizationResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgGrantAuthorizationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGrantAuthorizationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGrantAuthorizationResponse proto.InternalMessageInfo + +// MsgRevokeAuthorizationRequest revokes any authorization with the provided sdk.Msg type on the +// granter's account with that has been granted to the grantee. +type MsgRevokeAuthorizationRequest struct { + Granter string `protobuf:"bytes,1,opt,name=granter,proto3" json:"granter,omitempty"` + Grantee string `protobuf:"bytes,2,opt,name=grantee,proto3" json:"grantee,omitempty"` + MethodName string `protobuf:"bytes,3,opt,name=method_name,json=methodName,proto3" json:"method_name,omitempty"` +} + +func (m *MsgRevokeAuthorizationRequest) Reset() { *m = MsgRevokeAuthorizationRequest{} } +func (m *MsgRevokeAuthorizationRequest) String() string { return proto.CompactTextString(m) } +func (*MsgRevokeAuthorizationRequest) ProtoMessage() {} +func (*MsgRevokeAuthorizationRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_3ceddab7d8589ad1, []int{4} +} +func (m *MsgRevokeAuthorizationRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRevokeAuthorizationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRevokeAuthorizationRequest.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 *MsgRevokeAuthorizationRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRevokeAuthorizationRequest.Merge(m, src) +} +func (m *MsgRevokeAuthorizationRequest) XXX_Size() int { + return m.Size() +} +func (m *MsgRevokeAuthorizationRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRevokeAuthorizationRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRevokeAuthorizationRequest proto.InternalMessageInfo + +func (m *MsgRevokeAuthorizationRequest) GetGranter() string { + if m != nil { + return m.Granter + } + return "" +} + +func (m *MsgRevokeAuthorizationRequest) GetGrantee() string { + if m != nil { + return m.Grantee + } + return "" +} + +func (m *MsgRevokeAuthorizationRequest) GetMethodName() string { + if m != nil { + return m.MethodName + } + return "" +} + +// MsgRevokeAuthorizationResponse defines the Msg/MsgRevokeAuthorizationResponse response type. +type MsgRevokeAuthorizationResponse struct { +} + +func (m *MsgRevokeAuthorizationResponse) Reset() { *m = MsgRevokeAuthorizationResponse{} } +func (m *MsgRevokeAuthorizationResponse) String() string { return proto.CompactTextString(m) } +func (*MsgRevokeAuthorizationResponse) ProtoMessage() {} +func (*MsgRevokeAuthorizationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3ceddab7d8589ad1, []int{5} +} +func (m *MsgRevokeAuthorizationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRevokeAuthorizationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRevokeAuthorizationResponse.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 *MsgRevokeAuthorizationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRevokeAuthorizationResponse.Merge(m, src) +} +func (m *MsgRevokeAuthorizationResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgRevokeAuthorizationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRevokeAuthorizationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRevokeAuthorizationResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgGrantAuthorizationRequest)(nil), "cosmos.authz.v1beta1.MsgGrantAuthorizationRequest") + proto.RegisterType((*MsgExecAuthorizedResponse)(nil), "cosmos.authz.v1beta1.MsgExecAuthorizedResponse") + proto.RegisterType((*MsgExecAuthorizedRequest)(nil), "cosmos.authz.v1beta1.MsgExecAuthorizedRequest") + proto.RegisterType((*MsgGrantAuthorizationResponse)(nil), "cosmos.authz.v1beta1.MsgGrantAuthorizationResponse") + proto.RegisterType((*MsgRevokeAuthorizationRequest)(nil), "cosmos.authz.v1beta1.MsgRevokeAuthorizationRequest") + proto.RegisterType((*MsgRevokeAuthorizationResponse)(nil), "cosmos.authz.v1beta1.MsgRevokeAuthorizationResponse") +} + +func init() { proto.RegisterFile("cosmos/authz/v1beta1/tx.proto", fileDescriptor_3ceddab7d8589ad1) } + +var fileDescriptor_3ceddab7d8589ad1 = []byte{ + // 521 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xc1, 0x6e, 0xd3, 0x40, + 0x10, 0x8d, 0x93, 0xaa, 0xd0, 0x8d, 0x8a, 0xc4, 0x92, 0x83, 0x63, 0x51, 0xdb, 0x32, 0x97, 0x08, + 0xa9, 0x6b, 0x35, 0xe5, 0xc0, 0xb5, 0x51, 0x11, 0xa7, 0x70, 0xb0, 0xe0, 0xc2, 0x81, 0x6a, 0x9d, + 0x0c, 0x1b, 0xab, 0xb5, 0xd7, 0xf5, 0xae, 0xab, 0xa4, 0x12, 0x12, 0x9f, 0xd0, 0x8f, 0xe1, 0x23, + 0x2a, 0x4e, 0x3d, 0x72, 0x02, 0x94, 0x1c, 0xf8, 0x07, 0x4e, 0x28, 0xbb, 0x1b, 0x48, 0xda, 0x18, + 0x11, 0x89, 0x93, 0x3d, 0x33, 0x6f, 0x66, 0x9e, 0xdf, 0x5b, 0x2f, 0xda, 0x1b, 0x70, 0x91, 0x72, + 0x11, 0xd2, 0x52, 0x8e, 0x2e, 0xc3, 0x8b, 0x83, 0x18, 0x24, 0x3d, 0x08, 0xe5, 0x98, 0xe4, 0x05, + 0x97, 0x1c, 0xb7, 0x74, 0x99, 0xa8, 0x32, 0x31, 0x65, 0xa7, 0xad, 0xb3, 0x27, 0x0a, 0x13, 0x1a, + 0x88, 0x0a, 0x9c, 0x16, 0xe3, 0x8c, 0xeb, 0xfc, 0xfc, 0xcd, 0x64, 0x3d, 0xc6, 0x39, 0x3b, 0x83, + 0x50, 0x45, 0x71, 0xf9, 0x3e, 0x94, 0x49, 0x0a, 0x42, 0xd2, 0x34, 0x37, 0x80, 0xf6, 0x6d, 0x00, + 0xcd, 0x26, 0xa6, 0xf4, 0xc4, 0x30, 0x8c, 0xa9, 0x80, 0x90, 0xc6, 0x83, 0xe4, 0x37, 0xcb, 0x79, + 0xa0, 0x41, 0xc1, 0x0f, 0x0b, 0x3d, 0xee, 0x0b, 0xf6, 0xb2, 0xa0, 0x99, 0x3c, 0x2a, 0xe5, 0x88, + 0x17, 0xc9, 0x25, 0x95, 0x09, 0xcf, 0x22, 0x38, 0x2f, 0x41, 0x48, 0x6c, 0xa3, 0x7b, 0x6c, 0x5e, + 0x84, 0xc2, 0xb6, 0x7c, 0xab, 0xb3, 0x13, 0x2d, 0xc2, 0x3f, 0x15, 0xb0, 0xeb, 0xcb, 0x15, 0xc0, + 0x7d, 0xb4, 0x4b, 0x97, 0x67, 0xd9, 0x0d, 0xdf, 0xea, 0x34, 0xbb, 0x2d, 0xa2, 0xc9, 0x92, 0x05, + 0x59, 0x72, 0x94, 0x4d, 0x7a, 0x0f, 0x3f, 0x7f, 0xda, 0xdf, 0x5d, 0x5d, 0xbd, 0xda, 0x8d, 0x8f, + 0x11, 0x82, 0x71, 0x9e, 0x14, 0x7a, 0xd6, 0x96, 0x9a, 0xe5, 0xdc, 0x99, 0xf5, 0x7a, 0xa1, 0x4c, + 0xef, 0xfe, 0xf5, 0x57, 0xaf, 0x76, 0xf5, 0xcd, 0xb3, 0xa2, 0xa5, 0xbe, 0xe0, 0x0d, 0x6a, 0xf7, + 0x05, 0x7b, 0x31, 0x86, 0xc1, 0x62, 0x19, 0x0c, 0x23, 0x10, 0x39, 0xcf, 0x04, 0xe0, 0xe7, 0x68, + 0xbb, 0x00, 0x51, 0x9e, 0x49, 0xf5, 0x91, 0xcd, 0xae, 0x4f, 0x8c, 0x39, 0x73, 0xf1, 0x88, 0xd2, + 0xcb, 0x88, 0x47, 0x22, 0x85, 0x8b, 0x0c, 0x3e, 0x78, 0x87, 0xec, 0x35, 0x63, 0x6f, 0x69, 0x07, + 0xab, 0xda, 0x01, 0xee, 0xa0, 0xad, 0x54, 0x30, 0x61, 0xd7, 0xfd, 0x46, 0x95, 0x30, 0x91, 0x42, + 0x04, 0x1e, 0xda, 0xab, 0xf0, 0x47, 0x53, 0x0f, 0xa4, 0x02, 0x44, 0x70, 0xc1, 0x4f, 0xe1, 0xbf, + 0x39, 0xe8, 0xa1, 0x66, 0x0a, 0x72, 0xc4, 0x87, 0x27, 0x19, 0x4d, 0x41, 0xf9, 0xb7, 0x13, 0x21, + 0x9d, 0x7a, 0x45, 0x53, 0x08, 0x7c, 0xe4, 0x56, 0x6d, 0xd5, 0xbc, 0xba, 0x3f, 0xeb, 0xa8, 0xd1, + 0x17, 0x0c, 0x7f, 0x40, 0xf8, 0x2e, 0x7b, 0xdc, 0x25, 0xeb, 0x7e, 0x10, 0xf2, 0xb7, 0xa3, 0xe8, + 0x1c, 0x6e, 0xd4, 0x63, 0x9c, 0x3d, 0x47, 0x0f, 0x56, 0xcd, 0xc1, 0xa4, 0x72, 0xcc, 0x5a, 0x17, + 0x9d, 0xf0, 0x9f, 0xf1, 0x66, 0xe5, 0x47, 0x0b, 0x3d, 0x5a, 0xa3, 0x0c, 0xae, 0xe6, 0x5f, 0xed, + 0x9e, 0xf3, 0x6c, 0xb3, 0x26, 0x4d, 0xa1, 0x77, 0x7c, 0x3d, 0x75, 0xad, 0x9b, 0xa9, 0x6b, 0x7d, + 0x9f, 0xba, 0xd6, 0xd5, 0xcc, 0xad, 0xdd, 0xcc, 0xdc, 0xda, 0x97, 0x99, 0x5b, 0x7b, 0xfb, 0x94, + 0x25, 0x72, 0x54, 0xc6, 0x64, 0xc0, 0x53, 0x73, 0x01, 0x99, 0xc7, 0xbe, 0x18, 0x9e, 0x86, 0x63, + 0x73, 0x9f, 0xc9, 0x49, 0x0e, 0x22, 0xde, 0x56, 0xe7, 0xf1, 0xf0, 0x57, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x9d, 0x19, 0xe1, 0xec, 0x04, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // GrantAuthorization grants the provided authorization to the grantee on the granter's + // account with the provided expiration time. + GrantAuthorization(ctx context.Context, in *MsgGrantAuthorizationRequest, opts ...grpc.CallOption) (*MsgGrantAuthorizationResponse, error) + // ExecAuthorized attempts to execute the provided messages using + // authorizations granted to the grantee. Each message should have only + // one signer corresponding to the granter of the authorization. + ExecAuthorized(ctx context.Context, in *MsgExecAuthorizedRequest, opts ...grpc.CallOption) (*MsgExecAuthorizedResponse, error) + // RevokeAuthorization revokes any authorization corresponding to the provided method name on the + // granter's account that has been granted to the grantee. + RevokeAuthorization(ctx context.Context, in *MsgRevokeAuthorizationRequest, opts ...grpc.CallOption) (*MsgRevokeAuthorizationResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) GrantAuthorization(ctx context.Context, in *MsgGrantAuthorizationRequest, opts ...grpc.CallOption) (*MsgGrantAuthorizationResponse, error) { + out := new(MsgGrantAuthorizationResponse) + err := c.cc.Invoke(ctx, "/cosmos.authz.v1beta1.Msg/GrantAuthorization", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ExecAuthorized(ctx context.Context, in *MsgExecAuthorizedRequest, opts ...grpc.CallOption) (*MsgExecAuthorizedResponse, error) { + out := new(MsgExecAuthorizedResponse) + err := c.cc.Invoke(ctx, "/cosmos.authz.v1beta1.Msg/ExecAuthorized", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) RevokeAuthorization(ctx context.Context, in *MsgRevokeAuthorizationRequest, opts ...grpc.CallOption) (*MsgRevokeAuthorizationResponse, error) { + out := new(MsgRevokeAuthorizationResponse) + err := c.cc.Invoke(ctx, "/cosmos.authz.v1beta1.Msg/RevokeAuthorization", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // GrantAuthorization grants the provided authorization to the grantee on the granter's + // account with the provided expiration time. + GrantAuthorization(context.Context, *MsgGrantAuthorizationRequest) (*MsgGrantAuthorizationResponse, error) + // ExecAuthorized attempts to execute the provided messages using + // authorizations granted to the grantee. Each message should have only + // one signer corresponding to the granter of the authorization. + ExecAuthorized(context.Context, *MsgExecAuthorizedRequest) (*MsgExecAuthorizedResponse, error) + // RevokeAuthorization revokes any authorization corresponding to the provided method name on the + // granter's account that has been granted to the grantee. + RevokeAuthorization(context.Context, *MsgRevokeAuthorizationRequest) (*MsgRevokeAuthorizationResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) GrantAuthorization(ctx context.Context, req *MsgGrantAuthorizationRequest) (*MsgGrantAuthorizationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GrantAuthorization not implemented") +} +func (*UnimplementedMsgServer) ExecAuthorized(ctx context.Context, req *MsgExecAuthorizedRequest) (*MsgExecAuthorizedResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ExecAuthorized not implemented") +} +func (*UnimplementedMsgServer) RevokeAuthorization(ctx context.Context, req *MsgRevokeAuthorizationRequest) (*MsgRevokeAuthorizationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RevokeAuthorization not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_GrantAuthorization_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgGrantAuthorizationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).GrantAuthorization(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.authz.v1beta1.Msg/GrantAuthorization", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).GrantAuthorization(ctx, req.(*MsgGrantAuthorizationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ExecAuthorized_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgExecAuthorizedRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ExecAuthorized(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.authz.v1beta1.Msg/ExecAuthorized", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ExecAuthorized(ctx, req.(*MsgExecAuthorizedRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_RevokeAuthorization_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgRevokeAuthorizationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).RevokeAuthorization(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.authz.v1beta1.Msg/RevokeAuthorization", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).RevokeAuthorization(ctx, req.(*MsgRevokeAuthorizationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.authz.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GrantAuthorization", + Handler: _Msg_GrantAuthorization_Handler, + }, + { + MethodName: "ExecAuthorized", + Handler: _Msg_ExecAuthorized_Handler, + }, + { + MethodName: "RevokeAuthorization", + Handler: _Msg_RevokeAuthorization_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/authz/v1beta1/tx.proto", +} + +func (m *MsgGrantAuthorizationRequest) 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 *MsgGrantAuthorizationRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGrantAuthorizationRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expiration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expiration):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintTx(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x22 + if m.Authorization != nil { + { + size, err := m.Authorization.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintTx(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintTx(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgExecAuthorizedResponse) 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 *MsgExecAuthorizedResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgExecAuthorizedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Result != nil { + { + size, err := m.Result.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgExecAuthorizedRequest) 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 *MsgExecAuthorizedRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgExecAuthorizedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Msgs) > 0 { + for iNdEx := len(m.Msgs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Msgs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintTx(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgGrantAuthorizationResponse) 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 *MsgGrantAuthorizationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGrantAuthorizationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgRevokeAuthorizationRequest) 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 *MsgRevokeAuthorizationRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRevokeAuthorizationRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.MethodName) > 0 { + i -= len(m.MethodName) + copy(dAtA[i:], m.MethodName) + i = encodeVarintTx(dAtA, i, uint64(len(m.MethodName))) + i-- + dAtA[i] = 0x1a + } + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintTx(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintTx(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgRevokeAuthorizationResponse) 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 *MsgRevokeAuthorizationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRevokeAuthorizationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgGrantAuthorizationRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Authorization != nil { + l = m.Authorization.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Expiration) + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgExecAuthorizedResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Result != nil { + l = m.Result.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgExecAuthorizedRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Msgs) > 0 { + for _, e := range m.Msgs { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgGrantAuthorizationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgRevokeAuthorizationRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.MethodName) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgRevokeAuthorizationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgGrantAuthorizationRequest) 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: MsgGrantAuthorizationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGrantAuthorizationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", 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.Granter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", 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.Grantee = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authorization", 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 + } + if m.Authorization == nil { + m.Authorization = &types.Any{} + } + if err := m.Authorization.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Expiration", 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 + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Expiration, 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 *MsgExecAuthorizedResponse) 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: MsgExecAuthorizedResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgExecAuthorizedResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", 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 + } + if m.Result == nil { + m.Result = &types1.Result{} + } + if err := m.Result.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 *MsgExecAuthorizedRequest) 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: MsgExecAuthorizedRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgExecAuthorizedRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", 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.Grantee = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Msgs", 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.Msgs = append(m.Msgs, &types.Any{}) + if err := m.Msgs[len(m.Msgs)-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 *MsgGrantAuthorizationResponse) 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: MsgGrantAuthorizationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGrantAuthorizationResponse: 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 *MsgRevokeAuthorizationRequest) 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: MsgRevokeAuthorizationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRevokeAuthorizationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", 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.Granter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", 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.Grantee = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MethodName", 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.MethodName = string(dAtA[iNdEx:postIndex]) + 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 *MsgRevokeAuthorizationResponse) 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: MsgRevokeAuthorizationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRevokeAuthorizationResponse: 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 skipTx(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, ErrIntOverflowTx + } + 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, ErrIntOverflowTx + } + 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, ErrIntOverflowTx + } + 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, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/bank/app_test.go b/x/bank/app_test.go index 82d898b09088..6cb3655cc829 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 f1affb9d99b8..c7da72c199dd 100644 --- a/x/bank/bench_test.go +++ b/x/bank/bench_test.go @@ -18,6 +18,7 @@ import ( var moduleAccAddr = authtypes.NewModuleAddress(stakingtypes.BondedPoolName) func BenchmarkOneBankSendTxPerBlock(b *testing.B) { + b.ReportAllocs() // Add an account at genesis acc := authtypes.BaseAccount{ Address: addr1.String(), @@ -29,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 @@ -71,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 a8e94d24569f..98127101be7a 100644 --- a/x/bank/client/cli/cli_test.go +++ b/x/bank/client/cli/cli_test.go @@ -32,12 +32,57 @@ func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") cfg := network.DefaultConfig() + genesisState := cfg.GenesisState cfg.NumValidators = 1 + var bankGenesis types.GenesisState + s.Require().NoError(cfg.Codec.UnmarshalJSON(genesisState[types.ModuleName], &bankGenesis)) + + bankGenesis.DenomMetadata = []types.Metadata{ + { + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + { + Denom: "uatom", + Exponent: 0, + Aliases: []string{"microatom"}, + }, + { + Denom: "atom", + Exponent: 6, + Aliases: []string{"ATOM"}, + }, + }, + Base: "uatom", + Display: "atom", + }, + { + Description: "Ethereum mainnet token", + DenomUnits: []*types.DenomUnit{ + { + Denom: "wei", + Exponent: 0, + }, + { + Denom: "eth", + Exponent: 6, + Aliases: []string{"ETH"}, + }, + }, + Base: "wei", + Display: "eth", + }, + } + + bankGenesisBz, err := cfg.Codec.MarshalJSON(&bankGenesis) + s.Require().NoError(err) + genesisState[types.ModuleName] = bankGenesisBz + cfg.GenesisState = genesisState + s.cfg = cfg s.network = network.New(s.T(), cfg) - _, err := s.network.WaitForHeight(1) + _, err = s.network.WaitForHeight(1) s.Require().NoError(err) } @@ -181,6 +226,127 @@ func (s *IntegrationTestSuite) TestGetCmdQueryTotalSupply() { } } +func (s *IntegrationTestSuite) TestGetCmdQueryDenomsMetadata() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expected proto.Message + }{ + { + name: "all denoms client metadata", + args: []string{ + fmt.Sprintf("--%s=1", flags.FlagHeight), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + respType: &types.QueryDenomsMetadataResponse{}, + expected: &types.QueryDenomsMetadataResponse{ + Metadatas: []types.Metadata{ + { + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + { + Denom: "uatom", + Exponent: 0, + Aliases: []string{"microatom"}, + }, + { + Denom: "atom", + Exponent: 6, + Aliases: []string{"ATOM"}, + }, + }, + Base: "uatom", + Display: "atom", + }, + { + Description: "Ethereum mainnet token", + DenomUnits: []*types.DenomUnit{ + { + Denom: "wei", + Exponent: 0, + Aliases: []string{}, + }, + { + Denom: "eth", + Exponent: 6, + Aliases: []string{"ETH"}, + }, + }, + Base: "wei", + Display: "eth", + }, + }, + Pagination: &query.PageResponse{Total: 2}, + }, + }, + { + name: "client metadata of a specific denomination", + args: []string{ + fmt.Sprintf("--%s=1", flags.FlagHeight), + fmt.Sprintf("--%s=%s", cli.FlagDenom, "uatom"), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + respType: &types.QueryDenomMetadataResponse{}, + expected: &types.QueryDenomMetadataResponse{ + Metadata: types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + { + Denom: "uatom", + Exponent: 0, + Aliases: []string{"microatom"}, + }, + { + Denom: "atom", + Exponent: 6, + Aliases: []string{"ATOM"}, + }, + }, + Base: "uatom", + Display: "atom", + }, + }, + }, + { + name: "client metadata of a bogus denom", + args: []string{ + fmt.Sprintf("--%s=1", flags.FlagHeight), + fmt.Sprintf("--%s=foobar", cli.FlagDenom), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + expectErr: true, + respType: &types.QueryDenomMetadataResponse{}, + expected: &types.QueryDenomMetadataResponse{ + Metadata: types.Metadata{ + DenomUnits: []*types.DenomUnit{}, + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdDenomsMetadata() + 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().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType)) + s.Require().Equal(tc.expected, tc.respType) + } + }) + } +} + func (s *IntegrationTestSuite) TestNewSendTxCmdGenOnly() { val := s.network.Validators[0] @@ -206,7 +372,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() { @@ -295,61 +464,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/query.go b/x/bank/client/cli/query.go index 38badd7332d0..a192c8deba1b 100644 --- a/x/bank/client/cli/query.go +++ b/x/bank/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "strings" @@ -33,6 +32,7 @@ func GetQueryCmd() *cobra.Command { cmd.AddCommand( GetBalancesCmd(), GetCmdQueryTotalSupply(), + GetCmdDenomsMetadata(), ) return cmd @@ -74,11 +74,10 @@ Example: if err != nil { return err } - + ctx := cmd.Context() if denom == "" { params := types.NewQueryAllBalancesRequest(addr, pageReq) - - res, err := queryClient.AllBalances(context.Background(), params) + res, err := queryClient.AllBalances(ctx, params) if err != nil { return err } @@ -86,7 +85,7 @@ Example: } params := types.NewQueryBalanceRequest(addr, denom) - res, err := queryClient.Balance(context.Background(), params) + res, err := queryClient.Balance(ctx, params) if err != nil { return err } @@ -102,6 +101,60 @@ Example: return cmd } +// GetCmdDenomsMetadata defines the cobra command to query client denomination metadata. +func GetCmdDenomsMetadata() *cobra.Command { + cmd := &cobra.Command{ + Use: "denom-metadata", + Short: "Query the client metadata for coin denominations", + Long: strings.TrimSpace( + fmt.Sprintf(`Query the client metadata for all the registered coin denominations + +Example: + To query for the client metadata of all coin denominations use: + $ %s query %s denom-metadata + +To query for the client metadata of a specific coin denomination use: + $ %s query %s denom-metadata --denom=[denom] +`, + version.AppName, types.ModuleName, version.AppName, types.ModuleName, + ), + ), + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + denom, err := cmd.Flags().GetString(FlagDenom) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + if denom == "" { + res, err := queryClient.DenomsMetadata(cmd.Context(), &types.QueryDenomsMetadataRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + } + + res, err := queryClient.DenomMetadata(cmd.Context(), &types.QueryDenomMetadataRequest{Denom: denom}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + cmd.Flags().String(FlagDenom, "", "The specific denomination to query client metadata for") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + func GetCmdQueryTotalSupply() *cobra.Command { cmd := &cobra.Command{ Use: "total", @@ -129,9 +182,9 @@ To query for the total supply of a specific coin denomination use: } queryClient := types.NewQueryClient(clientCtx) - + ctx := cmd.Context() if denom == "" { - res, err := queryClient.TotalSupply(context.Background(), &types.QueryTotalSupplyRequest{}) + res, err := queryClient.TotalSupply(ctx, &types.QueryTotalSupplyRequest{}) if err != nil { return err } @@ -139,7 +192,7 @@ To query for the total supply of a specific coin denomination use: return clientCtx.PrintProto(res) } - res, err := queryClient.SupplyOf(context.Background(), &types.QuerySupplyOfRequest{Denom: denom}) + res, err := queryClient.SupplyOf(ctx, &types.QuerySupplyOfRequest{Denom: denom}) if err != nil { return err } diff --git a/x/bank/client/cli/tx.go b/x/bank/client/cli/tx.go index d88a95fc6c09..4a615d5045ca 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 6274946705ae..d6290a1fef94 100644 --- a/x/bank/client/rest/grpc_query_test.go +++ b/x/bank/client/rest/grpc_query_test.go @@ -98,6 +98,109 @@ func (s *IntegrationTestSuite) TestTotalSupplyGRPCHandler() { } } +func (s *IntegrationTestSuite) TestDenomMetadataGRPCHandler() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + headers map[string]string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "test GRPC client metadata", + fmt.Sprintf("%s/cosmos/bank/v1beta1/denoms_metadata", baseURL), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + false, + &types.QueryDenomsMetadataResponse{}, + &types.QueryDenomsMetadataResponse{ + Metadatas: []types.Metadata{ + { + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + { + Denom: "uatom", + Exponent: 0, + Aliases: []string{"microatom"}, + }, + { + Denom: "atom", + Exponent: 6, + Aliases: []string{"ATOM"}, + }, + }, + Base: "uatom", + Display: "atom", + }, + }, + Pagination: &query.PageResponse{Total: 1}, + }, + }, + { + "GRPC client metadata of a specific denom", + fmt.Sprintf("%s/cosmos/bank/v1beta1/denoms_metadata/uatom", baseURL), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + false, + &types.QueryDenomMetadataResponse{}, + &types.QueryDenomMetadataResponse{ + Metadata: types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + { + Denom: "uatom", + Exponent: 0, + Aliases: []string{"microatom"}, + }, + { + Denom: "atom", + Exponent: 6, + Aliases: []string{"ATOM"}, + }, + }, + Base: "uatom", + Display: "atom", + }, + }, + }, + { + "GRPC client metadata of a bogus denom", + fmt.Sprintf("%s/cosmos/bank/v1beta1/denoms_metadata/foobar", baseURL), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + true, + &types.QueryDenomMetadataResponse{}, + &types.QueryDenomMetadataResponse{ + Metadata: types.Metadata{ + DenomUnits: []*types.DenomUnit{}, + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + s.Require().NoError(err) + + if tc.expErr { + s.Require().Error(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + } else { + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + func (s *IntegrationTestSuite) TestBalancesGRPCHandler() { val := s.network.Validators[0] baseURL := val.APIAddress diff --git a/x/bank/client/rest/query_test.go b/x/bank/client/rest/query_test.go index 5ca3a15be506..11d731b6dcba 100644 --- a/x/bank/client/rest/query_test.go +++ b/x/bank/client/rest/query_test.go @@ -11,6 +11,7 @@ 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/bank/types" ) type IntegrationTestSuite struct { @@ -24,12 +25,41 @@ func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") cfg := network.DefaultConfig() + genesisState := cfg.GenesisState cfg.NumValidators = 1 + var bankGenesis types.GenesisState + s.Require().NoError(cfg.Codec.UnmarshalJSON(genesisState[types.ModuleName], &bankGenesis)) + + bankGenesis.DenomMetadata = []types.Metadata{ + { + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + { + Denom: "uatom", + Exponent: 0, + Aliases: []string{"microatom"}, + }, + { + Denom: "atom", + Exponent: 6, + Aliases: []string{"ATOM"}, + }, + }, + Base: "uatom", + Display: "atom", + }, + } + + bankGenesisBz, err := cfg.Codec.MarshalJSON(&bankGenesis) + s.Require().NoError(err) + genesisState[types.ModuleName] = bankGenesisBz + cfg.GenesisState = genesisState + s.cfg = cfg s.network = network.New(s.T(), cfg) - _, err := s.network.WaitForHeight(2) + _, err = s.network.WaitForHeight(2) s.Require().NoError(err) } diff --git a/x/bank/client/testutil/cli_helpers.go b/x/bank/client/testutil/cli_helpers.go index 820c668c4558..57a5a5798edc 100644 --- a/x/bank/client/testutil/cli_helpers.go +++ b/x/bank/client/testutil/cli_helpers.go @@ -4,17 +4,17 @@ import ( "context" "fmt" - gogogrpc "github.com/gogo/protobuf/grpc" - "github.com/spf13/cobra" "github.com/tendermint/tendermint/libs/cli" - grpc "google.golang.org/grpc" "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" + 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" ) @@ -33,86 +33,55 @@ func QueryBalancesExec(clientCtx client.Context, address fmt.Stringer, extraArgs return clitestutil.ExecTestCLICmd(clientCtx, bankcli.GetBalancesCmd(), args) } -// serviceMsgClientConn is an instance of grpc.ClientConn that is used to test building -// transactions with MsgClient's. It is intended to be replaced by the work in -// https://github.com/cosmos/cosmos-sdk/issues/7541 when that is ready. -type serviceMsgClientConn struct { - msgs []sdk.Msg -} - -func (t *serviceMsgClientConn) Invoke(_ context.Context, method string, args, _ interface{}, _ ...grpc.CallOption) error { - req, ok := args.(sdk.MsgRequest) - if !ok { - return fmt.Errorf("%T should implement %T", args, (*sdk.MsgRequest)(nil)) - } - - err := req.ValidateBasic() +// 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() + + // This sets a legacy Proto MsgSend. + err := txBuilder.SetMsgs(&types.MsgSend{ + FromAddress: from.String(), + ToAddress: to.String(), + Amount: amount, + }) if err != nil { - return err + return nil, err } - t.msgs = append(t.msgs, sdk.ServiceMsg{ - MethodName: method, - Request: req, - }) - - return nil -} - -func (t *serviceMsgClientConn) NewStream(context.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { - return nil, fmt.Errorf("not supported") -} - -var _ gogogrpc.ClientConn = &serviceMsgClientConn{} + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) -// 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 - } + // setup txFactory + txFactory := tx.Factory{}. + WithChainID(clientCtx.ChainID). + WithKeybase(clientCtx.Keyring). + WithTxConfig(clientCtx.TxConfig). + WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) - coins, err := sdk.ParseCoinsNormalized(args[2]) - if err != nil { - return err - } - - msg := types.NewMsgSend(clientCtx.GetFromAddress(), toAddr, coins) - svcMsgClientConn := &serviceMsgClientConn{} - bankMsgClient := types.NewMsgClient(svcMsgClientConn) - _, err = bankMsgClient.Send(context.Background(), msg) - if err != nil { - return err - } - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.msgs...) - }, + // Sign Tx. + err = authclient.SignTx(txFactory, clientCtx, keyName, txBuilder, false, true) + if err != nil { + return nil, err } - flags.AddTxFlagsToCmd(cmd) - - return cmd -} + txBytes, err := clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return nil, err + } -// 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...) + // Broadcast the tx via gRPC. + queryClient := txtypes.NewServiceClient(clientCtx) - return clitestutil.ExecTestCLICmd(clientCtx, newSendTxMsgServiceCmd(), args) + 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 d30415c6a285..2781645c4e5c 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 bdb8cdc304e1..7bf7dc920315 100644 --- a/x/bank/keeper/genesis_test.go +++ b/x/bank/keeper/genesis_test.go @@ -3,54 +3,64 @@ 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) + // add mint module balance as nil + expectedBalances = append(expectedBalances, types.Balance{Address: "cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q", Coins: nil}) 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) 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 } func (suite *IntegrationTestSuite) TestInitGenesis() { - require := suite.Require() m := types.Metadata{Description: sdk.DefaultBondDenom, Base: sdk.DefaultBondDenom, Display: sdk.DefaultBondDenom} g := types.DefaultGenesisState() g.DenomMetadata = []types.Metadata{m} bk := suite.app.BankKeeper bk.InitGenesis(suite.ctx, g) - m2 := bk.GetDenomMetaData(suite.ctx, m.Base) - require.Equal(m, m2) + m2, found := bk.GetDenomMetaData(suite.ctx, m.Base) + suite.Require().True(found) + suite.Require().Equal(m, m2) } diff --git a/x/bank/keeper/grpc_query.go b/x/bank/keeper/grpc_query.go index bcf143b8aa14..b8f69ef1275b 100644 --- a/x/bank/keeper/grpc_query.go +++ b/x/bank/keeper/grpc_query.go @@ -57,11 +57,9 @@ func (k BaseKeeper) AllBalances(ctx context.Context, req *types.QueryAllBalances sdkCtx := sdk.UnwrapSDKContext(ctx) balances := sdk.NewCoins() - store := sdkCtx.KVStore(k.storeKey) - balancesStore := prefix.NewStore(store, types.BalancesPrefix) - accountStore := prefix.NewStore(balancesStore, addr.Bytes()) + accountStore := k.getAccountStore(sdkCtx, addr) - pageRes, err := query.Paginate(accountStore, req.Pagination, func(key []byte, value []byte) error { + pageRes, err := query.Paginate(accountStore, req.Pagination, func(_, value []byte) error { var result sdk.Coin err := k.cdc.UnmarshalBinaryBare(value, &result) if err != nil { @@ -113,3 +111,53 @@ func (k BaseKeeper) Params(ctx context.Context, req *types.QueryParamsRequest) ( return &types.QueryParamsResponse{Params: params}, nil } + +// DenomsMetadata implements Query/DenomsMetadata gRPC method. +func (k BaseKeeper) DenomsMetadata(c context.Context, req *types.QueryDenomsMetadataRequest) (*types.QueryDenomsMetadataResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(c) + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.DenomMetadataPrefix) + + metadatas := []types.Metadata{} + pageRes, err := query.Paginate(store, req.Pagination, func(_, value []byte) error { + var metadata types.Metadata + k.cdc.MustUnmarshalBinaryBare(value, &metadata) + + metadatas = append(metadatas, metadata) + return nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryDenomsMetadataResponse{ + Metadatas: metadatas, + Pagination: pageRes, + }, nil +} + +// DenomMetadata implements Query/DenomMetadata gRPC method. +func (k BaseKeeper) DenomMetadata(c context.Context, req *types.QueryDenomMetadataRequest) (*types.QueryDenomMetadataResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + if req.Denom == "" { + return nil, status.Error(codes.InvalidArgument, "invalid denom") + } + + ctx := sdk.UnwrapSDKContext(c) + + metadata, found := k.GetDenomMetaData(ctx, req.Denom) + if !found { + return nil, status.Errorf(codes.NotFound, "client metadata for denom %s", req.Denom) + } + + return &types.QueryDenomMetadataResponse{ + Metadata: metadata, + }, nil +} diff --git a/x/bank/keeper/grpc_query_test.go b/x/bank/keeper/grpc_query_test.go index 27c29fd9e730..cd319b247b0d 100644 --- a/x/bank/keeper/grpc_query_test.go +++ b/x/bank/keeper/grpc_query_test.go @@ -4,6 +4,11 @@ package keeper_test 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" @@ -31,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) @@ -63,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) @@ -86,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) @@ -101,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) @@ -119,3 +128,182 @@ func (suite *IntegrationTestSuite) TestQueryParams() { suite.Require().NotNil(res) suite.Require().Equal(suite.app.BankKeeper.GetParams(suite.ctx), res.GetParams()) } + +func (suite *IntegrationTestSuite) QueryDenomsMetadataRequest() { + var ( + req *types.QueryDenomsMetadataRequest + expMetadata = []types.Metadata{} + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty pagination", + func() { + req = &types.QueryDenomsMetadataRequest{} + }, + true, + }, + { + "success, no results", + func() { + req = &types.QueryDenomsMetadataRequest{ + Pagination: &query.PageRequest{ + Limit: 3, + CountTotal: true, + }, + } + }, + true, + }, + { + "success", + func() { + metadataAtom := types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + { + Denom: "uatom", + Exponent: 0, + Aliases: []string{"microatom"}, + }, + { + Denom: "atom", + Exponent: 6, + Aliases: []string{"ATOM"}, + }, + }, + Base: "uatom", + Display: "atom", + } + + metadataEth := types.Metadata{ + Description: "Ethereum native token", + DenomUnits: []*types.DenomUnit{ + { + Denom: "wei", + Exponent: 0, + }, + { + Denom: "eth", + Exponent: 18, + Aliases: []string{"ETH", "ether"}, + }, + }, + Base: "wei", + Display: "eth", + } + + suite.app.BankKeeper.SetDenomMetaData(suite.ctx, metadataAtom) + suite.app.BankKeeper.SetDenomMetaData(suite.ctx, metadataEth) + expMetadata = []types.Metadata{metadataAtom, metadataEth} + req = &types.QueryDenomsMetadataRequest{ + Pagination: &query.PageRequest{ + Limit: 7, + CountTotal: true, + }, + } + }, + true, + }, + } + + 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.DenomsMetadata(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expMetadata, res.Metadatas) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *IntegrationTestSuite) QueryDenomMetadataRequest() { + var ( + req *types.QueryDenomMetadataRequest + expMetadata = types.Metadata{} + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty denom", + func() { + req = &types.QueryDenomMetadataRequest{} + }, + false, + }, + { + "not found denom", + func() { + req = &types.QueryDenomMetadataRequest{ + Denom: "foo", + } + }, + false, + }, + { + "success", + func() { + expMetadata := types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + { + Denom: "uatom", + Exponent: 0, + Aliases: []string{"microatom"}, + }, + { + Denom: "atom", + Exponent: 6, + Aliases: []string{"ATOM"}, + }, + }, + Base: "uatom", + Display: "atom", + } + + suite.app.BankKeeper.SetDenomMetaData(suite.ctx, expMetadata) + req = &types.QueryDenomMetadataRequest{ + Denom: expMetadata.Base, + } + }, + true, + }, + } + + 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.DenomMetadata(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expMetadata, res.Metadata) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/x/bank/keeper/keeper.go b/x/bank/keeper/keeper.go index 016537221f89..153d5d4a55d9 100644 --- a/x/bank/keeper/keeper.go +++ b/x/bank/keeper/keeper.go @@ -25,9 +25,8 @@ 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 + GetDenomMetaData(ctx sdk.Context, denom string) (types.Metadata, bool) SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata) IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool) @@ -102,7 +101,7 @@ 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 } @@ -112,7 +111,7 @@ func (k BaseKeeper) DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr return sdkerrors.Wrap(err, "failed to track delegation") } - err := k.AddCoins(ctx, moduleAccAddr, amt) + err := k.addCoins(ctx, moduleAccAddr, amt) if err != nil { return err } @@ -135,7 +134,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.subtractCoins(ctx, moduleAccAddr, amt) if err != nil { return err } @@ -144,7 +143,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 +167,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 { @@ -180,15 +179,19 @@ func (k BaseKeeper) SetSupply(ctx sdk.Context, supply exported.SupplyI) { } // GetDenomMetaData retrieves the denomination metadata -func (k BaseKeeper) GetDenomMetaData(ctx sdk.Context, denom string) types.Metadata { +func (k BaseKeeper) GetDenomMetaData(ctx sdk.Context, denom string) (types.Metadata, bool) { store := ctx.KVStore(k.storeKey) store = prefix.NewStore(store, types.DenomMetadataKey(denom)) bz := store.Get([]byte(denom)) + if bz == nil { + return types.Metadata{}, false + } + var metadata types.Metadata k.cdc.MustUnmarshalBinaryBare(bz, &metadata) - return metadata + return metadata, true } // GetAllDenomMetaData retrieves all denominations metadata @@ -328,7 +331,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 } @@ -337,7 +340,7 @@ 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) @@ -357,7 +360,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.subtractCoins(ctx, acc.GetAddress(), amt) if err != nil { return err } @@ -365,7 +368,7 @@ 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) diff --git a/x/bank/keeper/keeper_test.go b/x/bank/keeper/keeper_test.go index aea72006a45f..20ec854a81c2 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( @@ -215,7 +224,7 @@ func (suite *IntegrationTestSuite) TestSupply_MintCoins() { func (suite *IntegrationTestSuite) TestSupply_BurnCoins() { app := simapp.Setup(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1}) - appCodec, _ := simapp.MakeCodecs() + appCodec := simapp.MakeTestEncodingConfig().Marshaler // add module accounts to supply keeper maccPerms := simapp.GetMaccPerms() @@ -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) - - initialSupply := keeper.GetSupply(ctx) - initialSupply.Inflate(initCoins) - keeper.SetSupply(ctx, initialSupply) - - 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(). + NoError(keeper.MintCoins(ctx, authtypes.Minter, initCoins)) + suite. + Require(). + NoError(keeper.SendCoinsFromModuleToAccount(ctx, authtypes.Minter, burnerAcc.GetAddress(), initCoins)) + + // 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, 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(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(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))) @@ -560,15 +549,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(6, len(events)) + suite.Require().Equal(abci.Event(event1), events[4]) + suite.Require().Equal(abci.Event(event2), events[5]) } func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { @@ -603,12 +593,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(3, len(events)) // 3 events because minting event is there event1 := sdk.Event{ Type: sdk.EventTypeMessage, @@ -618,19 +607,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[2]) // it's the third event since we have the minting event before // 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(11, len(events)) event2 := sdk.Event{ Type: sdk.EventTypeMessage, @@ -664,10 +653,11 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { 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[7]) + suite.Require().Equal(abci.Event(event2), events[8]) + suite.Require().Equal(abci.Event(event3), events[9]) + suite.Require().Equal(abci.Event(event4), events[10]) } 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) @@ -981,8 +967,8 @@ func (suite *IntegrationTestSuite) TestSetDenomMetaData() { app.BankKeeper.SetDenomMetaData(ctx, metadata[i]) } - actualMetadata := app.BankKeeper.GetDenomMetaData(ctx, metadata[1].Base) - + actualMetadata, found := app.BankKeeper.GetDenomMetaData(ctx, metadata[1].Base) + suite.Require().True(found) suite.Require().Equal(metadata[1].GetBase(), actualMetadata.GetBase()) suite.Require().Equal(metadata[1].GetDisplay(), actualMetadata.GetDisplay()) suite.Require().Equal(metadata[1].GetDescription(), actualMetadata.GetDescription()) diff --git a/x/bank/keeper/migrations.go b/x/bank/keeper/migrations.go new file mode 100644 index 000000000000..de6b61a9c2b6 --- /dev/null +++ b/x/bank/keeper/migrations.go @@ -0,0 +1,20 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + 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 +} + +var _ MigrationKeeper = (*BaseKeeper)(nil) + +// Migrate1 implements MigrationKeeper.Migrate1 method. +func (keeper BaseKeeper) Migrate1(ctx sdk.Context) error { + return v042.MigrateStore(ctx, keeper.storeKey) +} diff --git a/x/bank/keeper/querier_test.go b/x/bank/keeper/querier_test.go index 84ec3d095799..ef1edc57679d 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 c1000b7d3025..c0c3a4cb62dc 100644 --- a/x/bank/keeper/send.go +++ b/x/bank/keeper/send.go @@ -2,7 +2,6 @@ package keeper import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -18,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) @@ -90,7 +83,7 @@ func (k BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input, return err } - err = k.SubtractCoins(ctx, inAddress, in.Coins) + err = k.subtractCoins(ctx, inAddress, in.Coins) if err != nil { return err } @@ -108,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 } @@ -151,12 +144,12 @@ func (k BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAd ), }) - err := k.SubtractCoins(ctx, fromAddr, amt) + err := k.subtractCoins(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 } @@ -174,9 +167,9 @@ 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 +// subtractCoins removes amt coins the account by the given address. 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 { +func (k BaseSendKeeper) subtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error { if !amt.IsValid() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) } @@ -195,7 +188,7 @@ 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 } @@ -204,10 +197,10 @@ func (k BaseSendKeeper) SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt return nil } -// AddCoins adds amt to the account balance given by the provided address. An +// 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 { +func (k BaseSendKeeper) addCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error { if !amt.IsValid() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) } @@ -216,7 +209,7 @@ 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 } @@ -233,23 +226,21 @@ func (k BaseSendKeeper) ClearBalances(ctx sdk.Context, addr sdk.AccAddress) { return false }) - store := ctx.KVStore(k.storeKey) - balancesStore := prefix.NewStore(store, types.BalancesPrefix) - accountStore := prefix.NewStore(balancesStore, addr.Bytes()) + accountStore := k.getAccountStore(ctx, addr) for _, key := range keys { accountStore.Delete(key) } } -// 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 { +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 } @@ -258,15 +249,13 @@ 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()) } - store := ctx.KVStore(k.storeKey) - balancesStore := prefix.NewStore(store, types.BalancesPrefix) - accountStore := prefix.NewStore(balancesStore, addr.Bytes()) + accountStore := k.getAccountStore(ctx, addr) bz := k.cdc.MustMarshalBinaryBare(&balance) accountStore.Set([]byte(balance.Denom), bz) diff --git a/x/bank/keeper/view.go b/x/bank/keeper/view.go index d4bcabad2b62..1b50fb81d3aa 100644 --- a/x/bank/keeper/view.go +++ b/x/bank/keeper/view.go @@ -97,9 +97,7 @@ func (k BaseViewKeeper) GetAccountsBalances(ctx sdk.Context) []types.Balance { // GetBalance returns the balance of a specific denomination for a given account // by address. func (k BaseViewKeeper) GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin { - store := ctx.KVStore(k.storeKey) - balancesStore := prefix.NewStore(store, types.BalancesPrefix) - accountStore := prefix.NewStore(balancesStore, addr.Bytes()) + accountStore := k.getAccountStore(ctx, addr) bz := accountStore.Get([]byte(denom)) if bz == nil { @@ -116,9 +114,7 @@ func (k BaseViewKeeper) GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom s // provides the token balance to a callback. If true is returned from the // callback, iteration is halted. func (k BaseViewKeeper) IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(sdk.Coin) bool) { - store := ctx.KVStore(k.storeKey) - balancesStore := prefix.NewStore(store, types.BalancesPrefix) - accountStore := prefix.NewStore(balancesStore, addr.Bytes()) + accountStore := k.getAccountStore(ctx, addr) iterator := accountStore.Iterator(nil, nil) defer iterator.Close() @@ -214,3 +210,10 @@ func (k BaseViewKeeper) ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) er return nil } + +// getAccountStore gets the account store of the given address. +func (k BaseViewKeeper) getAccountStore(ctx sdk.Context, addr sdk.AccAddress) prefix.Store { + store := ctx.KVStore(k.storeKey) + + return prefix.NewStore(store, types.CreateAccountBalancesPrefix(addr)) +} diff --git a/x/bank/legacy/v040/keys.go b/x/bank/legacy/v040/keys.go new file mode 100644 index 000000000000..bd1035599164 --- /dev/null +++ b/x/bank/legacy/v040/keys.go @@ -0,0 +1,47 @@ +package v040 + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" +) + +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 = []byte("balances") + 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 { + addr := key[:v040auth.AddrLen] + if len(addr) != v040auth.AddrLen { + panic(fmt.Sprintf("unexpected account address key length; got: %d, expected: %d", len(addr), v040auth.AddrLen)) + } + + return sdk.AccAddress(addr) +} diff --git a/x/bank/legacy/v040/types.go b/x/bank/legacy/v040/types.go deleted file mode 100644 index 04ac05f9fcd3..000000000000 --- a/x/bank/legacy/v040/types.go +++ /dev/null @@ -1,5 +0,0 @@ -package v040 - -const ( - ModuleName = "bank" -) diff --git a/x/bank/legacy/v042/keys.go b/x/bank/legacy/v042/keys.go new file mode 100644 index 000000000000..8d718b1c5d3c --- /dev/null +++ b/x/bank/legacy/v042/keys.go @@ -0,0 +1,50 @@ +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 new file mode 100644 index 000000000000..9295d8d2ff77 --- /dev/null +++ b/x/bank/legacy/v042/store.go @@ -0,0 +1,38 @@ +package v042 + +import ( + "github.com/cosmos/cosmos-sdk/store/prefix" + 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" +) + +// MigrateStore performs in-place store migrations from v0.40 to v0.42. The +// migration includes: +// +// - Change addresses to be length-prefixed. +// - Change balances prefix to 1 byte +func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error { + store := ctx.KVStore(storeKey) + + // old key is of format: + // prefix ("balances") || addrBytes (20 bytes) || denomBytes + // new key is of format + // prefix (0x02) || addrLen (1 byte) || addrBytes || denomBytes + oldStore := prefix.NewStore(store, v040bank.BalancesPrefix) + + oldStoreIter := oldStore.Iterator(nil, nil) + defer oldStoreIter.Close() + + for ; oldStoreIter.Valid(); oldStoreIter.Next() { + addr := v040bank.AddressFromBalancesStore(oldStoreIter.Key()) + denom := oldStoreIter.Key()[v040auth.AddrLen:] + newStoreKey := append(CreateAccountBalancesPrefix(addr), denom...) + + // Set new key on store. Values don't change. + store.Set(newStoreKey, oldStoreIter.Value()) + oldStore.Delete(oldStoreIter.Key()) + } + + return nil +} diff --git a/x/bank/legacy/v042/store_test.go b/x/bank/legacy/v042/store_test.go new file mode 100644 index 000000000000..e1b6a7987a04 --- /dev/null +++ b/x/bank/legacy/v042/store_test.go @@ -0,0 +1,36 @@ +package v042_test + +import ( + "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" + v040bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v040" + v042bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v042" +) + +func TestStoreMigration(t *testing.T) { + bankKey := sdk.NewKVStoreKey("bank") + ctx := testutil.DefaultContext(bankKey, sdk.NewTransientStoreKey("transient_test")) + store := ctx.KVStore(bankKey) + + _, _, addr := testdata.KeyTestPubAddr() + denom := []byte("foo") + value := []byte("bar") + + oldKey := append(append(v040bank.BalancesPrefix, addr...), denom...) + store.Set(oldKey, value) + + err := v042bank.MigrateStore(ctx, bankKey) + require.NoError(t, err) + + newKey := append(v042bank.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)) + require.Nil(t, store.Get(oldKey)) + require.Equal(t, value, store.Get(newKey)) +} diff --git a/x/bank/module.go b/x/bank/module.go index bc998634c12e..77bff6b73b61 100644 --- a/x/bank/module.go +++ b/x/bank/module.go @@ -58,7 +58,7 @@ func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, _ client.TxEncodi return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } - return types.ValidateGenesis(data) + return data.Validate() } // RegisterRESTRoutes registers the REST routes for the bank module. @@ -100,6 +100,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) + }) } // NewAppModule creates a new AppModule object @@ -151,6 +154,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(gs) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 2 } + // BeginBlock performs a no-op. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} diff --git a/x/bank/simulation/operations.go b/x/bank/simulation/operations.go index 9fcc80f0fe45..40be2feeb4a0 100644 --- a/x/bank/simulation/operations.go +++ b/x/bank/simulation/operations.go @@ -76,7 +76,7 @@ func SimulateMsgSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Operatio return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "invalid transfers"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } @@ -217,7 +217,7 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Ope return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "invalid transfers"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } diff --git a/x/bank/simulation/operations_test.go b/x/bank/simulation/operations_test.go index 144dc5dd3429..efe6de624f5d 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/01_state.md b/x/bank/spec/01_state.md index f744e2e779a1..6ca6b97e8fc1 100644 --- a/x/bank/spec/01_state.md +++ b/x/bank/spec/01_state.md @@ -7,5 +7,5 @@ order: 1 The `x/bank` module keeps state of two primary objects, account balances and the total supply of all balances. -- Balances: `[]byte("balances") | []byte(address) / []byte(balance.Denom) -> ProtocolBuffer(balance)` - Supply: `0x0 -> ProtocolBuffer(Supply)` +- Balances: `0x2 | byte(address length) | []byte(address) | []byte(balance.Denom) -> ProtocolBuffer(balance)` diff --git a/x/bank/spec/02_keepers.md b/x/bank/spec/02_keepers.md index ec74358fbb70..e7e3caba545f 100644 --- a/x/bank/spec/02_keepers.md +++ b/x/bank/spec/02_keepers.md @@ -44,7 +44,7 @@ The base keeper provides full-permission access: the ability to arbitrary modify type Keeper interface { SendKeeper - InitGenesis(sdk.Context, types.GenesisState) + InitGenesis(sdk.Context, *types.GenesisState) ExportGenesis(sdk.Context) *types.GenesisState GetSupply(ctx sdk.Context) exported.SupplyI @@ -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/03_messages.md b/x/bank/spec/03_messages.md index ecb9dc695fae..07c42f5f78e5 100644 --- a/x/bank/spec/03_messages.md +++ b/x/bank/spec/03_messages.md @@ -6,18 +6,11 @@ order: 3 ## MsgSend -```go -// MsgSend represents a message to send coins from one account to another. -message MsgSend { - string from_address = 1; - string to_address = 2; - repeated cosmos.base.v1beta1.Coin amount = 3; -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28 `handleMsgSend` just runs `inputOutputCoins`. -``` +```go handleMsgSend(msg MsgSend) inputSum = 0 for input in inputs diff --git a/x/bank/types/balance.go b/x/bank/types/balance.go new file mode 100644 index 000000000000..49330bc06af1 --- /dev/null +++ b/x/bank/types/balance.go @@ -0,0 +1,97 @@ +package types + +import ( + "bytes" + "encoding/json" + fmt "fmt" + "sort" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/exported" +) + +var _ exported.GenesisBalance = (*Balance)(nil) + +// GetAddress returns the account address of the Balance object. +func (b Balance) GetAddress() sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(b.Address) + if err != nil { + panic(fmt.Errorf("couldn't convert %q to account address: %v", b.Address, err)) + } + + return addr +} + +// GetCoins returns the account coins of the Balance object. +func (b Balance) GetCoins() sdk.Coins { + return b.Coins +} + +// Validate checks for address and coins correctness. +func (b Balance) Validate() error { + _, err := sdk.AccAddressFromBech32(b.Address) + if err != nil { + return err + } + + if len(b.Coins) == 0 { + return fmt.Errorf("empty or nil coins for address %s", b.Address) + } + + seenDenoms := make(map[string]bool) + + // NOTE: we perform a custom validation since the coins.Validate function + // errors on zero balance coins + for _, coin := range b.Coins { + if seenDenoms[coin.Denom] { + return fmt.Errorf("duplicate denomination %s", coin.Denom) + } + + if err := sdk.ValidateDenom(coin.Denom); err != nil { + return err + } + + if coin.IsNegative() { + return fmt.Errorf("coin %s amount is cannot be negative", coin.Denom) + } + + seenDenoms[coin.Denom] = true + } + + // sort the coins post validation + b.Coins = b.Coins.Sort() + + return nil +} + +// SanitizeGenesisBalances sorts addresses and coin sets. +func SanitizeGenesisBalances(balances []Balance) []Balance { + sort.Slice(balances, func(i, j int) bool { + addr1, _ := sdk.AccAddressFromBech32(balances[i].Address) + addr2, _ := sdk.AccAddressFromBech32(balances[j].Address) + return bytes.Compare(addr1.Bytes(), addr2.Bytes()) < 0 + }) + + for _, balance := range balances { + balance.Coins = balance.Coins.Sort() + } + + return balances +} + +// GenesisBalancesIterator implements genesis account iteration. +type GenesisBalancesIterator struct{} + +// IterateGenesisBalances iterates over all the genesis balances found in +// appGenesis and invokes a callback on each genesis account. If any call +// returns true, iteration stops. +func (GenesisBalancesIterator) IterateGenesisBalances( + cdc codec.JSONMarshaler, appState map[string]json.RawMessage, cb func(exported.GenesisBalance) (stop bool), +) { + for _, balance := range GetGenesisStateFromAppState(cdc, appState).Balances { + if cb(balance) { + break + } + } +} diff --git a/x/bank/types/balance_test.go b/x/bank/types/balance_test.go new file mode 100644 index 000000000000..6282b2478b19 --- /dev/null +++ b/x/bank/types/balance_test.go @@ -0,0 +1,103 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func TestBalanceValidate(t *testing.T) { + testCases := []struct { + name string + balance bank.Balance + expErr bool + }{ + { + "valid balance", + bank.Balance{ + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{sdk.NewInt64Coin("uatom", 1)}, + }, + false, + }, + {"empty balance", bank.Balance{}, true}, + { + "nil balance coins", + bank.Balance{ + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + }, + true, + }, + { + "dup coins", + bank.Balance{ + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{ + sdk.NewInt64Coin("uatom", 1), + sdk.NewInt64Coin("uatom", 1), + }, + }, + true, + }, + { + "invalid coin denom", + bank.Balance{ + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{ + sdk.Coin{Denom: "", Amount: sdk.OneInt()}, + }, + }, + true, + }, + { + "negative coin", + bank.Balance{ + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{ + sdk.Coin{Denom: "uatom", Amount: sdk.NewInt(-1)}, + }, + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + + err := tc.balance.Validate() + + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestBalance_GetAddress(t *testing.T) { + tests := []struct { + name string + Address string + wantPanic bool + }{ + {"empty address", "", true}, + {"malformed address", "invalid", true}, + {"valid address", "cosmos1vy0ga0klndqy92ceqehfkvgmn4t94eteq4hmqv", false}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + b := bank.Balance{Address: tt.Address} + if tt.wantPanic { + require.Panics(t, func() { b.GetAddress() }) + } else { + require.False(t, b.GetAddress().Empty()) + } + }) + } +} diff --git a/x/bank/types/bank.pb.go b/x/bank/types/bank.pb.go index ee27ea2cab57..ef1c52c1ccb5 100644 --- a/x/bank/types/bank.pb.go +++ b/x/bank/types/bank.pb.go @@ -1054,10 +1054,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthBank - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthBank } if (iNdEx + skippy) > l { @@ -1159,10 +1156,7 @@ func (m *SendEnabled) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthBank - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthBank } if (iNdEx + skippy) > l { @@ -1278,10 +1272,7 @@ func (m *Input) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthBank - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthBank } if (iNdEx + skippy) > l { @@ -1397,10 +1388,7 @@ func (m *Output) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthBank - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthBank } if (iNdEx + skippy) > l { @@ -1484,10 +1472,7 @@ func (m *Supply) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthBank - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthBank } if (iNdEx + skippy) > l { @@ -1620,10 +1605,7 @@ func (m *DenomUnit) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthBank - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthBank } if (iNdEx + skippy) > l { @@ -1803,10 +1785,7 @@ func (m *Metadata) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthBank - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthBank } if (iNdEx + skippy) > l { diff --git a/x/bank/types/errors.go b/x/bank/types/errors.go index dce69d0ac6bf..ab88b40e9b1d 100644 --- a/x/bank/types/errors.go +++ b/x/bank/types/errors.go @@ -6,8 +6,9 @@ import ( // x/bank module sentinel errors var ( - ErrNoInputs = sdkerrors.Register(ModuleName, 2, "no inputs to send transaction") - ErrNoOutputs = sdkerrors.Register(ModuleName, 3, "no outputs to send transaction") - ErrInputOutputMismatch = sdkerrors.Register(ModuleName, 4, "sum inputs != sum outputs") - ErrSendDisabled = sdkerrors.Register(ModuleName, 5, "send transactions are disabled") + ErrNoInputs = sdkerrors.Register(ModuleName, 2, "no inputs to send transaction") + ErrNoOutputs = sdkerrors.Register(ModuleName, 3, "no outputs to send transaction") + ErrInputOutputMismatch = sdkerrors.Register(ModuleName, 4, "sum inputs != sum outputs") + ErrSendDisabled = sdkerrors.Register(ModuleName, 5, "send transactions are disabled") + ErrDenomMetadataNotFound = sdkerrors.Register(ModuleName, 6, "client denom metadata not found") ) diff --git a/x/bank/types/genesis.go b/x/bank/types/genesis.go index ccf35be86231..4adc758f3bd9 100644 --- a/x/bank/types/genesis.go +++ b/x/bank/types/genesis.go @@ -1,51 +1,49 @@ package types import ( - "bytes" "encoding/json" - "sort" + "fmt" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/exported" ) -var _ exported.GenesisBalance = (*Balance)(nil) +// Validate performs basic validation of supply genesis data returning an +// error for any failed validation criteria. +func (gs GenesisState) Validate() error { + if err := gs.Params.Validate(); err != nil { + return err + } -// GetAddress returns the account address of the Balance object. -func (b Balance) GetAddress() sdk.AccAddress { - addr1, _ := sdk.AccAddressFromBech32(b.Address) - return addr1 -} + seenBalances := make(map[string]bool) + seenMetadatas := make(map[string]bool) -// GetAddress returns the account coins of the Balance object. -func (b Balance) GetCoins() sdk.Coins { - return b.Coins -} + for _, balance := range gs.Balances { + if seenBalances[balance.Address] { + return fmt.Errorf("duplicate balance for address %s", balance.Address) + } -// SanitizeGenesisAccounts sorts addresses and coin sets. -func SanitizeGenesisBalances(balances []Balance) []Balance { - sort.Slice(balances, func(i, j int) bool { - addr1, _ := sdk.AccAddressFromBech32(balances[i].Address) - addr2, _ := sdk.AccAddressFromBech32(balances[j].Address) - return bytes.Compare(addr1.Bytes(), addr2.Bytes()) < 0 - }) + if err := balance.Validate(); err != nil { + return err + } - for _, balance := range balances { - balance.Coins = balance.Coins.Sort() + seenBalances[balance.Address] = true } - return balances -} + for _, metadata := range gs.DenomMetadata { + if seenMetadatas[metadata.Base] { + return fmt.Errorf("duplicate client metadata for denom %s", metadata.Base) + } -// ValidateGenesis performs basic validation of supply genesis data returning an -// error for any failed validation criteria. -func ValidateGenesis(data GenesisState) error { - if err := data.Params.Validate(); err != nil { - return err + if err := metadata.Validate(); err != nil { + return err + } + + seenMetadatas[metadata.Base] = true } - return NewSupply(data.Supply).ValidateBasic() + // NOTE: this errors if supply for any given coin is zero + return NewSupply(gs.Supply).ValidateBasic() } // NewGenesisState creates a new genesis state. @@ -74,19 +72,3 @@ func GetGenesisStateFromAppState(cdc codec.JSONMarshaler, appState map[string]js return &genesisState } - -// GenesisAccountIterator implements genesis account iteration. -type GenesisBalancesIterator struct{} - -// IterateGenesisAccounts iterates over all the genesis accounts found in -// appGenesis and invokes a callback on each genesis account. If any call -// returns true, iteration stops. -func (GenesisBalancesIterator) IterateGenesisBalances( - cdc codec.JSONMarshaler, appState map[string]json.RawMessage, cb func(exported.GenesisBalance) (stop bool), -) { - for _, balance := range GetGenesisStateFromAppState(cdc, appState).Balances { - if cb(balance) { - break - } - } -} diff --git a/x/bank/types/genesis.pb.go b/x/bank/types/genesis.pb.go index a4b6f8b185ec..f1d124bdd4e6 100644 --- a/x/bank/types/genesis.pb.go +++ b/x/bank/types/genesis.pb.go @@ -529,10 +529,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -648,10 +645,7 @@ func (m *Balance) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/bank/types/genesis_test.go b/x/bank/types/genesis_test.go index 5f612aed726b..634b247e0190 100644 --- a/x/bank/types/genesis_test.go +++ b/x/bank/types/genesis_test.go @@ -1,52 +1,150 @@ -package types_test +package types import ( "testing" "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/bank/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestMarshalJSONMetaData(t *testing.T) { - cdc := codec.NewLegacyAmino() +func TestGenesisStateValidate(t *testing.T) { testCases := []struct { - name string - input []types.Metadata - strOutput string + name string + genesisState GenesisState + expErr bool }{ - {"nil metadata", nil, `null`}, - {"empty metadata", []types.Metadata{}, `[]`}, - {"non-empty coins", []types.Metadata{{ - Description: "The native staking token of the Cosmos Hub.", - DenomUnits: []*types.DenomUnit{ - {"uatom", uint32(0), []string{"microatom"}}, // The default exponent value 0 is omitted in the json - {"matom", uint32(3), []string{"milliatom"}}, - {"atom", uint32(6), nil}, + { + "valid genesisState", + GenesisState{ + Params: DefaultParams(), + Balances: []Balance{ + { + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{sdk.NewInt64Coin("uatom", 1)}, + }, + }, + Supply: sdk.Coins{sdk.NewInt64Coin("uatom", 1)}, + DenomMetadata: []Metadata{ + { + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + {"matom", uint32(3), []string{"milliatom"}}, + {"atom", uint32(6), nil}, + }, + Base: "uatom", + Display: "atom", + }, + }, }, - Base: "uatom", - Display: "atom", + false, }, + {"empty genesisState", GenesisState{}, false}, + { + "invalid params ", + GenesisState{ + Params: Params{ + SendEnabled: []*SendEnabled{ + {"", true}, + }, + }, + }, + true, + }, + { + "dup balances", + GenesisState{ + Balances: []Balance{ + { + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{sdk.NewInt64Coin("uatom", 1)}, + }, + { + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{sdk.NewInt64Coin("uatom", 1)}, + }, + }, + }, + true, + }, + { + "invalid balance", + GenesisState{ + Balances: []Balance{ + { + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + }, + }, + }, + true, + }, + { + "dup Metadata", + GenesisState{ + DenomMetadata: []Metadata{ + { + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + {"matom", uint32(3), []string{"milliatom"}}, + {"atom", uint32(6), nil}, + }, + Base: "uatom", + Display: "atom", + }, + { + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + {"matom", uint32(3), []string{"milliatom"}}, + {"atom", uint32(6), nil}, + }, + Base: "uatom", + Display: "atom", + }, + }, + }, + true, + }, + { + "invalid Metadata", + GenesisState{ + DenomMetadata: []Metadata{ + { + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + {"matom", uint32(3), []string{"milliatom"}}, + {"atom", uint32(6), nil}, + }, + Base: "", + Display: "", + }, + }, + }, + true, + }, + { + "invalid supply", + GenesisState{ + Supply: sdk.Coins{sdk.Coin{Denom: "", Amount: sdk.OneInt()}}, + }, + true, }, - `[{"description":"The native staking token of the Cosmos Hub.","denom_units":[{"denom":"uatom","aliases":["microatom"]},{"denom":"matom","exponent":3,"aliases":["milliatom"]},{"denom":"atom","exponent":6}],"base":"uatom","display":"atom"}]`}, } for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { - bz, err := cdc.MarshalJSON(tc.input) - require.NoError(t, err) - require.Equal(t, tc.strOutput, string(bz)) - var newMetadata []types.Metadata - require.NoError(t, cdc.UnmarshalJSON(bz, &newMetadata)) + err := tc.genesisState.Validate() - if len(tc.input) == 0 { - require.Nil(t, newMetadata) + if tc.expErr { + require.Error(t, err) } else { - require.Equal(t, tc.input, newMetadata) + require.NoError(t, err) } }) } diff --git a/x/bank/types/key.go b/x/bank/types/key.go index ec8619d00185..858fd2480ac9 100644 --- a/x/bank/types/key.go +++ b/x/bank/types/key.go @@ -1,9 +1,8 @@ package types import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" ) const ( @@ -22,7 +21,9 @@ const ( // KVStore keys var ( - BalancesPrefix = []byte("balances") + // 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} ) @@ -37,10 +38,13 @@ func DenomMetadataKey(denom string) []byte { // store. The key must not contain the perfix BalancesPrefix as the prefix store // iterator discards the actual prefix. func AddressFromBalancesStore(key []byte) sdk.AccAddress { - addr := key[:sdk.AddrLen] - if len(addr) != sdk.AddrLen { - panic(fmt.Sprintf("unexpected account address key length; got: %d, expected: %d", len(addr), sdk.AddrLen)) - } + 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/types/key_test.go b/x/bank/types/key_test.go index f3b5717fbecb..206ef8335c91 100644 --- a/x/bank/types/key_test.go +++ b/x/bank/types/key_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -19,8 +20,10 @@ func cloneAppend(bz []byte, tail []byte) (res []byte) { func TestAddressFromBalancesStore(t *testing.T) { addr, err := sdk.AccAddressFromBech32("cosmos1n88uc38xhjgxzw9nwre4ep2c8ga4fjxcar6mn7") require.NoError(t, err) + addrLen := len(addr) + require.Equal(t, 20, addrLen) - key := cloneAppend(addr.Bytes(), []byte("stake")) + key := cloneAppend(address.MustLengthPrefix(addr), []byte("stake")) res := types.AddressFromBalancesStore(key) require.Equal(t, res, addr) } diff --git a/x/bank/types/metadata.go b/x/bank/types/metadata.go new file mode 100644 index 000000000000..ce7794053d93 --- /dev/null +++ b/x/bank/types/metadata.go @@ -0,0 +1,91 @@ +package types + +import ( + "errors" + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Validate performs a basic validation of the coin metadata fields. It checks: +// - 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 err := sdk.ValidateDenom(m.Base); err != nil { + return fmt.Errorf("invalid metadata base denom: %w", err) + } + + if err := sdk.ValidateDenom(m.Display); err != nil { + return fmt.Errorf("invalid metadata display denom: %w", err) + } + + var ( + hasDisplay bool + currentExponent uint32 // check that the exponents are increasing + ) + + seenUnits := make(map[string]bool) + + for i, denomUnit := range m.DenomUnits { + // The first denomination unit MUST be the base + if i == 0 { + // validate denomination and exponent + if denomUnit.Denom != m.Base { + return fmt.Errorf("metadata's first denomination unit must be the one with base denom '%s'", m.Base) + } + if denomUnit.Exponent != 0 { + return fmt.Errorf("the exponent for base denomination unit %s must be 0", m.Base) + } + } else if currentExponent >= denomUnit.Exponent { + return errors.New("denom units should be sorted asc by exponent") + } + + currentExponent = denomUnit.Exponent + + if seenUnits[denomUnit.Denom] { + return fmt.Errorf("duplicate denomination unit %s", denomUnit.Denom) + } + + if denomUnit.Denom == m.Display { + hasDisplay = true + } + + if err := denomUnit.Validate(); err != nil { + return err + } + + seenUnits[denomUnit.Denom] = true + } + + if !hasDisplay { + return fmt.Errorf("metadata must contain a denomination unit with display denom '%s'", m.Display) + } + + return nil +} + +// Validate performs a basic validation of the denomination unit fields +func (du DenomUnit) Validate() error { + if err := sdk.ValidateDenom(du.Denom); err != nil { + return fmt.Errorf("invalid denom unit: %w", err) + } + + seenAliases := make(map[string]bool) + for _, alias := range du.Aliases { + if seenAliases[alias] { + return fmt.Errorf("duplicate denomination unit alias %s", alias) + } + + if strings.TrimSpace(alias) == "" { + return fmt.Errorf("alias for denom unit %s cannot be blank", du.Denom) + } + + seenAliases[alias] = true + } + + return nil +} diff --git a/x/bank/types/metadata_test.go b/x/bank/types/metadata_test.go new file mode 100644 index 000000000000..8ec2fe52ec02 --- /dev/null +++ b/x/bank/types/metadata_test.go @@ -0,0 +1,220 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func TestMetadataValidate(t *testing.T) { + testCases := []struct { + name string + metadata types.Metadata + expErr bool + }{ + { + "non-empty coins", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + {"matom", uint32(3), []string{"milliatom"}}, + {"atom", uint32(6), nil}, + }, + Base: "uatom", + Display: "atom", + }, + false, + }, + { + "base coin is display coin", + types.Metadata{ + 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}, + { + "invalid base denom", + types.Metadata{ + Base: "", + }, + true, + }, + { + "invalid display denom", + types.Metadata{ + Base: "uatom", + Display: "", + }, + true, + }, + { + "duplicate denom unit", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + {"uatom", uint32(1), []string{"microatom"}}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, + { + "invalid denom unit", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"", uint32(0), []string{"microatom"}}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, + { + "invalid denom unit alias", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(0), []string{""}}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, + { + "duplicate denom unit alias", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(0), []string{"microatom", "microatom"}}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, + { + "no base denom unit", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"matom", uint32(3), []string{"milliatom"}}, + {"atom", uint32(6), nil}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, + { + "base denom exponent not zero", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(1), []string{"microatom"}}, + {"matom", uint32(3), []string{"milliatom"}}, + {"atom", uint32(6), nil}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, + { + "no display denom unit", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, + { + "denom units not sorted", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + {"atom", uint32(6), nil}, + {"matom", uint32(3), []string{"milliatom"}}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + + err := tc.metadata.Validate() + + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMarshalJSONMetaData(t *testing.T) { + cdc := codec.NewLegacyAmino() + + testCases := []struct { + name string + input []types.Metadata + strOutput string + }{ + {"nil metadata", nil, `null`}, + {"empty metadata", []types.Metadata{}, `[]`}, + {"non-empty coins", []types.Metadata{{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, // The default exponent value 0 is omitted in the json + {"matom", uint32(3), []string{"milliatom"}}, + {"atom", uint32(6), nil}, + }, + Base: "uatom", + Display: "atom", + }, + }, + `[{"description":"The native staking token of the Cosmos Hub.","denom_units":[{"denom":"uatom","aliases":["microatom"]},{"denom":"matom","exponent":3,"aliases":["milliatom"]},{"denom":"atom","exponent":6}],"base":"uatom","display":"atom"}]`}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + bz, err := cdc.MarshalJSON(tc.input) + require.NoError(t, err) + require.Equal(t, tc.strOutput, string(bz)) + + var newMetadata []types.Metadata + require.NoError(t, cdc.UnmarshalJSON(bz, &newMetadata)) + + if len(tc.input) == 0 { + require.Nil(t, newMetadata) + } else { + require.Equal(t, tc.input, newMetadata) + } + }) + } +} diff --git a/x/bank/types/msgs.go b/x/bank/types/msgs.go index 22fdc30b3886..dd41975468ca 100644 --- a/x/bank/types/msgs.go +++ b/x/bank/types/msgs.go @@ -1,6 +1,13 @@ package types import ( + "fmt" + "strconv" + "strings" + + "github.com/coinbase/rosetta-sdk-go/types" + "github.com/gogo/protobuf/proto" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -62,6 +69,83 @@ func (msg MsgSend) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{from} } +// Rosetta interface +func (msg *MsgSend) ToOperations(withStatus bool, hasError bool) []*types.Operation { + var operations []*types.Operation + + fromAddress := msg.FromAddress + toAddress := msg.ToAddress + amounts := msg.Amount + if len(amounts) == 0 { + return []*types.Operation{} + } + + coin := amounts[0] + sendOp := func(account, amount string, index int) *types.Operation { + var status string + if withStatus { + status = "Success" + if hasError { + status = "Reverted" + } + } + return &types.Operation{ + OperationIdentifier: &types.OperationIdentifier{ + Index: int64(index), + }, + Type: proto.MessageName(msg), + Status: status, + Account: &types.AccountIdentifier{ + Address: account, + }, + Amount: &types.Amount{ + Value: amount, + Currency: &types.Currency{ + Symbol: coin.Denom, + }, + }, + } + } + operations = append(operations, + sendOp(fromAddress, "-"+coin.Amount.String(), 0), + sendOp(toAddress, coin.Amount.String(), 1), + ) + + return operations +} + +func (msg MsgSend) FromOperations(ops []*types.Operation) (sdk.Msg, error) { + var ( + from, to sdk.AccAddress + sendAmt sdk.Coin + err error + ) + + for _, op := range ops { + if strings.HasPrefix(op.Amount.Value, "-") { + from, err = sdk.AccAddressFromBech32(op.Account.Address) + if err != nil { + return nil, err + } + continue + } + + to, err = sdk.AccAddressFromBech32(op.Account.Address) + if err != nil { + return nil, err + } + + amount, err := strconv.ParseInt(op.Amount.Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid amount") + } + + sendAmt = sdk.NewCoin(op.Amount.Currency.Symbol, sdk.NewInt(amount)) + } + + return NewMsgSend(from, to, sdk.NewCoins(sendAmt)), nil +} + var _ sdk.Msg = &MsgMultiSend{} // NewMsgMultiSend - construct arbitrary multi-in, multi-out send msg. diff --git a/x/bank/types/msgs_test.go b/x/bank/types/msgs_test.go index 96132150ad4f..334d72c13243 100644 --- a/x/bank/types/msgs_test.go +++ b/x/bank/types/msgs_test.go @@ -23,7 +23,7 @@ func TestMsgSendValidation(t *testing.T) { addr1 := sdk.AccAddress([]byte("from________________")) addr2 := sdk.AccAddress([]byte("to__________________")) addrEmpty := sdk.AccAddress([]byte("")) - addrTooLong := sdk.AccAddress([]byte("Accidentally used 33 bytes pubkey")) + addrLong := sdk.AccAddress([]byte("Purposefully long address")) atom123 := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) atom0 := sdk.NewCoins(sdk.NewInt64Coin("atom", 0)) @@ -36,12 +36,12 @@ func TestMsgSendValidation(t *testing.T) { }{ {"", NewMsgSend(addr1, addr2, atom123)}, // valid send {"", NewMsgSend(addr1, addr2, atom123eth123)}, // valid send with multiple coins + {"", NewMsgSend(addrLong, addr2, atom123)}, // valid send with long addr sender + {"", NewMsgSend(addr1, addrLong, atom123)}, // valid send with long addr recipient {": invalid coins", NewMsgSend(addr1, addr2, atom0)}, // non positive coin {"123atom,0eth: invalid coins", NewMsgSend(addr1, addr2, atom123eth0)}, // non positive coin in multicoins {"Invalid sender address (empty address string is not allowed): invalid address", NewMsgSend(addrEmpty, addr2, atom123)}, - {"Invalid sender address (incorrect address length (expected: 20, actual: 33)): invalid address", NewMsgSend(addrTooLong, addr2, atom123)}, {"Invalid recipient address (empty address string is not allowed): invalid address", NewMsgSend(addr1, addrEmpty, atom123)}, - {"Invalid recipient address (incorrect address length (expected: 20, actual: 33)): invalid address", NewMsgSend(addr1, addrTooLong, atom123)}, } for _, tc := range cases { @@ -91,7 +91,7 @@ func TestInputValidation(t *testing.T) { addr1 := sdk.AccAddress([]byte("_______alice________")) addr2 := sdk.AccAddress([]byte("________bob_________")) addrEmpty := sdk.AccAddress([]byte("")) - addrTooLong := sdk.AccAddress([]byte("Accidentally used 33 bytes pubkey")) + addrLong := sdk.AccAddress([]byte("Purposefully long address")) someCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) multiCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 20)) @@ -109,9 +109,9 @@ func TestInputValidation(t *testing.T) { {"", NewInput(addr1, someCoins)}, {"", NewInput(addr2, someCoins)}, {"", NewInput(addr2, multiCoins)}, + {"", NewInput(addrLong, someCoins)}, {"empty address string is not allowed", NewInput(addrEmpty, someCoins)}, - {"incorrect address length (expected: 20, actual: 33)", NewInput(addrTooLong, someCoins)}, {": invalid coins", NewInput(addr1, emptyCoins)}, // invalid coins {": invalid coins", NewInput(addr1, emptyCoins2)}, // invalid coins {"10eth,0atom: invalid coins", NewInput(addr1, someEmptyCoins)}, // invalid coins @@ -132,7 +132,7 @@ func TestOutputValidation(t *testing.T) { addr1 := sdk.AccAddress([]byte("_______alice________")) addr2 := sdk.AccAddress([]byte("________bob_________")) addrEmpty := sdk.AccAddress([]byte("")) - addrTooLong := sdk.AccAddress([]byte("Accidentally used 33 bytes pubkey")) + addrLong := sdk.AccAddress([]byte("Purposefully long address")) someCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) multiCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 20)) @@ -150,9 +150,9 @@ func TestOutputValidation(t *testing.T) { {"", NewOutput(addr1, someCoins)}, {"", NewOutput(addr2, someCoins)}, {"", NewOutput(addr2, multiCoins)}, + {"", NewOutput(addrLong, someCoins)}, {"Invalid output address (empty address string is not allowed): invalid address", NewOutput(addrEmpty, someCoins)}, - {"Invalid output address (incorrect address length (expected: 20, actual: 33)): invalid address", NewOutput(addrTooLong, someCoins)}, {": invalid coins", NewOutput(addr1, emptyCoins)}, // invalid coins {": invalid coins", NewOutput(addr1, emptyCoins2)}, // invalid coins {"10eth,0atom: invalid coins", NewOutput(addr1, someEmptyCoins)}, // invalid coins @@ -251,8 +251,6 @@ func TestMsgMultiSendGetSigners(t *testing.T) { require.Equal(t, "[696E707574313131313131313131313131313131 696E707574323232323232323232323232323232 696E707574333333333333333333333333333333]", fmt.Sprintf("%v", res)) } -/* -// what to do w/ this test? func TestMsgSendSigners(t *testing.T) { signers := []sdk.AccAddress{ {1, 2, 3}, @@ -265,8 +263,7 @@ func TestMsgSendSigners(t *testing.T) { for i, signer := range signers { inputs[i] = NewInput(signer, someCoins) } - tx := NewMsgSend(inputs, nil) + tx := NewMsgMultiSend(inputs, nil) - require.Equal(t, signers, tx.Signers()) + require.Equal(t, signers, tx.GetSigners()) } -*/ diff --git a/x/bank/types/query.pb.go b/x/bank/types/query.pb.go index 0af6dac36dec..c4bedb0098c5 100644 --- a/x/bank/types/query.pb.go +++ b/x/bank/types/query.pb.go @@ -475,6 +475,201 @@ func (m *QueryParamsResponse) GetParams() Params { return Params{} } +// QueryDenomsMetadataRequest is the request type for the Query/DenomsMetadata RPC method. +type QueryDenomsMetadataRequest struct { + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDenomsMetadataRequest) Reset() { *m = QueryDenomsMetadataRequest{} } +func (m *QueryDenomsMetadataRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDenomsMetadataRequest) ProtoMessage() {} +func (*QueryDenomsMetadataRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{10} +} +func (m *QueryDenomsMetadataRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDenomsMetadataRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDenomsMetadataRequest.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 *QueryDenomsMetadataRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDenomsMetadataRequest.Merge(m, src) +} +func (m *QueryDenomsMetadataRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDenomsMetadataRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDenomsMetadataRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDenomsMetadataRequest proto.InternalMessageInfo + +func (m *QueryDenomsMetadataRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryDenomsMetadataResponse is the response type for the Query/DenomsMetadata RPC +// method. +type QueryDenomsMetadataResponse struct { + // metadata provides the client information for all the registered tokens. + Metadatas []Metadata `protobuf:"bytes,1,rep,name=metadatas,proto3" json:"metadatas"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDenomsMetadataResponse) Reset() { *m = QueryDenomsMetadataResponse{} } +func (m *QueryDenomsMetadataResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDenomsMetadataResponse) ProtoMessage() {} +func (*QueryDenomsMetadataResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{11} +} +func (m *QueryDenomsMetadataResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDenomsMetadataResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDenomsMetadataResponse.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 *QueryDenomsMetadataResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDenomsMetadataResponse.Merge(m, src) +} +func (m *QueryDenomsMetadataResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDenomsMetadataResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDenomsMetadataResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDenomsMetadataResponse proto.InternalMessageInfo + +func (m *QueryDenomsMetadataResponse) GetMetadatas() []Metadata { + if m != nil { + return m.Metadatas + } + return nil +} + +func (m *QueryDenomsMetadataResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryDenomMetadataRequest is the request type for the Query/DenomMetadata RPC method. +type QueryDenomMetadataRequest struct { + // denom is the coin denom to query the metadata for. + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` +} + +func (m *QueryDenomMetadataRequest) Reset() { *m = QueryDenomMetadataRequest{} } +func (m *QueryDenomMetadataRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDenomMetadataRequest) ProtoMessage() {} +func (*QueryDenomMetadataRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{12} +} +func (m *QueryDenomMetadataRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDenomMetadataRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDenomMetadataRequest.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 *QueryDenomMetadataRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDenomMetadataRequest.Merge(m, src) +} +func (m *QueryDenomMetadataRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDenomMetadataRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDenomMetadataRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDenomMetadataRequest proto.InternalMessageInfo + +func (m *QueryDenomMetadataRequest) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + +// QueryDenomMetadataResponse is the response type for the Query/DenomMetadata RPC +// method. +type QueryDenomMetadataResponse struct { + // metadata describes and provides all the client information for the requested token. + Metadata Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata"` +} + +func (m *QueryDenomMetadataResponse) Reset() { *m = QueryDenomMetadataResponse{} } +func (m *QueryDenomMetadataResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDenomMetadataResponse) ProtoMessage() {} +func (*QueryDenomMetadataResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{13} +} +func (m *QueryDenomMetadataResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDenomMetadataResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDenomMetadataResponse.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 *QueryDenomMetadataResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDenomMetadataResponse.Merge(m, src) +} +func (m *QueryDenomMetadataResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDenomMetadataResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDenomMetadataResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDenomMetadataResponse proto.InternalMessageInfo + +func (m *QueryDenomMetadataResponse) GetMetadata() Metadata { + if m != nil { + return m.Metadata + } + return Metadata{} +} + func init() { proto.RegisterType((*QueryBalanceRequest)(nil), "cosmos.bank.v1beta1.QueryBalanceRequest") proto.RegisterType((*QueryBalanceResponse)(nil), "cosmos.bank.v1beta1.QueryBalanceResponse") @@ -486,55 +681,68 @@ func init() { proto.RegisterType((*QuerySupplyOfResponse)(nil), "cosmos.bank.v1beta1.QuerySupplyOfResponse") proto.RegisterType((*QueryParamsRequest)(nil), "cosmos.bank.v1beta1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "cosmos.bank.v1beta1.QueryParamsResponse") + proto.RegisterType((*QueryDenomsMetadataRequest)(nil), "cosmos.bank.v1beta1.QueryDenomsMetadataRequest") + proto.RegisterType((*QueryDenomsMetadataResponse)(nil), "cosmos.bank.v1beta1.QueryDenomsMetadataResponse") + proto.RegisterType((*QueryDenomMetadataRequest)(nil), "cosmos.bank.v1beta1.QueryDenomMetadataRequest") + proto.RegisterType((*QueryDenomMetadataResponse)(nil), "cosmos.bank.v1beta1.QueryDenomMetadataResponse") } func init() { proto.RegisterFile("cosmos/bank/v1beta1/query.proto", fileDescriptor_9c6fc1939682df13) } var fileDescriptor_9c6fc1939682df13 = []byte{ - // 675 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x31, 0x6f, 0xd3, 0x40, - 0x14, 0xf6, 0x15, 0x9a, 0x96, 0xcb, 0x76, 0x0d, 0x22, 0x75, 0xc1, 0x41, 0xae, 0xa0, 0x49, 0x69, - 0x7d, 0x24, 0x1d, 0x2a, 0xd8, 0x48, 0x25, 0x18, 0x18, 0x1a, 0x0c, 0x13, 0xdb, 0x25, 0x39, 0x8c, - 0x55, 0xc7, 0xe7, 0xe6, 0x1c, 0x44, 0x54, 0x55, 0x20, 0x24, 0x24, 0x26, 0x40, 0x62, 0x60, 0x60, - 0xe9, 0xcc, 0x2f, 0xe9, 0xc0, 0x50, 0x89, 0x85, 0x09, 0x50, 0x02, 0x12, 0x3f, 0x03, 0xe5, 0xee, - 0x6c, 0x9c, 0xc6, 0x75, 0xb2, 0x30, 0xc5, 0xf7, 0xee, 0xbd, 0xef, 0x7d, 0xdf, 0xbb, 0xef, 0x2e, - 0xb0, 0xd4, 0x62, 0xbc, 0xc3, 0x38, 0x6e, 0x12, 0x7f, 0x0f, 0x3f, 0xab, 0x36, 0x69, 0x48, 0xaa, - 0x78, 0xbf, 0x47, 0xbb, 0x7d, 0x2b, 0xe8, 0xb2, 0x90, 0xa1, 0x25, 0x99, 0x60, 0x8d, 0x12, 0x2c, - 0x95, 0xa0, 0xaf, 0xc7, 0x55, 0x9c, 0xca, 0xec, 0xb8, 0x36, 0x20, 0x8e, 0xeb, 0x93, 0xd0, 0x65, - 0xbe, 0x04, 0xd0, 0x0b, 0x0e, 0x73, 0x98, 0xf8, 0xc4, 0xa3, 0x2f, 0x15, 0xbd, 0xec, 0x30, 0xe6, - 0x78, 0x14, 0x93, 0xc0, 0xc5, 0xc4, 0xf7, 0x59, 0x28, 0x4a, 0xb8, 0xda, 0x35, 0x92, 0xf8, 0x11, - 0x72, 0x8b, 0xb9, 0xfe, 0xc4, 0x7e, 0x82, 0xb5, 0x60, 0x28, 0xf6, 0xcd, 0x5d, 0xb8, 0xf4, 0x60, - 0xc4, 0xaa, 0x4e, 0x3c, 0xe2, 0xb7, 0xa8, 0x4d, 0xf7, 0x7b, 0x94, 0x87, 0xa8, 0x08, 0x17, 0x48, - 0xbb, 0xdd, 0xa5, 0x9c, 0x17, 0xc1, 0x55, 0x50, 0xbe, 0x60, 0x47, 0x4b, 0x54, 0x80, 0xf3, 0x6d, - 0xea, 0xb3, 0x4e, 0x71, 0x4e, 0xc4, 0xe5, 0xe2, 0xf6, 0xe2, 0x9b, 0xa3, 0x92, 0xf6, 0xe7, 0xa8, - 0xa4, 0x99, 0xf7, 0x61, 0x61, 0x1c, 0x90, 0x07, 0xcc, 0xe7, 0x14, 0x6d, 0xc1, 0x85, 0xa6, 0x0c, - 0x09, 0xc4, 0x7c, 0x6d, 0xd9, 0x8a, 0xe7, 0xc5, 0x69, 0x34, 0x2f, 0x6b, 0x87, 0xb9, 0xbe, 0x1d, - 0x65, 0x9a, 0xaf, 0x01, 0xbc, 0x24, 0xd0, 0xee, 0x78, 0x9e, 0x02, 0xe4, 0xd3, 0x29, 0xde, 0x85, - 0xf0, 0xdf, 0x6c, 0x05, 0xcf, 0x7c, 0xed, 0xfa, 0x58, 0x37, 0x79, 0x6c, 0x51, 0xcf, 0x06, 0x71, - 0x22, 0xe1, 0x76, 0xa2, 0x32, 0x21, 0xea, 0x0b, 0x80, 0xc5, 0x49, 0x1e, 0x4a, 0x99, 0x03, 0x17, - 0x15, 0xdf, 0x11, 0x93, 0x73, 0x99, 0xd2, 0xea, 0x37, 0x8f, 0xbf, 0x97, 0xb4, 0xcf, 0x3f, 0x4a, - 0x65, 0xc7, 0x0d, 0x9f, 0xf6, 0x9a, 0x56, 0x8b, 0x75, 0xb0, 0x3a, 0x22, 0xf9, 0xb3, 0xc9, 0xdb, - 0x7b, 0x38, 0xec, 0x07, 0x94, 0x8b, 0x02, 0x6e, 0xc7, 0xe0, 0xe8, 0x5e, 0x8a, 0xae, 0xb5, 0xa9, - 0xba, 0x24, 0xcb, 0xa4, 0x30, 0x73, 0x59, 0x4d, 0xf5, 0x11, 0x0b, 0x89, 0xf7, 0xb0, 0x17, 0x04, - 0x5e, 0x5f, 0xe9, 0x37, 0x5f, 0x28, 0xa1, 0x63, 0x5b, 0x4a, 0x68, 0x0b, 0xe6, 0xb8, 0x88, 0xfc, - 0x0f, 0x99, 0x0a, 0xda, 0xdc, 0x50, 0xfe, 0x91, 0xbd, 0x77, 0x9f, 0x44, 0xc7, 0x1d, 0xfb, 0x0e, - 0x24, 0x7c, 0x67, 0x36, 0xe0, 0xc5, 0x53, 0xd9, 0x8a, 0xeb, 0x36, 0xcc, 0x91, 0x0e, 0xeb, 0xf9, - 0xe1, 0x54, 0xb7, 0xd5, 0xcf, 0x8f, 0xb8, 0xda, 0x2a, 0xdd, 0x2c, 0x40, 0x24, 0x10, 0x1b, 0xa4, - 0x4b, 0x3a, 0x91, 0xd9, 0xcc, 0x86, 0xba, 0x26, 0x51, 0x54, 0x75, 0xb9, 0x05, 0x73, 0x81, 0x88, - 0xa8, 0x2e, 0x2b, 0x56, 0xca, 0x1b, 0x60, 0xc9, 0xa2, 0xa8, 0x8f, 0x2c, 0xa8, 0xfd, 0x9e, 0x87, - 0xf3, 0x02, 0x12, 0x7d, 0x04, 0x70, 0x41, 0x99, 0x0a, 0x95, 0x53, 0x01, 0x52, 0x6e, 0xa8, 0x5e, - 0x99, 0x21, 0x53, 0xb2, 0x34, 0xb7, 0x5f, 0x7d, 0xfd, 0xf5, 0x61, 0xae, 0x8a, 0x30, 0x4e, 0x7f, - 0x0c, 0xa4, 0xbd, 0xf0, 0x81, 0xba, 0x3f, 0x87, 0xf8, 0x40, 0x0c, 0xf7, 0x10, 0x7d, 0x02, 0x30, - 0x9f, 0x70, 0x3c, 0xda, 0x38, 0xbb, 0xe7, 0xe4, 0x05, 0xd5, 0x37, 0x67, 0xcc, 0x56, 0x2c, 0xb1, - 0x60, 0x59, 0x41, 0x6b, 0x33, 0xb2, 0x44, 0xef, 0x00, 0xcc, 0x27, 0x6c, 0x9a, 0xc5, 0x6e, 0xd2, - 0xe8, 0x59, 0xec, 0x52, 0xbc, 0x6f, 0xae, 0x0a, 0x76, 0x57, 0xd0, 0x4a, 0x2a, 0x3b, 0xe9, 0x5d, - 0xf4, 0x16, 0xc0, 0xc5, 0xc8, 0x89, 0x28, 0xe3, 0x80, 0x4e, 0x79, 0x5b, 0x5f, 0x9f, 0x25, 0x55, - 0x11, 0xb9, 0x21, 0x88, 0x5c, 0x43, 0xab, 0x19, 0x44, 0xe2, 0x03, 0x7c, 0x09, 0x60, 0x4e, 0xba, - 0x0f, 0xad, 0x9d, 0xdd, 0x63, 0xcc, 0xea, 0x7a, 0x79, 0x7a, 0xe2, 0x4c, 0x33, 0x91, 0x3e, 0xaf, - 0xef, 0x1c, 0x0f, 0x0c, 0x70, 0x32, 0x30, 0xc0, 0xcf, 0x81, 0x01, 0xde, 0x0f, 0x0d, 0xed, 0x64, - 0x68, 0x68, 0xdf, 0x86, 0x86, 0xf6, 0xb8, 0x92, 0xf9, 0x36, 0x3c, 0x97, 0x68, 0xe2, 0x89, 0x68, - 0xe6, 0xc4, 0x9f, 0xd5, 0xd6, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x31, 0x99, 0x37, 0x9b, 0x84, - 0x07, 0x00, 0x00, + // 824 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcf, 0x6b, 0x13, 0x5b, + 0x14, 0xce, 0xed, 0x7b, 0x4d, 0xd3, 0x13, 0xde, 0x5b, 0xdc, 0xe6, 0xf1, 0xd2, 0xe9, 0x6b, 0xf2, + 0x98, 0x6a, 0x9b, 0xd6, 0x74, 0xa6, 0x69, 0x85, 0xa2, 0x1b, 0x69, 0x2a, 0xba, 0x10, 0x69, 0x8c, + 0xae, 0x04, 0x91, 0x9b, 0x64, 0x1c, 0x43, 0x93, 0xb9, 0xd3, 0xdc, 0x89, 0x58, 0x4a, 0x51, 0x04, + 0xc1, 0x95, 0x0a, 0x2e, 0x5c, 0xb8, 0xa9, 0x1b, 0x41, 0x97, 0xfe, 0x15, 0x5d, 0xb8, 0x28, 0xb8, + 0x71, 0xa5, 0xd2, 0xba, 0xf0, 0xcf, 0x90, 0xdc, 0x1f, 0xd3, 0x49, 0x32, 0x4d, 0x06, 0xd1, 0x55, + 0x66, 0xee, 0x3d, 0xe7, 0x3b, 0xdf, 0x77, 0xee, 0xb9, 0xdf, 0x04, 0xb2, 0x55, 0xca, 0x9a, 0x94, + 0x99, 0x15, 0xe2, 0x6c, 0x9a, 0xf7, 0x0a, 0x15, 0xcb, 0x23, 0x05, 0x73, 0xab, 0x6d, 0xb5, 0xb6, + 0x0d, 0xb7, 0x45, 0x3d, 0x8a, 0x27, 0x44, 0x80, 0xd1, 0x09, 0x30, 0x64, 0x80, 0xb6, 0xe0, 0x67, + 0x31, 0x4b, 0x44, 0xfb, 0xb9, 0x2e, 0xb1, 0xeb, 0x0e, 0xf1, 0xea, 0xd4, 0x11, 0x00, 0x5a, 0xca, + 0xa6, 0x36, 0xe5, 0x8f, 0x66, 0xe7, 0x49, 0xae, 0xfe, 0x67, 0x53, 0x6a, 0x37, 0x2c, 0x93, 0xb8, + 0x75, 0x93, 0x38, 0x0e, 0xf5, 0x78, 0x0a, 0x93, 0xbb, 0x99, 0x20, 0xbe, 0x42, 0xae, 0xd2, 0xba, + 0xd3, 0xb7, 0x1f, 0x60, 0xcd, 0x19, 0xf2, 0x7d, 0x7d, 0x03, 0x26, 0xae, 0x75, 0x58, 0x15, 0x49, + 0x83, 0x38, 0x55, 0xab, 0x6c, 0x6d, 0xb5, 0x2d, 0xe6, 0xe1, 0x34, 0x8c, 0x91, 0x5a, 0xad, 0x65, + 0x31, 0x96, 0x46, 0xff, 0xa3, 0xdc, 0x78, 0x59, 0xbd, 0xe2, 0x14, 0x8c, 0xd6, 0x2c, 0x87, 0x36, + 0xd3, 0x23, 0x7c, 0x5d, 0xbc, 0x9c, 0x4f, 0x3c, 0xd9, 0xcb, 0xc6, 0xbe, 0xef, 0x65, 0x63, 0xfa, + 0x15, 0x48, 0x75, 0x03, 0x32, 0x97, 0x3a, 0xcc, 0xc2, 0x2b, 0x30, 0x56, 0x11, 0x4b, 0x1c, 0x31, + 0xb9, 0x3c, 0x69, 0xf8, 0xfd, 0x62, 0x96, 0xea, 0x97, 0xb1, 0x4e, 0xeb, 0x4e, 0x59, 0x45, 0xea, + 0x8f, 0x11, 0xfc, 0xcb, 0xd1, 0xd6, 0x1a, 0x0d, 0x09, 0xc8, 0x86, 0x53, 0xbc, 0x04, 0x70, 0xdc, + 0x5b, 0xce, 0x33, 0xb9, 0x3c, 0xdb, 0x55, 0x4d, 0x1c, 0x9b, 0xaa, 0x59, 0x22, 0xb6, 0x12, 0x5e, + 0x0e, 0x64, 0x06, 0x44, 0x7d, 0x40, 0x90, 0xee, 0xe7, 0x21, 0x95, 0xd9, 0x90, 0x90, 0x7c, 0x3b, + 0x4c, 0xfe, 0x18, 0x28, 0xad, 0xb8, 0xb4, 0xff, 0x39, 0x1b, 0x7b, 0xf7, 0x25, 0x9b, 0xb3, 0xeb, + 0xde, 0xdd, 0x76, 0xc5, 0xa8, 0xd2, 0xa6, 0x29, 0x8f, 0x48, 0xfc, 0x2c, 0xb2, 0xda, 0xa6, 0xe9, + 0x6d, 0xbb, 0x16, 0xe3, 0x09, 0xac, 0xec, 0x83, 0xe3, 0xcb, 0x21, 0xba, 0xe6, 0x86, 0xea, 0x12, + 0x2c, 0x83, 0xc2, 0xf4, 0x49, 0xd9, 0xd5, 0x1b, 0xd4, 0x23, 0x8d, 0xeb, 0x6d, 0xd7, 0x6d, 0x6c, + 0x4b, 0xfd, 0xfa, 0x03, 0x29, 0xb4, 0x6b, 0x4b, 0x0a, 0xad, 0x42, 0x9c, 0xf1, 0x95, 0xdf, 0x21, + 0x53, 0x42, 0xeb, 0x79, 0x39, 0x3f, 0xa2, 0xf6, 0xc6, 0x1d, 0x75, 0xdc, 0xfe, 0xdc, 0xa1, 0xc0, + 0xdc, 0xe9, 0x25, 0xf8, 0xa7, 0x27, 0x5a, 0x72, 0x5d, 0x85, 0x38, 0x69, 0xd2, 0xb6, 0xe3, 0x0d, + 0x9d, 0xb6, 0xe2, 0x9f, 0x1d, 0xae, 0x65, 0x19, 0xae, 0xa7, 0x00, 0x73, 0xc4, 0x12, 0x69, 0x91, + 0xa6, 0x1a, 0x36, 0xbd, 0x24, 0xaf, 0x89, 0x5a, 0x95, 0x55, 0xce, 0x41, 0xdc, 0xe5, 0x2b, 0xb2, + 0xca, 0x94, 0x11, 0xe2, 0x01, 0x86, 0x48, 0x52, 0x75, 0x44, 0x82, 0x5e, 0x03, 0x8d, 0x23, 0x5e, + 0xec, 0xe8, 0x60, 0x57, 0x2d, 0x8f, 0xd4, 0x88, 0x47, 0x94, 0xda, 0xee, 0x11, 0x46, 0x3f, 0x3b, + 0xc2, 0xfa, 0x5b, 0x04, 0x53, 0xa1, 0x65, 0xa4, 0x80, 0x35, 0x18, 0x6f, 0xca, 0x35, 0x35, 0xbc, + 0xd3, 0xa1, 0x1a, 0x54, 0xa6, 0x54, 0x71, 0x9c, 0xf5, 0xeb, 0xa6, 0xb2, 0x00, 0x93, 0xc7, 0x54, + 0x7b, 0x1b, 0x12, 0x7e, 0xfc, 0xb7, 0x82, 0x4d, 0xec, 0x13, 0x77, 0x01, 0x12, 0x8a, 0xa6, 0x6c, + 0x61, 0x24, 0x6d, 0x7e, 0xd2, 0xf2, 0xfb, 0x04, 0x8c, 0x72, 0x7c, 0xfc, 0x12, 0xc1, 0x98, 0xbc, + 0xf8, 0x38, 0x17, 0x0a, 0x12, 0xe2, 0xa2, 0xda, 0x7c, 0x84, 0x48, 0xc1, 0x55, 0x5f, 0x7d, 0xf4, + 0xf1, 0xdb, 0x8b, 0x91, 0x02, 0x36, 0xcd, 0x70, 0xc3, 0x16, 0x16, 0x60, 0xee, 0x48, 0x8f, 0xdb, + 0x35, 0x77, 0x78, 0x07, 0x76, 0xf1, 0x2b, 0x04, 0xc9, 0x80, 0x2b, 0xe1, 0xfc, 0xc9, 0x35, 0xfb, + 0x4d, 0x54, 0x5b, 0x8c, 0x18, 0x2d, 0x59, 0x9a, 0x9c, 0xe5, 0x3c, 0x9e, 0x8b, 0xc8, 0x12, 0x3f, + 0x43, 0x90, 0x0c, 0x58, 0xc9, 0x20, 0x76, 0xfd, 0x66, 0x34, 0x88, 0x5d, 0x88, 0x3f, 0xe9, 0x33, + 0x9c, 0xdd, 0x34, 0x9e, 0x0a, 0x65, 0x27, 0xfc, 0x05, 0x3f, 0x45, 0x90, 0x50, 0x6e, 0x81, 0x07, + 0x1c, 0x50, 0x8f, 0xff, 0x68, 0x0b, 0x51, 0x42, 0x25, 0x91, 0x33, 0x9c, 0xc8, 0x69, 0x3c, 0x33, + 0x80, 0x88, 0x7f, 0x80, 0x0f, 0x11, 0xc4, 0x85, 0x43, 0xe0, 0xb9, 0x93, 0x6b, 0x74, 0xd9, 0x91, + 0x96, 0x1b, 0x1e, 0x18, 0xa9, 0x27, 0xc2, 0x8b, 0xf0, 0x1b, 0x04, 0x7f, 0x75, 0x5d, 0x21, 0x6c, + 0x9c, 0x5c, 0x20, 0xec, 0x7a, 0x6a, 0x66, 0xe4, 0x78, 0xc9, 0xeb, 0x2c, 0xe7, 0x65, 0xe0, 0x7c, + 0x28, 0x2f, 0xde, 0x1a, 0x76, 0x5b, 0x5d, 0x44, 0xbf, 0x57, 0xaf, 0x11, 0xfc, 0xdd, 0xed, 0x64, + 0x78, 0x58, 0xe5, 0x5e, 0x6b, 0xd5, 0x96, 0xa2, 0x27, 0x48, 0xae, 0x79, 0xce, 0x75, 0x16, 0x9f, + 0x8a, 0xc2, 0xb5, 0xb8, 0xbe, 0x7f, 0x98, 0x41, 0x07, 0x87, 0x19, 0xf4, 0xf5, 0x30, 0x83, 0x9e, + 0x1f, 0x65, 0x62, 0x07, 0x47, 0x99, 0xd8, 0xa7, 0xa3, 0x4c, 0xec, 0xe6, 0xfc, 0xc0, 0x8f, 0xe1, + 0x7d, 0x01, 0xcb, 0xbf, 0x89, 0x95, 0x38, 0xff, 0x77, 0xb6, 0xf2, 0x23, 0x00, 0x00, 0xff, 0xff, + 0xb4, 0xd4, 0xb0, 0xfc, 0x75, 0x0a, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -559,6 +767,10 @@ type QueryClient interface { SupplyOf(ctx context.Context, in *QuerySupplyOfRequest, opts ...grpc.CallOption) (*QuerySupplyOfResponse, error) // Params queries the parameters of x/bank module. Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // DenomsMetadata queries the client metadata of a given coin denomination. + DenomMetadata(ctx context.Context, in *QueryDenomMetadataRequest, opts ...grpc.CallOption) (*QueryDenomMetadataResponse, error) + // DenomsMetadata queries the client metadata for all registered coin denominations. + DenomsMetadata(ctx context.Context, in *QueryDenomsMetadataRequest, opts ...grpc.CallOption) (*QueryDenomsMetadataResponse, error) } type queryClient struct { @@ -614,6 +826,24 @@ func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts . return out, nil } +func (c *queryClient) DenomMetadata(ctx context.Context, in *QueryDenomMetadataRequest, opts ...grpc.CallOption) (*QueryDenomMetadataResponse, error) { + out := new(QueryDenomMetadataResponse) + err := c.cc.Invoke(ctx, "/cosmos.bank.v1beta1.Query/DenomMetadata", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DenomsMetadata(ctx context.Context, in *QueryDenomsMetadataRequest, opts ...grpc.CallOption) (*QueryDenomsMetadataResponse, error) { + out := new(QueryDenomsMetadataResponse) + err := c.cc.Invoke(ctx, "/cosmos.bank.v1beta1.Query/DenomsMetadata", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Balance queries the balance of a single coin for a single account. @@ -626,6 +856,10 @@ type QueryServer interface { SupplyOf(context.Context, *QuerySupplyOfRequest) (*QuerySupplyOfResponse, error) // Params queries the parameters of x/bank module. Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // DenomsMetadata queries the client metadata of a given coin denomination. + DenomMetadata(context.Context, *QueryDenomMetadataRequest) (*QueryDenomMetadataResponse, error) + // DenomsMetadata queries the client metadata for all registered coin denominations. + DenomsMetadata(context.Context, *QueryDenomsMetadataRequest) (*QueryDenomsMetadataResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -647,6 +881,12 @@ func (*UnimplementedQueryServer) SupplyOf(ctx context.Context, req *QuerySupplyO func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") } +func (*UnimplementedQueryServer) DenomMetadata(ctx context.Context, req *QueryDenomMetadataRequest) (*QueryDenomMetadataResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DenomMetadata not implemented") +} +func (*UnimplementedQueryServer) DenomsMetadata(ctx context.Context, req *QueryDenomsMetadataRequest) (*QueryDenomsMetadataResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DenomsMetadata not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -742,6 +982,42 @@ func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interf return interceptor(ctx, in, info, handler) } +func _Query_DenomMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDenomMetadataRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DenomMetadata(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.bank.v1beta1.Query/DenomMetadata", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DenomMetadata(ctx, req.(*QueryDenomMetadataRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_DenomsMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDenomsMetadataRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DenomsMetadata(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.bank.v1beta1.Query/DenomsMetadata", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DenomsMetadata(ctx, req.(*QueryDenomsMetadataRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "cosmos.bank.v1beta1.Query", HandlerType: (*QueryServer)(nil), @@ -766,6 +1042,14 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "Params", Handler: _Query_Params_Handler, }, + { + MethodName: "DenomMetadata", + Handler: _Query_DenomMetadata_Handler, + }, + { + MethodName: "DenomsMetadata", + Handler: _Query_DenomsMetadata_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "cosmos/bank/v1beta1/query.proto", @@ -1113,81 +1397,228 @@ func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { - offset -= sovQuery(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *QueryBalanceRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Address) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } - l = len(m.Denom) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) +func (m *QueryDenomsMetadataRequest) 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 n + return dAtA[:n], nil } -func (m *QueryBalanceResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Balance != nil { - l = m.Balance.Size() - n += 1 + l + sovQuery(uint64(l)) - } - return n +func (m *QueryDenomsMetadataRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryAllBalancesRequest) Size() (n int) { - if m == nil { - return 0 - } +func (m *QueryDenomsMetadataRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.Address) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } if m.Pagination != nil { - l = m.Pagination.Size() - n += 1 + l + sovQuery(uint64(l)) + { + 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 n + return len(dAtA) - i, nil } -func (m *QueryAllBalancesResponse) Size() (n int) { - if m == nil { - return 0 +func (m *QueryDenomsMetadataResponse) 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 *QueryDenomsMetadataResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDenomsMetadataResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.Balances) > 0 { - for _, e := range m.Balances { - 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 + { + 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.Metadatas) > 0 { + for iNdEx := len(m.Metadatas) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Metadatas[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 *QueryDenomMetadataRequest) 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 *QueryDenomMetadataRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDenomMetadataRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDenomMetadataResponse) 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 *QueryDenomMetadataResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDenomMetadataResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Metadata.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 encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryBalanceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBalanceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Balance != nil { + l = m.Balance.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAllBalancesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAllBalancesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Balances) > 0 { + for _, e := range m.Balances { + 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 *QueryTotalSupplyRequest) Size() (n int) { @@ -1258,6 +1689,62 @@ func (m *QueryParamsResponse) Size() (n int) { return n } +func (m *QueryDenomsMetadataRequest) 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 *QueryDenomsMetadataResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Metadatas) > 0 { + for _, e := range m.Metadatas { + 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 *QueryDenomMetadataRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDenomMetadataResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Metadata.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1363,10 +1850,381 @@ func (m *QueryBalanceRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + 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 *QueryBalanceResponse) 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: QueryBalanceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBalanceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Balance", 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.Balance == nil { + m.Balance = &types.Coin{} + } + if err := m.Balance.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 *QueryAllBalancesRequest) 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: QueryAllBalancesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllBalancesRequest: 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 ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + 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.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 *QueryAllBalancesResponse) 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: QueryAllBalancesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllBalancesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Balances", 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.Balances = append(m.Balances, types.Coin{}) + if err := m.Balances[len(m.Balances)-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 *QueryTotalSupplyRequest) 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: QueryTotalSupplyRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTotalSupplyRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + 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 { @@ -1381,7 +2239,7 @@ func (m *QueryBalanceRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryBalanceResponse) Unmarshal(dAtA []byte) error { +func (m *QueryTotalSupplyResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1404,15 +2262,15 @@ func (m *QueryBalanceResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryBalanceResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QueryTotalSupplyResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryBalanceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryTotalSupplyResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Balance", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Supply", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1439,10 +2297,8 @@ func (m *QueryBalanceResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Balance == nil { - m.Balance = &types.Coin{} - } - if err := m.Balance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Supply = append(m.Supply, types.Coin{}) + if err := m.Supply[len(m.Supply)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1452,10 +2308,7 @@ func (m *QueryBalanceResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1470,7 +2323,7 @@ func (m *QueryBalanceResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryAllBalancesRequest) Unmarshal(dAtA []byte) error { +func (m *QuerySupplyOfRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1493,15 +2346,15 @@ func (m *QueryAllBalancesRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryAllBalancesRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QuerySupplyOfRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryAllBalancesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QuerySupplyOfRequest: 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) + return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1529,43 +2382,7 @@ func (m *QueryAllBalancesRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Address = string(dAtA[iNdEx:postIndex]) - 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.PageRequest{} - } - if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Denom = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -1573,10 +2390,7 @@ func (m *QueryAllBalancesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1591,7 +2405,7 @@ func (m *QueryAllBalancesRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryAllBalancesResponse) Unmarshal(dAtA []byte) error { +func (m *QuerySupplyOfResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1614,49 +2428,15 @@ func (m *QueryAllBalancesResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryAllBalancesResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QuerySupplyOfResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryAllBalancesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QuerySupplyOfResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Balances", 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.Balances = append(m.Balances, types.Coin{}) - if err := m.Balances[len(m.Balances)-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) + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1683,10 +2463,7 @@ func (m *QueryAllBalancesResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Pagination == nil { - m.Pagination = &query.PageResponse{} - } - if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1696,10 +2473,7 @@ func (m *QueryAllBalancesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1714,7 +2488,7 @@ func (m *QueryAllBalancesResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryTotalSupplyRequest) Unmarshal(dAtA []byte) error { +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1737,10 +2511,10 @@ func (m *QueryTotalSupplyRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryTotalSupplyRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryTotalSupplyRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: @@ -1749,10 +2523,7 @@ func (m *QueryTotalSupplyRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1767,7 +2538,7 @@ func (m *QueryTotalSupplyRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryTotalSupplyResponse) Unmarshal(dAtA []byte) error { +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1790,15 +2561,15 @@ func (m *QueryTotalSupplyResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryTotalSupplyResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryTotalSupplyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Supply", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1825,8 +2596,7 @@ func (m *QueryTotalSupplyResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Supply = append(m.Supply, types.Coin{}) - if err := m.Supply[len(m.Supply)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1836,10 +2606,7 @@ func (m *QueryTotalSupplyResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1854,7 +2621,7 @@ func (m *QueryTotalSupplyResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QuerySupplyOfRequest) Unmarshal(dAtA []byte) error { +func (m *QueryDenomsMetadataRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1877,17 +2644,17 @@ func (m *QuerySupplyOfRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QuerySupplyOfRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QueryDenomsMetadataRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QuerySupplyOfRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryDenomsMetadataRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowQuery @@ -1897,23 +2664,27 @@ func (m *QuerySupplyOfRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthQuery } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthQuery } if postIndex > l { return io.ErrUnexpectedEOF } - m.Denom = string(dAtA[iNdEx:postIndex]) + 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 @@ -1921,10 +2692,7 @@ func (m *QuerySupplyOfRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1939,7 +2707,7 @@ func (m *QuerySupplyOfRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *QuerySupplyOfResponse) Unmarshal(dAtA []byte) error { +func (m *QueryDenomsMetadataResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1962,15 +2730,15 @@ func (m *QuerySupplyOfResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QuerySupplyOfResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QueryDenomsMetadataResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QuerySupplyOfResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryDenomsMetadataResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Metadatas", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1997,7 +2765,44 @@ func (m *QuerySupplyOfResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Metadatas = append(m.Metadatas, Metadata{}) + if err := m.Metadatas[len(m.Metadatas)-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 @@ -2007,10 +2812,7 @@ func (m *QuerySupplyOfResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2025,7 +2827,7 @@ func (m *QuerySupplyOfResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { +func (m *QueryDenomMetadataRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2048,22 +2850,51 @@ func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QueryDenomMetadataRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryDenomMetadataRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2078,7 +2909,7 @@ func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { +func (m *QueryDenomMetadataResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2101,15 +2932,15 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QueryDenomMetadataResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryDenomMetadataResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -2136,7 +2967,7 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -2146,10 +2977,7 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/bank/types/query.pb.gw.go b/x/bank/types/query.pb.gw.go index b1c6372ab7c1..06c7e2d04aa5 100644 --- a/x/bank/types/query.pb.gw.go +++ b/x/bank/types/query.pb.gw.go @@ -269,6 +269,96 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal } +func request_Query_DenomMetadata_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDenomMetadataRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["denom"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "denom") + } + + protoReq.Denom, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "denom", err) + } + + msg, err := client.DenomMetadata(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DenomMetadata_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDenomMetadataRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["denom"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "denom") + } + + protoReq.Denom, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "denom", err) + } + + msg, err := server.DenomMetadata(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_DenomsMetadata_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_DenomsMetadata_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDenomsMetadataRequest + 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_DenomsMetadata_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DenomsMetadata(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DenomsMetadata_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDenomsMetadataRequest + 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_DenomsMetadata_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DenomsMetadata(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -375,6 +465,46 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_DenomMetadata_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_DenomMetadata_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_DenomMetadata_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DenomsMetadata_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_DenomsMetadata_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_DenomsMetadata_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -516,6 +646,46 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_DenomMetadata_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_DenomMetadata_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_DenomMetadata_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DenomsMetadata_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_DenomsMetadata_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_DenomsMetadata_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -529,6 +699,10 @@ var ( 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_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_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_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))) ) var ( @@ -541,4 +715,8 @@ var ( forward_Query_SupplyOf_0 = runtime.ForwardResponseMessage forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_DenomMetadata_0 = runtime.ForwardResponseMessage + + forward_Query_DenomsMetadata_0 = runtime.ForwardResponseMessage ) diff --git a/x/bank/types/supply.go b/x/bank/types/supply.go index 558c852c2b83..c5c14b15bc2c 100644 --- a/x/bank/types/supply.go +++ b/x/bank/types/supply.go @@ -50,8 +50,8 @@ func (supply Supply) String() string { // ValidateBasic validates the Supply coins and returns error if invalid func (supply Supply) ValidateBasic() error { - if !supply.Total.IsValid() { - return fmt.Errorf("invalid total supply: %s", supply.Total.String()) + if err := supply.Total.Validate(); err != nil { + return fmt.Errorf("invalid total supply: %w", err) } return nil diff --git a/x/bank/types/tx.pb.go b/x/bank/types/tx.pb.go index a9e70007c389..6dd52c4b64d8 100644 --- a/x/bank/types/tx.pb.go +++ b/x/bank/types/tx.pb.go @@ -718,10 +718,7 @@ func (m *MsgSend) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -771,10 +768,7 @@ func (m *MsgSendResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -892,10 +886,7 @@ func (m *MsgMultiSend) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -945,10 +936,7 @@ func (m *MsgMultiSendResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/x/capability/module.go b/x/capability/module.go index 7957f57747d6..06bee1e2d029 100644 --- a/x/capability/module.go +++ b/x/capability/module.go @@ -136,6 +136,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(genState) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock executes all ABCI BeginBlock logic respective to the capability module. func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} diff --git a/x/capability/simulation/decoder_test.go b/x/capability/simulation/decoder_test.go index 911580505944..51232b810ab7 100644 --- a/x/capability/simulation/decoder_test.go +++ b/x/capability/simulation/decoder_test.go @@ -14,7 +14,7 @@ import ( ) func TestDecodeStore(t *testing.T) { - cdc, _ := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Marshaler dec := simulation.NewDecodeStore(cdc) capOwners := types.CapabilityOwners{ diff --git a/x/capability/types/capability.pb.go b/x/capability/types/capability.pb.go index 2fb8308714b5..785742bd338a 100644 --- a/x/capability/types/capability.pb.go +++ b/x/capability/types/capability.pb.go @@ -403,10 +403,7 @@ func (m *Capability) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCapability - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCapability } if (iNdEx + skippy) > l { @@ -520,10 +517,7 @@ func (m *Owner) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCapability - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCapability } if (iNdEx + skippy) > l { @@ -607,10 +601,7 @@ func (m *CapabilityOwners) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCapability - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCapability } if (iNdEx + skippy) > l { diff --git a/x/capability/types/genesis.pb.go b/x/capability/types/genesis.pb.go index 4b79d4039082..e72c18eda53b 100644 --- a/x/capability/types/genesis.pb.go +++ b/x/capability/types/genesis.pb.go @@ -381,10 +381,7 @@ func (m *GenesisOwners) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -487,10 +484,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/crisis/client/cli/tx.go b/x/crisis/client/cli/tx.go index c18231724ee3..c1288b95ed27 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 dcde377f6d42..593763ad82cb 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,7 +36,6 @@ 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)) diff --git a/x/crisis/module.go b/x/crisis/module.go index 5d34c1ce28f9..66d829be810e 100644 --- a/x/crisis/module.go +++ b/x/crisis/module.go @@ -158,6 +158,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(gs) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock performs a no-op. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} diff --git a/x/crisis/spec/01_state.md b/x/crisis/spec/01_state.md index cbb949b0e327..581c79dc0e6d 100644 --- a/x/crisis/spec/01_state.md +++ b/x/crisis/spec/01_state.md @@ -14,5 +14,5 @@ with the standard gas consumption method. The ConstantFee param is held in the global params store. - - Params: `mint/params -> amino(sdk.Coin)` + - Params: `mint/params -> legacy_amino(sdk.Coin)` diff --git a/x/crisis/spec/02_messages.md b/x/crisis/spec/02_messages.md index 93a01aec2677..8634c8f9f506 100644 --- a/x/crisis/spec/02_messages.md +++ b/x/crisis/spec/02_messages.md @@ -11,12 +11,7 @@ corresponding updates to the state. Blockchain invariants can be checked using the `MsgVerifyInvariant` message. -```go -type MsgVerifyInvariant struct { - Sender sdk.AccAddress - InvariantRoute string -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc7/proto/cosmos/crisis/v1beta1/tx.proto#L14-L22 This message is expected to fail if: - the sender does not have enough coins for the constant fee diff --git a/x/crisis/types/genesis.pb.go b/x/crisis/types/genesis.pb.go index 472f5677b900..06a870ff7193 100644 --- a/x/crisis/types/genesis.pb.go +++ b/x/crisis/types/genesis.pb.go @@ -227,10 +227,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/crisis/types/tx.pb.go b/x/crisis/types/tx.pb.go index 7fed79516c1c..f84c4adbf106 100644 --- a/x/crisis/types/tx.pb.go +++ b/x/crisis/types/tx.pb.go @@ -463,10 +463,7 @@ func (m *MsgVerifyInvariant) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -516,10 +513,7 @@ func (m *MsgVerifyInvariantResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 3b6032ce7b99..aa087f5f8b27 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "strconv" "strings" @@ -50,7 +49,7 @@ func GetCmdQueryParams() *cobra.Command { } queryClient := types.NewQueryClient(clientCtx) - res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) + res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) if err != nil { return err } @@ -94,7 +93,7 @@ $ %s query distribution validator-outstanding-rewards %s1lwjmdnks33xwnmfayc64ycp } res, err := queryClient.ValidatorOutstandingRewards( - context.Background(), + cmd.Context(), &types.QueryValidatorOutstandingRewardsRequest{ValidatorAddress: validatorAddr.String()}, ) if err != nil { @@ -139,7 +138,7 @@ $ %s query distribution commission %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj } res, err := queryClient.ValidatorCommission( - context.Background(), + cmd.Context(), &types.QueryValidatorCommissionRequest{ValidatorAddress: validatorAddr.String()}, ) if err != nil { @@ -199,7 +198,7 @@ $ %s query distribution slashes %svaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj } res, err := queryClient.ValidatorSlashes( - context.Background(), + cmd.Context(), &types.QueryValidatorSlashesRequest{ ValidatorAddress: validatorAddr.String(), StartingHeight: startHeight, @@ -252,6 +251,7 @@ $ %s query distribution rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1ggh } // query for rewards from a particular delegation + ctx := cmd.Context() if len(args) == 2 { validatorAddr, err := sdk.ValAddressFromBech32(args[1]) if err != nil { @@ -259,7 +259,7 @@ $ %s query distribution rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1ggh } res, err := queryClient.DelegationRewards( - context.Background(), + ctx, &types.QueryDelegationRewardsRequest{DelegatorAddress: delegatorAddr.String(), ValidatorAddress: validatorAddr.String()}, ) if err != nil { @@ -270,7 +270,7 @@ $ %s query distribution rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1ggh } res, err := queryClient.DelegationTotalRewards( - context.Background(), + ctx, &types.QueryDelegationTotalRewardsRequest{DelegatorAddress: delegatorAddr.String()}, ) if err != nil { @@ -307,7 +307,7 @@ $ %s query distribution community-pool } queryClient := types.NewQueryClient(clientCtx) - res, err := queryClient.CommunityPool(context.Background(), &types.QueryCommunityPoolRequest{}) + res, err := queryClient.CommunityPool(cmd.Context(), &types.QueryCommunityPoolRequest{}) if err != nil { return err } diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index d62bcd21fa07..6237bf92ef5e 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "strings" @@ -12,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" @@ -155,7 +155,7 @@ $ %s tx distribution withdraw-all-rewards --from mykey } queryClient := types.NewQueryClient(clientCtx) - delValsRes, err := queryClient.DelegatorValidators(context.Background(), &types.QueryDelegatorValidatorsRequest{DelegatorAddress: delAddr.String()}) + delValsRes, err := queryClient.DelegatorValidators(cmd.Context(), &types.QueryDelegatorValidatorsRequest{DelegatorAddress: delAddr.String()}) if err != nil { return err } @@ -215,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()...) }, } @@ -254,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()...) }, } @@ -327,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 9d8b0012a51c..f2a33a81be51 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 ac8ac4a33402..51def978342a 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 4aa25671a32e..5cb65d760c1e 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 412144094626..8beb3c715164 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 a8205dfdc1db..ad1890225527 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/module.go b/x/distribution/module.go index 034be6d9651d..0f0c0d358a91 100644 --- a/x/distribution/module.go +++ b/x/distribution/module.go @@ -161,6 +161,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(gs) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock returns the begin blocker for the distribution module. func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { BeginBlocker(ctx, req, am.keeper) diff --git a/x/distribution/proposal_handler_test.go b/x/distribution/proposal_handler_test.go index 575dbb199cbe..c4d9bdb5c6a4 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/decoder_test.go b/x/distribution/simulation/decoder_test.go index 7a705f97da10..1032550acb10 100644 --- a/x/distribution/simulation/decoder_test.go +++ b/x/distribution/simulation/decoder_test.go @@ -22,7 +22,7 @@ var ( ) func TestDecodeDistributionStore(t *testing.T) { - cdc, _ := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Marshaler dec := simulation.NewDecodeStore(cdc) decCoins := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.OneDec())} diff --git a/x/distribution/simulation/operations.go b/x/distribution/simulation/operations.go index 09090d1f64d6..b83e6f8a4e4b 100644 --- a/x/distribution/simulation/operations.go +++ b/x/distribution/simulation/operations.go @@ -120,7 +120,7 @@ func SimulateMsgSetWithdrawAddress(ak types.AccountKeeper, bk types.BankKeeper, return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } @@ -172,7 +172,7 @@ func SimulateMsgWithdrawDelegatorReward(ak types.AccountKeeper, bk types.BankKee return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } @@ -227,7 +227,7 @@ func SimulateMsgWithdrawValidatorCommission(ak types.AccountKeeper, bk types.Ban return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } @@ -282,6 +282,6 @@ func SimulateMsgFundCommunityPool(ak types.AccountKeeper, bk types.BankKeeper, k return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } diff --git a/x/distribution/simulation/operations_test.go b/x/distribution/simulation/operations_test.go index 4a3dff30878e..8ac75201b174 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 624fb9f0ff61..b8dbde4a9ae4 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 -> amino(FeePool)` +- FeePool: `0x00 -> ProtocolBuffer(FeePool)` ```go // coins with decimal @@ -25,15 +25,10 @@ type DecCoin struct { Amount sdk.Dec Denom string } - -type FeePool struct { - TotalValAccumUpdateHeight int64 // last height which the total validator accum was updated - TotalValAccum sdk.Dec // total valdator accum held by validators - Pool DecCoins // funds for all validators which have yet to be withdrawn - CommunityPool DecCoins // pool for community funds yet to be spent -} ``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/distribution/v1beta1/distribution.proto#L94-L101 + ## Validator Distribution Validator distribution information for the relevant validator is updated each time: @@ -43,16 +38,13 @@ Validator distribution information for the relevant validator is updated each ti 3. any delegator withdraws from a validator, or 4. the validator withdraws it's commission. -- ValidatorDistInfo: `0x02 | ValOperatorAddr -> amino(validatorDistribution)` +- ValidatorDistInfo: `0x02 | ValOperatorAddr -> ProtocolBuffer(validatorDistribution)` ```go type ValidatorDistInfo struct { - FeePoolWithdrawalHeight int64 // last height this validator withdrew from the global fee pool - Pool DecCoins // rewards owed to delegators, commission has already been charged (includes proposer reward) - PoolCommission DecCoins // commission collected by this validator (pending withdrawal) - - TotalDelAccumUpdateHeight int64 // last height which the total delegator accum was updated - TotalDelAccum sdk.Dec // total proposer pool accumulation factor held by delegators + OperatorAddress sdk.AccAddress + SelfBondRewards sdk.DecCoins + ValidatorCommission types.ValidatorAccumulatedCommission } ``` @@ -64,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 -> amino(delegatorDist)` +- DelegationDistInfo: `0x02 | DelegatorAddr | ValOperatorAddr -> ProtocolBuffer(delegatorDist)` ```go type DelegationDistInfo struct { diff --git a/x/distribution/spec/04_messages.md b/x/distribution/spec/04_messages.md index ef57cb4c3106..4aff6b532317 100644 --- a/x/distribution/spec/04_messages.md +++ b/x/distribution/spec/04_messages.md @@ -4,57 +4,35 @@ order: 4 # Messages -## MsgWithdrawDelegationRewardsAll +## MsgSetWithdrawAddress -When a delegator wishes to withdraw their rewards it must send -`MsgWithdrawDelegationRewardsAll`. Note that parts of this transaction logic are also -triggered each with any change in individual delegations, such as an unbond, -redelegation, or delegation of additional tokens to a specific validator. +By default a withdrawal address is delegator address. If a delegator wants to change it's +withdrawal address it must send `MsgSetWithdrawAddress`. -```go -type MsgWithdrawDelegationRewardsAll struct { - DelegatorAddr sdk.AccAddress -} ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37 -func WithdrawDelegationRewardsAll(delegatorAddr, withdrawAddr sdk.AccAddress) - height = GetHeight() - withdraw = GetDelegatorRewardsAll(delegatorAddr, height) - SendCoins(distributionModuleAcc, withdrawAddr, withdraw.TruncateDecimal()) +```go -func GetDelegatorRewardsAll(delegatorAddr sdk.AccAddress, height int64) DecCoins - - // get all distribution scenarios - delegations = GetDelegations(delegatorAddr) - - // collect all entitled rewards - withdraw = 0 - pool = staking.GetPool() - feePool = GetFeePool() - for delegation = range delegations - delInfo = GetDelegationDistInfo(delegation.DelegatorAddr, - delegation.ValidatorAddr) - valInfo = GetValidatorDistInfo(delegation.ValidatorAddr) - validator = GetValidator(delegation.ValidatorAddr) +func (k Keeper) SetWithdrawAddr(ctx sdk.Context, delegatorAddr sdk.AccAddress, withdrawAddr sdk.AccAddress) error + if k.blockedAddrs[withdrawAddr.String()] { + fail with "`{withdrawAddr}` is not allowed to receive external funds" + } - feePool, diWithdraw = delInfo.WithdrawRewards(feePool, valInfo, height, pool.BondedTokens, - validator.Tokens, validator.DelegatorShares, validator.Commission) - withdraw += diWithdraw + if !k.GetWithdrawAddrEnabled(ctx) { + fail with `ErrSetWithdrawAddrDisabled` + } - SetFeePool(feePool) - return withdraw + k.SetDelegatorWithdrawAddr(ctx, delegatorAddr, withdrawAddr) ``` -## MsgWithdrawDelegationReward +## MsgWithdrawDelegatorReward under special circumstances a delegator may wish to withdraw rewards from only a single validator. -```go -type MsgWithdrawDelegationReward struct { - DelegatorAddr sdk.AccAddress - ValidatorAddr sdk.ValAddress -} ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50 +```go func WithdrawDelegationReward(delegatorAddr, validatorAddr, withdrawAddr sdk.AccAddress) height = GetHeight() @@ -74,19 +52,16 @@ func WithdrawDelegationReward(delegatorAddr, validatorAddr, withdrawAddr sdk.Acc ``` -## MsgWithdrawValidatorRewardsAll +### Withdraw Validator Rewards All When a validator wishes to withdraw their rewards it must send -`MsgWithdrawValidatorRewardsAll`. Note that parts of this transaction logic are also +array of `MsgWithdrawDelegatorReward`. Note that parts of this transaction logic are also triggered each with any change in individual delegations, such as an unbond, redelegation, or delegation of additional tokens to a specific validator. This transaction withdraws the validators commission fee, as well as any rewards earning on their self-delegation. ```go -type MsgWithdrawValidatorRewardsAll struct { - OperatorAddr sdk.ValAddress // validator address to withdraw from -} func WithdrawValidatorRewardsAll(operatorAddr, withdrawAddr sdk.AccAddress) diff --git a/x/distribution/spec/README.md b/x/distribution/spec/README.md index 6d97f8fa3a0c..fe5f5069d63f 100644 --- a/x/distribution/spec/README.md +++ b/x/distribution/spec/README.md @@ -89,9 +89,9 @@ to set up a script to periodically withdraw and rebond rewards. 2. **[State](02_state.md)** 3. **[End Block](03_end_block.md)** 4. **[Messages](04_messages.md)** - - [MsgWithdrawDelegationRewardsAll](04_messages.md#msgwithdrawdelegationrewardsall) - - [MsgWithdrawDelegationReward](04_messages.md#msgwithdrawdelegationreward) - - [MsgWithdrawValidatorRewardsAll](04_messages.md#msgwithdrawvalidatorrewardsall) + - [MsgSetWithdrawAddress](04_messages.md#msgsetwithdrawaddress) + - [MsgWithdrawDelegatorReward](04_messages.md#msgwithdrawdelegatorreward) + - [Withdraw Validator Rewards All](04_messages.md#withdraw-validator-rewards-all) - [Common calculations ](04_messages.md#common-calculations-) 5. **[Hooks](05_hooks.md)** - [Create or modify delegation distribution](05_hooks.md#create-or-modify-delegation-distribution) diff --git a/x/distribution/types/distribution.pb.go b/x/distribution/types/distribution.pb.go index 44e51876d7ad..2657160a6ea0 100644 --- a/x/distribution/types/distribution.pb.go +++ b/x/distribution/types/distribution.pb.go @@ -1965,10 +1965,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { @@ -2071,10 +2068,7 @@ func (m *ValidatorHistoricalRewards) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { @@ -2177,10 +2171,7 @@ func (m *ValidatorCurrentRewards) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { @@ -2264,10 +2255,7 @@ func (m *ValidatorAccumulatedCommission) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { @@ -2351,10 +2339,7 @@ func (m *ValidatorOutstandingRewards) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { @@ -2457,10 +2442,7 @@ func (m *ValidatorSlashEvent) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { @@ -2544,10 +2526,7 @@ func (m *ValidatorSlashEvents) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { @@ -2631,10 +2610,7 @@ func (m *FeePool) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { @@ -2814,10 +2790,7 @@ func (m *CommunityPoolSpendProposal) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { @@ -2939,10 +2912,7 @@ func (m *DelegatorStartingInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { @@ -3058,10 +3028,7 @@ func (m *DelegationDelegatorReward) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { @@ -3271,10 +3238,7 @@ func (m *CommunityPoolSpendProposalWithDeposit) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthDistribution - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDistribution } if (iNdEx + skippy) > l { diff --git a/x/distribution/types/expected_keepers.go b/x/distribution/types/expected_keepers.go index 29346d39081e..ac9ebc3a4eea 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/genesis.pb.go b/x/distribution/types/genesis.pb.go index 04069c435a0d..5c94d4359ffa 100644 --- a/x/distribution/types/genesis.pb.go +++ b/x/distribution/types/genesis.pb.go @@ -1214,10 +1214,7 @@ func (m *DelegatorWithdrawInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -1333,10 +1330,7 @@ func (m *ValidatorOutstandingRewardsRecord) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -1451,10 +1445,7 @@ func (m *ValidatorAccumulatedCommissionRecord) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -1588,10 +1579,7 @@ func (m *ValidatorHistoricalRewardsRecord) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -1706,10 +1694,7 @@ func (m *ValidatorCurrentRewardsRecord) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -1856,10 +1841,7 @@ func (m *DelegatorStartingInfoRecord) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -2012,10 +1994,7 @@ func (m *ValidatorSlashEventRecord) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -2401,10 +2380,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/distribution/types/keys.go b/x/distribution/types/keys.go index 12fe9b17b9ff..4f0f37f82303 100644 --- a/x/distribution/types/keys.go +++ b/x/distribution/types/keys.go @@ -4,6 +4,7 @@ import ( "encoding/binary" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" ) const ( @@ -27,19 +28,19 @@ const ( // // - 0x01: sdk.ConsAddress // -// - 0x02: ValidatorOutstandingRewards +// - 0x02: ValidatorOutstandingRewards // -// - 0x03: sdk.AccAddress +// - 0x03: sdk.AccAddress // -// - 0x04: DelegatorStartingInfo +// - 0x04: DelegatorStartingInfo // -// - 0x05: ValidatorHistoricalRewards +// - 0x05: ValidatorHistoricalRewards // -// - 0x06: ValidatorCurrentRewards +// - 0x06: ValidatorCurrentRewards // -// - 0x07: ValidatorCurrentRewards +// - 0x07: ValidatorCurrentRewards // -// - 0x08: ValidatorSlashEvent +// - 0x08: ValidatorSlashEvent var ( FeePoolKey = []byte{0x00} // key for global distribution state ProposerKey = []byte{0x01} // key for the proposer operator address @@ -53,47 +54,56 @@ var ( ValidatorSlashEventPrefix = []byte{0x08} // key for validator slash fraction ) -// gets an address from a validator's outstanding rewards key +// GetValidatorOutstandingRewardsAddress creates an address from a validator's outstanding rewards key. func GetValidatorOutstandingRewardsAddress(key []byte) (valAddr sdk.ValAddress) { - addr := key[1:] - if len(addr) != sdk.AddrLen { + // key is in the format: + // 0x02 + + // Remove prefix and address length. + addr := key[2:] + if len(addr) != int(key[1]) { panic("unexpected key length") } + return sdk.ValAddress(addr) } -// gets an address from a delegator's withdraw info key +// GetDelegatorWithdrawInfoAddress creates an address from a delegator's withdraw info key. func GetDelegatorWithdrawInfoAddress(key []byte) (delAddr sdk.AccAddress) { - addr := key[1:] - if len(addr) != sdk.AddrLen { + // key is in the format: + // 0x03 + + // Remove prefix and address length. + addr := key[2:] + if len(addr) != int(key[1]) { panic("unexpected key length") } + return sdk.AccAddress(addr) } -// gets the addresses from a delegator starting info key +// GetDelegatorStartingInfoAddresses creates the addresses from a delegator starting info key. func GetDelegatorStartingInfoAddresses(key []byte) (valAddr sdk.ValAddress, delAddr sdk.AccAddress) { - addr := key[1 : 1+sdk.AddrLen] - if len(addr) != sdk.AddrLen { + // key is in the format: + // 0x04 + valAddrLen := int(key[1]) + valAddr = sdk.ValAddress(key[2 : 2+valAddrLen]) + delAddrLen := int(key[2+valAddrLen]) + delAddr = sdk.AccAddress(key[3+valAddrLen:]) + if len(delAddr.Bytes()) != delAddrLen { panic("unexpected key length") } - valAddr = sdk.ValAddress(addr) - addr = key[1+sdk.AddrLen:] - if len(addr) != sdk.AddrLen { - panic("unexpected key length") - } - delAddr = sdk.AccAddress(addr) + return } -// gets the address & period from a validator's historical rewards key +// GetValidatorHistoricalRewardsAddressPeriod creates the address & period from a validator's historical rewards key. func GetValidatorHistoricalRewardsAddressPeriod(key []byte) (valAddr sdk.ValAddress, period uint64) { - addr := key[1 : 1+sdk.AddrLen] - if len(addr) != sdk.AddrLen { - panic("unexpected key length") - } - valAddr = sdk.ValAddress(addr) - b := key[1+sdk.AddrLen:] + // key is in the format: + // 0x05 + valAddrLen := int(key[1]) + valAddr = sdk.ValAddress(key[2 : 2+valAddrLen]) + b := key[2+valAddrLen:] if len(b) != 8 { panic("unexpected key length") } @@ -101,93 +111,104 @@ func GetValidatorHistoricalRewardsAddressPeriod(key []byte) (valAddr sdk.ValAddr return } -// gets the address from a validator's current rewards key +// GetValidatorCurrentRewardsAddress creates the address from a validator's current rewards key. func GetValidatorCurrentRewardsAddress(key []byte) (valAddr sdk.ValAddress) { - addr := key[1:] - if len(addr) != sdk.AddrLen { + // key is in the format: + // 0x06: ValidatorCurrentRewards + + // Remove prefix and address length. + addr := key[2:] + if len(addr) != int(key[1]) { panic("unexpected key length") } + return sdk.ValAddress(addr) } -// gets the address from a validator's accumulated commission key +// GetValidatorAccumulatedCommissionAddress creates the address from a validator's accumulated commission key. func GetValidatorAccumulatedCommissionAddress(key []byte) (valAddr sdk.ValAddress) { - addr := key[1:] - if len(addr) != sdk.AddrLen { + // key is in the format: + // 0x07: ValidatorCurrentRewards + + // Remove prefix and address length. + addr := key[2:] + if len(addr) != int(key[1]) { panic("unexpected key length") } + return sdk.ValAddress(addr) } -// gets the height from a validator's slash event key +// GetValidatorSlashEventAddressHeight creates the height from a validator's slash event key. func GetValidatorSlashEventAddressHeight(key []byte) (valAddr sdk.ValAddress, height uint64) { - addr := key[1 : 1+sdk.AddrLen] - if len(addr) != sdk.AddrLen { - panic("unexpected key length") - } - valAddr = sdk.ValAddress(addr) - startB := 1 + sdk.AddrLen + // key is in the format: + // 0x08: ValidatorSlashEvent + valAddrLen := int(key[1]) + valAddr = key[2 : 2+valAddrLen] + startB := 2 + valAddrLen 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 +// GetValidatorOutstandingRewardsKey creates the outstanding rewards key for a validator. func GetValidatorOutstandingRewardsKey(valAddr sdk.ValAddress) []byte { - return append(ValidatorOutstandingRewardsPrefix, valAddr.Bytes()...) + return append(ValidatorOutstandingRewardsPrefix, address.MustLengthPrefix(valAddr.Bytes())...) } -// gets the key for a delegator's withdraw addr +// GetDelegatorWithdrawAddrKey creates the key for a delegator's withdraw addr. func GetDelegatorWithdrawAddrKey(delAddr sdk.AccAddress) []byte { - return append(DelegatorWithdrawAddrPrefix, delAddr.Bytes()...) + return append(DelegatorWithdrawAddrPrefix, address.MustLengthPrefix(delAddr.Bytes())...) } -// gets the key for a delegator's starting info +// GetDelegatorStartingInfoKey creates 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()...) + return append(append(DelegatorStartingInfoPrefix, address.MustLengthPrefix(v.Bytes())...), address.MustLengthPrefix(d.Bytes())...) } -// gets the prefix key for a validator's historical rewards +// GetValidatorHistoricalRewardsPrefix creates the prefix key for a validator's historical rewards. func GetValidatorHistoricalRewardsPrefix(v sdk.ValAddress) []byte { - return append(ValidatorHistoricalRewardsPrefix, v.Bytes()...) + return append(ValidatorHistoricalRewardsPrefix, address.MustLengthPrefix(v.Bytes())...) } -// gets the key for a validator's historical rewards +// GetValidatorHistoricalRewardsKey creates 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...) + return append(append(ValidatorHistoricalRewardsPrefix, address.MustLengthPrefix(v.Bytes())...), b...) } -// gets the key for a validator's current rewards +// GetValidatorCurrentRewardsKey creates the key for a validator's current rewards. func GetValidatorCurrentRewardsKey(v sdk.ValAddress) []byte { - return append(ValidatorCurrentRewardsPrefix, v.Bytes()...) + return append(ValidatorCurrentRewardsPrefix, address.MustLengthPrefix(v.Bytes())...) } -// gets the key for a validator's current commission +// GetValidatorAccumulatedCommissionKey creates the key for a validator's current commission. func GetValidatorAccumulatedCommissionKey(v sdk.ValAddress) []byte { - return append(ValidatorAccumulatedCommissionPrefix, v.Bytes()...) + return append(ValidatorAccumulatedCommissionPrefix, address.MustLengthPrefix(v.Bytes())...) } -// gets the prefix key for a validator's slash fractions +// GetValidatorSlashEventPrefix creates the prefix key for a validator's slash fractions. func GetValidatorSlashEventPrefix(v sdk.ValAddress) []byte { - return append(ValidatorSlashEventPrefix, v.Bytes()...) + return append(ValidatorSlashEventPrefix, address.MustLengthPrefix(v.Bytes())...) } -// gets the prefix key for a validator's slash fraction (ValidatorSlashEventPrefix + height) +// GetValidatorSlashEventKeyPrefix creates 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...)..., + append(address.MustLengthPrefix(v.Bytes()), heightBz...)..., ) } -// gets the key for a validator's slash fraction +// GetValidatorSlashEventKey creates 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/types/msg.go b/x/distribution/types/msg.go index 8dc72081e9ac..62c8bfc2abbb 100644 --- a/x/distribution/types/msg.go +++ b/x/distribution/types/msg.go @@ -2,6 +2,12 @@ package types import ( + "fmt" + + rosettatypes "github.com/coinbase/rosetta-sdk-go/types" + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/server/rosetta" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -90,6 +96,50 @@ func (msg MsgWithdrawDelegatorReward) ValidateBasic() error { return nil } +func (msg *MsgWithdrawDelegatorReward) ToOperations(withStatus, hasError bool) []*rosettatypes.Operation { + + var status string + if withStatus { + status = rosetta.StatusSuccess + if hasError { + status = rosetta.StatusReverted + } + } + + op := &rosettatypes.Operation{ + OperationIdentifier: &rosettatypes.OperationIdentifier{ + Index: 0, + }, + RelatedOperations: nil, + Type: proto.MessageName(msg), + Status: status, + Account: &rosettatypes.AccountIdentifier{ + Address: msg.DelegatorAddress, + SubAccount: &rosettatypes.SubAccountIdentifier{ + Address: msg.ValidatorAddress, + }, + }, + } + return []*rosettatypes.Operation{op} +} + +func (msg *MsgWithdrawDelegatorReward) FromOperations(ops []*rosettatypes.Operation) (sdk.Msg, error) { + if len(ops) != 1 { + return nil, fmt.Errorf("expected one operation") + } + op := ops[0] + if op.Account == nil { + return nil, fmt.Errorf("account identifier must be specified") + } + if op.Account.SubAccount == nil { + return nil, fmt.Errorf("account identifier subaccount must be specified") + } + return &MsgWithdrawDelegatorReward{ + DelegatorAddress: op.Account.Address, + ValidatorAddress: op.Account.SubAccount.Address, + }, nil +} + func NewMsgWithdrawValidatorCommission(valAddr sdk.ValAddress) *MsgWithdrawValidatorCommission { return &MsgWithdrawValidatorCommission{ ValidatorAddress: valAddr.String(), diff --git a/x/distribution/types/query.pb.go b/x/distribution/types/query.pb.go index e527c0045d15..47acd5e73c52 100644 --- a/x/distribution/types/query.pb.go +++ b/x/distribution/types/query.pb.go @@ -2250,10 +2250,7 @@ func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2336,10 +2333,7 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2421,10 +2415,7 @@ func (m *QueryValidatorOutstandingRewardsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2507,10 +2498,7 @@ func (m *QueryValidatorOutstandingRewardsResponse) Unmarshal(dAtA []byte) error if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2592,10 +2580,7 @@ func (m *QueryValidatorCommissionRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2678,10 +2663,7 @@ func (m *QueryValidatorCommissionResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2837,10 +2819,7 @@ func (m *QueryValidatorSlashesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2960,10 +2939,7 @@ func (m *QueryValidatorSlashesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3077,10 +3053,7 @@ func (m *QueryDelegationRewardsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3164,10 +3137,7 @@ func (m *QueryDelegationRewardsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3249,10 +3219,7 @@ func (m *QueryDelegationTotalRewardsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3370,10 +3337,7 @@ func (m *QueryDelegationTotalRewardsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3455,10 +3419,7 @@ func (m *QueryDelegatorValidatorsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3540,10 +3501,7 @@ func (m *QueryDelegatorValidatorsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3625,10 +3583,7 @@ func (m *QueryDelegatorWithdrawAddressRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3710,10 +3665,7 @@ func (m *QueryDelegatorWithdrawAddressResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3763,10 +3715,7 @@ func (m *QueryCommunityPoolRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3850,10 +3799,7 @@ func (m *QueryCommunityPoolResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/distribution/types/tx.pb.go b/x/distribution/types/tx.pb.go index 7748badacf4f..55cfce4e78f4 100644 --- a/x/distribution/types/tx.pb.go +++ b/x/distribution/types/tx.pb.go @@ -1140,10 +1140,7 @@ func (m *MsgSetWithdrawAddress) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1193,10 +1190,7 @@ func (m *MsgSetWithdrawAddressResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1310,10 +1304,7 @@ func (m *MsgWithdrawDelegatorReward) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1363,10 +1354,7 @@ func (m *MsgWithdrawDelegatorRewardResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1448,10 +1436,7 @@ func (m *MsgWithdrawValidatorCommission) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1501,10 +1486,7 @@ func (m *MsgWithdrawValidatorCommissionResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1620,10 +1602,7 @@ func (m *MsgFundCommunityPool) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1673,10 +1652,7 @@ func (m *MsgFundCommunityPoolResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { 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 000000000000..e3df337513b8 --- /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 000000000000..e277c6802445 --- /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/infraction.go b/x/evidence/keeper/infraction.go index 427c8de429ed..3ae74bc216de 100644 --- a/x/evidence/keeper/infraction.go +++ b/x/evidence/keeper/infraction.go @@ -119,4 +119,5 @@ func (k Keeper) HandleEquivocationEvidence(ctx sdk.Context, evidence *types.Equi k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime) k.slashingKeeper.Tombstone(ctx, consAddr) + k.SetEvidence(ctx, evidence) } diff --git a/x/evidence/keeper/infraction_test.go b/x/evidence/keeper/infraction_test.go index 0f1adee14f46..4c7368b1df7c 100644 --- a/x/evidence/keeper/infraction_test.go +++ b/x/evidence/keeper/infraction_test.go @@ -69,6 +69,10 @@ func (suite *KeeperTestSuite) TestHandleDoubleSign() { tstaking.Ctx = ctx tstaking.Denom = stakingParams.BondDenom tstaking.Undelegate(sdk.AccAddress(operatorAddr), operatorAddr, totalBond, true) + + // query evidence from store + evidences := suite.app.EvidenceKeeper.GetAllEvidence(ctx) + suite.Len(evidences, 1) } func (suite *KeeperTestSuite) TestHandleDoubleSign_TooOld() { diff --git a/x/evidence/keeper/keeper_test.go b/x/evidence/keeper/keeper_test.go index 9165730466b7..6f2b071bed80 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/keeper/querier_test.go b/x/evidence/keeper/querier_test.go index 58a1ff81bb3e..40a9f94e5fb0 100644 --- a/x/evidence/keeper/querier_test.go +++ b/x/evidence/keeper/querier_test.go @@ -18,12 +18,12 @@ const ( func (suite *KeeperTestSuite) TestQuerier_QueryEvidence_Existing() { ctx := suite.ctx.WithIsCheckTx(false) numEvidence := 100 - _, cdc := simapp.MakeCodecs() + legacyCdc := simapp.MakeTestEncodingConfig().Amino evidence := suite.populateEvidence(ctx, numEvidence) query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryEvidence}, "/"), - Data: cdc.MustMarshalJSON(types.NewQueryEvidenceRequest(evidence[0].Hash())), + Data: legacyCdc.MustMarshalJSON(types.NewQueryEvidenceRequest(evidence[0].Hash())), } bz, err := suite.querier(ctx, []string{types.QueryEvidence}, query) @@ -31,13 +31,13 @@ func (suite *KeeperTestSuite) TestQuerier_QueryEvidence_Existing() { suite.NotNil(bz) var e exported.Evidence - suite.Nil(cdc.UnmarshalJSON(bz, &e)) + suite.Nil(legacyCdc.UnmarshalJSON(bz, &e)) suite.Equal(evidence[0], e) } func (suite *KeeperTestSuite) TestQuerier_QueryEvidence_NonExisting() { ctx := suite.ctx.WithIsCheckTx(false) - cdc, _ := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Marshaler numEvidence := 100 suite.populateEvidence(ctx, numEvidence) @@ -53,7 +53,7 @@ func (suite *KeeperTestSuite) TestQuerier_QueryEvidence_NonExisting() { func (suite *KeeperTestSuite) TestQuerier_QueryAllEvidence() { ctx := suite.ctx.WithIsCheckTx(false) - _, cdc := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Amino numEvidence := 100 suite.populateEvidence(ctx, numEvidence) @@ -73,7 +73,7 @@ func (suite *KeeperTestSuite) TestQuerier_QueryAllEvidence() { func (suite *KeeperTestSuite) TestQuerier_QueryAllEvidence_InvalidPagination() { ctx := suite.ctx.WithIsCheckTx(false) - _, cdc := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Amino numEvidence := 100 suite.populateEvidence(ctx, numEvidence) diff --git a/x/evidence/legacy/v038/types.go b/x/evidence/legacy/v038/types.go index f438e2bec562..994f718ca5fd 100644 --- a/x/evidence/legacy/v038/types.go +++ b/x/evidence/legacy/v038/types.go @@ -91,7 +91,7 @@ func (e Equivocation) Hash() tmbytes.HexBytes { // ValidateBasic performs basic stateless validation checks on an Equivocation object. func (e Equivocation) ValidateBasic() error { - if e.Time.IsZero() { + if e.Time.Unix() <= 0 { return fmt.Errorf("invalid equivocation time: %s", e.Time) } if e.Height < 1 { diff --git a/x/evidence/module.go b/x/evidence/module.go index 4367fe8d58ce..9143e208e716 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -175,6 +175,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(ExportGenesis(ctx, am.keeper)) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock executes all ABCI BeginBlock logic respective to the evidence module. func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { BeginBlocker(ctx, req, am.keeper) diff --git a/x/evidence/spec/01_concepts.md b/x/evidence/spec/01_concepts.md index 78a16523daaf..0d9f1a025d80 100644 --- a/x/evidence/spec/01_concepts.md +++ b/x/evidence/spec/01_concepts.md @@ -66,7 +66,8 @@ The `Handler` (defined below) is responsible for executing the entirety of the business logic for handling `Evidence`. This typically includes validating the evidence, both stateless checks via `ValidateBasic` and stateful checks via any keepers provided to the `Handler`. In addition, the `Handler` may also perform -capabilities such as slashing and jailing a validator. +capabilities such as slashing and jailing a validator. All `Evidence` handled +by the `Handler` should be persisted. ```go // Handler defines an agnostic Evidence handler. The handler is responsible diff --git a/x/evidence/types/evidence.go b/x/evidence/types/evidence.go index 8558623baa67..fca6126ec36f 100644 --- a/x/evidence/types/evidence.go +++ b/x/evidence/types/evidence.go @@ -43,7 +43,7 @@ func (e *Equivocation) Hash() tmbytes.HexBytes { // ValidateBasic performs basic stateless validation checks on an Equivocation object. func (e *Equivocation) ValidateBasic() error { - if e.Time.IsZero() { + if e.Time.Unix() <= 0 { return fmt.Errorf("invalid equivocation time: %s", e.Time) } if e.Height < 1 { @@ -88,7 +88,8 @@ func (e Equivocation) GetTotalPower() int64 { return 0 } // FromABCIEvidence converts a Tendermint concrete Evidence type to // SDK Evidence using Equivocation as the concrete type. func FromABCIEvidence(e abci.Evidence) exported.Evidence { - consAddr, err := sdk.Bech32ifyAddressBytes(sdk.Bech32PrefixConsAddr, e.Validator.Address) + bech32PrefixConsAddr := sdk.GetConfig().GetBech32ConsensusAddrPrefix() + consAddr, err := sdk.Bech32ifyAddressBytes(bech32PrefixConsAddr, e.Validator.Address) if err != nil { panic(err) } diff --git a/x/evidence/types/evidence.pb.go b/x/evidence/types/evidence.pb.go index 430f6cce694f..284f33a5ba0e 100644 --- a/x/evidence/types/evidence.pb.go +++ b/x/evidence/types/evidence.pb.go @@ -325,10 +325,7 @@ func (m *Equivocation) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthEvidence - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthEvidence } if (iNdEx + skippy) > l { diff --git a/x/evidence/types/evidence_test.go b/x/evidence/types/evidence_test.go index 07b9352f574d..c78f0b059cc9 100644 --- a/x/evidence/types/evidence_test.go +++ b/x/evidence/types/evidence_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/evidence/types" @@ -57,3 +58,22 @@ func TestEquivocationValidateBasic(t *testing.T) { }) } } + +func TestEvidenceAddressConversion(t *testing.T) { + sdk.GetConfig().SetBech32PrefixForConsensusNode("testcnclcons", "testcnclconspub") + tmEvidence := abci.Evidence{ + Type: abci.EvidenceType_DUPLICATE_VOTE, + Validator: abci.Validator{ + Address: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Power: 100, + }, + Height: 1, + Time: time.Now(), + TotalVotingPower: 100, + } + evidence := types.FromABCIEvidence(tmEvidence).(*types.Equivocation) + consAddr := evidence.GetConsensusAddress() + // Check the address is the same after conversion + require.Equal(t, tmEvidence.Validator.Address, consAddr.Bytes()) + sdk.GetConfig().SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) +} diff --git a/x/evidence/types/genesis.pb.go b/x/evidence/types/genesis.pb.go index c40bb8a5290c..672a0ce8cf2e 100644 --- a/x/evidence/types/genesis.pb.go +++ b/x/evidence/types/genesis.pb.go @@ -232,10 +232,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/evidence/types/query.pb.go b/x/evidence/types/query.pb.go index c88579655d63..e084ea29c899 100644 --- a/x/evidence/types/query.pb.go +++ b/x/evidence/types/query.pb.go @@ -685,10 +685,7 @@ func (m *QueryEvidenceRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -774,10 +771,7 @@ func (m *QueryEvidenceResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -863,10 +857,7 @@ func (m *QueryAllEvidenceRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -986,10 +977,7 @@ func (m *QueryAllEvidenceResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/evidence/types/tx.pb.go b/x/evidence/types/tx.pb.go index d2a2f563e177..0cce8d2cbb4a 100644 --- a/x/evidence/types/tx.pb.go +++ b/x/evidence/types/tx.pb.go @@ -479,10 +479,7 @@ func (m *MsgSubmitEvidence) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -566,10 +563,7 @@ func (m *MsgSubmitEvidenceResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/x/feegrant/ante/ante.go b/x/feegrant/ante/ante.go new file mode 100644 index 000000000000..483868c23e16 --- /dev/null +++ b/x/feegrant/ante/ante.go @@ -0,0 +1,35 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/signing" + feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + feegranttypes "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +// NewAnteHandler returns an AnteHandler that checks and increments sequence +// numbers, checks signatures & account numbers, and deducts fees from the +// fee_payer or from fee_granter (if valid grant exist). +func NewAnteHandler( + ak authkeeper.AccountKeeper, bankKeeper feegranttypes.BankKeeper, feeGrantKeeper feegrantkeeper.Keeper, + sigGasConsumer authante.SignatureVerificationGasConsumer, signModeHandler signing.SignModeHandler, +) sdk.AnteHandler { + + return sdk.ChainAnteDecorators( + authante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + authante.NewRejectExtensionOptionsDecorator(), + authante.NewMempoolFeeDecorator(), + authante.NewValidateBasicDecorator(), + authante.TxTimeoutHeightDecorator{}, + authante.NewValidateMemoDecorator(ak), + authante.NewConsumeGasForTxSizeDecorator(ak), + NewDeductGrantedFeeDecorator(ak, bankKeeper, feeGrantKeeper), + authante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators + authante.NewValidateSigCountDecorator(ak), + authante.NewSigGasConsumeDecorator(ak, sigGasConsumer), + authante.NewSigVerificationDecorator(ak, signModeHandler), + authante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator + ) +} diff --git a/x/feegrant/ante/fee.go b/x/feegrant/ante/fee.go new file mode 100644 index 000000000000..52cbda7210eb --- /dev/null +++ b/x/feegrant/ante/fee.go @@ -0,0 +1,82 @@ +package ante + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +// DeductGrantedFeeDecorator deducts fees from fee_payer or fee_granter (if exists a valid fee allowance) of the tx +// If the fee_payer or fee_granter does not have the funds to pay for the fees, return with InsufficientFunds error +// Call next AnteHandler if fees successfully deducted +// CONTRACT: Tx must implement GrantedFeeTx interface to use DeductGrantedFeeDecorator +type DeductGrantedFeeDecorator struct { + ak types.AccountKeeper + k keeper.Keeper + bk types.BankKeeper +} + +func NewDeductGrantedFeeDecorator(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) DeductGrantedFeeDecorator { + return DeductGrantedFeeDecorator{ + ak: ak, + k: k, + bk: bk, + } +} + +// AnteHandle performs a decorated ante-handler responsible for deducting transaction +// fees. Fees will be deducted from the account designated by the FeePayer on a +// transaction by default. However, if the fee payer differs from the transaction +// signer, the handler will check if a fee grant has been authorized. If the +// transaction's signer does not exist, it will be created. +func (d DeductGrantedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + feeTx, ok := tx.(sdk.FeeTx) + if !ok { + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a GrantedFeeTx") + } + + // sanity check from DeductFeeDecorator + if addr := d.ak.GetModuleAddress(authtypes.FeeCollectorName); addr == nil { + panic(fmt.Sprintf("%s module account has not been set", authtypes.FeeCollectorName)) + } + + fee := feeTx.GetFee() + feePayer := feeTx.FeePayer() + feeGranter := feeTx.FeeGranter() + + deductFeesFrom := feePayer + + // ensure the grant is allowed, if we request a different fee payer + if feeGranter != nil && !feeGranter.Equals(feePayer) { + err := d.k.UseGrantedFees(ctx, feeGranter, feePayer, fee) + if err != nil { + return ctx, sdkerrors.Wrapf(err, "%s not allowed to pay fees from %s", feeGranter, feePayer) + } + + deductFeesFrom = feeGranter + } + + // now, either way, we know that we are authorized to deduct the fees from the deductFeesFrom account + deductFeesFromAcc := d.ak.GetAccount(ctx, deductFeesFrom) + if deductFeesFromAcc == nil { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", deductFeesFrom) + } + + // move on if there is no fee to deduct + if fee.IsZero() { + return next(ctx, tx, simulate) + } + + // deduct fee if non-zero + err = authante.DeductFees(d.bk, ctx, deductFeesFromAcc, fee) + if err != nil { + return ctx, err + } + + return next(ctx, tx, simulate) +} diff --git a/x/feegrant/ante/fee_test.go b/x/feegrant/ante/fee_test.go new file mode 100644 index 000000000000..1a924db6a387 --- /dev/null +++ b/x/feegrant/ante/fee_test.go @@ -0,0 +1,286 @@ +package ante_test + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/suite" + "github.com/tendermint/tendermint/crypto" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/simapp/helpers" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + authsign "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/ante" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +// AnteTestSuite is a test suite to be used with ante handler tests. +type AnteTestSuite struct { + suite.Suite + + app *simapp.SimApp + anteHandler sdk.AnteHandler + ctx sdk.Context + clientCtx client.Context + txBuilder client.TxBuilder +} + +// SetupTest setups a new test, with new app, context, and anteHandler. +func (suite *AnteTestSuite) SetupTest(isCheckTx bool) { + suite.app, suite.ctx = createTestApp(isCheckTx) + suite.ctx = suite.ctx.WithBlockHeight(1) + + // Set up TxConfig. + encodingConfig := simapp.MakeTestEncodingConfig() + // We're using TestMsg encoding in some tests, so register it here. + encodingConfig.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil) + testdata.RegisterInterfaces(encodingConfig.InterfaceRegistry) + + suite.clientCtx = client.Context{}. + WithTxConfig(encodingConfig.TxConfig) + + suite.anteHandler = ante.NewAnteHandler(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.FeeGrantKeeper, authante.DefaultSigVerificationGasConsumer, encodingConfig.TxConfig.SignModeHandler()) +} + +func (suite *AnteTestSuite) TestDeductFeesNoDelegation() { + suite.SetupTest(false) + // setup + app, ctx := suite.app, suite.ctx + + protoTxCfg := tx.NewTxConfig(codec.NewProtoCodec(app.InterfaceRegistry()), tx.DefaultSignModes) + + // this just tests our handler + dfd := ante.NewDeductGrantedFeeDecorator(app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper) + ourAnteHandler := sdk.ChainAnteDecorators(dfd) + + // this tests the whole stack + anteHandlerStack := suite.anteHandler + + // keys and addresses + priv1, _, addr1 := testdata.KeyTestPubAddr() + priv2, _, addr2 := testdata.KeyTestPubAddr() + priv3, _, addr3 := testdata.KeyTestPubAddr() + priv4, _, addr4 := testdata.KeyTestPubAddr() + priv5, _, addr5 := testdata.KeyTestPubAddr() + + // Set addr1 with insufficient funds + 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 + 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{ + SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 500)), + }) + suite.Require().NoError(err) + + // grant low fee allowance (20atom), to check the tx requesting more than allowed. + err = app.FeeGrantKeeper.GrantFeeAllowance(ctx, addr2, addr4, &types.BasicFeeAllowance{ + SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 20)), + }) + suite.Require().NoError(err) + + cases := map[string]struct { + signerKey cryptotypes.PrivKey + signer sdk.AccAddress + feeAccount sdk.AccAddress + feeAccountKey cryptotypes.PrivKey + handler sdk.AnteHandler + fee int64 + valid bool + }{ + "paying with low funds (only ours)": { + signerKey: priv1, + signer: addr1, + fee: 50, + handler: ourAnteHandler, + valid: false, + }, + "paying with good funds (only ours)": { + signerKey: priv2, + signer: addr2, + fee: 50, + handler: ourAnteHandler, + valid: true, + }, + "paying with no account (only ours)": { + signerKey: priv3, + signer: addr3, + fee: 1, + handler: ourAnteHandler, + valid: false, + }, + "no fee with real account (only ours)": { + signerKey: priv1, + signer: addr1, + fee: 0, + handler: ourAnteHandler, + valid: true, + }, + "no fee with no account (only ours)": { + signerKey: priv5, + signer: addr5, + fee: 0, + handler: ourAnteHandler, + valid: false, + }, + "valid fee grant without account (only ours)": { + signerKey: priv3, + signer: addr3, + feeAccount: addr2, + fee: 50, + handler: ourAnteHandler, + valid: true, + }, + "no fee grant (only ours)": { + signerKey: priv3, + signer: addr3, + feeAccount: addr1, + fee: 2, + handler: ourAnteHandler, + valid: false, + }, + "allowance smaller than requested fee (only ours)": { + signerKey: priv4, + signer: addr4, + feeAccount: addr2, + fee: 50, + handler: ourAnteHandler, + valid: false, + }, + "granter cannot cover allowed fee grant (only ours)": { + signerKey: priv4, + signer: addr4, + feeAccount: addr1, + fee: 50, + handler: ourAnteHandler, + valid: false, + }, + } + + for name, stc := range cases { + tc := stc // to make scopelint happy + suite.T().Run(name, func(t *testing.T) { + fee := sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee)) + msgs := []sdk.Msg{testdata.NewTestMsg(tc.signer)} + + acc := app.AccountKeeper.GetAccount(ctx, tc.signer) + privs, accNums, seqs := []cryptotypes.PrivKey{tc.signerKey}, []uint64{0}, []uint64{0} + if acc != nil { + accNums, seqs = []uint64{acc.GetAccountNumber()}, []uint64{acc.GetSequence()} + } + + tx, err := genTxWithFeeGranter(protoTxCfg, msgs, fee, helpers.DefaultGenTxGas, ctx.ChainID(), accNums, seqs, tc.feeAccount, privs...) + suite.Require().NoError(err) + _, err = ourAnteHandler(ctx, tx, false) + if tc.valid { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + + _, err = anteHandlerStack(ctx, tx, false) + if tc.valid { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// returns context and app with params set on account keeper +func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { + app := simapp.Setup(isCheckTx) + ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) + app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) + + return app, ctx +} + +// don't consume any gas +func SigGasNoConsumer(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params authtypes.Params) error { + return nil +} + +func genTxWithFeeGranter(gen client.TxConfig, msgs []sdk.Msg, feeAmt sdk.Coins, gas uint64, chainID string, accNums, + accSeqs []uint64, feeGranter sdk.AccAddress, priv ...cryptotypes.PrivKey) (sdk.Tx, error) { + sigs := make([]signing.SignatureV2, len(priv)) + + // create a random length memo + r := rand.New(rand.NewSource(time.Now().UnixNano())) + + memo := simulation.RandStringOfLength(r, simulation.RandIntBetween(r, 0, 100)) + + signMode := gen.SignModeHandler().DefaultMode() + + // 1st round: set SignatureV2 with empty signatures, to set correct + // signer infos. + for i, p := range priv { + sigs[i] = signing.SignatureV2{ + PubKey: p.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: signMode, + }, + Sequence: accSeqs[i], + } + } + + tx := gen.NewTxBuilder() + err := tx.SetMsgs(msgs...) + if err != nil { + return nil, err + } + err = tx.SetSignatures(sigs...) + if err != nil { + return nil, err + } + tx.SetMemo(memo) + tx.SetFeeAmount(feeAmt) + tx.SetGasLimit(gas) + tx.SetFeeGranter(feeGranter) + + // 2nd round: once all signer infos are set, every signer can sign. + for i, p := range priv { + signerData := authsign.SignerData{ + ChainID: chainID, + AccountNumber: accNums[i], + Sequence: accSeqs[i], + } + signBytes, err := gen.SignModeHandler().GetSignBytes(signMode, signerData, tx.GetTx()) + if err != nil { + panic(err) + } + sig, err := p.Sign(signBytes) + if err != nil { + panic(err) + } + sigs[i].Data.(*signing.SingleSignatureData).Signature = sig + err = tx.SetSignatures(sigs...) + if err != nil { + panic(err) + } + } + + return tx.GetTx(), nil +} + +func TestAnteTestSuite(t *testing.T) { + suite.Run(t, new(AnteTestSuite)) +} diff --git a/x/feegrant/client/cli/cli_test.go b/x/feegrant/client/cli/cli_test.go new file mode 100644 index 000000000000..df4ac286f7d4 --- /dev/null +++ b/x/feegrant/client/cli/cli_test.go @@ -0,0 +1,622 @@ +// +build norace + +package cli_test + +import ( + "fmt" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" + tmcli "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/client/cli" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" + govtestutil "github.com/cosmos/cosmos-sdk/x/gov/client/testutil" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network + addedGranter sdk.AccAddress + addedGrantee sdk.AccAddress + addedGrant types.FeeAllowanceGrant +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + if testing.Short() { + s.T().Skip("skipping test in unit-tests mode.") + } + + cfg := network.DefaultConfig() + cfg.NumValidators = 2 + + s.cfg = cfg + s.network = network.New(s.T(), cfg) + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) + + val := s.network.Validators[0] + granter := val.Address + grantee := s.network.Validators[1].Address + + clientCtx := val.ClientCtx + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + } + + fee := sdk.NewCoin("stake", sdk.NewInt(100)) + duration := 365 * 24 * 60 * 60 + + args := append( + []string{ + granter.String(), + grantee.String(), + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + fmt.Sprintf("--%s=%v", cli.FlagExpiration, duration), + }, + commonFlags..., + ) + + cmd := cli.NewCmdFeeGrant() + + _, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + s.Require().NoError(err) + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) + + s.addedGranter = granter + s.addedGrantee = grantee + + grant, err := types.NewFeeAllowanceGrant(granter, grantee, &types.BasicFeeAllowance{ + SpendLimit: sdk.NewCoins(fee), + }) + s.Require().NoError(err) + + s.addedGrant = grant +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestCmdGetFeeGrant() { + val := s.network.Validators[0] + granter := val.Address + grantee := s.addedGrantee + clientCtx := val.ClientCtx + + testCases := []struct { + name string + args []string + expectErrMsg string + expectErr bool + respType *types.FeeAllowanceGrant + resp *types.FeeAllowanceGrant + }{ + { + "wrong granter", + []string{ + "wrong_granter", + grantee.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + "decoding bech32 failed", + true, nil, nil, + }, + { + "wrong grantee", + []string{ + granter.String(), + "wrong_grantee", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + "decoding bech32 failed", + true, nil, nil, + }, + { + "non existed grant", + []string{ + "cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl", + grantee.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + "no fee allowance found", + true, nil, nil, + }, + { + "valid req", + []string{ + granter.String(), + grantee.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + "", + false, + &types.FeeAllowanceGrant{}, + &s.addedGrant, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryFeeGrant() + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expectErrMsg) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + s.Require().Equal(tc.respType.Grantee, tc.respType.Grantee) + s.Require().Equal(tc.respType.Granter, tc.respType.Granter) + s.Require().Equal( + tc.respType.GetFeeGrant().(*types.BasicFeeAllowance).SpendLimit, + tc.resp.GetFeeGrant().(*types.BasicFeeAllowance).SpendLimit, + ) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdGetFeeGrants() { + val := s.network.Validators[0] + grantee := s.addedGrantee + clientCtx := val.ClientCtx + + testCases := []struct { + name string + args []string + expectErr bool + resp *types.QueryFeeAllowancesResponse + expectLength int + }{ + { + "wrong grantee", + []string{ + "wrong_grantee", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, nil, 0, + }, + { + "non existed grantee", + []string{ + "cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, &types.QueryFeeAllowancesResponse{}, 0, + }, + { + "valid req", + []string{ + grantee.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, &types.QueryFeeAllowancesResponse{}, 1, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryFeeGrants() + 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(), tc.resp), out.String()) + s.Require().Len(tc.resp.FeeAllowances, tc.expectLength) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { + val := s.network.Validators[0] + granter := val.Address + alreadyExistedGrantee := s.addedGrantee + clientCtx := val.ClientCtx + + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + } + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "wrong granter address", + append( + []string{ + "wrong_granter", + "cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl", + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + true, nil, 0, + }, + { + "wrong grantee address", + append( + []string{ + granter.String(), + "wrong_grantee", + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + true, nil, 0, + }, + { + "valid basic fee grant", + append( + []string{ + granter.String(), + "cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl", + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + false, &sdk.TxResponse{}, 0, + }, + { + "valid basic fee grant without spend limit", + append( + []string{ + granter.String(), + "cosmos17h5lzptx3ghvsuhk7wx4c4hnl7rsswxjer97em", + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + false, &sdk.TxResponse{}, 0, + }, + { + "valid basic fee grant without expiration", + append( + []string{ + granter.String(), + "cosmos16dlc38dcqt0uralyd8hksxyrny6kaeqfjvjwp5", + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + false, &sdk.TxResponse{}, 0, + }, + { + "valid basic fee grant without spend-limit and expiration", + append( + []string{ + granter.String(), + "cosmos1ku40qup9vwag4wtf8cls9mkszxfthaklxkp3c8", + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + false, &sdk.TxResponse{}, 0, + }, + { + "try to add existed grant", + append( + []string{ + granter.String(), + alreadyExistedGrantee.String(), + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + false, &sdk.TxResponse{}, 18, + }, + { + "invalid number of args(periodic fee grant)", + append( + []string{ + granter.String(), + "cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl", + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, 10*60*60), + }, + commonFlags..., + ), + true, nil, 0, + }, + { + "period mentioned and period limit omitted, invalid periodic grant", + append( + []string{ + granter.String(), + "cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl", + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%d", cli.FlagPeriod, 10*60*60), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, 60*60), + }, + commonFlags..., + ), + true, nil, 0, + }, + { + "period cannot be greater than the actual expiration(periodic fee grant)", + append( + []string{ + granter.String(), + "cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl", + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%d", cli.FlagPeriod, 10*60*60), + fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, 60*60), + }, + commonFlags..., + ), + true, nil, 0, + }, + { + "valid periodic fee grant", + append( + []string{ + granter.String(), + "cosmos1w55kgcf3ltaqdy4ww49nge3klxmrdavrr6frmp", + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60), + fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, 10*60*60), + }, + commonFlags..., + ), + false, &sdk.TxResponse{}, 0, + }, + { + "valid periodic fee grant without spend-limit", + append( + []string{ + granter.String(), + "cosmos1vevyks8pthkscvgazc97qyfjt40m6g9xe85ry8", + fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60), + fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + fmt.Sprintf("--%s=%d", cli.FlagExpiration, 10*60*60), + }, + commonFlags..., + ), + false, &sdk.TxResponse{}, 0, + }, + { + "valid periodic fee grant without expiration", + append( + []string{ + granter.String(), + "cosmos14cm33pvnrv2497tyt8sp9yavhmw83nwej3m0e8", + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60), + fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + false, &sdk.TxResponse{}, 0, + }, + { + "valid periodic fee grant without spend-limit and expiration", + append( + []string{ + granter.String(), + "cosmos12nyk4pcf4arshznkpz882e4l4ts0lt0ap8ce54", + fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60), + fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + false, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewCmdFeeGrant() + 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(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewCmdRevokeFeegrant() { + val := s.network.Validators[0] + granter := s.addedGranter + grantee := s.addedGrantee + clientCtx := val.ClientCtx + + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + } + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "invalid grantee", + append( + []string{ + "wrong_granter", + grantee.String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + true, + nil, + 0, + }, + { + "invalid grantee", + append( + []string{ + granter.String(), + "wrong_grantee", + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + true, + nil, + 0, + }, + { + "Non existed grant", + append( + []string{ + granter.String(), + "cosmos1aeuqja06474dfrj7uqsvukm6rael982kk89mqr", + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + false, + &sdk.TxResponse{}, + 4, + }, + { + "Valid revoke", + append( + []string{ + granter.String(), + grantee.String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + false, + &sdk.TxResponse{}, + 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewCmdRevokeFeegrant() + 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(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestTxWithFeeGrant() { + val := s.network.Validators[0] + clientCtx := val.ClientCtx + 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) + s.Require().NoError(err) + grantee := sdk.AccAddress(info.GetPubKey().Address()) + + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + } + + fee := sdk.NewCoin("stake", sdk.NewInt(100)) + duration := 365 * 24 * 60 * 60 + + args := append( + []string{ + granter.String(), + grantee.String(), + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + fmt.Sprintf("--%s=%v", cli.FlagExpiration, duration), + }, + commonFlags..., + ) + + cmd := cli.NewCmdFeeGrant() + + _, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + s.Require().NoError(err) + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) + + // granted fee allowance for an account which is not in state and creating + // any tx with it by using --fee-account shouldn't fail + out, err := govtestutil.MsgSubmitProposal(val.ClientCtx, grantee.String(), + "Text Proposal", "No desc", govtypes.ProposalTypeText, + fmt.Sprintf("--%s=%s", flags.FlagFeeAccount, granter.String()), + ) + + s.Require().NoError(err) + var resp sdk.TxResponse + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &resp), out.String()) + s.Require().Equal(uint32(0), resp.Code) +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/feegrant/client/cli/query.go b/x/feegrant/client/cli/query.go new file mode 100644 index 000000000000..5fd10cd1beda --- /dev/null +++ b/x/feegrant/client/cli/query.go @@ -0,0 +1,130 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd() *cobra.Command { + feegrantQueryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the feegrant module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + feegrantQueryCmd.AddCommand( + GetCmdQueryFeeGrant(), + GetCmdQueryFeeGrants(), + ) + + return feegrantQueryCmd +} + +// GetCmdQueryFeeGrant returns cmd to query for a grant between granter and grantee. +func GetCmdQueryFeeGrant() *cobra.Command { + cmd := &cobra.Command{ + Use: "grant [granter] [grantee]", + Args: cobra.ExactArgs(2), + Short: "Query details of a single grant", + Long: strings.TrimSpace( + fmt.Sprintf(`Query details for a grant. +You can find the fee-grant of a granter and grantee. + +Example: +$ %s query feegrant grant [granter] [grantee] +`, version.AppName), + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + queryClient := types.NewQueryClient(clientCtx) + + granterAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + granteeAddr, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + res, err := queryClient.FeeAllowance( + cmd.Context(), + &types.QueryFeeAllowanceRequest{ + Granter: granterAddr.String(), + Grantee: granteeAddr.String(), + }, + ) + + if err != nil { + return err + } + + return clientCtx.PrintProto(res.FeeAllowance) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQueryFeeGrants returns cmd to query for all grants for a grantee. +func GetCmdQueryFeeGrants() *cobra.Command { + cmd := &cobra.Command{ + Use: "grants [grantee]", + Args: cobra.ExactArgs(1), + Short: "Query all grants of a grantee", + Long: strings.TrimSpace( + fmt.Sprintf(`Queries all the grants for a grantee address. + +Example: +$ %s query feegrant grants [grantee] +`, version.AppName), + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + queryClient := types.NewQueryClient(clientCtx) + + granteeAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := queryClient.FeeAllowances( + cmd.Context(), + &types.QueryFeeAllowancesRequest{ + Grantee: granteeAddr.String(), + Pagination: pageReq, + }, + ) + + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "grants") + + return cmd +} diff --git a/x/feegrant/client/cli/tx.go b/x/feegrant/client/cli/tx.go new file mode 100644 index 000000000000..49702e0c7882 --- /dev/null +++ b/x/feegrant/client/cli/tx.go @@ -0,0 +1,211 @@ +package cli + +import ( + "fmt" + "strings" + "time" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "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/feegrant/types" +) + +// flag for feegrant module +const ( + FlagExpiration = "expiration" + FlagPeriod = "period" + FlagPeriodLimit = "period-limit" + FlagSpendLimit = "spend-limit" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd() *cobra.Command { + feegrantTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Feegrant transactions subcommands", + Long: "Grant and revoke fee allowance for a grantee by a granter", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + feegrantTxCmd.AddCommand( + NewCmdFeeGrant(), + NewCmdRevokeFeegrant(), + ) + + return feegrantTxCmd +} + +// NewCmdFeeGrant returns a CLI command handler for creating a MsgGrantFeeAllowance transaction. +func NewCmdFeeGrant() *cobra.Command { + cmd := &cobra.Command{ + Use: "grant [granter] [grantee]", + Short: "Grant Fee allowance to an address", + Long: strings.TrimSpace( + fmt.Sprintf( + `Grant authorization to pay fees from your address. Note, the'--from' flag is + ignored as it is implied from [granter]. + +Examples: +%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --expiration 36000 or +%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --period 3600 --period-limit 10stake --expiration 36000 + `, version.AppName, types.ModuleName, version.AppName, types.ModuleName, + ), + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + _, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + cmd.Flags().Set(flags.FlagFrom, args[0]) + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + grantee, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + granter := clientCtx.GetFromAddress() + sl, err := cmd.Flags().GetString(FlagSpendLimit) + if err != nil { + return err + } + + // if `FlagSpendLimit` isn't set, limit will be nil + limit, err := sdk.ParseCoinsNormalized(sl) + if err != nil { + return err + } + + exp, err := cmd.Flags().GetInt64(FlagExpiration) + if err != nil { + return err + } + + basic := types.BasicFeeAllowance{ + SpendLimit: limit, + } + + if exp != 0 { + expDuration := time.Duration(exp) * time.Second + basic.Expiration = types.ExpiresAtTime(time.Now().Add(expDuration)) + } + + var grant types.FeeAllowanceI + grant = &basic + + periodClock, err := cmd.Flags().GetInt64(FlagPeriod) + if err != nil { + return err + } + + periodLimitVal, err := cmd.Flags().GetString(FlagPeriodLimit) + if err != nil { + return err + } + + // Check any of period or periodLimit flags set, If set consider it as periodic fee allowance. + if periodClock > 0 || periodLimitVal != "" { + periodLimit, err := sdk.ParseCoinsNormalized(periodLimitVal) + if err != nil { + return err + } + + if periodClock > 0 && periodLimit != nil { + if exp > 0 && periodClock > exp { + return fmt.Errorf("period(%d) cannot be greater than the expiration(%d)", periodClock, exp) + } + + periodic := types.PeriodicFeeAllowance{ + Basic: basic, + Period: types.ClockDuration(time.Duration(periodClock) * time.Second), + PeriodReset: types.ExpiresAtTime(time.Now().Add(time.Duration(periodClock) * time.Second)), + PeriodSpendLimit: periodLimit, + PeriodCanSpend: periodLimit, + } + + grant = &periodic + + } else { + return fmt.Errorf("invalid number of args %d", len(args)) + } + } + + msg, err := types.NewMsgGrantFeeAllowance(grant, granter, grantee) + if err != nil { + return err + } + + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.GrantFeeAllowance(cmd.Context(), msg) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) + }, + } + + flags.AddTxFlagsToCmd(cmd) + cmd.Flags().Int64(FlagExpiration, 0, "The second unit of time duration which the grant is active for the user") + cmd.Flags().String(FlagSpendLimit, "", "Spend limit specifies the max limit can be used, if not mentioned there is no limit") + cmd.Flags().Int64(FlagPeriod, 0, "period specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset") + cmd.Flags().String(FlagPeriodLimit, "", "// period limit specifies the maximum number of coins that can be spent in the period") + + return cmd +} + +// NewCmdRevokeFeegrant returns a CLI command handler for creating a MsgRevokeFeeAllowance transaction. +func NewCmdRevokeFeegrant() *cobra.Command { + cmd := &cobra.Command{ + Use: "revoke [granter_address] [grantee_address]", + Short: "revoke fee-grant", + Long: strings.TrimSpace( + fmt.Sprintf(`revoke fee grant from a granter to a grantee. Note, the'--from' flag is + ignored as it is implied from [granter_address]. + +Example: + $ %s tx %s revoke cosmos1skj.. cosmos1skj.. + `, version.AppName, types.ModuleName), + ), + Args: cobra.ExactArgs(2), + 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 + } + + grantee, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + msg := types.NewMsgRevokeFeeAllowance(clientCtx.GetFromAddress(), grantee) + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.RevokeFeeAllowance(cmd.Context(), &msg) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) + }, + } + + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/feegrant/client/rest/grpc_query_test.go b/x/feegrant/client/rest/grpc_query_test.go new file mode 100644 index 000000000000..7b665ee9153b --- /dev/null +++ b/x/feegrant/client/rest/grpc_query_test.go @@ -0,0 +1,210 @@ +package rest_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" + "github.com/cosmos/cosmos-sdk/x/feegrant/client/cli" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +type IntegrationTestSuite struct { + suite.Suite + cfg network.Config + network *network.Network + grantee sdk.AccAddress +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := network.DefaultConfig() + + cfg.NumValidators = 1 + s.cfg = cfg + s.network = network.New(s.T(), cfg) + + val := s.network.Validators[0] + // Create new account in the keyring. + info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + newAddr := sdk.AccAddress(info.GetPubKey().Address()) + + // Send some funds to the new account. + _, err = banktestutil.MsgSendExec( + val.ClientCtx, + val.Address, + newAddr, + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(200))), 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()), + ) + s.Require().NoError(err) + + s.grantee = newAddr + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestQueryFeeAllowance() { + val := s.network.Validators[0] + baseURL := val.APIAddress + testCases := []struct { + name string + url string + expectErr bool + errorMsg string + preRun func() + postRun func(_ types.QueryFeeAllowanceResponse) + }{ + { + "fail: invalid granter", + fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, "invalid_granter", s.grantee.String()), + true, + "decoding bech32 failed: invalid index of 1: invalid request", + func() {}, + func(types.QueryFeeAllowanceResponse) {}, + }, + { + "fail: invalid grantee", + fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, val.Address.String(), "invalid_grantee"), + true, + "decoding bech32 failed: invalid index of 1: invalid request", + func() {}, + func(types.QueryFeeAllowanceResponse) {}, + }, + { + "fail: no grants", + fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, val.Address.String(), s.grantee.String()), + true, + "no fee allowance found", + func() {}, + func(types.QueryFeeAllowanceResponse) {}, + }, + { + "valid query: expect single grant", + fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, val.Address.String(), s.grantee.String()), + false, + "", + func() { + execFeeAllowance(val, s) + }, + func(allowance types.QueryFeeAllowanceResponse) { + s.Require().Equal(allowance.FeeAllowance.Granter, val.Address.String()) + s.Require().Equal(allowance.FeeAllowance.Grantee, s.grantee.String()) + }, + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + tc.preRun() + resp, _ := rest.GetRequest(tc.url) + if tc.expectErr { + s.Require().Contains(string(resp), tc.errorMsg) + } else { + var allowance types.QueryFeeAllowanceResponse + err := val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &allowance) + s.Require().NoError(err) + tc.postRun(allowance) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryGranteeAllowances() { + val := s.network.Validators[0] + baseURL := val.APIAddress + testCases := []struct { + name string + url string + expectErr bool + errorMsg string + preRun func() + postRun func(_ types.QueryFeeAllowancesResponse) + }{ + { + "fail: invalid grantee", + fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowances/%s", baseURL, "invalid_grantee"), + true, + "decoding bech32 failed: invalid index of 1: invalid request", + func() {}, + func(types.QueryFeeAllowancesResponse) {}, + }, + { + "success: no grants", + fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowances/%s?pagination.offset=1", baseURL, s.grantee.String()), + false, + "", + func() {}, + func(allowances types.QueryFeeAllowancesResponse) { + s.Require().Equal(len(allowances.FeeAllowances), 0) + }, + }, + { + "valid query: expect single grant", + fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowances/%s", baseURL, s.grantee.String()), + false, + "", + func() { + execFeeAllowance(val, s) + }, + func(allowances types.QueryFeeAllowancesResponse) { + s.Require().Equal(len(allowances.FeeAllowances), 1) + s.Require().Equal(allowances.FeeAllowances[0].Granter, val.Address.String()) + s.Require().Equal(allowances.FeeAllowances[0].Grantee, s.grantee.String()) + }, + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + tc.preRun() + resp, _ := rest.GetRequest(tc.url) + if tc.expectErr { + s.Require().Contains(string(resp), tc.errorMsg) + } else { + var allowance types.QueryFeeAllowancesResponse + err := val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &allowance) + s.Require().NoError(err) + tc.postRun(allowance) + } + }) + } +} + +func execFeeAllowance(val *network.Validator, s *IntegrationTestSuite) { + fee := sdk.NewCoin("steak", sdk.NewInt(100)) + duration := 365 * 24 * 60 * 60 + args := []string{ + val.Address.String(), + s.grantee.String(), + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%v", cli.FlagExpiration, duration), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + } + + cmd := cli.NewCmdFeeGrant() + _, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) + s.Require().NoError(err) +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/feegrant/doc.go b/x/feegrant/doc.go new file mode 100644 index 000000000000..4f190e3140c7 --- /dev/null +++ b/x/feegrant/doc.go @@ -0,0 +1,29 @@ +/* +Package feegrant provides functionality for authorizing the payment of transaction +fees from one account (key) to another account (key). + +Effectively, this allows for a user to pay fees using the balance of an account +different from their own. Example use cases would be allowing a key on a device to +pay for fees using a master wallet, or a third party service allowing users to +pay for transactions without ever really holding their own tokens. This package +provides ways for specifying fee allowances such that authorizing fee payment to +another account can be done with clear and safe restrictions. + +A user would authorize granting fee payment to another user using +MsgDelegateFeeAllowance and revoke that delegation using MsgRevokeFeeAllowance. +In both cases, Granter is the one who is authorizing fee payment and Grantee is +the one who is receiving the fee payment authorization. So grantee would correspond +to the one who is signing a transaction and the granter would be the address that +pays the fees. + +The fee allowance that a grantee receives is specified by an implementation of +the FeeAllowance interface. Two FeeAllowance implementations are provided in +this package: BasicFeeAllowance and PeriodicFeeAllowance. + +In order to integrate this into an application, we must use the DeductGrantedFeeDecorator +ante handler from this package instead of the default DeductFeeDecorator from x/auth. + +To allow handling txs from empty accounts (with fees paid from an existing account), +we have to re-order the decorators as well. +*/ +package feegrant diff --git a/x/feegrant/genesis.go b/x/feegrant/genesis.go new file mode 100644 index 000000000000..f65cfdb57825 --- /dev/null +++ b/x/feegrant/genesis.go @@ -0,0 +1,61 @@ +package feegrant + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +// GenesisState contains a set of fee allowances, persisted from the store +type GenesisState []types.FeeAllowanceGrant + +// ValidateBasic ensures all grants in the genesis state are valid +func (g GenesisState) ValidateBasic() error { + for _, f := range g { + err := f.GetFeeGrant().ValidateBasic() + if err != nil { + return err + } + } + return nil +} + +// InitGenesis will initialize the keeper from a *previously validated* GenesisState +func InitGenesis(ctx sdk.Context, k keeper.Keeper, data *types.GenesisState) { + for _, f := range data.FeeAllowances { + granter, err := sdk.AccAddressFromBech32(f.Granter) + if err != nil { + panic(err) + } + grantee, err := sdk.AccAddressFromBech32(f.Grantee) + if err != nil { + panic(err) + } + + err = k.GrantFeeAllowance(ctx, granter, grantee, f.GetFeeGrant()) + if err != nil { + panic(err) + } + } +} + +// ExportGenesis will dump the contents of the keeper into a serializable GenesisState +// +// All expiration heights will be thrown off if we dump state and start at a new +// chain at height 0. Thus, we allow the Allowances to "prepare themselves" +// for export, like if they have expiry at 5000 and current is 4000, they export with +// expiry of 1000. Every FeeAllowance has a method `PrepareForExport` that allows +// them to perform any changes needed prior to export. +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) (*types.GenesisState, error) { + time, height := ctx.BlockTime(), ctx.BlockHeight() + var grants []types.FeeAllowanceGrant + + err := k.IterateAllFeeAllowances(ctx, func(grant types.FeeAllowanceGrant) bool { + grants = append(grants, grant.PrepareForExport(time, height)) + return false + }) + + return &types.GenesisState{ + FeeAllowances: grants, + }, err +} diff --git a/x/feegrant/genesis_test.go b/x/feegrant/genesis_test.go new file mode 100644 index 000000000000..3ea58441a8a1 --- /dev/null +++ b/x/feegrant/genesis_test.go @@ -0,0 +1,57 @@ +package feegrant_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + feegrant "github.com/cosmos/cosmos-sdk/x/feegrant" + "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +type GenesisTestSuite struct { + suite.Suite + ctx sdk.Context + keeper keeper.Keeper +} + +func (suite *GenesisTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1}) + suite.keeper = app.FeeGrantKeeper +} + +var ( + granteePub = secp256k1.GenPrivKey().PubKey() + granterPub = secp256k1.GenPrivKey().PubKey() + granteeAddr = sdk.AccAddress(granteePub.Address()) + granterAddr = sdk.AccAddress(granterPub.Address()) +) + +func (suite *GenesisTestSuite) TestImportExportGenesis() { + coins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1_000))) + now := suite.ctx.BlockHeader().Time + + allowance := &types.BasicFeeAllowance{SpendLimit: coins, Expiration: types.ExpiresAtTime(now.AddDate(1, 0, 0))} + err := suite.keeper.GrantFeeAllowance(suite.ctx, granterAddr, granteeAddr, allowance) + suite.Require().NoError(err) + + genesis, err := feegrant.ExportGenesis(suite.ctx, suite.keeper) + suite.Require().NoError(err) + // Clear keeper + suite.keeper.RevokeFeeAllowance(suite.ctx, granterAddr, granteeAddr) + feegrant.InitGenesis(suite.ctx, suite.keeper, genesis) + newGenesis, err := feegrant.ExportGenesis(suite.ctx, suite.keeper) + suite.Require().NoError(err) + suite.Require().Equal(genesis, newGenesis) +} + +func TestGenesisTestSuite(t *testing.T) { + suite.Run(t, new(GenesisTestSuite)) +} diff --git a/x/feegrant/keeper/grpc_query.go b/x/feegrant/keeper/grpc_query.go new file mode 100644 index 000000000000..0fce084f90ba --- /dev/null +++ b/x/feegrant/keeper/grpc_query.go @@ -0,0 +1,94 @@ +package keeper + +import ( + "context" + + "github.com/gogo/protobuf/proto" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +var _ types.QueryServer = Keeper{} + +// FeeAllowance returns fee granted to the grantee by the granter. +func (q Keeper) FeeAllowance(c context.Context, req *types.QueryFeeAllowanceRequest) (*types.QueryFeeAllowanceResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + granterAddr, err := sdk.AccAddressFromBech32(req.Granter) + if err != nil { + return nil, err + } + + granteeAddr, err := sdk.AccAddressFromBech32(req.Grantee) + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + + feeAllowance := q.GetFeeAllowance(ctx, granterAddr, granteeAddr) + if feeAllowance == nil { + return nil, status.Errorf(codes.NotFound, "no fee allowance found") + } + + msg, ok := feeAllowance.(proto.Message) + if !ok { + return nil, status.Errorf(codes.Internal, "can't proto marshal %T", msg) + } + + feeAllowanceAny, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return nil, status.Errorf(codes.Internal, err.Error()) + } + + return &types.QueryFeeAllowanceResponse{ + FeeAllowance: &types.FeeAllowanceGrant{ + Granter: granterAddr.String(), + Grantee: granteeAddr.String(), + Allowance: feeAllowanceAny, + }, + }, nil +} + +func (q Keeper) FeeAllowances(c context.Context, req *types.QueryFeeAllowancesRequest) (*types.QueryFeeAllowancesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + granteeAddr, err := sdk.AccAddressFromBech32(req.Grantee) + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + + var grants []*types.FeeAllowanceGrant + + store := ctx.KVStore(q.storeKey) + grantsStore := prefix.NewStore(store, types.FeeAllowancePrefixByGrantee(granteeAddr)) + + pageRes, err := query.Paginate(grantsStore, req.Pagination, func(key []byte, value []byte) error { + var grant types.FeeAllowanceGrant + + if err := q.cdc.UnmarshalBinaryBare(value, &grant); err != nil { + return err + } + + grants = append(grants, &grant) + return nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryFeeAllowancesResponse{FeeAllowances: grants, Pagination: pageRes}, nil +} diff --git a/x/feegrant/keeper/keeper.go b/x/feegrant/keeper/keeper.go new file mode 100644 index 000000000000..e6606bf0ca55 --- /dev/null +++ b/x/feegrant/keeper/keeper.go @@ -0,0 +1,190 @@ +package keeper + +import ( + "fmt" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +// Keeper manages state of all fee grants, as well as calculating approval. +// It must have a codec with all available allowances registered. +type Keeper struct { + cdc codec.BinaryMarshaler + storeKey sdk.StoreKey + authKeeper types.AccountKeeper +} + +// NewKeeper creates a fee grant Keeper +func NewKeeper(cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, ak types.AccountKeeper) Keeper { + return Keeper{ + cdc: cdc, + storeKey: storeKey, + authKeeper: ak, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// GrantFeeAllowance creates a new grant +func (k Keeper) GrantFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress, feeAllowance types.FeeAllowanceI) error { + + // create the account if it is not in account state + granteeAcc := k.authKeeper.GetAccount(ctx, grantee) + if granteeAcc == nil { + granteeAcc = k.authKeeper.NewAccountWithAddress(ctx, grantee) + k.authKeeper.SetAccount(ctx, granteeAcc) + } + + store := ctx.KVStore(k.storeKey) + key := types.FeeAllowanceKey(granter, grantee) + grant, err := types.NewFeeAllowanceGrant(granter, grantee, feeAllowance) + if err != nil { + return err + } + + bz, err := k.cdc.MarshalBinaryBare(&grant) + if err != nil { + return err + } + + store.Set(key, bz) + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeSetFeeGrant, + sdk.NewAttribute(types.AttributeKeyGranter, grant.Granter), + sdk.NewAttribute(types.AttributeKeyGrantee, grant.Grantee), + ), + ) + + return nil +} + +// RevokeFeeAllowance removes an existing grant +func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) error { + store := ctx.KVStore(k.storeKey) + key := types.FeeAllowanceKey(granter, grantee) + _, found := k.GetFeeGrant(ctx, granter, grantee) + if !found { + return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "fee-grant not found") + } + + store.Delete(key) + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeRevokeFeeGrant, + sdk.NewAttribute(types.AttributeKeyGranter, granter.String()), + sdk.NewAttribute(types.AttributeKeyGrantee, grantee.String()), + ), + ) + return nil +} + +// GetFeeAllowance returns the allowance between the granter and grantee. +// If there is none, it returns nil, nil. +// Returns an error on parsing issues +func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) types.FeeAllowanceI { + grant, found := k.GetFeeGrant(ctx, granter, grantee) + if !found { + return nil + } + + return grant.GetFeeGrant() +} + +// GetFeeGrant returns entire grant between both accounts +func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) (types.FeeAllowanceGrant, bool) { + store := ctx.KVStore(k.storeKey) + key := types.FeeAllowanceKey(granter, grantee) + bz := store.Get(key) + if len(bz) == 0 { + return types.FeeAllowanceGrant{}, false + } + + var feegrant types.FeeAllowanceGrant + k.cdc.MustUnmarshalBinaryBare(bz, &feegrant) + + return feegrant, true +} + +// IterateAllGranteeFeeAllowances iterates over all the grants from anyone to the given grantee. +// Callback to get all data, returns true to stop, false to keep reading +func (k Keeper) IterateAllGranteeFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress, cb func(types.FeeAllowanceGrant) bool) error { + store := ctx.KVStore(k.storeKey) + prefix := types.FeeAllowancePrefixByGrantee(grantee) + iter := sdk.KVStorePrefixIterator(store, prefix) + defer iter.Close() + + stop := false + for ; iter.Valid() && !stop; iter.Next() { + bz := iter.Value() + + var feeGrant types.FeeAllowanceGrant + k.cdc.MustUnmarshalBinaryBare(bz, &feeGrant) + + stop = cb(feeGrant) + } + + return nil +} + +// IterateAllFeeAllowances iterates over all the grants in the store. +// Callback to get all data, returns true to stop, false to keep reading +// Calling this without pagination is very expensive and only designed for export genesis +func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(types.FeeAllowanceGrant) bool) error { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.FeeAllowanceKeyPrefix) + defer iter.Close() + + stop := false + for ; iter.Valid() && !stop; iter.Next() { + bz := iter.Value() + var feeGrant types.FeeAllowanceGrant + k.cdc.MustUnmarshalBinaryBare(bz, &feeGrant) + + stop = cb(feeGrant) + } + + return nil +} + +// UseGrantedFees will try to pay the given fee from the granter's account as requested by the grantee +func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) error { + grant, found := k.GetFeeGrant(ctx, granter, grantee) + if !found || grant.GetFeeGrant() == nil { + return sdkerrors.Wrapf(types.ErrNoAllowance, "grant missing") + } + + remove, err := grant.GetFeeGrant().Accept(fee, ctx.BlockTime(), ctx.BlockHeight()) + if err == nil { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeUseFeeGrant, + sdk.NewAttribute(types.AttributeKeyGranter, granter.String()), + sdk.NewAttribute(types.AttributeKeyGrantee, grantee.String()), + ), + ) + } + + if remove { + k.RevokeFeeAllowance(ctx, granter, grantee) + // note this returns nil if err == nil + return sdkerrors.Wrap(err, "removed grant") + } + + if err != nil { + return sdkerrors.Wrap(err, "invalid grant") + } + + // if we accepted, store the updated state of the allowance + return k.GrantFeeAllowance(ctx, granter, grantee, grant.GetFeeGrant()) +} diff --git a/x/feegrant/keeper/keeper_test.go b/x/feegrant/keeper/keeper_test.go new file mode 100644 index 000000000000..c14e141d31f0 --- /dev/null +++ b/x/feegrant/keeper/keeper_test.go @@ -0,0 +1,257 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +type KeeperTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + addrs []sdk.AccAddress +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +func (suite *KeeperTestSuite) SetupTest() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + suite.app = app + suite.ctx = ctx + suite.addrs = simapp.AddTestAddrsIncremental(app, ctx, 4, sdk.NewInt(30000000)) +} + +func (suite *KeeperTestSuite) TestKeeperCrud() { + ctx := suite.ctx + k := suite.app.FeeGrantKeeper + + // some helpers + atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) + eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) + basic := &types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(334455), + } + + basic2 := &types.BasicFeeAllowance{ + SpendLimit: eth, + Expiration: types.ExpiresAtHeight(172436), + } + + // let's set up some initial state here + err := k.GrantFeeAllowance(ctx, suite.addrs[0], suite.addrs[1], basic) + suite.Require().NoError(err) + + err = k.GrantFeeAllowance(ctx, suite.addrs[0], suite.addrs[2], basic2) + suite.Require().NoError(err) + + err = k.GrantFeeAllowance(ctx, suite.addrs[1], suite.addrs[2], basic) + suite.Require().NoError(err) + + err = k.GrantFeeAllowance(ctx, suite.addrs[1], suite.addrs[3], basic) + suite.Require().NoError(err) + + err = k.GrantFeeAllowance(ctx, suite.addrs[3], suite.addrs[0], basic2) + suite.Require().NoError(err) + + // remove some, overwrite other + k.RevokeFeeAllowance(ctx, suite.addrs[0], suite.addrs[1]) + k.RevokeFeeAllowance(ctx, suite.addrs[0], suite.addrs[2]) + + err = k.GrantFeeAllowance(ctx, suite.addrs[0], suite.addrs[2], basic) + suite.Require().NoError(err) + + err = k.GrantFeeAllowance(ctx, suite.addrs[1], suite.addrs[2], basic2) + suite.Require().NoError(err) + + // end state: + // addr -> addr3 (basic) + // addr2 -> addr3 (basic2), addr4(basic) + // addr4 -> addr (basic2) + + // then lots of queries + cases := map[string]struct { + grantee sdk.AccAddress + granter sdk.AccAddress + allowance types.FeeAllowanceI + }{ + "addr revoked": { + granter: suite.addrs[0], + grantee: suite.addrs[1], + }, + "addr revoked and added": { + granter: suite.addrs[0], + grantee: suite.addrs[2], + allowance: basic, + }, + "addr never there": { + granter: suite.addrs[0], + grantee: suite.addrs[3], + }, + "addr modified": { + granter: suite.addrs[1], + grantee: suite.addrs[2], + allowance: basic2, + }, + } + + for name, tc := range cases { + tc := tc + suite.Run(name, func() { + allow := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) + if tc.allowance == nil { + suite.Nil(allow) + return + } + suite.NotNil(allow) + suite.Equal(tc.allowance, allow) + }) + } + + grant1, err := types.NewFeeAllowanceGrant(suite.addrs[3], suite.addrs[0], basic2) + suite.NoError(err) + + grant2, err := types.NewFeeAllowanceGrant(suite.addrs[1], suite.addrs[2], basic2) + suite.NoError(err) + + grant3, err := types.NewFeeAllowanceGrant(suite.addrs[0], suite.addrs[2], basic) + suite.NoError(err) + + allCases := map[string]struct { + grantee sdk.AccAddress + grants []types.FeeAllowanceGrant + }{ + "addr2 has none": { + grantee: suite.addrs[1], + }, + "addr has one": { + grantee: suite.addrs[0], + grants: []types.FeeAllowanceGrant{ + grant1, + }, + }, + "addr3 has two": { + grantee: suite.addrs[2], + grants: []types.FeeAllowanceGrant{ + grant3, + grant2, + }, + }, + } + + for name, tc := range allCases { + tc := tc + suite.Run(name, func() { + var grants []types.FeeAllowanceGrant + err := k.IterateAllGranteeFeeAllowances(ctx, tc.grantee, func(grant types.FeeAllowanceGrant) bool { + grants = append(grants, grant) + return false + }) + suite.NoError(err) + suite.Equal(tc.grants, grants) + }) + } +} + +func (suite *KeeperTestSuite) TestUseGrantedFee() { + ctx := suite.ctx + k := suite.app.FeeGrantKeeper + + // some helpers + atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) + eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) + future := &types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(5678), + } + + expired := &types.BasicFeeAllowance{ + SpendLimit: eth, + Expiration: types.ExpiresAtHeight(55), + } + + // for testing limits of the contract + hugeAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 9999)) + _ = hugeAtom + smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1)) + _ = smallAtom + futureAfterSmall := &types.BasicFeeAllowance{ + SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 554)), + Expiration: types.ExpiresAtHeight(5678), + } + + // then lots of queries + cases := map[string]struct { + grantee sdk.AccAddress + granter sdk.AccAddress + fee sdk.Coins + allowed bool + final types.FeeAllowanceI + }{ + "use entire pot": { + granter: suite.addrs[0], + grantee: suite.addrs[1], + fee: atom, + allowed: true, + final: nil, + }, + "expired and removed": { + granter: suite.addrs[0], + grantee: suite.addrs[2], + fee: eth, + allowed: false, + final: nil, + }, + "too high": { + granter: suite.addrs[0], + grantee: suite.addrs[1], + fee: hugeAtom, + allowed: false, + final: future, + }, + "use a little": { + granter: suite.addrs[0], + grantee: suite.addrs[1], + fee: smallAtom, + allowed: true, + final: futureAfterSmall, + }, + } + + for name, tc := range cases { + tc := tc + suite.Run(name, func() { + // let's set up some initial state here + // addr -> addr2 (future) + // addr -> addr3 (expired) + + err := k.GrantFeeAllowance(ctx, suite.addrs[0], suite.addrs[1], future) + suite.Require().NoError(err) + + err = k.GrantFeeAllowance(ctx, suite.addrs[0], suite.addrs[3], expired) + suite.Require().NoError(err) + + err = k.UseGrantedFees(ctx, tc.granter, tc.grantee, tc.fee) + if tc.allowed { + suite.NoError(err) + } else { + suite.Error(err) + } + + loaded := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) + + suite.Equal(tc.final, loaded) + }) + } +} diff --git a/x/feegrant/keeper/msg_server.go b/x/feegrant/keeper/msg_server.go new file mode 100644 index 000000000000..3cd96e79ba88 --- /dev/null +++ b/x/feegrant/keeper/msg_server.go @@ -0,0 +1,72 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the feegrant MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(k Keeper) types.MsgServer { + return &msgServer{ + Keeper: k, + } +} + +var _ types.MsgServer = msgServer{} + +func (k msgServer) GrantFeeAllowance(goCtx context.Context, msg *types.MsgGrantFeeAllowance) (*types.MsgGrantFeeAllowanceResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + grantee, err := sdk.AccAddressFromBech32(msg.Grantee) + if err != nil { + return nil, err + } + + granter, err := sdk.AccAddressFromBech32(msg.Granter) + if err != nil { + return nil, err + } + + // Checking for duplicate entry + f := k.Keeper.GetFeeAllowance(ctx, granter, grantee) + if f != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee allowance already exists") + } + + err = k.Keeper.GrantFeeAllowance(ctx, granter, grantee, msg.GetFeeAllowanceI()) + if err != nil { + return nil, err + } + + return &types.MsgGrantFeeAllowanceResponse{}, nil +} + +func (k msgServer) RevokeFeeAllowance(goCtx context.Context, msg *types.MsgRevokeFeeAllowance) (*types.MsgRevokeFeeAllowanceResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + grantee, err := sdk.AccAddressFromBech32(msg.Grantee) + if err != nil { + return nil, err + } + + granter, err := sdk.AccAddressFromBech32(msg.Granter) + if err != nil { + return nil, err + } + + err = k.Keeper.RevokeFeeAllowance(ctx, granter, grantee) + if err != nil { + return nil, err + } + + return &types.MsgRevokeFeeAllowanceResponse{}, nil +} diff --git a/x/feegrant/module.go b/x/feegrant/module.go new file mode 100644 index 000000000000..31b08e6baab1 --- /dev/null +++ b/x/feegrant/module.go @@ -0,0 +1,213 @@ +package feegrant + +import ( + "context" + "encoding/json" + "math/rand" + + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + sdkclient "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "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/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/feegrant/client/cli" + "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + "github.com/cosmos/cosmos-sdk/x/feegrant/simulation" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic defines the basic application module used by the feegrant module. +type AppModuleBasic struct { + cdc codec.Marshaler +} + +// Name returns the feegrant module's name. +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterServices registers a gRPC query service to respond to the +// module-specific gRPC queries. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + +// RegisterLegacyAminoCodec registers the feegrant module's types for the given codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { +} + +// RegisterInterfaces registers the feegrant module's interface types +func (AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +// LegacyQuerierHandler returns the feegrant module sdk.Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return nil +} + +// DefaultGenesis returns default genesis state as raw bytes for the feegrant +// module. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) +} + +// ValidateGenesis performs genesis state validation for the feegrant module. +func (a AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config sdkclient.TxEncodingConfig, bz json.RawMessage) error { + var data types.GenesisState + if err := cdc.UnmarshalJSON(bz, &data); err != nil { + sdkerrors.Wrapf(err, "failed to unmarshal %s genesis state", types.ModuleName) + } + + return types.ValidateGenesis(data) +} + +// RegisterRESTRoutes registers the REST routes for the feegrant module. +func (AppModuleBasic) RegisterRESTRoutes(ctx sdkclient.Context, rtr *mux.Router) {} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the feegrant module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx sdkclient.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) +} + +// GetTxCmd returns the root tx command for the feegrant module. +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns no root query command for the feegrant module. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +// AppModule implements an application module for the feegrant module. +type AppModule struct { + AppModuleBasic + keeper keeper.Keeper + accountKeeper types.AccountKeeper + bankKeeper types.BankKeeper + registry cdctypes.InterfaceRegistry +} + +// NewAppModule creates a new AppModule object +func NewAppModule(cdc codec.Marshaler, ak types.AccountKeeper, bk types.BankKeeper, keeper keeper.Keeper, registry cdctypes.InterfaceRegistry) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{cdc: cdc}, + keeper: keeper, + accountKeeper: ak, + bankKeeper: bk, + registry: registry, + } +} + +// Name returns the feegrant module's name. +func (AppModule) Name() string { + return types.ModuleName +} + +// RegisterInvariants registers the feegrant module invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} + +// Route returns the message routing key for the feegrant module. +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(types.RouterKey, nil) +} + +// NewHandler returns an sdk.Handler for the feegrant module. +func (am AppModule) NewHandler() sdk.Handler { + return nil +} + +// QuerierRoute returns the feegrant module's querier route name. +func (AppModule) QuerierRoute() string { + return "" +} + +// InitGenesis performs genesis initialization for the feegrant module. It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, bz json.RawMessage) []abci.ValidatorUpdate { + var gs types.GenesisState + cdc.MustUnmarshalJSON(bz, &gs) + + InitGenesis(ctx, am.keeper, &gs) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the feegrant +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + gs, err := ExportGenesis(ctx, am.keeper) + if err != nil { + panic(err) + } + + return cdc.MustMarshalJSON(gs) +} + +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + +// BeginBlock returns the begin blocker for the feegrant module. +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock returns the end blocker for the feegrant module. It returns no validator +// updates. +func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +//____________________________________________________________________________ + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the feegrant module. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents returns all the feegrant content functions used to +// simulate governance proposals. +func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RandomizedParams creates randomized feegrant param changes for the simulator. +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { + return nil +} + +// RegisterStoreDecoder registers a decoder for feegrant module's types +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc) +} + +// WeightedOperations returns all the feegrant module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + protoCdc := codec.NewProtoCodec(am.registry) + return simulation.WeightedOperations( + simState.AppParams, simState.Cdc, am.accountKeeper, am.bankKeeper, am.keeper, protoCdc, + ) +} diff --git a/x/feegrant/simulation/decoder.go b/x/feegrant/simulation/decoder.go new file mode 100644 index 000000000000..46e35594fdf8 --- /dev/null +++ b/x/feegrant/simulation/decoder.go @@ -0,0 +1,26 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding feegrant type. +func NewDecodeStore(cdc codec.Marshaler) func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.FeeAllowanceKeyPrefix): + var grantA, grantB types.FeeAllowanceGrant + cdc.MustUnmarshalBinaryBare(kvA.Value, &grantA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &grantB) + return fmt.Sprintf("%v\n%v", grantA, grantB) + default: + panic(fmt.Sprintf("invalid feegrant key %X", kvA.Key)) + } + } +} diff --git a/x/feegrant/simulation/decoder_test.go b/x/feegrant/simulation/decoder_test.go new file mode 100644 index 000000000000..887111845636 --- /dev/null +++ b/x/feegrant/simulation/decoder_test.go @@ -0,0 +1,63 @@ +package simulation_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/feegrant/simulation" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +var ( + granterPk = ed25519.GenPrivKey().PubKey() + granterAddr = sdk.AccAddress(granterPk.Address()) + granteePk = ed25519.GenPrivKey().PubKey() + granteeAddr = sdk.AccAddress(granterPk.Address()) +) + +func TestDecodeStore(t *testing.T) { + cdc := simapp.MakeTestEncodingConfig().Marshaler + dec := simulation.NewDecodeStore(cdc) + + grant, err := types.NewFeeAllowanceGrant(granterAddr, granteeAddr, &types.BasicFeeAllowance{ + SpendLimit: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(100))), + }) + + require.NoError(t, err) + + grantBz, err := cdc.MarshalBinaryBare(&grant) + require.NoError(t, err) + + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + {Key: []byte(types.FeeAllowanceKeyPrefix), Value: grantBz}, + {Key: []byte{0x99}, Value: []byte{0x99}}, + }, + } + + tests := []struct { + name string + expectedLog string + }{ + {"Grant", fmt.Sprintf("%v\n%v", grant, grant)}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, 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) + } + }) + } +} diff --git a/x/feegrant/simulation/genesis.go b/x/feegrant/simulation/genesis.go new file mode 100644 index 000000000000..f9f4c7e12748 --- /dev/null +++ b/x/feegrant/simulation/genesis.go @@ -0,0 +1,38 @@ +package simulation + +import ( + "encoding/json" + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +// Simulation parameter constants +const feegrant = "feegrant" + +// GenFeeGrants returns an empty slice of evidences. +func GenFeeGrants(_ *rand.Rand, _ []simtypes.Account) []types.FeeAllowanceGrant { + return []types.FeeAllowanceGrant{} +} + +// RandomizedGenState generates a random GenesisState for feegrant +func RandomizedGenState(simState *module.SimulationState) { + var feegrants []types.FeeAllowanceGrant + + simState.AppParams.GetOrGenerate( + simState.Cdc, feegrant, &feegrants, simState.Rand, + func(r *rand.Rand) { feegrants = GenFeeGrants(r, simState.Accounts) }, + ) + feegrantGenesis := types.NewGenesisState(feegrants) + + bz, err := json.MarshalIndent(&feegrantGenesis, "", " ") + if err != nil { + panic(err) + } + + fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, bz) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(feegrantGenesis) +} diff --git a/x/feegrant/simulation/genesis_test.go b/x/feegrant/simulation/genesis_test.go new file mode 100644 index 000000000000..3d00ba3ecec3 --- /dev/null +++ b/x/feegrant/simulation/genesis_test.go @@ -0,0 +1,40 @@ +package simulation_test + +import ( + "encoding/json" + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/feegrant/simulation" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +func TestRandomizedGenState(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + + s := rand.NewSource(1) + r := rand.New(s) + + simState := module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + NumBonded: 3, + Accounts: simtypes.RandomAccounts(r, 3), + InitialStake: 1000, + GenState: make(map[string]json.RawMessage), + } + + simulation.RandomizedGenState(&simState) + var feegrantGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &feegrantGenesis) + + require.Len(t, feegrantGenesis.FeeAllowances, 0) +} diff --git a/x/feegrant/simulation/operations.go b/x/feegrant/simulation/operations.go new file mode 100644 index 000000000000..54821873b6b6 --- /dev/null +++ b/x/feegrant/simulation/operations.go @@ -0,0 +1,200 @@ +package simulation + +import ( + "context" + "math/rand" + "time" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp/helpers" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" + "github.com/cosmos/cosmos-sdk/x/simulation" +) + +// Simulation operation weights constants +const ( + OpWeightMsgGrantFeeAllowance = "op_weight_msg_grant_fee_allowance" + OpWeightMsgRevokeFeeAllowance = "op_weight_msg_grant_revoke_allowance" + TypeMsgGrantFeeAllowance = "/cosmos.feegrant.v1beta1.Msg/GrantFeeAllowance" + TypeMsgRevokeFeeAllowance = "/cosmos.feegrant.v1beta1.Msg/RevokeFeeAllowance" +) + +func WeightedOperations( + appParams simtypes.AppParams, cdc codec.JSONMarshaler, + ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, + protoCdc *codec.ProtoCodec, +) simulation.WeightedOperations { + + var ( + weightMsgGrantFeeAllowance int + weightMsgRevokeFeeAllowance int + ) + + appParams.GetOrGenerate(cdc, OpWeightMsgGrantFeeAllowance, &weightMsgGrantFeeAllowance, nil, + func(_ *rand.Rand) { + weightMsgGrantFeeAllowance = simappparams.DefaultWeightGrantFeeAllowance + }, + ) + + appParams.GetOrGenerate(cdc, OpWeightMsgRevokeFeeAllowance, &weightMsgRevokeFeeAllowance, nil, + func(_ *rand.Rand) { + weightMsgRevokeFeeAllowance = simappparams.DefaultWeightRevokeFeeAllowance + }, + ) + + return simulation.WeightedOperations{ + simulation.NewWeightedOperation( + weightMsgGrantFeeAllowance, + SimulateMsgGrantFeeAllowance(ak, bk, k, protoCdc), + ), + simulation.NewWeightedOperation( + weightMsgRevokeFeeAllowance, + SimulateMsgRevokeFeeAllowance(ak, bk, k, protoCdc), + ), + } +} + +// SimulateMsgGrantFeeAllowance generates MsgGrantFeeAllowance with random values. +func SimulateMsgGrantFeeAllowance(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, protoCdc *codec.ProtoCodec) simtypes.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + granter, _ := simtypes.RandomAcc(r, accs) + grantee, _ := simtypes.RandomAcc(r, accs) + if grantee.Address.String() == granter.Address.String() { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgGrantFeeAllowance, "grantee and granter cannot be same"), nil, nil + } + + f := k.GetFeeAllowance(ctx, granter.Address, grantee.Address) + if f != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgGrantFeeAllowance, "fee allowance exists"), nil, nil + } + + account := ak.GetAccount(ctx, granter.Address) + + spendableCoins := bk.SpendableCoins(ctx, account.GetAddress()) + fees, err := simtypes.RandomFees(r, ctx, spendableCoins) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgGrantFeeAllowance, err.Error()), nil, err + } + + spendableCoins = spendableCoins.Sub(fees) + if spendableCoins.Empty() { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgGrantFeeAllowance, "unable to grant empty coins as SpendLimit"), nil, nil + } + + msg, err := types.NewMsgGrantFeeAllowance(&types.BasicFeeAllowance{ + SpendLimit: spendableCoins, + Expiration: types.ExpiresAtTime(ctx.BlockTime().Add(30 * time.Hour)), + }, granter.Address, grantee.Address) + + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgGrantFeeAllowance, err.Error()), nil, err + } + txGen := simappparams.MakeTestEncodingConfig().TxConfig + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + feegrantMsgClient := types.NewMsgClient(svcMsgClientConn) + _, err = feegrantMsgClient.GrantFeeAllowance(context.Background(), msg) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgGrantFeeAllowance, err.Error()), nil, err + } + tx, err := helpers.GenTx( + txGen, + svcMsgClientConn.GetMsgs(), + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{account.GetAccountNumber()}, + []uint64{account.GetSequence()}, + granter.PrivKey, + ) + + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgGrantFeeAllowance, "unable to generate mock tx"), nil, err + } + + _, _, err = app.Deliver(txGen.TxEncoder(), tx) + + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, svcMsgClientConn.GetMsgs()[0].Type(), "unable to deliver tx"), nil, err + } + return simtypes.NewOperationMsg(svcMsgClientConn.GetMsgs()[0], true, "", protoCdc), nil, err + } +} + +// SimulateMsgRevokeFeeAllowance generates a MsgRevokeFeeAllowance with random values. +func SimulateMsgRevokeFeeAllowance(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, protoCdc *codec.ProtoCodec) simtypes.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + + hasGrant := false + var granterAddr sdk.AccAddress + var granteeAddr sdk.AccAddress + k.IterateAllFeeAllowances(ctx, func(grant types.FeeAllowanceGrant) bool { + + granter, err := sdk.AccAddressFromBech32(grant.Granter) + if err != nil { + panic(err) + } + grantee, err := sdk.AccAddressFromBech32(grant.Grantee) + if err != nil { + panic(err) + } + granterAddr = granter + granteeAddr = grantee + hasGrant = true + return true + }) + + if !hasGrant { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgRevokeFeeAllowance, "no grants"), nil, nil + } + granter, ok := simtypes.FindAccount(accs, granterAddr) + + if !ok { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgRevokeFeeAllowance, "Account not found"), nil, nil + } + + account := ak.GetAccount(ctx, granter.Address) + spendableCoins := bk.SpendableCoins(ctx, account.GetAddress()) + fees, err := simtypes.RandomFees(r, ctx, spendableCoins) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgRevokeFeeAllowance, err.Error()), nil, err + } + + msg := types.NewMsgRevokeFeeAllowance(granterAddr, granteeAddr) + + txGen := simappparams.MakeTestEncodingConfig().TxConfig + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + feegrantMsgClient := types.NewMsgClient(svcMsgClientConn) + _, err = feegrantMsgClient.RevokeFeeAllowance(context.Background(), &msg) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgGrantFeeAllowance, err.Error()), nil, err + } + + tx, err := helpers.GenTx( + txGen, + svcMsgClientConn.GetMsgs(), + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{account.GetAccountNumber()}, + []uint64{account.GetSequence()}, + granter.PrivKey, + ) + + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgRevokeFeeAllowance, err.Error()), nil, err + } + + _, _, err = app.Deliver(txGen.TxEncoder(), tx) + return simtypes.NewOperationMsg(svcMsgClientConn.GetMsgs()[0], true, "", protoCdc), nil, err + } +} diff --git a/x/feegrant/simulation/operations_test.go b/x/feegrant/simulation/operations_test.go new file mode 100644 index 000000000000..85db81f2ce82 --- /dev/null +++ b/x/feegrant/simulation/operations_test.go @@ -0,0 +1,169 @@ +package simulation_test + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/suite" + + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + sdk "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/feegrant/simulation" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +type SimTestSuite struct { + suite.Suite + + ctx sdk.Context + app *simapp.SimApp + protoCdc *codec.ProtoCodec +} + +func (suite *SimTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + suite.app = app + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{}) + suite.protoCdc = codec.NewProtoCodec(suite.app.InterfaceRegistry()) + +} + +func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Account { + accounts := simtypes.RandomAccounts(r, n) + + initAmt := sdk.TokensFromConsensusPower(200) + initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt)) + + // add coins to the accounts + for _, account := range accounts { + err := simapp.FundAccount(suite.app, suite.ctx, account.Address, initCoins) + suite.Require().NoError(err) + } + + return accounts +} + +func (suite *SimTestSuite) TestWeightedOperations() { + app, ctx := suite.app, suite.ctx + require := suite.Require() + + ctx.WithChainID("test-chain") + + cdc := app.AppCodec() + appParams := make(simtypes.AppParams) + + weightesOps := simulation.WeightedOperations( + appParams, cdc, app.AccountKeeper, + app.BankKeeper, app.FeeGrantKeeper, + suite.protoCdc, + ) + + s := rand.NewSource(1) + r := rand.New(s) + accs := suite.getTestingAccounts(r, 3) + + expected := []struct { + weight int + opMsgRoute string + opMsgName string + }{ + { + simappparams.DefaultWeightGrantFeeAllowance, + types.ModuleName, + simulation.TypeMsgGrantFeeAllowance, + }, + { + simappparams.DefaultWeightRevokeFeeAllowance, + types.ModuleName, + simulation.TypeMsgRevokeFeeAllowance, + }, + } + + for i, w := range weightesOps { + operationMsg, _, _ := w.Op()(r, app.BaseApp, ctx, accs, ctx.ChainID()) + // the following checks are very much dependent from the ordering of the output given + // by WeightedOperations. if the ordering in WeightedOperations changes some tests + // will fail + require.Equal(expected[i].weight, w.Weight(), "weight should be the same") + require.Equal(expected[i].opMsgRoute, operationMsg.Route, "route should be the same") + require.Equal(expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") + } +} + +func (suite *SimTestSuite) TestSimulateMsgGrantFeeAllowance() { + app, ctx := suite.app, suite.ctx + require := suite.Require() + + s := rand.NewSource(1) + r := rand.New(s) + accounts := suite.getTestingAccounts(r, 3) + + // begin a new block + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash}}) + + // execute operation + op := simulation.SimulateMsgGrantFeeAllowance(app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, suite.protoCdc) + operationMsg, futureOperations, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(err) + + var msg types.MsgGrantFeeAllowance + suite.app.AppCodec().UnmarshalJSON(operationMsg.Msg, &msg) + + require.True(operationMsg.OK) + require.Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Granter) + require.Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.Grantee) + require.Len(futureOperations, 0) +} + +func (suite *SimTestSuite) TestSimulateMsgRevokeFeeAllowance() { + app, ctx := suite.app, suite.ctx + require := suite.Require() + + s := rand.NewSource(1) + r := rand.New(s) + accounts := suite.getTestingAccounts(r, 3) + + // begin a new block + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash}}) + + feeAmt := sdk.TokensFromConsensusPower(200000) + feeCoins := sdk.NewCoins(sdk.NewCoin("foo", feeAmt)) + + granter, grantee := accounts[0], accounts[1] + + err := app.FeeGrantKeeper.GrantFeeAllowance( + ctx, + granter.Address, + grantee.Address, + &types.BasicFeeAllowance{ + SpendLimit: feeCoins, + Expiration: types.ExpiresAtTime(ctx.BlockTime().Add(30 * time.Hour)), + }, + ) + require.NoError(err) + + // execute operation + op := simulation.SimulateMsgRevokeFeeAllowance(app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, suite.protoCdc) + operationMsg, futureOperations, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(err) + + var msg types.MsgRevokeFeeAllowance + suite.app.AppCodec().UnmarshalJSON(operationMsg.Msg, &msg) + + require.True(operationMsg.OK) + require.Equal(granter.Address.String(), msg.Granter) + require.Equal(grantee.Address.String(), msg.Grantee) + require.Len(futureOperations, 0) +} + +func TestSimTestSuite(t *testing.T) { + suite.Run(t, new(SimTestSuite)) +} diff --git a/x/feegrant/spec/01_concepts.md b/x/feegrant/spec/01_concepts.md new file mode 100644 index 000000000000..92200f2d4797 --- /dev/null +++ b/x/feegrant/spec/01_concepts.md @@ -0,0 +1,70 @@ + + +# Concepts + +## FeeAllowanceGrant + +`FeeAllowanceGrant` is stored in the KVStore to record a grant with full context. Every grant will contain `granter`, `grantee` and what kind of `allowance` is granted. `granter` is an account address who is giving permission to `grantee` (the beneficiary account address) to pay for some or all of `grantee`'s transaction fees. `allowance` defines what kind of fee allowance (`BasicFeeAllowance` or `PeriodicFeeAllowance`, see below) is granted to `grantee`. `allowance` accepts an interface which implements `FeeAllowanceI`, encoded as `Any` type. There can be only one existing fee grant allowed for a `grantee` and `granter`, self grants are not allowed. + ++++ https://github.com/cosmos/cosmos-sdk/blob/d97e7907f176777ed8a464006d360bb3e1a223e4/proto/cosmos/feegrant/v1beta1/feegrant.proto#L75-L81 + +`FeeAllowanceI` looks like: + ++++ https://github.com/cosmos/cosmos-sdk/blob/d97e7907f176777ed8a464006d360bb3e1a223e4/x/feegrant/types/fees.go#L9-L32 + +## Fee Allowance types +There are two types of fee allowances present at the moment: +- `BasicFeeAllowance` +- `PeriodicFeeAllowance` + +## BasicFeeAllowance + +`BasicFeeAllowance` is permission for `grantee` to use fee from a `granter`'s account. If any of the `spend_limit` or `expiration` reaches its limit, the grant will be removed from the state. + + ++++ https://github.com/cosmos/cosmos-sdk/blob/d97e7907f176777ed8a464006d360bb3e1a223e4/proto/cosmos/feegrant/v1beta1/feegrant.proto#L13-L26 + +- `spend_limit` is the limit of coins that are allowed to be used from the `granter` account. If it is empty, it assumes there's no spend limit, `grantee` can use any number of available tokens from `granter` account address before the expiration. + +- `expiration` specifies an optional time when this allowance expires. If the value is left empty, there is no expiry for the grant. + +- When a grant is created with empty values for `spend_limit` and `expiration`, it is still a valid grant. It won't restrict the `grantee` to use any number of tokens from `granter` and it won't have any expiration. The only way to restrict the `grantee` is by revoking the grant. + +## PeriodicFeeAllowance + +`PeriodicFeeAllowance` is a repeating fee allowance for the mentioned period, we can mention when the grant can expire as well as when a period can reset. We can also define the maximum number of coins that can be used in a mentioned period of time. + ++++ https://github.com/cosmos/cosmos-sdk/blob/d97e7907f176777ed8a464006d360bb3e1a223e4/proto/cosmos/feegrant/v1beta1/feegrant.proto#L28-L73 + +- `basic` is the instance of `BasicFeeAllowance` which is optional for periodic fee allowance. If empty, the grant will have no `expiration` and no `spend_limit`. + +- `period` is the specific period of time or blocks, after each period passes, `period_spend_limit` will be reset. + +- `period_spend_limit` specifies the maximum number of coins that can be spent in the period. + +- `period_can_spend` is the number of coins left to be spent before the period_reset time. + +- `period_reset` keeps track of when a next period reset should happen. + +## FeeAccount flag + +`feegrant` module introduces a `FeeAccount` flag for CLI for the sake of executing transactions with fee granter. When this flag is set, `clientCtx` will append the granter account address for transactions generated through CLI. + ++++ https://github.com/cosmos/cosmos-sdk/blob/d97e7907f176777ed8a464006d360bb3e1a223e4/client/cmd.go#L224-L235 + ++++ https://github.com/cosmos/cosmos-sdk/blob/d97e7907f176777ed8a464006d360bb3e1a223e4/client/tx/tx.go#L120 + ++++ https://github.com/cosmos/cosmos-sdk/blob/d97e7907f176777ed8a464006d360bb3e1a223e4/x/auth/tx/builder.go#L268-L277 + ++++ https://github.com/cosmos/cosmos-sdk/blob/d97e7907f176777ed8a464006d360bb3e1a223e4/proto/cosmos/tx/v1beta1/tx.proto#L160-L181 + +Example cmd: +```go +./simd tx gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --from validator-key --fee-account=cosmos1xh44hxt7spr67hqaa7nyx5gnutrz5fraw6grxn --chain-id=testnet --fees="10stake" +``` + +## DeductGrantedFeeDecorator + +`feegrant` module also adds a `DeductGrantedFeeDecorator` ante handler. Whenever a transaction is being executed with `granter` field set, then this ante handler will check whether `payer` and `granter` have proper fee allowance grant in state. If it exists the fees will be deducted from the `granter`'s account address. If the `granter` field isn't set then this ante handler works as normal fee deductor. diff --git a/x/feegrant/spec/02_state.md b/x/feegrant/spec/02_state.md new file mode 100644 index 000000000000..463ea70d0685 --- /dev/null +++ b/x/feegrant/spec/02_state.md @@ -0,0 +1,15 @@ + + +# State + +## FeeAllowance + +Fee Allowances are identified by combining `Grantee` (the account address of fee allowance grantee) with the `Granter` (the account address of fee allowance granter). + +Fee allowances are stored in the state as follows: + +- FeeAllowance: `0x00 | grantee_addr_len (1 byte) | grantee_addr_bytes | granter_addr_len (1 byte) | granter_addr_bytes -> ProtocolBuffer(FeeAllowance)` + ++++ https://github.com/cosmos/cosmos-sdk/blob/d97e7907f176777ed8a464006d360bb3e1a223e4/x/feegrant/types/feegrant.pb.go#L358-L363 diff --git a/x/feegrant/spec/03_messages.md b/x/feegrant/spec/03_messages.md new file mode 100644 index 000000000000..3ca65e25cd3f --- /dev/null +++ b/x/feegrant/spec/03_messages.md @@ -0,0 +1,17 @@ + + +# Messages + +## Msg/GrantFeeAllowance + +A fee allowance grant will be created with the `MsgGrantFeeAllowance` message. + ++++ https://github.com/cosmos/cosmos-sdk/blob/d97e7907f176777ed8a464006d360bb3e1a223e4/proto/cosmos/feegrant/v1beta1/tx.proto#L22-L28 + +## Msg/RevokeFeeAllowance + +An allowed grant fee allowance can be removed with the `MsgRevokeFeeAllowance` message. + ++++ https://github.com/cosmos/cosmos-sdk/blob/d97e7907f176777ed8a464006d360bb3e1a223e4/proto/cosmos/feegrant/v1beta1/tx.proto#L33-L37 diff --git a/x/feegrant/spec/04_events.md b/x/feegrant/spec/04_events.md new file mode 100644 index 000000000000..021142d3904b --- /dev/null +++ b/x/feegrant/spec/04_events.md @@ -0,0 +1,33 @@ + + +# Events + +The feegrant module emits the following events: + +# Handlers + +### MsgGrantFeeAllowance + +| Type | Attribute Key | Attribute Value | +| -------- | ------------- | ------------------ | +| message | action | set_feegrant | +| message | granter | {granterAddress} | +| message | grantee | {granteeAddress} | + +### MsgRevokeFeeAllowance + +| Type | Attribute Key | Attribute Value | +| -------- | ------------- | ------------------ | +| message | action | revoke_feegrant | +| message | granter | {granterAddress} | +| message | grantee | {granteeAddress} | + +### Exec fee allowance + +| Type | Attribute Key | Attribute Value | +| -------- | ------------- | ------------------ | +| message | action | use_feegrant | +| message | granter | {granterAddress} | +| message | grantee | {granteeAddress} | \ No newline at end of file diff --git a/x/feegrant/spec/README.md b/x/feegrant/spec/README.md new file mode 100644 index 000000000000..1abdcf464b14 --- /dev/null +++ b/x/feegrant/spec/README.md @@ -0,0 +1,32 @@ + + +## Abstract + +This document specifies the feegrant module. For the full ADR, please see [Fee Grant ADR-029](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/docs/architecture/adr-029-fee-grant-module.md). + +This module allows accounts to grant fee allowances and to use fees from their accounts. Grantees can execute any transaction without the need to maintain sufficient fees. + +## Contents + +1. **[Concepts](01_concepts.md)** + - [FeeAllowanceGrant](01_concepts.md#feeallowancegrant) + - [Fee Allowance types](01_concepts.md#fee-allowance-types) + - [BasicFeeAllowance](01_concepts.md#basicfeeallowance) + - [PeriodicFeeAllowance](01_concepts.md#periodicfeeallowance) + - [FeeAccount flag](01_concepts.md#feeaccount-flag) + - [DeductGrantedFeeDecorator](01_concepts.md#deductgrantedfeedecorator) +2. **[State](02_state.md)** + - [FeeAllowance](02_state.md#feeallowance) +3. **[Messages](03_messages.md)** + - [Msg/GrantFeeAllowance](03_messages.md#msggrantfeeallowance) + - [Msg/RevokeFeeAllowance](03_messages.md#msgrevokefeeallowance) +3. **[Events](04_events.md)** + - [MsgGrantFeeAllowance](04_events.md#msggrantfeeallowance) + - [MsgrevokeFeeAllowance](04_events.md#msgrevokefeeallowance) + - [Exec fee allowance](04_events.md#exec-fee-allowance) + diff --git a/x/feegrant/types/basic_fee.go b/x/feegrant/types/basic_fee.go new file mode 100644 index 000000000000..6d970680510f --- /dev/null +++ b/x/feegrant/types/basic_fee.go @@ -0,0 +1,61 @@ +package types + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var _ FeeAllowanceI = (*BasicFeeAllowance)(nil) + +// Accept can use fee payment requested as well as timestamp/height of the current block +// to determine whether or not to process this. This is checked in +// Keeper.UseGrantedFees and the return values should match how it is handled there. +// +// If it returns an error, the fee payment is rejected, otherwise it is accepted. +// The FeeAllowance implementation is expected to update it's internal state +// and will be saved again after an acceptance. +// +// If remove is true (regardless of the error), the FeeAllowance will be deleted from storage +// (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees) +func (a *BasicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (bool, error) { + if a.Expiration.IsExpired(&blockTime, blockHeight) { + return true, sdkerrors.Wrap(ErrFeeLimitExpired, "basic allowance") + } + + if a.SpendLimit != nil { + left, invalid := a.SpendLimit.SafeSub(fee) + if invalid { + return false, sdkerrors.Wrap(ErrFeeLimitExceeded, "basic allowance") + } + + a.SpendLimit = left + return left.IsZero(), nil + } + + return false, nil +} + +// PrepareForExport will adjust the expiration based on export time. In particular, +// it will subtract the dumpHeight from any height-based expiration to ensure that +// the elapsed number of blocks this allowance is valid for is fixed. +func (a *BasicFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceI { + return &BasicFeeAllowance{ + SpendLimit: a.SpendLimit, + Expiration: a.Expiration.PrepareForExport(dumpTime, dumpHeight), + } +} + +// ValidateBasic implements FeeAllowance and enforces basic sanity checks +func (a BasicFeeAllowance) ValidateBasic() error { + if a.SpendLimit != nil { + if !a.SpendLimit.IsValid() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "send amount is invalid: %s", a.SpendLimit) + } + if !a.SpendLimit.IsAllPositive() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "spend limit must be positive") + } + } + return a.Expiration.ValidateBasic() +} diff --git a/x/feegrant/types/basic_fee_test.go b/x/feegrant/types/basic_fee_test.go new file mode 100644 index 000000000000..dd3f09590f8a --- /dev/null +++ b/x/feegrant/types/basic_fee_test.go @@ -0,0 +1,141 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +func TestBasicFeeValidAllow(t *testing.T) { + eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 10)) + atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) + smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43)) + bigAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1000)) + leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512)) + + cases := map[string]struct { + allow *types.BasicFeeAllowance + // all other checks are ignored if valid=false + fee sdk.Coins + blockTime time.Time + blockHeight int64 + valid bool + accept bool + remove bool + remains sdk.Coins + }{ + "empty": { + allow: &types.BasicFeeAllowance{}, + valid: true, + accept: true, + }, + "small fee without expire": { + allow: &types.BasicFeeAllowance{ + SpendLimit: atom, + }, + valid: true, + fee: smallAtom, + accept: true, + remove: false, + remains: leftAtom, + }, + "all fee without expire": { + allow: &types.BasicFeeAllowance{ + SpendLimit: smallAtom, + }, + valid: true, + fee: smallAtom, + accept: true, + remove: true, + }, + "wrong fee": { + allow: &types.BasicFeeAllowance{ + SpendLimit: smallAtom, + }, + valid: true, + fee: eth, + accept: false, + }, + "non-expired": { + allow: &types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }, + valid: true, + fee: smallAtom, + blockHeight: 85, + accept: true, + remove: false, + remains: leftAtom, + }, + "expired": { + allow: &types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }, + valid: true, + fee: smallAtom, + blockHeight: 121, + accept: false, + remove: true, + }, + "fee more than allowed": { + allow: &types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }, + valid: true, + fee: bigAtom, + blockHeight: 85, + accept: false, + }, + "with out spend limit": { + allow: &types.BasicFeeAllowance{ + Expiration: types.ExpiresAtHeight(100), + }, + valid: true, + fee: bigAtom, + blockHeight: 85, + accept: true, + }, + "expired no spend limit": { + allow: &types.BasicFeeAllowance{ + Expiration: types.ExpiresAtHeight(100), + }, + valid: true, + fee: bigAtom, + blockHeight: 120, + accept: false, + }, + } + + for name, stc := range cases { + tc := stc // to make scopelint happy + t.Run(name, func(t *testing.T) { + err := tc.allow.ValidateBasic() + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + + // now try to deduct + remove, err := tc.allow.Accept(tc.fee, tc.blockTime, tc.blockHeight) + if !tc.accept { + require.Error(t, err) + return + } + require.NoError(t, err) + + require.Equal(t, tc.remove, remove) + if !remove { + assert.Equal(t, tc.allow.SpendLimit, tc.remains) + } + }) + } +} diff --git a/x/feegrant/types/codec.go b/x/feegrant/types/codec.go new file mode 100644 index 000000000000..686187cd7510 --- /dev/null +++ b/x/feegrant/types/codec.go @@ -0,0 +1,24 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +// RegisterInterfaces registers the interfaces types with the interface registry +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.MsgRequest)(nil), + &MsgGrantFeeAllowance{}, + &MsgRevokeFeeAllowance{}, + ) + + registry.RegisterInterface( + "cosmos.feegrant.v1beta1.FeeAllowanceI", + (*FeeAllowanceI)(nil), + &BasicFeeAllowance{}, + &PeriodicFeeAllowance{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} diff --git a/x/feegrant/types/errors.go b/x/feegrant/types/errors.go new file mode 100644 index 000000000000..183f681ca5dc --- /dev/null +++ b/x/feegrant/types/errors.go @@ -0,0 +1,21 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// Codes for governance errors +const ( + DefaultCodespace = ModuleName +) + +var ( + // ErrFeeLimitExceeded error if there are not enough allowance to cover the fees + ErrFeeLimitExceeded = sdkerrors.Register(DefaultCodespace, 2, "fee limit exceeded") + // ErrFeeLimitExpired error if the allowance has expired + ErrFeeLimitExpired = sdkerrors.Register(DefaultCodespace, 3, "fee allowance expired") + // ErrInvalidDuration error if the Duration is invalid or doesn't match the expiration + ErrInvalidDuration = sdkerrors.Register(DefaultCodespace, 4, "invalid duration") + // ErrNoAllowance error if there is no allowance for that pair + ErrNoAllowance = sdkerrors.Register(DefaultCodespace, 5, "no allowance") +) diff --git a/x/feegrant/types/events.go b/x/feegrant/types/events.go new file mode 100644 index 000000000000..b82ccb7b1cd8 --- /dev/null +++ b/x/feegrant/types/events.go @@ -0,0 +1,13 @@ +package types + +// evidence module events +const ( + EventTypeUseFeeGrant = "use_feegrant" + EventTypeRevokeFeeGrant = "revoke_feegrant" + EventTypeSetFeeGrant = "set_feegrant" + + AttributeKeyGranter = "granter" + AttributeKeyGrantee = "grantee" + + AttributeValueCategory = ModuleName +) diff --git a/x/feegrant/types/expected_keepers.go b/x/feegrant/types/expected_keepers.go new file mode 100644 index 000000000000..40966b06d47f --- /dev/null +++ b/x/feegrant/types/expected_keepers.go @@ -0,0 +1,23 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" + // supply "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// AccountKeeper defines the expected auth Account Keeper (noalias) +type AccountKeeper interface { + GetModuleAddress(moduleName string) sdk.AccAddress + GetModuleAccount(ctx sdk.Context, moduleName string) auth.ModuleAccountI + + NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) auth.AccountI + GetAccount(ctx sdk.Context, addr sdk.AccAddress) auth.AccountI + SetAccount(ctx sdk.Context, acc auth.AccountI) +} + +// BankKeeper defines the expected supply Keeper (noalias) +type BankKeeper interface { + SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error +} diff --git a/x/feegrant/types/expiration.go b/x/feegrant/types/expiration.go new file mode 100644 index 000000000000..4922b6d481d0 --- /dev/null +++ b/x/feegrant/types/expiration.go @@ -0,0 +1,138 @@ +package types + +import ( + "time" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// ExpiresAtTime creates an expiration at the given time +func ExpiresAtTime(t time.Time) ExpiresAt { + return ExpiresAt{ + Sum: &ExpiresAt_Time{ + Time: &t, + }, + } +} + +// ExpiresAtHeight creates an expiration at the given height +func ExpiresAtHeight(h int64) ExpiresAt { + return ExpiresAt{ + &ExpiresAt_Height{ + Height: h, + }, + } +} + +// ValidateBasic performs basic sanity checks. +// Note that empty expiration is allowed +func (e ExpiresAt) ValidateBasic() error { + if e.HasDefinedTime() && e.GetHeight() != 0 { + return sdkerrors.Wrap(ErrInvalidDuration, "both time and height are set") + } + if e.GetHeight() < 0 { + return sdkerrors.Wrap(ErrInvalidDuration, "negative height") + } + return nil +} + +// Undefined returns true for an uninitialized struct +func (e ExpiresAt) Undefined() bool { + return (e.GetTime() == nil || e.GetTime().Unix() <= 0) && e.GetHeight() == 0 +} + +// HasDefinedTime returns true if `ExpiresAt` has valid time +func (e ExpiresAt) HasDefinedTime() bool { + t := e.GetTime() + return t != nil && t.Unix() > 0 +} + +// FastForward produces a new Expiration with the time or height set to the +// new value, depending on what was set on the original expiration +func (e ExpiresAt) FastForward(t time.Time, h int64) ExpiresAt { + if e.HasDefinedTime() { + return ExpiresAtTime(t) + } + return ExpiresAtHeight(h) +} + +// IsExpired returns if the time or height is *equal to* or greater +// than the defined expiration point. Note that it is expired upon +// an exact match. +// +// Note a "zero" ExpiresAt is never expired +func (e ExpiresAt) IsExpired(t *time.Time, h int64) bool { + if e.HasDefinedTime() && t.After(*e.GetTime()) { + return true + } + + return e.GetHeight() != 0 && h >= e.GetHeight() +} + +// IsCompatible returns true iff the two use the same units. +// If false, they cannot be added. +func (e ExpiresAt) IsCompatible(d Duration) bool { + if e.HasDefinedTime() { + return d.GetDuration() != nil && d.GetDuration().Seconds() > float64(0) + } + return d.GetBlocks() > 0 +} + +// Step will increase the expiration point by one Duration +// It returns an error if the Duration is incompatible +func (e ExpiresAt) Step(d Duration) (ExpiresAt, error) { + if !e.IsCompatible(d) { + return ExpiresAt{}, sdkerrors.Wrap(ErrInvalidDuration, "expiration time and provided duration have different units") + } + if e.HasDefinedTime() { + return ExpiresAtTime(e.GetTime().Add(*d.GetDuration())), nil + } + return ExpiresAtHeight(e.GetHeight() + int64(d.GetBlocks())), nil +} + +// MustStep is like Step, but panics on error +func (e ExpiresAt) MustStep(d Duration) ExpiresAt { + res, err := e.Step(d) + if err != nil { + panic(err) + } + return res +} + +// PrepareForExport will deduct the dumpHeight from the expiration, so when this is +// reloaded after a hard fork, the actual number of allowed blocks is constant +func (e ExpiresAt) PrepareForExport(dumpTime time.Time, dumpHeight int64) ExpiresAt { + if e.GetHeight() != 0 { + return ExpiresAtHeight(e.GetHeight() - dumpHeight) + } + return ExpiresAt{} +} + +// ClockDuration creates an Duration by clock time +func ClockDuration(d time.Duration) Duration { + return Duration{Sum: &Duration_Duration{ + Duration: &d, + }} +} + +// BlockDuration creates an Duration by block height +func BlockDuration(h uint64) Duration { + return Duration{Sum: &Duration_Blocks{ + Blocks: h, + }} +} + +// ValidateBasic performs basic sanity checks +// Note that exactly one must be set and it must be positive +func (d Duration) ValidateBasic() error { + if d.GetBlocks() == 0 && d.GetDuration() == nil { + return sdkerrors.Wrap(ErrInvalidDuration, "neither time and height are set") + } + if d.GetBlocks() != 0 && d.GetDuration() != nil && d.GetDuration().Seconds() != float64(0) { + return sdkerrors.Wrap(ErrInvalidDuration, "both time and height are set") + } + if d.GetDuration() != nil && d.GetDuration().Seconds() < 0 { + return sdkerrors.Wrap(ErrInvalidDuration, "negative clock step") + } + return nil +} diff --git a/x/feegrant/types/expiration_test.go b/x/feegrant/types/expiration_test.go new file mode 100644 index 000000000000..5f390c328a8b --- /dev/null +++ b/x/feegrant/types/expiration_test.go @@ -0,0 +1,162 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +func TestExpiresAt(t *testing.T) { + now := time.Now() + + cases := map[string]struct { + example types.ExpiresAt + valid bool + zero bool + before types.ExpiresAt + after types.ExpiresAt + }{ + "basic": { + example: types.ExpiresAtHeight(100), + valid: true, + before: types.ExpiresAtHeight(50), + after: types.ExpiresAtHeight(122), + }, + "zero": { + example: types.ExpiresAt{}, + zero: true, + valid: true, + before: types.ExpiresAtHeight(1), + }, + "match height": { + example: types.ExpiresAtHeight(1000), + valid: true, + before: types.ExpiresAtHeight(999), + after: types.ExpiresAtHeight(1000), + }, + "match time": { + example: types.ExpiresAtTime(now), + valid: true, + before: types.ExpiresAtTime(now.Add(-1 * time.Second)), + after: types.ExpiresAtTime(now.Add(1 * time.Second)), + }, + } + + for name, stc := range cases { + tc := stc // to make scopelint happy + t.Run(name, func(t *testing.T) { + err := tc.example.ValidateBasic() + assert.Equal(t, tc.zero, tc.example.Undefined()) + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + + if !tc.before.Undefined() { + assert.Equal(t, false, tc.example.IsExpired(tc.before.GetTime(), tc.before.GetHeight())) + } + if !tc.after.Undefined() { + assert.Equal(t, true, tc.example.IsExpired(tc.after.GetTime(), tc.after.GetHeight())) + } + }) + } +} + +func TestDurationValid(t *testing.T) { + now := time.Now() + + cases := map[string]struct { + period types.Duration + valid bool + compatible types.ExpiresAt + incompatible types.ExpiresAt + }{ + "basic height": { + period: types.BlockDuration(100), + valid: true, + compatible: types.ExpiresAtHeight(50), + incompatible: types.ExpiresAtTime(now), + }, + "basic time": { + period: types.ClockDuration(time.Hour), + valid: true, + compatible: types.ExpiresAtTime(now), + incompatible: types.ExpiresAtHeight(50), + }, + "zero": { + period: types.Duration{}, + valid: false, + }, + "negative clock": { + period: types.ClockDuration(-1 * time.Hour), + valid: false, + }, + } + + for name, stc := range cases { + tc := stc // to make scopelint happy + t.Run(name, func(t *testing.T) { + err := tc.period.ValidateBasic() + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + + assert.Equal(t, true, tc.compatible.IsCompatible(tc.period)) + assert.Equal(t, false, tc.incompatible.IsCompatible(tc.period)) + }) + } +} + +func TestDurationStep(t *testing.T) { + now := time.Now() + + cases := map[string]struct { + expires types.ExpiresAt + period types.Duration + valid bool + result types.ExpiresAt + }{ + "add height": { + expires: types.ExpiresAtHeight(789), + period: types.BlockDuration(100), + valid: true, + result: types.ExpiresAtHeight(889), + }, + "add time": { + expires: types.ExpiresAtTime(now), + period: types.ClockDuration(time.Hour), + valid: true, + result: types.ExpiresAtTime(now.Add(time.Hour)), + }, + "mismatch": { + expires: types.ExpiresAtHeight(789), + period: types.ClockDuration(time.Hour), + valid: false, + }, + } + + for name, stc := range cases { + tc := stc // to make scopelint happy + t.Run(name, func(t *testing.T) { + err := tc.period.ValidateBasic() + require.NoError(t, err) + err = tc.expires.ValidateBasic() + require.NoError(t, err) + + next, err := tc.expires.Step(tc.period) + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tc.result, next) + }) + } +} diff --git a/x/feegrant/types/feegrant.pb.go b/x/feegrant/types/feegrant.pb.go new file mode 100644 index 000000000000..2897bcc392d6 --- /dev/null +++ b/x/feegrant/types/feegrant.pb.go @@ -0,0 +1,1701 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/feegrant/v1beta1/feegrant.proto + +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" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + _ "github.com/golang/protobuf/ptypes/duration" + _ "github.com/golang/protobuf/ptypes/timestamp" + _ "github.com/regen-network/cosmos-proto" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// 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 + +// BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens +// that optionally expires. The delegatee can use up to SpendLimit to cover fees. +type BasicFeeAllowance struct { + // spend_limit specifies the maximum amount of tokens that can be spent + // by this allowance and will be updated as tokens are spent. If it is + // empty, there is no spend limit and any amount of coins can be spent. + 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"` + // expiration specifies an optional time when this allowance expires + Expiration ExpiresAt `protobuf:"bytes,2,opt,name=expiration,proto3" json:"expiration"` +} + +func (m *BasicFeeAllowance) Reset() { *m = BasicFeeAllowance{} } +func (m *BasicFeeAllowance) String() string { return proto.CompactTextString(m) } +func (*BasicFeeAllowance) ProtoMessage() {} +func (*BasicFeeAllowance) Descriptor() ([]byte, []int) { + return fileDescriptor_7279582900c30aea, []int{0} +} +func (m *BasicFeeAllowance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BasicFeeAllowance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BasicFeeAllowance.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 *BasicFeeAllowance) XXX_Merge(src proto.Message) { + xxx_messageInfo_BasicFeeAllowance.Merge(m, src) +} +func (m *BasicFeeAllowance) XXX_Size() int { + return m.Size() +} +func (m *BasicFeeAllowance) XXX_DiscardUnknown() { + xxx_messageInfo_BasicFeeAllowance.DiscardUnknown(m) +} + +var xxx_messageInfo_BasicFeeAllowance proto.InternalMessageInfo + +func (m *BasicFeeAllowance) GetSpendLimit() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.SpendLimit + } + return nil +} + +func (m *BasicFeeAllowance) GetExpiration() ExpiresAt { + if m != nil { + return m.Expiration + } + return ExpiresAt{} +} + +// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap, +// as well as a limit per time period. +type PeriodicFeeAllowance struct { + // basic specifies a struct of `BasicFeeAllowance` + Basic BasicFeeAllowance `protobuf:"bytes,1,opt,name=basic,proto3" json:"basic"` + // period specifies the time duration in which period_spend_limit coins can + // be spent before that allowance is reset + Period Duration `protobuf:"bytes,2,opt,name=period,proto3" json:"period"` + // period_spend_limit specifies the maximum number of coins that can be spent + // in the period + PeriodSpendLimit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=period_spend_limit,json=periodSpendLimit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"period_spend_limit"` + // period_can_spend is the number of coins left to be spent before the period_reset time + PeriodCanSpend github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=period_can_spend,json=periodCanSpend,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"period_can_spend"` + // period_reset is the time at which this period resets and a new one begins, + // it is calculated from the start time of the first transaction after the + // last period ended + PeriodReset ExpiresAt `protobuf:"bytes,5,opt,name=period_reset,json=periodReset,proto3" json:"period_reset"` +} + +func (m *PeriodicFeeAllowance) Reset() { *m = PeriodicFeeAllowance{} } +func (m *PeriodicFeeAllowance) String() string { return proto.CompactTextString(m) } +func (*PeriodicFeeAllowance) ProtoMessage() {} +func (*PeriodicFeeAllowance) Descriptor() ([]byte, []int) { + return fileDescriptor_7279582900c30aea, []int{1} +} +func (m *PeriodicFeeAllowance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PeriodicFeeAllowance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PeriodicFeeAllowance.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 *PeriodicFeeAllowance) XXX_Merge(src proto.Message) { + xxx_messageInfo_PeriodicFeeAllowance.Merge(m, src) +} +func (m *PeriodicFeeAllowance) XXX_Size() int { + return m.Size() +} +func (m *PeriodicFeeAllowance) XXX_DiscardUnknown() { + xxx_messageInfo_PeriodicFeeAllowance.DiscardUnknown(m) +} + +var xxx_messageInfo_PeriodicFeeAllowance proto.InternalMessageInfo + +func (m *PeriodicFeeAllowance) GetBasic() BasicFeeAllowance { + if m != nil { + return m.Basic + } + return BasicFeeAllowance{} +} + +func (m *PeriodicFeeAllowance) GetPeriod() Duration { + if m != nil { + return m.Period + } + return Duration{} +} + +func (m *PeriodicFeeAllowance) GetPeriodSpendLimit() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.PeriodSpendLimit + } + return nil +} + +func (m *PeriodicFeeAllowance) GetPeriodCanSpend() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.PeriodCanSpend + } + return nil +} + +func (m *PeriodicFeeAllowance) GetPeriodReset() ExpiresAt { + if m != nil { + return m.PeriodReset + } + return ExpiresAt{} +} + +// Duration is a span of a clock time or number of blocks. +// This is designed to be added to an ExpiresAt struct. +type Duration struct { + // sum is the oneof that represents either duration or block + // + // Types that are valid to be assigned to Sum: + // *Duration_Duration + // *Duration_Blocks + Sum isDuration_Sum `protobuf_oneof:"sum"` +} + +func (m *Duration) Reset() { *m = Duration{} } +func (m *Duration) String() string { return proto.CompactTextString(m) } +func (*Duration) ProtoMessage() {} +func (*Duration) Descriptor() ([]byte, []int) { + return fileDescriptor_7279582900c30aea, []int{2} +} +func (m *Duration) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Duration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Duration.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 *Duration) XXX_Merge(src proto.Message) { + xxx_messageInfo_Duration.Merge(m, src) +} +func (m *Duration) XXX_Size() int { + return m.Size() +} +func (m *Duration) XXX_DiscardUnknown() { + xxx_messageInfo_Duration.DiscardUnknown(m) +} + +var xxx_messageInfo_Duration proto.InternalMessageInfo + +type isDuration_Sum interface { + isDuration_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type Duration_Duration struct { + Duration *time.Duration `protobuf:"bytes,1,opt,name=duration,proto3,oneof,stdduration" json:"duration,omitempty"` +} +type Duration_Blocks struct { + Blocks uint64 `protobuf:"varint,2,opt,name=blocks,proto3,oneof" json:"blocks,omitempty"` +} + +func (*Duration_Duration) isDuration_Sum() {} +func (*Duration_Blocks) isDuration_Sum() {} + +func (m *Duration) GetSum() isDuration_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *Duration) GetDuration() *time.Duration { + if x, ok := m.GetSum().(*Duration_Duration); ok { + return x.Duration + } + return nil +} + +func (m *Duration) GetBlocks() uint64 { + if x, ok := m.GetSum().(*Duration_Blocks); ok { + return x.Blocks + } + return 0 +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Duration) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*Duration_Duration)(nil), + (*Duration_Blocks)(nil), + } +} + +// ExpiresAt is a point in time where something expires. +// It may be *either* block time or block height +type ExpiresAt struct { + // sum is the oneof that represents either time or height + // + // Types that are valid to be assigned to Sum: + // *ExpiresAt_Time + // *ExpiresAt_Height + Sum isExpiresAt_Sum `protobuf_oneof:"sum"` +} + +func (m *ExpiresAt) Reset() { *m = ExpiresAt{} } +func (m *ExpiresAt) String() string { return proto.CompactTextString(m) } +func (*ExpiresAt) ProtoMessage() {} +func (*ExpiresAt) Descriptor() ([]byte, []int) { + return fileDescriptor_7279582900c30aea, []int{3} +} +func (m *ExpiresAt) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExpiresAt) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExpiresAt.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 *ExpiresAt) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExpiresAt.Merge(m, src) +} +func (m *ExpiresAt) XXX_Size() int { + return m.Size() +} +func (m *ExpiresAt) XXX_DiscardUnknown() { + xxx_messageInfo_ExpiresAt.DiscardUnknown(m) +} + +var xxx_messageInfo_ExpiresAt proto.InternalMessageInfo + +type isExpiresAt_Sum interface { + isExpiresAt_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type ExpiresAt_Time struct { + Time *time.Time `protobuf:"bytes,1,opt,name=time,proto3,oneof,stdtime" json:"time,omitempty"` +} +type ExpiresAt_Height struct { + Height int64 `protobuf:"varint,2,opt,name=height,proto3,oneof" json:"height,omitempty"` +} + +func (*ExpiresAt_Time) isExpiresAt_Sum() {} +func (*ExpiresAt_Height) isExpiresAt_Sum() {} + +func (m *ExpiresAt) GetSum() isExpiresAt_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *ExpiresAt) GetTime() *time.Time { + if x, ok := m.GetSum().(*ExpiresAt_Time); ok { + return x.Time + } + return nil +} + +func (m *ExpiresAt) GetHeight() int64 { + if x, ok := m.GetSum().(*ExpiresAt_Height); ok { + return x.Height + } + return 0 +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*ExpiresAt) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*ExpiresAt_Time)(nil), + (*ExpiresAt_Height)(nil), + } +} + +// FeeAllowanceGrant is stored in the KVStore to record a grant with full context +type FeeAllowanceGrant struct { + Granter string `protobuf:"bytes,1,opt,name=granter,proto3" json:"granter,omitempty"` + Grantee string `protobuf:"bytes,2,opt,name=grantee,proto3" json:"grantee,omitempty"` + Allowance *types1.Any `protobuf:"bytes,3,opt,name=allowance,proto3" json:"allowance,omitempty"` +} + +func (m *FeeAllowanceGrant) Reset() { *m = FeeAllowanceGrant{} } +func (m *FeeAllowanceGrant) String() string { return proto.CompactTextString(m) } +func (*FeeAllowanceGrant) ProtoMessage() {} +func (*FeeAllowanceGrant) Descriptor() ([]byte, []int) { + return fileDescriptor_7279582900c30aea, []int{4} +} +func (m *FeeAllowanceGrant) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FeeAllowanceGrant) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FeeAllowanceGrant.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 *FeeAllowanceGrant) XXX_Merge(src proto.Message) { + xxx_messageInfo_FeeAllowanceGrant.Merge(m, src) +} +func (m *FeeAllowanceGrant) XXX_Size() int { + return m.Size() +} +func (m *FeeAllowanceGrant) XXX_DiscardUnknown() { + xxx_messageInfo_FeeAllowanceGrant.DiscardUnknown(m) +} + +var xxx_messageInfo_FeeAllowanceGrant proto.InternalMessageInfo + +func (m *FeeAllowanceGrant) GetGranter() string { + if m != nil { + return m.Granter + } + return "" +} + +func (m *FeeAllowanceGrant) GetGrantee() string { + if m != nil { + return m.Grantee + } + return "" +} + +func (m *FeeAllowanceGrant) GetAllowance() *types1.Any { + if m != nil { + return m.Allowance + } + return nil +} + +func init() { + proto.RegisterType((*BasicFeeAllowance)(nil), "cosmos.feegrant.v1beta1.BasicFeeAllowance") + proto.RegisterType((*PeriodicFeeAllowance)(nil), "cosmos.feegrant.v1beta1.PeriodicFeeAllowance") + proto.RegisterType((*Duration)(nil), "cosmos.feegrant.v1beta1.Duration") + proto.RegisterType((*ExpiresAt)(nil), "cosmos.feegrant.v1beta1.ExpiresAt") + proto.RegisterType((*FeeAllowanceGrant)(nil), "cosmos.feegrant.v1beta1.FeeAllowanceGrant") +} + +func init() { + proto.RegisterFile("cosmos/feegrant/v1beta1/feegrant.proto", fileDescriptor_7279582900c30aea) +} + +var fileDescriptor_7279582900c30aea = []byte{ + // 599 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x4f, 0x6e, 0xd3, 0x40, + 0x14, 0xc6, 0x6d, 0x9c, 0x96, 0x76, 0x02, 0x88, 0x8c, 0x22, 0xe1, 0x64, 0xe1, 0x94, 0x2c, 0x50, + 0x84, 0x14, 0x9b, 0x16, 0x89, 0x05, 0x12, 0x42, 0x71, 0x69, 0x1b, 0x04, 0x0b, 0x64, 0x58, 0xb1, + 0x89, 0x6c, 0x67, 0xea, 0x0c, 0xb5, 0x3d, 0x96, 0x67, 0x02, 0xcd, 0x25, 0x50, 0x97, 0x9c, 0x81, + 0x35, 0x87, 0xa8, 0x58, 0x55, 0xac, 0x58, 0xb5, 0x28, 0x39, 0x01, 0x37, 0x40, 0xf3, 0xc7, 0x4e, + 0x94, 0x10, 0x24, 0xa4, 0xae, 0xe2, 0x99, 0x79, 0xef, 0xfb, 0xbd, 0xf7, 0xbd, 0x99, 0x80, 0x07, + 0x21, 0xa1, 0x09, 0xa1, 0xce, 0x31, 0x42, 0x51, 0xee, 0xa7, 0xcc, 0xf9, 0xb8, 0x1b, 0x20, 0xe6, + 0xef, 0x96, 0x1b, 0x76, 0x96, 0x13, 0x46, 0xe0, 0x3d, 0x19, 0x67, 0x97, 0xdb, 0x2a, 0xae, 0x59, + 0x8f, 0x48, 0x44, 0x44, 0x8c, 0xc3, 0xbf, 0x64, 0x78, 0xb3, 0x11, 0x11, 0x12, 0xc5, 0xc8, 0x11, + 0xab, 0x60, 0x7c, 0xec, 0xf8, 0xe9, 0xa4, 0x38, 0x92, 0x4a, 0x03, 0x99, 0xa3, 0x64, 0xe5, 0x91, + 0xa5, 0x8a, 0x09, 0x7c, 0x8a, 0xca, 0x42, 0x42, 0x82, 0x53, 0x75, 0xde, 0x5a, 0x56, 0x65, 0x38, + 0x41, 0x94, 0xf9, 0x49, 0x56, 0x08, 0x2c, 0x07, 0x0c, 0xc7, 0xb9, 0xcf, 0x30, 0x51, 0x02, 0xed, + 0x4b, 0x1d, 0xd4, 0x5c, 0x9f, 0xe2, 0xf0, 0x10, 0xa1, 0x5e, 0x1c, 0x93, 0x4f, 0x7e, 0x1a, 0x22, + 0x18, 0x83, 0x2a, 0xcd, 0x50, 0x3a, 0x1c, 0xc4, 0x38, 0xc1, 0xcc, 0xd4, 0x77, 0x8c, 0x4e, 0x75, + 0xaf, 0x61, 0xab, 0xd2, 0x78, 0x31, 0x45, 0xb7, 0xf6, 0x3e, 0xc1, 0xa9, 0xfb, 0xe8, 0xfc, 0xb2, + 0xa5, 0x7d, 0xbd, 0x6a, 0x75, 0x22, 0xcc, 0x46, 0xe3, 0xc0, 0x0e, 0x49, 0xa2, 0xfa, 0x50, 0x3f, + 0x5d, 0x3a, 0x3c, 0x71, 0xd8, 0x24, 0x43, 0x54, 0x24, 0x50, 0x0f, 0x08, 0xfd, 0xd7, 0x5c, 0x1e, + 0xf6, 0x01, 0x40, 0xa7, 0x19, 0x96, 0x75, 0x99, 0x37, 0x76, 0xf4, 0x4e, 0x75, 0xaf, 0x6d, 0xaf, + 0xb1, 0xd7, 0x3e, 0xe0, 0xa1, 0x88, 0xf6, 0x98, 0x5b, 0xe1, 0x54, 0x6f, 0x21, 0xf7, 0x69, 0xed, + 0xc7, 0xb7, 0xee, 0xed, 0xc5, 0x4e, 0x5e, 0xb6, 0x7f, 0x1b, 0xa0, 0xfe, 0x06, 0xe5, 0x98, 0x0c, + 0x97, 0x7a, 0x3c, 0x04, 0x1b, 0x01, 0x6f, 0xdc, 0xd4, 0x05, 0xf0, 0xe1, 0x5a, 0xe0, 0x8a, 0x3d, + 0x0a, 0x2c, 0xd3, 0xe1, 0x73, 0xb0, 0x99, 0x09, 0x7d, 0x55, 0xf9, 0xfd, 0xb5, 0x42, 0x2f, 0x94, + 0xf5, 0x2a, 0x5f, 0xa5, 0xc1, 0x09, 0x80, 0xf2, 0x6b, 0xb0, 0xe8, 0xb9, 0x71, 0xfd, 0x9e, 0xdf, + 0x95, 0x98, 0xb7, 0x73, 0xe7, 0xc7, 0x40, 0xed, 0x0d, 0x42, 0x3f, 0x95, 0x78, 0xb3, 0x72, 0xfd, + 0xe0, 0x3b, 0x12, 0xb2, 0xef, 0xa7, 0x82, 0x0d, 0x5f, 0x81, 0x5b, 0x0a, 0x9b, 0x23, 0x8a, 0x98, + 0xb9, 0xf1, 0x9f, 0x23, 0xaf, 0xca, 0x6c, 0x8f, 0x27, 0xff, 0x6d, 0xe6, 0x1f, 0xc0, 0x56, 0xe1, + 0x35, 0x7c, 0x06, 0xb6, 0x8a, 0x2b, 0xaf, 0x26, 0xdd, 0xb0, 0xe5, 0x9b, 0xb0, 0x8b, 0x37, 0xb1, + 0x30, 0x98, 0x2f, 0x57, 0x2d, 0xbd, 0xaf, 0x79, 0x65, 0x0a, 0x34, 0xc1, 0x66, 0x10, 0x93, 0xf0, + 0x84, 0x8a, 0xe9, 0x56, 0xfa, 0x9a, 0xa7, 0xd6, 0xee, 0x06, 0x30, 0xe8, 0x38, 0x69, 0x0f, 0xc1, + 0x76, 0x59, 0x1e, 0x7c, 0x02, 0x2a, 0xfc, 0x01, 0x2a, 0x50, 0x73, 0x05, 0xf4, 0xae, 0x78, 0x9d, + 0x6e, 0xe5, 0x4c, 0x92, 0x44, 0x3c, 0xa7, 0x8c, 0x10, 0x8e, 0x46, 0x4c, 0x50, 0x0c, 0x4e, 0x91, + 0xeb, 0x82, 0xf2, 0x59, 0x07, 0xb5, 0xc5, 0x1e, 0x8f, 0xb8, 0x3f, 0xd0, 0x04, 0x37, 0x85, 0x51, + 0x28, 0x17, 0xc4, 0x6d, 0xaf, 0x58, 0xce, 0x4f, 0x90, 0x50, 0x2c, 0x4f, 0x10, 0x3c, 0x00, 0xdb, + 0x7e, 0xa1, 0x62, 0x1a, 0xa2, 0xce, 0xfa, 0x4a, 0x9d, 0xbd, 0x74, 0xe2, 0xd6, 0xbe, 0x2f, 0xfb, + 0xea, 0xcd, 0x33, 0xdd, 0xa3, 0xf3, 0xa9, 0xa5, 0x5f, 0x4c, 0x2d, 0xfd, 0xd7, 0xd4, 0xd2, 0xcf, + 0x66, 0x96, 0x76, 0x31, 0xb3, 0xb4, 0x9f, 0x33, 0x4b, 0x7b, 0xdf, 0xfd, 0xe7, 0xb5, 0x38, 0x9d, + 0xff, 0xaf, 0x8a, 0x1b, 0x12, 0x6c, 0x0a, 0xe8, 0xe3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x74, + 0x27, 0xe6, 0x00, 0x77, 0x05, 0x00, 0x00, +} + +func (m *BasicFeeAllowance) 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 *BasicFeeAllowance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BasicFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Expiration.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFeegrant(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + 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 = encodeVarintFeegrant(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *PeriodicFeeAllowance) 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 *PeriodicFeeAllowance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PeriodicFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.PeriodReset.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFeegrant(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.PeriodCanSpend) > 0 { + for iNdEx := len(m.PeriodCanSpend) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PeriodCanSpend[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFeegrant(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.PeriodSpendLimit) > 0 { + for iNdEx := len(m.PeriodSpendLimit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PeriodSpendLimit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFeegrant(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + { + size, err := m.Period.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFeegrant(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Basic.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFeegrant(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Duration) 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 *Duration) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Duration) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + return len(dAtA) - i, nil +} + +func (m *Duration_Duration) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Duration_Duration) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Duration != nil { + n5, err5 := github_com_gogo_protobuf_types.StdDurationMarshalTo(*m.Duration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(*m.Duration):]) + if err5 != nil { + return 0, err5 + } + i -= n5 + i = encodeVarintFeegrant(dAtA, i, uint64(n5)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *Duration_Blocks) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Duration_Blocks) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i = encodeVarintFeegrant(dAtA, i, uint64(m.Blocks)) + i-- + dAtA[i] = 0x10 + return len(dAtA) - i, nil +} +func (m *ExpiresAt) 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 *ExpiresAt) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExpiresAt) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + return len(dAtA) - i, nil +} + +func (m *ExpiresAt_Time) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExpiresAt_Time) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Time != nil { + n6, err6 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Time):]) + if err6 != nil { + return 0, err6 + } + i -= n6 + i = encodeVarintFeegrant(dAtA, i, uint64(n6)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *ExpiresAt_Height) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExpiresAt_Height) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i = encodeVarintFeegrant(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x10 + return len(dAtA) - i, nil +} +func (m *FeeAllowanceGrant) 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 *FeeAllowanceGrant) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FeeAllowanceGrant) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Allowance != nil { + { + size, err := m.Allowance.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFeegrant(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintFeegrant(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintFeegrant(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintFeegrant(dAtA []byte, offset int, v uint64) int { + offset -= sovFeegrant(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *BasicFeeAllowance) 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 + sovFeegrant(uint64(l)) + } + } + l = m.Expiration.Size() + n += 1 + l + sovFeegrant(uint64(l)) + return n +} + +func (m *PeriodicFeeAllowance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Basic.Size() + n += 1 + l + sovFeegrant(uint64(l)) + l = m.Period.Size() + n += 1 + l + sovFeegrant(uint64(l)) + if len(m.PeriodSpendLimit) > 0 { + for _, e := range m.PeriodSpendLimit { + l = e.Size() + n += 1 + l + sovFeegrant(uint64(l)) + } + } + if len(m.PeriodCanSpend) > 0 { + for _, e := range m.PeriodCanSpend { + l = e.Size() + n += 1 + l + sovFeegrant(uint64(l)) + } + } + l = m.PeriodReset.Size() + n += 1 + l + sovFeegrant(uint64(l)) + return n +} + +func (m *Duration) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sum != nil { + n += m.Sum.Size() + } + return n +} + +func (m *Duration_Duration) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Duration != nil { + l = github_com_gogo_protobuf_types.SizeOfStdDuration(*m.Duration) + n += 1 + l + sovFeegrant(uint64(l)) + } + return n +} +func (m *Duration_Blocks) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += 1 + sovFeegrant(uint64(m.Blocks)) + return n +} +func (m *ExpiresAt) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sum != nil { + n += m.Sum.Size() + } + return n +} + +func (m *ExpiresAt_Time) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Time != nil { + l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.Time) + n += 1 + l + sovFeegrant(uint64(l)) + } + return n +} +func (m *ExpiresAt_Height) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += 1 + sovFeegrant(uint64(m.Height)) + return n +} +func (m *FeeAllowanceGrant) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovFeegrant(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovFeegrant(uint64(l)) + } + if m.Allowance != nil { + l = m.Allowance.Size() + n += 1 + l + sovFeegrant(uint64(l)) + } + return n +} + +func sovFeegrant(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozFeegrant(x uint64) (n int) { + return sovFeegrant(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *BasicFeeAllowance) 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 ErrIntOverflowFeegrant + } + 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: BasicFeeAllowance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BasicFeeAllowance: 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 ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFeegrant + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + 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 + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Expiration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFeegrant + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Expiration.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipFeegrant(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthFeegrant + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PeriodicFeeAllowance) 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 ErrIntOverflowFeegrant + } + 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: PeriodicFeeAllowance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PeriodicFeeAllowance: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Basic", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFeegrant + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Basic.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Period", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFeegrant + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Period.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PeriodSpendLimit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFeegrant + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PeriodSpendLimit = append(m.PeriodSpendLimit, types.Coin{}) + if err := m.PeriodSpendLimit[len(m.PeriodSpendLimit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PeriodCanSpend", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFeegrant + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PeriodCanSpend = append(m.PeriodCanSpend, types.Coin{}) + if err := m.PeriodCanSpend[len(m.PeriodCanSpend)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PeriodReset", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFeegrant + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PeriodReset.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipFeegrant(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthFeegrant + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Duration) 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 ErrIntOverflowFeegrant + } + 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: Duration: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Duration: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Duration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFeegrant + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := new(time.Duration) + if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(v, dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &Duration_Duration{v} + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Blocks", wireType) + } + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sum = &Duration_Blocks{v} + default: + iNdEx = preIndex + skippy, err := skipFeegrant(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthFeegrant + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExpiresAt) 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 ErrIntOverflowFeegrant + } + 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: ExpiresAt: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExpiresAt: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFeegrant + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := new(time.Time) + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(v, dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &ExpiresAt_Time{v} + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sum = &ExpiresAt_Height{v} + default: + iNdEx = preIndex + skippy, err := skipFeegrant(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthFeegrant + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FeeAllowanceGrant) 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 ErrIntOverflowFeegrant + } + 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: FeeAllowanceGrant: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FeeAllowanceGrant: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + 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 ErrInvalidLengthFeegrant + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Granter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + 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 ErrInvalidLengthFeegrant + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Grantee = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Allowance", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFeegrant + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFeegrant + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFeegrant + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Allowance == nil { + m.Allowance = &types1.Any{} + } + if err := m.Allowance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipFeegrant(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthFeegrant + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipFeegrant(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, ErrIntOverflowFeegrant + } + 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, ErrIntOverflowFeegrant + } + 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, ErrIntOverflowFeegrant + } + 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, ErrInvalidLengthFeegrant + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupFeegrant + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthFeegrant + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthFeegrant = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowFeegrant = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupFeegrant = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/feegrant/types/fees.go b/x/feegrant/types/fees.go new file mode 100644 index 000000000000..cc526628f5f9 --- /dev/null +++ b/x/feegrant/types/fees.go @@ -0,0 +1,32 @@ +package types + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// FeeAllowance implementations are tied to a given fee delegator and delegatee, +// and are used to enforce fee grant limits. +type FeeAllowanceI interface { + // Accept can use fee payment requested as well as timestamp/height of the current block + // to determine whether or not to process this. This is checked in + // Keeper.UseGrantedFees and the return values should match how it is handled there. + // + // If it returns an error, the fee payment is rejected, otherwise it is accepted. + // The FeeAllowance implementation is expected to update it's internal state + // and will be saved again after an acceptance. + // + // If remove is true (regardless of the error), the FeeAllowance will be deleted from storage + // (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees) + Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error) + + // If we export fee allowances the timing info will be quite off (eg. go from height 100000 to 0) + // This callback allows the fee-allowance to change it's state and return a copy that is adjusted + // given the time and height of the actual dump (may safely return self if no changes needed) + PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceI + + // ValidateBasic should evaluate this FeeAllowance for internal consistency. + // Don't allow negative amounts, or negative periods for example. + ValidateBasic() error +} diff --git a/x/feegrant/types/genesis.go b/x/feegrant/types/genesis.go new file mode 100644 index 000000000000..57ade9b43de2 --- /dev/null +++ b/x/feegrant/types/genesis.go @@ -0,0 +1,40 @@ +package types + +import "github.com/cosmos/cosmos-sdk/codec/types" + +var _ types.UnpackInterfacesMessage = GenesisState{} + +// NewGenesisState creates new GenesisState object +func NewGenesisState(entries []FeeAllowanceGrant) *GenesisState { + return &GenesisState{ + FeeAllowances: entries, + } +} + +// ValidateGenesis ensures all grants in the genesis state are valid +func ValidateGenesis(data GenesisState) error { + for _, f := range data.FeeAllowances { + err := f.GetFeeGrant().ValidateBasic() + if err != nil { + return err + } + } + return nil +} + +// DefaultGenesisState returns default state for feegrant module. +func DefaultGenesisState() *GenesisState { + return &GenesisState{} +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (data GenesisState) UnpackInterfaces(unpacker types.AnyUnpacker) error { + for _, f := range data.FeeAllowances { + err := f.UnpackInterfaces(unpacker) + if err != nil { + return err + } + } + + return nil +} diff --git a/x/feegrant/types/genesis.pb.go b/x/feegrant/types/genesis.pb.go new file mode 100644 index 000000000000..499f57d626d6 --- /dev/null +++ b/x/feegrant/types/genesis.pb.go @@ -0,0 +1,333 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/feegrant/v1beta1/genesis.proto + +package types + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/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 + +// GenesisState contains a set of fee allowances, persisted from the store +type GenesisState struct { + FeeAllowances []FeeAllowanceGrant `protobuf:"bytes,1,rep,name=fee_allowances,json=feeAllowances,proto3" json:"fee_allowances"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_ac719d2d0954d1bf, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.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 *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetFeeAllowances() []FeeAllowanceGrant { + if m != nil { + return m.FeeAllowances + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.feegrant.v1beta1.GenesisState") +} + +func init() { + proto.RegisterFile("cosmos/feegrant/v1beta1/genesis.proto", fileDescriptor_ac719d2d0954d1bf) +} + +var fileDescriptor_ac719d2d0954d1bf = []byte{ + // 221 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4d, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x4b, 0x4d, 0x4d, 0x2f, 0x4a, 0xcc, 0x2b, 0xd1, 0x2f, 0x33, 0x4c, 0x4a, + 0x2d, 0x49, 0x34, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, + 0xc9, 0x17, 0x12, 0x87, 0x28, 0xd3, 0x83, 0x29, 0xd3, 0x83, 0x2a, 0x93, 0x12, 0x49, 0xcf, 0x4f, + 0xcf, 0x07, 0xab, 0xd1, 0x07, 0xb1, 0x20, 0xca, 0xa5, 0xd4, 0x70, 0x99, 0x0a, 0xd7, 0x0f, 0x56, + 0xa7, 0x94, 0xce, 0xc5, 0xe3, 0x0e, 0xb1, 0x27, 0xb8, 0x24, 0xb1, 0x24, 0x55, 0x28, 0x9c, 0x8b, + 0x2f, 0x2d, 0x35, 0x35, 0x3e, 0x31, 0x27, 0x27, 0xbf, 0x3c, 0x31, 0x2f, 0x39, 0xb5, 0x58, 0x82, + 0x51, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x4b, 0x0f, 0x87, 0xfd, 0x7a, 0x6e, 0xa9, 0xa9, 0x8e, 0x30, + 0xd5, 0xee, 0x20, 0x19, 0x27, 0x96, 0x13, 0xf7, 0xe4, 0x19, 0x82, 0x78, 0xd3, 0x90, 0x24, 0x8a, + 0x9d, 0xdc, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, + 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x37, 0x3d, 0xb3, + 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0xea, 0x6a, 0x08, 0xa5, 0x5b, 0x9c, 0x92, + 0xad, 0x5f, 0x81, 0xf0, 0x42, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0xe1, 0xc6, 0x80, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xaf, 0x53, 0x6e, 0xc5, 0x38, 0x01, 0x00, 0x00, +} + +func (m *GenesisState) 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 *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FeeAllowances) > 0 { + for iNdEx := len(m.FeeAllowances) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FeeAllowances[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.FeeAllowances) > 0 { + for _, e := range m.FeeAllowances { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) 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 ErrIntOverflowGenesis + } + 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: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeAllowances", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeeAllowances = append(m.FeeAllowances, FeeAllowanceGrant{}) + if err := m.FeeAllowances[len(m.FeeAllowances)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/feegrant/types/grant.go b/x/feegrant/types/grant.go new file mode 100644 index 000000000000..97a18de52952 --- /dev/null +++ b/x/feegrant/types/grant.go @@ -0,0 +1,93 @@ +package types + +import ( + "time" + + 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" +) + +var ( + _ types.UnpackInterfacesMessage = &FeeAllowanceGrant{} +) + +// NewFeeAllowanceGrant creates a new FeeAllowanceGrant. +//nolint:interfacer +func NewFeeAllowanceGrant(granter, grantee sdk.AccAddress, feeAllowance FeeAllowanceI) (FeeAllowanceGrant, error) { + msg, ok := feeAllowance.(proto.Message) + if !ok { + return FeeAllowanceGrant{}, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", feeAllowance) + } + + any, err := types.NewAnyWithValue(msg) + if err != nil { + return FeeAllowanceGrant{}, err + } + + return FeeAllowanceGrant{ + Granter: granter.String(), + Grantee: grantee.String(), + Allowance: any, + }, nil +} + +// ValidateBasic performs basic validation on +// FeeAllowanceGrant +func (a FeeAllowanceGrant) ValidateBasic() error { + if a.Granter == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing granter address") + } + if a.Grantee == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing grantee address") + } + if a.Grantee == a.Granter { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "cannot self-grant fee authorization") + } + + return a.GetFeeGrant().ValidateBasic() +} + +// GetFeeGrant unpacks allowance +func (a FeeAllowanceGrant) GetFeeGrant() FeeAllowanceI { + allowance, ok := a.Allowance.GetCachedValue().(FeeAllowanceI) + if !ok { + return nil + } + + return allowance +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (a FeeAllowanceGrant) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var allowance FeeAllowanceI + return unpacker.UnpackAny(a.Allowance, &allowance) +} + +// PrepareForExport will make all needed changes to the allowance to prepare to be +// re-imported at height 0, and return a copy of this grant. +func (a FeeAllowanceGrant) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceGrant { + feegrant := a.GetFeeGrant().PrepareForExport(dumpTime, dumpHeight) + if feegrant == nil { + return FeeAllowanceGrant{} + } + + granter, err := sdk.AccAddressFromBech32(a.Granter) + if err != nil { + return FeeAllowanceGrant{} + } + + grantee, err := sdk.AccAddressFromBech32(a.Grantee) + if err != nil { + return FeeAllowanceGrant{} + } + + grant, err := NewFeeAllowanceGrant(granter, grantee, feegrant) + if err != nil { + return FeeAllowanceGrant{} + } + + return grant +} diff --git a/x/feegrant/types/grant_test.go b/x/feegrant/types/grant_test.go new file mode 100644 index 000000000000..8b29d47ee079 --- /dev/null +++ b/x/feegrant/types/grant_test.go @@ -0,0 +1,100 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +func TestGrant(t *testing.T) { + app := simapp.Setup(false) + addr, err := sdk.AccAddressFromBech32("cosmos1qk93t4j0yyzgqgt6k5qf8deh8fq6smpn3ntu3x") + require.NoError(t, err) + addr2, err := sdk.AccAddressFromBech32("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts") + require.NoError(t, err) + atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) + + goodGrant, err := types.NewFeeAllowanceGrant(addr2, addr, &types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }) + require.NoError(t, err) + + noGranteeGrant, err := types.NewFeeAllowanceGrant(addr2, nil, &types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }) + require.NoError(t, err) + + noGranterGrant, err := types.NewFeeAllowanceGrant(nil, addr, &types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }) + require.NoError(t, err) + + selfGrant, err := types.NewFeeAllowanceGrant(addr2, addr2, &types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }) + require.NoError(t, err) + + badAllowanceGrant, err := types.NewFeeAllowanceGrant(addr2, addr, &types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(-1), + }) + require.NoError(t, err) + + cdc := app.AppCodec() + // RegisterLegacyAminoCodec(cdc) + + cases := map[string]struct { + grant types.FeeAllowanceGrant + valid bool + }{ + "good": { + grant: goodGrant, + valid: true, + }, + "no grantee": { + grant: noGranteeGrant, + }, + "no granter": { + grant: noGranterGrant, + }, + "self-grant": { + grant: selfGrant, + }, + "bad allowance": { + grant: badAllowanceGrant, + }, + } + + for name, tc := range cases { + tc := tc + t.Run(name, func(t *testing.T) { + err := tc.grant.ValidateBasic() + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + + // if it is valid, let's try to serialize, deserialize, and make sure it matches + bz, err := cdc.MarshalBinaryBare(&tc.grant) + require.NoError(t, err) + var loaded types.FeeAllowanceGrant + err = cdc.UnmarshalBinaryBare(bz, &loaded) + require.NoError(t, err) + + err = loaded.ValidateBasic() + require.NoError(t, err) + + assert.Equal(t, tc.grant, loaded) + }) + } +} diff --git a/x/feegrant/types/key.go b/x/feegrant/types/key.go new file mode 100644 index 000000000000..7cd1ce4f8c8f --- /dev/null +++ b/x/feegrant/types/key.go @@ -0,0 +1,36 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" +) + +const ( + // ModuleName is the module name constant used in many places + ModuleName = "feegrant" + + // StoreKey is the store key string for supply + StoreKey = ModuleName + + // RouterKey is the message route for supply + RouterKey = ModuleName + + // QuerierRoute is the querier route for supply + QuerierRoute = ModuleName +) + +var ( + // FeeAllowanceKeyPrefix is the set of the kvstore for fee allowance data + FeeAllowanceKeyPrefix = []byte{0x00} +) + +// FeeAllowanceKey is the canonical key to store a grant from granter to grantee +// We store by grantee first to allow searching by everyone who granted to you +func FeeAllowanceKey(granter sdk.AccAddress, grantee sdk.AccAddress) []byte { + return append(FeeAllowancePrefixByGrantee(grantee), address.MustLengthPrefix(granter.Bytes())...) +} + +// FeeAllowancePrefixByGrantee returns a prefix to scan for all grants to this given address. +func FeeAllowancePrefixByGrantee(grantee sdk.AccAddress) []byte { + return append(FeeAllowanceKeyPrefix, address.MustLengthPrefix(grantee.Bytes())...) +} diff --git a/x/feegrant/types/msgs.go b/x/feegrant/types/msgs.go new file mode 100644 index 000000000000..04f09c8f3fd5 --- /dev/null +++ b/x/feegrant/types/msgs.go @@ -0,0 +1,102 @@ +package types + +import ( + "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" +) + +var ( + _, _ sdk.MsgRequest = &MsgGrantFeeAllowance{}, &MsgRevokeFeeAllowance{} + _ types.UnpackInterfacesMessage = &MsgGrantFeeAllowance{} +) + +// feegrant message types +const ( + TypeMsgGrantFeeAllowance = "grant_fee_allowance" + TypeMsgRevokeFeeAllowance = "revoke_fee_allowance" +) + +// NewMsgGrantFeeAllowance creates a new MsgGrantFeeAllowance. +//nolint:interfacer +func NewMsgGrantFeeAllowance(feeAllowance FeeAllowanceI, granter, grantee sdk.AccAddress) (*MsgGrantFeeAllowance, error) { + msg, ok := feeAllowance.(proto.Message) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", msg) + } + any, err := types.NewAnyWithValue(msg) + if err != nil { + return nil, err + } + + return &MsgGrantFeeAllowance{ + Granter: granter.String(), + Grantee: grantee.String(), + Allowance: any, + }, nil +} + +// ValidateBasic implements the sdk.Msg interface. +func (msg MsgGrantFeeAllowance) ValidateBasic() error { + if msg.Granter == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing granter address") + } + if msg.Grantee == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing grantee address") + } + if msg.Grantee == msg.Granter { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "cannot self-grant fee authorization") + } + + return msg.GetFeeAllowanceI().ValidateBasic() +} + +func (msg MsgGrantFeeAllowance) GetSigners() []sdk.AccAddress { + granter, err := sdk.AccAddressFromBech32(msg.Granter) + if err != nil { + panic(err) + } + return []sdk.AccAddress{granter} +} + +// GetFeeAllowanceI returns unpacked FeeAllowance +func (msg MsgGrantFeeAllowance) GetFeeAllowanceI() FeeAllowanceI { + allowance, ok := msg.Allowance.GetCachedValue().(FeeAllowanceI) + if !ok { + return nil + } + + return allowance +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (msg MsgGrantFeeAllowance) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var allowance FeeAllowanceI + return unpacker.UnpackAny(msg.Allowance, &allowance) +} + +//nolint:interfacer +func NewMsgRevokeFeeAllowance(granter sdk.AccAddress, grantee sdk.AccAddress) MsgRevokeFeeAllowance { + return MsgRevokeFeeAllowance{Granter: granter.String(), Grantee: grantee.String()} +} + +func (msg MsgRevokeFeeAllowance) ValidateBasic() error { + if msg.Granter == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing granter address") + } + if msg.Grantee == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing grantee address") + } + + return nil +} + +func (msg MsgRevokeFeeAllowance) GetSigners() []sdk.AccAddress { + granter, err := sdk.AccAddressFromBech32(msg.Granter) + if err != nil { + panic(err) + } + return []sdk.AccAddress{granter} +} diff --git a/x/feegrant/types/periodic_fee.go b/x/feegrant/types/periodic_fee.go new file mode 100644 index 000000000000..61c9ba2ffe2d --- /dev/null +++ b/x/feegrant/types/periodic_fee.go @@ -0,0 +1,120 @@ +package types + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var _ FeeAllowanceI = (*PeriodicFeeAllowance)(nil) + +// Accept can use fee payment requested as well as timestamp/height of the current block +// to determine whether or not to process this. This is checked in +// Keeper.UseGrantedFees and the return values should match how it is handled there. +// +// If it returns an error, the fee payment is rejected, otherwise it is accepted. +// The FeeAllowance implementation is expected to update it's internal state +// and will be saved again after an acceptance. +// +// If remove is true (regardless of the error), the FeeAllowance will be deleted from storage +// (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees) +func (a *PeriodicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (bool, error) { + if a.Basic.Expiration.IsExpired(&blockTime, blockHeight) { + return true, sdkerrors.Wrap(ErrFeeLimitExpired, "absolute limit") + } + + a.tryResetPeriod(blockTime, blockHeight) + + // deduct from both the current period and the max amount + var isNeg bool + a.PeriodCanSpend, isNeg = a.PeriodCanSpend.SafeSub(fee) + if isNeg { + return false, sdkerrors.Wrap(ErrFeeLimitExceeded, "period limit") + } + + if a.Basic.SpendLimit != nil { + a.Basic.SpendLimit, isNeg = a.Basic.SpendLimit.SafeSub(fee) + if isNeg { + return false, sdkerrors.Wrap(ErrFeeLimitExceeded, "absolute limit") + } + + return a.Basic.SpendLimit.IsZero(), nil + } + + return false, nil +} + +// tryResetPeriod will check if the PeriodReset has been hit. If not, it is a no-op. +// If we hit the reset period, it will top up the PeriodCanSpend amount to +// min(PeriodicSpendLimit, a.Basic.SpendLimit) so it is never more than the maximum allowed. +// It will also update the PeriodReset. If we are within one Period, it will update from the +// last PeriodReset (eg. if you always do one tx per day, it will always reset the same time) +// If we are more then one period out (eg. no activity in a week), reset is one Period from the execution of this method +func (a *PeriodicFeeAllowance) tryResetPeriod(blockTime time.Time, blockHeight int64) { + if !a.PeriodReset.Undefined() && !a.PeriodReset.IsExpired(&blockTime, blockHeight) { + return + } + // set CanSpend to the lesser of PeriodSpendLimit and the TotalLimit + if _, isNeg := a.Basic.SpendLimit.SafeSub(a.PeriodSpendLimit); isNeg { + a.PeriodCanSpend = a.Basic.SpendLimit + } else { + a.PeriodCanSpend = a.PeriodSpendLimit + } + + // If we are within the period, step from expiration (eg. if you always do one tx per day, it will always reset the same time) + // If we are more then one period out (eg. no activity in a week), reset is one period from this time + a.PeriodReset = a.PeriodReset.MustStep(a.Period) + if a.PeriodReset.IsExpired(&blockTime, blockHeight) { + a.PeriodReset = a.PeriodReset.FastForward(blockTime, blockHeight).MustStep(a.Period) + } +} + +// PrepareForExport will adjust the expiration based on export time. In particular, +// it will subtract the dumpHeight from any height-based expiration to ensure that +// the elapsed number of blocks this allowance is valid for is fixed. +// (For PeriodReset and Basic.Expiration) +func (a *PeriodicFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceI { + return &PeriodicFeeAllowance{ + Basic: BasicFeeAllowance{ + SpendLimit: a.Basic.SpendLimit, + Expiration: a.Basic.Expiration.PrepareForExport(dumpTime, dumpHeight), + }, + PeriodSpendLimit: a.PeriodSpendLimit, + PeriodCanSpend: a.PeriodCanSpend, + Period: a.Period, + PeriodReset: a.PeriodReset.PrepareForExport(dumpTime, dumpHeight), + } +} + +// ValidateBasic implements FeeAllowance and enforces basic sanity checks +func (a PeriodicFeeAllowance) ValidateBasic() error { + if err := a.Basic.ValidateBasic(); err != nil { + return err + } + + if !a.PeriodSpendLimit.IsValid() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "spend amount is invalid: %s", a.PeriodSpendLimit) + } + if !a.PeriodSpendLimit.IsAllPositive() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "spend limit must be positive") + } + if !a.PeriodCanSpend.IsValid() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "can spend amount is invalid: %s", a.PeriodCanSpend) + } + // We allow 0 for CanSpend + if a.PeriodCanSpend.IsAnyNegative() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "can spend must not be negative") + } + + // ensure PeriodSpendLimit can be subtracted from total (same coin types) + if a.Basic.SpendLimit != nil && !a.PeriodSpendLimit.DenomsSubsetOf(a.Basic.SpendLimit) { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "period spend limit has different currency than basic spend limit") + } + + // check times + if err := a.Period.ValidateBasic(); err != nil { + return err + } + return a.PeriodReset.ValidateBasic() +} diff --git a/x/feegrant/types/periodic_fee_test.go b/x/feegrant/types/periodic_fee_test.go new file mode 100644 index 000000000000..229fd99cea96 --- /dev/null +++ b/x/feegrant/types/periodic_fee_test.go @@ -0,0 +1,206 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +func TestPeriodicFeeValidAllow(t *testing.T) { + atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) + smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43)) + leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512)) + oneAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1)) + eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 1)) + + cases := map[string]struct { + allow types.PeriodicFeeAllowance + // all other checks are ignored if valid=false + fee sdk.Coins + blockTime time.Time + blockHeight int64 + valid bool + accept bool + remove bool + remains sdk.Coins + remainsPeriod sdk.Coins + periodReset types.ExpiresAt + }{ + "empty": { + allow: types.PeriodicFeeAllowance{}, + valid: false, + }, + "only basic": { + allow: types.PeriodicFeeAllowance{ + Basic: types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }, + }, + valid: false, + }, + "empty basic": { + allow: types.PeriodicFeeAllowance{ + Period: types.BlockDuration(10), + PeriodSpendLimit: smallAtom, + PeriodReset: types.ExpiresAtHeight(70), + }, + blockHeight: 75, + valid: true, + accept: true, + remove: false, + periodReset: types.ExpiresAtHeight(80), + }, + "mismatched currencies": { + allow: types.PeriodicFeeAllowance{ + Basic: types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }, + Period: types.BlockDuration(10), + PeriodSpendLimit: eth, + }, + valid: false, + }, + "first time": { + allow: types.PeriodicFeeAllowance{ + Basic: types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }, + Period: types.BlockDuration(10), + PeriodSpendLimit: smallAtom, + }, + valid: true, + fee: smallAtom, + blockHeight: 75, + accept: true, + remove: false, + remainsPeriod: nil, + remains: leftAtom, + periodReset: types.ExpiresAtHeight(85), + }, + "same period": { + allow: types.PeriodicFeeAllowance{ + Basic: types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }, + Period: types.BlockDuration(10), + PeriodReset: types.ExpiresAtHeight(80), + PeriodSpendLimit: leftAtom, + PeriodCanSpend: smallAtom, + }, + valid: true, + fee: smallAtom, + blockHeight: 75, + accept: true, + remove: false, + remainsPeriod: nil, + remains: leftAtom, + periodReset: types.ExpiresAtHeight(80), + }, + "step one period": { + allow: types.PeriodicFeeAllowance{ + Basic: types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }, + Period: types.BlockDuration(10), + PeriodReset: types.ExpiresAtHeight(70), + PeriodSpendLimit: leftAtom, + }, + valid: true, + fee: leftAtom, + blockHeight: 75, + accept: true, + remove: false, + remainsPeriod: nil, + remains: smallAtom, + periodReset: types.ExpiresAtHeight(80), // one step from last reset, not now + }, + "step limited by global allowance": { + allow: types.PeriodicFeeAllowance{ + Basic: types.BasicFeeAllowance{ + SpendLimit: smallAtom, + Expiration: types.ExpiresAtHeight(100), + }, + Period: types.BlockDuration(10), + PeriodReset: types.ExpiresAtHeight(70), + PeriodSpendLimit: atom, + }, + valid: true, + fee: oneAtom, + blockHeight: 75, + accept: true, + remove: false, + remainsPeriod: smallAtom.Sub(oneAtom), + remains: smallAtom.Sub(oneAtom), + periodReset: types.ExpiresAtHeight(80), // one step from last reset, not now + }, + "expired": { + allow: types.PeriodicFeeAllowance{ + Basic: types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }, + Period: types.BlockDuration(10), + PeriodSpendLimit: smallAtom, + }, + valid: true, + fee: smallAtom, + blockHeight: 101, + accept: false, + remove: true, + }, + "over period limit": { + allow: types.PeriodicFeeAllowance{ + Basic: types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(100), + }, + Period: types.BlockDuration(10), + PeriodReset: types.ExpiresAtHeight(80), + PeriodSpendLimit: leftAtom, + PeriodCanSpend: smallAtom, + }, + valid: true, + fee: leftAtom, + blockHeight: 70, + accept: false, + remove: true, + }, + } + + for name, stc := range cases { + tc := stc // to make scopelint happy + t.Run(name, func(t *testing.T) { + err := tc.allow.ValidateBasic() + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + + // now try to deduct + remove, err := tc.allow.Accept(tc.fee, tc.blockTime, tc.blockHeight) + if !tc.accept { + require.Error(t, err) + return + } + require.NoError(t, err) + + require.Equal(t, tc.remove, remove) + if !remove { + assert.Equal(t, tc.remains, tc.allow.Basic.SpendLimit) + assert.Equal(t, tc.remainsPeriod, tc.allow.PeriodCanSpend) + assert.Equal(t, tc.periodReset, tc.allow.PeriodReset) + } + }) + } +} diff --git a/x/feegrant/types/query.pb.go b/x/feegrant/types/query.pb.go new file mode 100644 index 000000000000..e2597c3bc833 --- /dev/null +++ b/x/feegrant/types/query.pb.go @@ -0,0 +1,1172 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/feegrant/v1beta1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + 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" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + 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 + +// QueryFeeAllowanceRequest is the request type for the Query/FeeAllowance RPC method. +type QueryFeeAllowanceRequest struct { + Granter string `protobuf:"bytes,1,opt,name=granter,proto3" json:"granter,omitempty"` + Grantee string `protobuf:"bytes,2,opt,name=grantee,proto3" json:"grantee,omitempty"` +} + +func (m *QueryFeeAllowanceRequest) Reset() { *m = QueryFeeAllowanceRequest{} } +func (m *QueryFeeAllowanceRequest) String() string { return proto.CompactTextString(m) } +func (*QueryFeeAllowanceRequest) ProtoMessage() {} +func (*QueryFeeAllowanceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_59efc303945de53f, []int{0} +} +func (m *QueryFeeAllowanceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFeeAllowanceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFeeAllowanceRequest.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 *QueryFeeAllowanceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFeeAllowanceRequest.Merge(m, src) +} +func (m *QueryFeeAllowanceRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryFeeAllowanceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFeeAllowanceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFeeAllowanceRequest proto.InternalMessageInfo + +func (m *QueryFeeAllowanceRequest) GetGranter() string { + if m != nil { + return m.Granter + } + return "" +} + +func (m *QueryFeeAllowanceRequest) GetGrantee() string { + if m != nil { + return m.Grantee + } + return "" +} + +// QueryFeeAllowanceResponse is the response type for the Query/FeeAllowance RPC method. +type QueryFeeAllowanceResponse struct { + // fee_allowance is a fee_allowance granted for grantee by granter. + FeeAllowance *FeeAllowanceGrant `protobuf:"bytes,1,opt,name=fee_allowance,json=feeAllowance,proto3" json:"fee_allowance,omitempty"` +} + +func (m *QueryFeeAllowanceResponse) Reset() { *m = QueryFeeAllowanceResponse{} } +func (m *QueryFeeAllowanceResponse) String() string { return proto.CompactTextString(m) } +func (*QueryFeeAllowanceResponse) ProtoMessage() {} +func (*QueryFeeAllowanceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_59efc303945de53f, []int{1} +} +func (m *QueryFeeAllowanceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFeeAllowanceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFeeAllowanceResponse.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 *QueryFeeAllowanceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFeeAllowanceResponse.Merge(m, src) +} +func (m *QueryFeeAllowanceResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryFeeAllowanceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFeeAllowanceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFeeAllowanceResponse proto.InternalMessageInfo + +func (m *QueryFeeAllowanceResponse) GetFeeAllowance() *FeeAllowanceGrant { + if m != nil { + return m.FeeAllowance + } + return nil +} + +// QueryFeeAllowancesRequest is the request type for the Query/FeeAllowances RPC method. +type QueryFeeAllowancesRequest struct { + Grantee string `protobuf:"bytes,1,opt,name=grantee,proto3" json:"grantee,omitempty"` + // pagination defines an pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryFeeAllowancesRequest) Reset() { *m = QueryFeeAllowancesRequest{} } +func (m *QueryFeeAllowancesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryFeeAllowancesRequest) ProtoMessage() {} +func (*QueryFeeAllowancesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_59efc303945de53f, []int{2} +} +func (m *QueryFeeAllowancesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFeeAllowancesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFeeAllowancesRequest.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 *QueryFeeAllowancesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFeeAllowancesRequest.Merge(m, src) +} +func (m *QueryFeeAllowancesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryFeeAllowancesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFeeAllowancesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFeeAllowancesRequest proto.InternalMessageInfo + +func (m *QueryFeeAllowancesRequest) GetGrantee() string { + if m != nil { + return m.Grantee + } + return "" +} + +func (m *QueryFeeAllowancesRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryFeeAllowancesResponse is the response type for the Query/FeeAllowances RPC method. +type QueryFeeAllowancesResponse struct { + // fee_allowances are fee_allowance's granted for grantee by granter. + FeeAllowances []*FeeAllowanceGrant `protobuf:"bytes,1,rep,name=fee_allowances,json=feeAllowances,proto3" json:"fee_allowances,omitempty"` + // pagination defines an pagination for the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryFeeAllowancesResponse) Reset() { *m = QueryFeeAllowancesResponse{} } +func (m *QueryFeeAllowancesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryFeeAllowancesResponse) ProtoMessage() {} +func (*QueryFeeAllowancesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_59efc303945de53f, []int{3} +} +func (m *QueryFeeAllowancesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFeeAllowancesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFeeAllowancesResponse.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 *QueryFeeAllowancesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFeeAllowancesResponse.Merge(m, src) +} +func (m *QueryFeeAllowancesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryFeeAllowancesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFeeAllowancesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFeeAllowancesResponse proto.InternalMessageInfo + +func (m *QueryFeeAllowancesResponse) GetFeeAllowances() []*FeeAllowanceGrant { + if m != nil { + return m.FeeAllowances + } + return nil +} + +func (m *QueryFeeAllowancesResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +func init() { + proto.RegisterType((*QueryFeeAllowanceRequest)(nil), "cosmos.feegrant.v1beta1.QueryFeeAllowanceRequest") + proto.RegisterType((*QueryFeeAllowanceResponse)(nil), "cosmos.feegrant.v1beta1.QueryFeeAllowanceResponse") + proto.RegisterType((*QueryFeeAllowancesRequest)(nil), "cosmos.feegrant.v1beta1.QueryFeeAllowancesRequest") + proto.RegisterType((*QueryFeeAllowancesResponse)(nil), "cosmos.feegrant.v1beta1.QueryFeeAllowancesResponse") +} + +func init() { + proto.RegisterFile("cosmos/feegrant/v1beta1/query.proto", fileDescriptor_59efc303945de53f) +} + +var fileDescriptor_59efc303945de53f = []byte{ + // 455 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0x31, 0x8f, 0xd3, 0x30, + 0x14, 0xc7, 0xeb, 0x22, 0x40, 0xf8, 0xae, 0x0c, 0x16, 0x12, 0x21, 0x42, 0xd1, 0x29, 0x48, 0x07, + 0x3a, 0xe9, 0x62, 0x35, 0x9d, 0x40, 0x2c, 0xdc, 0x70, 0xdd, 0x80, 0xcb, 0xc8, 0x82, 0x9c, 0xf2, + 0x6a, 0x22, 0x72, 0x71, 0x2e, 0x76, 0x81, 0x13, 0xea, 0xc2, 0x27, 0x40, 0xe2, 0xa3, 0xb0, 0xc0, + 0x37, 0x60, 0x3c, 0x89, 0x85, 0x11, 0xb5, 0x7c, 0x09, 0x36, 0x14, 0x3b, 0x6e, 0x52, 0x91, 0x00, + 0x99, 0xe2, 0xe4, 0xfd, 0xdf, 0x7b, 0xbf, 0xf7, 0xcf, 0x33, 0xbe, 0x33, 0x13, 0xf2, 0x54, 0x48, + 0x3a, 0x07, 0xe0, 0x05, 0xcb, 0x14, 0x7d, 0x3d, 0x8e, 0x41, 0xb1, 0x31, 0x3d, 0x5b, 0x40, 0x71, + 0x1e, 0xe4, 0x85, 0x50, 0x82, 0xdc, 0x34, 0xa2, 0xc0, 0x8a, 0x82, 0x4a, 0xe4, 0xde, 0xe0, 0x82, + 0x0b, 0xad, 0xa1, 0xe5, 0xc9, 0xc8, 0xdd, 0xfd, 0xae, 0x9a, 0x9b, 0x7c, 0xa3, 0x3b, 0xa8, 0x74, + 0x31, 0x93, 0x60, 0xfa, 0x6d, 0x94, 0x39, 0xe3, 0x49, 0xc6, 0x54, 0x22, 0xb2, 0x4a, 0x7b, 0x9b, + 0x0b, 0xc1, 0x53, 0xa0, 0x2c, 0x4f, 0x28, 0xcb, 0x32, 0xa1, 0x74, 0x50, 0x9a, 0xa8, 0xff, 0x18, + 0x3b, 0x27, 0x65, 0xfe, 0x31, 0xc0, 0xa3, 0x34, 0x15, 0x6f, 0x58, 0x36, 0x83, 0x08, 0xce, 0x16, + 0x20, 0x15, 0x71, 0xf0, 0x55, 0xdd, 0x14, 0x0a, 0x07, 0xed, 0xa1, 0x7b, 0xd7, 0x22, 0xfb, 0x5a, + 0x47, 0xc0, 0x19, 0x36, 0x23, 0xe0, 0xa7, 0xf8, 0x56, 0x4b, 0x3d, 0x99, 0x8b, 0x4c, 0x02, 0x79, + 0x82, 0x47, 0x73, 0x80, 0xe7, 0xcc, 0x06, 0x74, 0xd9, 0x9d, 0xf0, 0x20, 0xe8, 0x70, 0x29, 0x68, + 0x56, 0x99, 0x96, 0x91, 0x68, 0x77, 0xde, 0xf8, 0xe4, 0x2f, 0x5b, 0xba, 0xc9, 0x3f, 0xf0, 0x61, + 0x1b, 0x1f, 0xc8, 0x31, 0xc6, 0xb5, 0x4d, 0x7a, 0x82, 0x9d, 0x70, 0xdf, 0x42, 0x94, 0x9e, 0x06, + 0xe6, 0x1f, 0x5a, 0x8c, 0xa7, 0x8c, 0x5b, 0x53, 0xa2, 0x46, 0xa6, 0xff, 0x19, 0x61, 0xb7, 0xad, + 0x7f, 0x35, 0xee, 0x09, 0xbe, 0xbe, 0x35, 0xae, 0x74, 0xd0, 0xde, 0xa5, 0x9e, 0xf3, 0x8e, 0x9a, + 0xf3, 0x4a, 0x32, 0x6d, 0x21, 0xbf, 0xfb, 0x4f, 0x72, 0xc3, 0xd3, 0x44, 0x0f, 0x7f, 0x0d, 0xf1, + 0x65, 0x8d, 0x4e, 0xbe, 0x20, 0xbc, 0xdb, 0xec, 0x4b, 0xc6, 0x9d, 0x78, 0x5d, 0x9b, 0xe2, 0x86, + 0x7d, 0x52, 0x0c, 0x8d, 0x7f, 0xf4, 0xfe, 0xdb, 0xcf, 0x8f, 0xc3, 0x87, 0xe4, 0x01, 0xfd, 0xcb, + 0xd2, 0xd7, 0xe6, 0xd1, 0x77, 0xd5, 0xf2, 0x2d, 0xed, 0x09, 0x96, 0xe4, 0x13, 0xc2, 0xa3, 0x2d, + 0xef, 0x49, 0x0f, 0x12, 0xbb, 0x28, 0xee, 0xa4, 0x57, 0x4e, 0x85, 0x7f, 0x5f, 0xe3, 0x4f, 0xc8, + 0xf8, 0xff, 0xf0, 0x65, 0x4d, 0x7d, 0x34, 0xfd, 0xba, 0xf2, 0xd0, 0xc5, 0xca, 0x43, 0x3f, 0x56, + 0x1e, 0xfa, 0xb0, 0xf6, 0x06, 0x17, 0x6b, 0x6f, 0xf0, 0x7d, 0xed, 0x0d, 0x9e, 0x1d, 0xf2, 0x44, + 0xbd, 0x5c, 0xc4, 0xc1, 0x4c, 0x9c, 0xda, 0xb2, 0xe6, 0x71, 0x28, 0x5f, 0xbc, 0xa2, 0x6f, 0xeb, + 0x1e, 0xea, 0x3c, 0x07, 0x19, 0x5f, 0xd1, 0x77, 0x78, 0xf2, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xb8, + 0x46, 0x4c, 0xba, 0x8b, 0x04, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// 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 { + // FeeAllowance returns fee granted to the grantee by the granter. + FeeAllowance(ctx context.Context, in *QueryFeeAllowanceRequest, opts ...grpc.CallOption) (*QueryFeeAllowanceResponse, error) + // FeeAllowances returns all the grants for address. + FeeAllowances(ctx context.Context, in *QueryFeeAllowancesRequest, opts ...grpc.CallOption) (*QueryFeeAllowancesResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) FeeAllowance(ctx context.Context, in *QueryFeeAllowanceRequest, opts ...grpc.CallOption) (*QueryFeeAllowanceResponse, error) { + out := new(QueryFeeAllowanceResponse) + err := c.cc.Invoke(ctx, "/cosmos.feegrant.v1beta1.Query/FeeAllowance", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) FeeAllowances(ctx context.Context, in *QueryFeeAllowancesRequest, opts ...grpc.CallOption) (*QueryFeeAllowancesResponse, error) { + out := new(QueryFeeAllowancesResponse) + err := c.cc.Invoke(ctx, "/cosmos.feegrant.v1beta1.Query/FeeAllowances", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // FeeAllowance returns fee granted to the grantee by the granter. + FeeAllowance(context.Context, *QueryFeeAllowanceRequest) (*QueryFeeAllowanceResponse, error) + // FeeAllowances returns all the grants for address. + FeeAllowances(context.Context, *QueryFeeAllowancesRequest) (*QueryFeeAllowancesResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) FeeAllowance(ctx context.Context, req *QueryFeeAllowanceRequest) (*QueryFeeAllowanceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FeeAllowance not implemented") +} +func (*UnimplementedQueryServer) FeeAllowances(ctx context.Context, req *QueryFeeAllowancesRequest) (*QueryFeeAllowancesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FeeAllowances not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_FeeAllowance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryFeeAllowanceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).FeeAllowance(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.feegrant.v1beta1.Query/FeeAllowance", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).FeeAllowance(ctx, req.(*QueryFeeAllowanceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_FeeAllowances_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryFeeAllowancesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).FeeAllowances(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.feegrant.v1beta1.Query/FeeAllowances", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).FeeAllowances(ctx, req.(*QueryFeeAllowancesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.feegrant.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "FeeAllowance", + Handler: _Query_FeeAllowance_Handler, + }, + { + MethodName: "FeeAllowances", + Handler: _Query_FeeAllowances_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/feegrant/v1beta1/query.proto", +} + +func (m *QueryFeeAllowanceRequest) 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 *QueryFeeAllowanceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFeeAllowanceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryFeeAllowanceResponse) 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 *QueryFeeAllowanceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFeeAllowanceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.FeeAllowance != nil { + { + size, err := m.FeeAllowance.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 *QueryFeeAllowancesRequest) 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 *QueryFeeAllowancesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFeeAllowancesRequest) 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.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryFeeAllowancesResponse) 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 *QueryFeeAllowancesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFeeAllowancesResponse) 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.FeeAllowances) > 0 { + for iNdEx := len(m.FeeAllowances) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FeeAllowances[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 encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryFeeAllowanceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFeeAllowanceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FeeAllowance != nil { + l = m.FeeAllowance.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFeeAllowancesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFeeAllowancesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.FeeAllowances) > 0 { + for _, e := range m.FeeAllowances { + 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 sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryFeeAllowanceRequest) 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: QueryFeeAllowanceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFeeAllowanceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Granter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Grantee = string(dAtA[iNdEx:postIndex]) + 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 *QueryFeeAllowanceResponse) 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: QueryFeeAllowanceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFeeAllowanceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeAllowance", 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.FeeAllowance == nil { + m.FeeAllowance = &FeeAllowanceGrant{} + } + if err := m.FeeAllowance.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 *QueryFeeAllowancesRequest) 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: QueryFeeAllowancesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFeeAllowancesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Grantee = string(dAtA[iNdEx:postIndex]) + 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.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 *QueryFeeAllowancesResponse) 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: QueryFeeAllowancesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFeeAllowancesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeAllowances", 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.FeeAllowances = append(m.FeeAllowances, &FeeAllowanceGrant{}) + if err := m.FeeAllowances[len(m.FeeAllowances)-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 skipQuery(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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/feegrant/types/query.pb.gw.go b/x/feegrant/types/query.pb.gw.go new file mode 100644 index 000000000000..499e4a441d18 --- /dev/null +++ b/x/feegrant/types/query.pb.gw.go @@ -0,0 +1,322 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/feegrant/v1beta1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage + +func request_Query_FeeAllowance_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFeeAllowanceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["granter"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "granter") + } + + protoReq.Granter, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "granter", err) + } + + val, ok = pathParams["grantee"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "grantee") + } + + protoReq.Grantee, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "grantee", err) + } + + msg, err := client.FeeAllowance(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_FeeAllowance_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFeeAllowanceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["granter"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "granter") + } + + protoReq.Granter, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "granter", err) + } + + val, ok = pathParams["grantee"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "grantee") + } + + protoReq.Grantee, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "grantee", err) + } + + msg, err := server.FeeAllowance(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_FeeAllowances_0 = &utilities.DoubleArray{Encoding: map[string]int{"grantee": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_FeeAllowances_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFeeAllowancesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["grantee"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "grantee") + } + + protoReq.Grantee, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "grantee", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_FeeAllowances_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.FeeAllowances(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_FeeAllowances_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFeeAllowancesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["grantee"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "grantee") + } + + protoReq.Grantee, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "grantee", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_FeeAllowances_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.FeeAllowances(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// 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_FeeAllowance_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_FeeAllowance_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_FeeAllowance_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FeeAllowances_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_FeeAllowances_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_FeeAllowances_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_FeeAllowance_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_FeeAllowance_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_FeeAllowance_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FeeAllowances_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_FeeAllowances_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_FeeAllowances_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +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_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))) +) + +var ( + forward_Query_FeeAllowance_0 = runtime.ForwardResponseMessage + + forward_Query_FeeAllowances_0 = runtime.ForwardResponseMessage +) diff --git a/x/feegrant/types/tx.pb.go b/x/feegrant/types/tx.pb.go new file mode 100644 index 000000000000..d3080f10e3df --- /dev/null +++ b/x/feegrant/types/tx.pb.go @@ -0,0 +1,1033 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/feegrant/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + _ "github.com/regen-network/cosmos-proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + 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 + +// MsgGrantFeeAllowance adds permission for Grantee to spend up to Allowance +// of fees from the account of Granter. +type MsgGrantFeeAllowance struct { + Granter string `protobuf:"bytes,1,opt,name=granter,proto3" json:"granter,omitempty"` + Grantee string `protobuf:"bytes,2,opt,name=grantee,proto3" json:"grantee,omitempty"` + Allowance *types.Any `protobuf:"bytes,3,opt,name=allowance,proto3" json:"allowance,omitempty"` +} + +func (m *MsgGrantFeeAllowance) Reset() { *m = MsgGrantFeeAllowance{} } +func (m *MsgGrantFeeAllowance) String() string { return proto.CompactTextString(m) } +func (*MsgGrantFeeAllowance) ProtoMessage() {} +func (*MsgGrantFeeAllowance) Descriptor() ([]byte, []int) { + return fileDescriptor_dd44ad7946dad783, []int{0} +} +func (m *MsgGrantFeeAllowance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGrantFeeAllowance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGrantFeeAllowance.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 *MsgGrantFeeAllowance) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGrantFeeAllowance.Merge(m, src) +} +func (m *MsgGrantFeeAllowance) XXX_Size() int { + return m.Size() +} +func (m *MsgGrantFeeAllowance) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGrantFeeAllowance.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGrantFeeAllowance proto.InternalMessageInfo + +func (m *MsgGrantFeeAllowance) GetGranter() string { + if m != nil { + return m.Granter + } + return "" +} + +func (m *MsgGrantFeeAllowance) GetGrantee() string { + if m != nil { + return m.Grantee + } + return "" +} + +func (m *MsgGrantFeeAllowance) GetAllowance() *types.Any { + if m != nil { + return m.Allowance + } + return nil +} + +// MsgGrantFeeAllowanceResponse defines the Msg/GrantFeeAllowanceResponse response type. +type MsgGrantFeeAllowanceResponse struct { +} + +func (m *MsgGrantFeeAllowanceResponse) Reset() { *m = MsgGrantFeeAllowanceResponse{} } +func (m *MsgGrantFeeAllowanceResponse) String() string { return proto.CompactTextString(m) } +func (*MsgGrantFeeAllowanceResponse) ProtoMessage() {} +func (*MsgGrantFeeAllowanceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd44ad7946dad783, []int{1} +} +func (m *MsgGrantFeeAllowanceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGrantFeeAllowanceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGrantFeeAllowanceResponse.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 *MsgGrantFeeAllowanceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGrantFeeAllowanceResponse.Merge(m, src) +} +func (m *MsgGrantFeeAllowanceResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgGrantFeeAllowanceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGrantFeeAllowanceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGrantFeeAllowanceResponse proto.InternalMessageInfo + +// MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee. +type MsgRevokeFeeAllowance struct { + Granter string `protobuf:"bytes,1,opt,name=granter,proto3" json:"granter,omitempty"` + Grantee string `protobuf:"bytes,2,opt,name=grantee,proto3" json:"grantee,omitempty"` +} + +func (m *MsgRevokeFeeAllowance) Reset() { *m = MsgRevokeFeeAllowance{} } +func (m *MsgRevokeFeeAllowance) String() string { return proto.CompactTextString(m) } +func (*MsgRevokeFeeAllowance) ProtoMessage() {} +func (*MsgRevokeFeeAllowance) Descriptor() ([]byte, []int) { + return fileDescriptor_dd44ad7946dad783, []int{2} +} +func (m *MsgRevokeFeeAllowance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRevokeFeeAllowance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRevokeFeeAllowance.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 *MsgRevokeFeeAllowance) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRevokeFeeAllowance.Merge(m, src) +} +func (m *MsgRevokeFeeAllowance) XXX_Size() int { + return m.Size() +} +func (m *MsgRevokeFeeAllowance) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRevokeFeeAllowance.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRevokeFeeAllowance proto.InternalMessageInfo + +func (m *MsgRevokeFeeAllowance) GetGranter() string { + if m != nil { + return m.Granter + } + return "" +} + +func (m *MsgRevokeFeeAllowance) GetGrantee() string { + if m != nil { + return m.Grantee + } + return "" +} + +// MsgRevokeFeeAllowanceResponse defines the Msg/RevokeFeeAllowanceResponse response type. +type MsgRevokeFeeAllowanceResponse struct { +} + +func (m *MsgRevokeFeeAllowanceResponse) Reset() { *m = MsgRevokeFeeAllowanceResponse{} } +func (m *MsgRevokeFeeAllowanceResponse) String() string { return proto.CompactTextString(m) } +func (*MsgRevokeFeeAllowanceResponse) ProtoMessage() {} +func (*MsgRevokeFeeAllowanceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd44ad7946dad783, []int{3} +} +func (m *MsgRevokeFeeAllowanceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRevokeFeeAllowanceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRevokeFeeAllowanceResponse.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 *MsgRevokeFeeAllowanceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRevokeFeeAllowanceResponse.Merge(m, src) +} +func (m *MsgRevokeFeeAllowanceResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgRevokeFeeAllowanceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRevokeFeeAllowanceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRevokeFeeAllowanceResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgGrantFeeAllowance)(nil), "cosmos.feegrant.v1beta1.MsgGrantFeeAllowance") + proto.RegisterType((*MsgGrantFeeAllowanceResponse)(nil), "cosmos.feegrant.v1beta1.MsgGrantFeeAllowanceResponse") + proto.RegisterType((*MsgRevokeFeeAllowance)(nil), "cosmos.feegrant.v1beta1.MsgRevokeFeeAllowance") + proto.RegisterType((*MsgRevokeFeeAllowanceResponse)(nil), "cosmos.feegrant.v1beta1.MsgRevokeFeeAllowanceResponse") +} + +func init() { proto.RegisterFile("cosmos/feegrant/v1beta1/tx.proto", fileDescriptor_dd44ad7946dad783) } + +var fileDescriptor_dd44ad7946dad783 = []byte{ + // 345 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x48, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x4b, 0x4d, 0x4d, 0x2f, 0x4a, 0xcc, 0x2b, 0xd1, 0x2f, 0x33, 0x4c, 0x4a, + 0x2d, 0x49, 0x34, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x87, 0xa8, + 0xd0, 0x83, 0xa9, 0xd0, 0x83, 0xaa, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xab, 0xd1, 0x07, + 0xb1, 0x20, 0xca, 0xa5, 0x24, 0xd3, 0xf3, 0xf3, 0xd3, 0x73, 0x52, 0xf5, 0xc1, 0xbc, 0xa4, 0xd2, + 0x34, 0xfd, 0xc4, 0xbc, 0x4a, 0x98, 0x14, 0xc4, 0xa4, 0x78, 0x88, 0x1e, 0xa8, 0xb1, 0x60, 0x8e, + 0xd2, 0x44, 0x46, 0x2e, 0x11, 0xdf, 0xe2, 0x74, 0x77, 0x90, 0x05, 0x6e, 0xa9, 0xa9, 0x8e, 0x39, + 0x39, 0xf9, 0xe5, 0x89, 0x79, 0xc9, 0xa9, 0x42, 0x12, 0x5c, 0xec, 0x60, 0x5b, 0x53, 0x8b, 0x24, + 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0x60, 0x5c, 0x84, 0x4c, 0xaa, 0x04, 0x13, 0xb2, 0x4c, 0xaa, + 0x90, 0x2b, 0x17, 0x67, 0x22, 0xcc, 0x00, 0x09, 0x66, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x11, 0x3d, + 0x88, 0xb3, 0xf4, 0x60, 0xce, 0xd2, 0x73, 0xcc, 0xab, 0x74, 0x12, 0x3c, 0xb5, 0x45, 0x97, 0x17, + 0xd9, 0x3a, 0xcf, 0x20, 0x84, 0x4e, 0x25, 0x39, 0x2e, 0x19, 0x6c, 0x4e, 0x0a, 0x4a, 0x2d, 0x2e, + 0xc8, 0xcf, 0x2b, 0x4e, 0x55, 0xf2, 0xe6, 0x12, 0xf5, 0x2d, 0x4e, 0x0f, 0x4a, 0x2d, 0xcb, 0xcf, + 0x4e, 0xa5, 0xd4, 0xcd, 0x4a, 0xf2, 0x5c, 0xb2, 0x58, 0x0d, 0x83, 0xd9, 0x66, 0xf4, 0x8f, 0x91, + 0x8b, 0xd9, 0xb7, 0x38, 0x5d, 0xa8, 0x92, 0x4b, 0x10, 0x33, 0x94, 0x74, 0xf5, 0x70, 0x44, 0x92, + 0x1e, 0x36, 0x1f, 0x48, 0x99, 0x92, 0xa4, 0x1c, 0xe6, 0x04, 0xa1, 0x1a, 0x2e, 0x21, 0x2c, 0xbe, + 0xd5, 0xc3, 0x67, 0x18, 0xa6, 0x7a, 0x29, 0x33, 0xd2, 0xd4, 0xc3, 0x6c, 0x77, 0x72, 0x3f, 0xf1, + 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, + 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0xdd, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, + 0xbd, 0xe4, 0xfc, 0x5c, 0x68, 0xaa, 0x82, 0x52, 0xba, 0xc5, 0x29, 0xd9, 0xfa, 0x15, 0x88, 0xb4, + 0x5d, 0x52, 0x59, 0x90, 0x5a, 0x9c, 0xc4, 0x06, 0x4e, 0x03, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x7b, 0xbe, 0x24, 0x15, 0xfb, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // GrantFeeAllowance grants fee allowance to the grantee on the granter's + // account with the provided expiration time. + GrantFeeAllowance(ctx context.Context, in *MsgGrantFeeAllowance, opts ...grpc.CallOption) (*MsgGrantFeeAllowanceResponse, error) + // RevokeFeeAllowance revokes any fee allowance of granter's account that + // has been granted to the grantee. + RevokeFeeAllowance(ctx context.Context, in *MsgRevokeFeeAllowance, opts ...grpc.CallOption) (*MsgRevokeFeeAllowanceResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) GrantFeeAllowance(ctx context.Context, in *MsgGrantFeeAllowance, opts ...grpc.CallOption) (*MsgGrantFeeAllowanceResponse, error) { + out := new(MsgGrantFeeAllowanceResponse) + err := c.cc.Invoke(ctx, "/cosmos.feegrant.v1beta1.Msg/GrantFeeAllowance", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) RevokeFeeAllowance(ctx context.Context, in *MsgRevokeFeeAllowance, opts ...grpc.CallOption) (*MsgRevokeFeeAllowanceResponse, error) { + out := new(MsgRevokeFeeAllowanceResponse) + err := c.cc.Invoke(ctx, "/cosmos.feegrant.v1beta1.Msg/RevokeFeeAllowance", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // GrantFeeAllowance grants fee allowance to the grantee on the granter's + // account with the provided expiration time. + GrantFeeAllowance(context.Context, *MsgGrantFeeAllowance) (*MsgGrantFeeAllowanceResponse, error) + // RevokeFeeAllowance revokes any fee allowance of granter's account that + // has been granted to the grantee. + RevokeFeeAllowance(context.Context, *MsgRevokeFeeAllowance) (*MsgRevokeFeeAllowanceResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) GrantFeeAllowance(ctx context.Context, req *MsgGrantFeeAllowance) (*MsgGrantFeeAllowanceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GrantFeeAllowance not implemented") +} +func (*UnimplementedMsgServer) RevokeFeeAllowance(ctx context.Context, req *MsgRevokeFeeAllowance) (*MsgRevokeFeeAllowanceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RevokeFeeAllowance not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_GrantFeeAllowance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgGrantFeeAllowance) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).GrantFeeAllowance(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.feegrant.v1beta1.Msg/GrantFeeAllowance", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).GrantFeeAllowance(ctx, req.(*MsgGrantFeeAllowance)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_RevokeFeeAllowance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgRevokeFeeAllowance) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).RevokeFeeAllowance(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.feegrant.v1beta1.Msg/RevokeFeeAllowance", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).RevokeFeeAllowance(ctx, req.(*MsgRevokeFeeAllowance)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.feegrant.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GrantFeeAllowance", + Handler: _Msg_GrantFeeAllowance_Handler, + }, + { + MethodName: "RevokeFeeAllowance", + Handler: _Msg_RevokeFeeAllowance_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/feegrant/v1beta1/tx.proto", +} + +func (m *MsgGrantFeeAllowance) 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 *MsgGrantFeeAllowance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGrantFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Allowance != nil { + { + size, err := m.Allowance.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintTx(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintTx(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgGrantFeeAllowanceResponse) 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 *MsgGrantFeeAllowanceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGrantFeeAllowanceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgRevokeFeeAllowance) 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 *MsgRevokeFeeAllowance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRevokeFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintTx(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintTx(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgRevokeFeeAllowanceResponse) 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 *MsgRevokeFeeAllowanceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRevokeFeeAllowanceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgGrantFeeAllowance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Allowance != nil { + l = m.Allowance.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgGrantFeeAllowanceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgRevokeFeeAllowance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgRevokeFeeAllowanceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgGrantFeeAllowance) 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: MsgGrantFeeAllowance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGrantFeeAllowance: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", 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.Granter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", 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.Grantee = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Allowance", 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 + } + if m.Allowance == nil { + m.Allowance = &types.Any{} + } + if err := m.Allowance.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 *MsgGrantFeeAllowanceResponse) 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: MsgGrantFeeAllowanceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGrantFeeAllowanceResponse: 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 *MsgRevokeFeeAllowance) 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: MsgRevokeFeeAllowance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRevokeFeeAllowance: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", 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.Granter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", 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.Grantee = string(dAtA[iNdEx:postIndex]) + 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 *MsgRevokeFeeAllowanceResponse) 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: MsgRevokeFeeAllowanceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRevokeFeeAllowanceResponse: 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 skipTx(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, ErrIntOverflowTx + } + 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, ErrIntOverflowTx + } + 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, ErrIntOverflowTx + } + 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, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/genutil/client/cli/gentx_test.go b/x/genutil/client/cli/gentx_test.go index d15ee8896265..b5733675951a 100644 --- a/x/genutil/client/cli/gentx_test.go +++ b/x/genutil/client/cli/gentx_test.go @@ -72,7 +72,7 @@ func (s *IntegrationTestSuite) TestGenTxCmd() { err := cmd.ExecuteContext(ctx) s.Require().NoError(err) - // Validate generated transaction. + // validate generated transaction. open, err := os.Open(genTxFile) s.Require().NoError(err) diff --git a/x/genutil/gentx_test.go b/x/genutil/gentx_test.go index b4b843bc986e..79c5bad39c78 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 bfaeb0c59168..7a65531e453f 100644 --- a/x/genutil/module.go +++ b/x/genutil/module.go @@ -110,3 +110,6 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data j func (am AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { return am.DefaultGenesis(cdc) } + +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } diff --git a/x/genutil/types/genesis.pb.go b/x/genutil/types/genesis.pb.go index e9a9ebf9ae1e..369cd0edfaf6 100644 --- a/x/genutil/types/genesis.pb.go +++ b/x/genutil/types/genesis.pb.go @@ -228,10 +228,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/gov/abci_test.go b/x/gov/abci_test.go index bfda1b3590b6..cefd12eedf5a 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 981b13024c47..54bcdde07d48 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/query.go b/x/gov/client/cli/query.go index 8c67cd96e5e1..b382f540e26b 100644 --- a/x/gov/client/cli/query.go +++ b/x/gov/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "strconv" "strings" @@ -74,7 +73,7 @@ $ %s query gov proposal 1 // Query the proposal res, err := queryClient.Proposal( - context.Background(), + cmd.Context(), &types.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { @@ -149,7 +148,7 @@ $ %s query gov proposals --page=2 --limit=100 } res, err := queryClient.Proposals( - context.Background(), + cmd.Context(), &types.QueryProposalsRequest{ ProposalStatus: proposalStatus, Voter: bechVoterAddr, @@ -208,8 +207,9 @@ $ %s query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk } // check to see if the proposal is in the store + ctx := cmd.Context() _, err = queryClient.Proposal( - context.Background(), + ctx, &types.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { @@ -222,7 +222,7 @@ $ %s query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk } res, err := queryClient.Vote( - context.Background(), + ctx, &types.QueryVoteRequest{ProposalId: proposalID, Voter: args[1]}, ) if err != nil { @@ -282,8 +282,9 @@ $ %[1]s query gov votes 1 --page=2 --limit=100 } // check to see if the proposal is in the store + ctx := cmd.Context() proposalRes, err := queryClient.Proposal( - context.Background(), + ctx, &types.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { @@ -315,7 +316,7 @@ $ %[1]s query gov votes 1 --page=2 --limit=100 } res, err := queryClient.Votes( - context.Background(), + ctx, &types.QueryVotesRequest{ProposalId: proposalID, Pagination: pageReq}, ) @@ -364,8 +365,9 @@ $ %s query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk } // check to see if the proposal is in the store + ctx := cmd.Context() _, err = queryClient.Proposal( - context.Background(), + ctx, &types.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { @@ -378,7 +380,7 @@ $ %s query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk } res, err := queryClient.Deposit( - context.Background(), + ctx, &types.QueryDepositRequest{ProposalId: proposalID, Depositor: args[1]}, ) if err != nil { @@ -434,8 +436,9 @@ $ %s query gov deposits 1 } // check to see if the proposal is in the store + ctx := cmd.Context() proposalRes, err := queryClient.Proposal( - context.Background(), + ctx, &types.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { @@ -464,7 +467,7 @@ $ %s query gov deposits 1 } res, err := queryClient.Deposits( - context.Background(), + ctx, &types.QueryDepositsRequest{ProposalId: proposalID, Pagination: pageReq}, ) @@ -512,8 +515,9 @@ $ %s query gov tally 1 } // check to see if the proposal is in the store + ctx := cmd.Context() _, err = queryClient.Proposal( - context.Background(), + ctx, &types.QueryProposalRequest{ProposalId: proposalID}, ) if err != nil { @@ -522,7 +526,7 @@ $ %s query gov tally 1 // Query store res, err := queryClient.TallyResult( - context.Background(), + ctx, &types.QueryTallyResultRequest{ProposalId: proposalID}, ) if err != nil { @@ -561,8 +565,9 @@ $ %s query gov params queryClient := types.NewQueryClient(clientCtx) // Query store for all 3 params + ctx := cmd.Context() votingRes, err := queryClient.Params( - context.Background(), + ctx, &types.QueryParamsRequest{ParamsType: "voting"}, ) if err != nil { @@ -570,7 +575,7 @@ $ %s query gov params } tallyRes, err := queryClient.Params( - context.Background(), + ctx, &types.QueryParamsRequest{ParamsType: "tallying"}, ) if err != nil { @@ -578,7 +583,7 @@ $ %s query gov params } depositRes, err := queryClient.Params( - context.Background(), + ctx, &types.QueryParamsRequest{ParamsType: "deposit"}, ) if err != nil { @@ -626,7 +631,7 @@ $ %s query gov param deposit // Query store res, err := queryClient.Params( - context.Background(), + cmd.Context(), &types.QueryParamsRequest{ParamsType: args[0]}, ) if err != nil { diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 16ca7f7c68bc..8ea8f00238bf 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 8e2d4efa9ffa..9e4984a9aaab 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_test.go b/x/gov/client/rest/rest_test.go index 0b1a274022cb..adca107ada07 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 535cb8d09ca6..ce9d5de725a5 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 e543f526b3f0..de5f5f7c5e47 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, - Option: voteMsg.Option, + 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, + 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 81b15a457fb1..fb7d002c8583 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 84c2884d1a7f..0e0bf04de7db 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,6 +26,20 @@ func NormalizeVoteOption(option string) string { } } +// 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 { diff --git a/x/gov/client/utils/utils_test.go b/x/gov/client/utils/utils_test.go new file mode 100644 index 000000000000..0da66b3c29ba --- /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 553edbaa0f77..62a194d9c0a9 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 2fb3aea9467b..f504ef5a1f45 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 25d97bfabb47..6af451d3dd31 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/common_test.go b/x/gov/keeper/common_test.go index 7ba9c5827f87..c7516eb69773 100644 --- a/x/gov/keeper/common_test.go +++ b/x/gov/keeper/common_test.go @@ -21,10 +21,10 @@ func createValidators(t *testing.T, ctx sdk.Context, app *simapp.SimApp, powers addrs := simapp.AddTestAddrsIncremental(app, ctx, 5, sdk.NewInt(30000000)) valAddrs := simapp.ConvertAddrsToValAddrs(addrs) pks := simapp.CreateTestPubKeys(5) + cdc := simapp.MakeTestEncodingConfig().Marshaler - appCodec, _ := simapp.MakeCodecs() app.StakingKeeper = stakingkeeper.NewKeeper( - appCodec, + cdc, app.GetKey(stakingtypes.StoreKey), app.AccountKeeper, app.BankKeeper, diff --git a/x/gov/keeper/grpc_query_test.go b/x/gov/keeper/grpc_query_test.go index dc330d734829..4789f954e2dc 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/msg_server.go b/x/gov/keeper/msg_server.go index 39ddf3ab4539..86e6e9326704 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 1a833737d940..7f62d5a56cc4 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 9960fb116bed..16f2694cd50d 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 cddfd59027dd..f1f1f325455c 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 77f468cd2c0b..ef9cbd02789a 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 a35569bc3465..b554826acd9e 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 5f5bf0cc73a4..ae6ebc4a5693 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/v040/migrate.go b/x/gov/legacy/v040/migrate.go index 2accb74284cd..69b32a621cc5 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/module.go b/x/gov/module.go index ad2191660c08..b72a4bfe5f5c 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -177,6 +177,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(gs) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock performs a no-op. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} diff --git a/x/gov/simulation/decoder.go b/x/gov/simulation/decoder.go index 75cb4a5fccc8..180754ddee08 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 7a5b0fc1bc9d..4f6273a171e4 100644 --- a/x/gov/simulation/decoder_test.go +++ b/x/gov/simulation/decoder_test.go @@ -22,52 +22,71 @@ var ( ) func TestDecodeStore(t *testing.T) { - cdc, _ := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Marshaler dec := simulation.NewDecodeStore(cdc) 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 2ea89ca97bb1..8ce709da834f 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( @@ -162,7 +174,7 @@ func SimulateMsgSubmitProposal( return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - opMsg := simtypes.NewOperationMsg(msg, true, "") + opMsg := simtypes.NewOperationMsg(msg, true, "", nil) // get the submitted proposal ID proposalID, err := k.GetProposalID(ctx) @@ -248,7 +260,7 @@ func SimulateMsgDeposit(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Ke return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } @@ -311,7 +323,70 @@ func operationSimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, k kee return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil + } +} + +// 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 } } @@ -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 9c6e8306f821..2d3bbe22fcd7 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/01_concepts.md b/x/gov/spec/01_concepts.md index 0acbfa3b709b..becb7cc6de2f 100644 --- a/x/gov/spec/01_concepts.md +++ b/x/gov/spec/01_concepts.md @@ -29,18 +29,24 @@ its unique `proposalID`. ### Proposal types -In the initial version of the governance module, there are two types of -proposal: +In the initial version of the governance module, there are five types of +proposals: -- `PlainTextProposal` All the proposals that do not involve a modification of +- `TextProposal` All the proposals that do not involve a modification of the source code go under this type. For example, an opinion poll would use a - proposal of type `PlainTextProposal`. + proposal of type `TextProposal`. - `SoftwareUpgradeProposal`. If accepted, validators are expected to update their software in accordance with the proposal. They must do so by following a 2-steps process described in the [Software Upgrade](#software-upgrade) section below. Software upgrade roadmap may be discussed and agreed on via - `PlainTextProposals`, but actual software upgrades must be performed via + `TextProposals`, but actual software upgrades must be performed via `SoftwareUpgradeProposals`. +- `CommunityPoolSpendProposal` details a proposal for use of community funds, + together with how many coins are proposed to be spent, and to which recipient account. +- `ParameterChangeProposal` defines a proposal to change one or + more parameters. If accepted, the requested parameter change is updated + automatically by the proposal handler upon conclusion of the voting period. +- `CancelSoftwareUpgradeProposal` is a gov Content type for cancelling a software upgrade. Other modules may expand upon the governance module by implementing their own proposal types and handlers. These types are registered and processed through the diff --git a/x/gov/spec/02_state.md b/x/gov/spec/02_state.md index ba3b12b3f3a7..4175367fdfb8 100644 --- a/x/gov/spec/02_state.md +++ b/x/gov/spec/02_state.md @@ -11,26 +11,14 @@ be one active parameter set at any given time. If governance wants to change a parameter set, either to modify a value or add/remove a parameter field, a new parameter set has to be created and the previous one rendered inactive. -```go -type DepositParams struct { - MinDeposit sdk.Coins // Minimum deposit for a proposal to enter voting period. - MaxDepositPeriod time.Time // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months -} -``` +### DepositParams ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/gov.proto#L127-L145 -```go -type VotingParams struct { - VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks -} -``` +### VotingParams ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/gov.proto#L147-L156 -```go -type TallyParams struct { - Quorum sdk.Dec // Minimum percentage of stake that needs to vote for a proposal to be considered valid - Threshold sdk.Dec // Minimum proportion of Yes votes for proposal to pass. Initial value: 0.5 - Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 -} -``` +### TallyParams ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/gov.proto#L158-L183 Parameters are stored in a global `GlobalParams` KVStore. @@ -68,12 +56,7 @@ const ( ## Deposit -```go - type Deposit struct { - Amount sdk.Coins // Amount of coins deposited by depositor - Depositor crypto.address // Address of depositor - } -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/gov.proto#L43-L53 ## ValidatorGovInfo @@ -92,22 +75,7 @@ This type is used in a temp map when tallying what this proposal is about, and other fields, which are the mutable state of the governance process. -```go -type Proposal struct { - Content // Proposal content interface - - ProposalID uint64 - Status ProposalStatus // Status of the Proposal {Pending, Active, Passed, Rejected} - FinalTallyResult TallyResult // Result of Tallies - - SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included - DepositEndTime time.Time // Time that the Proposal would expire if deposit amount isn't met - TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit - - VotingStartTime time.Time // Time of the block where MinDeposit was reached. -1 if MinDeposit is not reached - VotingEndTime time.Time // Time that the VotingPeriod for this proposal will end and votes will be tallied -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/gov.proto#L55-L77 ```go type Content interface { diff --git a/x/gov/spec/03_messages.md b/x/gov/spec/03_messages.md index cac55b5ed945..59aa255f4cb4 100644 --- a/x/gov/spec/03_messages.md +++ b/x/gov/spec/03_messages.md @@ -6,18 +6,12 @@ order: 3 ## Proposal Submission -Proposals can be submitted by any Atom holder via a `TxGovSubmitProposal` +Proposals can be submitted by any account via a `MsgSubmitProposal` transaction. -```go -type TxGovSubmitProposal struct { - Content Content - InitialDeposit sdk.Coins - Proposer sdk.AccAddress -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/tx.proto#L24-L39 -The `Content` of a `TxGovSubmitProposal` message must have an appropriate router +The `Content` of a `MsgSubmitProposal` message must have an appropriate router set in the governance module. **State modifications:** @@ -30,12 +24,12 @@ set in the governance module. - Push `proposalID` in `ProposalProcessingQueue` - Transfer `InitialDeposit` from the `Proposer` to the governance `ModuleAccount` -A `TxGovSubmitProposal` transaction can be handled according to the following +A `MsgSubmitProposal` transaction can be handled according to the following pseudocode. ```go // PSEUDOCODE // -// Check if TxGovSubmitProposal is valid. If it is, create proposal // +// Check if MsgSubmitProposal is valid. If it is, create proposal // upon receiving txGovSubmitProposal from sender do @@ -79,14 +73,9 @@ upon receiving txGovSubmitProposal from sender do Once a proposal is submitted, if `Proposal.TotalDeposit < ActiveParam.MinDeposit`, Atom holders can send -`TxGovDeposit` transactions to increase the proposal's deposit. +`MsgDeposit` transactions to increase the proposal's deposit. -```go -type TxGovDeposit struct { - ProposalID int64 // ID of the proposal - Deposit sdk.Coins // Number of Atoms to add to the proposal's deposit -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/tx.proto#L61-L72 **State modifications:** @@ -97,12 +86,12 @@ type TxGovDeposit struct { - Push `proposalID` in `ProposalProcessingQueueEnd` - Transfer `Deposit` from the `proposer` to the governance `ModuleAccount` -A `TxGovDeposit` transaction has to go through a number of checks to be valid. +A `MsgDeposit` transaction has to go through a number of checks to be valid. These checks are outlined in the following pseudocode. ```go // PSEUDOCODE // -// Check if TxGovDeposit is valid. If it is, increase deposit and check if MinDeposit is reached +// Check if MsgDeposit is valid. If it is, increase deposit and check if MinDeposit is reached upon receiving txGovDeposit from sender do // check if proposal is correctly formatted. Includes fee payment. @@ -149,15 +138,10 @@ upon receiving txGovDeposit from sender do ## Vote Once `ActiveParam.MinDeposit` is reached, voting period starts. From there, -bonded Atom holders are able to send `TxGovVote` transactions to cast their +bonded Atom holders are able to send `MsgVote` transactions to cast their vote on the proposal. -```go - type TxGovVote struct { - ProposalID int64 // proposalID of the proposal - Vote byte // option from OptionSet chosen by the voter - } -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/tx.proto#L46-L56 **State modifications:** @@ -165,12 +149,12 @@ vote on the proposal. _Note: Gas cost for this message has to take into account the future tallying of the vote in EndBlocker_ -Next is a pseudocode proposal of the way `TxGovVote` transactions are +Next is a pseudocode outline of the way `MsgVote` transactions are handled: ```go // PSEUDOCODE // - // Check if TxGovVote is valid. If it is, count vote// + // Check if MsgVote is valid. If it is, count vote// upon receiving txGovVote from sender do // check if proposal is correctly formatted. Includes fee payment. diff --git a/x/gov/spec/04_events.md b/x/gov/spec/04_events.md index dc3b1bf3a231..300d4d187c80 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 46deb5c59ecb..e59e0ef49c66 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 f862a9bce563..1ad39abca0ec 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/genesis.pb.go b/x/gov/types/genesis.pb.go index 67022597280d..4df93da3d07b 100644 --- a/x/gov/types/genesis.pb.go +++ b/x/gov/types/genesis.pb.go @@ -568,10 +568,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/gov/types/gov.pb.go b/x/gov/types/gov.pb.go index 922186d01f0d..caa52a67a818 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 @@ -1355,10 +1565,7 @@ func (m *TextProposal) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGov - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGov } if (iNdEx + skippy) > l { @@ -1493,10 +1700,7 @@ func (m *Deposit) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGov - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGov } if (iNdEx + skippy) > l { @@ -1819,10 +2023,7 @@ func (m *Proposal) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGov - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGov } if (iNdEx + skippy) > l { @@ -2008,10 +2209,7 @@ func (m *TallyResult) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGov - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGov } if (iNdEx + skippy) > l { @@ -2106,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 @@ -2120,21 +2318,33 @@ 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:]) if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGov - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGov } if (iNdEx + skippy) > l { @@ -2251,10 +2461,7 @@ func (m *DepositParams) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGov - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGov } if (iNdEx + skippy) > l { @@ -2337,10 +2544,7 @@ func (m *VotingParams) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGov - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGov } if (iNdEx + skippy) > l { @@ -2489,10 +2693,7 @@ func (m *TallyParams) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGov - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGov } if (iNdEx + skippy) > l { diff --git a/x/gov/types/keys.go b/x/gov/types/keys.go index 7681116fc46a..fe98bcbf81c4 100644 --- a/x/gov/types/keys.go +++ b/x/gov/types/keys.go @@ -6,6 +6,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" ) const ( @@ -33,9 +34,9 @@ const ( // // - 0x03: nextProposalID // -// - 0x10: Deposit +// - 0x10: Deposit // -// - 0x20: Voter +// - 0x20: Voter var ( ProposalsKeyPrefix = []byte{0x00} ActiveProposalQueuePrefix = []byte{0x01} @@ -93,7 +94,7 @@ func DepositsKey(proposalID uint64) []byte { // DepositKey key of a specific deposit from the store func DepositKey(proposalID uint64, depositorAddr sdk.AccAddress) []byte { - return append(DepositsKey(proposalID), depositorAddr.Bytes()...) + return append(DepositsKey(proposalID), address.MustLengthPrefix(depositorAddr.Bytes())...) } // VotesKey gets the first part of the votes key based on the proposalID @@ -103,7 +104,7 @@ func VotesKey(proposalID uint64) []byte { // VoteKey key of a specific vote from the store func VoteKey(proposalID uint64, voterAddr sdk.AccAddress) []byte { - return append(VotesKey(proposalID), voterAddr.Bytes()...) + return append(VotesKey(proposalID), address.MustLengthPrefix(voterAddr.Bytes())...) } // Split keys function; used for iterators @@ -154,11 +155,9 @@ func splitKeyWithTime(key []byte) (proposalID uint64, endTime time.Time) { } func splitKeyWithAddress(key []byte) (proposalID uint64, addr sdk.AccAddress) { - if len(key[1:]) != 8+sdk.AddrLen { - panic(fmt.Sprintf("unexpected key length (%d ≠ %d)", len(key), 8+sdk.AddrLen)) - } - + // Both Vote and Deposit store keys are of format: + // proposalID = GetProposalIDFromBytes(key[1:9]) - addr = sdk.AccAddress(key[9:]) + addr = sdk.AccAddress(key[10:]) return } diff --git a/x/gov/types/keys_test.go b/x/gov/types/keys_test.go index 80dfa7d207a3..30266f8f4d78 100644 --- a/x/gov/types/keys_test.go +++ b/x/gov/types/keys_test.go @@ -46,11 +46,6 @@ func TestDepositKeys(t *testing.T) { proposalID, depositorAddr := SplitKeyDeposit(key) require.Equal(t, int(proposalID), 2) require.Equal(t, addr, depositorAddr) - - // invalid key - addr2 := sdk.AccAddress("test1") - key = DepositKey(5, addr2) - require.Panics(t, func() { SplitKeyDeposit(key) }) } func TestVoteKeys(t *testing.T) { @@ -63,9 +58,4 @@ func TestVoteKeys(t *testing.T) { proposalID, voterAddr := SplitKeyDeposit(key) require.Equal(t, int(proposalID), 2) require.Equal(t, addr, voterAddr) - - // invalid key - addr2 := sdk.AccAddress("test1") - key = VoteKey(5, addr2) - require.Panics(t, func() { SplitKeyVote(key) }) } diff --git a/x/gov/types/msgs.go b/x/gov/types/msgs.go index e97c25ce8f01..098aad437857 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 9d7d6a36828a..b942b898646d 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 0c856647f64b..f9b59cbfc0a0 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.go b/x/gov/types/query.pb.go index 190cd901b3fa..4ca1f33ae5f4 100644 --- a/x/gov/types/query.pb.go +++ b/x/gov/types/query.pb.go @@ -2185,10 +2185,7 @@ func (m *QueryProposalRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2271,10 +2268,7 @@ func (m *QueryProposalResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2443,10 +2437,7 @@ func (m *QueryProposalsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2566,10 +2557,7 @@ func (m *QueryProposalsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2670,10 +2658,7 @@ func (m *QueryVoteRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2756,10 +2741,7 @@ func (m *QueryVoteResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2864,10 +2846,7 @@ func (m *QueryVotesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2987,10 +2966,7 @@ func (m *QueryVotesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3072,10 +3048,7 @@ func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3224,10 +3197,7 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3328,10 +3298,7 @@ func (m *QueryDepositRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3414,10 +3381,7 @@ func (m *QueryDepositResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3522,10 +3486,7 @@ func (m *QueryDepositsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3645,10 +3606,7 @@ func (m *QueryDepositsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3717,10 +3675,7 @@ func (m *QueryTallyResultRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3803,10 +3758,7 @@ func (m *QueryTallyResultResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/gov/types/tally.go b/x/gov/types/tally.go index a4e9ee908608..3d5efe51cad3 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 c4d9a9670153..851f2b622fde 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 MsgVoteWeighted 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 @@ -946,10 +1169,7 @@ func (m *MsgSubmitProposal) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1018,10 +1238,7 @@ func (m *MsgSubmitProposalResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1141,10 +1358,142 @@ func (m *MsgVote) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } - if (iNdEx + skippy) < 0 { + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + 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 { @@ -1194,10 +1543,57 @@ func (m *MsgVoteResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } - if (iNdEx + skippy) < 0 { + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + 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 { @@ -1332,10 +1728,7 @@ func (m *MsgDeposit) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1385,10 +1778,7 @@ func (m *MsgDepositResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/x/gov/types/vote.go b/x/gov/types/vote.go index 363887a289da..da4317285c1f 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,36 @@ func (v Vote) Empty() bool { return v.String() == Vote{}.String() } +// NewNonSplitVoteOption creates a single option vote with weight 1 +//nolint:interfacer +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 +95,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/query.go b/x/ibc/applications/transfer/client/cli/query.go index 7d281bb4155f..b9658e05ae92 100644 --- a/x/ibc/applications/transfer/client/cli/query.go +++ b/x/ibc/applications/transfer/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "github.com/spf13/cobra" @@ -31,7 +30,7 @@ func GetCmdQueryDenomTrace() *cobra.Command { Hash: args[0], } - res, err := queryClient.DenomTrace(context.Background(), req) + res, err := queryClient.DenomTrace(cmd.Context(), req) if err != nil { return err } @@ -69,7 +68,7 @@ func GetCmdQueryDenomTraces() *cobra.Command { Pagination: pageReq, } - res, err := queryClient.DenomTraces(context.Background(), req) + res, err := queryClient.DenomTraces(cmd.Context(), req) if err != nil { return err } @@ -98,7 +97,7 @@ func GetCmdParams() *cobra.Command { } queryClient := types.NewQueryClient(clientCtx) - res, _ := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) + res, _ := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) return clientCtx.PrintProto(res.Params) }, } diff --git a/x/ibc/applications/transfer/client/cli/tx.go b/x/ibc/applications/transfer/client/cli/tx.go index c9c6fde51794..1f9e92f63cad 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 defcbbbc8db6..cd64fbabc252 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 9f303175175f..89058ac2952a 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 bf324e2c9b36..25290d69a64c 100644 --- a/x/ibc/applications/transfer/module.go +++ b/x/ibc/applications/transfer/module.go @@ -46,7 +46,9 @@ func (AppModuleBasic) Name() string { } // RegisterLegacyAminoCodec implements AppModuleBasic interface -func (AppModuleBasic) RegisterLegacyAminoCodec(*codec.LegacyAmino) {} +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} // RegisterInterfaces registers module concrete types into protobuf Any. func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { @@ -143,6 +145,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(gs) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock implements the AppModule interface func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { } @@ -200,8 +205,8 @@ func ValidateTransferChannelParams( if err != nil { return err } - if channelSequence > math.MaxUint32 { - return sdkerrors.Wrapf(types.ErrMaxTransferChannels, "channel sequence %d is greater than max allowed transfer channels %d", channelSequence, math.MaxUint32) + if channelSequence > uint64(math.MaxUint32) { + return sdkerrors.Wrapf(types.ErrMaxTransferChannels, "channel sequence %d is greater than max allowed transfer channels %d", channelSequence, uint64(math.MaxUint32)) } if order != channeltypes.UNORDERED { return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s ", channeltypes.UNORDERED, order) @@ -377,7 +382,7 @@ func (am AppModule) OnAcknowledgementPacket( sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), sdk.NewAttribute(types.AttributeKeyDenom, data.Denom), sdk.NewAttribute(types.AttributeKeyAmount, fmt.Sprintf("%d", data.Amount)), - sdk.NewAttribute(types.AttributeKeyAck, fmt.Sprintf("%v", ack)), + sdk.NewAttribute(types.AttributeKeyAck, ack.String()), ), ) diff --git a/x/ibc/applications/transfer/types/codec.go b/x/ibc/applications/transfer/types/codec.go index 18b594f8a77a..24ad7e5a9023 100644 --- a/x/ibc/applications/transfer/types/codec.go +++ b/x/ibc/applications/transfer/types/codec.go @@ -7,6 +7,12 @@ import ( "github.com/cosmos/cosmos-sdk/types/msgservice" ) +// RegisterLegacyAminoCodec registers the necessary x/ibc transfer interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgTransfer{}, "cosmos-sdk/MsgTransfer", nil) +} + // RegisterInterfaces register the ibc transfer module interfaces to protobuf // Any. func RegisterInterfaces(registry codectypes.InterfaceRegistry) { @@ -16,10 +22,20 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { } var ( + amino = codec.NewLegacyAmino() + // ModuleCdc references the global x/ibc-transfer module codec. Note, the codec // should ONLY be used in certain instances of tests and for JSON encoding. // - // The actual codec used for serialization should be provided to x/ibc-transfer and + // The actual codec used for serialization should be provided to x/ibc transfer and // defined at the application level. ModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry()) + + // AminoCdc is a amino codec created to support amino json compatible msgs. + AminoCdc = codec.NewAminoCodec(amino) ) + +func init() { + RegisterLegacyAminoCodec(amino) + amino.Seal() +} diff --git a/x/ibc/applications/transfer/types/genesis.pb.go b/x/ibc/applications/transfer/types/genesis.pb.go index 7dc0370d8eaf..3ae0442f826b 100644 --- a/x/ibc/applications/transfer/types/genesis.pb.go +++ b/x/ibc/applications/transfer/types/genesis.pb.go @@ -342,10 +342,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/ibc/applications/transfer/types/msgs.go b/x/ibc/applications/transfer/types/msgs.go index 065482671375..cf2293213a82 100644 --- a/x/ibc/applications/transfer/types/msgs.go +++ b/x/ibc/applications/transfer/types/msgs.go @@ -70,10 +70,9 @@ func (msg MsgTransfer) ValidateBasic() error { return ValidateIBCDenom(msg.Token.Denom) } -// GetSignBytes implements sdk.Msg. The function will panic since it is used -// for amino transaction verification which IBC does not support. +// GetSignBytes implements sdk.Msg. func (msg MsgTransfer) GetSignBytes() []byte { - panic("IBC messages do not support amino") + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) } // GetSigners implements sdk.Msg diff --git a/x/ibc/applications/transfer/types/msgs_test.go b/x/ibc/applications/transfer/types/msgs_test.go index 89bd39524a51..1fc70c543bbe 100644 --- a/x/ibc/applications/transfer/types/msgs_test.go +++ b/x/ibc/applications/transfer/types/msgs_test.go @@ -1,6 +1,7 @@ package types import ( + "fmt" "testing" "github.com/stretchr/testify/require" @@ -51,6 +52,15 @@ func TestMsgTransferType(t *testing.T) { require.Equal(t, "transfer", msg.Type()) } +func TestMsgTransferGetSignBytes(t *testing.T) { + msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0) + expected := fmt.Sprintf(`{"type":"cosmos-sdk/MsgTransfer","value":{"receiver":"%s","sender":"%s","source_channel":"testchannel","source_port":"testportid","timeout_height":{"revision_height":"10"},"token":{"amount":"100","denom":"atom"}}}`, addr2, addr1) + require.NotPanics(t, func() { + res := msg.GetSignBytes() + require.Equal(t, expected, string(res)) + }) +} + // TestMsgTransferValidation tests ValidateBasic for MsgTransfer func TestMsgTransferValidation(t *testing.T) { testCases := []struct { diff --git a/x/ibc/applications/transfer/types/query.pb.go b/x/ibc/applications/transfer/types/query.pb.go index ee069d35010e..1c1d69295192 100644 --- a/x/ibc/applications/transfer/types/query.pb.go +++ b/x/ibc/applications/transfer/types/query.pb.go @@ -889,10 +889,7 @@ func (m *QueryDenomTraceRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -978,10 +975,7 @@ func (m *QueryDenomTraceResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1067,10 +1061,7 @@ func (m *QueryDenomTracesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1190,10 +1181,7 @@ func (m *QueryDenomTracesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1243,10 +1231,7 @@ func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1332,10 +1317,7 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/ibc/applications/transfer/types/transfer.pb.go b/x/ibc/applications/transfer/types/transfer.pb.go index c051db12a815..62734b85a49f 100644 --- a/x/ibc/applications/transfer/types/transfer.pb.go +++ b/x/ibc/applications/transfer/types/transfer.pb.go @@ -604,10 +604,7 @@ func (m *FungibleTokenPacketData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTransfer - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTransfer } if (iNdEx + skippy) > l { @@ -721,10 +718,7 @@ func (m *DenomTrace) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTransfer - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTransfer } if (iNdEx + skippy) > l { @@ -814,10 +808,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTransfer - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTransfer } if (iNdEx + skippy) > l { diff --git a/x/ibc/applications/transfer/types/tx.pb.go b/x/ibc/applications/transfer/types/tx.pb.go index dd46dbe2610e..e3a630b42738 100644 --- a/x/ibc/applications/transfer/types/tx.pb.go +++ b/x/ibc/applications/transfer/types/tx.pb.go @@ -653,10 +653,7 @@ func (m *MsgTransfer) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -706,10 +703,7 @@ func (m *MsgTransferResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/02-client/client/cli/cli.go b/x/ibc/core/02-client/client/cli/cli.go index 375d8f63693c..33c991521582 100644 --- a/x/ibc/core/02-client/client/cli/cli.go +++ b/x/ibc/core/02-client/client/cli/cli.go @@ -29,3 +29,23 @@ func GetQueryCmd() *cobra.Command { return queryCmd } + +// NewTxCmd returns the command to create and handle IBC clients +func NewTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: types.SubModuleName, + Short: "IBC client transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + NewCreateClientCmd(), + NewUpdateClientCmd(), + NewSubmitMisbehaviourCmd(), + NewUpgradeClientCmd(), + ) + + return txCmd +} diff --git a/x/ibc/core/02-client/client/cli/query.go b/x/ibc/core/02-client/client/cli/query.go index a1e9b45d22cf..c1b5e51a050f 100644 --- a/x/ibc/core/02-client/client/cli/query.go +++ b/x/ibc/core/02-client/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "errors" "fmt" @@ -44,7 +43,7 @@ func GetCmdQueryClientStates() *cobra.Command { Pagination: pageReq, } - res, err := queryClient.ClientStates(context.Background(), req) + res, err := queryClient.ClientStates(cmd.Context(), req) if err != nil { return err } @@ -118,7 +117,7 @@ func GetCmdQueryConsensusStates() *cobra.Command { Pagination: pageReq, } - res, err := queryClient.ConsensusStates(context.Background(), req) + res, err := queryClient.ConsensusStates(cmd.Context(), req) if err != nil { return err } @@ -193,12 +192,11 @@ func GetCmdQueryHeader() *cobra.Command { if err != nil { return err } - header, height, err := utils.QueryTendermintHeader(clientCtx) + header, _, err := utils.QueryTendermintHeader(clientCtx) if err != nil { return err } - clientCtx = clientCtx.WithHeight(height) return clientCtx.PrintProto(&header) }, } @@ -222,12 +220,11 @@ func GetCmdNodeConsensusState() *cobra.Command { if err != nil { return err } - state, height, err := utils.QueryNodeConsensusState(clientCtx) + state, _, err := utils.QueryNodeConsensusState(clientCtx) if err != nil { return err } - clientCtx = clientCtx.WithHeight(height) return clientCtx.PrintProto(state) }, } @@ -252,7 +249,7 @@ func GetCmdParams() *cobra.Command { } queryClient := types.NewQueryClient(clientCtx) - res, _ := queryClient.ClientParams(context.Background(), &types.QueryClientParamsRequest{}) + res, _ := queryClient.ClientParams(cmd.Context(), &types.QueryClientParamsRequest{}) return clientCtx.PrintProto(res.Params) }, } diff --git a/x/ibc/core/02-client/client/cli/tx.go b/x/ibc/core/02-client/client/cli/tx.go new file mode 100644 index 000000000000..bdaa53a8ae1e --- /dev/null +++ b/x/ibc/core/02-client/client/cli/tx.go @@ -0,0 +1,328 @@ +package cli + +import ( + "fmt" + "io/ioutil" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "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/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" +) + +// NewCreateClientCmd defines the command to create a new IBC light client. +func NewCreateClientCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create [path/to/client_state.json] [path/to/consensus_state.json]", + Short: "create new IBC client", + Long: `create a new IBC client with the specified client state and consensus state + - ClientState JSON example: {"@type":"/ibc.lightclients.solomachine.v1.ClientState","sequence":"1","frozen_sequence":"0","consensus_state":{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"testing","timestamp":"10"},"allow_update_after_proposal":false} + - ConsensusState JSON example: {"@type":"/ibc.lightclients.solomachine.v1.ConsensusState","public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"testing","timestamp":"10"}`, + Example: fmt.Sprintf("%s tx ibc %s create [path/to/client_state.json] [path/to/consensus_state.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + // attempt to unmarshal client state argument + var clientState exported.ClientState + clientContentOrFileName := args[0] + if err := cdc.UnmarshalInterfaceJSON([]byte(clientContentOrFileName), &clientState); err != nil { + + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(clientContentOrFileName) + if err != nil { + return errors.Wrap(err, "neither JSON input nor path to .json file for client state were provided") + } + + if err := cdc.UnmarshalInterfaceJSON(contents, &clientState); err != nil { + return errors.Wrap(err, "error unmarshalling client state file") + } + } + + // attempt to unmarshal consensus state argument + var consensusState exported.ConsensusState + consensusContentOrFileName := args[1] + if err := cdc.UnmarshalInterfaceJSON([]byte(consensusContentOrFileName), &consensusState); err != nil { + + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(consensusContentOrFileName) + if err != nil { + return errors.Wrap(err, "neither JSON input nor path to .json file for consensus state were provided") + } + + if err := cdc.UnmarshalInterfaceJSON(contents, &consensusState); err != nil { + return errors.Wrap(err, "error unmarshalling consensus state file") + } + } + + msg, err := types.NewMsgCreateClient(clientState, consensusState, clientCtx.GetFromAddress()) + if err != nil { + return err + } + + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.CreateClient(cmd.Context(), msg) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// NewUpdateClientCmd defines the command to update an IBC client. +func NewUpdateClientCmd() *cobra.Command { + return &cobra.Command{ + Use: "update [client-id] [path/to/header.json]", + Short: "update existing client with a header", + Long: "update existing client with a header", + Example: fmt.Sprintf("%s tx ibc %s update [client-id] [path/to/header.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + clientID := args[0] + + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + var header exported.Header + headerContentOrFileName := args[1] + if err := cdc.UnmarshalInterfaceJSON([]byte(headerContentOrFileName), &header); err != nil { + + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(headerContentOrFileName) + if err != nil { + return errors.Wrap(err, "neither JSON input nor path to .json file for header were provided") + } + + if err := cdc.UnmarshalInterfaceJSON(contents, &header); err != nil { + return errors.Wrap(err, "error unmarshalling header file") + } + } + + msg, err := types.NewMsgUpdateClient(clientID, header, clientCtx.GetFromAddress()) + if err != nil { + return err + } + + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.UpdateClient(cmd.Context(), msg) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) + }, + } +} + +// NewSubmitMisbehaviourCmd defines the command to submit a misbehaviour to prevent +// future updates. +func NewSubmitMisbehaviourCmd() *cobra.Command { + return &cobra.Command{ + Use: "misbehaviour [path/to/misbehaviour.json]", + Short: "submit a client misbehaviour", + Long: "submit a client misbehaviour to prevent future updates", + Example: fmt.Sprintf("%s tx ibc %s misbehaviour [path/to/misbehaviour.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + var misbehaviour exported.Misbehaviour + misbehaviourContentOrFileName := args[0] + if err := cdc.UnmarshalInterfaceJSON([]byte(misbehaviourContentOrFileName), &misbehaviour); err != nil { + + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(misbehaviourContentOrFileName) + if err != nil { + return errors.Wrap(err, "neither JSON input nor path to .json file for misbehaviour were provided") + } + + if err := cdc.UnmarshalInterfaceJSON(contents, misbehaviour); err != nil { + return errors.Wrap(err, "error unmarshalling misbehaviour file") + } + } + + msg, err := types.NewMsgSubmitMisbehaviour(misbehaviour.GetClientID(), misbehaviour, clientCtx.GetFromAddress()) + if err != nil { + return err + } + + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.SubmitMisbehaviour(cmd.Context(), msg) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) + }, + } +} + +// NewUpgradeClientCmd defines the command to upgrade an IBC light client. +func NewUpgradeClientCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "upgrade [client-identifier] [path/to/client_state.json] [path/to/consensus_state.json] [upgrade-client-proof] [upgrade-consensus-state-proof]", + Short: "upgrade an IBC client", + Long: `upgrade the IBC client associated with the provided client identifier while providing proof committed by the counterparty chain to the new client and consensus states + - ClientState JSON example: {"@type":"/ibc.lightclients.solomachine.v1.ClientState","sequence":"1","frozen_sequence":"0","consensus_state":{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"testing","timestamp":"10"},"allow_update_after_proposal":false} + - ConsensusState JSON example: {"@type":"/ibc.lightclients.solomachine.v1.ConsensusState","public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"testing","timestamp":"10"}`, + Example: fmt.Sprintf("%s tx ibc %s upgrade [client-identifier] [path/to/client_state.json] [path/to/consensus_state.json] [client-state-proof] [consensus-state-proof] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), + Args: cobra.ExactArgs(5), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + clientID := args[0] + + // attempt to unmarshal client state argument + var clientState exported.ClientState + clientContentOrFileName := args[1] + if err := cdc.UnmarshalInterfaceJSON([]byte(clientContentOrFileName), &clientState); err != nil { + + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(clientContentOrFileName) + if err != nil { + return errors.Wrap(err, "neither JSON input nor path to .json file for client state were provided") + } + + if err := cdc.UnmarshalInterfaceJSON(contents, &clientState); err != nil { + return errors.Wrap(err, "error unmarshalling client state file") + } + } + + // attempt to unmarshal consensus state argument + var consensusState exported.ConsensusState + consensusContentOrFileName := args[2] + if err := cdc.UnmarshalInterfaceJSON([]byte(consensusContentOrFileName), &consensusState); err != nil { + + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(consensusContentOrFileName) + if err != nil { + return errors.Wrap(err, "neither JSON input nor path to .json file for consensus state were provided") + } + + if err := cdc.UnmarshalInterfaceJSON(contents, &consensusState); err != nil { + return errors.Wrap(err, "error unmarshalling consensus state file") + } + } + + proofUpgradeClient := []byte(args[3]) + proofUpgradeConsensus := []byte(args[4]) + + msg, err := types.NewMsgUpgradeClient(clientID, clientState, consensusState, proofUpgradeClient, proofUpgradeConsensus, clientCtx.GetFromAddress()) + if err != nil { + return err + } + + svcMsgClientConn := &msgservice.ServiceMsgClientConn{} + msgClient := types.NewMsgClient(svcMsgClientConn) + _, err = msgClient.UpgradeClient(cmd.Context(), msg) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + 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 000000000000..63585cbe50b2 --- /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 6328e5de3b81..2134427729ae 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/grpc_query_test.go b/x/ibc/core/02-client/keeper/grpc_query_test.go index 35b0691ad249..5e361a76f083 100644 --- a/x/ibc/core/02-client/keeper/grpc_query_test.go +++ b/x/ibc/core/02-client/keeper/grpc_query_test.go @@ -69,9 +69,11 @@ func (suite *KeeperTestSuite) TestQueryClientState() { if tc.expPass { suite.Require().NoError(err) suite.Require().NotNil(res) - - expClientState.ClearCachedValue() suite.Require().Equal(expClientState, res.ClientState) + + // ensure UnpackInterfaces is defined + cachedValue := res.ClientState.GetCachedValue() + suite.Require().NotNil(cachedValue) } else { suite.Require().Error(err) } @@ -255,9 +257,11 @@ func (suite *KeeperTestSuite) TestQueryConsensusState() { if tc.expPass { suite.Require().NoError(err) suite.Require().NotNil(res) - - expConsensusState.ClearCachedValue() suite.Require().Equal(expConsensusState, res.ConsensusState) + + // ensure UnpackInterfaces is defined + cachedValue := res.ConsensusState.GetCachedValue() + suite.Require().NotNil(cachedValue) } else { suite.Require().Error(err) } @@ -356,8 +360,11 @@ func (suite *KeeperTestSuite) TestQueryConsensusStates() { suite.Require().Equal(len(expConsensusStates), len(res.ConsensusStates)) for i := range expConsensusStates { suite.Require().NotNil(res.ConsensusStates[i]) - expConsensusStates[i].ConsensusState.ClearCachedValue() suite.Require().Equal(expConsensusStates[i], res.ConsensusStates[i]) + + // ensure UnpackInterfaces is defined + cachedValue := res.ConsensusStates[i].ConsensusState.GetCachedValue() + suite.Require().NotNil(cachedValue) } } else { suite.Require().Error(err) diff --git a/x/ibc/core/02-client/keeper/proposal.go b/x/ibc/core/02-client/keeper/proposal.go index 6b17278e09dd..6d4ff350df32 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 err != nil { - return err + 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, consensusState, err := clientState.CheckProposedHeaderAndUpdateState(ctx, k.cdc, k.ClientStore(ctx, p.ClientId), header) + 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) - 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 ada205402b34..8dbe43f7d7f7 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) + content = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, substitute, initialHeight) + }, true, + }, + { + "subject and substitute use different revision numbers", func() { + tmClientState, ok := substituteClientState.(*ibctmtypes.ClientState) suite.Require().True(ok) - tmClientState.AllowUpdateAfterMisbehaviour = true - tmClientState.FrozenHeight = tmClientState.LatestHeight - suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), clientA, tmClientState) + consState, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientConsensusState(suite.chainA.GetContext(), substitute, tmClientState.LatestHeight) + suite.Require().True(found) + newRevisionNumber := tmClientState.GetLatestHeight().GetRevisionNumber() + 1 - // use next header for chainB to update the client on chainA - header, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, clientA) - suite.Require().NoError(err) + 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, 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) + "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/module.go b/x/ibc/core/02-client/module.go index 9a051ba1abcf..08efee8b1adc 100644 --- a/x/ibc/core/02-client/module.go +++ b/x/ibc/core/02-client/module.go @@ -18,6 +18,11 @@ func GetQueryCmd() *cobra.Command { return cli.GetQueryCmd() } +// GetTxCmd returns the root tx command for 02-client. +func GetTxCmd() *cobra.Command { + return cli.NewTxCmd() +} + // RegisterQueryService registers the gRPC query service for IBC client. func RegisterQueryService(server grpc.Server, queryServer types.QueryServer) { types.RegisterQueryServer(server, queryServer) diff --git a/x/ibc/core/02-client/proposal_handler_test.go b/x/ibc/core/02-client/proposal_handler_test.go index 91c1451b7076..41b893186d65 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.go b/x/ibc/core/02-client/types/client.go index 1c44d6e2b9c2..6d51828af0f7 100644 --- a/x/ibc/core/02-client/types/client.go +++ b/x/ibc/core/02-client/types/client.go @@ -93,7 +93,7 @@ func ValidateClientType(clientType string) error { } smallestPossibleClientID := FormatClientIdentifier(clientType, 0) - largestPossibleClientID := FormatClientIdentifier(clientType, math.MaxUint64) + largestPossibleClientID := FormatClientIdentifier(clientType, uint64(math.MaxUint64)) // IsValidClientID will check client type format and if the sequence is a uint64 if !IsValidClientID(smallestPossibleClientID) { diff --git a/x/ibc/core/02-client/types/client.pb.go b/x/ibc/core/02-client/types/client.pb.go index f6233435ca09..61bdd9bf804d 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 } @@ -861,10 +877,7 @@ func (m *IdentifiedClientState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthClient - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthClient } if (iNdEx + skippy) > l { @@ -983,10 +996,7 @@ func (m *ConsensusStateWithHeight) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthClient - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthClient } if (iNdEx + skippy) > l { @@ -1102,10 +1112,7 @@ func (m *ClientConsensusStates) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthClient - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthClient } if (iNdEx + skippy) > l { @@ -1215,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 { @@ -1243,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 { @@ -1274,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 @@ -1287,10 +1323,7 @@ func (m *ClientUpdateProposal) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthClient - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthClient } if (iNdEx + skippy) > l { @@ -1378,10 +1411,7 @@ func (m *Height) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthClient - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthClient } if (iNdEx + skippy) > l { @@ -1463,10 +1493,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthClient - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthClient } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/02-client/types/codec.go b/x/ibc/core/02-client/types/codec.go index 8d79dcdaa45c..59a15832bec1 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 09dc92959a2b..5b44cd522211 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 779e87cee332..d0760ba89c8a 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/genesis.pb.go b/x/ibc/core/02-client/types/genesis.pb.go index 07f996ac203d..a8253c343db0 100644 --- a/x/ibc/core/02-client/types/genesis.pb.go +++ b/x/ibc/core/02-client/types/genesis.pb.go @@ -722,10 +722,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -843,10 +840,7 @@ func (m *GenesisMetadata) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -962,10 +956,7 @@ func (m *IdentifiedGenesisMetadata) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/02-client/types/proposal.go b/x/ibc/core/02-client/types/proposal.go index 334a9d4599ef..95b10aaf408e 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{} -// 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 init() { + govtypes.RegisterProposalType(ProposalTypeClientUpdate) +} +// NewClientUpdateProposal creates a new client update proposal. +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 } - - header, err := UnpackHeader(cup.Header) - if err != nil { + if _, _, err := ParseClientIdentifier(cup.SubstituteClientId); err != nil { return err } - return header.ValidateBasic() -} + if cup.InitialHeight.IsZero() { + return sdkerrors.Wrap(ErrInvalidHeight, "initial height cannot be zero height") + } -// 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 5a47cf2f01cb..597e5cf8f801 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, + }, + { + "subject and substitute use the same identifier", + types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, subject, initialHeight), + false, + }, + { + "invalid subject clientID", + types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, ibctesting.InvalidID, substitute, initialHeight), false, }, { - "fails to unpack header", - &types.ClientUpdateProposal{ibctesting.Title, ibctesting.Description, clientID, nil}, + "invalid substitute clientID", + types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, ibctesting.InvalidID, initialHeight), false, }, { - "fails header validate basic", - &types.ClientUpdateProposal{ibctesting.Title, ibctesting.Description, clientID, invalidHeader}, + "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.go b/x/ibc/core/02-client/types/query.go index 3f898dadb4b0..c46bbfcfe778 100644 --- a/x/ibc/core/02-client/types/query.go +++ b/x/ibc/core/02-client/types/query.go @@ -2,8 +2,26 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) +var ( + _ codectypes.UnpackInterfacesMessage = QueryClientStateResponse{} + _ codectypes.UnpackInterfacesMessage = QueryClientStatesResponse{} + _ codectypes.UnpackInterfacesMessage = QueryConsensusStateResponse{} + _ codectypes.UnpackInterfacesMessage = QueryConsensusStatesResponse{} +) + +// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces +func (qcsr QueryClientStatesResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, cs := range qcsr.ClientStates { + if err := cs.UnpackInterfaces(unpacker); err != nil { + return err + } + } + return nil +} + // NewQueryClientStateResponse creates a new QueryClientStateResponse instance. func NewQueryClientStateResponse( clientStateAny *codectypes.Any, proof []byte, height Height, @@ -15,6 +33,21 @@ func NewQueryClientStateResponse( } } +// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces +func (qcsr QueryClientStateResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(qcsr.ClientState, new(exported.ClientState)) +} + +// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces +func (qcsr QueryConsensusStatesResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, cs := range qcsr.ConsensusStates { + if err := cs.UnpackInterfaces(unpacker); err != nil { + return err + } + } + return nil +} + // NewQueryConsensusStateResponse creates a new QueryConsensusStateResponse instance. func NewQueryConsensusStateResponse( consensusStateAny *codectypes.Any, proof []byte, height Height, @@ -25,3 +58,8 @@ func NewQueryConsensusStateResponse( ProofHeight: height, } } + +// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces +func (qcsr QueryConsensusStateResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(qcsr.ConsensusState, new(exported.ConsensusState)) +} diff --git a/x/ibc/core/02-client/types/query.pb.go b/x/ibc/core/02-client/types/query.pb.go index afe6670d4b67..651becb899ae 100644 --- a/x/ibc/core/02-client/types/query.pb.go +++ b/x/ibc/core/02-client/types/query.pb.go @@ -1556,10 +1556,7 @@ func (m *QueryClientStateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1712,10 +1709,7 @@ func (m *QueryClientStateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1801,10 +1795,7 @@ func (m *QueryClientStatesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1924,10 +1915,7 @@ func (m *QueryClientStatesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2067,10 +2055,7 @@ func (m *QueryConsensusStateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2223,10 +2208,7 @@ func (m *QueryConsensusStateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2344,10 +2326,7 @@ func (m *QueryConsensusStatesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2467,10 +2446,7 @@ func (m *QueryConsensusStatesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2520,10 +2496,7 @@ func (m *QueryClientParamsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2609,10 +2582,7 @@ func (m *QueryClientParamsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/02-client/types/tx.pb.go b/x/ibc/core/02-client/types/tx.pb.go index b0bf22d0cc11..a314223fcff9 100644 --- a/x/ibc/core/02-client/types/tx.pb.go +++ b/x/ibc/core/02-client/types/tx.pb.go @@ -1216,10 +1216,7 @@ func (m *MsgCreateClient) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1269,10 +1266,7 @@ func (m *MsgCreateClientResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1422,10 +1416,7 @@ func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1475,10 +1466,7 @@ func (m *MsgUpdateClientResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1732,10 +1720,7 @@ func (m *MsgUpgradeClient) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1785,10 +1770,7 @@ func (m *MsgUpgradeClientResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1938,10 +1920,7 @@ func (m *MsgSubmitMisbehaviour) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1991,10 +1970,7 @@ func (m *MsgSubmitMisbehaviourResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/03-connection/client/cli/query.go b/x/ibc/core/03-connection/client/cli/query.go index 071410398db5..21c4bd8f578e 100644 --- a/x/ibc/core/03-connection/client/cli/query.go +++ b/x/ibc/core/03-connection/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "github.com/spf13/cobra" @@ -39,7 +38,7 @@ func GetCmdQueryConnections() *cobra.Command { Pagination: pageReq, } - res, err := queryClient.Connections(context.Background(), req) + res, err := queryClient.Connections(cmd.Context(), req) if err != nil { return err } diff --git a/x/ibc/core/03-connection/client/cli/tx.go b/x/ibc/core/03-connection/client/cli/tx.go index 339e2e9770f4..68b1a6208200 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/keeper/grpc_query_test.go b/x/ibc/core/03-connection/keeper/grpc_query_test.go index faa77b3c3e99..14fdb425d9eb 100644 --- a/x/ibc/core/03-connection/keeper/grpc_query_test.go +++ b/x/ibc/core/03-connection/keeper/grpc_query_test.go @@ -308,6 +308,10 @@ func (suite *KeeperTestSuite) TestQueryConnectionClientState() { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().Equal(&expIdentifiedClientState, res.IdentifiedClientState) + + // ensure UnpackInterfaces is defined + cachedValue := res.IdentifiedClientState.ClientState.GetCachedValue() + suite.Require().NotNil(cachedValue) } else { suite.Require().Error(err) } @@ -404,6 +408,10 @@ func (suite *KeeperTestSuite) TestQueryConnectionConsensusState() { suite.Require().NoError(err) suite.Require().Equal(expConsensusState, consensusState) suite.Require().Equal(expClientID, res.ClientId) + + // ensure UnpackInterfaces is defined + cachedValue := res.ConsensusState.GetCachedValue() + suite.Require().NotNil(cachedValue) } else { suite.Require().Error(err) } diff --git a/x/ibc/core/03-connection/types/connection.pb.go b/x/ibc/core/03-connection/types/connection.pb.go index cdfb752b9075..14b85c62d432 100644 --- a/x/ibc/core/03-connection/types/connection.pb.go +++ b/x/ibc/core/03-connection/types/connection.pb.go @@ -1023,10 +1023,7 @@ func (m *ConnectionEnd) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthConnection - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthConnection } if (iNdEx + skippy) > l { @@ -1245,10 +1242,7 @@ func (m *IdentifiedConnection) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthConnection - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthConnection } if (iNdEx + skippy) > l { @@ -1395,10 +1389,7 @@ func (m *Counterparty) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthConnection - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthConnection } if (iNdEx + skippy) > l { @@ -1480,10 +1471,7 @@ func (m *ClientPaths) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthConnection - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthConnection } if (iNdEx + skippy) > l { @@ -1597,10 +1585,7 @@ func (m *ConnectionPaths) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthConnection - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthConnection } if (iNdEx + skippy) > l { @@ -1714,10 +1699,7 @@ func (m *Version) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthConnection - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthConnection } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/03-connection/types/genesis.pb.go b/x/ibc/core/03-connection/types/genesis.pb.go index 19c4e6aeb823..5f33f166ae07 100644 --- a/x/ibc/core/03-connection/types/genesis.pb.go +++ b/x/ibc/core/03-connection/types/genesis.pb.go @@ -337,10 +337,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/03-connection/types/msgs.go b/x/ibc/core/03-connection/types/msgs.go index 34a4b9d63050..3ba1aed8e7e0 100644 --- a/x/ibc/core/03-connection/types/msgs.go +++ b/x/ibc/core/03-connection/types/msgs.go @@ -10,7 +10,15 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) -var _ sdk.Msg = &MsgConnectionOpenInit{} +var ( + _ sdk.Msg = &MsgConnectionOpenInit{} + _ sdk.Msg = &MsgConnectionOpenConfirm{} + _ sdk.Msg = &MsgConnectionOpenAck{} + _ sdk.Msg = &MsgConnectionOpenTry{} + + _ codectypes.UnpackInterfacesMessage = MsgConnectionOpenTry{} + _ codectypes.UnpackInterfacesMessage = MsgConnectionOpenAck{} +) // NewMsgConnectionOpenInit creates a new MsgConnectionOpenInit instance. It sets the // counterparty connection identifier to be empty. @@ -78,8 +86,6 @@ func (msg MsgConnectionOpenInit) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{accAddr} } -var _ sdk.Msg = &MsgConnectionOpenTry{} - // NewMsgConnectionOpenTry creates a new MsgConnectionOpenTry instance //nolint:interfacer func NewMsgConnectionOpenTry( @@ -175,13 +181,7 @@ func (msg MsgConnectionOpenTry) ValidateBasic() error { // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (msg MsgConnectionOpenTry) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - var clientState exported.ClientState - err := unpacker.UnpackAny(msg.ClientState, &clientState) - if err != nil { - return err - } - - return nil + return unpacker.UnpackAny(msg.ClientState, new(exported.ClientState)) } // GetSignBytes implements sdk.Msg. The function will panic since it is used @@ -199,8 +199,6 @@ func (msg MsgConnectionOpenTry) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{accAddr} } -var _ sdk.Msg = &MsgConnectionOpenAck{} - // NewMsgConnectionOpenAck creates a new MsgConnectionOpenAck instance //nolint:interfacer func NewMsgConnectionOpenAck( @@ -298,8 +296,6 @@ func (msg MsgConnectionOpenAck) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{accAddr} } -var _ sdk.Msg = &MsgConnectionOpenConfirm{} - // NewMsgConnectionOpenConfirm creates a new MsgConnectionOpenConfirm instance //nolint:interfacer func NewMsgConnectionOpenConfirm( diff --git a/x/ibc/core/03-connection/types/msgs_test.go b/x/ibc/core/03-connection/types/msgs_test.go index 57c1925f66db..6aff3b0904b3 100644 --- a/x/ibc/core/03-connection/types/msgs_test.go +++ b/x/ibc/core/03-connection/types/msgs_test.go @@ -138,7 +138,7 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() { {"invalid counterparty client ID", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "test/conn1", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, {"invalid nil counterparty client", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", nil, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, {"invalid client unpacking", &types.MsgConnectionOpenTry{connectionID, "clienttotesta", invalidAny, counterparty, 500, []*types.Version{ibctesting.ConnectionVersion}, clientHeight, suite.proof, suite.proof, suite.proof, clientHeight, signer.String()}, false}, - {"counterparty failed Validate", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", invalidClient, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"counterparty failed validate", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", invalidClient, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, {"empty counterparty prefix", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, emptyPrefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, {"empty counterpartyVersions", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, {"empty proofInit", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, emptyProof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, @@ -188,7 +188,7 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenAck() { {"invalid counterparty connection ID", types.NewMsgConnectionOpenAck(connectionID, "test/conn1", clientState, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, {"invalid nil counterparty client", types.NewMsgConnectionOpenAck(connectionID, connectionID, nil, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, {"invalid unpacking counterparty client", &types.MsgConnectionOpenAck{connectionID, connectionID, ibctesting.ConnectionVersion, invalidAny, clientHeight, suite.proof, suite.proof, suite.proof, clientHeight, signer.String()}, false}, - {"counterparty client failed Validate", types.NewMsgConnectionOpenAck(connectionID, connectionID, invalidClient, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, + {"counterparty client failed validate", types.NewMsgConnectionOpenAck(connectionID, connectionID, invalidClient, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, {"empty proofTry", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, emptyProof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, {"empty proofClient", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, emptyProof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, {"empty proofConsensus", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, emptyProof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, diff --git a/x/ibc/core/03-connection/types/query.go b/x/ibc/core/03-connection/types/query.go index 09058e6e8e81..7661b38d9bba 100644 --- a/x/ibc/core/03-connection/types/query.go +++ b/x/ibc/core/03-connection/types/query.go @@ -6,6 +6,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) +var ( + _ codectypes.UnpackInterfacesMessage = QueryConnectionClientStateResponse{} + _ codectypes.UnpackInterfacesMessage = QueryConnectionConsensusStateResponse{} +) + // NewQueryConnectionResponse creates a new QueryConnectionResponse instance func NewQueryConnectionResponse( connection ConnectionEnd, proof []byte, height clienttypes.Height, @@ -44,6 +49,11 @@ func NewQueryConnectionClientStateResponse(identifiedClientState clienttypes.Ide } } +// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces +func (qccsr QueryConnectionClientStateResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return qccsr.IdentifiedClientState.UnpackInterfaces(unpacker) +} + // NewQueryConnectionConsensusStateResponse creates a newQueryConnectionConsensusStateResponse instance func NewQueryConnectionConsensusStateResponse(clientID string, anyConsensusState *codectypes.Any, consensusStateHeight exported.Height, proof []byte, height clienttypes.Height) *QueryConnectionConsensusStateResponse { return &QueryConnectionConsensusStateResponse{ @@ -53,3 +63,8 @@ func NewQueryConnectionConsensusStateResponse(clientID string, anyConsensusState ProofHeight: height, } } + +// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces +func (qccsr QueryConnectionConsensusStateResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(qccsr.ConsensusState, new(exported.ConsensusState)) +} diff --git a/x/ibc/core/03-connection/types/query.pb.go b/x/ibc/core/03-connection/types/query.pb.go index 2861c8a5fbcc..6796cdb1f8ad 100644 --- a/x/ibc/core/03-connection/types/query.pb.go +++ b/x/ibc/core/03-connection/types/query.pb.go @@ -1628,10 +1628,7 @@ func (m *QueryConnectionRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1784,10 +1781,7 @@ func (m *QueryConnectionResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1873,10 +1867,7 @@ func (m *QueryConnectionsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2029,10 +2020,7 @@ func (m *QueryConnectionsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2114,10 +2102,7 @@ func (m *QueryClientConnectionsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2266,10 +2251,7 @@ func (m *QueryClientConnectionsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2351,10 +2333,7 @@ func (m *QueryConnectionClientStateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2507,10 +2486,7 @@ func (m *QueryConnectionClientStateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2630,10 +2606,7 @@ func (m *QueryConnectionConsensusStateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -2818,10 +2791,7 @@ func (m *QueryConnectionConsensusStateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/03-connection/types/tx.pb.go b/x/ibc/core/03-connection/types/tx.pb.go index 5b1ce774649b..00e4fd9d0bb7 100644 --- a/x/ibc/core/03-connection/types/tx.pb.go +++ b/x/ibc/core/03-connection/types/tx.pb.go @@ -1474,10 +1474,7 @@ func (m *MsgConnectionOpenInit) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1527,10 +1524,7 @@ func (m *MsgConnectionOpenInitResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1966,10 +1960,7 @@ func (m *MsgConnectionOpenTry) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2019,10 +2010,7 @@ func (m *MsgConnectionOpenTryResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2408,10 +2396,7 @@ func (m *MsgConnectionOpenAck) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2461,10 +2446,7 @@ func (m *MsgConnectionOpenAckResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2645,10 +2627,7 @@ func (m *MsgConnectionOpenConfirm) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2698,10 +2677,7 @@ func (m *MsgConnectionOpenConfirmResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/04-channel/client/cli/query.go b/x/ibc/core/04-channel/client/cli/query.go index 5d059c08cf94..03df474f1e79 100644 --- a/x/ibc/core/04-channel/client/cli/query.go +++ b/x/ibc/core/04-channel/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "strconv" @@ -44,7 +43,7 @@ func GetCmdQueryChannels() *cobra.Command { Pagination: pageReq, } - res, err := queryClient.Channels(context.Background(), req) + res, err := queryClient.Channels(cmd.Context(), req) if err != nil { return err } @@ -118,7 +117,7 @@ func GetCmdQueryConnectionChannels() *cobra.Command { Pagination: pageReq, } - res, err := queryClient.ConnectionChannels(context.Background(), req) + res, err := queryClient.ConnectionChannels(cmd.Context(), req) if err != nil { return err } @@ -189,7 +188,7 @@ func GetCmdQueryPacketCommitments() *cobra.Command { Pagination: pageReq, } - res, err := queryClient.PacketCommitments(context.Background(), req) + res, err := queryClient.PacketCommitments(cmd.Context(), req) if err != nil { return err } @@ -357,7 +356,7 @@ The return value represents: PacketCommitmentSequences: seqs, } - res, err := queryClient.UnreceivedPackets(context.Background(), req) + res, err := queryClient.UnreceivedPackets(cmd.Context(), req) if err != nil { return err } @@ -407,7 +406,7 @@ The return value represents: PacketAckSequences: seqs, } - res, err := queryClient.UnreceivedAcks(context.Background(), req) + res, err := queryClient.UnreceivedAcks(cmd.Context(), req) if err != nil { return err } diff --git a/x/ibc/core/04-channel/client/cli/tx.go b/x/ibc/core/04-channel/client/cli/tx.go index 21aafba19460..20afe6226746 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/client/utils/utils.go b/x/ibc/core/04-channel/client/utils/utils.go index c10dc05c7fd3..167e05d048e9 100644 --- a/x/ibc/core/04-channel/client/utils/utils.go +++ b/x/ibc/core/04-channel/client/utils/utils.go @@ -133,8 +133,9 @@ func QueryLatestConsensusState( if err != nil { return nil, clienttypes.Height{}, clienttypes.Height{}, err } - clientState, err := clienttypes.UnpackClientState(clientRes.IdentifiedClientState.ClientState) - if err != nil { + + var clientState exported.ClientState + if err := clientCtx.InterfaceRegistry.UnpackAny(clientRes.IdentifiedClientState.ClientState, &clientState); err != nil { return nil, clienttypes.Height{}, clienttypes.Height{}, err } @@ -148,8 +149,8 @@ func QueryLatestConsensusState( return nil, clienttypes.Height{}, clienttypes.Height{}, err } - consensusState, err := clienttypes.UnpackConsensusState(res.ConsensusState) - if err != nil { + var consensusState exported.ConsensusState + if err := clientCtx.InterfaceRegistry.UnpackAny(res.ConsensusState, &consensusState); err != nil { return nil, clienttypes.Height{}, clienttypes.Height{}, err } diff --git a/x/ibc/core/04-channel/keeper/grpc_query_test.go b/x/ibc/core/04-channel/keeper/grpc_query_test.go index 94f07bc069ed..689c241c7b8b 100644 --- a/x/ibc/core/04-channel/keeper/grpc_query_test.go +++ b/x/ibc/core/04-channel/keeper/grpc_query_test.go @@ -407,6 +407,10 @@ func (suite *KeeperTestSuite) TestQueryChannelClientState() { suite.Require().NoError(err) suite.Require().NotNil(res) suite.Require().Equal(&expIdentifiedClientState, res.IdentifiedClientState) + + // ensure UnpackInterfaces is defined + cachedValue := res.IdentifiedClientState.ClientState.GetCachedValue() + suite.Require().NotNil(cachedValue) } else { suite.Require().Error(err) } @@ -542,6 +546,10 @@ func (suite *KeeperTestSuite) TestQueryChannelConsensusState() { suite.Require().NoError(err) suite.Require().Equal(expConsensusState, consensusState) suite.Require().Equal(expClientID, res.ClientId) + + // ensure UnpackInterfaces is defined + cachedValue := res.ConsensusState.GetCachedValue() + suite.Require().NotNil(cachedValue) } else { suite.Require().Error(err) } diff --git a/x/ibc/core/04-channel/keeper/packet.go b/x/ibc/core/04-channel/keeper/packet.go index 9af59745d549..49b59733c5a3 100644 --- a/x/ibc/core/04-channel/keeper/packet.go +++ b/x/ibc/core/04-channel/keeper/packet.go @@ -129,6 +129,9 @@ func (k Keeper) SendPacket( sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()), + // we only support 1-hop packets now, and that is the most important hop for a relayer + // (is it going to a chain I am connected to) + sdk.NewAttribute(types.AttributeKeyConnection, channel.ConnectionHops[0]), ), sdk.NewEvent( sdk.EventTypeMessage, @@ -289,6 +292,9 @@ func (k Keeper) RecvPacket( sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()), + // we only support 1-hop packets now, and that is the most important hop for a relayer + // (is it going to a chain I am connected to) + sdk.NewAttribute(types.AttributeKeyConnection, channel.ConnectionHops[0]), ), sdk.NewEvent( sdk.EventTypeMessage, @@ -370,6 +376,9 @@ func (k Keeper) WriteAcknowledgement( sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), sdk.NewAttribute(types.AttributeKeyAck, string(acknowledgement)), + // we only support 1-hop packets now, and that is the most important hop for a relayer + // (is it going to a chain I am connected to) + sdk.NewAttribute(types.AttributeKeyConnection, channel.ConnectionHops[0]), ), sdk.NewEvent( sdk.EventTypeMessage, @@ -505,6 +514,9 @@ func (k Keeper) AcknowledgePacket( sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()), + // we only support 1-hop packets now, and that is the most important hop for a relayer + // (is it going to a chain I am connected to) + sdk.NewAttribute(types.AttributeKeyConnection, channel.ConnectionHops[0]), ), sdk.NewEvent( sdk.EventTypeMessage, diff --git a/x/ibc/core/04-channel/types/channel.pb.go b/x/ibc/core/04-channel/types/channel.pb.go index e1be136186e6..1384a150d578 100644 --- a/x/ibc/core/04-channel/types/channel.pb.go +++ b/x/ibc/core/04-channel/types/channel.pb.go @@ -1239,10 +1239,7 @@ func (m *Channel) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthChannel - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthChannel } if (iNdEx + skippy) > l { @@ -1491,10 +1488,7 @@ func (m *IdentifiedChannel) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthChannel - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthChannel } if (iNdEx + skippy) > l { @@ -1608,10 +1602,7 @@ func (m *Counterparty) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthChannel - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthChannel } if (iNdEx + skippy) > l { @@ -1894,10 +1885,7 @@ func (m *Packet) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthChannel - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthChannel } if (iNdEx + skippy) > l { @@ -2064,10 +2052,7 @@ func (m *PacketState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthChannel - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthChannel } if (iNdEx + skippy) > l { @@ -2182,10 +2167,7 @@ func (m *Acknowledgement) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthChannel - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthChannel } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/04-channel/types/events.go b/x/ibc/core/04-channel/types/events.go index 923587d439d7..b9ddb3052c30 100644 --- a/x/ibc/core/04-channel/types/events.go +++ b/x/ibc/core/04-channel/types/events.go @@ -30,6 +30,7 @@ const ( AttributeKeyDstPort = "packet_dst_port" AttributeKeyDstChannel = "packet_dst_channel" AttributeKeyChannelOrdering = "packet_channel_ordering" + AttributeKeyConnection = "packet_connection" ) // IBC channel events vars diff --git a/x/ibc/core/04-channel/types/genesis.pb.go b/x/ibc/core/04-channel/types/genesis.pb.go index 51ff4e50771c..c54c6f9deb03 100644 --- a/x/ibc/core/04-channel/types/genesis.pb.go +++ b/x/ibc/core/04-channel/types/genesis.pb.go @@ -781,10 +781,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -917,10 +914,7 @@ func (m *PacketSequence) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/04-channel/types/query.go b/x/ibc/core/04-channel/types/query.go index 1f1962a6e594..d1536dfc0565 100644 --- a/x/ibc/core/04-channel/types/query.go +++ b/x/ibc/core/04-channel/types/query.go @@ -6,6 +6,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) +var ( + _ codectypes.UnpackInterfacesMessage = QueryChannelClientStateResponse{} + _ codectypes.UnpackInterfacesMessage = QueryChannelConsensusStateResponse{} +) + // NewQueryChannelResponse creates a new QueryChannelResponse instance func NewQueryChannelResponse(channel Channel, proof []byte, height clienttypes.Height) *QueryChannelResponse { return &QueryChannelResponse{ @@ -24,6 +29,11 @@ func NewQueryChannelClientStateResponse(identifiedClientState clienttypes.Identi } } +// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces +func (qccsr QueryChannelClientStateResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return qccsr.IdentifiedClientState.UnpackInterfaces(unpacker) +} + // NewQueryChannelConsensusStateResponse creates a newQueryChannelConsensusStateResponse instance func NewQueryChannelConsensusStateResponse(clientID string, anyConsensusState *codectypes.Any, consensusStateHeight exported.Height, proof []byte, height clienttypes.Height) *QueryChannelConsensusStateResponse { return &QueryChannelConsensusStateResponse{ @@ -34,6 +44,11 @@ func NewQueryChannelConsensusStateResponse(clientID string, anyConsensusState *c } } +// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces +func (qccsr QueryChannelConsensusStateResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(qccsr.ConsensusState, new(exported.ConsensusState)) +} + // NewQueryPacketCommitmentResponse creates a new QueryPacketCommitmentResponse instance func NewQueryPacketCommitmentResponse( commitment []byte, proof []byte, height clienttypes.Height, diff --git a/x/ibc/core/04-channel/types/query.pb.go b/x/ibc/core/04-channel/types/query.pb.go index d163f3d8ce62..c5cbd6e8f899 100644 --- a/x/ibc/core/04-channel/types/query.pb.go +++ b/x/ibc/core/04-channel/types/query.pb.go @@ -4231,10 +4231,7 @@ func (m *QueryChannelRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4387,10 +4384,7 @@ func (m *QueryChannelResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4476,10 +4470,7 @@ func (m *QueryChannelsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4632,10 +4623,7 @@ func (m *QueryChannelsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4753,10 +4741,7 @@ func (m *QueryConnectionChannelsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4909,10 +4894,7 @@ func (m *QueryConnectionChannelsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5026,10 +5008,7 @@ func (m *QueryChannelClientStateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5182,10 +5161,7 @@ func (m *QueryChannelClientStateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5337,10 +5313,7 @@ func (m *QueryChannelConsensusStateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5525,10 +5498,7 @@ func (m *QueryChannelConsensusStateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5661,10 +5631,7 @@ func (m *QueryPacketCommitmentRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5815,10 +5782,7 @@ func (m *QueryPacketCommitmentResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5968,10 +5932,7 @@ func (m *QueryPacketCommitmentsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6124,10 +6085,7 @@ func (m *QueryPacketCommitmentsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6260,10 +6218,7 @@ func (m *QueryPacketReceiptRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6400,10 +6355,7 @@ func (m *QueryPacketReceiptResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6536,10 +6488,7 @@ func (m *QueryPacketAcknowledgementRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6690,10 +6639,7 @@ func (m *QueryPacketAcknowledgementResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6843,10 +6789,7 @@ func (m *QueryPacketAcknowledgementsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6999,10 +6942,7 @@ func (m *QueryPacketAcknowledgementsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -7192,10 +7132,7 @@ func (m *QueryUnreceivedPacketsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -7354,10 +7291,7 @@ func (m *QueryUnreceivedPacketsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -7547,10 +7481,7 @@ func (m *QueryUnreceivedAcksRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -7709,10 +7640,7 @@ func (m *QueryUnreceivedAcksResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -7826,10 +7754,7 @@ func (m *QueryNextSequenceReceiveRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -7965,10 +7890,7 @@ func (m *QueryNextSequenceReceiveResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/04-channel/types/tx.pb.go b/x/ibc/core/04-channel/types/tx.pb.go index 9d2f3e221e98..df6a9653f671 100644 --- a/x/ibc/core/04-channel/types/tx.pb.go +++ b/x/ibc/core/04-channel/types/tx.pb.go @@ -2697,10 +2697,7 @@ func (m *MsgChannelOpenInit) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2750,10 +2747,7 @@ func (m *MsgChannelOpenInitResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -3031,10 +3025,7 @@ func (m *MsgChannelOpenTry) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -3084,10 +3075,7 @@ func (m *MsgChannelOpenTryResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -3364,10 +3352,7 @@ func (m *MsgChannelOpenAck) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -3417,10 +3402,7 @@ func (m *MsgChannelOpenAckResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -3633,10 +3615,7 @@ func (m *MsgChannelOpenConfirm) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -3686,10 +3665,7 @@ func (m *MsgChannelOpenConfirmResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -3835,10 +3811,7 @@ func (m *MsgChannelCloseInit) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -3888,10 +3861,7 @@ func (m *MsgChannelCloseInitResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -4104,10 +4074,7 @@ func (m *MsgChannelCloseConfirm) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -4157,10 +4124,7 @@ func (m *MsgChannelCloseConfirmResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -4342,10 +4306,7 @@ func (m *MsgRecvPacket) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -4395,10 +4356,7 @@ func (m *MsgRecvPacketResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -4599,10 +4557,7 @@ func (m *MsgTimeout) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -4652,10 +4607,7 @@ func (m *MsgTimeoutResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -4890,10 +4842,7 @@ func (m *MsgTimeoutOnClose) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -4943,10 +4892,7 @@ func (m *MsgTimeoutOnCloseResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -5162,10 +5108,7 @@ func (m *MsgAcknowledgement) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -5215,10 +5158,7 @@ func (m *MsgAcknowledgementResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/23-commitment/types/commitment.pb.go b/x/ibc/core/23-commitment/types/commitment.pb.go index 9634e25735c9..06bc66522611 100644 --- a/x/ibc/core/23-commitment/types/commitment.pb.go +++ b/x/ibc/core/23-commitment/types/commitment.pb.go @@ -512,10 +512,7 @@ func (m *MerkleRoot) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCommitment - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCommitment } if (iNdEx + skippy) > l { @@ -599,10 +596,7 @@ func (m *MerklePrefix) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCommitment - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCommitment } if (iNdEx + skippy) > l { @@ -684,10 +678,7 @@ func (m *MerklePath) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCommitment - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCommitment } if (iNdEx + skippy) > l { @@ -771,10 +762,7 @@ func (m *MerkleProof) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthCommitment - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCommitment } if (iNdEx + skippy) > l { diff --git a/x/ibc/core/client/cli/cli.go b/x/ibc/core/client/cli/cli.go index 51bb063a6a0b..bda4123be0ec 100644 --- a/x/ibc/core/client/cli/cli.go +++ b/x/ibc/core/client/cli/cli.go @@ -8,8 +8,6 @@ import ( connection "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection" channel "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel" host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" - solomachine "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine" - tendermint "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint" ) // GetTxCmd returns the transaction commands for this module @@ -23,8 +21,7 @@ func GetTxCmd() *cobra.Command { } ibcTxCmd.AddCommand( - solomachine.GetTxCmd(), - tendermint.GetTxCmd(), + ibcclient.GetTxCmd(), connection.GetTxCmd(), channel.GetTxCmd(), ) diff --git a/x/ibc/core/exported/client.go b/x/ibc/core/exported/client.go index 656a233b3a9c..3d552b07724a 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/keeper/msg_server.go b/x/ibc/core/keeper/msg_server.go index 5ea590fea93a..dcddcaed16d4 100644 --- a/x/ibc/core/keeper/msg_server.go +++ b/x/ibc/core/keeper/msg_server.go @@ -326,15 +326,15 @@ func (k Keeper) ChannelOpenAck(goCtx context.Context, msg *channeltypes.MsgChann return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } - if err = cbs.OnChanOpenAck(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyVersion); err != nil { - return nil, sdkerrors.Wrap(err, "channel open ack callback failed") - } - _, err = channel.HandleMsgChannelOpenAck(ctx, k.ChannelKeeper, cap, msg) if err != nil { return nil, err } + if err = cbs.OnChanOpenAck(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyVersion); err != nil { + return nil, sdkerrors.Wrap(err, "channel open ack callback failed") + } + return &channeltypes.MsgChannelOpenAckResponse{}, nil } @@ -354,15 +354,15 @@ func (k Keeper) ChannelOpenConfirm(goCtx context.Context, msg *channeltypes.MsgC return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) } - if err = cbs.OnChanOpenConfirm(ctx, msg.PortId, msg.ChannelId); err != nil { - return nil, sdkerrors.Wrap(err, "channel open confirm callback failed") - } - _, err = channel.HandleMsgChannelOpenConfirm(ctx, k.ChannelKeeper, cap, msg) if err != nil { return nil, err } + if err = cbs.OnChanOpenConfirm(ctx, msg.PortId, msg.ChannelId); err != nil { + return nil, sdkerrors.Wrap(err, "channel open confirm callback failed") + } + return &channeltypes.MsgChannelOpenConfirmResponse{}, nil } diff --git a/x/ibc/core/module.go b/x/ibc/core/module.go index 3371dc88a446..6527ab71eb2f 100644 --- a/x/ibc/core/module.go +++ b/x/ibc/core/module.go @@ -156,6 +156,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(ExportGenesis(ctx, *am.keeper)) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock returns the begin blocker for the ibc module. func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { ibcclient.BeginBlocker(ctx, am.keeper.ClientKeeper) diff --git a/x/ibc/core/spec/01_concepts.md b/x/ibc/core/spec/01_concepts.md index 045508999d39..4347fb674154 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 be3b508b795c..518ff9247b97 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/core/types/genesis.pb.go b/x/ibc/core/types/genesis.pb.go index aed5541827ff..a6f2b1754052 100644 --- a/x/ibc/core/types/genesis.pb.go +++ b/x/ibc/core/types/genesis.pb.go @@ -339,10 +339,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/ibc/light-clients/06-solomachine/client/cli/cli.go b/x/ibc/light-clients/06-solomachine/client/cli/cli.go deleted file mode 100644 index ab44926204f1..000000000000 --- a/x/ibc/light-clients/06-solomachine/client/cli/cli.go +++ /dev/null @@ -1,27 +0,0 @@ -package cli - -import ( - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" -) - -// NewTxCmd returns a root CLI command handler for all solo machine transaction commands. -func NewTxCmd() *cobra.Command { - txCmd := &cobra.Command{ - Use: types.SubModuleName, - Short: "Solo Machine transaction subcommands", - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - RunE: client.ValidateCmd, - } - - txCmd.AddCommand( - NewCreateClientCmd(), - NewUpdateClientCmd(), - NewSubmitMisbehaviourCmd(), - ) - - return txCmd -} diff --git a/x/ibc/light-clients/06-solomachine/client/cli/tx.go b/x/ibc/light-clients/06-solomachine/client/cli/tx.go deleted file mode 100644 index dea2d2ae7388..000000000000 --- a/x/ibc/light-clients/06-solomachine/client/cli/tx.go +++ /dev/null @@ -1,169 +0,0 @@ -package cli - -import ( - "fmt" - "io/ioutil" - "strconv" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "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/codec" - "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/light-clients/06-solomachine/types" -) - -const ( - flagAllowUpdateAfterProposal = "allow_update_after_proposal" -) - -// NewCreateClientCmd defines the command to create a new solo machine client. -func NewCreateClientCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create [sequence] [path/to/consensus_state.json]", - Short: "create new solo machine client", - Long: `create a new solo machine client with the specified identifier and public key - - ConsensusState json example: {"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A/3SXL2ONYaOkxpdR5P8tHTlSlPv1AwQwSFxKRee5JQW"},"diversifier":"diversifier","timestamp":"10"}`, - Example: fmt.Sprintf("%s tx ibc %s create [sequence] [path/to/consensus_state] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - sequence, err := strconv.ParseUint(args[0], 10, 64) - if err != nil { - return err - } - - cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - - // attempt to unmarshal consensus state argument - consensusState := &types.ConsensusState{} - if err := cdc.UnmarshalJSON([]byte(args[1]), consensusState); err != nil { - - // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(args[1]) - if err != nil { - return errors.Wrap(err, "neither JSON input nor path to .json file for consensus state were provided") - } - - if err := cdc.UnmarshalJSON(contents, consensusState); err != nil { - return errors.Wrap(err, "error unmarshalling consensus state file") - } - } - - allowUpdateAfterProposal, _ := cmd.Flags().GetBool(flagAllowUpdateAfterProposal) - - clientState := types.NewClientState(sequence, consensusState, allowUpdateAfterProposal) - msg, err := clienttypes.NewMsgCreateClient(clientState, consensusState, clientCtx.GetFromAddress()) - if err != nil { - return err - } - - if err := msg.ValidateBasic(); err != nil { - return err - } - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - cmd.Flags().Bool(flagAllowUpdateAfterProposal, false, "allow governance proposal to update client") - flags.AddTxFlagsToCmd(cmd) - - return cmd -} - -// NewUpdateClientCmd defines the command to update a solo machine client. -func NewUpdateClientCmd() *cobra.Command { - return &cobra.Command{ - Use: "update [client-id] [path/to/header.json]", - Short: "update existing client with a header", - Long: "update existing client with a solo machine header", - Example: fmt.Sprintf("%s tx ibc %s update [client-id] [path/to/header.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - clientID := args[0] - - cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - - header := &types.Header{} - if err := cdc.UnmarshalJSON([]byte(args[1]), header); err != nil { - - // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(args[1]) - if err != nil { - return errors.Wrap(err, "neither JSON input nor path to .json file for header were provided") - } - - if err := cdc.UnmarshalJSON(contents, header); err != nil { - return errors.Wrap(err, "error unmarshalling header file") - } - } - - msg, err := clienttypes.NewMsgUpdateClient(clientID, header, clientCtx.GetFromAddress()) - if err != nil { - return err - } - - if err := msg.ValidateBasic(); err != nil { - return err - } - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } -} - -// NewSubmitMisbehaviourCmd defines the command to submit a misbehaviour to prevent -// future updates. -func NewSubmitMisbehaviourCmd() *cobra.Command { - return &cobra.Command{ - Use: "misbehaviour [path/to/misbehaviour.json]", - Short: "submit a client misbehaviour", - Long: "submit a client misbehaviour to prevent future updates", - Example: fmt.Sprintf("%s tx ibc %s misbehaviour [path/to/misbehaviour.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - - m := &types.Misbehaviour{} - if err := cdc.UnmarshalJSON([]byte(args[0]), m); err != nil { - - // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(args[0]) - if err != nil { - return errors.Wrap(err, "neither JSON input nor path to .json file for misbehaviour were provided") - } - - if err := cdc.UnmarshalJSON(contents, m); err != nil { - return errors.Wrap(err, "error unmarshalling misbehaviour file") - } - } - - msg, err := clienttypes.NewMsgSubmitMisbehaviour(m.ClientId, m, clientCtx.GetFromAddress()) - if err != nil { - return err - } - - if err := msg.ValidateBasic(); err != nil { - return err - } - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } -} diff --git a/x/ibc/light-clients/06-solomachine/module.go b/x/ibc/light-clients/06-solomachine/module.go index bdc7d9c04838..bafbd0152f1c 100644 --- a/x/ibc/light-clients/06-solomachine/module.go +++ b/x/ibc/light-clients/06-solomachine/module.go @@ -1,9 +1,6 @@ package solomachine import ( - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/client/cli" "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" ) @@ -11,8 +8,3 @@ import ( func Name() string { return types.SubModuleName } - -// GetTxCmd returns the root tx command for the solo machine client. -func GetTxCmd() *cobra.Command { - return cli.NewTxCmd() -} 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 fd8a4f71b20e..de486b71b1a8 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 3ca4b7017acf..48a1e18f1c90 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/misbehaviour.go b/x/ibc/light-clients/06-solomachine/types/misbehaviour.go index 65b8a4ee4753..f5b218ccf208 100644 --- a/x/ibc/light-clients/06-solomachine/types/misbehaviour.go +++ b/x/ibc/light-clients/06-solomachine/types/misbehaviour.go @@ -9,9 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) -var ( - _ exported.Misbehaviour = (*Misbehaviour)(nil) -) +var _ exported.Misbehaviour = &Misbehaviour{} // ClientType is a Solo Machine light client. func (misbehaviour Misbehaviour) ClientType() string { 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 b55e612b3c96..e38155b23615 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 da5c815ecbc8..0113da104497 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) - - // 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) + subjectClientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), solomachine.ClientID) + substituteClientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), substitute.ClientID) - 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/06-solomachine/types/solomachine.pb.go b/x/ibc/light-clients/06-solomachine/types/solomachine.pb.go index 592d73b22b4e..8b31fb0d4116 100644 --- a/x/ibc/light-clients/06-solomachine/types/solomachine.pb.go +++ b/x/ibc/light-clients/06-solomachine/types/solomachine.pb.go @@ -2064,10 +2064,7 @@ func (m *ClientState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -2204,10 +2201,7 @@ func (m *ConsensusState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -2397,10 +2391,7 @@ func (m *Header) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -2573,10 +2564,7 @@ func (m *Misbehaviour) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -2732,10 +2720,7 @@ func (m *SignatureAndData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -2838,10 +2823,7 @@ func (m *TimestampedSignatureData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -3014,10 +2996,7 @@ func (m *SignBytes) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -3135,10 +3114,7 @@ func (m *HeaderData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -3258,10 +3234,7 @@ func (m *ClientStateData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -3381,10 +3354,7 @@ func (m *ConsensusStateData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -3504,10 +3474,7 @@ func (m *ConnectionStateData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -3627,10 +3594,7 @@ func (m *ChannelStateData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -3748,10 +3712,7 @@ func (m *PacketCommitmentData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -3869,10 +3830,7 @@ func (m *PacketAcknowledgementData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -3956,10 +3914,7 @@ func (m *PacketReceiptAbsenceData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { @@ -4062,10 +4017,7 @@ func (m *NextSequenceRecvData) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSolomachine } if (iNdEx + skippy) > l { diff --git a/x/ibc/light-clients/07-tendermint/client/cli/cli.go b/x/ibc/light-clients/07-tendermint/client/cli/cli.go deleted file mode 100644 index a214869d089e..000000000000 --- a/x/ibc/light-clients/07-tendermint/client/cli/cli.go +++ /dev/null @@ -1,25 +0,0 @@ -package cli - -import ( - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" -) - -// NewTxCmd returns a root CLI command handler for all x/ibc/light-clients/07-tendermint transaction commands. -func NewTxCmd() *cobra.Command { - txCmd := &cobra.Command{ - Use: types.SubModuleName, - Short: "Tendermint client transaction subcommands", - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - } - - txCmd.AddCommand( - NewCreateClientCmd(), - NewUpdateClientCmd(), - NewSubmitMisbehaviourCmd(), - ) - - return txCmd -} diff --git a/x/ibc/light-clients/07-tendermint/client/cli/tx.go b/x/ibc/light-clients/07-tendermint/client/cli/tx.go deleted file mode 100644 index f8b925e4f0cc..000000000000 --- a/x/ibc/light-clients/07-tendermint/client/cli/tx.go +++ /dev/null @@ -1,281 +0,0 @@ -package cli - -import ( - "fmt" - "io/ioutil" - "strconv" - "strings" - "time" - - ics23 "github.com/confio/ics23/go" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/tendermint/tendermint/light" - - "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/codec" - "github.com/cosmos/cosmos-sdk/version" - clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" - commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" - "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" -) - -const ( - flagTrustLevel = "trust-level" - flagProofSpecs = "proof-specs" - flagUpgradePath = "upgrade-path" - flagAllowUpdateAfterExpiry = "allow_update_after_expiry" - flagAllowUpdateAfterMisbehaviour = "allow_update_after_misbehaviour" -) - -// NewCreateClientCmd defines the command to create a new IBC Client as defined -// in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create -func NewCreateClientCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create [path/to/consensus_state.json] [trusting_period] [unbonding_period] [max_clock_drift]", - Short: "create new tendermint client", - Long: `Create a new tendermint IBC client. - - 'trust-level' flag can be a fraction (eg: '1/3') or 'default' - - 'proof-specs' flag can be JSON input, a path to a .json file or 'default' - - 'upgrade-path' flag is a string specifying the upgrade path for this chain where a future upgraded client will be stored. The path is a comma-separated list representing the keys in order of the keyPath to the committed upgraded client. - e.g. 'upgrade/upgradedClient'`, - Example: fmt.Sprintf("%s tx ibc %s create [path/to/consensus_state.json] [trusting_period] [unbonding_period] [max_clock_drift] --trust-level default --consensus-params [path/to/consensus-params.json] --proof-specs [path/to/proof-specs.json] --upgrade-path upgrade/upgradedClient --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), - Args: cobra.ExactArgs(4), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - legacyAmino := codec.NewLegacyAmino() - - var header *types.Header - if err := cdc.UnmarshalJSON([]byte(args[0]), header); err != nil { - // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(args[0]) - if err != nil { - return errors.New("neither JSON input nor path to .json file were provided for consensus header") - } - if err := cdc.UnmarshalJSON(contents, header); err != nil { - return errors.Wrap(err, "error unmarshalling consensus header file") - } - } - - var ( - trustLevel types.Fraction - specs []*ics23.ProofSpec - ) - - lvl, _ := cmd.Flags().GetString(flagTrustLevel) - - if lvl == "default" { - trustLevel = types.NewFractionFromTm(light.DefaultTrustLevel) - } else { - trustLevel, err = parseFraction(lvl) - if err != nil { - return err - } - } - - trustingPeriod, err := time.ParseDuration(args[1]) - if err != nil { - return err - } - - ubdPeriod, err := time.ParseDuration(args[2]) - if err != nil { - return err - } - - maxClockDrift, err := time.ParseDuration(args[3]) - if err != nil { - return err - } - - spc, _ := cmd.Flags().GetString(flagProofSpecs) - if spc == "default" { - specs = commitmenttypes.GetSDKSpecs() - // TODO migrate to use JSONMarshaler (implement MarshalJSONArray - // or wrap lists of proto.Message in some other message) - } else if err := legacyAmino.UnmarshalJSON([]byte(spc), &specs); err != nil { - // check for file path if JSON input not provided - contents, err := ioutil.ReadFile(spc) - if err != nil { - return errors.New("neither JSON input nor path to .json file was provided for proof specs flag") - } - // TODO migrate to use JSONMarshaler (implement MarshalJSONArray - // or wrap lists of proto.Message in some other message) - if err := legacyAmino.UnmarshalJSON(contents, &specs); err != nil { - return errors.Wrap(err, "error unmarshalling proof specs file") - } - } - - allowUpdateAfterExpiry, _ := cmd.Flags().GetBool(flagAllowUpdateAfterExpiry) - allowUpdateAfterMisbehaviour, _ := cmd.Flags().GetBool(flagAllowUpdateAfterMisbehaviour) - - upgradePathStr, _ := cmd.Flags().GetString(flagUpgradePath) - upgradePath := strings.Split(upgradePathStr, ",") - - // validate header - if err := header.ValidateBasic(); err != nil { - return err - } - - height := header.GetHeight().(clienttypes.Height) - - clientState := types.NewClientState( - header.GetHeader().GetChainID(), trustLevel, trustingPeriod, ubdPeriod, maxClockDrift, - height, specs, upgradePath, allowUpdateAfterExpiry, allowUpdateAfterMisbehaviour, - ) - - consensusState := header.ConsensusState() - - msg, err := clienttypes.NewMsgCreateClient( - clientState, consensusState, clientCtx.GetFromAddress(), - ) - if err != nil { - return err - } - - if err := msg.ValidateBasic(); err != nil { - return err - } - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - cmd.Flags().String(flagTrustLevel, "default", "light client trust level fraction for header updates") - cmd.Flags().String(flagProofSpecs, "default", "proof specs format to be used for verification") - cmd.Flags().Bool(flagAllowUpdateAfterExpiry, false, "allow governance proposal to update client after expiry") - cmd.Flags().Bool(flagAllowUpdateAfterMisbehaviour, false, "allow governance proposal to update client after misbehaviour") - flags.AddTxFlagsToCmd(cmd) - - return cmd -} - -// NewUpdateClientCmd defines the command to update a client as defined in -// https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#update -func NewUpdateClientCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "update [client-id] [path/to/header.json]", - Short: "update existing client with a header", - Long: "update existing tendermint client with a tendermint header", - Example: fmt.Sprintf( - "$ %s tx ibc %s update [client-id] [path/to/header.json] --from node0 --home ../node0/cli --chain-id $CID", - version.AppName, types.SubModuleName, - ), - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - clientID := args[0] - - cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - - var header *types.Header - if err := cdc.UnmarshalJSON([]byte(args[1]), header); err != nil { - // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(args[1]) - if err != nil { - return errors.New("neither JSON input nor path to .json file were provided") - } - if err := cdc.UnmarshalJSON(contents, header); err != nil { - return errors.Wrap(err, "error unmarshalling header file") - } - } - - msg, err := clienttypes.NewMsgUpdateClient(clientID, header, clientCtx.GetFromAddress()) - if err != nil { - return err - } - - if err := msg.ValidateBasic(); err != nil { - return err - } - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) - - return cmd -} - -// NewSubmitMisbehaviourCmd defines the command to submit a misbehaviour to invalidate -// previous state roots and prevent future updates as defined in -// https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#misbehaviour -func NewSubmitMisbehaviourCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "misbehaviour [path/to/misbehaviour.json]", - Short: "submit a client misbehaviour", - Long: "submit a client misbehaviour to invalidate to invalidate previous state roots and prevent future updates", - Example: fmt.Sprintf( - "$ %s tx ibc %s misbehaviour [path/to/misbehaviour.json] --from node0 --home ../node0/cli --chain-id $CID", - version.AppName, types.SubModuleName, - ), - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - - var m *types.Misbehaviour - if err := cdc.UnmarshalJSON([]byte(args[0]), m); err != nil { - // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(args[0]) - if err != nil { - return errors.New("neither JSON input nor path to .json file were provided") - } - if err := cdc.UnmarshalJSON(contents, m); err != nil { - return errors.Wrap(err, "error unmarshalling misbehaviour file") - } - } - - msg, err := clienttypes.NewMsgSubmitMisbehaviour(m.ClientId, m, clientCtx.GetFromAddress()) - if err != nil { - return err - } - - if err := msg.ValidateBasic(); err != nil { - return err - } - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) - - return cmd -} - -func parseFraction(fraction string) (types.Fraction, error) { - fr := strings.Split(fraction, "/") - if len(fr) != 2 || fr[0] == fraction { - return types.Fraction{}, fmt.Errorf("fraction must have format 'numerator/denominator' got %s", fraction) - } - - numerator, err := strconv.ParseUint(fr[0], 10, 64) - if err != nil { - return types.Fraction{}, fmt.Errorf("invalid trust-level numerator: %w", err) - } - - denominator, err := strconv.ParseUint(fr[1], 10, 64) - if err != nil { - return types.Fraction{}, fmt.Errorf("invalid trust-level denominator: %w", err) - } - - return types.Fraction{ - Numerator: numerator, - Denominator: denominator, - }, nil - -} diff --git a/x/ibc/light-clients/07-tendermint/module.go b/x/ibc/light-clients/07-tendermint/module.go index 38c7fa621b11..4c5cc2f947f4 100644 --- a/x/ibc/light-clients/07-tendermint/module.go +++ b/x/ibc/light-clients/07-tendermint/module.go @@ -1,9 +1,6 @@ package tendermint import ( - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/client/cli" "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" ) @@ -11,8 +8,3 @@ import ( func Name() string { return types.SubModuleName } - -// GetTxCmd returns the root tx command for the IBC client -func GetTxCmd() *cobra.Command { - return cli.NewTxCmd() -} diff --git a/x/ibc/light-clients/07-tendermint/types/consensus_state.go b/x/ibc/light-clients/07-tendermint/types/consensus_state.go index b581983944fd..adb469a3d108 100644 --- a/x/ibc/light-clients/07-tendermint/types/consensus_state.go +++ b/x/ibc/light-clients/07-tendermint/types/consensus_state.go @@ -48,11 +48,8 @@ func (cs ConsensusState) ValidateBasic() error { if err := tmtypes.ValidateHash(cs.NextValidatorsHash); err != nil { return sdkerrors.Wrap(err, "next validators hash is invalid") } - if cs.Timestamp.IsZero() { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "timestamp cannot be zero Unix time") - } - if cs.Timestamp.UnixNano() < 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "timestamp cannot be negative Unix time") + if cs.Timestamp.Unix() <= 0 { + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "timestamp must be a positive Unix time") } return nil } 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 4cd3eb376cbd..c64c52b3f810 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") } -} - -// 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) { + // 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) - // 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 - } + consensusState, err := GetConsensusState(substituteClientStore, cdc, height) + if err != nil { + // not all consensus states will be filled in + continue + } + SetConsensusState(subjectClientStore, cdc, consensusState, height) - newClientState, consensusState := update(ctx, clientStore, &cs, header) - return newClientState, consensusState, nil -} + processedTime, found := GetProcessedTime(substituteClientStore, height) + if !found { + continue + } + SetProcessedTime(subjectClientStore, height, processedTime) -// 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) - 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) + cs.LatestHeight = substituteClientState.LatestHeight + + // 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") } - // 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(), + latestConsensusState, err := GetConsensusState(subjectClientStore, cdc, cs.GetLatestHeight()) + if err != nil { + 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 &cs, nil +} - return 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 6863ad1d1c80..c1fb05413d9d 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) - - // 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) - - clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) - - // 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) +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) + + 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 + + // 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 + + // 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 substitue + 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 8b2720c5a995..7d6a841b89f1 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/07-tendermint/types/tendermint.pb.go b/x/ibc/light-clients/07-tendermint/types/tendermint.pb.go index 9993ed261db0..510a643c256b 100644 --- a/x/ibc/light-clients/07-tendermint/types/tendermint.pb.go +++ b/x/ibc/light-clients/07-tendermint/types/tendermint.pb.go @@ -1230,10 +1230,7 @@ func (m *ClientState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTendermint - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTendermint } if (iNdEx + skippy) > l { @@ -1383,10 +1380,7 @@ func (m *ConsensusState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTendermint - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTendermint } if (iNdEx + skippy) > l { @@ -1540,10 +1534,7 @@ func (m *Misbehaviour) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTendermint - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTendermint } if (iNdEx + skippy) > l { @@ -1734,10 +1725,7 @@ func (m *Header) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTendermint - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTendermint } if (iNdEx + skippy) > l { @@ -1825,10 +1813,7 @@ func (m *Fraction) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTendermint - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTendermint } if (iNdEx + skippy) > l { 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 e0ba7a2f0b20..5a4a41a17988 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 13a1367d5c21..bc58f62539be 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/light-clients/09-localhost/types/localhost.pb.go b/x/ibc/light-clients/09-localhost/types/localhost.pb.go index 28f7f74d3879..53f01758492b 100644 --- a/x/ibc/light-clients/09-localhost/types/localhost.pb.go +++ b/x/ibc/light-clients/09-localhost/types/localhost.pb.go @@ -268,10 +268,7 @@ func (m *ClientState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthLocalhost - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLocalhost } if (iNdEx + skippy) > l { diff --git a/x/mint/client/cli/query.go b/x/mint/client/cli/query.go index cce0d7c12bbb..792fa8a6798f 100644 --- a/x/mint/client/cli/query.go +++ b/x/mint/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "github.com/spf13/cobra" @@ -45,7 +44,7 @@ func GetCmdQueryParams() *cobra.Command { queryClient := types.NewQueryClient(clientCtx) params := &types.QueryParamsRequest{} - res, err := queryClient.Params(context.Background(), params) + res, err := queryClient.Params(cmd.Context(), params) if err != nil { return err @@ -75,7 +74,7 @@ func GetCmdQueryInflation() *cobra.Command { queryClient := types.NewQueryClient(clientCtx) params := &types.QueryInflationRequest{} - res, err := queryClient.Inflation(context.Background(), params) + res, err := queryClient.Inflation(cmd.Context(), params) if err != nil { return err @@ -105,7 +104,7 @@ func GetCmdQueryAnnualProvisions() *cobra.Command { queryClient := types.NewQueryClient(clientCtx) params := &types.QueryAnnualProvisionsRequest{} - res, err := queryClient.AnnualProvisions(context.Background(), params) + res, err := queryClient.AnnualProvisions(cmd.Context(), params) if err != nil { return err diff --git a/x/mint/module.go b/x/mint/module.go index 44e96ce74bda..d33043b36a2e 100644 --- a/x/mint/module.go +++ b/x/mint/module.go @@ -146,6 +146,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(gs) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock returns the begin blocker for the mint module. func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { BeginBlocker(ctx, am.keeper) diff --git a/x/mint/simulation/decoder.go b/x/mint/simulation/decoder.go index 37d9930f7da5..f0a89be43965 100644 --- a/x/mint/simulation/decoder.go +++ b/x/mint/simulation/decoder.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/mint/types" ) -// NewDecodeStore returns a decoder function closure that umarshals the KVPair's +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's // Value to the corresponding mint type. func NewDecodeStore(cdc codec.Marshaler) func(kvA, kvB kv.Pair) string { return func(kvA, kvB kv.Pair) string { diff --git a/x/mint/simulation/decoder_test.go b/x/mint/simulation/decoder_test.go index 762768c07eaa..0116108b4ed3 100644 --- a/x/mint/simulation/decoder_test.go +++ b/x/mint/simulation/decoder_test.go @@ -14,7 +14,7 @@ import ( ) func TestDecodeStore(t *testing.T) { - cdc, _ := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Marshaler dec := simulation.NewDecodeStore(cdc) minter := types.NewMinter(sdk.OneDec(), sdk.NewDec(15)) diff --git a/x/mint/spec/02_state.md b/x/mint/spec/02_state.md index c4d7a3eff185..1f9eae344754 100644 --- a/x/mint/spec/02_state.md +++ b/x/mint/spec/02_state.md @@ -8,28 +8,14 @@ order: 2 The minter is a space for holding current inflation information. - - Minter: `0x00 -> amino(minter)` + - Minter: `0x00 -> ProtocolBuffer(minter)` -```go -type Minter struct { - Inflation sdk.Dec // current annual inflation rate - AnnualProvisions sdk.Dec // current annual exptected provisions -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc7/proto/cosmos/mint/v1beta1/mint.proto#L8-L19 ## Params Minting params are held in the global params store. - - Params: `mint/params -> amino(params)` - -```go -type Params struct { - MintDenom string // type of coin to mint - InflationRateChange sdk.Dec // maximum annual change in inflation rate - InflationMax sdk.Dec // maximum inflation rate - InflationMin sdk.Dec // minimum inflation rate - GoalBonded sdk.Dec // goal of percent bonded atoms - BlocksPerYear uint64 // expected blocks per year -} -``` + - Params: `mint/params -> legacy_amino(params)` + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc7/proto/cosmos/mint/v1beta1/mint.proto#L21-L53 \ No newline at end of file diff --git a/x/mint/types/genesis.pb.go b/x/mint/types/genesis.pb.go index d764cfcf46f5..1486c35f2232 100644 --- a/x/mint/types/genesis.pb.go +++ b/x/mint/types/genesis.pb.go @@ -276,10 +276,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/mint/types/mint.pb.go b/x/mint/types/mint.pb.go index 2575c2f96fbe..4ed1cccd6978 100644 --- a/x/mint/types/mint.pb.go +++ b/x/mint/types/mint.pb.go @@ -441,10 +441,7 @@ func (m *Minter) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthMint - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthMint } if (iNdEx + skippy) > l { @@ -681,10 +678,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthMint - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthMint } if (iNdEx + skippy) > l { diff --git a/x/mint/types/minter_test.go b/x/mint/types/minter_test.go index 8760a66f4466..4abedb51cd05 100644 --- a/x/mint/types/minter_test.go +++ b/x/mint/types/minter_test.go @@ -89,6 +89,7 @@ func TestBlockProvision(t *testing.T) { // using sdk.Dec operations: (current implementation) // BenchmarkBlockProvision-4 3000000 429 ns/op func BenchmarkBlockProvision(b *testing.B) { + b.ReportAllocs() minter := InitialMinter(sdk.NewDecWithPrec(1, 1)) params := DefaultParams() @@ -105,6 +106,7 @@ func BenchmarkBlockProvision(b *testing.B) { // Next inflation benchmarking // BenchmarkNextInflation-4 1000000 1828 ns/op func BenchmarkNextInflation(b *testing.B) { + b.ReportAllocs() minter := InitialMinter(sdk.NewDecWithPrec(1, 1)) params := DefaultParams() bondedRatio := sdk.NewDecWithPrec(1, 1) @@ -119,6 +121,7 @@ func BenchmarkNextInflation(b *testing.B) { // Next annual provisions benchmarking // BenchmarkNextAnnualProvisions-4 5000000 251 ns/op func BenchmarkNextAnnualProvisions(b *testing.B) { + b.ReportAllocs() minter := InitialMinter(sdk.NewDecWithPrec(1, 1)) params := DefaultParams() totalSupply := sdk.NewInt(100000000000000) diff --git a/x/mint/types/query.pb.go b/x/mint/types/query.pb.go index ea5f843cd0e3..f707eb810d2d 100644 --- a/x/mint/types/query.pb.go +++ b/x/mint/types/query.pb.go @@ -749,10 +749,7 @@ func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -835,10 +832,7 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -888,10 +882,7 @@ func (m *QueryInflationRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -974,10 +965,7 @@ func (m *QueryInflationResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1027,10 +1015,7 @@ func (m *QueryAnnualProvisionsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1113,10 +1098,7 @@ func (m *QueryAnnualProvisionsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/params/client/cli/query.go b/x/params/client/cli/query.go index 516ff3b6b474..ce7b45d0602d 100644 --- a/x/params/client/cli/query.go +++ b/x/params/client/cli/query.go @@ -1,8 +1,6 @@ package cli import ( - "context" - "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -41,7 +39,7 @@ func NewQuerySubspaceParamsCmd() *cobra.Command { queryClient := proposal.NewQueryClient(clientCtx) params := proposal.QueryParamsRequest{Subspace: args[0], Key: args[1]} - res, err := queryClient.Params(context.Background(), ¶ms) + res, err := queryClient.Params(cmd.Context(), ¶ms) if err != nil { return err } diff --git a/x/params/client/cli/tx.go b/x/params/client/cli/tx.go index 39b0242522f4..f83793a1404c 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/doc.go b/x/params/doc.go index 68ab4dee0e1f..2a79b0fd51a6 100644 --- a/x/params/doc.go +++ b/x/params/doc.go @@ -6,9 +6,8 @@ namespace for a parameter store, where keys are prefixed by pre-configured subspace names which modules provide. The Keeper has a permission to access all existing subspaces. -Subspace can be used by the individual keepers, who needs a private parameter store -that the other keeper cannot modify. Keeper can be used by the Governance keeper, -who need to modify any parameter in case of the proposal passes. +Subspace can be used by the individual keepers, which need a private parameter store +that the other keepers cannot modify. Basic Usage: @@ -23,11 +22,11 @@ Basic Usage: KeyParameter2 = "myparameter2" ) -2. Create a concrete parameter struct and define the validation functions: +2. Define parameters as proto message and define the validation functions: - type MyParams struct { - MyParam1 int64 `json:"my_param1" yaml:"my_param1"` - MyParam2 bool `json:"my_param2" yaml:"my_param2"` + message MyParams { + int64 my_param1 = 1; + bool my_param2 = 2; } func validateMyParam1(i interface{}) error { @@ -56,12 +55,12 @@ Basic Usage: func (p *MyParams) ParamSetPairs() params.ParamSetPairs { return params.ParamSetPairs{ - {KeyParameter1, &p.MyParam1, validateMyParam1}, - {KeyParameter2, &p.MyParam2, validateMyParam2}, + params.NewParamSetPair(KeyParameter1, &p.MyParam1, validateMyParam1), + params.NewParamSetPair(KeyParameter2, &p.MyParam2, validateMyParam2), } } - func paramKeyTable() params.KeyTable { + func ParamKeyTable() params.KeyTable { return params.NewKeyTable().RegisterParamSet(&MyParams{}) } @@ -70,7 +69,7 @@ Basic Usage: func NewKeeper(..., paramSpace params.Subspace, ...) Keeper { // set KeyTable if it has not already been set if !paramSpace.HasKeyTable() { - paramSpace = paramSpace.WithKeyTable(paramKeyTable()) + paramSpace = paramSpace.WithKeyTable(ParamKeyTable()) } return Keeper { diff --git a/x/params/keeper/common_test.go b/x/params/keeper/common_test.go index f6d567db11f7..3ba444173588 100644 --- a/x/params/keeper/common_test.go +++ b/x/params/keeper/common_test.go @@ -1,14 +1,9 @@ package keeper_test import ( - "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/codec" - "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" ) @@ -18,7 +13,7 @@ func testComponents() (*codec.LegacyAmino, sdk.Context, sdk.StoreKey, sdk.StoreK legacyAmino := createTestCodec() mkey := sdk.NewKVStoreKey("test") tkey := sdk.NewTransientStoreKey("transient_test") - ctx := defaultContext(mkey, tkey) + ctx := testutil.DefaultContext(mkey, tkey) keeper := paramskeeper.NewKeeper(marshaler, legacyAmino, mkey, tkey) return legacyAmino, ctx, mkey, tkey, keeper @@ -37,16 +32,3 @@ func createTestCodec() *codec.LegacyAmino { cdc.RegisterConcrete(invalid{}, "test/invalid", nil) return cdc } - -func defaultContext(key sdk.StoreKey, tkey sdk.StoreKey) sdk.Context { - db := dbm.NewMemDB() - cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) - cms.MountStoreWithDB(tkey, sdk.StoreTypeTransient, db) - err := cms.LoadLatestVersion() - if err != nil { - panic(err) - } - ctx := sdk.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()) - return ctx -} diff --git a/x/params/module.go b/x/params/module.go index b0a4584129ef..c1a115b210c1 100644 --- a/x/params/module.go +++ b/x/params/module.go @@ -139,6 +139,9 @@ func (am AppModule) ExportGenesis(_ sdk.Context, _ codec.JSONMarshaler) json.Raw return nil } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock performs a no-op. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} diff --git a/x/params/spec/01_keeper.md b/x/params/spec/01_keeper.md index 8cf7e2a53f97..fa97ec5b37fc 100644 --- a/x/params/spec/01_keeper.md +++ b/x/params/spec/01_keeper.md @@ -4,24 +4,16 @@ order: 1 # Keeper -In the app initialization stage, `Keeper.Subspace(Paramspace)` is passed to the -user modules, and the subspaces are stored in `Keeper.spaces`. Later it can be -retrieved with `Keeper.GetSubspace`, so the keepers holding `Keeper` can access -to any subspace. For example, Gov module can take `Keeper` as its argument and -modify parameter of any subspace when a `ParameterChangeProposal` is accepted. +In the app initialization stage, [subspaces](02_subspace.md) can be allocated for other modules' keeper using `Keeper.Subspace` and are stored in `Keeper.spaces`. Then, those modules can have a reference to their specific parameter store through `Keeper.GetSubspace`. Example: ```go -type MasterKeeper struct { - pk params.Keeper +type ExampleKeeper struct { + paramSpace paramtypes.Subspace } -func (k MasterKeeper) SetParam(ctx sdk.Context, space string, key string, param interface{}) { - space, ok := k.ps.GetSubspace(space) - if !ok { - return - } - space.Set(ctx, key, param) +func (k ExampleKeeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSpace.SetParamSet(ctx, ¶ms) } ``` diff --git a/x/params/spec/02_subspace.md b/x/params/spec/02_subspace.md index 5e36eaf498f5..e6ffa760d208 100644 --- a/x/params/spec/02_subspace.md +++ b/x/params/spec/02_subspace.md @@ -4,8 +4,8 @@ order: 2 # Subspace -`Subspace` is a prefixed subspace of the parameter store. Each module who use the -parameter store will take a `Subspace`, not the `Keeper`, to isolate permission to access. +`Subspace` is a prefixed subspace of the parameter store. Each module which uses the +parameter store will take a `Subspace` to isolate permission to access. ## Key @@ -21,20 +21,18 @@ Subkeys can be used for grouping or dynamic parameter key generation during runt All of the parameter keys that will be used should be registered at the compile time. `KeyTable` is essentially a `map[string]attribute`, where the `string` is a parameter key. -Currently, `attribute` only consists of `reflect.Type`, which indicates the parameter -type. It is needed even if the state machine has no error, because the paraeter -can be modified externally, for example via the governance. +Currently, `attribute` consists of a `reflect.Type`, which indicates the parameter +type to check that provided key and value are compatible and registered, as well as a function `ValueValidatorFn` to validate values. Only primary keys have to be registered on the `KeyTable`. Subkeys inherit the attribute of the primary key. ## ParamSet -Modules often define a struct of parameters. Instead of calling methods with -each of those parameters, when the struct implements `ParamSet`, it can be used -with the following methods: +Modules often define parameters as a proto message. The generated struct can implement +`ParamSet` interface to be used with the following methods: * `KeyTable.RegisterParamSet()`: registers all parameters in the struct * `Subspace.{Get, Set}ParamSet()`: Get to & Set from the struct -The implementor should be a pointer in order to use `GetParamSet()` +The implementor should be a pointer in order to use `GetParamSet()`. diff --git a/x/params/spec/README.md b/x/params/spec/README.md index c84ad6e5bc9a..4a136cce57e3 100644 --- a/x/params/spec/README.md +++ b/x/params/spec/README.md @@ -15,9 +15,8 @@ There are two main types, Keeper and Subspace. Subspace is an isolated namespace paramstore, where keys are prefixed by preconfigured spacename. Keeper has a permission to access all existing spaces. -Subspace can be used by the individual keepers, who needs a private parameter store -that the other keeper cannot modify. Keeper can be used by the Governance keeper, -who need to modify any parameter in case of the proposal passes. +Subspace can be used by the individual keepers, which need a private parameter store +that the other keepers cannot modify. The params Keeper can be used to add a route to `x/gov` router in order to modify any parameter in case a proposal passes. The following contents explains how to use params module for master and user modules. diff --git a/x/params/types/proposal/params.pb.go b/x/params/types/proposal/params.pb.go index 68b2f8782066..1681b3960e63 100644 --- a/x/params/types/proposal/params.pb.go +++ b/x/params/types/proposal/params.pb.go @@ -510,10 +510,7 @@ func (m *ParameterChangeProposal) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthParams - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthParams } if (iNdEx + skippy) > l { @@ -659,10 +656,7 @@ func (m *ParamChange) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthParams - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthParams } if (iNdEx + skippy) > l { diff --git a/x/params/types/proposal/query.pb.go b/x/params/types/proposal/query.pb.go index 40bdab3960ce..e0c770d70e24 100644 --- a/x/params/types/proposal/query.pb.go +++ b/x/params/types/proposal/query.pb.go @@ -460,10 +460,7 @@ func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -546,10 +543,7 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/slashing/client/cli/query.go b/x/slashing/client/cli/query.go index b6b71a344cd3..6cb2f803d085 100644 --- a/x/slashing/client/cli/query.go +++ b/x/slashing/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "strings" "github.com/spf13/cobra" @@ -58,7 +57,7 @@ $ query slashing signing-info cosmosvalconspub1zcjduepqfhvwcmt7p06fvdgexx consAddr := sdk.ConsAddress(pk.Address()) params := &types.QuerySigningInfoRequest{ConsAddress: consAddr.String()} - res, err := queryClient.SigningInfo(context.Background(), params) + res, err := queryClient.SigningInfo(cmd.Context(), params) if err != nil { return err } @@ -81,7 +80,7 @@ func GetCmdQuerySigningInfos() *cobra.Command { $ query slashing signing-infos `), - Args: cobra.ExactArgs(1), + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { @@ -95,7 +94,7 @@ $ query slashing signing-infos } params := &types.QuerySigningInfosRequest{Pagination: pageReq} - res, err := queryClient.SigningInfos(context.Background(), params) + res, err := queryClient.SigningInfos(cmd.Context(), params) if err != nil { return err } @@ -128,7 +127,7 @@ $ query slashing params queryClient := types.NewQueryClient(clientCtx) params := &types.QueryParamsRequest{} - res, err := queryClient.Params(context.Background(), params) + res, err := queryClient.Params(cmd.Context(), params) if err != nil { return err } diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index d896c8570cf5..67a83e6a6510 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/client/rest/rest.go b/x/slashing/client/rest/rest.go index a3a88545900b..059cb17c6bdc 100644 --- a/x/slashing/client/rest/rest.go +++ b/x/slashing/client/rest/rest.go @@ -3,9 +3,8 @@ package rest import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/rest" - "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/rest" ) func RegisterHandlers(clientCtx client.Context, rtr *mux.Router) { diff --git a/x/slashing/module.go b/x/slashing/module.go index 91ad472e90df..ba5cfb60adb9 100644 --- a/x/slashing/module.go +++ b/x/slashing/module.go @@ -159,6 +159,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(gs) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock returns the begin blocker for the slashing module. func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { BeginBlocker(ctx, req, am.keeper) diff --git a/x/slashing/simulation/decoder_test.go b/x/slashing/simulation/decoder_test.go index e6122f0e0a98..32fb3f077408 100644 --- a/x/slashing/simulation/decoder_test.go +++ b/x/slashing/simulation/decoder_test.go @@ -25,7 +25,7 @@ var ( ) func TestDecodeStore(t *testing.T) { - cdc, _ := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Marshaler dec := simulation.NewDecodeStore(cdc) info := types.NewValidatorSigningInfo(consAddr1, 0, 1, time.Now().UTC(), false, 0) diff --git a/x/slashing/simulation/operations.go b/x/slashing/simulation/operations.go index 20acc9e91a88..1c49775cc193 100644 --- a/x/slashing/simulation/operations.go +++ b/x/slashing/simulation/operations.go @@ -115,23 +115,23 @@ func SimulateMsgUnjail(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Kee validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) { if res != nil && err == nil { if info.Tombstoned { - return simtypes.NewOperationMsg(msg, true, ""), nil, errors.New("validator should not have been unjailed if validator tombstoned") + return simtypes.NewOperationMsg(msg, true, "", nil), nil, errors.New("validator should not have been unjailed if validator tombstoned") } if ctx.BlockHeader().Time.Before(info.JailedUntil) { - return simtypes.NewOperationMsg(msg, true, ""), nil, errors.New("validator unjailed while validator still in jail period") + return simtypes.NewOperationMsg(msg, true, "", nil), nil, errors.New("validator unjailed while validator still in jail period") } if validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) { - return simtypes.NewOperationMsg(msg, true, ""), nil, errors.New("validator unjailed even though self-delegation too low") + return simtypes.NewOperationMsg(msg, true, "", nil), nil, errors.New("validator unjailed even though self-delegation too low") } } // msg failed as expected - return simtypes.NewOperationMsg(msg, false, ""), nil, nil + return simtypes.NewOperationMsg(msg, false, "", nil), nil, nil } if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, errors.New(res.Log) } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } diff --git a/x/slashing/simulation/operations_test.go b/x/slashing/simulation/operations_test.go index 15764c631ebf..a224d460a310 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 17931ce866a0..3644ff9651e9 100644 --- a/x/slashing/spec/02_state.md +++ b/x/slashing/spec/02_state.md @@ -10,9 +10,16 @@ Every block includes a set of precommits by the validators for the previous bloc known as the `LastCommitInfo` provided by Tendermint. A `LastCommitInfo` is valid so long as it contains precommits from +2/3 of total voting power. -Proposers are incentivized to include precommits from all validators in the `LastCommitInfo` +Proposers are incentivized to include precommits from all validators in the Tendermint `LastCommitInfo` by receiving additional fees proportional to the difference between the voting -power included in the `LastCommitInfo` and +2/3 (see [TODO](https://github.com/cosmos/cosmos-sdk/issues/967)). +power included in the `LastCommitInfo` and +2/3 (see [fee distribution](https://github.com/cosmos/cosmos-sdk/blob/master/docs/spec/fee_distribution)). + +``` +type LastCommitInfo struct { + Round int32 + Votes []VoteInfo +} +``` Validators are penalized for failing to be included in the `LastCommitInfo` for some number of blocks by being automatically jailed, potentially slashed, and unbonded. @@ -20,15 +27,16 @@ 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 -> amino(valSigningInfo)` -- MissedBlocksBitArray: ` 0x02 | ConsAddress | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)` +- ValidatorSigningInfo: ` 0x01 | ConsAddress -> ProtocolBuffer(ValSigningInfo)` +- MissedBlocksBitArray: ` 0x02 | 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. The second mapping acts +validator based on the validator's consensus address. + +The second mapping (`MissedBlocksBitArray`) acts as a bit-array of size `SignedBlocksWindow` that tells us if the validator missed the block for a given index in the bit-array. The index in the bit-array is given as little endian uint64. - The result is a `varint` that takes on `0` or `1`, where `0` indicates the validator did not miss (did sign) the corresponding block, and `1` indicates they missed the block (did not sign). @@ -40,35 +48,4 @@ bonded validator. The `SignedBlocksWindow` parameter defines the size The information stored for tracking validator liveness is as follows: -```protobuf -// ValidatorSigningInfo defines a validator's signing info for monitoring their -// liveness activity. -message ValidatorSigningInfo { - string address = 1; - // height at which validator was first a candidate OR was unjailed - int64 start_height = 2; - // index offset into signed block bit array - int64 index_offset = 3; - // timestamp validator cannot be unjailed until - google.protobuf.Timestamp jailed_until = 4; - // whether or not a validator has been tombstoned (killed out of validator - // set) - bool tombstoned = 5; - // missed blocks counter (to avoid scanning the array every time) - int64 missed_blocks_counter = 6; -} -``` - -Where: - -- **Address**: The validator's consensus address. -- **StartHeight**: The height that the candidate became an active validator - (with non-zero voting power). -- **IndexOffset**: Index which is incremented each time the validator was a bonded - in a block and may have signed a precommit or not. This in conjunction with the - `SignedBlocksWindow` param determines the index in the `MissedBlocksBitArray`. -- **JailedUntil**: Time for which the validator is jailed until due to liveness downtime. -- **Tombstoned**: Desribes if the validator is tombstoned or not. It is set once the - validator commits an equivocation or for any other configured misbehiavor. -- **MissedBlocksCounter**: A counter kept to avoid unnecessary array reads. Note - that `Sum(MissedBlocksBitArray)` equals `MissedBlocksCounter` always. ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/slashing/v1beta1/slashing.proto#L11-L33 diff --git a/x/slashing/spec/03_messages.md b/x/slashing/spec/03_messages.md index 6e7d168ffdb5..d680b83cdfee 100644 --- a/x/slashing/spec/03_messages.md +++ b/x/slashing/spec/03_messages.md @@ -20,14 +20,17 @@ message MsgUnjail { } ``` -And below is its corresponding handler: +Below is a pseudocode of the `MsgSrv/Unjail` RPC: ``` -handleMsgUnjail(tx MsgUnjail) +unjail(tx MsgUnjail) validator = getValidator(tx.ValidatorAddr) if validator == nil fail with "No validator found" + if getSelfDelegation(validator) == 0 + fail with "validator must self delegate before unjailing" + if !validator.Jailed fail with "Validator not jailed, cannot unjail" @@ -43,6 +46,6 @@ handleMsgUnjail(tx MsgUnjail) return ``` -If the validator has enough stake to be in the top `n = MaximumBondedValidators`, they will be automatically rebonded, +If the validator has enough stake to be in the top `n = MaximumBondedValidators`, it will be automatically rebonded, and all delegators still delegated to the validator will be rebonded and begin to again collect provisions and rewards. diff --git a/x/slashing/spec/05_hooks.md b/x/slashing/spec/05_hooks.md index 8f78cdff01b0..2e26dfd3d411 100644 --- a/x/slashing/spec/05_hooks.md +++ b/x/slashing/spec/05_hooks.md @@ -4,7 +4,18 @@ order: 5 # Hooks -In this section we describe the "hooks" - slashing module code that runs when other events happen. +This section contains a description of the module's `hooks`. Hooks are operations that are executed automatically when events are raised. + +## Staking hooks + +The slashing module implements the `StakingHooks` defined in `x/staking` and are used as record-keeping of validators information. During the app initialization, these hooks should be registered in the staking module struct. + +The following hooks impact the slashing state: + ++ `AfterValidatorBonded` creates a `ValidatorSigningInfo` instance as described in the following section. ++ `AfterValidatorCreated` stores a validator's consensus key. ++ `AfterValidatorRemoved` removes a validator's consensus key. + ## Validator Bonded diff --git a/x/slashing/spec/06_events.md b/x/slashing/spec/06_events.md index c5fceda51981..cd79a652725c 100644 --- a/x/slashing/spec/06_events.md +++ b/x/slashing/spec/06_events.md @@ -6,7 +6,19 @@ order: 6 The slashing module emits the following events/tags: -## BeginBlocker +## MsgServer + +### MsgUnjail + +| Type | Attribute Key | Attribute Value | +| ------- | ------------- | --------------- | +| message | module | slashing | +| message | sender | {validatorAddress} | + + +## Keeper + +## BeginBlocker: HandleValidatorSignature | Type | Attribute Key | Attribute Value | | ----- | ------------- | --------------------------- | @@ -23,12 +35,14 @@ The slashing module emits the following events/tags: | liveness | missed_blocks | {missedBlocksCounter} | | liveness | height | {blockHeight} | -## Handlers -### MsgUnjail +### Slash -| Type | Attribute Key | Attribute Value | -| ------- | ------------- | --------------- | -| message | module | slashing | -| message | action | unjail | -| message | sender | {senderAddress} | ++ same as `"slash"` event from `HandleValidatorSignature`, but without the `jailed` attribute. + +### Jail + + +| Type | Attribute Key | Attribute Value | +| ----- | ------------- | ------------------ | +| slash | jailed | {validatorAddress} | diff --git a/x/slashing/spec/07_tombstone.md b/x/slashing/spec/07_tombstone.md index 4759a89b7190..216d1836f5a3 100644 --- a/x/slashing/spec/07_tombstone.md +++ b/x/slashing/spec/07_tombstone.md @@ -87,15 +87,17 @@ Currently, in the jail period implementation, once a validator unjails, all of their delegators who are delegated to them (haven't unbonded / redelegated away), stay with them. Given that consensus safety faults are so egregious (way more so than liveness faults), it is probably prudent to have delegators not -"auto-rebond" to the validator. Thus, we propose setting the "jail time" for a +"auto-rebond" to the validator. + +### Proposal: infinite jail + +We propose setting the "jail time" for a validator who commits a consensus safety fault, to `infinite` (i.e. a tombstone state). This essentially kicks the validator out of the validator set and does not allow them to re-enter the validator set. All of their delegators (including the operator themselves) have to either unbond or redelegate away. The validator operator can create a new validator if they would like, with a new operator key and consensus key, but they -have to "re-earn" their delegations back. To put the validator in the tombstone -state, we set `DoubleSignJailEndTime` to `time.Unix(253402300800)`, the maximum -time supported by Amino. +have to "re-earn" their delegations back. Implementing the tombstone system and getting rid of the slashing period tracking will make the `slashing` module way simpler, especially because we can remove all diff --git a/x/slashing/spec/08_params.md b/x/slashing/spec/08_params.md index 0ebfb9e27ef2..defed189ab9e 100644 --- a/x/slashing/spec/08_params.md +++ b/x/slashing/spec/08_params.md @@ -6,10 +6,10 @@ order: 8 The slashing module contains the following parameters: -| Key | Type | Example | -| ----------------------- | ---------------- | ---------------------- | -| SignedBlocksWindow | string (int64) | "100" | -| MinSignedPerWindow | string (dec) | "0.500000000000000000" | -| DowntimeJailDuration | string (time ns) | "600000000000" | -| SlashFractionDoubleSign | string (dec) | "0.050000000000000000" | -| SlashFractionDowntime | string (dec) | "0.010000000000000000" | +| Key | Type | Example | +| ----------------------- | -------------- | ---------------------- | +| SignedBlocksWindow | string (int64) | "100" | +| MinSignedPerWindow | string (dec) | "0.500000000000000000" | +| DowntimeJailDuration | string (ns) | "600000000000" | +| SlashFractionDoubleSign | string (dec) | "0.050000000000000000" | +| SlashFractionDowntime | string (dec) | "0.010000000000000000" | diff --git a/x/slashing/types/expected_keepers.go b/x/slashing/types/expected_keepers.go index 538a030baac0..9710ad1786e7 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/genesis.pb.go b/x/slashing/types/genesis.pb.go index 2a600bdb6c44..9819899d095e 100644 --- a/x/slashing/types/genesis.pb.go +++ b/x/slashing/types/genesis.pb.go @@ -705,10 +705,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -823,10 +820,7 @@ func (m *SigningInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -942,10 +936,7 @@ func (m *ValidatorMissedBlocks) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -1034,10 +1025,7 @@ func (m *MissedBlock) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/slashing/types/keys.go b/x/slashing/types/keys.go index c9792d22085f..f0049760f0a0 100644 --- a/x/slashing/types/keys.go +++ b/x/slashing/types/keys.go @@ -4,6 +4,7 @@ import ( "encoding/binary" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" ) const ( @@ -23,11 +24,11 @@ const ( // Keys for slashing store // Items are stored with the following key: values // -// - 0x01: ValidatorSigningInfo +// - 0x01: ValidatorSigningInfo // -// - 0x02: bool +// - 0x02: bool // -// - 0x03: crypto.PubKey +// - 0x03: cryptotypes.PubKey var ( ValidatorSigningInfoKeyPrefix = []byte{0x01} // Prefix for signing info ValidatorMissedBlockBitArrayKeyPrefix = []byte{0x02} // Prefix for missed block bit array @@ -36,31 +37,31 @@ var ( // ValidatorSigningInfoKey - stored by *Consensus* address (not operator address) func ValidatorSigningInfoKey(v sdk.ConsAddress) []byte { - return append(ValidatorSigningInfoKeyPrefix, v.Bytes()...) + return append(ValidatorSigningInfoKeyPrefix, address.MustLengthPrefix(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) != sdk.AddrLen { - panic("unexpected key length") - } + // Remove prefix and address length. + addr := key[2:] + return sdk.ConsAddress(addr) } // ValidatorMissedBlockBitArrayPrefixKey - stored by *Consensus* address (not operator address) func ValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte { - return append(ValidatorMissedBlockBitArrayKeyPrefix, v.Bytes()...) + return append(ValidatorMissedBlockBitArrayKeyPrefix, address.MustLengthPrefix(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...) +func AddrPubkeyRelationKey(addr []byte) []byte { + return append(AddrPubkeyRelationKeyPrefix, address.MustLengthPrefix(addr)...) } diff --git a/x/slashing/types/query.pb.go b/x/slashing/types/query.pb.go index 31f168becf3b..3a0c997a3056 100644 --- a/x/slashing/types/query.pb.go +++ b/x/slashing/types/query.pb.go @@ -846,10 +846,7 @@ func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -932,10 +929,7 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1017,10 +1011,7 @@ func (m *QuerySigningInfoRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1103,10 +1094,7 @@ func (m *QuerySigningInfoResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1192,10 +1180,7 @@ func (m *QuerySigningInfosRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1315,10 +1300,7 @@ func (m *QuerySigningInfosResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/slashing/types/slashing.pb.go b/x/slashing/types/slashing.pb.go index 87d3baf7c316..2f48e117ebc9 100644 --- a/x/slashing/types/slashing.pb.go +++ b/x/slashing/types/slashing.pb.go @@ -33,16 +33,19 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // liveness activity. type ValidatorSigningInfo struct { Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - // height at which validator was first a candidate OR was unjailed + // Height at which validator was first a candidate OR was unjailed StartHeight int64 `protobuf:"varint,2,opt,name=start_height,json=startHeight,proto3" json:"start_height,omitempty" yaml:"start_height"` - // index offset into signed block bit array + // Index which is incremented each time the validator was a bonded + // in a block and may have signed a precommit or not. This in conjunction with the + // `SignedBlocksWindow` param determines the index in the `MissedBlocksBitArray`. IndexOffset int64 `protobuf:"varint,3,opt,name=index_offset,json=indexOffset,proto3" json:"index_offset,omitempty" yaml:"index_offset"` - // timestamp validator cannot be unjailed until + // Timestamp until which the validator is jailed due to liveness downtime. JailedUntil time.Time `protobuf:"bytes,4,opt,name=jailed_until,json=jailedUntil,proto3,stdtime" json:"jailed_until" yaml:"jailed_until"` - // whether or not a validator has been tombstoned (killed out of validator - // set) + // Whether or not a validator has been tombstoned (killed out of validator set). It is set + // once the validator commits an equivocation or for any other configured misbehiavor. Tombstoned bool `protobuf:"varint,5,opt,name=tombstoned,proto3" json:"tombstoned,omitempty"` - // missed blocks counter (to avoid scanning the array every time) + // A counter kept to avoid unnecessary array reads. + // Note that `Sum(MissedBlocksBitArray)` always equals `MissedBlocksCounter`. MissedBlocksCounter int64 `protobuf:"varint,6,opt,name=missed_blocks_counter,json=missedBlocksCounter,proto3" json:"missed_blocks_counter,omitempty" yaml:"missed_blocks_counter"` } @@ -674,10 +677,7 @@ func (m *ValidatorSigningInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSlashing - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSlashing } if (iNdEx + skippy) > l { @@ -878,10 +878,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthSlashing - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSlashing } if (iNdEx + skippy) > l { diff --git a/x/slashing/types/tx.pb.go b/x/slashing/types/tx.pb.go index 8412b3291beb..ed6dbcd170ab 100644 --- a/x/slashing/types/tx.pb.go +++ b/x/slashing/types/tx.pb.go @@ -422,10 +422,7 @@ func (m *MsgUnjail) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -475,10 +472,7 @@ func (m *MsgUnjailResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/x/staking/client/cli/query.go b/x/staking/client/cli/query.go index 1e8b0a5c539a..7bf3ae41ccbe 100644 --- a/x/staking/client/cli/query.go +++ b/x/staking/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "strconv" "strings" @@ -115,7 +114,7 @@ $ %s query staking validators return err } - result, err := queryClient.Validators(context.Background(), &types.QueryValidatorsRequest{ + result, err := queryClient.Validators(cmd.Context(), &types.QueryValidatorsRequest{ // Leaving status empty on purpose to query all validators. Pagination: pageReq, }) @@ -172,7 +171,7 @@ $ %s query staking unbonding-delegations-from %s1gghjut3ccd8ay0zduzj64hwre2fxs9l Pagination: pageReq, } - res, err := queryClient.ValidatorUnbondingDelegations(context.Background(), params) + res, err := queryClient.ValidatorUnbondingDelegations(cmd.Context(), params) if err != nil { return err } @@ -227,7 +226,7 @@ $ %s query staking redelegations-from %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj Pagination: pageReq, } - res, err := queryClient.Redelegations(context.Background(), params) + res, err := queryClient.Redelegations(cmd.Context(), params) if err != nil { return err } @@ -282,7 +281,7 @@ $ %s query staking delegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1gghju ValidatorAddr: valAddr.String(), } - res, err := queryClient.Delegation(context.Background(), params) + res, err := queryClient.Delegation(cmd.Context(), params) if err != nil { return err } @@ -336,7 +335,7 @@ $ %s query staking delegations %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p Pagination: pageReq, } - res, err := queryClient.DelegatorDelegations(context.Background(), params) + res, err := queryClient.DelegatorDelegations(cmd.Context(), params) if err != nil { return err } @@ -391,7 +390,7 @@ $ %s query staking delegations-to %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj Pagination: pageReq, } - res, err := queryClient.ValidatorDelegations(context.Background(), params) + res, err := queryClient.ValidatorDelegations(cmd.Context(), params) if err != nil { return err } @@ -447,7 +446,7 @@ $ %s query staking unbonding-delegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9 ValidatorAddr: valAddr.String(), } - res, err := queryClient.UnbondingDelegation(context.Background(), params) + res, err := queryClient.UnbondingDelegation(cmd.Context(), params) if err != nil { return err } @@ -501,7 +500,7 @@ $ %s query staking unbonding-delegations %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru Pagination: pageReq, } - res, err := queryClient.DelegatorUnbondingDelegations(context.Background(), params) + res, err := queryClient.DelegatorUnbondingDelegations(cmd.Context(), params) if err != nil { return err } @@ -563,7 +562,7 @@ $ %s query staking redelegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1l2r SrcValidatorAddr: valSrcAddr.String(), } - res, err := queryClient.Redelegations(context.Background(), params) + res, err := queryClient.Redelegations(cmd.Context(), params) if err != nil { return err } @@ -617,7 +616,7 @@ $ %s query staking redelegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p Pagination: pageReq, } - res, err := queryClient.Redelegations(context.Background(), params) + res, err := queryClient.Redelegations(cmd.Context(), params) if err != nil { return err } @@ -660,7 +659,7 @@ $ %s query staking historical-info 5 } params := &types.QueryHistoricalInfoRequest{Height: height} - res, err := queryClient.HistoricalInfo(context.Background(), params) + res, err := queryClient.HistoricalInfo(cmd.Context(), params) if err != nil { return err @@ -697,7 +696,7 @@ $ %s query staking pool } queryClient := types.NewQueryClient(clientCtx) - res, err := queryClient.Pool(context.Background(), &types.QueryPoolRequest{}) + res, err := queryClient.Pool(cmd.Context(), &types.QueryPoolRequest{}) if err != nil { return err } @@ -733,7 +732,7 @@ $ %s query staking params } queryClient := types.NewQueryClient(clientCtx) - res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) + res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) if err != nil { return err } diff --git a/x/staking/client/cli/tx.go b/x/staking/client/cli/tx.go index 10ceba6a45e0..b7cd500cfae8 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/query.go b/x/staking/client/rest/query.go index d515d5dfed9b..7fae344d6748 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/rest/rest.go b/x/staking/client/rest/rest.go index 35bb8da68139..6f9b7e1e2a60 100644 --- a/x/staking/client/rest/rest.go +++ b/x/staking/client/rest/rest.go @@ -3,9 +3,8 @@ package rest import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/rest" - "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/rest" ) func RegisterHandlers(clientCtx client.Context, rtr *mux.Router) { diff --git a/x/staking/genesis.go b/x/staking/genesis.go index 02f6bc317473..96c7be8e44dc 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 28e3dec8a0fd..67f6c7a872ae 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 4b48f09427c9..bceef6b3f4e7 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/delegation_test.go b/x/staking/keeper/delegation_test.go index 77f4cb8e2c81..6458c35d3624 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/grpc_query_test.go b/x/staking/keeper/grpc_query_test.go index acf6f9cb5bfb..aa3b341b0953 100644 --- a/x/staking/keeper/grpc_query_test.go +++ b/x/staking/keeper/grpc_query_test.go @@ -732,10 +732,9 @@ func createValidators(t *testing.T, ctx sdk.Context, app *simapp.SimApp, powers addrs := simapp.AddTestAddrsIncremental(app, ctx, 5, sdk.TokensFromConsensusPower(300)) valAddrs := simapp.ConvertAddrsToValAddrs(addrs) pks := simapp.CreateTestPubKeys(5) - - appCodec, _ := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Marshaler app.StakingKeeper = keeper.NewKeeper( - appCodec, + cdc, app.GetKey(types.StoreKey), app.AccountKeeper, app.BankKeeper, diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go index 74d85a645bf7..81613d92bca9 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/slash_test.go b/x/staking/keeper/slash_test.go index 356c022e4e26..146ed5868732 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 @@ -408,10 +404,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 +567,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/val_state_change.go b/x/staking/keeper/val_state_change.go index f06994f44f70..52b739230076 100644 --- a/x/staking/keeper/val_state_change.go +++ b/x/staking/keeper/val_state_change.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// Calculate the ValidatorUpdates for the current block +// BlockValidatorUpdates calculates the ValidatorUpdates for the current block // Called in each EndBlock func (k Keeper) BlockValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate { // Calculate validator set changes. @@ -97,7 +97,7 @@ func (k Keeper) BlockValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate { return validatorUpdates } -// Apply and return accumulated updates to the bonded validator set. Also, +// ApplyAndReturnValidatorSetUpdates applies and return accumulated updates to the bonded validator set. Also, // * Updates the active valset as keyed by LastValidatorPowerKey. // * Updates the total power as keyed by LastTotalPowerKey. // * Updates validator status' according to updated powers. @@ -117,7 +117,10 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // Retrieve the last validator set. // The persistent set is updated later in this function. // (see LastValidatorPowerKey). - last := k.getLastValidatorsByAddr(ctx) + last, err := k.getLastValidatorsByAddr(ctx) + if err != nil { + return nil, err + } // Iterate over validators, highest power to lowest. iterator := k.ValidatorsPowerStoreIterator(ctx) @@ -160,10 +163,11 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab } // fetch the old power bytes - var valAddrBytes [sdk.AddrLen]byte - - copy(valAddrBytes[:], valAddr[:]) - oldPowerBytes, found := last[valAddrBytes] + valAddrStr, err := sdk.Bech32ifyAddressBytes(sdk.Bech32PrefixValAddr, valAddr) + if err != nil { + return nil, err + } + oldPowerBytes, found := last[valAddrStr] newPower := validator.ConsensusPower() newPowerBytes := k.cdc.MustMarshalBinaryBare(&gogotypes.Int64Value{Value: newPower}) @@ -174,13 +178,17 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab k.SetLastValidatorPower(ctx, valAddr, newPower) } - delete(last, valAddrBytes) + delete(last, valAddrStr) count++ totalPower = totalPower.Add(sdk.NewInt(newPower)) } - noLongerBonded := sortNoLongerBonded(last) + noLongerBonded, err := sortNoLongerBonded(last) + if err != nil { + return nil, err + } + for _, valAddrBytes := range noLongerBonded { validator := k.mustGetValidator(ctx, sdk.ValAddress(valAddrBytes)) validator, err = k.bondedToUnbonding(ctx, validator) @@ -339,39 +347,46 @@ func (k Keeper) completeUnbondingValidator(ctx sdk.Context, validator types.Vali return validator } -// map of operator addresses to serialized power -type validatorsByAddr map[[sdk.AddrLen]byte][]byte +// map of operator bech32-addresses to serialized power +// We use bech32 strings here, because we can't have slices as keys: map[[]byte][]byte +type validatorsByAddr map[string][]byte // get the last validator set -func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) validatorsByAddr { +func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) (validatorsByAddr, error) { last := make(validatorsByAddr) iterator := k.LastValidatorsIterator(ctx) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - var valAddr [sdk.AddrLen]byte - // extract the validator address from the key (prefix is 1-byte) - copy(valAddr[:], iterator.Key()[1:]) + // extract the validator address from the key (prefix is 1-byte, addrLen is 1-byte) + valAddr := types.AddressFromLastValidatorPowerKey(iterator.Key()) + valAddrStr, err := sdk.Bech32ifyAddressBytes(sdk.Bech32PrefixValAddr, valAddr) + if err != nil { + return nil, err + } + powerBytes := iterator.Value() - last[valAddr] = make([]byte, len(powerBytes)) - copy(last[valAddr], powerBytes) + last[valAddrStr] = make([]byte, len(powerBytes)) + copy(last[valAddrStr], powerBytes) } - return last + return last, nil } // given a map of remaining validators to previous bonded power // returns the list of validators to be unbonded, sorted by operator address -func sortNoLongerBonded(last validatorsByAddr) [][]byte { +func sortNoLongerBonded(last validatorsByAddr) ([][]byte, error) { // sort the map keys for determinism noLongerBonded := make([][]byte, len(last)) index := 0 - for valAddrBytes := range last { - valAddr := make([]byte, sdk.AddrLen) - copy(valAddr, valAddrBytes[:]) - noLongerBonded[index] = valAddr + for valAddrStr := range last { + valAddrBytes, err := sdk.ValAddressFromBech32(valAddrStr) + if err != nil { + return nil, err + } + noLongerBonded[index] = valAddrBytes index++ } // sorted by address - order doesn't matter @@ -380,5 +395,5 @@ func sortNoLongerBonded(last validatorsByAddr) [][]byte { return bytes.Compare(noLongerBonded[i], noLongerBonded[j]) == -1 }) - return noLongerBonded + return noLongerBonded, nil } diff --git a/x/staking/keeper/validator.go b/x/staking/keeper/validator.go index edb00ddba55b..0253c197aed3 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 } @@ -327,7 +288,7 @@ func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, handler func(operato defer iter.Close() for ; iter.Valid(); iter.Next() { - addr := sdk.ValAddress(iter.Key()[len(types.LastValidatorPowerKey):]) + addr := sdk.ValAddress(types.AddressFromLastValidatorPowerKey(iter.Key())) intV := &gogotypes.Int64Value{} k.cdc.MustUnmarshalBinaryBare(iter.Value(), intV) diff --git a/x/staking/keeper/validator_bench_test.go b/x/staking/keeper/validator_bench_test.go new file mode 100644 index 000000000000..54a616c90e50 --- /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 ae98c177ef30..3a1842397c59 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/module.go b/x/staking/module.go index f2e422117476..48c67e6f5cd1 100644 --- a/x/staking/module.go +++ b/x/staking/module.go @@ -157,6 +157,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json return cdc.MustMarshalJSON(gs) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock returns the begin blocker for the staking module. func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { BeginBlocker(ctx, am.keeper) diff --git a/x/staking/simulation/decoder_test.go b/x/staking/simulation/decoder_test.go index 60210fa89b37..36b7404fb282 100644 --- a/x/staking/simulation/decoder_test.go +++ b/x/staking/simulation/decoder_test.go @@ -32,9 +32,8 @@ func makeTestCodec() (cdc *codec.LegacyAmino) { } func TestDecodeStore(t *testing.T) { - cdc, _ := simapp.MakeCodecs() + cdc := simapp.MakeTestEncodingConfig().Marshaler dec := simulation.NewDecodeStore(cdc) - bondTime := time.Now().UTC() val, err := types.NewValidator(valAddr1, delPk1, types.NewDescription("test", "test", "test", "test", "test")) diff --git a/x/staking/simulation/operations.go b/x/staking/simulation/operations.go index 0bcb274ed717..d9621bc7321c 100644 --- a/x/staking/simulation/operations.go +++ b/x/staking/simulation/operations.go @@ -173,7 +173,7 @@ func SimulateMsgCreateValidator(ak types.AccountKeeper, bk types.BankKeeper, k k return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } @@ -244,7 +244,7 @@ func SimulateMsgEditValidator(ak types.AccountKeeper, bk types.BankKeeper, k kee return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } @@ -317,7 +317,7 @@ func SimulateMsgDelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.K return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } @@ -407,7 +407,7 @@ func SimulateMsgUndelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } @@ -520,6 +520,6 @@ func SimulateMsgBeginRedelegate(ak types.AccountKeeper, bk types.BankKeeper, k k return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simtypes.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } diff --git a/x/staking/simulation/operations_test.go b/x/staking/simulation/operations_test.go index fca3b78125a9..8930e2921ee4 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 c5f7376d7655..e090acb01541 100644 --- a/x/staking/spec/01_state.md +++ b/x/staking/spec/01_state.md @@ -8,23 +8,16 @@ order: 1 LastTotalPower tracks the total amounts of bonded tokens recorded during the previous end block. -- LastTotalPower: `0x12 -> amino(sdk.Int)` +- LastTotalPower: `0x12 -> ProtocolBuffer(sdk.Int)` ## Params Params is a module-wide configuration structure that stores system parameters and defines overall functioning of the staking module. -- Params: `Paramsspace("staking") -> amino(params)` +- Params: `Paramsspace("staking") -> legacy_amino(params)` -```go -type Params struct { - UnbondingTime time.Duration // time duration of unbonding - MaxValidators uint16 // maximum number of validators - MaxEntries uint16 // max entries for either unbonding delegation or redelegation (per pair/trio) - BondDenom string // bondable coin denomination -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.1/proto/cosmos/staking/v1beta1/staking.proto#L230-L241 ## Validator @@ -37,8 +30,8 @@ Validators can have one of three statuses They are signing blocks and receiving rewards. They can receive further delegations. They can be slashed for misbehavior. Delegators to this validator who unbond their delegation must wait the duration of the UnbondingTime, a chain-specific param. during which time - they are still slashable for offences of the source validator if those offences were committed - during the period of time that the tokens were bonded. + they are still slashable for offences of the source validator if those offences were committed + 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`. @@ -51,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 -> amino(validator)` +- Validators: `0x21 | OperatorAddr -> ProtocolBuffer(validator)` - ValidatorsByConsAddr: `0x22 | ConsAddr -> OperatorAddr` - ValidatorsByPower: `0x23 | BigEndian(ConsensusPower) | OperatorAddr -> OperatorAddr` -- LastValidatorsPower: `0x11 OperatorAddr -> amino(ConsensusPower)` +- LastValidatorsPower: `0x11 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 @@ -68,7 +61,7 @@ address which can be derived from the validator's `ConsPubKey`. `ValidatorsByPower` is an additional index that provides a sorted list o potential validators to quickly determine the current active set. Here -ConsensusPower is validator.Tokens/10^6. Note that all validators where +ConsensusPower is validator.Tokens/10^6. Note that all validators where `Jailed` is true are not stored within this index. `LastValidatorsPower` is a special index that provides a historical list of the @@ -77,60 +70,23 @@ is updated during the validator set update process which takes place in [`EndBlo Each validator's state is stored in a `Validator` struct: -```go -type Validator struct { - OperatorAddress sdk.ValAddress // address of the validator's operator; bech encoded in JSON - ConsPubKey crypto.PubKey // the consensus public key of the validator; bech encoded in JSON - Jailed bool // has the validator been jailed from bonded status? - Status sdk.BondStatus // validator status (bonded/unbonding/unbonded) - Tokens sdk.Int // delegated tokens (incl. self-delegation) - DelegatorShares sdk.Dec // total shares issued to a validator's delegators - Description Description // description terms for the validator - UnbondingHeight int64 // if unbonding, height at which this validator has begun unbonding - UnbondingCompletionTime time.Time // if unbonding, min time for the validator to complete unbonding - Commission Commission // commission parameters - MinSelfDelegation sdk.Int // validator's self declared minimum self delegation -} - -type Commission struct { - CommissionRates - UpdateTime time.Time // the last time the commission rate was changed -} - -CommissionRates struct { - Rate sdk.Dec // the commission rate charged to delegators, as a fraction - MaxRate sdk.Dec // maximum commission rate which validator can ever charge, as a fraction - MaxChangeRate sdk.Dec // maximum daily increase of the validator commission, as a fraction -} - -type Description struct { - Moniker string // name - Identity string // optional identity signature (ex. UPort or Keybase) - Website string // optional website link - SecurityContact string // optional email for security contact - Details string // optional details -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/staking.proto#L65-L99 + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/staking.proto#L24-L63 ## Delegation 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 -> amino(delegation)` +- Delegation: `0x31 | DelegatorAddr | 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 delegator, and is associated with the shares for one validator. The sender of the transaction is the owner of the bond. -```go -type Delegation struct { - DelegatorAddr sdk.AccAddress - ValidatorAddr sdk.ValAddress - Shares sdk.Dec // delegation shares received -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/staking.proto#L159-L170 ### Delegator Shares @@ -159,10 +115,8 @@ detected. `UnbondingDelegation` are indexed in the store as: -- UnbondingDelegation: `0x32 | DelegatorAddr | ValidatorAddr -> - amino(unbondingDelegation)` -- UnbondingDelegationsFromValidator: `0x33 | ValidatorAddr | DelegatorAddr -> - nil` +- UnbondingDelegation: `0x32 | DelegatorAddr | ValidatorAddr -> ProtocolBuffer(unbondingDelegation)` +- UnbondingDelegationsFromValidator: `0x33 | ValidatorAddr | 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 @@ -171,20 +125,7 @@ slashed. A UnbondingDelegation object is created every time an unbonding is initiated. -```go -type UnbondingDelegation struct { - DelegatorAddr sdk.AccAddress // delegator - ValidatorAddr sdk.ValAddress // validator unbonding from operator addr - Entries []UnbondingDelegationEntry // unbonding delegation entries -} - -type UnbondingDelegationEntry struct { - CreationHeight int64 // height which the unbonding took place - CompletionTime time.Time // unix time for unbonding completion - InitialBalance sdk.Coin // atoms initially scheduled to receive at completion - Balance sdk.Coin // atoms to receive at completion -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/staking.proto#L172-L198 ## Redelegation @@ -196,7 +137,7 @@ committed by the source validator. `Redelegation` are indexed in the store as: -- Redelegations: `0x34 | DelegatorAddr | ValidatorSrcAddr | ValidatorDstAddr -> amino(redelegation)` +- Redelegations: `0x34 | DelegatorAddr | ValidatorSrcAddr | ValidatorDstAddr -> ProtocolBuffer(redelegation)` - RedelegationsBySrc: `0x35 | ValidatorSrcAddr | ValidatorDstAddr | DelegatorAddr -> nil` - RedelegationsByDst: `0x36 | ValidatorDstAddr | ValidatorSrcAddr | DelegatorAddr -> nil` @@ -204,30 +145,15 @@ 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`, while the third map is for slashing based on the `ValidatorDstAddr`. -A redelegation object is created every time a redelegation occurs. To prevent +A redelegation object is created every time a redelegation occurs. To prevent "redelegation hopping" redelegations may not occur under the situation that: - the (re)delegator already has another immature redelegation in progress -with a destination to a validator (let's call it `Validator X`) + with a destination to a validator (let's call it `Validator X`) - and, the (re)delegator is attempting to create a _new_ redelegation -where the source validator for this new redelegation is `Validator-X`. - -```go -type Redelegation struct { - DelegatorAddr sdk.AccAddress // delegator - ValidatorSrcAddr sdk.ValAddress // validator redelegation source operator addr - ValidatorDstAddr sdk.ValAddress // validator redelegation destination operator addr - Entries []RedelegationEntry // redelegation entries -} - -type RedelegationEntry struct { - CreationHeight int64 // height which the redelegation took place - CompletionTime time.Time // unix time for redelegation completion - InitialBalance sdk.Coin // initial balance when redelegation started - Balance sdk.Coin // current balance (current value held in destination validator) - SharesDst sdk.Dec // amount of destination-validator shares created by redelegation -} -``` + where the source validator for this new redelegation is `Validator-X`. + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/staking.proto#L200-L228 ## Queues @@ -249,12 +175,7 @@ delegations queue is kept. - UnbondingDelegation: `0x41 | format(time) -> []DVPair` -```go -type DVPair struct { - DelegatorAddr sdk.AccAddress - ValidatorAddr sdk.ValAddress -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/staking.proto#L123-L133 ### RedelegationQueue @@ -263,13 +184,7 @@ kept. - UnbondingDelegation: `0x42 | format(time) -> []DVVTriplet` -```go -type DVVTriplet struct { - DelegatorAddr sdk.AccAddress - ValidatorSrcAddr sdk.ValAddress - ValidatorDstAddr sdk.ValAddress -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/staking.proto#L140-L152 ### ValidatorQueue @@ -279,7 +194,7 @@ queue is kept. - ValidatorQueueTime: `0x43 | format(time) -> []sdk.ValAddress` The stored object as each key is an array of validator operator addresses from -which the validator object can be accessed. Typically it is expected that only +which the validator object can be accessed. Typically it is expected that only a single validator record will be associated with a given timestamp however it is possible that multiple validators exist in the queue at the same location. @@ -288,15 +203,10 @@ that multiple validators exist in the queue at the same location. HistoricalInfo objects are stored and pruned at each block such that the staking keeper persists the `n` most recent historical info defined by staking module parameter: `HistoricalEntries`. -```go -type HistoricalInfo struct { - Header tmproto.Header - ValSet []types.Validator -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/staking.proto#L15-L22 At each BeginBlock, the staking keeper will persist the current Header and the Validators that committed -the current block in a `HistoricalInfo` object. The Validators are sorted on their address to ensure that +the current block in a `HistoricalInfo` object. The Validators are sorted on their address to ensure that they are in a determisnistic order. -The oldest HistoricalEntries will be pruned to ensure that there only exist the parameter-defined number of +The oldest HistoricalEntries will be pruned to ensure that there only exist the parameter-defined number of historical entries. diff --git a/x/staking/spec/02_state_transitions.md b/x/staking/spec/02_state_transitions.md index c2766aa43b79..9e245719cc24 100644 --- a/x/staking/spec/02_state_transitions.md +++ b/x/staking/spec/02_state_transitions.md @@ -11,12 +11,13 @@ This document describes the state transition operations pertaining to: 3. [Slashing](./02_state_transitions.md#slashing) ## Validators -State transitions in validators are performed on every [`EndBlock`](./05_end_block.md#validator-set-changes) + +State transitions in validators are performed on every [`EndBlock`](./05_end_block.md#validator-set-changes) in order to check for changes in the active `ValidatorSet`. ### Unbonded to Bonded -The following transition occurs when a validator's ranking in the `ValidatorPowerIndex` surpasses +The following transition occurs when a validator's ranking in the `ValidatorPowerIndex` surpasses that of the `LastValidator`. - set `validator.Status` to `Bonded` @@ -98,9 +99,9 @@ Redelegations affect the delegation, source and destination validators. - perform an `unbond` delegation from the source validator to retrieve the tokens worth of the unbonded shares - using the unbonded tokens, `Delegate` them to the destination validator -- if the `sourceValidator.Status` is `Bonded`, and the `destinationValidator` is not, +- if the `sourceValidator.Status` is `Bonded`, and the `destinationValidator` is not, transfer the newly delegated tokens from the `BondedPool` to the `NotBondedPool` `ModuleAccount` -- otherwise, if the `sourceValidator.Status` is not `Bonded`, and the `destinationValidator` +- otherwise, if the `sourceValidator.Status` is not `Bonded`, and the `destinationValidator` 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` @@ -116,20 +117,20 @@ When a redelegations complete the following occurs: 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. +- 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. + percentage of the initialBalance. - Each amount slashed from redelegations and unbonding delegations is subtracted from the -total slash amount. + 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. + `NonBondedPool` depending on the validator's status. This reduces the total supply of tokens. ### Slash Unbonding Delegation -When a validator is slashed, so are those unbonding delegations from the validator that began unbonding -after the time of the infraction. Every entry in every unbonding delegation from the validator -is slashed by `slashFactor`. The amount slashed is calculated from the `InitialBalance` of the +When a validator is slashed, so are those unbonding delegations from the validator that began unbonding +after the time of the infraction. Every entry in every unbonding delegation from the validator +is slashed by `slashFactor`. The amount slashed is calculated from the `InitialBalance` of the delegation and is capped to prevent a resulting negative balance. Completed (or mature) unbondings are not slashed. ### Slash Redelegation diff --git a/x/staking/spec/03_messages.md b/x/staking/spec/03_messages.md index bd9f21e2ceb0..1155da93d97d 100644 --- a/x/staking/spec/03_messages.md +++ b/x/staking/spec/03_messages.md @@ -6,23 +6,15 @@ order: 3 In this section we describe the processing of the staking messages and the corresponding updates to the state. All created/modified state objects specified by each message are defined within the [state](./02_state_transitions.md) section. -## MsgCreateValidator +## Msg/CreateValidator -A validator is created using the `MsgCreateValidator` message. +A validator is created using the `Msg/CreateValidator` service message. -```go -type MsgCreateValidator struct { - Description Description - Commission Commission ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L16-L17 - DelegatorAddr sdk.AccAddress - ValidatorAddr sdk.ValAddress - PubKey crypto.PubKey - Delegation sdk.Coin -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L35-L51 -This message is expected to fail if: +This service message is expected to fail if: - another validator with this operator address is already registered - another validator with this pubkey is already registered @@ -33,71 +25,63 @@ This message is expected to fail if: - the initial `MaxChangeRate` is either negative or > `MaxRate` - the description fields are too large -This message creates and stores the `Validator` object at appropriate indexes. +This service message creates and stores the `Validator` object at appropriate indexes. Additionally a self-delegation is made with the initial tokens delegation tokens `Delegation`. The validator always starts as unbonded but may be bonded in the first end-block. -## MsgEditValidator +## Msg/EditValidator The `Description`, `CommissionRate` of a validator can be updated using the -`MsgEditCandidacy`. +`Msg/EditCandidacy` service message. -```go -type MsgEditCandidacy struct { - Description Description - ValidatorAddr sdk.ValAddress - CommissionRate sdk.Dec -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L19-L20 -This message is expected to fail if: ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L56-L76 + +This service message is expected to fail if: - the initial `CommissionRate` is either negative or > `MaxRate` - the `CommissionRate` has already been updated within the previous 24 hours - the `CommissionRate` is > `MaxChangeRate` - the description fields are too large -This message stores the updated `Validator` object. +This service message stores the updated `Validator` object. -## MsgDelegate +## Msg/Delegate -Within this message the delegator provides coins, and in return receives +Within this service message the delegator provides coins, and in return receives some amount of their validator's (newly created) delegator-shares that are assigned to `Delegation.Shares`. -```go -type MsgDelegate struct { - DelegatorAddr sdk.AccAddress - ValidatorAddr sdk.ValAddress - Amount sdk.Coin -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L22-L24 + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90 -This message is expected to fail if: +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` If an existing `Delegation` object for provided addresses does not already -exist than it is created as part of this message otherwise the existing +exist than it is created as part of this service message otherwise the existing `Delegation` is updated to include the newly received shares. -## MsgBeginUnbonding +## Msg/Undelegate -The begin unbonding message allows delegators to undelegate their tokens from +The `Msg/Undelegate` service message allows delegators to undelegate their tokens from validator. -```go -type MsgBeginUnbonding struct { - DelegatorAddr sdk.AccAddress - ValidatorAddr sdk.ValAddress - Amount sdk.Coin -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L30-L32 + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121 + +This service message returns a response containing the completion time of the undelegation: -This message is expected to fail if: ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L123-L126 + +This service message is expected to fail if: - the delegation doesn't exist - the validator doesn't exist @@ -105,7 +89,7 @@ This message is expected to fail if: - existing `UnbondingDelegation` has maximum entries as defined by `params.MaxEntries` - the `Amount` has a denomination different than one defined by `params.BondDenom` -When this message is processed the following actions occur: +When this service message is processed the following actions occur: - validator's `DelegatorShares` and the delegation's `Shares` are both reduced by the message `SharesAmount` - calculate the token worth of the shares remove that amount tokens held within the validator @@ -116,22 +100,21 @@ When this message is processed the following actions occur: - if there are no more `Shares` in the delegation, then the delegation object is removed from the store - under this situation if the delegation is the validator's self-delegation then also jail the validator. -## MsgBeginRedelegate +## Msg/BeginRedelegate The redelegation command allows delegators to instantly switch validators. Once the unbonding period has passed, the redelegation is automatically completed in the EndBlocker. -```go -type MsgBeginRedelegate struct { - DelegatorAddr sdk.AccAddress - ValidatorSrcAddr sdk.ValAddress - ValidatorDstAddr sdk.ValAddress - Amount sdk.Coin -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L26-L28 + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105 + +This service message returns a response containing the completion time of the redelegation: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L107-L110 -This message is expected to fail if: +This service message is expected to fail if: - the delegation doesn't exist - the source or destination validators don't exist @@ -140,7 +123,7 @@ This message is expected to fail if: - existing `Redelegation` has maximum entries as defined by `params.MaxEntries` - the `Amount` `Coin` has a denomination different than one defined by `params.BondDenom` -When this message is processed the following actions occur: +When this service message is processed the following actions occur: - the source validator's `DelegatorShares` and the delegations `Shares` are both reduced by the message `SharesAmount` - calculate the token worth of the shares remove that amount tokens held within the source validator. @@ -148,6 +131,6 @@ When this message is processed the following actions occur: - `Bonded` - add an entry to the `Redelegation` (create `Redelegation` if it doesn't exist) with a completion time a full unbonding period from the current time. Update pool shares to reduce BondedTokens and increase NotBondedTokens by token worth of the shares (this may be effectively reversed in the next step however). - `Unbonding` - add an entry to the `Redelegation` (create `Redelegation` if it doesn't exist) with the same completion time as the validator (`UnbondingMinTime`). - `Unbonded` - no action required in this step -- Delegate the token worth to the destination validator, possibly moving tokens back to the bonded state. +- Delegate the token worth to the destination validator, possibly moving tokens back to the bonded state. - if there are no more `Shares` in the source delegation, then the source delegation object is removed from the store - under this situation if the delegation is the validator's self-delegation then also jail the validator. diff --git a/x/staking/spec/07_events.md b/x/staking/spec/07_events.md index d8255ca92b13..660319b3c1a4 100644 --- a/x/staking/spec/07_events.md +++ b/x/staking/spec/07_events.md @@ -18,9 +18,9 @@ The staking module emits the following events: | complete_redelegation | destination_validator | {dstValidatorAddress} | | complete_redelegation | delegator | {delegatorAddress} | -## Handlers +## Service Messages -### MsgCreateValidator +### Msg/CreateValidator | Type | Attribute Key | Attribute Value | | ---------------- | ------------- | ------------------ | @@ -30,7 +30,7 @@ The staking module emits the following events: | message | action | create_validator | | message | sender | {senderAddress} | -### MsgEditValidator +### Msg/EditValidator | Type | Attribute Key | Attribute Value | | -------------- | ------------------- | ------------------- | @@ -40,7 +40,7 @@ The staking module emits the following events: | message | action | edit_validator | | message | sender | {senderAddress} | -### MsgDelegate +### Msg/Delegate | Type | Attribute Key | Attribute Value | | -------- | ------------- | ------------------ | @@ -50,7 +50,7 @@ The staking module emits the following events: | message | action | delegate | | message | sender | {senderAddress} | -### MsgUndelegate +### Msg/Undelegate | Type | Attribute Key | Attribute Value | | ------- | ------------------- | ------------------ | @@ -61,9 +61,9 @@ The staking module emits the following events: | message | action | begin_unbonding | | message | sender | {senderAddress} | -* [0] Time is formatted in the RFC3339 standard +- [0] Time is formatted in the RFC3339 standard -### MsgBeginRedelegate +### Msg/BeginRedelegate | Type | Attribute Key | Attribute Value | | ---------- | --------------------- | --------------------- | @@ -75,4 +75,4 @@ The staking module emits the following events: | message | action | begin_redelegate | | message | sender | {senderAddress} | -* [0] Time is formatted in the RFC3339 standard +- [0] Time is formatted in the RFC3339 standard diff --git a/x/staking/spec/README.md b/x/staking/spec/README.md index 0005088ae837..a2cb7cf3a8df 100644 --- a/x/staking/spec/README.md +++ b/x/staking/spec/README.md @@ -38,18 +38,18 @@ network. - [Delegations](02_state_transitions.md#delegations) - [Slashing](02_state_transitions.md#slashing) 3. **[Messages](03_messages.md)** - - [MsgCreateValidator](03_messages.md#msgcreatevalidator) - - [MsgEditValidator](03_messages.md#msgeditvalidator) - - [MsgDelegate](03_messages.md#msgdelegate) - - [MsgBeginUnbonding](03_messages.md#msgbeginunbonding) - - [MsgBeginRedelegate](03_messages.md#msgbeginredelegate) + - [Msg/CreateValidator](03_messages.md#msgcreatevalidator) + - [Msg/EditValidator](03_messages.md#msgeditvalidator) + - [Msg/Delegate](03_messages.md#msgdelegate) + - [Msg/BeginUnbonding](03_messages.md#msgbeginunbonding) + - [Msg/BeginRedelegate](03_messages.md#msgbeginredelegate) 4. **[Begin-Block](04_begin_block.md)** - [Historical Info Tracking](04_begin_block.md#historical-info-tracking) -4. **[End-Block ](05_end_block.md)** +5. **[End-Block ](05_end_block.md)** - [Validator Set Changes](05_end_block.md#validator-set-changes) - [Queues ](05_end_block.md#queues-) -5. **[Hooks](06_hooks.md)** -6. **[Events](07_events.md)** +6. **[Hooks](06_hooks.md)** +7. **[Events](07_events.md)** - [EndBlocker](07_events.md#endblocker) - [Handlers](07_events.md#handlers) -7. **[Parameters](08_params.md)** +8. **[Parameters](08_params.md)** diff --git a/x/staking/teststaking/validator.go b/x/staking/teststaking/validator.go index 901395d76e92..71459581f0e6 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/expected_keepers.go b/x/staking/types/expected_keepers.go index 1c1d7f7de96b..07ceb7af1787 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 diff --git a/x/staking/types/genesis.pb.go b/x/staking/types/genesis.pb.go index 1f81d9fed41d..2ea6f8bf9964 100644 --- a/x/staking/types/genesis.pb.go +++ b/x/staking/types/genesis.pb.go @@ -740,10 +740,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { @@ -844,10 +841,7 @@ func (m *LastValidatorPower) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthGenesis - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenesis } if (iNdEx + skippy) > l { diff --git a/x/staking/types/keys.go b/x/staking/types/keys.go index 40d62244a041..1854ddac6159 100644 --- a/x/staking/types/keys.go +++ b/x/staking/types/keys.go @@ -8,6 +8,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" ) const ( @@ -48,24 +49,29 @@ var ( HistoricalInfoKey = []byte{0x50} // prefix for the historical info ) -// gets the key for the validator with address +// GetValidatorKey creates the key for the validator with address // VALUE: staking/Validator func GetValidatorKey(operatorAddr sdk.ValAddress) []byte { - return append(ValidatorsKey, operatorAddr.Bytes()...) + return append(ValidatorsKey, address.MustLengthPrefix(operatorAddr)...) } -// gets the key for the validator with pubkey +// GetValidatorByConsAddrKey creates the key for the validator with pubkey // VALUE: validator operator address ([]byte) func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte { - return append(ValidatorsByConsAddrKey, addr.Bytes()...) + return append(ValidatorsByConsAddrKey, address.MustLengthPrefix(addr)...) } -// Get the validator operator address from LastValidatorPowerKey +// AddressFromValidatorsKey creates the validator operator address from ValidatorsKey +func AddressFromValidatorsKey(key []byte) []byte { + return key[2:] // remove prefix bytes and address length +} + +// AddressFromLastValidatorPowerKey creates the validator operator address from LastValidatorPowerKey func AddressFromLastValidatorPowerKey(key []byte) []byte { - return key[1:] // remove prefix bytes + return key[2:] // remove prefix bytes and address length } -// get the validator by power index. +// GetValidatorsByPowerIndexKey creates 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) @@ -80,39 +86,39 @@ func GetValidatorsByPowerIndexKey(validator Validator) []byte { powerBytes := consensusPowerBytes powerBytesLen := len(powerBytes) // 8 - // key is of format prefix || powerbytes || addrBytes - key := make([]byte, 1+powerBytesLen+sdk.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) + addrLen := len(operAddrInvr) for i, b := range operAddrInvr { operAddrInvr[i] = ^b } - copy(key[powerBytesLen+1:], operAddrInvr) + // key is of format prefix || powerbytes || addrLen (1byte) || addrBytes + key := make([]byte, 1+powerBytesLen+1+addrLen) + + key[0] = ValidatorsByPowerIndexKey[0] + copy(key[1:powerBytesLen+1], powerBytes) + key[powerBytesLen+1] = byte(addrLen) + copy(key[powerBytesLen+2:], operAddrInvr) return key } -// get the bonded validator index key for an operator address +// GetLastValidatorPowerKey creates the bonded validator index key for an operator address func GetLastValidatorPowerKey(operator sdk.ValAddress) []byte { - return append(LastValidatorPowerKey, operator...) + return append(LastValidatorPowerKey, address.MustLengthPrefix(operator)...) } -// parse the validators operator address from power rank key +// ParseValidatorPowerRankKey parses the validators operator address from power rank key func ParseValidatorPowerRankKey(key []byte) (operAddr []byte) { powerBytesLen := 8 - if len(key) != 1+powerBytesLen+sdk.AddrLen { - panic("Invalid validator power rank key length") - } - operAddr = sdk.CopyBytes(key[powerBytesLen+1:]) + // key is of format prefix (1 byte) || powerbytes || addrLen (1byte) || addrBytes + operAddr = sdk.CopyBytes(key[powerBytesLen+2:]) for i, b := range operAddr { operAddr[i] = ^b @@ -165,55 +171,51 @@ func ParseValidatorQueueKey(bz []byte) (time.Time, int64, error) { return ts, int64(height), nil } -// gets the key for delegator bond with validator +// GetDelegationKey creates 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()...) + return append(GetDelegationsKey(delAddr), address.MustLengthPrefix(valAddr)...) } -// gets the prefix for a delegator for all validators +// GetDelegationsKey creates the prefix for a delegator for all validators func GetDelegationsKey(delAddr sdk.AccAddress) []byte { - return append(DelegationKey, delAddr.Bytes()...) + return append(DelegationKey, address.MustLengthPrefix(delAddr)...) } -// gets the key for an unbonding delegation by delegator and validator addr +// GetUBDKey creates 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()...) + return append(GetUBDsKey(delAddr.Bytes()), address.MustLengthPrefix(valAddr)...) } -// gets the index-key for an unbonding delegation, stored by validator-index +// GetUBDByValIndexKey creates 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()...) + return append(GetUBDsByValIndexKey(valAddr), address.MustLengthPrefix(delAddr)...) } -// rearranges the ValIndexKey to get the UBDKey +// GetUBDKeyFromValIndexKey rearranges the ValIndexKey to get the UBDKey func GetUBDKeyFromValIndexKey(indexKey []byte) []byte { addrs := indexKey[1:] // remove prefix bytes - if len(addrs) != 2*sdk.AddrLen { - panic("unexpected key length") - } - valAddr := addrs[:sdk.AddrLen] - delAddr := addrs[sdk.AddrLen:] + valAddrLen := addrs[0] + valAddr := addrs[1 : 1+valAddrLen] + delAddr := addrs[valAddrLen+2:] return GetUBDKey(delAddr, valAddr) } -// gets the prefix for all unbonding delegations from a delegator +// GetUBDsKey creates the prefix for all unbonding delegations from a delegator func GetUBDsKey(delAddr sdk.AccAddress) []byte { - return append(UnbondingDelegationKey, delAddr.Bytes()...) + return append(UnbondingDelegationKey, address.MustLengthPrefix(delAddr)...) } -// gets the prefix keyspace for the indexes of unbonding delegations for a validator +// GetUBDsByValIndexKey creates the prefix keyspace for the indexes of unbonding delegations for a validator func GetUBDsByValIndexKey(valAddr sdk.ValAddress) []byte { - return append(UnbondingDelegationByValIndexKey, valAddr.Bytes()...) + return append(UnbondingDelegationByValIndexKey, address.MustLengthPrefix(valAddr)...) } -// gets the prefix for all unbonding delegations from a delegator +// GetUnbondingDelegationTimeKey creates the prefix for all unbonding delegations from a delegator func GetUnbondingDelegationTimeKey(timestamp time.Time) []byte { bz := sdk.FormatTimeBytes(timestamp) return append(UnbondingQueueKey, bz...) @@ -222,69 +224,76 @@ func GetUnbondingDelegationTimeKey(timestamp time.Time) []byte { // 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+sdk.AddrLen*3) + // key is of the form GetREDsKey || valSrcAddrLen (1 byte) || valSrcAddr || valDstAddrLen (1 byte) || valDstAddr + key := make([]byte, 1+3+len(delAddr)+len(valSrcAddr)+len(valDstAddr)) - copy(key[0:sdk.AddrLen+1], GetREDsKey(delAddr.Bytes())) - copy(key[sdk.AddrLen+1:2*sdk.AddrLen+1], valSrcAddr.Bytes()) - copy(key[2*sdk.AddrLen+1:3*sdk.AddrLen+1], valDstAddr.Bytes()) + copy(key[0:2+len(delAddr)], GetREDsKey(delAddr.Bytes())) + key[2+len(delAddr)] = byte(len(valSrcAddr)) + copy(key[3+len(delAddr):3+len(delAddr)+len(valSrcAddr)], valSrcAddr.Bytes()) + key[3+len(delAddr)+len(valSrcAddr)] = byte(len(valDstAddr)) + copy(key[4+len(delAddr)+len(valSrcAddr):], valDstAddr.Bytes()) return key } -// gets the index-key for a redelegation, stored by source-validator-index +// GetREDByValSrcIndexKey creates 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*sdk.AddrLen) + // key is of the form REDSFromValsSrcKey || delAddrLen (1 byte) || delAddr || valDstAddrLen (1 byte) || valDstAddr + key := make([]byte, offset+2+len(delAddr)+len(valDstAddr)) copy(key[0:offset], REDSFromValsSrcKey) - copy(key[offset:offset+sdk.AddrLen], delAddr.Bytes()) - copy(key[offset+sdk.AddrLen:offset+2*sdk.AddrLen], valDstAddr.Bytes()) + key[offset] = byte(len(delAddr)) + copy(key[offset+1:offset+1+len(delAddr)], delAddr.Bytes()) + key[offset+1+len(delAddr)] = byte(len(valDstAddr)) + copy(key[offset+2+len(delAddr):], valDstAddr.Bytes()) return key } -// gets the index-key for a redelegation, stored by destination-validator-index +// GetREDByValDstIndexKey creates 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*sdk.AddrLen) + // key is of the form REDSToValsDstKey || delAddrLen (1 byte) || delAddr || valSrcAddrLen (1 byte) || valSrcAddr + key := make([]byte, offset+2+len(delAddr)+len(valSrcAddr)) copy(key[0:offset], REDSToValsDstKey) - copy(key[offset:offset+sdk.AddrLen], delAddr.Bytes()) - copy(key[offset+sdk.AddrLen:offset+2*sdk.AddrLen], valSrcAddr.Bytes()) + key[offset] = byte(len(delAddr)) + copy(key[offset+1:offset+1+len(delAddr)], delAddr.Bytes()) + key[offset+1+len(delAddr)] = byte(len(valSrcAddr)) + copy(key[offset+2+len(delAddr):], 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*sdk.AddrLen+1 { - panic("unexpected key length") - } + // note that first byte is prefix byte, which we remove + addrs := indexKey[1:] - valSrcAddr := indexKey[1 : sdk.AddrLen+1] - delAddr := indexKey[sdk.AddrLen+1 : 2*sdk.AddrLen+1] - valDstAddr := indexKey[2*sdk.AddrLen+1 : 3*sdk.AddrLen+1] + valSrcAddrLen := addrs[0] + valSrcAddr := addrs[1 : valSrcAddrLen+1] + delAddrLen := addrs[valSrcAddrLen+1] + delAddr := addrs[valSrcAddrLen+2 : valSrcAddrLen+2+delAddrLen] + valDstAddr := addrs[valSrcAddrLen+delAddrLen+3:] 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*sdk.AddrLen+1 { - panic("unexpected key length") - } + // note that first byte is prefix byte, which we remove + addrs := indexKey[1:] - valDstAddr := indexKey[1 : sdk.AddrLen+1] - delAddr := indexKey[sdk.AddrLen+1 : 2*sdk.AddrLen+1] - valSrcAddr := indexKey[2*sdk.AddrLen+1 : 3*sdk.AddrLen+1] + valDstAddrLen := addrs[0] + valDstAddr := addrs[1 : valDstAddrLen+1] + delAddrLen := addrs[valDstAddrLen+1] + delAddr := addrs[valDstAddrLen+2 : valDstAddrLen+2+delAddrLen] + valSrcAddr := addrs[valDstAddrLen+delAddrLen+3:] return GetREDKey(delAddr, valSrcAddr, valDstAddr) } @@ -299,25 +308,25 @@ func GetRedelegationTimeKey(timestamp time.Time) []byte { // GetREDsKey returns a key prefix for indexing a redelegation from a delegator // address. func GetREDsKey(delAddr sdk.AccAddress) []byte { - return append(RedelegationKey, delAddr.Bytes()...) + return append(RedelegationKey, address.MustLengthPrefix(delAddr)...) } // GetREDsFromValSrcIndexKey returns a key prefix for indexing a redelegation to // a source validator. func GetREDsFromValSrcIndexKey(valSrcAddr sdk.ValAddress) []byte { - return append(RedelegationByValSrcIndexKey, valSrcAddr.Bytes()...) + return append(RedelegationByValSrcIndexKey, address.MustLengthPrefix(valSrcAddr)...) } // 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()...) + return append(RedelegationByValDstIndexKey, address.MustLengthPrefix(valDstAddr)...) } // 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()...) + return append(GetREDsToValDstIndexKey(valDstAddr), address.MustLengthPrefix(delAddr)...) } // GetHistoricalInfoKey returns a key prefix for indexing HistoricalInfo objects. diff --git a/x/staking/types/keys_test.go b/x/staking/types/keys_test.go index 0f63617f26b3..949c7caedb66 100644 --- a/x/staking/types/keys_test.go +++ b/x/staking/types/keys_test.go @@ -37,10 +37,10 @@ func TestGetValidatorPowerRank(t *testing.T) { validator types.Validator wantHex string }{ - {val1, "2300000000000000009c288ede7df62742fc3b7d0962045a8cef0f79f6"}, - {val2, "2300000000000000019c288ede7df62742fc3b7d0962045a8cef0f79f6"}, - {val3, "23000000000000000a9c288ede7df62742fc3b7d0962045a8cef0f79f6"}, - {val4, "2300000100000000009c288ede7df62742fc3b7d0962045a8cef0f79f6"}, + {val1, "230000000000000000149c288ede7df62742fc3b7d0962045a8cef0f79f6"}, + {val2, "230000000000000001149c288ede7df62742fc3b7d0962045a8cef0f79f6"}, + {val3, "23000000000000000a149c288ede7df62742fc3b7d0962045a8cef0f79f6"}, + {val4, "230000010000000000149c288ede7df62742fc3b7d0962045a8cef0f79f6"}, } for i, tt := range tests { got := hex.EncodeToString(types.GetValidatorsByPowerIndexKey(tt.validator)) @@ -57,11 +57,11 @@ func TestGetREDByValDstIndexKey(t *testing.T) { wantHex string }{ {sdk.AccAddress(keysAddr1), sdk.ValAddress(keysAddr1), sdk.ValAddress(keysAddr1), - "3663d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f08609"}, + "361463d771218209d8bd03c482f69dfba57310f086091463d771218209d8bd03c482f69dfba57310f086091463d771218209d8bd03c482f69dfba57310f08609"}, {sdk.AccAddress(keysAddr1), sdk.ValAddress(keysAddr2), sdk.ValAddress(keysAddr3), - "363ab62f0d93849be495e21e3e9013a517038f45bd63d771218209d8bd03c482f69dfba57310f086095ef3b5f25c54946d4a89fc0d09d2f126614540f2"}, + "36143ab62f0d93849be495e21e3e9013a517038f45bd1463d771218209d8bd03c482f69dfba57310f08609145ef3b5f25c54946d4a89fc0d09d2f126614540f2"}, {sdk.AccAddress(keysAddr2), sdk.ValAddress(keysAddr1), sdk.ValAddress(keysAddr3), - "363ab62f0d93849be495e21e3e9013a517038f45bd5ef3b5f25c54946d4a89fc0d09d2f126614540f263d771218209d8bd03c482f69dfba57310f08609"}, + "36143ab62f0d93849be495e21e3e9013a517038f45bd145ef3b5f25c54946d4a89fc0d09d2f126614540f21463d771218209d8bd03c482f69dfba57310f08609"}, } for i, tt := range tests { got := hex.EncodeToString(types.GetREDByValDstIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr)) @@ -78,11 +78,11 @@ func TestGetREDByValSrcIndexKey(t *testing.T) { wantHex string }{ {sdk.AccAddress(keysAddr1), sdk.ValAddress(keysAddr1), sdk.ValAddress(keysAddr1), - "3563d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f08609"}, + "351463d771218209d8bd03c482f69dfba57310f086091463d771218209d8bd03c482f69dfba57310f086091463d771218209d8bd03c482f69dfba57310f08609"}, {sdk.AccAddress(keysAddr1), sdk.ValAddress(keysAddr2), sdk.ValAddress(keysAddr3), - "355ef3b5f25c54946d4a89fc0d09d2f126614540f263d771218209d8bd03c482f69dfba57310f086093ab62f0d93849be495e21e3e9013a517038f45bd"}, + "35145ef3b5f25c54946d4a89fc0d09d2f126614540f21463d771218209d8bd03c482f69dfba57310f08609143ab62f0d93849be495e21e3e9013a517038f45bd"}, {sdk.AccAddress(keysAddr2), sdk.ValAddress(keysAddr1), sdk.ValAddress(keysAddr3), - "3563d771218209d8bd03c482f69dfba57310f086095ef3b5f25c54946d4a89fc0d09d2f126614540f23ab62f0d93849be495e21e3e9013a517038f45bd"}, + "351463d771218209d8bd03c482f69dfba57310f08609145ef3b5f25c54946d4a89fc0d09d2f126614540f2143ab62f0d93849be495e21e3e9013a517038f45bd"}, } for i, tt := range tests { got := hex.EncodeToString(types.GetREDByValSrcIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr)) diff --git a/x/staking/types/msg.go b/x/staking/types/msg.go index 2525aa66468f..eda27b768a32 100644 --- a/x/staking/types/msg.go +++ b/x/staking/types/msg.go @@ -2,9 +2,17 @@ package types import ( "bytes" + "fmt" + "strconv" + "strings" + + "github.com/gogo/protobuf/proto" + + rosettatypes "github.com/coinbase/rosetta-sdk-go/types" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/server/rosetta" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -16,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 ( @@ -249,6 +264,90 @@ func (msg MsgDelegate) ValidateBasic() error { return nil } +// Rosetta Msg interface. +func (msg *MsgDelegate) ToOperations(withStatus bool, hasError bool) []*rosettatypes.Operation { + var operations []*rosettatypes.Operation + delAddr := msg.DelegatorAddress + valAddr := msg.ValidatorAddress + coin := msg.Amount + delOp := func(account *rosettatypes.AccountIdentifier, amount string, index int) *rosettatypes.Operation { + var status string + if withStatus { + status = rosetta.StatusSuccess + if hasError { + status = rosetta.StatusReverted + } + } + return &rosettatypes.Operation{ + OperationIdentifier: &rosettatypes.OperationIdentifier{ + Index: int64(index), + }, + Type: proto.MessageName(msg), + Status: status, + Account: account, + Amount: &rosettatypes.Amount{ + Value: amount, + Currency: &rosettatypes.Currency{ + Symbol: coin.Denom, + }, + }, + } + } + delAcc := &rosettatypes.AccountIdentifier{ + Address: delAddr, + } + valAcc := &rosettatypes.AccountIdentifier{ + Address: "staking_account", + SubAccount: &rosettatypes.SubAccountIdentifier{ + Address: valAddr, + }, + } + operations = append(operations, + delOp(delAcc, "-"+coin.Amount.String(), 0), + delOp(valAcc, coin.Amount.String(), 1), + ) + return operations +} + +func (msg *MsgDelegate) FromOperations(ops []*rosettatypes.Operation) (sdk.Msg, error) { + var ( + delAddr sdk.AccAddress + valAddr sdk.ValAddress + sendAmt sdk.Coin + err error + ) + + for _, op := range ops { + if strings.HasPrefix(op.Amount.Value, "-") { + if op.Account == nil { + return nil, fmt.Errorf("account identifier must be specified") + } + delAddr, err = sdk.AccAddressFromBech32(op.Account.Address) + if err != nil { + return nil, err + } + continue + } + + if op.Account.SubAccount == nil { + return nil, fmt.Errorf("account identifier subaccount must be specified") + } + valAddr, err = sdk.ValAddressFromBech32(op.Account.SubAccount.Address) + if err != nil { + return nil, err + } + + amount, err := strconv.ParseInt(op.Amount.Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid amount: %w", err) + } + + sendAmt = sdk.NewCoin(op.Amount.Currency.Symbol, sdk.NewInt(amount)) + } + + return NewMsgDelegate(delAddr, valAddr, sendAmt), nil +} + // NewMsgBeginRedelegate creates a new MsgBeginRedelegate instance. //nolint:interfacer func NewMsgBeginRedelegate( @@ -304,6 +403,103 @@ func (msg MsgBeginRedelegate) ValidateBasic() error { return nil } +// Rosetta Msg interface. +func (msg *MsgBeginRedelegate) ToOperations(withStatus bool, hasError bool) []*rosettatypes.Operation { + var operations []*rosettatypes.Operation + delAddr := msg.DelegatorAddress + srcValAddr := msg.ValidatorSrcAddress + destValAddr := msg.ValidatorDstAddress + coin := msg.Amount + delOp := func(account *rosettatypes.AccountIdentifier, amount string, index int) *rosettatypes.Operation { + var status string + if withStatus { + status = rosetta.StatusSuccess + if hasError { + status = rosetta.StatusReverted + } + } + return &rosettatypes.Operation{ + OperationIdentifier: &rosettatypes.OperationIdentifier{ + Index: int64(index), + }, + Type: proto.MessageName(msg), + Status: status, + Account: account, + Amount: &rosettatypes.Amount{ + Value: amount, + Currency: &rosettatypes.Currency{ + Symbol: coin.Denom, + }, + }, + } + } + srcValAcc := &rosettatypes.AccountIdentifier{ + Address: delAddr, + SubAccount: &rosettatypes.SubAccountIdentifier{ + Address: srcValAddr, + }, + } + destValAcc := &rosettatypes.AccountIdentifier{ + Address: "staking_account", + SubAccount: &rosettatypes.SubAccountIdentifier{ + Address: destValAddr, + }, + } + operations = append(operations, + delOp(srcValAcc, "-"+coin.Amount.String(), 0), + delOp(destValAcc, coin.Amount.String(), 1), + ) + return operations +} + +func (msg *MsgBeginRedelegate) FromOperations(ops []*rosettatypes.Operation) (sdk.Msg, error) { + var ( + delAddr sdk.AccAddress + srcValAddr sdk.ValAddress + destValAddr sdk.ValAddress + sendAmt sdk.Coin + err error + ) + + for _, op := range ops { + if strings.HasPrefix(op.Amount.Value, "-") { + if op.Account == nil { + return nil, fmt.Errorf("account identifier must be specified") + } + delAddr, err = sdk.AccAddressFromBech32(op.Account.Address) + if err != nil { + return nil, err + } + + if op.Account.SubAccount == nil { + return nil, fmt.Errorf("account identifier subaccount must be specified") + } + srcValAddr, err = sdk.ValAddressFromBech32(op.Account.SubAccount.Address) + if err != nil { + return nil, err + } + continue + } + + if op.Account.SubAccount == nil { + return nil, fmt.Errorf("account identifier subaccount must be specified") + } + destValAddr, err = sdk.ValAddressFromBech32(op.Account.SubAccount.Address) + if err != nil { + return nil, err + } + + amount, err := strconv.ParseInt(op.Amount.Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid amount: %w", err) + } + + sendAmt = sdk.NewCoin(op.Amount.Currency.Symbol, sdk.NewInt(amount)) + } + + return NewMsgBeginRedelegate(delAddr, srcValAddr, destValAddr, sendAmt), nil +} + // NewMsgUndelegate creates a new MsgUndelegate instance. //nolint:interfacer func NewMsgUndelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) *MsgUndelegate { @@ -351,3 +547,88 @@ func (msg MsgUndelegate) ValidateBasic() error { return nil } + +// Rosetta Msg interface. +func (msg *MsgUndelegate) ToOperations(withStatus bool, hasError bool) []*rosettatypes.Operation { + var operations []*rosettatypes.Operation + delAddr := msg.DelegatorAddress + valAddr := msg.ValidatorAddress + coin := msg.Amount + delOp := func(account *rosettatypes.AccountIdentifier, amount string, index int) *rosettatypes.Operation { + var status string + if withStatus { + status = rosetta.StatusSuccess + if hasError { + status = rosetta.StatusReverted + } + } + return &rosettatypes.Operation{ + OperationIdentifier: &rosettatypes.OperationIdentifier{ + Index: int64(index), + }, + Type: proto.MessageName(msg), + Status: status, + Account: account, + Amount: &rosettatypes.Amount{ + Value: amount, + Currency: &rosettatypes.Currency{ + Symbol: coin.Denom, + }, + }, + } + } + delAcc := &rosettatypes.AccountIdentifier{ + Address: delAddr, + } + valAcc := &rosettatypes.AccountIdentifier{ + Address: "staking_account", + SubAccount: &rosettatypes.SubAccountIdentifier{ + Address: valAddr, + }, + } + operations = append(operations, + delOp(valAcc, "-"+coin.Amount.String(), 0), + delOp(delAcc, coin.Amount.String(), 1), + ) + return operations +} + +func (msg *MsgUndelegate) FromOperations(ops []*rosettatypes.Operation) (sdk.Msg, error) { + var ( + delAddr sdk.AccAddress + valAddr sdk.ValAddress + undelAmt sdk.Coin + err error + ) + + for _, op := range ops { + if strings.HasPrefix(op.Amount.Value, "-") { + if op.Account.SubAccount == nil { + return nil, fmt.Errorf("account identifier subaccount must be specified") + } + valAddr, err = sdk.ValAddressFromBech32(op.Account.SubAccount.Address) + if err != nil { + return nil, err + } + continue + } + + if op.Account == nil { + return nil, fmt.Errorf("account identifier must be specified") + } + + delAddr, err = sdk.AccAddressFromBech32(op.Account.Address) + if err != nil { + return nil, err + } + + amount, err := strconv.ParseInt(op.Amount.Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid amount") + } + + undelAmt = sdk.NewCoin(op.Amount.Currency.Symbol, sdk.NewInt(amount)) + } + + return NewMsgUndelegate(delAddr, valAddr, undelAmt), nil +} diff --git a/x/staking/types/query.pb.go b/x/staking/types/query.pb.go index 0df0d02b0fd7..29f84adc27ac 100644 --- a/x/staking/types/query.pb.go +++ b/x/staking/types/query.pb.go @@ -3750,10 +3750,7 @@ func (m *QueryValidatorsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3873,10 +3870,7 @@ func (m *QueryValidatorsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -3958,10 +3952,7 @@ func (m *QueryValidatorRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4044,10 +4035,7 @@ func (m *QueryValidatorResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4165,10 +4153,7 @@ func (m *QueryValidatorDelegationsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4288,10 +4273,7 @@ func (m *QueryValidatorDelegationsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4409,10 +4391,7 @@ func (m *QueryValidatorUnbondingDelegationsRequest) Unmarshal(dAtA []byte) error if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4532,10 +4511,7 @@ func (m *QueryValidatorUnbondingDelegationsResponse) Unmarshal(dAtA []byte) erro if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4649,10 +4625,7 @@ func (m *QueryDelegationRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4738,10 +4711,7 @@ func (m *QueryDelegationResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4855,10 +4825,7 @@ func (m *QueryUnbondingDelegationRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -4941,10 +4908,7 @@ func (m *QueryUnbondingDelegationResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5062,10 +5026,7 @@ func (m *QueryDelegatorDelegationsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5185,10 +5146,7 @@ func (m *QueryDelegatorDelegationsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5306,10 +5264,7 @@ func (m *QueryDelegatorUnbondingDelegationsRequest) Unmarshal(dAtA []byte) error if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5429,10 +5384,7 @@ func (m *QueryDelegatorUnbondingDelegationsResponse) Unmarshal(dAtA []byte) erro if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5614,10 +5566,7 @@ func (m *QueryRedelegationsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5737,10 +5686,7 @@ func (m *QueryRedelegationsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5858,10 +5804,7 @@ func (m *QueryDelegatorValidatorsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5981,10 +5924,7 @@ func (m *QueryDelegatorValidatorsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6098,10 +6038,7 @@ func (m *QueryDelegatorValidatorRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6184,10 +6121,7 @@ func (m *QueryDelegatorValidatorResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6256,10 +6190,7 @@ func (m *QueryHistoricalInfoRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6345,10 +6276,7 @@ func (m *QueryHistoricalInfoResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6398,10 +6326,7 @@ func (m *QueryPoolRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6484,10 +6409,7 @@ func (m *QueryPoolResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6537,10 +6459,7 @@ func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -6623,10 +6542,7 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/staking/types/staking.pb.go b/x/staking/types/staking.pb.go index 4dbe0e3e300d..45b30b7a0e5e 100644 --- a/x/staking/types/staking.pb.go +++ b/x/staking/types/staking.pb.go @@ -135,8 +135,11 @@ func (m *HistoricalInfo) GetValset() []Validator { // CommissionRates defines the initial commission rates to be used for creating // a validator. type CommissionRates struct { - Rate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=rate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"rate"` - MaxRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=max_rate,json=maxRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"max_rate" yaml:"max_rate"` + // rate is the commission rate charged to delegators, as a fraction. + Rate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=rate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"rate"` + // max_rate defines the maximum commission rate which validator can ever charge, as a fraction. + MaxRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=max_rate,json=maxRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"max_rate" yaml:"max_rate"` + // max_change_rate defines the maximum daily increase of the validator commission, as a fraction. MaxChangeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=max_change_rate,json=maxChangeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"max_change_rate" yaml:"max_change_rate"` } @@ -174,8 +177,10 @@ var xxx_messageInfo_CommissionRates proto.InternalMessageInfo // Commission defines commission parameters for a given validator. type Commission struct { + // commission_rates defines the initial commission rates to be used for creating a validator. CommissionRates `protobuf:"bytes,1,opt,name=commission_rates,json=commissionRates,proto3,embedded=commission_rates" json:"commission_rates"` - UpdateTime time.Time `protobuf:"bytes,2,opt,name=update_time,json=updateTime,proto3,stdtime" json:"update_time" yaml:"update_time"` + // update_time is the last time the commission rate was changed. + UpdateTime time.Time `protobuf:"bytes,2,opt,name=update_time,json=updateTime,proto3,stdtime" json:"update_time" yaml:"update_time"` } func (m *Commission) Reset() { *m = Commission{} } @@ -219,11 +224,16 @@ func (m *Commission) GetUpdateTime() time.Time { // Description defines a validator description. type Description struct { - Moniker string `protobuf:"bytes,1,opt,name=moniker,proto3" json:"moniker,omitempty"` - Identity string `protobuf:"bytes,2,opt,name=identity,proto3" json:"identity,omitempty"` - Website string `protobuf:"bytes,3,opt,name=website,proto3" json:"website,omitempty"` + // moniker defines a human-readable name for the validator. + Moniker string `protobuf:"bytes,1,opt,name=moniker,proto3" json:"moniker,omitempty"` + // identity defines an optional identity signature (ex. UPort or Keybase). + Identity string `protobuf:"bytes,2,opt,name=identity,proto3" json:"identity,omitempty"` + // website defines an optional website link. + Website string `protobuf:"bytes,3,opt,name=website,proto3" json:"website,omitempty"` + // security_contact defines an optional email for security contact. SecurityContact string `protobuf:"bytes,4,opt,name=security_contact,json=securityContact,proto3" json:"security_contact,omitempty" yaml:"security_contact"` - Details string `protobuf:"bytes,5,opt,name=details,proto3" json:"details,omitempty"` + // details define other optional details. + Details string `protobuf:"bytes,5,opt,name=details,proto3" json:"details,omitempty"` } func (m *Description) Reset() { *m = Description{} } @@ -302,16 +312,27 @@ func (m *Description) GetDetails() string { // exchange rate. Voting power can be calculated as total bonded shares // multiplied by exchange rate. type Validator struct { - OperatorAddress string `protobuf:"bytes,1,opt,name=operator_address,json=operatorAddress,proto3" json:"operator_address,omitempty" yaml:"operator_address"` - ConsensusPubkey *types1.Any `protobuf:"bytes,2,opt,name=consensus_pubkey,json=consensusPubkey,proto3" json:"consensus_pubkey,omitempty" yaml:"consensus_pubkey"` - Jailed bool `protobuf:"varint,3,opt,name=jailed,proto3" json:"jailed,omitempty"` - Status BondStatus `protobuf:"varint,4,opt,name=status,proto3,enum=cosmos.staking.v1beta1.BondStatus" json:"status,omitempty"` - Tokens github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,5,opt,name=tokens,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"tokens"` - DelegatorShares github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,6,opt,name=delegator_shares,json=delegatorShares,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"delegator_shares" yaml:"delegator_shares"` - Description Description `protobuf:"bytes,7,opt,name=description,proto3" json:"description"` - UnbondingHeight int64 `protobuf:"varint,8,opt,name=unbonding_height,json=unbondingHeight,proto3" json:"unbonding_height,omitempty" yaml:"unbonding_height"` - UnbondingTime time.Time `protobuf:"bytes,9,opt,name=unbonding_time,json=unbondingTime,proto3,stdtime" json:"unbonding_time" yaml:"unbonding_time"` - Commission Commission `protobuf:"bytes,10,opt,name=commission,proto3" json:"commission"` + // operator_address defines the address of the validator's operator; bech encoded in JSON. + OperatorAddress string `protobuf:"bytes,1,opt,name=operator_address,json=operatorAddress,proto3" json:"operator_address,omitempty" yaml:"operator_address"` + // consensus_pubkey is the consensus public key of the validator, as a Protobuf Any. + ConsensusPubkey *types1.Any `protobuf:"bytes,2,opt,name=consensus_pubkey,json=consensusPubkey,proto3" json:"consensus_pubkey,omitempty" yaml:"consensus_pubkey"` + // jailed defined whether the validator has been jailed from bonded status or not. + Jailed bool `protobuf:"varint,3,opt,name=jailed,proto3" json:"jailed,omitempty"` + // status is the validator status (bonded/unbonding/unbonded). + Status BondStatus `protobuf:"varint,4,opt,name=status,proto3,enum=cosmos.staking.v1beta1.BondStatus" json:"status,omitempty"` + // tokens define the delegated tokens (incl. self-delegation). + Tokens github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,5,opt,name=tokens,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"tokens"` + // delegator_shares defines total shares issued to a validator's delegators. + DelegatorShares github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,6,opt,name=delegator_shares,json=delegatorShares,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"delegator_shares" yaml:"delegator_shares"` + // description defines the description terms for the validator. + Description Description `protobuf:"bytes,7,opt,name=description,proto3" json:"description"` + // unbonding_height defines, if unbonding, the height at which this validator has begun unbonding. + UnbondingHeight int64 `protobuf:"varint,8,opt,name=unbonding_height,json=unbondingHeight,proto3" json:"unbonding_height,omitempty" yaml:"unbonding_height"` + // unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. + UnbondingTime time.Time `protobuf:"bytes,9,opt,name=unbonding_time,json=unbondingTime,proto3,stdtime" json:"unbonding_time" yaml:"unbonding_time"` + // commission defines the commission parameters. + Commission Commission `protobuf:"bytes,10,opt,name=commission,proto3" json:"commission"` + // min_self_delegation is the validator's self declared minimum self delegation. MinSelfDelegation github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,11,opt,name=min_self_delegation,json=minSelfDelegation,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_self_delegation" yaml:"min_self_delegation"` } @@ -567,9 +588,12 @@ func (m *DVVTriplets) GetTriplets() []DVVTriplet { // owned by one delegator, and is associated with the voting power of one // validator. type Delegation struct { - DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty" yaml:"delegator_address"` - ValidatorAddress string `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` - Shares github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=shares,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"shares"` + // delegator_address is the bech32-encoded address of the delegator. + DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty" yaml:"delegator_address"` + // validator_address is the bech32-encoded address of the validator. + ValidatorAddress string `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` + // shares define the delegation shares received. + Shares github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=shares,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"shares"` } func (m *Delegation) Reset() { *m = Delegation{} } @@ -607,9 +631,12 @@ var xxx_messageInfo_Delegation proto.InternalMessageInfo // UnbondingDelegation stores all of a single delegator's unbonding bonds // for a single validator in an time-ordered list. type UnbondingDelegation struct { - DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty" yaml:"delegator_address"` - ValidatorAddress string `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` - Entries []UnbondingDelegationEntry `protobuf:"bytes,3,rep,name=entries,proto3" json:"entries"` + // delegator_address is the bech32-encoded address of the delegator. + DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty" yaml:"delegator_address"` + // validator_address is the bech32-encoded address of the validator. + ValidatorAddress string `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` + // entries are the unbonding delegation entries. + Entries []UnbondingDelegationEntry `protobuf:"bytes,3,rep,name=entries,proto3" json:"entries"` } func (m *UnbondingDelegation) Reset() { *m = UnbondingDelegation{} } @@ -646,10 +673,14 @@ var xxx_messageInfo_UnbondingDelegation proto.InternalMessageInfo // UnbondingDelegationEntry defines an unbonding object with relevant metadata. type UnbondingDelegationEntry struct { - CreationHeight int64 `protobuf:"varint,1,opt,name=creation_height,json=creationHeight,proto3" json:"creation_height,omitempty" yaml:"creation_height"` - CompletionTime time.Time `protobuf:"bytes,2,opt,name=completion_time,json=completionTime,proto3,stdtime" json:"completion_time" yaml:"completion_time"` + // creation_height is the height which the unbonding took place. + CreationHeight int64 `protobuf:"varint,1,opt,name=creation_height,json=creationHeight,proto3" json:"creation_height,omitempty" yaml:"creation_height"` + // completion_time is the unix time for unbonding completion. + CompletionTime time.Time `protobuf:"bytes,2,opt,name=completion_time,json=completionTime,proto3,stdtime" json:"completion_time" yaml:"completion_time"` + // initial_balance defines the tokens initially scheduled to receive at completion. InitialBalance github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=initial_balance,json=initialBalance,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"initial_balance" yaml:"initial_balance"` - Balance github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=balance,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"balance"` + // balance defines the tokens to receive at completion. + Balance github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=balance,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"balance"` } func (m *UnbondingDelegationEntry) Reset() { *m = UnbondingDelegationEntry{} } @@ -700,10 +731,14 @@ func (m *UnbondingDelegationEntry) GetCompletionTime() time.Time { // RedelegationEntry defines a redelegation object with relevant metadata. type RedelegationEntry struct { - CreationHeight int64 `protobuf:"varint,1,opt,name=creation_height,json=creationHeight,proto3" json:"creation_height,omitempty" yaml:"creation_height"` - CompletionTime time.Time `protobuf:"bytes,2,opt,name=completion_time,json=completionTime,proto3,stdtime" json:"completion_time" yaml:"completion_time"` + // creation_height defines the height which the redelegation took place. + CreationHeight int64 `protobuf:"varint,1,opt,name=creation_height,json=creationHeight,proto3" json:"creation_height,omitempty" yaml:"creation_height"` + // completion_time defines the unix time for redelegation completion. + CompletionTime time.Time `protobuf:"bytes,2,opt,name=completion_time,json=completionTime,proto3,stdtime" json:"completion_time" yaml:"completion_time"` + // initial_balance defines the initial balance when redelegation started. InitialBalance github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=initial_balance,json=initialBalance,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"initial_balance" yaml:"initial_balance"` - SharesDst github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,4,opt,name=shares_dst,json=sharesDst,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"shares_dst"` + // shares_dst is the amount of destination-validator shares created by redelegation. + SharesDst github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,4,opt,name=shares_dst,json=sharesDst,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"shares_dst"` } func (m *RedelegationEntry) Reset() { *m = RedelegationEntry{} } @@ -755,10 +790,14 @@ func (m *RedelegationEntry) GetCompletionTime() time.Time { // Redelegation contains the list of a particular delegator's redelegating bonds // from a particular source validator to a particular destination validator. type Redelegation struct { - DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty" yaml:"delegator_address"` - ValidatorSrcAddress string `protobuf:"bytes,2,opt,name=validator_src_address,json=validatorSrcAddress,proto3" json:"validator_src_address,omitempty" yaml:"validator_src_address"` - ValidatorDstAddress string `protobuf:"bytes,3,opt,name=validator_dst_address,json=validatorDstAddress,proto3" json:"validator_dst_address,omitempty" yaml:"validator_dst_address"` - Entries []RedelegationEntry `protobuf:"bytes,4,rep,name=entries,proto3" json:"entries"` + // delegator_address is the bech32-encoded address of the delegator. + DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty" yaml:"delegator_address"` + // validator_src_address is the validator redelegation source operator address. + ValidatorSrcAddress string `protobuf:"bytes,2,opt,name=validator_src_address,json=validatorSrcAddress,proto3" json:"validator_src_address,omitempty" yaml:"validator_src_address"` + // validator_dst_address is the validator redelegation destination operator address. + ValidatorDstAddress string `protobuf:"bytes,3,opt,name=validator_dst_address,json=validatorDstAddress,proto3" json:"validator_dst_address,omitempty" yaml:"validator_dst_address"` + // entries are the redelegation entries. + Entries []RedelegationEntry `protobuf:"bytes,4,rep,name=entries,proto3" json:"entries"` } func (m *Redelegation) Reset() { *m = Redelegation{} } @@ -795,11 +834,16 @@ var xxx_messageInfo_Redelegation proto.InternalMessageInfo // Params defines the parameters for the staking module. type Params struct { - UnbondingTime time.Duration `protobuf:"bytes,1,opt,name=unbonding_time,json=unbondingTime,proto3,stdduration" json:"unbonding_time" yaml:"unbonding_time"` - MaxValidators uint32 `protobuf:"varint,2,opt,name=max_validators,json=maxValidators,proto3" json:"max_validators,omitempty" yaml:"max_validators"` - MaxEntries uint32 `protobuf:"varint,3,opt,name=max_entries,json=maxEntries,proto3" json:"max_entries,omitempty" yaml:"max_entries"` - HistoricalEntries uint32 `protobuf:"varint,4,opt,name=historical_entries,json=historicalEntries,proto3" json:"historical_entries,omitempty" yaml:"historical_entries"` - BondDenom string `protobuf:"bytes,5,opt,name=bond_denom,json=bondDenom,proto3" json:"bond_denom,omitempty" yaml:"bond_denom"` + // unbonding_time is the time duration of unbonding. + UnbondingTime time.Duration `protobuf:"bytes,1,opt,name=unbonding_time,json=unbondingTime,proto3,stdduration" json:"unbonding_time" yaml:"unbonding_time"` + // max_validators is the maximum number of validators. + MaxValidators uint32 `protobuf:"varint,2,opt,name=max_validators,json=maxValidators,proto3" json:"max_validators,omitempty" yaml:"max_validators"` + // max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio). + MaxEntries uint32 `protobuf:"varint,3,opt,name=max_entries,json=maxEntries,proto3" json:"max_entries,omitempty" yaml:"max_entries"` + // historical_entries is the number of historical entries to persist. + HistoricalEntries uint32 `protobuf:"varint,4,opt,name=historical_entries,json=historicalEntries,proto3" json:"historical_entries,omitempty" yaml:"historical_entries"` + // bond_denom defines the bondable coin denomination. + BondDenom string `protobuf:"bytes,5,opt,name=bond_denom,json=bondDenom,proto3" json:"bond_denom,omitempty" yaml:"bond_denom"` } func (m *Params) Reset() { *m = Params{} } @@ -3602,10 +3646,7 @@ func (m *HistoricalInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -3757,10 +3798,7 @@ func (m *CommissionRates) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -3876,10 +3914,7 @@ func (m *Commission) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -4089,10 +4124,7 @@ func (m *Description) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -4469,10 +4501,7 @@ func (m *Validator) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -4554,10 +4583,7 @@ func (m *ValAddresses) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -4671,10 +4697,7 @@ func (m *DVPair) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -4758,10 +4781,7 @@ func (m *DVPairs) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -4907,10 +4927,7 @@ func (m *DVVTriplet) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -4994,10 +5011,7 @@ func (m *DVVTriplets) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -5145,10 +5159,7 @@ func (m *Delegation) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -5296,10 +5307,7 @@ func (m *UnbondingDelegation) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -5469,10 +5477,7 @@ func (m *UnbondingDelegationEntry) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -5642,10 +5647,7 @@ func (m *RedelegationEntry) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -5825,10 +5827,7 @@ func (m *Redelegation) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -6000,10 +5999,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -6119,10 +6115,7 @@ func (m *DelegationResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -6239,10 +6232,7 @@ func (m *RedelegationEntryResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -6359,10 +6349,7 @@ func (m *RedelegationResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { @@ -6480,10 +6467,7 @@ func (m *Pool) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthStaking - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStaking } if (iNdEx + skippy) > l { diff --git a/x/staking/types/tx.pb.go b/x/staking/types/tx.pb.go index f4a4f3c6864c..50e1765ff6c0 100644 --- a/x/staking/types/tx.pb.go +++ b/x/staking/types/tx.pb.go @@ -1642,10 +1642,7 @@ func (m *MsgCreateValidator) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1695,10 +1692,7 @@ func (m *MsgCreateValidatorResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1885,10 +1879,7 @@ func (m *MsgEditValidator) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -1938,10 +1929,7 @@ func (m *MsgEditValidatorResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2088,10 +2076,7 @@ func (m *MsgDelegate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2141,10 +2126,7 @@ func (m *MsgDelegateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2323,10 +2305,7 @@ func (m *MsgBeginRedelegate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2409,10 +2388,7 @@ func (m *MsgBeginRedelegateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2559,10 +2535,7 @@ func (m *MsgUndelegate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { @@ -2645,10 +2618,7 @@ func (m *MsgUndelegateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } if (iNdEx + skippy) > l { diff --git a/x/staking/types/validator.go b/x/staking/types/validator.go index e551e752528c..b23a7708f094 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/query.go b/x/upgrade/client/cli/query.go index 675dba3a9e8d..39674cbc733a 100644 --- a/x/upgrade/client/cli/query.go +++ b/x/upgrade/client/cli/query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "github.com/spf13/cobra" @@ -41,7 +40,7 @@ func GetCurrentPlanCmd() *cobra.Command { queryClient := types.NewQueryClient(clientCtx) params := types.QueryCurrentPlanRequest{} - res, err := queryClient.CurrentPlan(context.Background(), ¶ms) + res, err := queryClient.CurrentPlan(cmd.Context(), ¶ms) if err != nil { return err } @@ -74,9 +73,9 @@ func GetAppliedPlanCmd() *cobra.Command { return err } queryClient := types.NewQueryClient(clientCtx) - + ctx := cmd.Context() params := types.QueryAppliedPlanRequest{Name: args[0]} - res, err := queryClient.AppliedPlan(context.Background(), ¶ms) + res, err := queryClient.AppliedPlan(ctx, ¶ms) if err != nil { return err } @@ -90,7 +89,7 @@ func GetAppliedPlanCmd() *cobra.Command { if err != nil { return err } - headers, err := node.BlockchainInfo(context.Background(), res.Height, res.Height) + headers, err := node.BlockchainInfo(ctx, res.Height, res.Height) if err != nil { return err } diff --git a/x/upgrade/client/cli/tx.go b/x/upgrade/client/cli/tx.go index 32472ad84d16..bb8382af03c4 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/client/rest/query.go b/x/upgrade/client/rest/query.go index 946430634eff..299f7f7d8f96 100644 --- a/x/upgrade/client/rest/query.go +++ b/x/upgrade/client/rest/query.go @@ -34,7 +34,7 @@ func getCurrentPlanHandler(clientCtx client.Context) func(http.ResponseWriter, * } var plan types.Plan - err = clientCtx.LegacyAmino.UnmarshalBinaryBare(res, &plan) + err = clientCtx.LegacyAmino.UnmarshalJSON(res, &plan) if rest.CheckInternalServerError(w, err) { return } diff --git a/x/upgrade/client/rest/rest.go b/x/upgrade/client/rest/rest.go index 3192083f8da4..8f104852496c 100644 --- a/x/upgrade/client/rest/rest.go +++ b/x/upgrade/client/rest/rest.go @@ -3,9 +3,8 @@ package rest import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/rest" - "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/rest" ) // RegisterRoutes registers REST routes for the upgrade module under the path specified by routeName. diff --git a/x/upgrade/keeper/keeper.go b/x/upgrade/keeper/keeper.go index 0cf619cf36fe..1092885bcb3f 100644 --- a/x/upgrade/keeper/keeper.go +++ b/x/upgrade/keeper/keeper.go @@ -60,7 +60,7 @@ func (k Keeper) ScheduleUpgrade(ctx sdk.Context, plan types.Plan) error { return err } - if !plan.Time.IsZero() { + if plan.Time.Unix() > 0 { if !plan.Time.After(ctx.BlockHeader().Time) { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "upgrade cannot be scheduled in the past") } diff --git a/x/upgrade/legacy/v038/types.go b/x/upgrade/legacy/v038/types.go index fb06a6dc5a1c..db833477bf23 100644 --- a/x/upgrade/legacy/v038/types.go +++ b/x/upgrade/legacy/v038/types.go @@ -64,10 +64,11 @@ func (p Plan) ValidateBasic() error { if p.Height < 0 { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "height cannot be negative") } - if p.Time.IsZero() && p.Height == 0 { + isValidTime := p.Time.Unix() > 0 + if !isValidTime && p.Height == 0 { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "must set either time or height") } - if !p.Time.IsZero() && p.Height != 0 { + if isValidTime && p.Height != 0 { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "cannot set both time and height") } @@ -76,7 +77,7 @@ func (p Plan) ValidateBasic() error { // ShouldExecute returns true if the Plan is ready to execute given the current context func (p Plan) ShouldExecute(ctx sdk.Context) bool { - if !p.Time.IsZero() { + if p.Time.Unix() > 0 { return !ctx.BlockTime().Before(p.Time) } if p.Height > 0 { @@ -87,7 +88,7 @@ func (p Plan) ShouldExecute(ctx sdk.Context) bool { // DueAt is a string representation of when this plan is due to be executed func (p Plan) DueAt() string { - if !p.Time.IsZero() { + if p.Time.Unix() > 0 { return fmt.Sprintf("time: %s", p.Time.UTC().Format(time.RFC3339)) } return fmt.Sprintf("height: %d", p.Height) diff --git a/x/upgrade/module.go b/x/upgrade/module.go index 4e4982a324cf..23311c22d5f3 100644 --- a/x/upgrade/module.go +++ b/x/upgrade/module.go @@ -120,6 +120,9 @@ func (am AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONMarshaler) json.R return am.DefaultGenesis(cdc) } +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + // BeginBlock calls the upgrade module hooks // // CONTRACT: this is registered in BeginBlocker *before* all other modules' BeginBlock functions diff --git a/x/upgrade/types/plan.go b/x/upgrade/types/plan.go index 2cbacd60b0be..aa1a0601ffca 100644 --- a/x/upgrade/types/plan.go +++ b/x/upgrade/types/plan.go @@ -39,13 +39,13 @@ func (p Plan) ValidateBasic() error { if p.Height < 0 { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "height cannot be negative") } - if p.Time.IsZero() && p.Height == 0 { + if p.Time.Unix() <= 0 && p.Height == 0 { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "must set either time or height") } - if !p.Time.IsZero() && p.Height != 0 { + if p.Time.Unix() > 0 && p.Height != 0 { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "cannot set both time and height") } - if !p.Time.IsZero() && p.UpgradedClientState != nil { + if p.Time.Unix() > 0 && p.UpgradedClientState != nil { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "IBC chain upgrades must only set height") } @@ -54,7 +54,7 @@ func (p Plan) ValidateBasic() error { // ShouldExecute returns true if the Plan is ready to execute given the current context func (p Plan) ShouldExecute(ctx sdk.Context) bool { - if !p.Time.IsZero() { + if p.Time.Unix() > 0 { return !ctx.BlockTime().Before(p.Time) } if p.Height > 0 { @@ -65,7 +65,7 @@ func (p Plan) ShouldExecute(ctx sdk.Context) bool { // DueAt is a string representation of when this plan is due to be executed func (p Plan) DueAt() string { - if !p.Time.IsZero() { + if p.Time.Unix() > 0 { return fmt.Sprintf("time: %s", p.Time.UTC().Format(time.RFC3339)) } return fmt.Sprintf("height: %d", p.Height) diff --git a/x/upgrade/types/query.pb.go b/x/upgrade/types/query.pb.go index 3c49f6c4ade5..40caf74e8f6d 100644 --- a/x/upgrade/types/query.pb.go +++ b/x/upgrade/types/query.pb.go @@ -817,10 +817,7 @@ func (m *QueryCurrentPlanRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -906,10 +903,7 @@ func (m *QueryCurrentPlanResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -991,10 +985,7 @@ func (m *QueryAppliedPlanRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1063,10 +1054,7 @@ func (m *QueryAppliedPlanResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1135,10 +1123,7 @@ func (m *QueryUpgradedConsensusStateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -1224,10 +1209,7 @@ func (m *QueryUpgradedConsensusStateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { diff --git a/x/upgrade/types/upgrade.pb.go b/x/upgrade/types/upgrade.pb.go index cb608f874747..642f4446fe28 100644 --- a/x/upgrade/types/upgrade.pb.go +++ b/x/upgrade/types/upgrade.pb.go @@ -712,10 +712,7 @@ func (m *Plan) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUpgrade - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUpgrade } if (iNdEx + skippy) > l { @@ -862,10 +859,7 @@ func (m *SoftwareUpgradeProposal) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUpgrade - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUpgrade } if (iNdEx + skippy) > l { @@ -979,10 +973,7 @@ func (m *CancelSoftwareUpgradeProposal) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthUpgrade - } - if (iNdEx + skippy) < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthUpgrade } if (iNdEx + skippy) > l {